# Copyright 2015 Mirantis, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. # # TestRail API binding for Python 2.x (API v2, available since # TestRail 3.0) # # Learn more: # # http://docs.gurock.com/testrail-api2/start # http://docs.gurock.com/testrail-api2/accessing # # Copyright Gurock Software GmbH. See license.md for details. # from __future__ import unicode_literals import base64 import json import time # pylint: disable=import-error from six.moves.urllib.request import urlopen from six.moves.urllib.request import Request from six.moves.urllib.error import HTTPError # pylint: enable=import-error from fuelweb_test.testrail.settings import logger def request_retry(codes): log_msg = "Got {0} Error! Waiting {1} seconds and trying again..." def retry_request(func): def wrapper(*args, **kwargs): iter_number = 0 while True: try: response = func(*args, **kwargs) except HTTPError as e: if e.code in codes: if iter_number < codes[e.code]: wait = 5 if 'Retry-After' in e.hdrs: wait = int(e.hdrs['Retry-after']) logger.debug(log_msg.format(e.code, wait)) time.sleep(wait) iter_number += 1 continue raise else: return response return wrapper return retry_request class APIClient(object): """APIClient.""" # TODO documentation def __init__(self, base_url): self.user = '' self.password = '' if not base_url.endswith('/'): base_url += '/' self.__url = base_url + 'index.php?/api/v2/' # # Send Get # # Issues a GET request (read) against the API and returns the result # (as Python dict). # # Arguments: # # uri The API method to call including parameters # (e.g. get_case/1) # def send_get(self, uri): return self.__send_request('GET', uri, None) # # Send POST # # Issues a POST request (write) against the API and returns the result # (as Python dict). # # Arguments: # # uri The API method to call including parameters # (e.g. add_case/1) # data The data to submit as part of the request (as # Python dict, strings must be UTF-8 encoded) # def send_post(self, uri, data): return self.__send_request('POST', uri, data) def __send_request(self, method, uri, data): retry_codes = {429: 3} @request_retry(codes=retry_codes) def __get_response(_request): return urlopen(_request).read() url = self.__url + uri request = Request(url) if method == 'POST': request.add_data(json.dumps(data)) auth = base64.encodestring( '{0}:{1}'.format(self.user, self.password)).strip() request.add_header('Authorization', 'Basic {}'.format(auth)) request.add_header('Content-Type', 'application/json') e = None try: response = __get_response(request) except HTTPError as e: response = e.read() if response: result = json.loads(response) else: result = {} if e is not None: if result and 'error' in result: error = '"' + result['error'] + '"' else: error = 'No additional error message received' raise APIError('TestRail API returned HTTP %s (%s)' % (e.code, error)) return result class APIError(Exception): """APIError.""" # TODO documentation pass