Skip to content

Commit

Permalink
Add methods to manipulate shared slices
Browse files Browse the repository at this point in the history
  • Loading branch information
gsteel committed Oct 30, 2024
1 parent a7702c3 commit 4e94c1c
Show file tree
Hide file tree
Showing 25 changed files with 656 additions and 26 deletions.
8 changes: 8 additions & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -1,3 +1,11 @@
#
# *.http files in /test/ are response fixtures and should have CRLF line endings
#
/test/Integration/responses/*.http text eol=crlf

#
# Ignores
#
/.cache/ export-ignore
/.github/ export-ignore
/.laminas-ci/ export-ignore
Expand Down
103 changes: 102 additions & 1 deletion src/BaseClient.php
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@

use function sprintf;

final class BaseClient implements Client
final class BaseClient implements Client, SharedSliceManagementClient
{
private const DEFAULT_BASE_URI = 'https://customtypes.prismic.io';

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

return $response;
}

/** @inheritDoc */
public function fetchAllSharedSlices(): iterable
{
$response = $this->send(
$this->request('GET', '/slices'),
);

$body = Json::decodeToArray((string) $response->getBody());
$list = [];
foreach ($body as $item) {
Assert::isArray($item);

Check warning on line 202 in src/BaseClient.php

View workflow job for this annotation

GitHub Actions / Mutation Tests

Escaped Mutant for Mutator "MethodCallRemoval": --- Original +++ New @@ @@ $body = Json::decodeToArray((string) $response->getBody()); $list = []; foreach ($body as $item) { - Assert::isArray($item); + $definition = SharedSlice::fromArray($item); $list[$definition->id] = $definition; }

Check warning on line 202 in src/BaseClient.php

View workflow job for this annotation

GitHub Actions / Mutation Tests

Escaped Mutant for Mutator "MethodCallRemoval": --- Original +++ New @@ @@ $body = Json::decodeToArray((string) $response->getBody()); $list = []; foreach ($body as $item) { - Assert::isArray($item); + $definition = SharedSlice::fromArray($item); $list[$definition->id] = $definition; }
$definition = SharedSlice::fromArray($item);
$list[$definition->id] = $definition;
}

return $list;
}

public function createSharedSlice(SharedSlice $definition): void
{
$request = $this->request('POST', '/slices/insert')
->withHeader('Content-Type', 'application/json')
->withBody($this->streamFactory->createStream($definition->json));

$response = $this->send($request);

if ($response->getStatusCode() === 400) {
throw InvalidDefinition::invalidSlice($request, $response);
}

if ($response->getStatusCode() === 409) {
throw InsertFailed::forSlice($definition, $request, $response);
}

if ($response->getStatusCode() !== 201) {
throw UnexpectedStatusCode::withExpectedCode(201, $request, $response);
}
}

public function updateSharedSlice(SharedSlice $definition): void
{
$request = $this->request('POST', '/slices/update')
->withHeader('Content-Type', 'application/json')
->withBody($this->streamFactory->createStream($definition->json));

$response = $this->send($request);

if ($response->getStatusCode() === 400) {
throw InvalidDefinition::invalidSlice($request, $response);
}

if ($response->getStatusCode() === 422) {
throw UpdateFailed::forSlice($definition, $request, $response);
}

if ($response->getStatusCode() !== 204) {
throw UnexpectedStatusCode::withExpectedCode(204, $request, $response);
}
}

