diff --git a/py_jama_rest_client/client.py b/py_jama_rest_client/client.py index 4c0a953..41f3b7c 100644 --- a/py_jama_rest_client/client.py +++ b/py_jama_rest_client/client.py @@ -11,7 +11,7 @@ class APIException(Exception): """This is the base class for all exceptions raised by the JamaClient""" def __init__(self, message, status_code=None, reason=None): - super(APIException, self).__init__(message) + super().__init__(message) self.status_code = status_code self.reason = reason @@ -72,8 +72,8 @@ def __init__(self, host_domain, raise APIException(str(err)) # Log client creation - py_jama_rest_client_logger.info('Created a new JamaClient instance. Domain: {} ' - 'Connecting via Oauth: {}'.format(host_domain, oauth)) + py_jama_rest_client_logger.info(f'Created a new JamaClient instance. Domain: {host_domain} ' + f'Connecting via Oauth: {oauth}') def get_available_endpoints(self): """ @@ -113,7 +113,7 @@ def get_baseline(self, baseline_id): Returns: a dictionary object representing the baseline """ - resource_path = 'baselines/' + str(baseline_id) + resource_path = f'baselines/{baseline_id}' try: response = self.__core.get(resource_path) except CoreException as err: @@ -130,7 +130,7 @@ def get_baselines_versioneditems(self, baseline_id, allowed_results_per_page=__a allowed_results_per_page: Number of results per page Returns: A list of versioned items belonging to the baseline """ - resource_path = 'baselines/' + str(baseline_id) + '/versioneditems' + resource_path = f'baselines/{baseline_id}/versioneditems' baseline_items = self.__get_all(resource_path, allowed_results_per_page=allowed_results_per_page) return baseline_items @@ -155,7 +155,7 @@ def get_filter_results(self, filter_id, project_id=None, allowed_results_per_pag A List of items that match the filter. """ - resource_path = 'filters/' + str(filter_id) + '/results' + resource_path = f'filters/{filter_id}/results' params = None if project_id is not None: params = {'project': str(project_id)} @@ -186,7 +186,7 @@ def get_item(self, item_id): Returns: a dictonary object representing the item """ - resource_path = 'items/' + str(item_id) + resource_path = f'items/{item_id}' try: response = self.__core.get(resource_path) except CoreException as err: @@ -205,7 +205,7 @@ def get_item_lock(self, item_id): A JSON object with the lock information for the item with the specified ID. """ - resource_path = 'items/' + str(item_id) + '/lock' + resource_path = f'items/{item_id}/lock' try: response = self.__core.get(resource_path) except CoreException as err: @@ -228,7 +228,7 @@ def put_item_lock(self, item_id, locked): body = { "locked": locked, } - resource_path = 'items/' + str(item_id) + '/lock' + resource_path = f'items/{item_id}/lock' headers = {'content-type': 'application/json'} try: response = self.__core.put(resource_path, data=json.dumps(body), headers=headers) @@ -248,7 +248,7 @@ def get_item_tags(self, item_id, allowed_results_per_page=__allowed_results_per_ Returns: a dictionary object representing the item's tags """ - resource_path = 'items/' + str(item_id) + '/tags' + resource_path = f'items/{item_id}/tags' item_tags = self.__get_all(resource_path, allowed_results_per_page=allowed_results_per_page) return item_tags @@ -261,7 +261,7 @@ def get_attachment(self, attachment_id): Returns: a dictonary object representing the attachment """ - resource_path = 'attachments/' + str(attachment_id) + resource_path = f'attachments/{attachment_id}' try: response = self.__core.get(resource_path) except CoreException as err: @@ -297,7 +297,7 @@ def get_relationship_rule_set(self, id): Returns: A dictionary object representing a rule set and its associated rules """ - resource_path = 'relationshiprulesets/' + str(id) + resource_path = f'relationshiprulesets/{id}' response = self.__core.get(resource_path) JamaClient.__handle_response_status(response) return response.json()['data'] @@ -309,7 +309,7 @@ def get_relationship_rule_set_projects(self, id): Returns: An array of the dictionary objects representing the projects with a given rule set assigned """ - resource_path = 'relationshiprulesets/' + str(id) + '/projects' + resource_path = f'relationshiprulesets/{id}/projects' projects = self.__get_all(resource_path) return projects @@ -337,7 +337,7 @@ def get_relationship_type(self, relationship_type_id): Returns: JSON object """ - resource_path = 'relationshiptypes/' + str(relationship_type_id) + resource_path = f'relationshiptypes/{relationship_type_id}' try: response = self.__core.get(resource_path) except CoreException as err: @@ -370,7 +370,7 @@ def get_item_type(self, item_type_id): Returns: JSON object """ - resource_path = 'itemtypes/' + str(item_type_id) + resource_path = f'itemtypes/{item_type_id}' try: response = self.__core.get(resource_path) except CoreException as err: @@ -391,7 +391,7 @@ def get_items_synceditems(self, item_id, allowed_results_per_page=__allowed_resu specified item. """ - resource_path = 'items/' + str(item_id) + '/synceditems' + resource_path = f'items/{item_id}/synceditems' synced_items = self.__get_all(resource_path, allowed_results_per_page=allowed_results_per_page) return synced_items @@ -406,58 +406,7 @@ def get_items_synceditems_status(self, item_id, synced_item_id): Returns: The response JSON from the API which contains a single field 'inSync' with a boolean value. """ - resource_path = 'items/' + str(item_id) + '/synceditems/' + str(synced_item_id) + '/syncstatus' - try: - response = self.__core.get(resource_path) - except CoreException as err: - py_jama_rest_client_logger.error(err) - raise APIException(str(err)) - JamaClient.__handle_response_status(response) - return response.json()['data'] - - def get_item_versions(self, item_id): - """ - Get all versions for the item with the specified ID - - Args: - item_id: the item id of the item to fetch - - Returns: JSON array with all versions for the item - """ - resource_path = 'items/' + str(item_id) + '/versions' - versions = self.__get_all(resource_path) - return versions - - def get_item_version(self, item_id, version_num): - """ - Get the numbered version for the item with the specified ID - - Args: - item_id: the item id of the item to fetch - version_num: the version number for the item - - Returns: a dictionary object representing the numbered version - """ - resource_path = 'items/' + str(item_id) + '/versions/' + str(version_num) - try: - response = self.__core.get(resource_path) - except CoreException as err: - py_jama_rest_client_logger.error(err) - raise APIException(str(err)) - JamaClient.__handle_response_status(response) - return response.json()['data'] - - def get_versioned_item(self, item_id, version_num): - """ - Get the snapshot of the item at the specified version - - Args: - item_id: the item id of the item to fetch - version_num: the version number for the item - - Returns: a dictionary object representing the versioned item - """ - resource_path = 'items/' + str(item_id) + '/versions/' + str(version_num) + '/versioneditem' + resource_path = f'items/{item_id}/synceditems/{synced_item_id}/syncstatus' try: response = self.__core.get(resource_path) except CoreException as err: @@ -476,7 +425,7 @@ def get_item_versions(self, item_id, allowed_results_per_page=__allowed_results_ Returns: JSON array with all versions for the item """ - resource_path = 'items/' + str(item_id) + '/versions' + resource_path = f'items/{item_id}/versions' versions = self.__get_all(resource_path, allowed_results_per_page=allowed_results_per_page) return versions @@ -490,7 +439,7 @@ def get_item_version(self, item_id, version_num): Returns: a dictionary object representing the numbered version """ - resource_path = 'items/' + str(item_id) + '/versions/' + str(version_num) + resource_path = f'items/{item_id}/versions/{version_num}' response = self.__core.get(resource_path) JamaClient.__handle_response_status(response) return response.json()['data'] @@ -505,7 +454,7 @@ def get_versioned_item(self, item_id, version_num): Returns: a dictionary object representing the versioned item """ - resource_path = 'items/' + str(item_id) + '/versions/' + str(version_num) + '/versioneditem' + resource_path = f'items/{item_id}/versions/{version_num}/versioneditem' response = self.__core.get(resource_path) JamaClient.__handle_response_status(response) return response.json()['data'] @@ -534,7 +483,7 @@ def get_pick_list(self, pick_list_id): Returns: a dictionary object representing the picklist. """ - resource_path = 'picklists/' + str(pick_list_id) + resource_path = f'picklists/{pick_list_id}' try: response = self.__core.get(resource_path) except CoreException as err: @@ -553,7 +502,7 @@ def get_pick_list_options(self, pick_list_id, allowed_results_per_page=__allowed Returns: an array of dictionary objects that represent the picklist options. """ - resource_path = 'picklists/' + str(pick_list_id) + '/options' + resource_path = f'picklists/{pick_list_id}/options' pick_list_options = self.__get_all(resource_path, allowed_results_per_page=allowed_results_per_page) return pick_list_options @@ -566,7 +515,7 @@ def get_pick_list_option(self, pick_list_option_id): Returns: A dictonary object representing the picklist option. """ - resource_path = 'picklistoptions/' + str(pick_list_option_id) + resource_path = f'picklistoptions/{pick_list_option_id}' try: response = self.__core.get(resource_path) except CoreException as err: @@ -588,7 +537,7 @@ def get_relationships(self, project_id, allowed_results_per_page=__allowed_resul """ resource_path = 'relationships' params = {'project': project_id} - relationship_data = self.__get_all(resource_path, params=params, + relationship_data = self.__get_all_last_id(resource_path, params=params, allowed_results_per_page=allowed_results_per_page) return relationship_data @@ -602,7 +551,7 @@ def get_relationship(self, relationship_id): Returns: a dictionary object that represents a relationship """ - resource_path = 'relationships/' + str(relationship_id) + resource_path = f'relationships/{relationship_id}' try: response = self.__core.get(resource_path) except CoreException as err: @@ -642,34 +591,18 @@ def get_abstract_items(self, resource_path = 'abstractitems' # Add each parameter that is not null to the request. - params = {} - - if project is not None: - params['project'] = project - - if item_type is not None: - params['itemType'] = item_type - - if document_key is not None: - params['documentKey'] = document_key - - if release is not None: - params['release'] = release - - if created_date is not None: - params['createdDate'] = created_date - - if modified_date is not None: - params['modifiedDate'] = modified_date - - if last_activity_date is not None: - params['lastActivityDate'] = last_activity_date - - if contains is not None: - params['contains'] = contains - - if sort_by is not None: - params['sortBy'] = sort_by + param_mapping = { + 'project': project, + 'itemType': item_type, + 'documentKey': document_key, + 'release': release, + 'createdDate': created_date, + 'modifiedDate': modified_date, + 'lastActivityDate': last_activity_date, + 'contains': contains, + 'sortBy': sort_by + } + params = {k: v for k, v in param_mapping.items() if v is not None} abstract_items = self.__get_all(resource_path, params=params) return abstract_items @@ -683,7 +616,7 @@ def get_abstract_item(self, item_id): Returns: a dictonary object representing the abstract item """ - resource_path = 'abstractitems/' + str(item_id) + resource_path = f'abstractitems/{item_id}' try: response = self.__core.get(resource_path) except CoreException as err: @@ -701,7 +634,7 @@ def get_abstract_item_versions(self, item_id): Returns: JSON array with all versions for the item """ - resource_path = 'abstractitems/' + str(item_id) + '/versions' + resource_path = f'abstractitems/{item_id}/versions' versions = self.__get_all(resource_path) return versions @@ -715,7 +648,7 @@ def get_abtract_item_version(self, item_id, version_num): Returns: a dictionary object representing the numbered version """ - resource_path = 'abstractitems/' + str(item_id) + '/versions/' + str(version_num) + resource_path = f'abstractitems/{item_id}/versions/{version_num}' try: response = self.__core.get(resource_path) except CoreException as err: @@ -734,7 +667,7 @@ def get_abstract_versioned_item(self, item_id, version_num): Returns: a dictionary object representing the versioned item """ - resource_path = 'abstractitems/' + str(item_id) + '/versions/' + str(version_num) + '/versioneditem' + resource_path = f'abstractitems/{item_id}/versions/{version_num}/versioneditem' try: response = self.__core.get(resource_path) except CoreException as err: @@ -753,14 +686,14 @@ def get_item_children(self, item_id, allowed_results_per_page=__allowed_results_ Returns: a List of Objects that represent the children of the item passed in. """ - resource_path = 'items/' + str(item_id) + '/children' + resource_path = f'items/{item_id}/children' child_items = self.__get_all(resource_path, allowed_results_per_page=allowed_results_per_page) return child_items def get_testruns(self, test_cycle_id, allowed_results_per_page=__allowed_results_per_page): """This method will return all test runs associated with the specified test cycle. Test runs will be returned as a list of json objects.""" - resource_path = 'testcycles/' + str(test_cycle_id) + '/testruns' + resource_path = f'testcycles/{test_cycle_id}/testruns' testrun_data = self.__get_all(resource_path, allowed_results_per_page=allowed_results_per_page) return testrun_data @@ -774,7 +707,7 @@ def get_items_upstream_relationships(self, item_id, allowed_results_per_page=__a Returns: an array of dictionary objects that represent the upstream relationships for the item. """ - resource_path = 'items/' + str(item_id) + '/upstreamrelationships' + resource_path = f'items/{item_id}/upstreamrelationships' return self.__get_all(resource_path, allowed_results_per_page=allowed_results_per_page) def get_items_downstream_related(self, item_id, allowed_results_per_page=__allowed_results_per_page): @@ -788,7 +721,7 @@ def get_items_downstream_related(self, item_id, allowed_results_per_page=__allow Returns: an array of dictionary objects that represent the downstream related items for the specified item. """ - resource_path = 'items/' + str(item_id) + '/downstreamrelated' + resource_path = f'items/{item_id}/downstreamrelated' return self.__get_all(resource_path, allowed_results_per_page=allowed_results_per_page) def get_items_downstream_relationships(self, item_id, allowed_results_per_page=__allowed_results_per_page): @@ -801,7 +734,7 @@ def get_items_downstream_relationships(self, item_id, allowed_results_per_page=_ Returns: an array of dictionary objects that represent the downstream relationships for the item. """ - resource_path = 'items/' + str(item_id) + '/downstreamrelationships' + resource_path = f'items/{item_id}/downstreamrelationships' return self.__get_all(resource_path, allowed_results_per_page=allowed_results_per_page) def get_items_upstream_related(self, item_id): @@ -814,7 +747,7 @@ def get_items_upstream_related(self, item_id): Returns: an array of dictionary objects that represent the upstream related items for the specified item. """ - resource_path = 'items/' + str(item_id) + '/upstreamrelated' + resource_path = f'items/{item_id}/upstreamrelated' return self.__get_all(resource_path) def get_item_workflow_transitions(self, item_id): @@ -828,7 +761,7 @@ def get_item_workflow_transitions(self, item_id): Returns: an array of dictionary objects that represent the workflow transitions for the item. """ - resource_path = 'items/' + str(item_id) + '/workflowtransitionoptions' + resource_path = f'items/{item_id}/workflowtransitionoptions' return self.__get_all(resource_path) def get_tags(self, project, allowed_results_per_page=__allowed_results_per_page): @@ -858,7 +791,7 @@ def get_tagged_items(self, tag_id, allowed_results_per_page=__allowed_results_pe A List of items that match the tag. """ - resource_path = 'tags/' + str(tag_id) + '/items' + resource_path = f'tags/{tag_id}/items' params = None tag_results = self.__get_all(resource_path, params=params, allowed_results_per_page=allowed_results_per_page) return tag_results @@ -887,7 +820,7 @@ def get_user(self, user_id): Returns: JSON obect """ - resource_path = 'users/' + str(user_id) + resource_path = f'users/{user_id}' try: response = self.__core.get(resource_path) except CoreException as err: @@ -920,7 +853,7 @@ def get_test_cycle(self, test_cycle_id): Returns: a dictionary object that represents the test cycle """ - resource_path = 'testcycles/' + str(test_cycle_id) + resource_path = f'testcycles/{test_cycle_id}' try: response = self.__core.get(resource_path) except CoreException as err: @@ -938,7 +871,27 @@ def delete_item(self, item_id): Returns: The success status code. """ - resource_path = 'items/' + str(item_id) + resource_path = f'items/{item_id}' + try: + response = self.__core.delete(resource_path) + except CoreException as err: + py_jama_rest_client_logger.error(err) + raise APIException(str(err)) + JamaClient.__handle_response_status(response) + return response.status_code + + + def delete_tag_from_item(self, item_id, tag_id): + """ + This method will delete a tag from an item in Jama Connect. + + Args: + item_id: The jama connect API ID of the item to be deleted + tag_id: The jama connect API ID of the tag to be deleted + + Returns: The success status code. + """ + resource_path = f'items/{item_id}/tags/{tag_id}' try: response = self.__core.delete(resource_path) except CoreException as err: @@ -947,7 +900,7 @@ def delete_item(self, item_id): JamaClient.__handle_response_status(response) return response.status_code - def delete_relationships(self, relationship_id): + def delete_relationship(self, relationship_id): """ Deletes a relationship with the specified relationship ID @@ -957,7 +910,7 @@ def delete_relationships(self, relationship_id): Returns: The success status code. """ - resource_path = 'relationships/' + str(relationship_id) + resource_path = f'relationships/{relationship_id}' try: response = self.__core.delete(resource_path) except CoreException as err: @@ -983,7 +936,7 @@ def patch_item(self, item_id, patches): Returns: The response status code """ - resource_path = 'items/' + str(item_id) + resource_path = f'items/{item_id}' headers = {'Content-Type': 'application/json', 'Accept': 'application/json' } @@ -1082,7 +1035,7 @@ def post_testplans_testcycles(self, testplan_id, testcycle_name, start_date, end Returns: (int): Returns the integer id for the newly created testcycle, or None if something went terribly wrong. """ - resource_path = 'testplans/' + str(testplan_id) + '/testcycles' + resource_path = f'testplans/{testplan_id}/testcycles' headers = {'content-type': 'application/json'} fields = { 'name': testcycle_name, @@ -1159,7 +1112,7 @@ def post_item_tag(self, item_id, tag_id): body = { "tag": tag_id } - resource_path = 'items/' + str(item_id) + '/tags' + resource_path = f'items/{item_id}/tags' headers = {'content-type': 'application/json'} try: response = self.__core.post(resource_path, data=json.dumps(body), headers=headers) @@ -1183,7 +1136,7 @@ def post_item_sync(self, source_item: int, pool_item: int): 'item': source_item } - resource_path = 'items/' + str(pool_item) + '/synceditems' + resource_path = f'items/{pool_item}/synceditems' headers = {'content-type': 'application/json'} try: response = self.__core.post(resource_path, data=json.dumps(body), headers=headers) @@ -1236,7 +1189,7 @@ def put_relationship(self, relationship_id: int, from_item: int, to_item: int, r } if relationship_type is not None: body['relationshipType'] = relationship_type - resource_path = 'relationships/{}'.format(relationship_id) + resource_path = f'relationships/{relationship_id}' headers = {'content-type': 'application/json'} try: response = self.__core.put(resource_path, data=json.dumps(body), headers=headers) @@ -1253,7 +1206,7 @@ def post_item_attachment(self, item_id, attachment_id): :return: 201 if successful / the response status of the post operation """ body = {"attachment": attachment_id} - resource_path = 'items/' + str(item_id) + '/attachments' + resource_path = f'items/{item_id}/attachments' headers = {'content-type': 'application/json'} try: response = self.__core.post(resource_path, data=json.dumps(body), headers=headers) @@ -1278,7 +1231,7 @@ def post_project_attachment(self, project_id, name, description): } } - resource_path = 'projects/' + str(project_id) + '/attachments' + resource_path = f'projects/{project_id}/attachments' headers = {'content-type': 'application/json'} try: response = self.__core.post(resource_path, data=json.dumps(body), headers=headers) @@ -1308,7 +1261,7 @@ def put_item(self, project, item_id, item_type_id, child_item_type_id, location, }, "fields": fields } - resource_path = 'items/' + str(item_id) + resource_path = f'items/{item_id}' headers = {'content-type': 'application/json'} try: response = self.__core.put(resource_path, data=json.dumps(body), headers=headers) @@ -1324,7 +1277,7 @@ def put_attachments_file(self, attachment_id, file_path): :param file_path: the file path of the file to be uploaded :return: returns the status code of the call """ - resource_path = 'attachments/' + str(attachment_id) + '/file' + resource_path = f'attachments/{attachment_id}/file' with open(file_path, 'rb') as f: files = {'file': f} try: @@ -1364,14 +1317,13 @@ def put_user(self, user_id, username, password, first_name, last_name, email, ph 'title': title, 'location': location } - resource_path = 'users/' + str(user_id) + resource_path = f'users/{user_id}' headers = {'content-type': 'application/json'} try: response = self.__core.put(resource_path, data=json.dumps(body), headers=headers) except CoreException as err: py_jama_rest_client_logger.error(err) raise APIException(str(err)) - raise APIException return self.__handle_response_status(response) def put_user_active(self, user_id, is_active): @@ -1387,7 +1339,7 @@ def put_user_active(self, user_id, is_active): body = { 'active': is_active } - resource_path = 'users/' + str(user_id) + '/active' + resource_path = f'users/{user_id}/active' headers = {'content-type': 'application/json'} try: response = self.__core.put(resource_path, data=json.dumps(body), headers=headers) @@ -1398,7 +1350,7 @@ def put_user_active(self, user_id, is_active): def put_test_run(self, test_run_id, data=None): """ This method will post a test run to Jama through the API""" - resource_path = 'testruns/' + str(test_run_id) + resource_path = f'testruns/{test_run_id}' headers = {'content-type': 'application/json'} try: response = self.__core.put(resource_path, data=data, headers=headers) @@ -1416,7 +1368,6 @@ def __get_all(self, resource, params=None, allowed_results_per_page=__allowed_re raise ValueError("Allowed results per page must be between 1 and 50") start_index = 0 - allowed_results_per_page = 20 total_results = float("inf") data = [] @@ -1433,18 +1384,65 @@ def __get_all(self, resource, params=None, allowed_results_per_page=__allowed_re return data + + def __get_all_last_id(self, resource, params=None, allowed_results_per_page=__allowed_results_per_page, **kwargs): + """This method will get all of the resources specified by the resource parameter, if an id or some other + parameter is required for the resource, include it in the params parameter. + Returns a single JSON array with all of the retrieved items.""" + + if allowed_results_per_page < 1 or allowed_results_per_page > 50: + raise ValueError("Allowed results per page must be between 1 and 50") + + total_results = float("inf") + + data = [] + last_id = 1 + start_at = 0 + while len(data) < total_results: + page_response = self.__get_page_last_id(resource, start_at, last_id, params=params, **kwargs) + page_json = page_response.json() + + page_info = page_json['meta']['pageInfo'] + total_results = page_info.get('totalResults') + page_data = page_json.get('data') + if len(page_data) > 0: + last_id = page_data[-1]['id'] + else: + break + + data.extend(page_data) + start_at = len(data) + + return data + def __get_page(self, resource, start_at, params=None, allowed_results_per_page=__allowed_results_per_page, **kwargs): """This method will return one page of results from the specified resource type. Pass any needed parameters along The response object will be returned""" parameters = { 'startAt': start_at, - 'maxResults': allowed_results_per_page + 'maxResults': allowed_results_per_page, + **(params or {}) } - if params is not None: - for k, v in params.items(): - parameters[k] = v + try: + response = self.__core.get(resource, params=parameters, **kwargs) + except CoreException as err: + py_jama_rest_client_logger.error(err) + raise APIException(str(err)) + JamaClient.__handle_response_status(response) + return response + + def __get_page_last_id(self, resource, start_at, last_id, params=None, allowed_results_per_page=__allowed_results_per_page, **kwargs): + """This method will return one page of results from the specified resource type. + Pass any needed parameters along + The response object will be returned""" + parameters = { + 'startAt': start_at, + 'lastId': last_id, + 'maxResults': allowed_results_per_page, + **(params or {}) + } try: response = self.__core.get(resource, params=parameters, **kwargs) @@ -1477,8 +1475,7 @@ def __handle_response_status(response): pass # Log the error - py_jama_rest_client_logger.error('API Client Error. Status: {} Message: {}'.format(status, - response_message)) + py_jama_rest_client_logger.error(f'API Client Error. Status: {status} Message: {response_message}') if response_message is not None and "already exists" in response_message: raise AlreadyExistsException("Entity already exists.", @@ -1486,8 +1483,8 @@ def __handle_response_status(response): reason=response_message) if status == 401: - raise UnauthorizedException("Unauthorized: check credentials and permissions. " - "API response message {}".format(response_message), + raise UnauthorizedException(f"Unauthorized: check credentials and permissions. " + f"API response message {response_message}", status_code=status, reason=response_message) @@ -1502,8 +1499,8 @@ def __handle_response_status(response): status_code=status, reason=response_message) - raise APIClientException("{} {} Client Error. Bad Request. " - "API response message: {}".format(status, response.reason, response_message), + raise APIClientException(f"{status} {response.reason} Client Error. Bad Request. " + f"API response message: {response_message}", status_code=status, reason=response_message) @@ -1511,14 +1508,14 @@ def __handle_response_status(response): """These are server errors and network errors.""" # Log The Error - py_jama_rest_client_logger.error('{} Server error. {}'.format(status, response.reason)) - raise APIServerException("{} Server Error.".format(status), + py_jama_rest_client_logger.error(f'{status} Server error. {response.reason}') + raise APIServerException(f"{status} Server Error.", status_code=status, reason=response.reason) # Catch anything unexpected - py_jama_rest_client_logger.error('{} error. {}'.format(status, response.reason)) - raise APIException("{} error".format(status), + py_jama_rest_client_logger.error(f'{status} error. {response.reason}') + raise APIException(f"{status} error", status_code=status, reason=response.reason)