diff --git a/features/authorization/deny.feature b/features/authorization/deny.feature index cac6b867eed..91227419479 100644 --- a/features/authorization/deny.feature +++ b/features/authorization/deny.feature @@ -217,5 +217,3 @@ Feature: Authorization checking And I send a "GET" request to "/secured_dummies" Then the response status code should be 200 And the response should contain "ownerOnlyProperty" - - diff --git a/features/http_cache/headers.feature b/features/http_cache/headers.feature index 1a95d130f4d..b14b412bdbd 100644 --- a/features/http_cache/headers.feature +++ b/features/http_cache/headers.feature @@ -7,6 +7,6 @@ Feature: Default values of HTTP cache headers Scenario: Cache headers default value When I send a "GET" request to "/relation_embedders" Then the response status code should be 200 - And the header "Etag" should be equal to '"21248afbca1f242fd3009ac7cdf13293"' + And the header "Etag" should be equal to '"7bfa587950d675e222660f68623f5f89"' And the header "Cache-Control" should be equal to "max-age=60, public, s-maxage=3600" And the header "Vary" should be equal to "Accept, Cookie" diff --git a/src/Hydra/Serializer/CollectionNormalizer.php b/src/Hydra/Serializer/CollectionNormalizer.php index 28dd4f80694..3e7d9aa5fec 100644 --- a/src/Hydra/Serializer/CollectionNormalizer.php +++ b/src/Hydra/Serializer/CollectionNormalizer.php @@ -56,8 +56,8 @@ public function __construct(private readonly ContextBuilderInterface $contextBui protected function getPaginationData(iterable $object, array $context = []): array { $resourceClass = $this->resourceClassResolver->getResourceClass($object, $context['resource_class']); - $context = $this->initContext($resourceClass, $context); - $context['api_collection_sub_level'] = true; + // Note that this adds jsonld_has_context by reference, we moved the code to this class, this follows a note from ItemNormalizer that we need to change + // context generation as it's more complicated then it should. $data = $this->addJsonLdContext($this->contextBuilder, $resourceClass, $context); $data['@id'] = $this->iriConverter->getIriFromResource($resourceClass, UrlGeneratorInterface::ABS_PATH, $context['operation'] ?? null, $context); $data['@type'] = 'hydra:Collection'; @@ -104,10 +104,18 @@ protected function getItemsData(iterable $object, string $format = null, array $ if ($iriOnly) { $data['hydra:member'][] = $this->iriConverter->getIriFromResource($obj); } else { - $data['hydra:member'][] = $this->normalizer->normalize($obj, $format, $context); + $data['hydra:member'][] = $this->normalizer->normalize($obj, $format, $context + ['jsonld_has_context' => true]); } } return $data; } + + protected function initContext(string $resourceClass, array $context): array + { + $context = parent::initContext($resourceClass, $context); + $context['api_collection_sub_level'] = true; + + return $context; + } } diff --git a/tests/Serializer/SerializerContextBuilderTest.php b/tests/Serializer/SerializerContextBuilderTest.php index 30731329348..51bb8428b9b 100644 --- a/tests/Serializer/SerializerContextBuilderTest.php +++ b/tests/Serializer/SerializerContextBuilderTest.php @@ -67,42 +67,42 @@ public function testCreateFromRequest(): void { $request = Request::create('/foos/1'); $request->attributes->replace(['_api_resource_class' => 'Foo', '_api_operation_name' => 'get', '_api_format' => 'xml', '_api_mime_type' => 'text/xml']); - $expected = ['foo' => 'bar', 'operation_name' => 'get', 'resource_class' => 'Foo', 'request_uri' => '/foos/1', 'uri' => 'http://localhost/foos/1', 'output' => null, 'input' => null, 'iri_only' => false, 'skip_null_values' => true, 'operation' => $this->operation]; + $expected = ['foo' => 'bar', 'operation_name' => 'get', 'resource_class' => 'Foo', 'request_uri' => '/foos/1', 'uri' => 'http://localhost/foos/1', 'output' => null, 'input' => null, 'iri_only' => false, 'skip_null_values' => true, 'operation' => $this->operation, 'exclude_from_cache_key' => ['root_operation', 'operation']]; $this->assertEquals($expected, $this->builder->createFromRequest($request, true)); $request = Request::create('/foos'); $request->attributes->replace(['_api_resource_class' => 'Foo', '_api_operation_name' => 'get_collection', '_api_format' => 'xml', '_api_mime_type' => 'text/xml']); - $expected = ['foo' => 'bar', 'operation_name' => 'get_collection', 'resource_class' => 'Foo', 'request_uri' => '/foos', 'uri' => 'http://localhost/foos', 'output' => null, 'input' => null, 'iri_only' => false, 'skip_null_values' => true, 'operation' => $this->operation->withName('get_collection')]; + $expected = ['foo' => 'bar', 'operation_name' => 'get_collection', 'resource_class' => 'Foo', 'request_uri' => '/foos', 'uri' => 'http://localhost/foos', 'output' => null, 'input' => null, 'iri_only' => false, 'skip_null_values' => true, 'operation' => $this->operation->withName('get_collection'), 'exclude_from_cache_key' => ['root_operation', 'operation']]; $this->assertEquals($expected, $this->builder->createFromRequest($request, true)); $request = Request::create('/foos/1'); $request->attributes->replace(['_api_resource_class' => 'Foo', '_api_operation_name' => 'get', '_api_format' => 'xml', '_api_mime_type' => 'text/xml']); - $expected = ['bar' => 'baz', 'operation_name' => 'get', 'resource_class' => 'Foo', 'request_uri' => '/foos/1', 'api_allow_update' => false, 'uri' => 'http://localhost/foos/1', 'output' => null, 'input' => null, 'iri_only' => false, 'skip_null_values' => true, 'operation' => $this->operation]; + $expected = ['bar' => 'baz', 'operation_name' => 'get', 'resource_class' => 'Foo', 'request_uri' => '/foos/1', 'api_allow_update' => false, 'uri' => 'http://localhost/foos/1', 'output' => null, 'input' => null, 'iri_only' => false, 'skip_null_values' => true, 'operation' => $this->operation, 'exclude_from_cache_key' => ['root_operation', 'operation']]; $this->assertEquals($expected, $this->builder->createFromRequest($request, false)); $request = Request::create('/foos', 'POST'); $request->attributes->replace(['_api_resource_class' => 'Foo', '_api_operation_name' => 'post', '_api_format' => 'xml', '_api_mime_type' => 'text/xml']); - $expected = ['bar' => 'baz', 'operation_name' => 'post', 'resource_class' => 'Foo', 'request_uri' => '/foos', 'api_allow_update' => false, 'uri' => 'http://localhost/foos', 'output' => null, 'input' => null, 'iri_only' => false, 'skip_null_values' => true, 'operation' => $this->operation->withName('post')]; + $expected = ['bar' => 'baz', 'operation_name' => 'post', 'resource_class' => 'Foo', 'request_uri' => '/foos', 'api_allow_update' => false, 'uri' => 'http://localhost/foos', 'output' => null, 'input' => null, 'iri_only' => false, 'skip_null_values' => true, 'operation' => $this->operation->withName('post'), 'exclude_from_cache_key' => ['root_operation', 'operation']]; $this->assertEquals($expected, $this->builder->createFromRequest($request, false)); $request = Request::create('/foos', 'PUT'); $request->attributes->replace(['_api_resource_class' => 'Foo', '_api_operation_name' => 'put', '_api_format' => 'xml', '_api_mime_type' => 'text/xml']); - $expected = ['bar' => 'baz', 'operation_name' => 'put', 'resource_class' => 'Foo', 'request_uri' => '/foos', 'api_allow_update' => true, 'uri' => 'http://localhost/foos', 'output' => null, 'input' => null, 'iri_only' => false, 'skip_null_values' => true, 'operation' => (new Put(name: 'put'))->withOperation($this->operation)]; + $expected = ['bar' => 'baz', 'operation_name' => 'put', 'resource_class' => 'Foo', 'request_uri' => '/foos', 'api_allow_update' => true, 'uri' => 'http://localhost/foos', 'output' => null, 'input' => null, 'iri_only' => false, 'skip_null_values' => true, 'operation' => (new Put(name: 'put'))->withOperation($this->operation), 'exclude_from_cache_key' => ['root_operation', 'operation']]; $this->assertEquals($expected, $this->builder->createFromRequest($request, false)); $request = Request::create('/bars/1/foos'); $request->attributes->replace(['_api_resource_class' => 'Foo', '_api_operation_name' => 'get', '_api_format' => 'xml', '_api_mime_type' => 'text/xml']); - $expected = ['bar' => 'baz', 'operation_name' => 'get', 'resource_class' => 'Foo', 'request_uri' => '/bars/1/foos', 'api_allow_update' => false, 'uri' => 'http://localhost/bars/1/foos', 'output' => null, 'input' => null, 'iri_only' => false, 'skip_null_values' => true, 'operation' => $this->operation]; + $expected = ['bar' => 'baz', 'operation_name' => 'get', 'resource_class' => 'Foo', 'request_uri' => '/bars/1/foos', 'api_allow_update' => false, 'uri' => 'http://localhost/bars/1/foos', 'output' => null, 'input' => null, 'iri_only' => false, 'skip_null_values' => true, 'operation' => $this->operation, 'exclude_from_cache_key' => ['root_operation', 'operation']]; $this->assertEquals($expected, $this->builder->createFromRequest($request, false)); $request = Request::create('/foowithpatch/1', 'PATCH'); $request->attributes->replace(['_api_resource_class' => 'FooWithPatch', '_api_operation_name' => 'patch', '_api_format' => 'json', '_api_mime_type' => 'application/json']); - $expected = ['operation_name' => 'patch', 'resource_class' => 'FooWithPatch', 'request_uri' => '/foowithpatch/1', 'api_allow_update' => true, 'uri' => 'http://localhost/foowithpatch/1', 'output' => null, 'input' => null, 'deep_object_to_populate' => true, 'skip_null_values' => true, 'iri_only' => false, 'operation' => $this->patchOperation]; + $expected = ['operation_name' => 'patch', 'resource_class' => 'FooWithPatch', 'request_uri' => '/foowithpatch/1', 'api_allow_update' => true, 'uri' => 'http://localhost/foowithpatch/1', 'output' => null, 'input' => null, 'deep_object_to_populate' => true, 'skip_null_values' => true, 'iri_only' => false, 'operation' => $this->patchOperation, 'exclude_from_cache_key' => ['root_operation', 'operation']]; $this->assertEquals($expected, $this->builder->createFromRequest($request, false)); $request = Request::create('/bars/1/foos'); $request->attributes->replace(['_api_resource_class' => 'Foo', '_api_operation_name' => 'get', '_api_format' => 'xml', '_api_mime_type' => 'text/xml', 'id' => '1']); - $expected = ['bar' => 'baz', 'operation_name' => 'get', 'resource_class' => 'Foo', 'request_uri' => '/bars/1/foos', 'api_allow_update' => false, 'uri' => 'http://localhost/bars/1/foos', 'output' => null, 'input' => null, 'iri_only' => false, 'operation' => $this->operation, 'skip_null_values' => true]; + $expected = ['bar' => 'baz', 'operation_name' => 'get', 'resource_class' => 'Foo', 'request_uri' => '/bars/1/foos', 'api_allow_update' => false, 'uri' => 'http://localhost/bars/1/foos', 'output' => null, 'input' => null, 'iri_only' => false, 'operation' => $this->operation, 'skip_null_values' => true, 'exclude_from_cache_key' => ['root_operation', 'operation']]; $this->assertEquals($expected, $this->builder->createFromRequest($request, false)); } @@ -115,7 +115,7 @@ public function testThrowExceptionOnInvalidRequest(): void public function testReuseExistingAttributes(): void { - $expected = ['bar' => 'baz', 'operation_name' => 'get', 'resource_class' => 'Foo', 'request_uri' => '/foos/1', 'api_allow_update' => false, 'uri' => 'http://localhost/foos/1', 'output' => null, 'input' => null, 'iri_only' => false, 'skip_null_values' => true, 'operation' => $this->operation]; + $expected = ['bar' => 'baz', 'operation_name' => 'get', 'resource_class' => 'Foo', 'request_uri' => '/foos/1', 'api_allow_update' => false, 'uri' => 'http://localhost/foos/1', 'output' => null, 'input' => null, 'iri_only' => false, 'skip_null_values' => true, 'operation' => $this->operation, 'exclude_from_cache_key' => ['root_operation', 'operation']]; $this->assertEquals($expected, $this->builder->createFromRequest(Request::create('/foos/1'), false, ['resource_class' => 'Foo', 'operation_name' => 'get'])); } }