diff --git a/src/Doctrine/Orm/Tests/config.yml b/src/Doctrine/Orm/Tests/config.yml index e3e948ba753..f89f33e7d0f 100644 --- a/src/Doctrine/Orm/Tests/config.yml +++ b/src/Doctrine/Orm/Tests/config.yml @@ -9,7 +9,8 @@ doctrine: orm: auto_generate_proxy_classes: '%kernel.debug%' controller_resolver: - auto_mapping: true + enabled: false + auto_mapping: false enable_lazy_ghost_objects: true mappings: ApiPlatform\Doctrine\Orm\Tests\Fixtures\Entity: diff --git a/src/GraphQl/Serializer/SerializerContextBuilder.php b/src/GraphQl/Serializer/SerializerContextBuilder.php index 4dad4812b34..7ceef729ad1 100644 --- a/src/GraphQl/Serializer/SerializerContextBuilder.php +++ b/src/GraphQl/Serializer/SerializerContextBuilder.php @@ -20,6 +20,7 @@ use Symfony\Component\Serializer\NameConverter\AdvancedNameConverterInterface; use Symfony\Component\Serializer\NameConverter\MetadataAwareNameConverter; use Symfony\Component\Serializer\NameConverter\NameConverterInterface; +use Symfony\Component\Serializer\Normalizer\AbstractObjectNormalizer; /** * Builds the context used by the Symfony Serializer. @@ -53,6 +54,15 @@ public function create(?string $resourceClass, Operation $operation, array $reso $context['attributes'] = $this->fieldsToAttributes($resourceClass, $operation, $resolverContext, $context); } + // to keep the cache computation smaller, we have "operation_name" and "iri" anyways + $context[AbstractObjectNormalizer::EXCLUDE_FROM_CACHE_KEY][] = 'root_operation'; + $context[AbstractObjectNormalizer::EXCLUDE_FROM_CACHE_KEY][] = 'operation'; + $context[AbstractObjectNormalizer::EXCLUDE_FROM_CACHE_KEY][] = 'object'; + $context[AbstractObjectNormalizer::EXCLUDE_FROM_CACHE_KEY][] = 'data'; + $context[AbstractObjectNormalizer::EXCLUDE_FROM_CACHE_KEY][] = 'property_metadata'; + $context[AbstractObjectNormalizer::EXCLUDE_FROM_CACHE_KEY][] = 'circular_reference_limit_counters'; + $context[AbstractObjectNormalizer::EXCLUDE_FROM_CACHE_KEY][] = 'debug_trace_id'; + return $context; } diff --git a/src/GraphQl/Tests/Serializer/SerializerContextBuilderTest.php b/src/GraphQl/Tests/Serializer/SerializerContextBuilderTest.php index 32a6790b59d..847169ef871 100644 --- a/src/GraphQl/Tests/Serializer/SerializerContextBuilderTest.php +++ b/src/GraphQl/Tests/Serializer/SerializerContextBuilderTest.php @@ -141,6 +141,15 @@ public static function createNormalizationContextProvider(): iterable 'id' => 3, 'field' => 'foo', ], + 'exclude_from_cache_key' => [ + 'root_operation', + 'operation', + 'object', + 'data', + 'property_metadata', + 'circular_reference_limit_counters', + 'debug_trace_id', + ], ], ]; yield 'nominal with advanced name converter' => [ @@ -161,6 +170,15 @@ public static function createNormalizationContextProvider(): iterable 'id' => 3, 'denormalizedField' => 'foo', ], + 'exclude_from_cache_key' => [ + 'root_operation', + 'operation', + 'object', + 'data', + 'property_metadata', + 'circular_reference_limit_counters', + 'debug_trace_id', + ], ], $advancedNameConverterFactory, ]; @@ -181,6 +199,15 @@ public static function createNormalizationContextProvider(): iterable 'attributes' => [ 'nodeField' => 'baz', ], + 'exclude_from_cache_key' => [ + 'root_operation', + 'operation', + 'object', + 'data', + 'property_metadata', + 'circular_reference_limit_counters', + 'debug_trace_id', + ], ], ]; yield 'no resource class' => [ @@ -197,6 +224,15 @@ public static function createNormalizationContextProvider(): iterable 'attributes' => [ 'related' => ['id' => 9], ], + 'exclude_from_cache_key' => [ + 'root_operation', + 'operation', + 'object', + 'data', + 'property_metadata', + 'circular_reference_limit_counters', + 'debug_trace_id', + ], ], ]; yield 'mutation' => [ @@ -217,6 +253,15 @@ public static function createNormalizationContextProvider(): iterable 'id' => 7, 'related' => ['field' => 'bar'], ], + 'exclude_from_cache_key' => [ + 'root_operation', + 'operation', + 'object', + 'data', + 'property_metadata', + 'circular_reference_limit_counters', + 'debug_trace_id', + ], ], ]; yield 'subscription (using fields in context)' => [ @@ -238,6 +283,15 @@ public static function createNormalizationContextProvider(): iterable 'id' => 7, 'related' => ['field' => 'bar'], ], + 'exclude_from_cache_key' => [ + 'root_operation', + 'operation', + 'object', + 'data', + 'property_metadata', + 'circular_reference_limit_counters', + 'debug_trace_id', + ], ], ]; } @@ -268,6 +322,15 @@ public static function createDenormalizationContextProvider(): array 'graphql_operation_name' => $operationName, 'input' => ['class' => 'inputClass'], 'output' => ['class' => 'outputClass'], + 'exclude_from_cache_key' => [ + 'root_operation', + 'operation', + 'object', + 'data', + 'property_metadata', + 'circular_reference_limit_counters', + 'debug_trace_id', + ], ], ], 'no resource class' => [ @@ -277,6 +340,15 @@ public static function createDenormalizationContextProvider(): array 'resource_class' => $resourceClass, 'operation_name' => $operationName, 'graphql_operation_name' => $operationName, + 'exclude_from_cache_key' => [ + 'root_operation', + 'operation', + 'object', + 'data', + 'property_metadata', + 'circular_reference_limit_counters', + 'debug_trace_id', + ], ], ], ]; diff --git a/src/Serializer/SerializerContextBuilder.php b/src/Serializer/SerializerContextBuilder.php index 4487a5bac45..5626657dadc 100644 --- a/src/Serializer/SerializerContextBuilder.php +++ b/src/Serializer/SerializerContextBuilder.php @@ -113,6 +113,11 @@ public function createFromRequest(Request $request, bool $normalization, ?array // to keep the cache computation smaller, we have "operation_name" and "iri" anyways $context[AbstractObjectNormalizer::EXCLUDE_FROM_CACHE_KEY][] = 'root_operation'; $context[AbstractObjectNormalizer::EXCLUDE_FROM_CACHE_KEY][] = 'operation'; + $context[AbstractObjectNormalizer::EXCLUDE_FROM_CACHE_KEY][] = 'object'; + $context[AbstractObjectNormalizer::EXCLUDE_FROM_CACHE_KEY][] = 'data'; + $context[AbstractObjectNormalizer::EXCLUDE_FROM_CACHE_KEY][] = 'property_metadata'; + $context[AbstractObjectNormalizer::EXCLUDE_FROM_CACHE_KEY][] = 'circular_reference_limit_counters'; + $context[AbstractObjectNormalizer::EXCLUDE_FROM_CACHE_KEY][] = 'debug_trace_id'; // JSON API see JsonApiProvider if ($included = $request->attributes->get('_api_included')) { diff --git a/src/Serializer/Tests/SerializerContextBuilderTest.php b/src/Serializer/Tests/SerializerContextBuilderTest.php index dacc366a9fb..294f255b383 100644 --- a/src/Serializer/Tests/SerializerContextBuilderTest.php +++ b/src/Serializer/Tests/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, 'exclude_from_cache_key' => ['root_operation', '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', 'object', 'data', 'property_metadata', 'circular_reference_limit_counters', 'debug_trace_id']]; $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'), 'exclude_from_cache_key' => ['root_operation', 'operation']]; + $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', 'object', 'data', 'property_metadata', 'circular_reference_limit_counters', 'debug_trace_id']]; $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, 'exclude_from_cache_key' => ['root_operation', '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', 'object', 'data', 'property_metadata', 'circular_reference_limit_counters', 'debug_trace_id']]; $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'), 'exclude_from_cache_key' => ['root_operation', 'operation']]; + $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', 'object', 'data', 'property_metadata', 'circular_reference_limit_counters', 'debug_trace_id']]; $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), 'exclude_from_cache_key' => ['root_operation', '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', 'object', 'data', 'property_metadata', 'circular_reference_limit_counters', 'debug_trace_id']]; $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, 'exclude_from_cache_key' => ['root_operation', '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', 'object', 'data', 'property_metadata', 'circular_reference_limit_counters', 'debug_trace_id']]; $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, 'exclude_from_cache_key' => ['root_operation', 'operation']]; + $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', 'object', 'data', 'property_metadata', 'circular_reference_limit_counters', 'debug_trace_id']]; $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, 'exclude_from_cache_key' => ['root_operation', '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, 'operation' => $this->operation, 'skip_null_values' => true, 'exclude_from_cache_key' => ['root_operation', 'operation', 'object', 'data', 'property_metadata', 'circular_reference_limit_counters', 'debug_trace_id']]; $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, 'exclude_from_cache_key' => ['root_operation', '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', 'object', 'data', 'property_metadata', 'circular_reference_limit_counters', 'debug_trace_id']]; $this->assertEquals($expected, $this->builder->createFromRequest(Request::create('/foos/1'), false, ['resource_class' => 'Foo', 'operation_name' => 'get'])); }