From 7ec9a428c47278d6551b9dbd9dc9815ad7631c05 Mon Sep 17 00:00:00 2001 From: Dan Date: Tue, 19 May 2026 14:17:29 +0300 Subject: [PATCH] Content API endpoints; updated get to list --- CHANGELOG.md | 6 + README.md | 289 ++++++++++++++++++++++++++++-- src/Campaigns.php | 49 +++++ src/Components.php | 35 ++++ src/ContactProperties.php | 2 +- src/DedicatedSendingIps.php | 20 +++ src/EmailMessages.php | 27 +++ src/LoopsClient.php | 10 ++ src/MailingLists.php | 2 +- src/Themes.php | 35 ++++ src/Transactional.php | 2 +- tests/CampaignsTest.php | 130 ++++++++++++++ tests/ComponentsTest.php | 65 +++++++ tests/DedicatedSendingIpsTest.php | 36 ++++ tests/EmailMessagesTest.php | 94 ++++++++++ tests/ThemesTest.php | 72 ++++++++ tests/TransactionalTest.php | 2 +- 17 files changed, 861 insertions(+), 15 deletions(-) create mode 100644 src/Campaigns.php create mode 100644 src/Components.php create mode 100644 src/DedicatedSendingIps.php create mode 100644 src/EmailMessages.php create mode 100644 src/Themes.php create mode 100644 tests/CampaignsTest.php create mode 100644 tests/ComponentsTest.php create mode 100644 tests/DedicatedSendingIpsTest.php create mode 100644 tests/EmailMessagesTest.php create mode 100644 tests/ThemesTest.php diff --git a/CHANGELOG.md b/CHANGELOG.md index c22d04a..61028a4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,9 @@ +## v3.0.0 - May 19, 2026 + +Added support for dedicated sending IPs, themes, components, campaigns, and email messages. + +Renamed methods for consistency: single-resource lookups use `get()`, collection endpoints use `list()`. This affects `contactProperties`, `mailingLists`, and `transactional` from prior versions. + ## v2.1.0 - Apr 8, 2026 Added `contacts->checkSuppression()` and `contacts->removeSuppression()` for managing contact suppressions. diff --git a/README.md b/README.md index fccbaa2..42399b7 100644 --- a/README.md +++ b/README.md @@ -103,11 +103,22 @@ You can use custom contact properties in API calls. Please make sure to [add cus - [contacts->checkSuppression()](#contacts-checksuppression) - [contacts->removeSuppression()](#contacts-removesuppression) - [contactProperties->create()](#contactproperties-create) -- [contactProperties->get()](#contactproperties-get) -- [mailingLists->get()](#mailinglists-get) +- [contactProperties->list()](#contactproperties-list) +- [mailingLists->list()](#mailinglists-list) - [events->send()](#events-send) - [transactional->send()](#transactional-send) -- [transactional->get()](#transactional-get) +- [transactional->list()](#transactional-list) +- [dedicatedSendingIps->list()](#dedicatedsendingips-list) +- [themes->list()](#themes-list) +- [themes->get()](#themes-get) +- [components->list()](#components-list) +- [components->get()](#components-get) +- [campaigns->list()](#campaigns-list) +- [campaigns->create()](#campaigns-create) +- [campaigns->get()](#campaigns-get) +- [campaigns->update()](#campaigns-update) +- [emailMessages->get()](#emailmessages-get) +- [emailMessages->update()](#emailmessages-update) --- @@ -524,7 +535,7 @@ Error handling is done through the `APIError` class, which provides `getStatusCo --- -### contactProperties->get() +### contactProperties->list() Get a list of your account's contact properties. @@ -539,9 +550,9 @@ Get a list of your account's contact properties. #### Example ```php -$result = $loops->contactProperties->get(); +$result = $loops->contactProperties->list(); -$result = $loops->contactProperties->get(list: "custom"); +$result = $loops->contactProperties->list(list: "custom"); ``` #### Response @@ -610,7 +621,7 @@ This method will return a list of contact property objects containing `key`, `la --- -### mailingLists->get() +### mailingLists->list() Get a list of your account's mailing lists. [Read more about mailing lists](https://loops.so/docs/contacts/mailing-lists) @@ -623,7 +634,7 @@ None #### Example ```php -$result = $loops->mailingLists->get(); +$result = $loops->mailingLists->list(); ``` #### Response @@ -830,7 +841,7 @@ If there is a problem with the request, a descriptive error message will be retu --- -### transactional->get() +### transactional->list() Get a list of published transactional emails. @@ -846,9 +857,9 @@ Get a list of published transactional emails. #### Example ```php -$result = $loops->transactional->get(); +$result = $loops->transactional->list(); -$result = $loops->transactional->get(per_page: 15); +$result = $loops->transactional->list(per_page: 15); ``` #### Response @@ -892,6 +903,262 @@ $result = $loops->transactional->get(per_page: 15); --- +### dedicatedSendingIps->list() + +Get a list of Loops' dedicated sending IP addresses. + +[API Reference](https://loops.so/docs/api-reference/get-dedicated-sending-ips) + +#### Parameters + +None + +#### Example + +```php +$result = $loops->dedicatedSendingIps->list(); +``` + +#### Response + +```json +["1.2.3.4", "5.6.7.8"] +``` + +--- + +### themes->list() + +List email themes. + +[API Reference](https://loops.so/docs/api-reference/list-themes) + +#### Parameters + +| Name | Type | Required | Notes | +| ----------- | ------- | -------- | ----------------------------------------------------------------------------------------------------------------------------- | +| `$per_page` | integer | No | How many results to return per page. Must be between 10 and 50. Defaults to 20 if omitted. | +| `$cursor` | string | No | A cursor, to return a specific page of results. Cursors can be found from the `pagination.nextCursor` value in each response. | + +#### Example + +```php +$result = $loops->themes->list(); + +$result = $loops->themes->list(per_page: 15, cursor: 'cursor123'); +``` + +--- + +### themes->get() + +Get a single email theme by ID. + +[API Reference](https://loops.so/docs/api-reference/get-theme) + +#### Parameters + +| Name | Type | Required | Notes | +| ----------- | ------ | -------- | ------------------ | +| `$theme_id` | string | Yes | The ID of the theme. | + +#### Example + +```php +$result = $loops->themes->get(theme_id: 'theme_abc123'); +``` + +--- + +### components->list() + +List email components. + +[API Reference](https://loops.so/docs/api-reference/list-components) + +#### Parameters + +| Name | Type | Required | Notes | +| ----------- | ------- | -------- | ----------------------------------------------------------------------------------------------------------------------------- | +| `$per_page` | integer | No | How many results to return per page. Must be between 10 and 50. Defaults to 20 if omitted. | +| `$cursor` | string | No | A cursor, to return a specific page of results. Cursors can be found from the `pagination.nextCursor` value in each response. | + +#### Example + +```php +$result = $loops->components->list(); +``` + +--- + +### components->get() + +Get a single email component by ID. + +[API Reference](https://loops.so/docs/api-reference/get-component) + +#### Parameters + +| Name | Type | Required | Notes | +| --------------- | ------ | -------- | ----------------------- | +| `$component_id` | string | Yes | The ID of the component. | + +#### Example + +```php +$result = $loops->components->get(component_id: 'component_abc123'); +``` + +--- + +### campaigns->list() + +List campaigns. + +[API Reference](https://loops.so/docs/api-reference/list-campaigns) + +#### Parameters + +| Name | Type | Required | Notes | +| ----------- | ------- | -------- | ----------------------------------------------------------------------------------------------------------------------------- | +| `$per_page` | integer | No | How many results to return per page. Must be between 10 and 50. Defaults to 20 if omitted. | +| `$cursor` | string | No | A cursor, to return a specific page of results. Cursors can be found from the `pagination.nextCursor` value in each response. | + +#### Example + +```php +$result = $loops->campaigns->list(); +``` + +--- + +### campaigns->create() + +Create a new draft campaign. + +[API Reference](https://loops.so/docs/api-reference/create-campaign) + +#### Parameters + +| Name | Type | Required | Notes | +| ------- | ------ | -------- | ------------------ | +| `$name` | string | Yes | The campaign name. | + +#### Example + +```php +$result = $loops->campaigns->create(name: 'Spring announcement'); +``` + +#### Response + +```json +{ + "success": true, + "campaignId": "camp_123", + "name": "Spring announcement", + "status": "Draft", + "createdAt": "2025-01-01T00:00:00.000Z", + "updatedAt": "2025-01-01T00:00:00.000Z", + "emailMessageId": "msg_123", + "emailMessageContentRevisionId": "rev_123" +} +``` + +--- + +### campaigns->get() + +Get a single campaign by ID. + +[API Reference](https://loops.so/docs/api-reference/get-campaign) + +#### Parameters + +| Name | Type | Required | Notes | +| -------------- | ------ | -------- | --------------------- | +| `$campaign_id` | string | Yes | The ID of the campaign. | + +#### Example + +```php +$result = $loops->campaigns->get(campaign_id: 'camp_123'); +``` + +--- + +### campaigns->update() + +Update a draft campaign's name. + +[API Reference](https://loops.so/docs/api-reference/update-campaign) + +#### Parameters + +| Name | Type | Required | Notes | +| -------------- | ------ | -------- | --------------------- | +| `$campaign_id` | string | Yes | The ID of the campaign. | +| `$name` | string | Yes | The updated name. | + +#### Example + +```php +$result = $loops->campaigns->update( + campaign_id: 'camp_123', + name: 'Updated name' +); +``` + +--- + +### emailMessages->get() + +Get an email message, including its compiled LMX content. + +[API Reference](https://loops.so/docs/api-reference/get-email-message) + +#### Parameters + +| Name | Type | Required | Notes | +| ------------------- | ------ | -------- | --------------------------- | +| `$email_message_id` | string | Yes | The ID of the email message. | + +#### Example + +```php +$result = $loops->emailMessages->get(email_message_id: 'msg_123'); +``` + +--- + +### emailMessages->update() + +Update an email message's subject, preview text, sender, or LMX content. + +[API Reference](https://loops.so/docs/api-reference/update-email-message) + +#### Parameters + +| Name | Type | Required | Notes | +| ------------------- | ----- | -------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `$email_message_id` | string | Yes | The ID of the email message. | +| `$fields` | array | No | Fields to update. Use API field names: `expectedRevisionId`, `subject`, `previewText`, `fromName`, `fromEmail`, `replyToEmail`, `lmx`. Supply `expectedRevisionId` matching the current `contentRevisionId` to avoid 409 conflicts. | + +#### Example + +```php +$result = $loops->emailMessages->update( + email_message_id: 'msg_123', + fields: [ + 'expectedRevisionId' => 'rev_123', + 'subject' => 'Updated subject', + 'lmx' => 'Hello' + ] +); +``` + +--- + ## Testing ```bash diff --git a/src/Campaigns.php b/src/Campaigns.php new file mode 100644 index 0000000..e4d863c --- /dev/null +++ b/src/Campaigns.php @@ -0,0 +1,49 @@ +client = $client; + } + + public function list(?int $per_page = null, ?string $cursor = null): mixed + { + $query = []; + if ($per_page !== null) { + $query['perPage'] = $per_page; + } + if ($cursor) { + $query['cursor'] = $cursor; + } + + return $this->client->query(method: 'GET', endpoint: 'v1/campaigns', options: [ + 'query' => $query + ]); + } + + public function create(string $name): mixed + { + return $this->client->query(method: 'POST', endpoint: 'v1/campaigns', options: [ + 'json' => ['name' => $name] + ]); + } + + public function get(string $campaign_id): mixed + { + return $this->client->query(method: 'GET', endpoint: 'v1/campaigns/' . $campaign_id); + } + + public function update(string $campaign_id, string $name): mixed + { + return $this->client->query(method: 'POST', endpoint: 'v1/campaigns/' . $campaign_id, options: [ + 'json' => ['name' => $name] + ]); + } +} diff --git a/src/Components.php b/src/Components.php new file mode 100644 index 0000000..9f38afa --- /dev/null +++ b/src/Components.php @@ -0,0 +1,35 @@ +client = $client; + } + + public function list(?int $per_page = null, ?string $cursor = null): mixed + { + $query = []; + if ($per_page !== null) { + $query['perPage'] = $per_page; + } + if ($cursor) { + $query['cursor'] = $cursor; + } + + return $this->client->query(method: 'GET', endpoint: 'v1/components', options: [ + 'query' => $query + ]); + } + + public function get(string $component_id): mixed + { + return $this->client->query(method: 'GET', endpoint: 'v1/components/' . $component_id); + } +} diff --git a/src/ContactProperties.php b/src/ContactProperties.php index b6cb2b4..eb45062 100644 --- a/src/ContactProperties.php +++ b/src/ContactProperties.php @@ -24,7 +24,7 @@ public function create(string $name, string $type = 'string' | 'number' | 'boole 'json' => $payload ]); } - public function get(?string $list = null): mixed + public function list(?string $list = null): mixed { $query = []; if ($list) { diff --git a/src/DedicatedSendingIps.php b/src/DedicatedSendingIps.php new file mode 100644 index 0000000..35a4a89 --- /dev/null +++ b/src/DedicatedSendingIps.php @@ -0,0 +1,20 @@ +client = $client; + } + + public function list(): mixed + { + return $this->client->query(method: 'GET', endpoint: 'v1/dedicated-sending-ips'); + } +} diff --git a/src/EmailMessages.php b/src/EmailMessages.php new file mode 100644 index 0000000..6aa99b3 --- /dev/null +++ b/src/EmailMessages.php @@ -0,0 +1,27 @@ +client = $client; + } + + public function get(string $email_message_id): mixed + { + return $this->client->query(method: 'GET', endpoint: 'v1/email-messages/' . $email_message_id); + } + + public function update(string $email_message_id, array $fields = []): mixed + { + return $this->client->query(method: 'POST', endpoint: 'v1/email-messages/' . $email_message_id, options: [ + 'json' => $fields + ]); + } +} diff --git a/src/LoopsClient.php b/src/LoopsClient.php index 09ad506..84c505b 100644 --- a/src/LoopsClient.php +++ b/src/LoopsClient.php @@ -13,6 +13,11 @@ class LoopsClient public MailingLists $mailingLists; public Transactional $transactional; public ContactProperties $contactProperties; + public DedicatedSendingIps $dedicatedSendingIps; + public Themes $themes; + public Components $components; + public Campaigns $campaigns; + public EmailMessages $emailMessages; public function __construct(string $api_key) { @@ -31,6 +36,11 @@ public function __construct(string $api_key) $this->mailingLists = new MailingLists(client: $this); $this->transactional = new Transactional(client: $this); $this->contactProperties = new ContactProperties(client: $this); + $this->dedicatedSendingIps = new DedicatedSendingIps(client: $this); + $this->themes = new Themes(client: $this); + $this->components = new Components(client: $this); + $this->campaigns = new Campaigns(client: $this); + $this->emailMessages = new EmailMessages(client: $this); } /** diff --git a/src/MailingLists.php b/src/MailingLists.php index 505dc5e..d1bf6d9 100644 --- a/src/MailingLists.php +++ b/src/MailingLists.php @@ -13,7 +13,7 @@ public function __construct(LoopsClient $client) $this->client = $client; } - public function get() + public function list() { return $this->client->query(method: 'GET', endpoint: 'v1/lists'); } diff --git a/src/Themes.php b/src/Themes.php new file mode 100644 index 0000000..8c7d6e7 --- /dev/null +++ b/src/Themes.php @@ -0,0 +1,35 @@ +client = $client; + } + + public function list(?int $per_page = null, ?string $cursor = null): mixed + { + $query = []; + if ($per_page !== null) { + $query['perPage'] = $per_page; + } + if ($cursor) { + $query['cursor'] = $cursor; + } + + return $this->client->query(method: 'GET', endpoint: 'v1/themes', options: [ + 'query' => $query + ]); + } + + public function get(string $theme_id): mixed + { + return $this->client->query(method: 'GET', endpoint: 'v1/themes/' . $theme_id); + } +} diff --git a/src/Transactional.php b/src/Transactional.php index c46b64b..b7220d5 100644 --- a/src/Transactional.php +++ b/src/Transactional.php @@ -35,7 +35,7 @@ public function send( ]); } - public function get(?int $per_page = 20, ?string $cursor = null): mixed + public function list(?int $per_page = 20, ?string $cursor = null): mixed { $query = [ diff --git a/tests/CampaignsTest.php b/tests/CampaignsTest.php new file mode 100644 index 0000000..39ee98d --- /dev/null +++ b/tests/CampaignsTest.php @@ -0,0 +1,130 @@ +mockHttpClient = $this->createMock(\GuzzleHttp\Client::class); + $this->client = new LoopsClient('test_api_key'); + $this->client->setHttpClient($this->mockHttpClient); + } + + public function testListCampaigns(): void + { + $this->mockHttpClient + ->expects($this->once()) + ->method('get') + ->with('v1/campaigns') + ->willReturn(new Response( + status: 200, + body: json_encode([ + 'success' => true, + 'pagination' => ['nextCursor' => null], + 'data' => [] + ]) + )); + + $result = $this->client->campaigns->list(); + + $this->assertTrue($result['success']); + } + + public function testCreateCampaign(): void + { + $this->mockHttpClient + ->expects($this->once()) + ->method('post') + ->with( + 'v1/campaigns', + $this->callback(function ($options) { + return $options['json']['name'] === 'Spring announcement'; + }) + ) + ->willReturn(new Response( + status: 201, + body: json_encode([ + 'success' => true, + 'campaignId' => 'camp_123', + 'name' => 'Spring announcement', + 'status' => 'Draft', + 'createdAt' => '2025-01-01T00:00:00.000Z', + 'updatedAt' => '2025-01-01T00:00:00.000Z', + 'emailMessageId' => 'msg_123', + 'emailMessageContentRevisionId' => 'rev_123' + ]) + )); + + $result = $this->client->campaigns->create(name: 'Spring announcement'); + + $this->assertEquals('camp_123', $result['campaignId']); + } + + public function testFindCampaign(): void + { + $campaignId = 'camp_123'; + + $this->mockHttpClient + ->expects($this->once()) + ->method('get') + ->with('v1/campaigns/' . $campaignId) + ->willReturn(new Response( + status: 200, + body: json_encode([ + 'success' => true, + 'campaignId' => $campaignId, + 'name' => 'Spring announcement', + 'status' => 'Draft', + 'createdAt' => '2025-01-01T00:00:00.000Z', + 'updatedAt' => '2025-01-01T00:00:00.000Z', + 'emailMessageId' => 'msg_123' + ]) + )); + + $result = $this->client->campaigns->get(campaign_id: $campaignId); + + $this->assertEquals($campaignId, $result['campaignId']); + } + + public function testUpdateCampaign(): void + { + $campaignId = 'camp_123'; + + $this->mockHttpClient + ->expects($this->once()) + ->method('post') + ->with( + 'v1/campaigns/' . $campaignId, + $this->callback(function ($options) { + return $options['json']['name'] === 'Updated name'; + }) + ) + ->willReturn(new Response( + status: 200, + body: json_encode([ + 'success' => true, + 'campaignId' => $campaignId, + 'name' => 'Updated name', + 'status' => 'Draft', + 'createdAt' => '2025-01-01T00:00:00.000Z', + 'updatedAt' => '2025-01-02T00:00:00.000Z', + 'emailMessageId' => 'msg_123' + ]) + )); + + $result = $this->client->campaigns->update( + campaign_id: $campaignId, + name: 'Updated name' + ); + + $this->assertEquals('Updated name', $result['name']); + } +} diff --git a/tests/ComponentsTest.php b/tests/ComponentsTest.php new file mode 100644 index 0000000..b5f5ce0 --- /dev/null +++ b/tests/ComponentsTest.php @@ -0,0 +1,65 @@ +mockHttpClient = $this->createMock(\GuzzleHttp\Client::class); + $this->client = new LoopsClient('test_api_key'); + $this->client->setHttpClient($this->mockHttpClient); + } + + public function testListComponents(): void + { + $this->mockHttpClient + ->expects($this->once()) + ->method('get') + ->with('v1/components', $this->callback(function ($options) { + return $options['query'] === []; + })) + ->willReturn(new Response( + status: 200, + body: json_encode([ + 'success' => true, + 'pagination' => ['nextCursor' => null], + 'data' => [] + ]) + )); + + $result = $this->client->components->list(); + + $this->assertTrue($result['success']); + } + + public function testFindComponent(): void + { + $componentId = 'component_abc123'; + + $this->mockHttpClient + ->expects($this->once()) + ->method('get') + ->with('v1/components/' . $componentId) + ->willReturn(new Response( + status: 200, + body: json_encode([ + 'success' => true, + 'componentId' => $componentId, + 'name' => 'Header', + 'lmx' => '
' + ]) + )); + + $result = $this->client->components->get(component_id: $componentId); + + $this->assertEquals($componentId, $result['componentId']); + } +} diff --git a/tests/DedicatedSendingIpsTest.php b/tests/DedicatedSendingIpsTest.php new file mode 100644 index 0000000..833106d --- /dev/null +++ b/tests/DedicatedSendingIpsTest.php @@ -0,0 +1,36 @@ +mockHttpClient = $this->createMock(\GuzzleHttp\Client::class); + $this->client = new LoopsClient('test_api_key'); + $this->client->setHttpClient($this->mockHttpClient); + } + + public function testGetDedicatedSendingIps(): void + { + $this->mockHttpClient + ->expects($this->once()) + ->method('get') + ->with('v1/dedicated-sending-ips') + ->willReturn(new Response( + status: 200, + body: json_encode(['1.2.3.4', '5.6.7.8']) + )); + + $result = $this->client->dedicatedSendingIps->list(); + + $this->assertEquals(['1.2.3.4', '5.6.7.8'], $result); + } +} diff --git a/tests/EmailMessagesTest.php b/tests/EmailMessagesTest.php new file mode 100644 index 0000000..028db8c --- /dev/null +++ b/tests/EmailMessagesTest.php @@ -0,0 +1,94 @@ +mockHttpClient = $this->createMock(\GuzzleHttp\Client::class); + $this->client = new LoopsClient('test_api_key'); + $this->client->setHttpClient($this->mockHttpClient); + } + + public function testFindEmailMessage(): void + { + $emailMessageId = 'msg_123'; + + $this->mockHttpClient + ->expects($this->once()) + ->method('get') + ->with('v1/email-messages/' . $emailMessageId) + ->willReturn(new Response( + status: 200, + body: json_encode([ + 'success' => true, + 'emailMessageId' => $emailMessageId, + 'campaignId' => 'camp_123', + 'subject' => 'Hello', + 'previewText' => '', + 'fromName' => 'Loops', + 'fromEmail' => 'hello', + 'replyToEmail' => '', + 'lmx' => '', + 'contentRevisionId' => 'rev_123', + 'updatedAt' => '2025-01-01T00:00:00.000Z' + ]) + )); + + $result = $this->client->emailMessages->get(email_message_id: $emailMessageId); + + $this->assertEquals($emailMessageId, $result['emailMessageId']); + } + + public function testUpdateEmailMessage(): void + { + $emailMessageId = 'msg_123'; + $fields = [ + 'expectedRevisionId' => 'rev_123', + 'subject' => 'Updated subject', + 'lmx' => 'Hello' + ]; + + $this->mockHttpClient + ->expects($this->once()) + ->method('post') + ->with( + 'v1/email-messages/' . $emailMessageId, + $this->callback(function ($options) use ($fields) { + return $options['json'] === $fields; + }) + ) + ->willReturn(new Response( + status: 200, + body: json_encode([ + 'success' => true, + 'emailMessageId' => $emailMessageId, + 'campaignId' => 'camp_123', + 'subject' => 'Updated subject', + 'previewText' => '', + 'fromName' => 'Loops', + 'fromEmail' => 'hello', + 'replyToEmail' => '', + 'lmx' => $fields['lmx'], + 'contentRevisionId' => 'rev_456', + 'updatedAt' => '2025-01-02T00:00:00.000Z' + ]) + )); + + $result = $this->client->emailMessages->update( + email_message_id: $emailMessageId, + fields: $fields + ); + + $this->assertEquals('Updated subject', $result['subject']); + $this->assertEquals('rev_456', $result['contentRevisionId']); + } +} diff --git a/tests/ThemesTest.php b/tests/ThemesTest.php new file mode 100644 index 0000000..b4995b6 --- /dev/null +++ b/tests/ThemesTest.php @@ -0,0 +1,72 @@ +mockHttpClient = $this->createMock(\GuzzleHttp\Client::class); + $this->client = new LoopsClient('test_api_key'); + $this->client->setHttpClient($this->mockHttpClient); + } + + public function testListThemes(): void + { + $this->mockHttpClient + ->expects($this->once()) + ->method('get') + ->with( + 'v1/themes', + $this->callback(function ($options) { + return $options['query']['perPage'] === 20 + && $options['query']['cursor'] === 'cursor123'; + }) + ) + ->willReturn(new Response( + status: 200, + body: json_encode([ + 'success' => true, + 'pagination' => ['nextCursor' => null], + 'data' => [] + ]) + )); + + $result = $this->client->themes->list(per_page: 20, cursor: 'cursor123'); + + $this->assertTrue($result['success']); + } + + public function testFindTheme(): void + { + $themeId = 'theme_abc123'; + + $this->mockHttpClient + ->expects($this->once()) + ->method('get') + ->with('v1/themes/' . $themeId) + ->willReturn(new Response( + status: 200, + body: json_encode([ + 'success' => true, + 'themeId' => $themeId, + 'name' => 'Default', + 'styles' => [], + 'isDefault' => true, + 'createdAt' => '2025-01-01T00:00:00.000Z', + 'updatedAt' => '2025-01-01T00:00:00.000Z' + ]) + )); + + $result = $this->client->themes->get(theme_id: $themeId); + + $this->assertEquals($themeId, $result['themeId']); + } +} diff --git a/tests/TransactionalTest.php b/tests/TransactionalTest.php index 90bd220..6121b52 100644 --- a/tests/TransactionalTest.php +++ b/tests/TransactionalTest.php @@ -146,7 +146,7 @@ public function testGetTransactionals(): void )); // Make the API call - $result = $this->client->transactional->get( + $result = $this->client->transactional->list( per_page: $per_page, cursor: $cursor );