From df55b000112aa38a9a92abde24f7652baa2931d1 Mon Sep 17 00:00:00 2001 From: jjinno Date: Wed, 8 May 2019 10:38:45 -0700 Subject: [PATCH 1/9] Update Collection.count() to v2 compatible The v2 REST API no longer officially supports the "/count" path. Although empirical evidence would suggest it still exists for "/incidents/count", there is no documentation supporting this, and plenty of support conversations suggesting otherwise. This is especially true for sub-container elements such as Incident.log_entries. This update converts all "count" calls to use the documented "total=true" parameter on a single paginated request. The use of "limit=1" has been explicitly avoided, as this seems to cause HTTP errors with certain incidents. --- pygerduty/v2.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/pygerduty/v2.py b/pygerduty/v2.py index 22703ec..d15a68f 100755 --- a/pygerduty/v2.py +++ b/pygerduty/v2.py @@ -175,7 +175,12 @@ def list(self, **kwargs): break def count(self, **kwargs): - path = "{0}/count".format(self.name) + path = "{0}".format(self.name) + kwargs["total"] = "true" + if self.base_container: + path = "{0}/{1}/{2}".format( + self.base_container.collection.name, + self.base_container.id, self.name) response = self.pagerduty.request("GET", path, query_params=kwargs) return response.get("total", None) From 8cabbb3038679700c459ad729171b87800921515 Mon Sep 17 00:00:00 2001 From: jjinno Date: Thu, 9 May 2019 17:38:40 -0700 Subject: [PATCH 2/9] add backoff/retry logic Partially ported from __init__.py, with the addition of now being exponential (instead of additive), and also now including some randomness (milliseconds) to the sleep. This logic is specifically targeted toward URLError (url timeouts, etc), and HTTPError (429: too many requests). --- pygerduty/common.py | 24 ++++++++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/pygerduty/common.py b/pygerduty/common.py index ae27a36..4f6b298 100644 --- a/pygerduty/common.py +++ b/pygerduty/common.py @@ -5,15 +5,19 @@ from .exceptions import Error, IntegrationAPIError, BadRequest, NotFound from six import string_types from six.moves import urllib +from random import randint +from time import sleep ISO8601_FORMAT = "%Y-%m-%dT%H:%M:%SZ" class Requester(object): - def __init__(self, timeout=10, proxies=None, parse_datetime=False): + def __init__(self, timeout=10, proxies=None, parse_datetime=False, min_backoff=15.0, max_retries=5): self.timeout = timeout - self.json_loader = json.loads + self.min_backoff = min_backoff + self.max_retries = max_retries + self.json_loader = json.loads if parse_datetime: self.json_loader = _json_loader @@ -22,11 +26,20 @@ def __init__(self, timeout=10, proxies=None, parse_datetime=False): handlers.append(urllib.request.ProxyHandler(proxies)) self.opener = urllib.request.build_opener(*handlers) - def execute_request(self, request): + def execute_request(self, request, retry_count=0): + if retry_count > 0: + exponential_backoff = self.min_backoff * (2 ** retry_count) + randomness = randint(0,1000) / 1000.0 + sleep(exponential_backoff + randomness) try: response = (self.opener.open(request, timeout=self.timeout). read().decode("utf-8")) + except urllib.error.URLError as err: + if retry_count < self.max_retries: + return self.execute_request(request, retry_count + 1) + else: + raise except urllib.error.HTTPError as err: if err.code / 100 == 2: response = err.read().decode("utf-8") @@ -38,7 +51,10 @@ def execute_request(self, request): raise NotFound("URL ({0}) Not Found.".format( request.get_full_url())) elif err.code == 429: - raise + if retry_count < self.max_retries: + return self.execute_request(request, retry_count + 1) + else: + raise else: raise From f71a14cd5d5b6ba056e4719e4d7b59cad7fa7bf1 Mon Sep 17 00:00:00 2001 From: jjinno Date: Thu, 9 May 2019 18:22:41 -0700 Subject: [PATCH 3/9] fixed flake8 comma complaints --- pygerduty/common.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pygerduty/common.py b/pygerduty/common.py index 4f6b298..eb5f3de 100644 --- a/pygerduty/common.py +++ b/pygerduty/common.py @@ -29,7 +29,7 @@ def __init__(self, timeout=10, proxies=None, parse_datetime=False, min_backoff=1 def execute_request(self, request, retry_count=0): if retry_count > 0: exponential_backoff = self.min_backoff * (2 ** retry_count) - randomness = randint(0,1000) / 1000.0 + randomness = randint(0, 1000) / 1000.0 sleep(exponential_backoff + randomness) try: From ef33ced95ea70689d6dd8b6c7773dd16c585d593 Mon Sep 17 00:00:00 2001 From: jjinno Date: Thu, 9 May 2019 18:28:23 -0700 Subject: [PATCH 4/9] fixed flake8 unused variable complaint --- pygerduty/common.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pygerduty/common.py b/pygerduty/common.py index eb5f3de..4b9142e 100644 --- a/pygerduty/common.py +++ b/pygerduty/common.py @@ -35,7 +35,7 @@ def execute_request(self, request, retry_count=0): try: response = (self.opener.open(request, timeout=self.timeout). read().decode("utf-8")) - except urllib.error.URLError as err: + except urllib.error.URLError: if retry_count < self.max_retries: return self.execute_request(request, retry_count + 1) else: From 936a5b1f7135e8689c971fbab4548a114dfb69d6 Mon Sep 17 00:00:00 2001 From: jjinno Date: Thu, 9 May 2019 20:00:16 -0700 Subject: [PATCH 5/9] fixed URLError Exception hierarchy --- pygerduty/common.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/pygerduty/common.py b/pygerduty/common.py index 4b9142e..b3eac1c 100644 --- a/pygerduty/common.py +++ b/pygerduty/common.py @@ -35,11 +35,6 @@ def execute_request(self, request, retry_count=0): try: response = (self.opener.open(request, timeout=self.timeout). read().decode("utf-8")) - except urllib.error.URLError: - if retry_count < self.max_retries: - return self.execute_request(request, retry_count + 1) - else: - raise except urllib.error.HTTPError as err: if err.code / 100 == 2: response = err.read().decode("utf-8") @@ -57,6 +52,11 @@ def execute_request(self, request, retry_count=0): raise else: raise + except urllib.error.URLError: + if retry_count < self.max_retries: + return self.execute_request(request, retry_count + 1) + else: + raise try: response = self.json_loader(response) From 8e70bb3a19ecdfd653fd23a492bb770f4544f7ca Mon Sep 17 00:00:00 2001 From: jjinno Date: Thu, 9 May 2019 22:11:01 -0700 Subject: [PATCH 6/9] JSON for testing Incident.log_entries --- tests/fixtures/incident_log_entries.json | 592 +++++++++++++++++++++++ 1 file changed, 592 insertions(+) create mode 100644 tests/fixtures/incident_log_entries.json diff --git a/tests/fixtures/incident_log_entries.json b/tests/fixtures/incident_log_entries.json new file mode 100644 index 0000000..c61e7a9 --- /dev/null +++ b/tests/fixtures/incident_log_entries.json @@ -0,0 +1,592 @@ +{ + "log_entries": [ + { + "service": { + "self": "https://api.pagerduty.com/services/P0K4MD5", + "summary": "Test Service", + "type": "service_reference", + "id": "P0K4MD5", + "html_url": "https://subdomain.pagerduty.com/services/P0K4MD5" + }, + "contexts": [], + "created_at": "2019-01-01T00:57:11Z", + "html_url": null, + "agent": { + "self": "https://api.pagerduty.com/services/P0K4MD5/integrations/PM4L3BM", + "summary": "Test-Service-API-v2", + "type": "events_api_v2_inbound_integration_reference", + "id": "PM4L3BM", + "html_url": "https://subdomain.pagerduty.com/services/P0K4MD5/integrations/PM4L3BM" + }, + "teams": [ + { + "self": "https://api.pagerduty.com/teams/PL09PSG", + "summary": "Team 123", + "type": "team_reference", + "id": "PL09PSG", + "html_url": "https://subdomain.pagerduty.com/teams/PL09PSG" + } + ], + "self": "https://api.pagerduty.com/log_entries/RP2836ITHU9F3ADA1T9MF3F97O", + "incident": { + "self": "https://api.pagerduty.com/incidents/PT4KHLK", + "summary": "[#1234567] The server is on fire.", + "type": "incident_reference", + "id": "PT4KHLK", + "html_url": "https://subdomain.pagerduty.com/incidents/PT4KHLK" + }, + "id": "RP2836ITHU9F3ADA1T9MF3F97O", + "summary": "Resolved through the API", + "type": "resolve_log_entry", + "event_details": {}, + "channel": { + "type": "integration" + } + }, + { + "service": { + "self": "https://api.pagerduty.com/services/P0K4MD5", + "summary": "Test Service", + "type": "service_reference", + "id": "P0K4MD5", + "html_url": "https://subdomain.pagerduty.com/services/P0K4MD5" + }, + "contexts": [], + "created_at": "2019-01-01T00:30:01Z", + "html_url": null, + "teams": [ + { + "self": "https://api.pagerduty.com/teams/PL09PSG", + "summary": "Team 123", + "type": "team_reference", + "id": "PL09PSG", + "html_url": "https://subdomain.pagerduty.com/teams/PL09PSG" + } + ], + "self": "https://api.pagerduty.com/log_entries/R2PDIDKABLHDOXPXVG6YQPDJUD", + "incident": { + "self": "https://api.pagerduty.com/incidents/PT4KHLK", + "summary": "[#1234567] The server is on fire.", + "type": "incident_reference", + "id": "PT4KHLK", + "html_url": "https://subdomain.pagerduty.com/incidents/PT4KHLK" + }, + "user": { + "self": "https://api.pagerduty.com/users/PT3D7UU", + "summary": "FakeUser", + "type": "user_reference", + "id": "PT3D7UU", + "html_url": "https://subdomain.pagerduty.com/users/PT3D7UU" + }, + "summary": "Notified FakeUser by email", + "type": "notify_log_entry", + "id": "R2PDIDKABLHDOXPXVG6YQPDJUD", + "channel": { + "notification": { + "status": "bad_address", + "conferenceAddress": null, + "type": "email", + "address": "FakeUser@subdomain.com" + }, + "type": "auto" + } + }, + { + "service": { + "self": "https://api.pagerduty.com/services/P0K4MD5", + "summary": "Test Service", + "type": "service_reference", + "id": "P0K4MD5", + "html_url": "https://subdomain.pagerduty.com/services/P0K4MD5" + }, + "contexts": [], + "created_at": "2019-01-01T00:17:26Z", + "html_url": null, + "agent": { + "self": "https://api.pagerduty.com/users/P2BXFK0", + "summary": "FakeUser2", + "type": "user_reference", + "id": "P2BXFK0", + "html_url": "https://subdomain.pagerduty.com/users/P2BXFK0" + }, + "teams": [ + { + "self": "https://api.pagerduty.com/teams/PL09PSG", + "summary": "Team 123", + "type": "team_reference", + "id": "PL09PSG", + "html_url": "https://subdomain.pagerduty.com/teams/PL09PSG" + } + ], + "self": "https://api.pagerduty.com/log_entries/RQ84W5G7Y0VQ2JFN5U85PFUGKK", + "incident": { + "self": "https://api.pagerduty.com/incidents/PT4KHLK", + "summary": "[#1234567] The server is on fire.", + "type": "incident_reference", + "id": "PT4KHLK", + "html_url": "https://subdomain.pagerduty.com/incidents/PT4KHLK" + }, + "summary": "Snoozed by FakeUser2 for 8 hrs 0 min", + "type": "snooze_log_entry", + "id": "RQ84W5G7Y0VQ2JFN5U85PFUGKK", + "channel": { + "duration": 28800, + "type": "website" + }, + "changed_actions": [ + { + "type": "unacknowledge", + "at": "2019-01-01T08:17:26Z" + } + ] + }, + { + "service": { + "self": "https://api.pagerduty.com/services/P0K4MD5", + "summary": "Test Service", + "type": "service_reference", + "id": "P0K4MD5", + "html_url": "https://subdomain.pagerduty.com/services/P0K4MD5" + }, + "contexts": [], + "created_at": "2019-01-01T00:10:49Z", + "teams": [ + { + "self": "https://api.pagerduty.com/teams/PL09PSG", + "summary": "Team 123", + "type": "team_reference", + "id": "PL09PSG", + "html_url": "https://subdomain.pagerduty.com/teams/PL09PSG" + } + ], + "html_url": null, + "agent": { + "self": "https://api.pagerduty.com/users/P2BXFK0", + "summary": "FakeUser2", + "type": "user_reference", + "id": "P2BXFK0", + "html_url": "https://subdomain.pagerduty.com/users/P2BXFK0" + }, + "acknowledgement_timeout": 7200, + "self": "https://api.pagerduty.com/log_entries/R01BG1J3L9NLRCC69YS7BNFNBB", + "incident": { + "self": "https://api.pagerduty.com/incidents/PT4KHLK", + "summary": "[#1234567] The server is on fire.", + "type": "incident_reference", + "id": "PT4KHLK", + "html_url": "https://subdomain.pagerduty.com/incidents/PT4KHLK" + }, + "id": "R01BG1J3L9NLRCC69YS7BNFNBB", + "summary": "Acknowledged by FakeUser2", + "type": "acknowledge_log_entry", + "event_details": {}, + "channel": { + "type": "website" + } + }, + { + "service": { + "self": "https://api.pagerduty.com/services/P0K4MD5", + "summary": "Test Service", + "type": "service_reference", + "id": "P0K4MD5", + "html_url": "https://subdomain.pagerduty.com/services/P0K4MD5" + }, + "contexts": [], + "created_at": "2019-01-01T00:08:43Z", + "html_url": null, + "teams": [ + { + "self": "https://api.pagerduty.com/teams/PL09PSG", + "summary": "Team 123", + "type": "team_reference", + "id": "PL09PSG", + "html_url": "https://subdomain.pagerduty.com/teams/PL09PSG" + } + ], + "self": "https://api.pagerduty.com/log_entries/R09XFP122Z3S4J8B9S8DWELVYZ", + "incident": { + "self": "https://api.pagerduty.com/incidents/PT4KHLK", + "summary": "[#1234567] The server is on fire.", + "type": "incident_reference", + "id": "PT4KHLK", + "html_url": "https://subdomain.pagerduty.com/incidents/PT4KHLK" + }, + "user": { + "self": "https://api.pagerduty.com/users/P2BXFK0", + "summary": "FakeUser2", + "type": "user_reference", + "id": "P2BXFK0", + "html_url": "https://subdomain.pagerduty.com/users/P2BXFK0" + }, + "summary": "Notified FakeUser2 by push notification", + "type": "notify_log_entry", + "id": "R09XFP122Z3S4J8B9S8DWELVYZ", + "channel": { + "notification": { + "status": "success", + "conferenceAddress": null, + "type": "android_push_notification", + "address": "SM-N9005" + }, + "type": "auto" + } + }, + { + "service": { + "self": "https://api.pagerduty.com/services/P0K4MD5", + "summary": "Test Service", + "type": "service_reference", + "id": "P0K4MD5", + "html_url": "https://subdomain.pagerduty.com/services/P0K4MD5" + }, + "contexts": [], + "created_at": "2019-01-01T00:08:34Z", + "html_url": null, + "teams": [ + { + "self": "https://api.pagerduty.com/teams/PL09PSG", + "summary": "Team 123", + "type": "team_reference", + "id": "PL09PSG", + "html_url": "https://subdomain.pagerduty.com/teams/PL09PSG" + } + ], + "self": "https://api.pagerduty.com/log_entries/RNJ5P6LTMD7GBKQPTWSFUT7094", + "incident": { + "self": "https://api.pagerduty.com/incidents/PT4KHLK", + "summary": "[#1234567] The server is on fire.", + "type": "incident_reference", + "id": "PT4KHLK", + "html_url": "https://subdomain.pagerduty.com/incidents/PT4KHLK" + }, + "user": { + "self": "https://api.pagerduty.com/users/P2BXFK0", + "summary": "FakeUser2", + "type": "user_reference", + "id": "P2BXFK0", + "html_url": "https://subdomain.pagerduty.com/users/P2BXFK0" + }, + "summary": "Notified FakeUser2 by push notification", + "type": "notify_log_entry", + "id": "RNJ5P6LTMD7GBKQPTWSFUT7094", + "channel": { + "notification": { + "status": "success", + "conferenceAddress": null, + "type": "android_push_notification", + "address": "MI 5" + }, + "type": "auto" + } + }, + { + "service": { + "self": "https://api.pagerduty.com/services/P0K4MD5", + "summary": "Test Service", + "type": "service_reference", + "id": "P0K4MD5", + "html_url": "https://subdomain.pagerduty.com/services/P0K4MD5" + }, + "contexts": [], + "created_at": "2019-01-01T00:08:24Z", + "html_url": null, + "teams": [ + { + "self": "https://api.pagerduty.com/teams/PL09PSG", + "summary": "Team 123", + "type": "team_reference", + "id": "PL09PSG", + "html_url": "https://subdomain.pagerduty.com/teams/PL09PSG" + } + ], + "self": "https://api.pagerduty.com/log_entries/RP0FXADPZIDGOCSKC76A8F2KXS", + "incident": { + "self": "https://api.pagerduty.com/incidents/PT4KHLK", + "summary": "[#1234567] The server is on fire.", + "type": "incident_reference", + "id": "PT4KHLK", + "html_url": "https://subdomain.pagerduty.com/incidents/PT4KHLK" + }, + "user": { + "self": "https://api.pagerduty.com/users/P2BXFK0", + "summary": "FakeUser2", + "type": "user_reference", + "id": "P2BXFK0", + "html_url": "https://subdomain.pagerduty.com/users/P2BXFK0" + }, + "summary": "Notified FakeUser2 by push notification", + "type": "notify_log_entry", + "id": "RP0FXADPZIDGOCSKC76A8F2KXS", + "channel": { + "notification": { + "status": "success", + "conferenceAddress": null, + "type": "android_push_notification", + "address": "SM-A520F" + }, + "type": "auto" + } + }, + { + "service": { + "self": "https://api.pagerduty.com/services/P0K4MD5", + "summary": "Test Service", + "type": "service_reference", + "id": "P0K4MD5", + "html_url": "https://subdomain.pagerduty.com/services/P0K4MD5" + }, + "contexts": [], + "created_at": "2019-01-01T00:08:12Z", + "html_url": null, + "agent": { + "self": "https://api.pagerduty.com/services/P0K4MD5", + "summary": "Test Service", + "type": "service_reference", + "id": "P0K4MD5", + "html_url": "https://subdomain.pagerduty.com/services/P0K4MD5" + }, + "teams": [ + { + "self": "https://api.pagerduty.com/teams/PL09PSG", + "summary": "Team 123", + "type": "team_reference", + "id": "PL09PSG", + "html_url": "https://subdomain.pagerduty.com/teams/PL09PSG" + } + ], + "self": "https://api.pagerduty.com/log_entries/R23A105I7I0HS4BUWWTEI79OV4", + "assignees": [ + { + "self": "https://api.pagerduty.com/users/P2BXFK0", + "summary": "FakeUser2", + "type": "user_reference", + "id": "P2BXFK0", + "html_url": "https://subdomain.pagerduty.com/users/P2BXFK0" + } + ], + "incident": { + "self": "https://api.pagerduty.com/incidents/PT4KHLK", + "summary": "[#1234567] The server is on fire.", + "type": "incident_reference", + "id": "PT4KHLK", + "html_url": "https://subdomain.pagerduty.com/incidents/PT4KHLK" + }, + "summary": "Escalated to FakeUser2 through the API", + "type": "escalate_log_entry", + "id": "R23A105I7I0HS4BUWWTEI79OV4", + "channel": { + "type": "timeout" + } + }, + { + "service": { + "self": "https://api.pagerduty.com/services/P0K4MD5", + "summary": "Test Service", + "type": "service_reference", + "id": "P0K4MD5", + "html_url": "https://subdomain.pagerduty.com/services/P0K4MD5" + }, + "contexts": [], + "created_at": "2019-01-01T00:03:12Z", + "html_url": null, + "agent": { + "self": "https://api.pagerduty.com/services/P0K4MD5/integrations/PM4L3BM", + "summary": "Test-Service-API-v2", + "type": "events_api_v2_inbound_integration_reference", + "id": "PM4L3BM", + "html_url": "https://subdomain.pagerduty.com/services/P0K4MD5/integrations/PM4L3BM" + }, + "teams": [ + { + "self": "https://api.pagerduty.com/teams/PL09PSG", + "summary": "Team 123", + "type": "team_reference", + "id": "PL09PSG", + "html_url": "https://subdomain.pagerduty.com/teams/PL09PSG" + } + ], + "self": "https://api.pagerduty.com/log_entries/R1LRHYM5AS4Z5CM1SPMCX3R32U", + "incident": { + "self": "https://api.pagerduty.com/incidents/PT4KHLK", + "summary": "[#1234567] The server is on fire.", + "type": "incident_reference", + "id": "PT4KHLK", + "html_url": "https://subdomain.pagerduty.com/incidents/PT4KHLK" + }, + "summary": "Event rule set urgency to high through the API", + "type": "event_rule_action_log_entry", + "id": "R1LRHYM5AS4Z5CM1SPMCX3R32U", + "channel": { + "alert_based_severity": "critical", + "type": "", + "urgency": "high" + } + }, + { + "service": { + "self": "https://api.pagerduty.com/services/P0K4MD5", + "summary": "Test Service", + "type": "service_reference", + "id": "P0K4MD5", + "html_url": "https://subdomain.pagerduty.com/services/P0K4MD5" + }, + "contexts": [], + "created_at": "2019-01-01T00:03:12Z", + "linked_incident": { + "self": "https://api.pagerduty.com/alerts/P61BLED", + "summary": "The server is on fire.", + "type": "alert_reference", + "id": "P61BLED", + "html_url": "https://subdomain.pagerduty.com/alerts/P61BLED" + }, + "html_url": null, + "agent": { + "self": "https://api.pagerduty.com/services/P0K4MD5", + "summary": "Test Service", + "type": "service_reference", + "id": "P0K4MD5", + "html_url": "https://subdomain.pagerduty.com/services/P0K4MD5" + }, + "teams": [ + { + "self": "https://api.pagerduty.com/teams/PL09PSG", + "summary": "Team 123", + "type": "team_reference", + "id": "PL09PSG", + "html_url": "https://subdomain.pagerduty.com/teams/PL09PSG" + } + ], + "self": "https://api.pagerduty.com/log_entries/R9KU0JVDG26SY99UHIRV6WD1JU", + "incident": { + "self": "https://api.pagerduty.com/incidents/PT4KHLK", + "summary": "[#1234567] The server is on fire.", + "type": "incident_reference", + "id": "PT4KHLK", + "html_url": "https://subdomain.pagerduty.com/incidents/PT4KHLK" + }, + "summary": "Alert \"The server is on fire.\" added through the API", + "type": "link_log_entry", + "id": "R9KU0JVDG26SY99UHIRV6WD1JU", + "channel": { + "type": "website" + } + }, + { + "service": { + "self": "https://api.pagerduty.com/services/P0K4MD5", + "summary": "Test Service", + "type": "service_reference", + "id": "P0K4MD5", + "html_url": "https://subdomain.pagerduty.com/services/P0K4MD5" + }, + "contexts": [], + "created_at": "2019-01-01T00:03:12Z", + "html_url": null, + "agent": { + "self": "https://api.pagerduty.com/services/P0K4MD5", + "summary": "Test Service", + "type": "service_reference", + "id": "P0K4MD5", + "html_url": "https://subdomain.pagerduty.com/services/P0K4MD5" + }, + "teams": [ + { + "self": "https://api.pagerduty.com/teams/PL09PSG", + "summary": "Team 123", + "type": "team_reference", + "id": "PL09PSG", + "html_url": "https://subdomain.pagerduty.com/teams/PL09PSG" + } + ], + "self": "https://api.pagerduty.com/log_entries/R4S2OL7UGPYDHV0CQ3C7IMP3WX", + "assignees": [ + { + "self": "https://api.pagerduty.com/users/PT3D7UU", + "summary": "FakeUser1", + "type": "user_reference", + "id": "PT3D7UU", + "html_url": "https://subdomain.pagerduty.com/users/PT3D7UU" + } + ], + "incident": { + "self": "https://api.pagerduty.com/incidents/PT4KHLK", + "summary": "[#1234567] The server is on fire.", + "type": "incident_reference", + "id": "PT4KHLK", + "html_url": "https://subdomain.pagerduty.com/incidents/PT4KHLK" + }, + "summary": "Assigned to FakeUser1", + "type": "assign_log_entry", + "id": "R4S2OL7UGPYDHV0CQ3C7IMP3WX", + "channel": { + "type": "auto" + } + }, + { + "service": { + "self": "https://api.pagerduty.com/services/P0K4MD5", + "summary": "Test Service", + "type": "service_reference", + "id": "P0K4MD5", + "html_url": "https://subdomain.pagerduty.com/services/P0K4MD5" + }, + "contexts": [], + "created_at": "2019-01-01T00:03:12Z", + "html_url": "https://subdomain.pagerduty.com/incidents/PT4KHLK/log_entries/RNSAN8BDFJ9LJXR92T19ERR70D", + "agent": { + "self": "https://api.pagerduty.com/services/P0K4MD5/integrations/PM4L3BM", + "summary": "Test-Service-API-v2", + "type": "events_api_v2_inbound_integration_reference", + "id": "PM4L3BM", + "html_url": "https://subdomain.pagerduty.com/services/P0K4MD5/integrations/PM4L3BM" + }, + "teams": [ + { + "self": "https://api.pagerduty.com/teams/PL09PSG", + "summary": "Team 123", + "type": "team_reference", + "id": "PL09PSG", + "html_url": "https://subdomain.pagerduty.com/teams/PL09PSG" + } + ], + "self": "https://api.pagerduty.com/log_entries/RNSAN8BDFJ9LJXR92T19ERR70D", + "incident": { + "self": "https://api.pagerduty.com/incidents/PT4KHLK", + "summary": "[#1234567] The server is on fire.", + "type": "incident_reference", + "id": "PT4KHLK", + "html_url": "https://subdomain.pagerduty.com/incidents/PT4KHLK" + }, + "id": "RNSAN8BDFJ9LJXR92T19ERR70D", + "summary": "Triggered through the API", + "type": "trigger_log_entry", + "event_details": { + "description": "The server is on fire." + }, + "channel": { + "cef_details": { + "severity": "critical", + "contexts": [], + "mutations": [], + "dedup_key": "The server is on fire.", + "version": "1.0", + "source_origin": "localhost", + "details": {}, + "message": "The server is on fire.", + "description": "The server is on fire." + }, + "description": "The server is on fire.", + "summary": "The server is on fire.", + "incident_key": "The server is on fire.", + "details": {}, + "service_key": "b48db8033932495b827ebb1b3f825920", + "type": "api" + } + } + ], + "limit": 25, + "offset": 0, + "total": 12, + "more": false +} From 1bb6896be25c77ac090d3381ff5487b409ba3a09 Mon Sep 17 00:00:00 2001 From: jjinno Date: Thu, 9 May 2019 22:12:48 -0700 Subject: [PATCH 7/9] tests for log_entries.count() & log_entries.list() --- tests/incident_test.py | 64 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 64 insertions(+) diff --git a/tests/incident_test.py b/tests/incident_test.py index 0958ef5..2e93919 100644 --- a/tests/incident_test.py +++ b/tests/incident_test.py @@ -120,3 +120,67 @@ def test_reassign_v2(): assert len(incident1.assignments) == 0 assert len(incident2.assignments) == 2 + +@httpretty.activate +def test_log_entries_count(): + body1 = open('tests/fixtures/get_incident_v2.json').read() + httpretty.register_uri( + httpretty.GET, "https://api.pagerduty.com/incidents/PT4KHLK", + body=body1, status=200) + + p = pygerduty.v2.PagerDuty("password") + incident = p.incidents.show("PT4KHLK") + assert str(incident.id) == "PT4KHLK" + assert incident.created_at == '2015-10-06T21:30:42Z' + + body2 = open('tests/fixtures/incident_log_entries.json').read() + httpretty.register_uri( + httpretty.GET, "https://api.pagerduty.com/incidents/PT4KHLK/log_entries", + body=body2, status=200) + + total = incident.log_entries.count() + assert total == 12 + +@httpretty.activate +def test_log_entries_list(): + body1 = open('tests/fixtures/get_incident_v2.json').read() + httpretty.register_uri( + httpretty.GET, "https://api.pagerduty.com/incidents/PT4KHLK", + body=body1, status=200) + + p = pygerduty.v2.PagerDuty("password") + incident = p.incidents.show("PT4KHLK") + assert str(incident.id) == "PT4KHLK" + assert incident.created_at == '2015-10-06T21:30:42Z' + + body2 = open('tests/fixtures/incident_log_entries.json').read() + httpretty.register_uri( + httpretty.GET, "https://api.pagerduty.com/incidents/PT4KHLK/log_entries", + body=body2, status=200) + + log_entries = [i for i in incident.log_entries.list(offset=0,limit=25)] + assert len(log_entries) == 12 + assert log_entries[0].created_at == '2019-01-01T00:57:11Z' + assert log_entries[0].self_ == 'https://api.pagerduty.com/log_entries/RP2836ITHU9F3ADA1T9MF3F97O' + + body2 = open('tests/fixtures/incident_log_entries.json').read() + httpretty.register_uri( + httpretty.GET, "https://api.pagerduty.com/incidents/PT4KHLK/log_entries", + responses=[ + httpretty.Response(body=body2, status=200), + httpretty.Response(body=textwrap.dedent("""\ + { + "limit": 25, + "more": false, + "offset": 13, + "log_entries": [], + "total": null + } + """), status=200) + ] + ) + + log_entries = [i for i in incident.log_entries.list()] + assert len(log_entries) == 12 + assert log_entries[0].created_at == '2019-01-01T00:57:11Z' + assert log_entries[0].self_ == 'https://api.pagerduty.com/log_entries/RP2836ITHU9F3ADA1T9MF3F97O' From b9f2a32a34e642e09dbad4f6516e9508d6f1cbd1 Mon Sep 17 00:00:00 2001 From: jjinno Date: Thu, 16 May 2019 10:15:52 -0700 Subject: [PATCH 8/9] separated log_entries test into 2 tests Instead of one hard-to-read test, incident.log_entries testing is now broken into 2 less-hard-to-read tests --- tests/incident_test.py | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/tests/incident_test.py b/tests/incident_test.py index 2e93919..f45a615 100644 --- a/tests/incident_test.py +++ b/tests/incident_test.py @@ -142,7 +142,7 @@ def test_log_entries_count(): assert total == 12 @httpretty.activate -def test_log_entries_list(): +def test_log_entries_list_offset(): body1 = open('tests/fixtures/get_incident_v2.json').read() httpretty.register_uri( httpretty.GET, "https://api.pagerduty.com/incidents/PT4KHLK", @@ -163,6 +163,18 @@ def test_log_entries_list(): assert log_entries[0].created_at == '2019-01-01T00:57:11Z' assert log_entries[0].self_ == 'https://api.pagerduty.com/log_entries/RP2836ITHU9F3ADA1T9MF3F97O' +@httpretty.activate +def test_log_entries_list(): + body1 = open('tests/fixtures/get_incident_v2.json').read() + httpretty.register_uri( + httpretty.GET, "https://api.pagerduty.com/incidents/PT4KHLK", + body=body1, status=200) + + p = pygerduty.v2.PagerDuty("password") + incident = p.incidents.show("PT4KHLK") + assert str(incident.id) == "PT4KHLK" + assert incident.created_at == '2015-10-06T21:30:42Z' + body2 = open('tests/fixtures/incident_log_entries.json').read() httpretty.register_uri( httpretty.GET, "https://api.pagerduty.com/incidents/PT4KHLK/log_entries", From eb1129af0e60c2725a3ac4f208eb0ea5bca0c619 Mon Sep 17 00:00:00 2001 From: jjinno Date: Tue, 2 Jul 2019 10:28:34 -0700 Subject: [PATCH 9/9] added retry on 5xx errors According to documentation, all 5xx errors should be retried https://v2.developer.pagerduty.com/docs/events-api-v2#api-response-codes--retry-logic --- pygerduty/common.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/pygerduty/common.py b/pygerduty/common.py index b3eac1c..84e10c6 100644 --- a/pygerduty/common.py +++ b/pygerduty/common.py @@ -50,6 +50,11 @@ def execute_request(self, request, retry_count=0): return self.execute_request(request, retry_count + 1) else: raise + elif err.code / 100 == 5: + if retry_count < self.max_retries: + return self.execute_request(request, retry_count + 1) + else: + raise else: raise except urllib.error.URLError: