Skip to content

Commit

Permalink
Reimplement getListChunkIterator() as a generator function that uses …
Browse files Browse the repository at this point in the history
…the fetch (/contactIds) endpoint instead of the legacy (/contacts) endpoint

AUT-3246

Co-authored-by: Bence Kadar <[email protected]>
  • Loading branch information
fqqdk and bencekadaremar committed Sep 19, 2024
1 parent 1ffec2b commit 5653bba
Show file tree
Hide file tree
Showing 6 changed files with 101 additions and 12 deletions.
20 changes: 11 additions & 9 deletions src/Suite/Api/ContactList.php
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ public function getContactsOfList(int $customerId, int $contactListId, int $limi
}
}

public function getContactIdsInList(int $customerId, int $contactListId, int $limit = null, int $skipToken = null)
public function getContactIdsInList(int $customerId, int $contactListId, int $limit = null, string $skipToken = null)
{
try {
$response = $this->apiClient->get($this->endPoints->contactIdsInList($customerId, $contactListId, $limit, $skipToken));
Expand All @@ -129,14 +129,16 @@ public function getContactIdsInList(int $customerId, int $contactListId, int $li
}
}

/**
* @param int $customerId
* @param int $contactListId
* @param int $chunkSize
* @return Traversable
*/
public function getListChunkIterator(int $customerId, int $contactListId, int $chunkSize = 10000) : Traversable
public function getListChunkIterator(int $customerId, int $contactListId, int $chunkSize = 10000) : iterable
{
return new ContactListChunkIterator($this, $customerId, $contactListId, $chunkSize);
$next = $this->endPoints->contactIdsInList($customerId, $contactListId, $chunkSize, 'first batch');
try {
do {
['value' => $value, 'next' => $next] = $this->apiClient->get($next)['data'];
yield $value;
} while ($next !== null);
} catch (Error $error) {
throw new RequestFailed('Could not fetch contact ids: ' . $error->getMessage(), $error->getCode(), $error);
}
}
}
2 changes: 1 addition & 1 deletion src/Suite/Api/ContactListEndPoints.php
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ public function contactsOfList(int $customerId, int $contactListId, int $limit,
return $this->baseUrl($customerId) . "/{$contactListId}/contacts/?limit={$limit}&offset={$offset}";
}

