Skip to content

Commit 40d6433

Browse files
authored
Merge pull request #143 from netglue/slices
Add methods to manipulate shared slices
2 parents a7702c3 + 4e94c1c commit 40d6433

25 files changed

+656
-26
lines changed

.gitattributes

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,11 @@
1+
#
2+
# *.http files in /test/ are response fixtures and should have CRLF line endings
3+
#
4+
/test/Integration/responses/*.http text eol=crlf
5+
6+
#
7+
# Ignores
8+
#
19
/.cache/ export-ignore
210
/.github/ export-ignore
311
/.laminas-ci/ export-ignore

src/BaseClient.php

Lines changed: 102 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121

2222
use function sprintf;
2323

24-
final class BaseClient implements Client
24+
final class BaseClient implements Client, SharedSliceManagementClient
2525
{
2626
private const DEFAULT_BASE_URI = 'https://customtypes.prismic.io';
2727

@@ -188,4 +188,105 @@ private function send(RequestInterface $request): ResponseInterface
188188

189189
return $response;
190190
}
191+
192+
/** @inheritDoc */
193+
public function fetchAllSharedSlices(): iterable
194+
{
195+
$response = $this->send(
196+
$this->request('GET', '/slices'),
197+
);
198+
199+
$body = Json::decodeToArray((string) $response->getBody());
200+
$list = [];
201+
foreach ($body as $item) {
202+
Assert::isArray($item);
203+
$definition = SharedSlice::fromArray($item);
204+
$list[$definition->id] = $definition;
205+
}
206+
207+
return $list;
208+
}
209+
210+
public function createSharedSlice(SharedSlice $definition): void
211+
{
212+
$request = $this->request('POST', '/slices/insert')
213+
->withHeader('Content-Type', 'application/json')
214+
->withBody($this->streamFactory->createStream($definition->json));
215+
216+
$response = $this->send($request);
217+
218+
if ($response->getStatusCode() === 400) {
219+
throw InvalidDefinition::invalidSlice($request, $response);
220+
}
221+
222+
if ($response->getStatusCode() === 409) {
223+
throw InsertFailed::forSlice($definition, $request, $response);
224+
}
225+
226+
if ($response->getStatusCode() !== 201) {
227+
throw UnexpectedStatusCode::withExpectedCode(201, $request, $response);
228+
}
229+
}
230+
231+
public function updateSharedSlice(SharedSlice $definition): void
232+
{
233+
$request = $this->request('POST', '/slices/update')
234+
->withHeader('Content-Type', 'application/json')
235+
->withBody($this->streamFactory->createStream($definition->json));
236+
237+
$response = $this->send($request);
238+
239+
if ($response->getStatusCode() === 400) {
240+
throw InvalidDefinition::invalidSlice($request, $response);
241+
}
242+
243+
if ($response->getStatusCode() === 422) {
244+
throw UpdateFailed::forSlice($definition, $request, $response);
245+
}
246+
247+
if ($response->getStatusCode() !== 204) {
248+
throw UnexpectedStatusCode::withExpectedCode(204, $request, $response);
249+
}
250+
}
251+
252+
public function saveSharedSlice(SharedSlice $slice): void
253+
{
254+
try {
255+
$current = $this->getSharedSlice($slice->id);
256+
} catch (DefinitionNotFound) {
257+
$this->createSharedSlice($slice);
258+
259+
return;
260+
}
261+
262+
if ($slice->equals($current)) {
263+
return;
264+
}
265+
266+
$this->updateSharedSlice($slice);
267+
}
268+
269+
public function getSharedSlice(string $id): SharedSlice
270+
{
271+
$request = $this->request('GET', sprintf('/slices/%s', $id));
272+
$response = $this->send($request);
273+
274+
if ($response->getStatusCode() === 404) {
275+
throw DefinitionNotFound::forSharedSlice($id, $request, $response);
276+
}
277+
278+
return SharedSlice::fromArray(
279+
Json::decodeToArray((string) $response->getBody()),
280+
);
281+
}
282+
283+
public function deleteSharedSlice(string $id): void
284+
{
285+
$request = $this->request('DELETE', sprintf('/slices/%s', $id));
286+
$response = $this->send($request);
287+
288+
if ($response->getStatusCode() !== 204) {
289+
throw UnexpectedStatusCode::withExpectedCode(204, $request, $response);
290+
}
291+
}
191292
}

src/Client.php

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ interface Client
1313
/**
1414
* Retrieve a document type definition by its identifier
1515
*
16+
* @param non-empty-string $id
17+
*
1618
* @throws DefinitionNotFound if there is no such type with the given id.
1719
* @throws Exception if any errors occur communicating with the remote API.
1820
*/
@@ -37,6 +39,8 @@ public function fetchAllDefinitions(): iterable;
3739
/**
3840
* Deletes the definition with the given identifier
3941
*
42+
* @param non-empty-string $id
43+
*
4044
* @throws Exception if any errors occur communicating with the remote API.
4145
*/
4246
public function deleteDefinition(string $id): void;

