# flake8: noqa
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
"""TestRail API binding for Python 3.x.
(API v2, available since TestRail 3.0)
Compatible
with TestRail 3.0
and later.
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.
"""
import base64
import json
import requests
class APIClient:
def __init__(self, base_url):
self.user =
""
self.password =
""
if not base_url.endswith(
"/"):
base_url +=
"/"
self.__url = base_url +
"index.php?/api/v2/"
def send_get(self, uri, filepath=
None):
"""Issue a GET request (read) against the API.
Args:
uri: The API method to call including parameters, e.g. get_case/1.
filepath: The path
and file name
for attachment download; used only
for 'get_attachment/:attachment_id'.
Returns:
A dict containing the result of the request.
"""
return self.__send_request(
"GET", uri, filepath)
def send_post(self, uri, data):
"""Issue a POST request (write) against the API.
Args:
uri: The API method to call, including parameters, e.g. add_case/1.
data: The data to submit
as part of the request
as a dict; strings
must be UTF-8 encoded.
If adding an attachment, must be the
path to the file.
Returns:
A dict containing the result of the request.
"""
return self.__send_request(
"POST", uri, data)
def __send_request(self, method, uri, data):
url = self.__url + uri
auth = str(
base64.b64encode(bytes(
"%s:%s" % (self.user, self.password),
"utf-8")),
"ascii",
).strip()
headers = {
"Authorization":
"Basic " + auth}
if method ==
"POST":
if uri[:14] ==
"add_attachment":
# add_attachment API method
files = {
"attachment": (open(data,
"rb"))}
response = requests.post(url, headers=headers, files=files)
files[
"attachment"].close()
else:
headers[
"Content-Type"] =
"application/json"
payload = bytes(json.dumps(data),
"utf-8")
response = requests.post(url, headers=headers, data=payload)
else:
headers[
"Content-Type"] =
"application/json"
response = requests.get(url, headers=headers)
if response.status_code > 201:
try:
error = response.json()
except (
requests.exceptions.HTTPError
):
# response.content not formatted as JSON
error = str(response.content)
raise APIError(
"TestRail API returned HTTP %s (%s)" % (response.status_code, error)
)
else:
if uri[:15] ==
"get_attachment/":
# Expecting file, not JSON
try:
open(data,
"wb").write(response.content)
return data
except FileNotFoundError:
return "Error saving attachment."
else:
try:
return response.json()
except requests.exceptions.HTTPError:
return {}
class APIError(Exception):
pass