From 12c0618093421c03d98c8112c8d2e8d1f9e1bd52 Mon Sep 17 00:00:00 2001 From: Tim Carr Date: Mon, 1 Apr 2024 17:26:02 +0100 Subject: [PATCH 1/3] Add API support --- src/ConvertKit_API.php | 46 ++++++++++++++++++++++++++++++++++++------ 1 file changed, 40 insertions(+), 6 deletions(-) diff --git a/src/ConvertKit_API.php b/src/ConvertKit_API.php index e0e0d53..f20b254 100644 --- a/src/ConvertKit_API.php +++ b/src/ConvertKit_API.php @@ -1485,6 +1485,38 @@ public function destroy_broadcast(int $id) return $this->delete(sprintf('broadcasts/%s', $id)); } + /** + * List webhooks. + * + * @param boolean $include_total_count To include the total count of records in the response, use true. + * @param string $after_cursor Return results after the given pagination cursor. + * @param string $before_cursor Return results before the given pagination cursor. + * @param integer $per_page Number of results to return. + * + * @since 2.0.0 + * + * @see https://developers.convertkit.com/v4.html#list-webhooks + * + * @return false|mixed + */ + public function get_webhooks( + bool $include_total_count = false, + string $after_cursor = '', + string $before_cursor = '', + int $per_page = 100 + ) { + // Send request. + return $this->get( + endpoint: 'webhooks', + args: $this->build_total_count_and_pagination_params( + include_total_count: $include_total_count, + after_cursor: $after_cursor, + before_cursor: $before_cursor, + per_page: $per_page + ) + ); + } + /** * Creates a webhook that will be called based on the chosen event types. * @@ -1494,7 +1526,7 @@ public function destroy_broadcast(int $id) * * @since 1.0.0 * - * @see https://developers.convertkit.com/#create-a-webhook + * @see https://developers.convertkit.com/v4.html#create-a-webhook * * @throws \InvalidArgumentException If the event is not supported. * @@ -1506,6 +1538,8 @@ public function create_webhook(string $url, string $event, string $parameter = ' switch ($event) { case 'subscriber.subscriber_activate': case 'subscriber.subscriber_unsubscribe': + case 'subscriber.subscriber_bounce': + case 'subscriber.subscriber_complain': case 'purchase.purchase_create': $eventData = ['name' => $event]; break; @@ -1553,7 +1587,7 @@ public function create_webhook(string $url, string $event, string $parameter = ' // Send request. return $this->post( - 'automations/hooks', + 'webhooks', [ 'target_url' => $url, 'event' => $eventData, @@ -1564,17 +1598,17 @@ public function create_webhook(string $url, string $event, string $parameter = ' /** * Deletes an existing webhook. * - * @param integer $rule_id Rule ID. + * @param integer $id Webhook ID. * * @since 1.0.0 * - * @see https://developers.convertkit.com/#destroy-webhook + * @see https://developers.convertkit.com/v4.html#delete-a-webhook * * @return false|object */ - public function destroy_webhook(int $rule_id) + public function delete_webhook(int $id) { - return $this->delete(sprintf('automations/hooks/%s', $rule_id)); + return $this->delete(sprintf('webhooks/%s', $id)); } /** From b04f62812ab0d04ad2dec338a723ad4f1bcc062f Mon Sep 17 00:00:00 2001 From: Tim Carr Date: Mon, 1 Apr 2024 17:28:42 +0100 Subject: [PATCH 2/3] Started tests --- tests/ConvertKitAPITest.php | 125 ++++++++++++++++++++++++++++++------ 1 file changed, 106 insertions(+), 19 deletions(-) diff --git a/tests/ConvertKitAPITest.php b/tests/ConvertKitAPITest.php index e9bd2ec..d494928 100644 --- a/tests/ConvertKitAPITest.php +++ b/tests/ConvertKitAPITest.php @@ -3791,7 +3791,102 @@ public function testDestroyBroadcastWithInvalidBroadcastID() } /** - * Test that create_webhook() and destroy_webhook() works. + * Test that get_webhooks() returns the expected data. + * + * @since 2.0.0 + * + * @return void + */ + public function testGetWebhooks() + { + $result = $this->api->get_webhooks(); + + // Assert webhooks and pagination exist. + $this->assertDataExists($result, 'webhooks'); + $this->assertPaginationExists($result); + } + + /** + * Test that get_webhooks() returns the expected data + * when the total count is included. + * + * @since 2.0.0 + * + * @return void + */ + public function testGetWebhooksWithTotalCount() + { + $result = $this->api->get_webhooks( + include_total_count: true + ); + + // Assert webhooks and pagination exist. + $this->assertDataExists($result, 'webhooks'); + $this->assertPaginationExists($result); + + // Assert total count is included. + $this->assertArrayHasKey('total_count', get_object_vars($result->pagination)); + $this->assertGreaterThan(0, $result->pagination->total_count); + } + + /** + * Test that get_webhooks() returns the expected data + * when pagination parameters and per_page limits are specified. + * + * @since 2.0.0 + * + * @return void + */ + public function testGetWebhooksPagination() + { + $result = $this->api->get_webhooks( + per_page: 1 + ); + + // Assert webhooks and pagination exist. + $this->assertDataExists($result, 'webhooks'); + $this->assertPaginationExists($result); + + // Assert a single webhook was returned. + $this->assertCount(1, $result->webhooks); + + // Assert has_previous_page and has_next_page are correct. + $this->assertFalse($result->pagination->has_previous_page); + $this->assertTrue($result->pagination->has_next_page); + + // Use pagination to fetch next page. + $result = $this->api->get_webhooks( + per_page: 1, + after_cursor: $result->pagination->end_cursor + ); + + // Assert webhooks and pagination exist. + $this->assertDataExists($result, 'webhooks'); + $this->assertPaginationExists($result); + + // Assert a single webhook was returned. + $this->assertCount(1, $result->webhooks); + + // Assert has_previous_page and has_next_page are correct. + $this->assertTrue($result->pagination->has_previous_page); + $this->assertTrue($result->pagination->has_next_page); + + // Use pagination to fetch previous page. + $result = $this->api->get_webhooks( + per_page: 1, + before_cursor: $result->pagination->start_cursor + ); + + // Assert webhooks and pagination exist. + $this->assertDataExists($result, 'webhooks'); + $this->assertPaginationExists($result); + + // Assert a single webhook was returned. + $this->assertCount(1, $result->webhooks); + } + + /** + * Test that create_webhook() and delete_webhook() works. * * We do both, so we don't end up with unnecessary webhooks remaining * on the ConvertKit account when running tests. @@ -3800,10 +3895,8 @@ public function testDestroyBroadcastWithInvalidBroadcastID() * * @return void */ - public function testCreateAndDestroyWebhook() + public function testCreateAndDeleteWebhook() { - $this->markTestIncomplete(); - // Create a webhook first. $result = $this->api->create_webhook( url: 'https://webhook.site/9c731823-7e61-44c8-af39-43b11f700ecb', @@ -3811,13 +3904,13 @@ public function testCreateAndDestroyWebhook() ); $ruleID = $result->rule->id; - // Destroy the webhook. - $result = $this->api->destroy_webhook($ruleID); + // Delete the webhook. + $result = $this->api->delete_webhook($ruleID); $this->assertEquals($result->success, true); } /** - * Test that create_webhook() and destroy_webhook() works with an event parameter. + * Test that create_webhook() and delete_webhook() works with an event parameter. * * We do both, so we don't end up with unnecessary webhooks remaining * on the ConvertKit account when running tests. @@ -3826,10 +3919,8 @@ public function testCreateAndDestroyWebhook() * * @return void */ - public function testCreateAndDestroyWebhookWithEventParameter() + public function testCreateAndDeleteWebhookWithEventParameter() { - $this->markTestIncomplete(); - // Create a webhook first. $result = $this->api->create_webhook( url: 'https://webhook.site/9c731823-7e61-44c8-af39-43b11f700ecb', @@ -3838,8 +3929,8 @@ public function testCreateAndDestroyWebhookWithEventParameter() ); $ruleID = $result->rule->id; - // Destroy the webhook. - $result = $this->api->destroy_webhook($ruleID); + // Delete the webhook. + $result = $this->api->delete_webhook($ruleID); $this->assertEquals($result->success, true); } @@ -3853,8 +3944,6 @@ public function testCreateAndDestroyWebhookWithEventParameter() */ public function testCreateWebhookWithInvalidEvent() { - $this->markTestIncomplete(); - $this->expectException(InvalidArgumentException::class); $this->api->create_webhook( url: 'https://webhook.site/9c731823-7e61-44c8-af39-43b11f700ecb', @@ -3863,19 +3952,17 @@ public function testCreateWebhookWithInvalidEvent() } /** - * Test that destroy_webhook() throws a ClientException when an invalid + * Test that delete_webhook() throws a ClientException when an invalid * rule ID is specified. * * @since 1.0.0 * * @return void */ - public function testDestroyWebhookWithInvalidRuleID() + public function testsDeleteWebhookWithInvalidRuleID() { - $this->markTestIncomplete(); - $this->expectException(ClientException::class); - $this->api->destroy_webhook(12345); + $this->api->delete_webhook(12345); } /** From bf449bf19b0a4bac23a368b80f22e3b6aee8e9d3 Mon Sep 17 00:00:00 2001 From: Tim Carr Date: Tue, 2 Apr 2024 11:43:35 +0100 Subject: [PATCH 3/3] Completed tests --- tests/ConvertKitAPITest.php | 135 ++++++++++++++++++++---------------- 1 file changed, 76 insertions(+), 59 deletions(-) diff --git a/tests/ConvertKitAPITest.php b/tests/ConvertKitAPITest.php index d494928..42563cd 100644 --- a/tests/ConvertKitAPITest.php +++ b/tests/ConvertKitAPITest.php @@ -51,6 +51,15 @@ class ConvertKitAPITest extends TestCase */ protected $subscriber_ids = []; + /** + * Webhook IDs to delete on teardown of a test. + * + * @since 2.0.0 + * + * @var array + */ + protected $webhook_ids = []; + /** * Load .env configuration into $_ENV superglobal, and initialize the API * class before each test. @@ -97,6 +106,11 @@ protected function tearDown(): void foreach ($this->subscriber_ids as $id) { $this->api->unsubscribe($id); } + + // Delete any Webhooks. + foreach ($this->webhook_ids as $id) { + $this->api->delete_webhook($id); + } } /** @@ -3790,45 +3804,6 @@ public function testDestroyBroadcastWithInvalidBroadcastID() $this->api->destroy_broadcast(12345); } - /** - * Test that get_webhooks() returns the expected data. - * - * @since 2.0.0 - * - * @return void - */ - public function testGetWebhooks() - { - $result = $this->api->get_webhooks(); - - // Assert webhooks and pagination exist. - $this->assertDataExists($result, 'webhooks'); - $this->assertPaginationExists($result); - } - - /** - * Test that get_webhooks() returns the expected data - * when the total count is included. - * - * @since 2.0.0 - * - * @return void - */ - public function testGetWebhooksWithTotalCount() - { - $result = $this->api->get_webhooks( - include_total_count: true - ); - - // Assert webhooks and pagination exist. - $this->assertDataExists($result, 'webhooks'); - $this->assertPaginationExists($result); - - // Assert total count is included. - $this->assertArrayHasKey('total_count', get_object_vars($result->pagination)); - $this->assertGreaterThan(0, $result->pagination->total_count); - } - /** * Test that get_webhooks() returns the expected data * when pagination parameters and per_page limits are specified. @@ -3839,6 +3814,25 @@ public function testGetWebhooksWithTotalCount() */ public function testGetWebhooksPagination() { + // Create webhooks first. + $results = [ + $this->api->create_webhook( + url: 'https://webhook.site/' . str_shuffle('wfervdrtgsdewrafvwefds'), + event: 'subscriber.subscriber_activate', + ), + $this->api->create_webhook( + url: 'https://webhook.site/' . str_shuffle('wfervdrtgsdewrafvwefds'), + event: 'subscriber.subscriber_activate', + ), + ]; + + // Set webhook_ids to ensure webhooks are deleted after test. + $this->webhook_ids = [ + $results[0]->webhook->id, + $results[1]->webhook->id, + ]; + + // Get webhooks. $result = $this->api->get_webhooks( per_page: 1 ); @@ -3869,7 +3863,7 @@ public function testGetWebhooksPagination() // Assert has_previous_page and has_next_page are correct. $this->assertTrue($result->pagination->has_previous_page); - $this->assertTrue($result->pagination->has_next_page); + $this->assertFalse($result->pagination->has_next_page); // Use pagination to fetch previous page. $result = $this->api->get_webhooks( @@ -3886,7 +3880,7 @@ public function testGetWebhooksPagination() } /** - * Test that create_webhook() and delete_webhook() works. + * Test that create_webhook(), get_webhooks() and delete_webhook() works. * * We do both, so we don't end up with unnecessary webhooks remaining * on the ConvertKit account when running tests. @@ -3895,43 +3889,66 @@ public function testGetWebhooksPagination() * * @return void */ - public function testCreateAndDeleteWebhook() + public function testCreateGetAndDeleteWebhook() { // Create a webhook first. $result = $this->api->create_webhook( - url: 'https://webhook.site/9c731823-7e61-44c8-af39-43b11f700ecb', + url: 'https://webhook.site/' . str_shuffle('wfervdrtgsdewrafvwefds'), event: 'subscriber.subscriber_activate', ); - $ruleID = $result->rule->id; + $id = $result->webhook->id; + + // Get webhooks. + $result = $this->api->get_webhooks(); + + // Assert webhooks and pagination exist. + $this->assertDataExists($result, 'webhooks'); + $this->assertPaginationExists($result); + + // Get webhooks including total count. + $result = $this->api->get_webhooks( + include_total_count: true + ); + + // Assert webhooks and pagination exist. + $this->assertDataExists($result, 'webhooks'); + $this->assertPaginationExists($result); + + // Assert total count is included. + $this->assertArrayHasKey('total_count', get_object_vars($result->pagination)); + $this->assertGreaterThan(0, $result->pagination->total_count); // Delete the webhook. - $result = $this->api->delete_webhook($ruleID); - $this->assertEquals($result->success, true); + $result = $this->api->delete_webhook($id); } /** - * Test that create_webhook() and delete_webhook() works with an event parameter. - * - * We do both, so we don't end up with unnecessary webhooks remaining - * on the ConvertKit account when running tests. + * Test that create_webhook() works with an event parameter. * * @since 1.0.0 * * @return void */ - public function testCreateAndDeleteWebhookWithEventParameter() + public function testCreateWebhookWithEventParameter() { - // Create a webhook first. + // Create a webhook. + $url = 'https://webhook.site/' . str_shuffle('wfervdrtgsdewrafvwefds'); $result = $this->api->create_webhook( - url: 'https://webhook.site/9c731823-7e61-44c8-af39-43b11f700ecb', + url: $url, event: 'subscriber.form_subscribe', parameter: $_ENV['CONVERTKIT_API_FORM_ID'] ); - $ruleID = $result->rule->id; + + // Confirm webhook created with correct data. + $this->assertArrayHasKey('webhook', get_object_vars($result)); + $this->assertArrayHasKey('id', get_object_vars($result->webhook)); + $this->assertArrayHasKey('target_url', get_object_vars($result->webhook)); + $this->assertEquals($result->webhook->target_url, $url); + $this->assertEquals($result->webhook->event->name, 'form_subscribe'); + $this->assertEquals($result->webhook->event->form_id, $_ENV['CONVERTKIT_API_FORM_ID']); // Delete the webhook. - $result = $this->api->delete_webhook($ruleID); - $this->assertEquals($result->success, true); + $result = $this->api->delete_webhook($result->webhook->id); } /** @@ -3946,20 +3963,20 @@ public function testCreateWebhookWithInvalidEvent() { $this->expectException(InvalidArgumentException::class); $this->api->create_webhook( - url: 'https://webhook.site/9c731823-7e61-44c8-af39-43b11f700ecb', + url: 'https://webhook.site/' . str_shuffle('wfervdrtgsdewrafvwefds'), event: 'invalid.event' ); } /** * Test that delete_webhook() throws a ClientException when an invalid - * rule ID is specified. + * ID is specified. * * @since 1.0.0 * * @return void */ - public function testsDeleteWebhookWithInvalidRuleID() + public function testDeleteWebhookWithInvalidID() { $this->expectException(ClientException::class); $this->api->delete_webhook(12345);