From a90a245951804b80cc5dc784d9e2eccd2544eb46 Mon Sep 17 00:00:00 2001 From: Ana Paula Gomes Date: Fri, 15 Feb 2019 09:09:13 +0100 Subject: [PATCH] Add support to webhook subscription API (#137) --- closeio/closeio.py | 35 ++++++++++++++ closeio/contrib/testing_stub.py | 50 ++++++++++++++++++++ tests/test_end_to_end.py | 77 ++++++++++++++++++++++++++++-- tests/test_testing_stub.py | 84 +++++++++++++++++++++++++++++++++ 4 files changed, 243 insertions(+), 3 deletions(-) diff --git a/closeio/closeio.py b/closeio/closeio.py index b58e074..d4e114f 100644 --- a/closeio/closeio.py +++ b/closeio/closeio.py @@ -516,3 +516,38 @@ def get_event_logs(self, **kwargs): self._api.event.get, **kwargs ) + + @parse_response + @handle_errors + def get_webhooks(self): + return paginate( + self._api.webhook.get, + ) + + @parse_response + @handle_errors + def get_webhook(self, webhook_id): + return self._api.webhook(webhook_id).get() + + @parse_response + @handle_errors + def create_webhook(self, data): + return self._api.webhook.post(data) + + @parse_response + @handle_errors + def update_webhook(self, webhook_id, data): + """ + Update a webhook status. + + :param webhook_id: + :param data (dict): { status: active or paused } + :return: + """ + status = convert(data) + return self._api.webhook(webhook_id).put(status) + + @parse_response + @handle_errors + def delete_webhook(self, webhook_id): + return self._api.webhook(webhook_id).delete() diff --git a/closeio/contrib/testing_stub.py b/closeio/contrib/testing_stub.py index b2e4352..fcd0876 100644 --- a/closeio/contrib/testing_stub.py +++ b/closeio/contrib/testing_stub.py @@ -689,3 +689,53 @@ def api_key(self): 'key': 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx', }], }) + + @parse_response + def get_webhooks(self): + return self._data('webhooks', []) + + @parse_response + def get_webhook(self, webhook_id): + webhooks = self._data('webhooks', []) + + for webhook in webhooks: + if webhook['id'] == webhook_id: + return webhook + + raise CloseIOError() + + @parse_response + def create_webhook(self, data): + webhooks = self._data('webhooks', []) + + new_webhook = { + 'status': 'active', + 'updated_by': 'user_{}'.format(uuid.uuid4().hex), + 'url': data['url'], + 'created_by': 'user_{}'.format(uuid.uuid4().hex), + 'signature_key': 'xxxxxx', + 'date_created': datetime.utcnow().isoformat(), + 'date_updated': datetime.utcnow().isoformat(), + 'verify_ssl': True, + 'events': data['events'], + 'id': 'whsub_{}'.format(uuid.uuid4().hex) + } + webhooks.append(new_webhook) + return new_webhook + + @parse_response + def update_webhook(self, webhook_id, status): + webhook = self.get_webhook(webhook_id) + webhook['status'] = status['status'] + return webhook + + @parse_response + def delete_webhook(self, webhook_id): + webhooks = self._data('webhooks', []) + + for index, webhook in enumerate(webhooks): + if webhook['id'] == webhook_id: + webhooks.pop(index) + return True + else: + raise CloseIOError() diff --git a/tests/test_end_to_end.py b/tests/test_end_to_end.py index 06e41b5..6f50960 100644 --- a/tests/test_end_to_end.py +++ b/tests/test_end_to_end.py @@ -82,6 +82,21 @@ def wait_for_index(lead_id): def export(self, client, random_string): return client.create_lead_export(random_string) + @pytest.fixture + def webhook(self, client): + data = { + 'url': 'https://example.thermondo.de/notes/closeio/', + 'events': [{ + 'object_type': 'activity.note', + 'action': 'created' + }] + } + new_webhook = client.create_webhook(data) + + yield new_webhook + + client.delete_webhook(new_webhook['id']) + def test_create_lead(self, client): with open(os.path.join(FIXTURE_DIR, 'lead.json')) as f: lead = utils.parse(json.load(f)) @@ -103,7 +118,7 @@ def test_get_leads(self, client, lead): def test_update_lead(self, client, lead): fields = { - "description": "Best show ever canceled. Sad." + 'description': 'Best show ever canceled. Sad.' } response = client.update_lead(lead['id'], fields) assert fields['description'] == response['description'] @@ -125,7 +140,7 @@ def test_create_task(self, client, lead, _date): @pytest.mark.flaky(reruns=3) def test_update_task(self, client, task): fields = { - "is_complete": True + 'is_complete': True } response = client.update_task(task['id'], fields) assert response['is_complete'], dict(response) @@ -153,7 +168,7 @@ def test_create_opportunity(self, client, lead): def test_update_opportunity(self, client, opportunity): fields = { - "confidence": 75 + 'confidence': 75 } response = client.update_opportunity(opportunity['id'], fields) assert response['confidence'] == 75, dict(response) @@ -246,3 +261,59 @@ def test_get_export(self, client, export): def test_api_key(self, client): assert client.api_key() + + def test_get_webhooks(self, client, webhook): + existent_webhooks = next(client.get_webhooks()) + + assert existent_webhooks['id'] + assert existent_webhooks['status'] + assert existent_webhooks['url'] + assert existent_webhooks['events'] + + def test_create_webhook(self, client): + data = { + 'url': 'https://example.thermondo.de/notes/closeio/', + 'events': [{ + 'object_type': 'activity.note', + 'action': 'created' + }] + } + new_webhook = client.create_webhook(data) + + assert new_webhook['id'] + assert new_webhook['status'] + assert new_webhook['url'] + assert new_webhook['events'] + + client.delete_webhook(new_webhook['id']) + + def test_get_webhook(self, client, webhook): + retrieved_webhook = client.get_webhook(webhook['id']) + + assert retrieved_webhook['id'] + assert retrieved_webhook['status'] + assert retrieved_webhook['url'] + assert retrieved_webhook['events'] + + def test_update_webhook(self, client, webhook): + data = {'status': 'paused'} + updated_webhook = client.update_webhook(webhook['id'], data) + + assert updated_webhook['id'] + assert updated_webhook['status'] + assert updated_webhook['url'] + assert updated_webhook['events'] + + def test_delete_webhook(self, client): + data = { + 'url': 'https://example.thermondo.de/notes/closeio/', + 'events': [{ + 'object_type': 'activity.note', + 'action': 'created' + }] + } + new_webhook = client.create_webhook(data) + + deleted_webhook = client.delete_webhook(new_webhook['id']) + + assert isinstance(deleted_webhook, bool) diff --git a/tests/test_testing_stub.py b/tests/test_testing_stub.py index 9cedc5a..808f3c7 100644 --- a/tests/test_testing_stub.py +++ b/tests/test_testing_stub.py @@ -1,5 +1,8 @@ import datetime +import pytest + +from closeio import CloseIOError from closeio.contrib.testing_stub import CloseIOStub @@ -91,3 +94,84 @@ def test_create_activity_email(self): } response = client.create_activity_email(**kwargs) assert response['id'].startswith('acti_') + + +class TestWebhooksSubscription: + def test_get_webhooks(self): + client = CloseIOStub() + + response = client.get_webhooks() + + assert response == [] + + data = { + 'url': 'aweso.me', + 'events': [ + { + 'object_type': 'activity.note', + 'action': 'created' + } + ] + } + client.create_webhook(data) + + response = client.get_webhooks() + + assert len(response) == 1 + assert response[0]['id'] + assert response[0]['status'] + assert response[0]['url'] + assert response[0]['events'] + + def test_create_webhook(self): + client = CloseIOStub() + + data = { + 'url': 'aweso.me', + 'events': [ + { + 'object_type': 'activity.note', + 'action': 'created' + } + ] + } + new_webhook = client.create_webhook(data) + assert new_webhook['id'] + assert new_webhook['status'] + assert new_webhook['url'] + assert new_webhook['events'] + + def test_update_webhook(self): + client = CloseIOStub() + + data = { + 'url': 'aweso.me', + 'events': [ + { + 'object_type': 'activity.note', + 'action': 'created' + } + ] + } + new_webhook = client.create_webhook(data) + + data = {'status': 'paused'} + updated_webhook = client.update_webhook(new_webhook['id'], data) + + assert updated_webhook['status'] == data['status'] + + def test_delete_webhook(self): + client = CloseIOStub() + data = { + 'url': 'https://example.com/notes/closeio/', + 'events': [{ + 'object_type': 'activity.note', + 'action': 'created' + }] + + } + new_webhook = client.create_webhook(data) + + assert client.delete_webhook(new_webhook['id']) is True + with pytest.raises(CloseIOError): + client.get_webhook(new_webhook['id'])