public function saveSharedSlice(SharedSlice $slice): void
{
try {
$current = $this->getSharedSlice($slice->id);
} catch (DefinitionNotFound) {
$this->createSharedSlice($slice);

Check warning on line 257 in src/BaseClient.php

View workflow job for this annotation

GitHub Actions / Mutation Tests

Escaped Mutant for Mutator "MethodCallRemoval": --- Original +++ New @@ @@ try { $current = $this->getSharedSlice($slice->id); } catch (DefinitionNotFound) { - $this->createSharedSlice($slice); + return; } if ($slice->equals($current)) {

Check warning on line 257 in src/BaseClient.php

View workflow job for this annotation

GitHub Actions / Mutation Tests

Escaped Mutant for Mutator "MethodCallRemoval": --- Original +++ New @@ @@ try { $current = $this->getSharedSlice($slice->id); } catch (DefinitionNotFound) { - $this->createSharedSlice($slice); + return; } if ($slice->equals($current)) {

return;
}

if ($slice->equals($current)) {
return;
}

$this->updateSharedSlice($slice);
}

public function getSharedSlice(string $id): SharedSlice
{
$request = $this->request('GET', sprintf('/slices/%s', $id));
$response = $this->send($request);

if ($response->getStatusCode() === 404) {
throw DefinitionNotFound::forSharedSlice($id, $request, $response);
}

return SharedSlice::fromArray(
Json::decodeToArray((string) $response->getBody()),
);
}

public function deleteSharedSlice(string $id): void
{
$request = $this->request('DELETE', sprintf('/slices/%s', $id));
$response = $this->send($request);

if ($response->getStatusCode() !== 204) {
throw UnexpectedStatusCode::withExpectedCode(204, $request, $response);
}
}
}
4 changes: 4 additions & 0 deletions src/Client.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ interface Client
/**
* Retrieve a document type definition by its identifier
*
* @param non-empty-string $id
*
* @throws DefinitionNotFound if there is no such type with the given id.
* @throws Exception if any errors occur communicating with the remote API.
*/
Expand All @@ -37,6 +39,8 @@ public function fetchAllDefinitions(): iterable;
/**
* Deletes the definition with the given identifier
*
* @param non-empty-string $id
*
* @throws Exception if any errors occur communicating with the remote API.
*/
public function deleteDefinition(string $id): void;
Expand Down
11 changes: 11 additions & 0 deletions src/Definition.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,10 @@
/** @psalm-immutable */
final class Definition implements JsonSerializable
{
/**
* @param non-empty-string $id
* @param non-empty-string $json
*/
private function __construct(
private string $id,
private string $label,
Expand All @@ -19,6 +23,10 @@ private function __construct(
) {
}

/**
* @param non-empty-string $id
* @param non-empty-string $json
*/
public static function new(
string $id,
string $label,
Expand Down Expand Up @@ -66,6 +74,7 @@ public function jsonSerialize(): array
];
}

/** @return non-empty-string */
public function id(): string
{
return $this->id;
Expand All @@ -86,11 +95,13 @@ public function isActive(): bool
return $this->active;
}

/** @return non-empty-string */
public function json(): string
{
return $this->json;
}

/** @param non-empty-string $json */
public function withAlteredPayload(string $json): self
{
$clone = clone $this;
Expand Down
12 changes: 12 additions & 0 deletions src/Exception/DefinitionNotFound.php
Original file line number Diff line number Diff line change
Expand Up @@ -22,4 +22,16 @@ public static function withIdentifier(
$response,
);
}

public static function forSharedSlice(
string $id,
RequestInterface $request,
ResponseInterface $response,
): self {
return self::withHttpExchange(
sprintf('A shared slice with the id "%s" cannot be found', $id),
$request,
$response,
);
}
}
16 changes: 16 additions & 0 deletions src/Exception/InsertFailed.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
namespace Prismic\DocumentType\Exception;

use Prismic\DocumentType\Definition;
use Prismic\DocumentType\SharedSlice;
use Psr\Http\Message\RequestInterface;
use Psr\Http\Message\ResponseInterface;

Expand All @@ -26,4 +27,19 @@ public static function withDefinition(
$response,
);
}

public static function forSlice(
SharedSlice $definition,
RequestInterface $request,
ResponseInterface $response,
): self {
return self::withHttpExchange(
sprintf(
'Failed to insert the shared slice "%s" because one already exists with that identifier',
$definition->id,
),
$request,
$response,
);
}
}
9 changes: 9 additions & 0 deletions src/Exception/InvalidDefinition.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,13 @@ public static function new(RequestInterface $request, ResponseInterface $respons
$response,
);
}

public static function invalidSlice(RequestInterface $request, ResponseInterface $response): self
{
return self::withHttpExchange(
'The slice definition was rejected because it (most likely) has validation errors',
$request,
$response,
);
}
}
16 changes: 16 additions & 0 deletions src/Exception/UpdateFailed.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
namespace Prismic\DocumentType\Exception;

use Prismic\DocumentType\Definition;
use Prismic\DocumentType\SharedSlice;
use Psr\Http\Message\RequestInterface;
use Psr\Http\Message\ResponseInterface;

Expand All @@ -26,4 +27,19 @@ public static function withDefinition(
$response,
);
}