src/Definition.php

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,10 @@
1010
/** @psalm-immutable */
1111
final class Definition implements JsonSerializable
1212
{
13+
/**
14+
* @param non-empty-string $id
15+
* @param non-empty-string $json
16+
*/
1317
private function __construct(
1418
private string $id,
1519
private string $label,
@@ -19,6 +23,10 @@ private function __construct(
1923
) {
2024
}
2125

26+
/**
27+
* @param non-empty-string $id
28+
* @param non-empty-string $json
29+
*/
2230
public static function new(
2331
string $id,
2432
string $label,
@@ -66,6 +74,7 @@ public function jsonSerialize(): array
6674
];
6775
}
6876

77+
/** @return non-empty-string */
6978
public function id(): string
7079
{
7180
return $this->id;
@@ -86,11 +95,13 @@ public function isActive(): bool
8695
return $this->active;
8796
}
8897

98+
/** @return non-empty-string */
8999
public function json(): string
90100
{
91101
return $this->json;
92102
}
93103

104+
/** @param non-empty-string $json */
94105
public function withAlteredPayload(string $json): self
95106
{
96107
$clone = clone $this;

src/Exception/DefinitionNotFound.php

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,4 +22,16 @@ public static function withIdentifier(
2222
$response,
2323
);
2424
}
25+
26+
public static function forSharedSlice(
27+
string $id,
28+
RequestInterface $request,
29+
ResponseInterface $response,
30+
): self {
31+
return self::withHttpExchange(
32+
sprintf('A shared slice with the id "%s" cannot be found', $id),
33+
$request,
34+
$response,
35+
);
36+
}
2537
}

src/Exception/InsertFailed.php

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
namespace Prismic\DocumentType\Exception;
66

77
use Prismic\DocumentType\Definition;
8+
use Prismic\DocumentType\SharedSlice;
89
use Psr\Http\Message\RequestInterface;
910
use Psr\Http\Message\ResponseInterface;
1011

@@ -26,4 +27,19 @@ public static function withDefinition(
2627
$response,
2728
);
2829
}
30+
31+
public static function forSlice(
32+
SharedSlice $definition,
33+
RequestInterface $request,
34+
ResponseInterface $response,
35+
): self {
36+
return self::withHttpExchange(
37+
sprintf(
38+
'Failed to insert the shared slice "%s" because one already exists with that identifier',
39+
$definition->id,
40+
),
41+
$request,
42+
$response,
43+
);
44+
}
2945
}

src/Exception/InvalidDefinition.php

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,4 +17,13 @@ public static function new(RequestInterface $request, ResponseInterface $respons
1717
$response,
1818
);
1919
}
20+
21+
public static function invalidSlice(RequestInterface $request, ResponseInterface $response): self
22+
{
23+
return self::withHttpExchange(
24+
'The slice definition was rejected because it (most likely) has validation errors',
25+
$request,
26+
$response,
27+
);
28+
}
2029
}

src/Exception/UpdateFailed.php

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
namespace Prismic\DocumentType\Exception;
66

77
use Prismic\DocumentType\Definition;
8+
use Prismic\DocumentType\SharedSlice;
89
use Psr\Http\Message\RequestInterface;
910
use Psr\Http\Message\ResponseInterface;
1011

@@ -26,4 +27,19 @@ public static function withDefinition(
2627
$response,
2728
);
2829
}
30+
31+
public static function forSlice(
32+
SharedSlice $definition,
33+
RequestInterface $request,
34+
ResponseInterface $response,
35+
): self {
36+
return self::withHttpExchange(
37+
sprintf(
38+
'Failed to update the shared slice "%s" because it has not yet been created',
39+
$definition->id,
40+
),
41+
$request,
42+
$response,
43+
);
44+
}
2945
}

src/Json.php

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,11 @@ public static function decodeToArray(string $json): array
3434
}
3535
}
3636

37-
/** @param array<array-key, mixed> $parameters */
37+
/**
38+
* @param array<array-key, mixed> $parameters
39+
*
40+
* @return non-empty-string
41+
*/
3842
public static function encodeArray(array $parameters): string
3943
{
4044
try {

src/SharedSlice.php

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Prismic\DocumentType;
6+
7+
use function json_encode;
8+
9+
final class SharedSlice
10+
{
11+
/**
12+
* @param non-empty-string $id
13+
* @param non-empty-string $json
14+
*/
15+
private function __construct(
16+
public readonly string $id,
17+
public readonly string $json,
18+
) {
19+
}
20+
21+
/**
22+
* @param non-empty-string $id
23+
* @param non-empty-string $json
24+
*/
25+
public static function new(string $id, string $json): self
26+
{
27+
return new self($id, $json);
28+
}
29+
30+
/** @param array<array-key, mixed> $payload */
31+
public static function fromArray(array $payload): self
32+
{
33+
Assert::keyExists($payload, 'id');
34+
Assert::stringNotEmpty($payload['id']);
35+
36+
return new self($payload['id'], json_encode($payload));
37+
}
38+
39+
public function equals(SharedSlice $other): bool
40+
{
41+
return $this->id === $other->id
42+
&& $this->json === $other->json;
43+
}
44+
}

src/SharedSliceManagementClient.php

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Prismic\DocumentType;
6+
7+
use Countable;
8+
use Prismic\DocumentType\Exception\DefinitionNotFound;
9+
use Prismic\DocumentType\Exception\Exception;
10+
11+
interface SharedSliceManagementClient
12+
{
13+
/**
14+
* Return a list of the Shared Slices found in the remote repo
15+
*
16+
* @return iterable<string, SharedSlice>&Countable
17+
*/
18+
public function fetchAllSharedSlices(): iterable;
19+
20+
/**
21+
* Insert or update a shared slice
22+
*
23+
* @throws Exception
24+
*/
25+
public function saveSharedSlice(SharedSlice $slice): void;
26+
27+
/**
28+
* Fetch a shared slice
29+
*
30+
* @param non-empty-string $id
31+
*
32+
* @throws DefinitionNotFound if there is no such type with the given id.
33+
* @throws Exception if any errors occur communicating with the remote API.
34+
*/
35+
public function getSharedSlice(string $id): SharedSlice;
36+
37+
/**
38+
* Deletes the shared slice with the given identifier
39+
*
40+
* @param non-empty-string $id
41+
*
42+
* @throws Exception if any errors occur communicating with the remote API.
43+
*/
44+
public function deleteSharedSlice(string $id): void;
45+
}

0 commit comments

Comments
 (0)