diff --git a/src/Endpoint/Concerns/BuildsMeta.php b/src/Endpoint/Concerns/BuildsMeta.php new file mode 100644 index 0000000..769ecfe --- /dev/null +++ b/src/Endpoint/Concerns/BuildsMeta.php @@ -0,0 +1,29 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Tobyz\JsonApiServer\Endpoint\Concerns; + +use JsonApiPhp\JsonApi\Meta; +use Tobyz\JsonApiServer\Context; + +trait BuildsMeta +{ + private function buildMeta(Context $context): array + { + $meta = []; + + foreach ($context->getMeta() as $item) { + $meta[] = new Meta($item->getName(), $item->getValue()($context)); + } + + return $meta; + } +} diff --git a/src/Endpoint/Create.php b/src/Endpoint/Create.php index 66c493d..43bc626 100644 --- a/src/Endpoint/Create.php +++ b/src/Endpoint/Create.php @@ -13,6 +13,8 @@ namespace Tobyz\JsonApiServer\Endpoint; use Psr\Http\Message\ResponseInterface; use Tobyz\JsonApiServer\Context; +use Tobyz\JsonApiServer\Endpoint\Concerns\BuildsMeta; +use Tobyz\JsonApiServer\Endpoint\Concerns\SavesData; use Tobyz\JsonApiServer\Exception\ForbiddenException; use Tobyz\JsonApiServer\ResourceType; @@ -23,7 +25,7 @@ use function Tobyz\JsonApiServer\set_value; class Create { - use Concerns\SavesData; + use SavesData; /** * @throws ForbiddenException if the resource is not creatable. diff --git a/src/Endpoint/Delete.php b/src/Endpoint/Delete.php index b4f9c40..c07223a 100644 --- a/src/Endpoint/Delete.php +++ b/src/Endpoint/Delete.php @@ -11,17 +11,23 @@ namespace Tobyz\JsonApiServer\Endpoint; +use JsonApiPhp\JsonApi\Meta; +use JsonApiPhp\JsonApi\MetaDocument; use Nyholm\Psr7\Response; use Psr\Http\Message\ResponseInterface; use Tobyz\JsonApiServer\Context; +use Tobyz\JsonApiServer\Endpoint\Concerns\BuildsMeta; use Tobyz\JsonApiServer\Exception\ForbiddenException; use Tobyz\JsonApiServer\ResourceType; use function Tobyz\JsonApiServer\evaluate; +use function Tobyz\JsonApiServer\json_api_response; use function Tobyz\JsonApiServer\run_callbacks; class Delete { + use BuildsMeta; + /** * @throws ForbiddenException if the resource is not deletable. */ @@ -47,6 +53,12 @@ class Delete run_callbacks($schema->getListeners('deleted'), [&$model, $context]); + if (count($meta = $this->buildMeta($context))) { + return json_api_response( + new MetaDocument(...$meta) + ); + } + return new Response(204); } } diff --git a/src/Endpoint/Index.php b/src/Endpoint/Index.php index 631a7e4..0fab39a 100644 --- a/src/Endpoint/Index.php +++ b/src/Endpoint/Index.php @@ -18,6 +18,8 @@ use JsonApiPhp\JsonApi\Link\PrevLink; use Psr\Http\Message\ResponseInterface; use Psr\Http\Message\ServerRequestInterface as Request; use Tobyz\JsonApiServer\Context; +use Tobyz\JsonApiServer\Endpoint\Concerns\BuildsMeta; +use Tobyz\JsonApiServer\Endpoint\Concerns\IncludesData; use Tobyz\JsonApiServer\Exception\BadRequestException; use Tobyz\JsonApiServer\Exception\ForbiddenException; use Tobyz\JsonApiServer\ResourceType; @@ -29,7 +31,8 @@ use function Tobyz\JsonApiServer\run_callbacks; class Index { - use Concerns\IncludesData; + use IncludesData; + use BuildsMeta; /** * Handle a request to show a resource listing. @@ -99,9 +102,7 @@ class Index $meta[] = new Structure\Meta('total', $total); } - foreach ($context->getMeta() as $item) { - $meta[] = new Structure\Meta($item->getName(), $item->getValue()($context)); - } + $meta = array_merge($meta, $this->buildMeta($context)); return json_api_response( new Structure\CompoundDocument( diff --git a/src/Endpoint/Show.php b/src/Endpoint/Show.php index a4cf9c0..c167e37 100644 --- a/src/Endpoint/Show.php +++ b/src/Endpoint/Show.php @@ -15,6 +15,8 @@ use JsonApiPhp\JsonApi\CompoundDocument; use JsonApiPhp\JsonApi\Included; use Psr\Http\Message\ResponseInterface; use Tobyz\JsonApiServer\Context; +use Tobyz\JsonApiServer\Endpoint\Concerns\BuildsMeta; +use Tobyz\JsonApiServer\Endpoint\Concerns\IncludesData; use Tobyz\JsonApiServer\ResourceType; use Tobyz\JsonApiServer\Serializer; @@ -23,7 +25,8 @@ use function Tobyz\JsonApiServer\run_callbacks; class Show { - use Concerns\IncludesData; + use IncludesData; + use BuildsMeta; public function handle(Context $context, ResourceType $resourceType, $model): ResponseInterface { @@ -39,7 +42,8 @@ class Show return json_api_response( new CompoundDocument( $primary[0], - new Included(...$included) + new Included(...$included), + ...$this->buildMeta($context) ) ); } diff --git a/src/Endpoint/Update.php b/src/Endpoint/Update.php index 8427805..3112e3d 100644 --- a/src/Endpoint/Update.php +++ b/src/Endpoint/Update.php @@ -13,6 +13,8 @@ namespace Tobyz\JsonApiServer\Endpoint; use Psr\Http\Message\ResponseInterface; use Tobyz\JsonApiServer\Context; +use Tobyz\JsonApiServer\Endpoint\Concerns\BuildsMeta; +use Tobyz\JsonApiServer\Endpoint\Concerns\SavesData; use Tobyz\JsonApiServer\Exception\ForbiddenException; use Tobyz\JsonApiServer\ResourceType; @@ -21,7 +23,7 @@ use function Tobyz\JsonApiServer\run_callbacks; class Update { - use Concerns\SavesData; + use SavesData; /** * @throws ForbiddenException if the resource is not updatable. diff --git a/tests/MockAdapter.php b/tests/MockAdapter.php index 266f252..00bf4c0 100644 --- a/tests/MockAdapter.php +++ b/tests/MockAdapter.php @@ -36,6 +36,10 @@ class MockAdapter implements AdapterInterface public function find($query, string $id) { + if ($id === '404') { + return null; + } + return $this->models[$id] ?? (object) ['id' => $id]; } diff --git a/tests/specification/DeletingResourcesTest.php b/tests/specification/DeletingResourcesTest.php index 2e20aa8..3a19f11 100644 --- a/tests/specification/DeletingResourcesTest.php +++ b/tests/specification/DeletingResourcesTest.php @@ -11,12 +11,15 @@ namespace Tobyz\Tests\JsonApiServer\specification; +use Tobyz\JsonApiServer\Context; +use Tobyz\JsonApiServer\Exception\ResourceNotFoundException; use Tobyz\JsonApiServer\JsonApi; +use Tobyz\JsonApiServer\Schema\Type; use Tobyz\Tests\JsonApiServer\AbstractTestCase; use Tobyz\Tests\JsonApiServer\MockAdapter; /** - * @see https://jsonapi.org/format/1.0/#crud-deleting + * @see https://jsonapi.org/format/1.1/#crud-deleting */ class DeletingResourcesTest extends AbstractTestCase { @@ -25,25 +28,48 @@ class DeletingResourcesTest extends AbstractTestCase */ private $api; - /** - * @var MockAdapter - */ - private $adapter; - public function setUp(): void { $this->api = new JsonApi('http://example.com'); - $this->adapter = new MockAdapter(); + $this->api->resourceType('users', new MockAdapter(), function (Type $type) { + $type->deletable(); + }); } public function test_no_content_response_if_resource_successfully_deleted() { - $this->markTestIncomplete(); + $response = $this->api->handle( + $this->buildRequest('DELETE', '/users/1') + ); + + $this->assertEquals(204, $response->getStatusCode()); + $this->assertEmpty($response->getBody()->getContents()); + } + + public function test_ok_response_if_meta() + { + $this->api->resourceType('users', new MockAdapter(), function (Type $type) { + $type->deletable(); + $type->deleting(function ($model, Context $context) { + $context->meta('foo', 'bar'); + }); + }); + + $response = $this->api->handle( + $this->buildRequest('DELETE', '/users/1') + ); + + $this->assertEquals(200, $response->getStatusCode()); + $this->assertJsonApiDocumentSubset(['meta' => ['foo' => 'bar']], $response->getBody()); } public function test_not_found_error_if_resource_does_not_exist() { - $this->markTestIncomplete(); + $this->expectException(ResourceNotFoundException::class); + + $this->api->handle( + $this->buildRequest('DELETE', '/users/404') + ); } }