public function contactIdsInList(int $customerId, int $contactListId, int $limit = null, int $skipToken = null)
public function contactIdsInList(int $customerId, int $contactListId, int $limit = null, string $skipToken = null)
{
$result = $this->baseUrl($customerId) . "/{$contactListId}/contactIds";
if (null !== $limit && null !== $skipToken) {
Expand Down
5 changes: 5 additions & 0 deletions src/Suite/Api/Factory.php
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,11 @@ public function createContact()
return new Contact($this->apiClient, new ContactEndPoints($this->apiBaseUrl));
}

public function createContactList()
{
return new ContactList($this->apiClient, new ContactListEndPoints($this->apiBaseUrl));
}

public function createPreview()
{
return new Preview($this->apiClient, new CampaignEndPoints($this->apiBaseUrl));
Expand Down
57 changes: 57 additions & 0 deletions test/acceptance/ContactListTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
<?php

namespace Suite\Api\Acceptance;

use Suite\Api\RequestFailed;
use Suite\Api\Test\Helper\AcceptanceBaseTestCase;
use Suite\Api\Test\Helper\ApiStub;

class ContactListChunkIteratorTest extends AcceptanceBaseTestCase
{
protected int $customerId = 123456;

/**
* @test
*/
public function getListChunkIterator_EmptyContactList_ReturnsSingleEmptyChunk(): void
{
$this->assertEquals([[]], $this->getChunksOfContactList(ApiStub::LIST_ID_FOR_EMPTY_LIST));
}

/**
* @test
*/
public function getListChunkIterator_ContactListContainsSingleChunk_ReturnsContactIds(): void
{
$this->assertEquals([[1, 2, 3]], $this->getChunksOfContactList(ApiStub::LIST_ID_FOR_LIST_WITH_SINGLE_CHUNK));
}


/**
* @test
*/
public function getListChunkIterator_SeveralChunks_ReturnsContactIdsInChunks(): void
{
$this->assertEquals([
[1, 2, 3],
[4, 5, 6],
[7, 8, 9],
[10, 11],
], $this->getChunksOfContactList(ApiStub::LIST_ID_FOR_LIST_WITH_MULTIPLE_CHUNKS));
}


/**
* @test
*/
public function getListChunkIterator_ApiRequestFailed_ThrowsException(): void
{
$this->expectException(RequestFailed::class);
$this->getChunksOfContactList(ApiStub::LIST_ID_FOR_WRONG_RESPONSE);
}

private function getChunksOfContactList(int $contactListId): array
{
return iterator_to_array($this->factory->createContactList()->getListChunkIterator($this->customerId, $contactListId));
}
}
20 changes: 20 additions & 0 deletions test/helper/ApiStub.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,11 @@

class ApiStub
{
public const LIST_ID_FOR_EMPTY_LIST = 123;
public const LIST_ID_FOR_LIST_WITH_SINGLE_CHUNK = 456;
public const LIST_ID_FOR_LIST_WITH_MULTIPLE_CHUNKS = 789;
public const LIST_ID_FOR_WRONG_RESPONSE = 666;

public static function setUp()
{
$app = new Application();
Expand Down Expand Up @@ -47,6 +52,21 @@ public static function setUp()
return new Response(self::success(self::getRetryCount()));
});

$app->get('/{customerId}/contactlist/{contactListId}/contactIds', function (Request $request, $contactListId, $customerId) {
return match ($contactListId) {
(string) self::LIST_ID_FOR_EMPTY_LIST => new Response(self::success('{"value":[],"next":null}')),
(string) self::LIST_ID_FOR_LIST_WITH_SINGLE_CHUNK => new Response(self::success('{"value":[1,2,3],"next":null}')),
(string) self::LIST_ID_FOR_LIST_WITH_MULTIPLE_CHUNKS => match ($request->query->get('$skiptoken')) {
'first batch' => new Response(self::success('{"value":[1,2,3],"next":"http://localhost:7984/'.$customerId.'/contactlist/'.$contactListId.'/contactIds?$skiptoken=second%20batch"}')),
'second batch' => new Response(self::success('{"value":[4,5,6],"next":"http://localhost:7984/'.$customerId.'/contactlist/'.$contactListId.'/contactIds?$skiptoken=third%20batch"}')),
'third batch' => new Response(self::success('{"value":[7,8,9],"next":"http://localhost:7984/'.$customerId.'/contactlist/'.$contactListId.'/contactIds?$skiptoken=fourth%20batch"}')),
'fourth batch' => new Response(self::success('{"value":[10,11],"next":null}')),
},
(string) self::LIST_ID_FOR_WRONG_RESPONSE => new Response(self::error('invalid response format')),
default => new Response(self::error('contact list not found'), 404),
};
});

return $app;
}

Expand Down
9 changes: 7 additions & 2 deletions test/unit/Suite/Api/ContactListTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -329,11 +329,16 @@ public function getContactsOfList_ApiCallFails_ExceptionThrown(): void
/**
* @test
*/
public function getContactListChunkIterator_Perfect_Perfect(): void
public function getContactListChunkIterator_ListFitsInSingleChunk_ContactIdsReturned(): void
{
$chunkSize = 3;
$this->apiClient->expects($this->once())->method('get')
->with("api_base_url/$this->customerId/contactlist/654321/contactIds?\$top=3&\$skiptoken=first batch")
->willReturn(
$this->apiSuccess(['value' => [1, 2, 3], 'next' => null])
);
$iterator = $this->listService->getListChunkIterator($this->customerId, $this->contactListId, $chunkSize);
$this->assertInstanceOf(\Traversable::class, $iterator);
$this->assertEquals([[1, 2, 3]], iterator_to_array($iterator));
}

/**
Expand Down

0 comments on commit 5653bba

Please sign in to comment.