public static function forSlice(
SharedSlice $definition,
RequestInterface $request,
ResponseInterface $response,
): self {
return self::withHttpExchange(
sprintf(
'Failed to update the shared slice "%s" because it has not yet been created',
$definition->id,
),
$request,
$response,
);
}
}
6 changes: 5 additions & 1 deletion src/Json.php
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,11 @@ public static function decodeToArray(string $json): array
}
}

/** @param array<array-key, mixed> $parameters */
/**
* @param array<array-key, mixed> $parameters
*
* @return non-empty-string
*/
public static function encodeArray(array $parameters): string
{
try {
Expand Down
44 changes: 44 additions & 0 deletions src/SharedSlice.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
<?php

declare(strict_types=1);

namespace Prismic\DocumentType;

use function json_encode;

final class SharedSlice
{
/**
* @param non-empty-string $id
* @param non-empty-string $json
*/
private function __construct(
public readonly string $id,
public readonly string $json,
) {
}

/**
* @param non-empty-string $id
* @param non-empty-string $json
*/
public static function new(string $id, string $json): self
{
return new self($id, $json);
}

/** @param array<array-key, mixed> $payload */
public static function fromArray(array $payload): self
{
Assert::keyExists($payload, 'id');

Check warning on line 33 in src/SharedSlice.php

View workflow job for this annotation

GitHub Actions / Mutation Tests

Escaped Mutant for Mutator "MethodCallRemoval": --- Original +++ New @@ @@ /** @param array<array-key, mixed> $payload */ public static function fromArray(array $payload) : self { - Assert::keyExists($payload, 'id'); + Assert::stringNotEmpty($payload['id']); return new self($payload['id'], json_encode($payload)); }

Check warning on line 33 in src/SharedSlice.php

View workflow job for this annotation

GitHub Actions / Mutation Tests

Escaped Mutant for Mutator "MethodCallRemoval": --- Original +++ New @@ @@ /** @param array<array-key, mixed> $payload */ public static function fromArray(array $payload) : self { - Assert::keyExists($payload, 'id'); + Assert::stringNotEmpty($payload['id']); return new self($payload['id'], json_encode($payload)); }
Assert::stringNotEmpty($payload['id']);

Check warning on line 34 in src/SharedSlice.php

View workflow job for this annotation

GitHub Actions / Mutation Tests

Escaped Mutant for Mutator "MethodCallRemoval": --- Original +++ New @@ @@ public static function fromArray(array $payload) : self { Assert::keyExists($payload, 'id'); - Assert::stringNotEmpty($payload['id']); + return new self($payload['id'], json_encode($payload)); } public function equals(SharedSlice $other) : bool

Check warning on line 34 in src/SharedSlice.php

View workflow job for this annotation

GitHub Actions / Mutation Tests

Escaped Mutant for Mutator "MethodCallRemoval": --- Original +++ New @@ @@ public static function fromArray(array $payload) : self { Assert::keyExists($payload, 'id'); - Assert::stringNotEmpty($payload['id']); + return new self($payload['id'], json_encode($payload)); } public function equals(SharedSlice $other) : bool

return new self($payload['id'], json_encode($payload));
}

public function equals(SharedSlice $other): bool
{
return $this->id === $other->id
&& $this->json === $other->json;
}
}
45 changes: 45 additions & 0 deletions src/SharedSliceManagementClient.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
<?php

declare(strict_types=1);

namespace Prismic\DocumentType;

use Countable;
use Prismic\DocumentType\Exception\DefinitionNotFound;
use Prismic\DocumentType\Exception\Exception;

interface SharedSliceManagementClient
{
/**
* Return a list of the Shared Slices found in the remote repo
*
* @return iterable<string, SharedSlice>&Countable
*/
public function fetchAllSharedSlices(): iterable;

/**
* Insert or update a shared slice
*
* @throws Exception
*/
public function saveSharedSlice(SharedSlice $slice): void;

/**
* Fetch a shared slice
*
* @param non-empty-string $id
*
* @throws DefinitionNotFound if there is no such type with the given id.
* @throws Exception if any errors occur communicating with the remote API.
*/
public function getSharedSlice(string $id): SharedSlice;

/**
* Deletes the shared slice with the given identifier
*
* @param non-empty-string $id
*
* @throws Exception if any errors occur communicating with the remote API.
*/
public function deleteSharedSlice(string $id): void;
}
Loading

0 comments on commit 4e94c1c

Please sign in to comment.