{"action":"created","comment":{"url":"https://api.github.com/repos/ONSdigital/sdx-seft-consumer-service/pulls/comments/185007092","pull_request_review_id":116311979,"id":185007092,"diff_hunk":"@@ -0,0 +1,136 @@\n+import collections\n+import time\n+\n+import requests\n+from sdc.rabbit.exceptions import QuarantinableError, RetryableError, BadMessageError\n+\n+from app import create_and_wrap_logger\n+from app import settings\n+\n+logger = create_and_wrap_logger(__name__)\n+\n+AVResult = collections.namedtuple('AVResult', 'safe ready scan_results')\n+\n+\n+class AntiVirusCheck:\n+    def __init__(self, tx_id):\n+        self.bound_logger = logger.bind(tx_id=tx_id)\n+\n+    def send_for_av_scan(self, payload):\n+        self.bound_logger.info(\"Sending for AV check\", filename=payload.file_name)\n+        data_id = self._send_for_anti_virus_check(payload.file_name, payload.decoded_contents)\n+        self.bound_logger.info(\"Sent for A/V check\", data_id=data_id)\n+\n+        attempts = 0\n+        while attempts <= settings.ANTI_VIRUS_MAX_ATTEMPTS:\n+            attempts += 1\n+            results = self._get_anti_virus_result(data_id)\n+            if not results.ready:\n+                time.sleep(settings.ANTI_VIRUS_WAIT_TIME)\n+            elif not results.safe:\n+                error = \"Unsafe file detected for case id {} with filename {}\".format(payload.case_id,\n+                                                                                      payload.file_name)\n+                self._write_scan_report(results, payload.file_name)\n+                self.bound_logger.error(error)\n+                raise QuarantinableError()\n+            else:\n+                self.bound_logger.debug(\n+                    \"File {} for case id {} has been virus checked and confirmed safe\".format(payload.case_id,\n+                                                                                              payload.file_name))\n+                return True\n+\n+        # out of attempts raise retryable error to force the response back to the queue.\n+        self.bound_logger.error(\n+            \"Unable to get results of Anti-virus scan for case id {} and file {} \".format(payload.case_id,\n+                                                                                          payload.file_name))\n+        raise RetryableError()\n+\n+    def _add_api_key(self, headers):\n+        # the API key is only needed for the online cloud service\n+        if settings.ANTI_VIRUS_API_KEY:\n+            self.bound_logger.debug(\"Setting A/V API key\")\n+            headers['apikey'] = settings.ANTI_VIRUS_API_KEY\n+\n+    def _check_av_response(self, response):\n+        if response.status_code != 200:\n+            if response.status_code == 401:\n+                self.bound_logger.critical(\"Invalid OPSWAT API Key - unable to continue\")\n+                raise RetryableError()\n+            elif response.status_code == 404:\n+                # this could mean that the primary A/V server has failed and we've failed over to the backup\n+                # in this scenario we need to start over\n+                self.bound_logger.critical(\"OPSWAT AV does not know about this scan - the primary server may have failed\")\n+                raise RetryableError()\n+            elif 400 <= response.status_code < 500:\n+                raise BadMessageError()\n+            elif 300 < response.status_code >= 500:\n+                raise RetryableError()\n+        else:\n+            self.bound_logger.info(\"Response from AV OK\")\n+\n+    def _send_for_anti_virus_check(self, filename, contents):\n+        url = settings.ANTI_VIRUS_BASE_URL + \"file\"\n+        headers = {\n+            \"filename\": filename,\n+        }\n+        self._add_api_key(headers)\n+\n+        self.bound_logger.info(\"Sending for A/V scan\", url=url)\n+        response = requests.post(url=url, headers=headers, data=contents)\n+\n+        self._check_av_response(response)\n+\n+        self.bound_logger.info(\"Response received {}\".format(response.text))\n+        result = response.json()\n+\n+        if result.get(\"err\"):\n+            self.bound_logger.error(\"Unable to send file for anti virus scan: {}\".format(result.get(\"err\")))\n+            self.bound_logger.info(\"Waiting before attempting again\")\n+            time.sleep(settings.ANTI_VIRUS_WAIT_TIME)\n+            self.bound_logger.info(\"Return message to rabbit\")\n+            raise RetryableError()\n+        else:\n+            data_id = result.get(\"data_id\")\n+            self.bound_logger.info(\"File sent successfully for anti virus scan\", data_id=data_id)\n+            return data_id\n+\n+    def _get_anti_virus_result(self, data_id):\n+        url = settings.ANTI_VIRUS_BASE_URL + \"file/\" + data_id\n+        headers = {}\n+        self._add_api_key(headers)\n+\n+        self.bound_logger.info(\"Getting result for A/V scan\", url=url)\n+\n+        response = requests.get(url=url, headers=headers)\n+\n+        self._check_av_response(response)\n+\n+        self.bound_logger.info(\"Response from AV {}\".format(response.text))\n+\n+        result = response.json()","path":"app/anti_virus_check.py","position":110,"original_position":110,"commit_id":"a2be2d30a7af5f3d2e93a71e442d1e8bcd855ea3","original_commit_id":"a2be2d30a7af5f3d2e93a71e442d1e8bcd855ea3","user":{"login":"warrenbailey","id":14093825,"avatar_url":"https://avatars1.githubusercontent.com/u/14093825?v=4","gravatar_id":"","url":"https://api.github.com/users/warrenbailey","html_url":"https://github.com/warrenbailey","followers_url":"https://api.github.com/users/warrenbailey/followers","following_url":"https://api.github.com/users/warrenbailey/following{/other_user}","gists_url":"https://api.github.com/users/warrenbailey/gists{/gist_id}","starred_url":"https://api.github.com/users/warrenbailey/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/warrenbailey/subscriptions","organizations_url":"https://api.github.com/users/warrenbailey/orgs","repos_url":"https://api.github.com/users/warrenbailey/repos","events_url":"https://api.github.com/users/warrenbailey/events{/privacy}","received_events_url":"https://api.github.com/users/warrenbailey/received_events","type":"User","site_admin":false},"body":"Done","created_at":"2018-04-30T14:43:58Z","updated_at":"2018-04-30T14:43:58Z","html_url":"https://github.com/ONSdigital/sdx-seft-consumer-service/pull/46#discussion_r185007092","pull_request_url":"https://api.github.com/repos/ONSdigital/sdx-seft-consumer-service/pulls/46","author_association":"CONTRIBUTOR","_links":{"self":{"href":"https://api.github.com/repos/ONSdigital/sdx-seft-consumer-service/pulls/comments/185007092"},"html":{"href":"https://github.com/ONSdigital/sdx-seft-consumer-service/pull/46#discussion_r185007092"},"pull_request":{"href":"https://api.github.com/repos/ONSdigital/sdx-seft-consumer-service/pulls/46"}},"in_reply_to_id":184955462},"pull_request":{"url":"https://api.github.com/repos/ONSdigital/sdx-seft-consumer-service/pulls/46","id":183049615,"html_url":"https://github.com/ONSdigital/sdx-seft-consumer-service/pull/46","diff_url":"https://github.com/ONSdigital/sdx-seft-consumer-service/pull/46.diff","patch_url":"https://github.com/ONSdigital/sdx-seft-consumer-service/pull/46.patch","issue_url":"https://api.github.com/repos/ONSdigital/sdx-seft-consumer-service/issues/46","number":46,"state":"open","locked":false,"title":"Integrating OPSWAT","user":{"login":"warrenbailey","id":14093825,"avatar_url":"https://avatars1.githubusercontent.com/u/14093825?v=4","gravatar_id":"","url":"https://api.github.com/users/warrenbailey","html_url":"https://github.com/warrenbailey","followers_url":"https://api.github.com/users/warrenbailey/followers","following_url":"https://api.github.com/users/warrenbailey/following{/other_user}","gists_url":"https://api.github.com/users/warrenbailey/gists{/gist_id}","starred_url":"https://api.github.com/users/warrenbailey/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/warrenbailey/subscriptions","organizations_url":"https://api.github.com/users/warrenbailey/orgs","repos_url":"https://api.github.com/users/warrenbailey/repos","events_url":"https://api.github.com/users/warrenbailey/events{/privacy}","received_events_url":"https://api.github.com/users/warrenbailey/received_events","type":"User","site_admin":false},"body":"## What? and Why?\r\nThis PR integrates OPSWAT antivirus into the seft consumer pipeline. It works in a synchronous 1 by 1 manner. Once decrypted the file is sent to the OPSWAT server and then this process sits and waits for the results. If the file is deemed safe it is moved to the FTP server otherwise an error is raised, the AV report is written to disk and the file itself is quarantined.\r\n\r\nI've also done some refactoring to make the code easier to read and split up into logical modules. I've corrected and fixed the health check unit tests.  \r\n\r\n## Checklist\r\n  - [ ] README.md updated? \r\n  - [ ] CHANGELOG.md updated? ","created_at":"2018-04-20T12:37:16Z","updated_at":"2018-04-30T14:43:58Z","closed_at":null,"merged_at":null,"merge_commit_sha":"ea98852bcde846c24c4ac8b9afd31245dc324f94","assignee":null,"assignees":[],"requested_reviewers":[],"requested_teams":[],"labels":[{"id":908409436,"url":"https://api.github.com/repos/ONSdigital/sdx-seft-consumer-service/labels/WIP","name":"WIP","color":"0ecc44","default":false}],"milestone":null,"commits_url":"https://api.github.com/repos/ONSdigital/sdx-seft-consumer-service/pulls/46/commits","review_comments_url":"https://api.github.com/repos/ONSdigital/sdx-seft-consumer-service/pulls/46/comments","review_comment_url":"https://api.github.com/repos/ONSdigital/sdx-seft-consumer-service/pulls/comments{/number}","comments_url":"https://api.github.com/repos/ONSdigital/sdx-seft-consumer-service/issues/46/comments","statuses_url":"https://api.github.com/repos/ONSdigital/sdx-seft-consumer-service/statuses/a2be2d30a7af5f3d2e93a71e442d1e8bcd855ea3","head":{"label":"ONSdigital:integrate-opswat-av","ref":"integrate-opswat-av","sha":"a2be2d30a7af5f3d2e93a71e442d1e8bcd855ea3","user":{"login":"ONSdigital","id":6865137,"avatar_url":"https://avatars0.githubusercontent.com/u/6865137?v=4","gravatar_id":"","url":"https://api.github.com/users/ONSdigital","html_url":"https://github.com/ONSdigital","followers_url":"https://api.github.com/users/ONSdigital/followers","following_url":"https://api.github.com/users/ONSdigital/following{/other_user}","gists_url":"https://api.github.com/users/ONSdigital/gists{/gist_id}","starred_url":"https://api.github.com/users/ONSdigital/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/ONSdigital/subscriptions","organizations_url":"https://api.github.com/users/ONSdigital/orgs","repos_url":"https://api.github.com/users/ONSdigital/repos","events_url":"https://api.github.com/users/ONSdigital/events{/privacy}","received_events_url":"https://api.github.com/users/ONSdigital/received_events","type":"Organization","site_admin":false},"repo":{"id":92816474,"name":"sdx-seft-consumer-service","full_name":"ONSdigital/sdx-seft-consumer-service","owner":{"login":"ONSdigital","id":6865137,"avatar_url":"https://avatars0.githubusercontent.com/u/6865137?v=4","gravatar_id":"","url":"https://api.github.com/users/ONSdigital","html_url":"https://github.com/ONSdigital","followers_url":"https://api.github.com/users/ONSdigital/followers","following_url":"https://api.github.com/users/ONSdigital/following{/other_user}","gists_url":"https://api.github.com/users/ONSdigital/gists{/gist_id}","starred_url":"https://api.github.com/users/ONSdigital/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/ONSdigital/subscriptions","organizations_url":"https://api.github.com/users/ONSdigital/orgs","repos_url":"https://api.github.com/users/ONSdigital/repos","events_url":"https://api.github.com/users/ONSdigital/events{/privacy}","received_events_url":"https://api.github.com/users/ONSdigital/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/ONSdigital/sdx-seft-consumer-service","description":"Microservice for consuming SEFT files from RAS and sending to ONS internal ","fork":false,"url":"https://api.github.com/repos/ONSdigital/sdx-seft-consumer-service","forks_url":"https://api.github.com/repos/ONSdigital/sdx-seft-consumer-service/forks","keys_url":"https://api.github.com/repos/ONSdigital/sdx-seft-consumer-service/keys{/key_id}","collaborators_url":"https://api.github.com/repos/ONSdigital/sdx-seft-consumer-service/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/ONSdigital/sdx-seft-consumer-service/teams","hooks_url":"https://api.github.com/repos/ONSdigital/sdx-seft-consumer-service/hooks","issue_events_url":"https://api.github.com/repos/ONSdigital/sdx-seft-consumer-service/issues/events{/number}","events_url":"https://api.github.com/repos/ONSdigital/sdx-seft-consumer-service/events","assignees_url":"https://api.github.com/repos/ONSdigital/sdx-seft-consumer-service/assignees{/user}","branches_url":"https://api.github.com/repos/ONSdigital/sdx-seft-consumer-service/branches{/branch}","tags_url":"https://api.github.com/repos/ONSdigital/sdx-seft-consumer-service/tags","blobs_url":"https://api.github.com/repos/ONSdigital/sdx-seft-consumer-service/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/ONSdigital/sdx-seft-consumer-service/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/ONSdigital/sdx-seft-consumer-service/git/refs{/sha}","trees_url":"https://api.github.com/repos/ONSdigital/sdx-seft-consumer-service/git/trees{/sha}","statuses_url":"https://api.github.com/repos/ONSdigital/sdx-seft-consumer-service/statuses/{sha}","languages_url":"https://api.github.com/repos/ONSdigital/sdx-seft-consumer-service/languages","stargazers_url":"https://api.github.com/repos/ONSdigital/sdx-seft-consumer-service/stargazers","contributors_url":"https://api.github.com/repos/ONSdigital/sdx-seft-consumer-service/contributors","subscribers_url":"https://api.github.com/repos/ONSdigital/sdx-seft-consumer-service/subscribers","subscription_url":"https://api.github.com/repos/ONSdigital/sdx-seft-consumer-service/subscription","commits_url":"https://api.github.com/repos/ONSdigital/sdx-seft-consumer-service/commits{/sha}","git_commits_url":"https://api.github.com/repos/ONSdigital/sdx-seft-consumer-service/git/commits{/sha}","comments_url":"https://api.github.com/repos/ONSdigital/sdx-seft-consumer-service/comments{/number}","issue_comment_url":"https://api.github.com/repos/ONSdigital/sdx-seft-consumer-service/issues/comments{/number}","contents_url":"https://api.github.com/repos/ONSdigital/sdx-seft-consumer-service/contents/{+path}","compare_url":"https://api.github.com/repos/ONSdigital/sdx-seft-consumer-service/compare/{base}...{head}","merges_url":"https://api.github.com/repos/ONSdigital/sdx-seft-consumer-service/merges","archive_url":"https://api.github.com/repos/ONSdigital/sdx-seft-consumer-service/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/ONSdigital/sdx-seft-consumer-service/downloads","issues_url":"https://api.github.com/repos/ONSdigital/sdx-seft-consumer-service/issues{/number}","pulls_url":"https://api.github.com/repos/ONSdigital/sdx-seft-consumer-service/pulls{/number}","milestones_url":"https://api.github.com/repos/ONSdigital/sdx-seft-consumer-service/milestones{/number}","notifications_url":"https://api.github.com/repos/ONSdigital/sdx-seft-consumer-service/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/ONSdigital/sdx-seft-consumer-service/labels{/name}","releases_url":"https://api.github.com/repos/ONSdigital/sdx-seft-consumer-service/releases{/id}","deployments_url":"https://api.github.com/repos/ONSdigital/sdx-seft-consumer-service/deployments","created_at":"2017-05-30T09:19:39Z","updated_at":"2018-03-06T12:35:30Z","pushed_at":"2018-04-30T10:13:19Z","git_url":"git://github.com/ONSdigital/sdx-seft-consumer-service.git","ssh_url":"git@github.com:ONSdigital/sdx-seft-consumer-service.git","clone_url":"https://github.com/ONSdigital/sdx-seft-consumer-service.git","svn_url":"https://github.com/ONSdigital/sdx-seft-consumer-service","homepage":null,"size":527,"stargazers_count":1,"watchers_count":1,"language":"Python","has_issues":true,"has_projects":false,"has_downloads":true,"has_wiki":false,"has_pages":false,"forks_count":0,"mirror_url":null,"archived":false,"open_issues_count":2,"license":{"key":"mit","name":"MIT License","spdx_id":"MIT","url":"https://api.github.com/licenses/mit"},"forks":0,"open_issues":2,"watchers":1,"default_branch":"master"}},"base":{"label":"ONSdigital:master","ref":"master","sha":"46aa6390830716f27e95ba94bde030652e7fcf4e","user":{"login":"ONSdigital","id":6865137,"avatar_url":"https://avatars0.githubusercontent.com/u/6865137?v=4","gravatar_id":"","url":"https://api.github.com/users/ONSdigital","html_url":"https://github.com/ONSdigital","followers_url":"https://api.github.com/users/ONSdigital/followers","following_url":"https://api.github.com/users/ONSdigital/following{/other_user}","gists_url":"https://api.github.com/users/ONSdigital/gists{/gist_id}","starred_url":"https://api.github.com/users/ONSdigital/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/ONSdigital/subscriptions","organizations_url":"https://api.github.com/users/ONSdigital/orgs","repos_url":"https://api.github.com/users/ONSdigital/repos","events_url":"https://api.github.com/users/ONSdigital/events{/privacy}","received_events_url":"https://api.github.com/users/ONSdigital/received_events","type":"Organization","site_admin":false},"repo":{"id":92816474,"name":"sdx-seft-consumer-service","full_name":"ONSdigital/sdx-seft-consumer-service","owner":{"login":"ONSdigital","id":6865137,"avatar_url":"https://avatars0.githubusercontent.com/u/6865137?v=4","gravatar_id":"","url":"https://api.github.com/users/ONSdigital","html_url":"https://github.com/ONSdigital","followers_url":"https://api.github.com/users/ONSdigital/followers","following_url":"https://api.github.com/users/ONSdigital/following{/other_user}","gists_url":"https://api.github.com/users/ONSdigital/gists{/gist_id}","starred_url":"https://api.github.com/users/ONSdigital/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/ONSdigital/subscriptions","organizations_url":"https://api.github.com/users/ONSdigital/orgs","repos_url":"https://api.github.com/users/ONSdigital/repos","events_url":"https://api.github.com/users/ONSdigital/events{/privacy}","received_events_url":"https://api.github.com/users/ONSdigital/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/ONSdigital/sdx-seft-consumer-service","description":"Microservice for consuming SEFT files from RAS and sending to ONS internal ","fork":false,"url":"https://api.github.com/repos/ONSdigital/sdx-seft-consumer-service","forks_url":"https://api.github.com/repos/ONSdigital/sdx-seft-consumer-service/forks","keys_url":"https://api.github.com/repos/ONSdigital/sdx-seft-consumer-service/keys{/key_id}","collaborators_url":"https://api.github.com/repos/ONSdigital/sdx-seft-consumer-service/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/ONSdigital/sdx-seft-consumer-service/teams","hooks_url":"https://api.github.com/repos/ONSdigital/sdx-seft-consumer-service/hooks","issue_events_url":"https://api.github.com/repos/ONSdigital/sdx-seft-consumer-service/issues/events{/number}","events_url":"https://api.github.com/repos/ONSdigital/sdx-seft-consumer-service/events","assignees_url":"https://api.github.com/repos/ONSdigital/sdx-seft-consumer-service/assignees{/user}","branches_url":"https://api.github.com/repos/ONSdigital/sdx-seft-consumer-service/branches{/branch}","tags_url":"https://api.github.com/repos/ONSdigital/sdx-seft-consumer-service/tags","blobs_url":"https://api.github.com/repos/ONSdigital/sdx-seft-consumer-service/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/ONSdigital/sdx-seft-consumer-service/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/ONSdigital/sdx-seft-consumer-service/git/refs{/sha}","trees_url":"https://api.github.com/repos/ONSdigital/sdx-seft-consumer-service/git/trees{/sha}","statuses_url":"https://api.github.com/repos/ONSdigital/sdx-seft-consumer-service/statuses/{sha}","languages_url":"https://api.github.com/repos/ONSdigital/sdx-seft-consumer-service/languages","stargazers_url":"https://api.github.com/repos/ONSdigital/sdx-seft-consumer-service/stargazers","contributors_url":"https://api.github.com/repos/ONSdigital/sdx-seft-consumer-service/contributors","subscribers_url":"https://api.github.com/repos/ONSdigital/sdx-seft-consumer-service/subscribers","subscription_url":"https://api.github.com/repos/ONSdigital/sdx-seft-consumer-service/subscription","commits_url":"https://api.github.com/repos/ONSdigital/sdx-seft-consumer-service/commits{/sha}","git_commits_url":"https://api.github.com/repos/ONSdigital/sdx-seft-consumer-service/git/commits{/sha}","comments_url":"https://api.github.com/repos/ONSdigital/sdx-seft-consumer-service/comments{/number}","issue_comment_url":"https://api.github.com/repos/ONSdigital/sdx-seft-consumer-service/issues/comments{/number}","contents_url":"https://api.github.com/repos/ONSdigital/sdx-seft-consumer-service/contents/{+path}","compare_url":"https://api.github.com/repos/ONSdigital/sdx-seft-consumer-service/compare/{base}...{head}","merges_url":"https://api.github.com/repos/ONSdigital/sdx-seft-consumer-service/merges","archive_url":"https://api.github.com/repos/ONSdigital/sdx-seft-consumer-service/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/ONSdigital/sdx-seft-consumer-service/downloads","issues_url":"https://api.github.com/repos/ONSdigital/sdx-seft-consumer-service/issues{/number}","pulls_url":"https://api.github.com/repos/ONSdigital/sdx-seft-consumer-service/pulls{/number}","milestones_url":"https://api.github.com/repos/ONSdigital/sdx-seft-consumer-service/milestones{/number}","notifications_url":"https://api.github.com/repos/ONSdigital/sdx-seft-consumer-service/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/ONSdigital/sdx-seft-consumer-service/labels{/name}","releases_url":"https://api.github.com/repos/ONSdigital/sdx-seft-consumer-service/releases{/id}","deployments_url":"https://api.github.com/repos/ONSdigital/sdx-seft-consumer-service/deployments","created_at":"2017-05-30T09:19:39Z","updated_at":"2018-03-06T12:35:30Z","pushed_at":"2018-04-30T10:13:19Z","git_url":"git://github.com/ONSdigital/sdx-seft-consumer-service.git","ssh_url":"git@github.com:ONSdigital/sdx-seft-consumer-service.git","clone_url":"https://github.com/ONSdigital/sdx-seft-consumer-service.git","svn_url":"https://github.com/ONSdigital/sdx-seft-consumer-service","homepage":null,"size":527,"stargazers_count":1,"watchers_count":1,"language":"Python","has_issues":true,"has_projects":false,"has_downloads":true,"has_wiki":false,"has_pages":false,"forks_count":0,"mirror_url":null,"archived":false,"open_issues_count":2,"license":{"key":"mit","name":"MIT License","spdx_id":"MIT","url":"https://api.github.com/licenses/mit"},"forks":0,"open_issues":2,"watchers":1,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/ONSdigital/sdx-seft-consumer-service/pulls/46"},"html":{"href":"https://github.com/ONSdigital/sdx-seft-consumer-service/pull/46"},"issue":{"href":"https://api.github.com/repos/ONSdigital/sdx-seft-consumer-service/issues/46"},"comments":{"href":"https://api.github.com/repos/ONSdigital/sdx-seft-consumer-service/issues/46/comments"},"review_comments":{"href":"https://api.github.com/repos/ONSdigital/sdx-seft-consumer-service/pulls/46/comments"},"review_comment":{"href":"https://api.github.com/repos/ONSdigital/sdx-seft-consumer-service/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/ONSdigital/sdx-seft-consumer-service/pulls/46/commits"},"statuses":{"href":"https://api.github.com/repos/ONSdigital/sdx-seft-consumer-service/statuses/a2be2d30a7af5f3d2e93a71e442d1e8bcd855ea3"}},"author_association":"CONTRIBUTOR"}}
{"actor":{"display_login":"warrenbailey"}}