| Serhii Turivnyi | 9eccd84 | 2019-07-18 17:02:29 +0300 | [diff] [blame] | 1 | # | 
|  | 2 | # TestRail API binding for Python 3.x (API v2, available since | 
|  | 3 | # TestRail 3.0) | 
|  | 4 | # | 
|  | 5 | # Learn more: | 
|  | 6 | # | 
|  | 7 | # http://docs.gurock.com/testrail-api2/start | 
|  | 8 | # http://docs.gurock.com/testrail-api2/accessing | 
|  | 9 | # | 
|  | 10 | # Copyright Gurock Software GmbH. See license.md for details. | 
|  | 11 | # | 
|  | 12 |  | 
|  | 13 | import urllib.request, urllib.error | 
|  | 14 | import json, base64 | 
|  | 15 | import time | 
|  | 16 |  | 
|  | 17 | class APIClient: | 
|  | 18 | def __init__(self, base_url): | 
|  | 19 | self.user = '' | 
|  | 20 | self.password = '' | 
|  | 21 | if not base_url.endswith('/'): | 
|  | 22 | base_url += '/' | 
|  | 23 | self.__url = base_url + 'index.php?/api/v2/' | 
|  | 24 |  | 
|  | 25 | # | 
|  | 26 | # Send Get | 
|  | 27 | # | 
|  | 28 | # Issues a GET request (read) against the API and returns the result | 
|  | 29 | # (as Python dict). | 
|  | 30 | # | 
|  | 31 | # Arguments: | 
|  | 32 | # | 
|  | 33 | # uri                 The API method to call including parameters | 
|  | 34 | #                     (e.g. get_case/1) | 
|  | 35 | # | 
|  | 36 | def send_get(self, uri): | 
|  | 37 | try: | 
|  | 38 | return self.__send_request('GET', uri, None) | 
|  | 39 | except APIError: | 
|  | 40 | print("Got an API Exception. Waiting  30 sec.") | 
|  | 41 | time.sleep(30) | 
|  | 42 | return self.__send_request('GET', uri, None) | 
|  | 43 |  | 
|  | 44 | # | 
|  | 45 | # Send POST | 
|  | 46 | # | 
|  | 47 | # Issues a POST request (write) against the API and returns the result | 
|  | 48 | # (as Python dict). | 
|  | 49 | # | 
|  | 50 | # Arguments: | 
|  | 51 | # | 
|  | 52 | # uri                 The API method to call including parameters | 
|  | 53 | #                     (e.g. add_case/1) | 
|  | 54 | # data                The data to submit as part of the request (as | 
|  | 55 | #                     Python dict, strings must be UTF-8 encoded) | 
|  | 56 | # | 
|  | 57 | def send_post(self, uri, data): | 
|  | 58 | return self.__send_request('POST', uri, data) | 
|  | 59 |  | 
|  | 60 | def __send_request(self, method, uri, data): | 
|  | 61 | url = self.__url + uri | 
|  | 62 | request = urllib.request.Request(url) | 
|  | 63 | if (method == 'POST'): | 
|  | 64 | request.data = bytes(json.dumps(data), 'utf-8') | 
|  | 65 | auth = str( | 
|  | 66 | base64.b64encode( | 
|  | 67 | bytes('%s:%s' % (self.user, self.password), 'utf-8') | 
|  | 68 | ), | 
|  | 69 | 'ascii' | 
|  | 70 | ).strip() | 
|  | 71 | request.add_header('Authorization', 'Basic %s' % auth) | 
|  | 72 | request.add_header('Content-Type', 'application/json') | 
|  | 73 |  | 
|  | 74 | e = None | 
|  | 75 | try: | 
|  | 76 | response = urllib.request.urlopen(request).read() | 
|  | 77 | except urllib.error.HTTPError as ex: | 
|  | 78 | response = ex.read() | 
|  | 79 | e = ex | 
|  | 80 |  | 
|  | 81 | if response: | 
|  | 82 | result = json.loads(response.decode()) | 
|  | 83 | else: | 
|  | 84 | result = {} | 
|  | 85 |  | 
|  | 86 | if e != None: | 
|  | 87 | if result and 'error' in result: | 
|  | 88 | error = '"' + result['error'] + '"' | 
|  | 89 | else: | 
|  | 90 | error = 'No additional error message received' | 
|  | 91 | raise APIError('TestRail API returned HTTP %s (%s)' % | 
|  | 92 | (e.code, error)) | 
|  | 93 |  | 
|  | 94 | return result | 
|  | 95 |  | 
|  | 96 | class APIError(Exception): | 
|  | 97 | pass |