2121
2222from testlinkhelper import TestLinkHelper , VERSION
2323import testlinkerrors
24- from compiler . ast import TryExcept
24+
2525
2626
2727# Default Definition which (python) API-Method expects which postional arguments
4040 'createTestSuite' : ['testprojectid' , 'testsuitename' , 'details' ],
4141 'doesUserExist' : ['user' ],
4242 'repeat' : ['str' ],
43- 'reportTCResult' : ['testplanid' , 'status' ]
43+ 'reportTCResult' : ['testplanid' , 'status' ],
44+ 'uploadExecutionAttachment' : ['executionid' ]
4445}
4546
4647# decorators for generic api calls
@@ -69,15 +70,33 @@ def wrapper(self, *argsPositional, **argsOptional):
6970 return methodAPI (self , * argsPositional , ** argsOptional )
7071 return wrapper
7172
73+ def decoApiCallAddAttachment (methodAPI ):
74+ """ Decorator to expand parameter list with devKey and attachmentfile
75+ attachmentfile is a python file descriptor pointing to the file
76+ """
77+ def wrapper (self , attachmentfile , * argsPositional , ** argsOptional ):
78+ if not ('devKey' in argsOptional ):
79+ argsOptional ['devKey' ] = self .devKey
80+ argsAttachment = self ._getAttachmentArgs (attachmentfile )
81+ # add additional key/value pairs from argsOptional
82+ # although overwrites filename, filetype, content with user definition
83+ # if they exist
84+ argsAttachment .update (argsOptional )
85+ return methodAPI (self , * argsPositional , ** argsAttachment )
86+ return wrapper
87+
7288
7389class TestlinkAPIGeneric (object ):
7490
7591 __slots__ = ['server' , 'devKey' , '_server_url' , '_positionalArgNames' ]
7692
7793 __VERSION__ = VERSION
7894
79- def __init__ (self , server_url , devKey ,
80- transport = None , encoding = None , verbose = 0 , allow_none = 0 ):
95+ def __init__ (self , server_url , devKey , ** args ):
96+ transport = args .get ('transport' )
97+ encoding = args .get ('encoding' )
98+ verbose = args .get ('verbose' ,0 )
99+ allow_none = args .get ('allow_none' ,0 )
81100 self .server = xmlrpclib .Server (server_url , transport , encoding ,
82101 verbose , allow_none )
83102 self .devKey = devKey
@@ -692,26 +711,24 @@ def checkDevKey(self):
692711# */
693712# public function uploadTestCaseAttachment($args)
694713
695- # /**
696- # * Uploads an attachment for an execution.
697- # *
698- # * The attachment content must be Base64 encoded by the client before sending it.
699- # *
700- # * @param struct $args
701- # * @param string $args["devKey"] Developer key
702- # * @param int $args["executionid"] execution ID
703- # * @param string $args["title"] (Optional) The title of the Attachment
704- # * @param string $args["description"] (Optional) The description of the Attachment
705- # * @param string $args["filename"] The file name of the Attachment (e.g.:notes.txt)
706- # * @param string $args["filetype"] The file type of the Attachment (e.g.: text/plain)
707- # * @param string $args["content"] The content (Base64 encoded) of the Attachment
708- # *
709- # * @since 1.9beta6
710- # * @return mixed $resultInfo an array containing the fk_id, fk_table, title,
711- # * description, file_name, file_size and file_type. If any errors occur it
712- # * returns the erros map.
713- # */
714- # public function uploadExecutionAttachment($args)
714+
715+ @decoApiCallAddAttachment
716+ @decoApiCallWithArgs
717+ def uploadExecutionAttachment (self ):
718+ """ uploadExecutionAttachment: Uploads an attachment for an execution
719+ mandatory args: attachmentfile
720+ positional args: executionid
721+ optional args : title, description, filename, filetype, content
722+
723+ attachmentfile: python file descriptor pointing to the file
724+ !Attention - on WINDOWS use binary mode for none text file
725+ see http://docs.python.org/2/tutorial/inputoutput.html#reading-and-writing-files
726+
727+ default values for filename, filetype, content are determine from
728+ ATTACHMENTFILE, but user could overwrite it, if they want to store the
729+ attachment with a different name
730+ """
731+
715732
716733# /**
717734# * Uploads an attachment for specified table. You must specify the table that
@@ -1152,6 +1169,19 @@ def _convertPostionalArgs(self, methodName, valueList):
11521169 raise testlinkerrors .TLArgError (new_msg )
11531170 return {nameList [x ] : valueList [x ] for x in range (len (nameList )) }
11541171
1172+ def _getAttachmentArgs (self , attachmentfile ):
1173+ """ returns dictionary with key/value pairs needed, to transfer
1174+ ATTACHMENTFILE via the api to into TL
1175+ ATTACHMENTFILE: python file descriptor pointing to the file """
1176+ import mimetypes
1177+ import base64
1178+ import os .path
1179+ return {'filename' :os .path .basename (attachmentfile .name ),
1180+ 'filetype' :mimetypes .guess_type (attachmentfile .name )[0 ],
1181+ 'content' :base64 .encodestring (attachmentfile .read ())
1182+ }
1183+
1184+
11551185 def _checkResponse (self , response , methodNameAPI , argsOptional ):
11561186 """ Checks if RESPONSE is empty or includes Error Messages
11571187 Will raise TLRepsonseError in this case """
@@ -1161,10 +1191,14 @@ def _checkResponse(self, response, methodNameAPI, argsOptional):
11611191 raise testlinkerrors .TLResponseError (
11621192 methodNameAPI , argsOptional ,
11631193 response [0 ]['message' ], response [0 ]['code' ])
1164- except TypeError :
1165- # some Response like doesUserExist returns boolean
1166- # they are not iterable an will raise an TypeError
1167- # - this reponses are ok
1194+ except (TypeError , KeyError ):
1195+ # if the reponse has not a [{..}] structure, the check
1196+ # 'code' in response[0]
1197+ # raise an error. Following causes are ok
1198+ # TypeError: raised from doesUserExist, cause the postiv
1199+ # response is simply 'True'
1200+ # KeyError: raise from uploadExecutionAttachment, cause the
1201+ # positiv response is directly a dictionary
11681202 pass
11691203 else :
11701204 raise testlinkerrors .TLResponseError (methodNameAPI , argsOptional ,
0 commit comments