21
21
22
22
from testlinkhelper import TestLinkHelper , VERSION
23
23
import testlinkerrors
24
- from compiler . ast import TryExcept
24
+
25
25
26
26
27
27
# Default Definition which (python) API-Method expects which postional arguments
40
40
'createTestSuite' : ['testprojectid' , 'testsuitename' , 'details' ],
41
41
'doesUserExist' : ['user' ],
42
42
'repeat' : ['str' ],
43
- 'reportTCResult' : ['testplanid' , 'status' ]
43
+ 'reportTCResult' : ['testplanid' , 'status' ],
44
+ 'uploadExecutionAttachment' : ['executionid' ]
44
45
}
45
46
46
47
# decorators for generic api calls
@@ -69,15 +70,33 @@ def wrapper(self, *argsPositional, **argsOptional):
69
70
return methodAPI (self , * argsPositional , ** argsOptional )
70
71
return wrapper
71
72
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
+
72
88
73
89
class TestlinkAPIGeneric (object ):
74
90
75
91
__slots__ = ['server' , 'devKey' , '_server_url' , '_positionalArgNames' ]
76
92
77
93
__VERSION__ = VERSION
78
94
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 )
81
100
self .server = xmlrpclib .Server (server_url , transport , encoding ,
82
101
verbose , allow_none )
83
102
self .devKey = devKey
@@ -692,26 +711,24 @@ def checkDevKey(self):
692
711
# */
693
712
# public function uploadTestCaseAttachment($args)
694
713
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
+
715
732
716
733
# /**
717
734
# * Uploads an attachment for specified table. You must specify the table that
@@ -1152,6 +1169,19 @@ def _convertPostionalArgs(self, methodName, valueList):
1152
1169
raise testlinkerrors .TLArgError (new_msg )
1153
1170
return {nameList [x ] : valueList [x ] for x in range (len (nameList )) }
1154
1171
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
+
1155
1185
def _checkResponse (self , response , methodNameAPI , argsOptional ):
1156
1186
""" Checks if RESPONSE is empty or includes Error Messages
1157
1187
Will raise TLRepsonseError in this case """
@@ -1161,10 +1191,14 @@ def _checkResponse(self, response, methodNameAPI, argsOptional):
1161
1191
raise testlinkerrors .TLResponseError (
1162
1192
methodNameAPI , argsOptional ,
1163
1193
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
1168
1202
pass
1169
1203
else :
1170
1204
raise testlinkerrors .TLResponseError (methodNameAPI , argsOptional ,
0 commit comments