From dba31d3b41f9923740e339388ae8c30b61d4c158 Mon Sep 17 00:00:00 2001 From: soyuka Date: Sun, 1 Dec 2024 11:22:40 +0100 Subject: [PATCH] fix(jsonld): anonymous context hydra_prefix value fixes #6810 --- src/JsonLd/ContextBuilder.php | 10 +++---- src/JsonLd/Serializer/JsonLdContextTrait.php | 11 +++++-- .../Issue6810/JsonLdContextOutput.php | 30 +++++++++++++++++++ .../ApiResource/Issue6810/Output.php | 19 ++++++++++++ tests/Functional/JsonLdTest.php | 25 ++++++++++++++-- 5 files changed, 84 insertions(+), 11 deletions(-) create mode 100644 tests/Fixtures/TestBundle/ApiResource/Issue6810/JsonLdContextOutput.php create mode 100644 tests/Fixtures/TestBundle/ApiResource/Issue6810/Output.php diff --git a/src/JsonLd/ContextBuilder.php b/src/JsonLd/ContextBuilder.php index a3d638f5a7b..36dc391350f 100644 --- a/src/JsonLd/ContextBuilder.php +++ b/src/JsonLd/ContextBuilder.php @@ -115,14 +115,15 @@ public function getResourceContextUri(string $resourceClass, ?int $referenceType public function getAnonymousResourceContext(object $object, array $context = [], int $referenceType = UrlGeneratorInterface::ABS_PATH): array { $outputClass = $this->getObjectClass($object); - $operation = $context['operation'] ?? new Get(shortName: (new \ReflectionClass($outputClass))->getShortName()); + $operation = $context['operation'] ?? new Get(shortName: (new \ReflectionClass($outputClass))->getShortName(), normalizationContext: [self::HYDRA_CONTEXT_HAS_PREFIX => $context[self::HYDRA_CONTEXT_HAS_PREFIX] ?? $this->defaultContext[self::HYDRA_CONTEXT_HAS_PREFIX] ?? true]); $shortName = $operation->getShortName(); $jsonLdContext = [ '@context' => $this->getResourceContextWithShortname( $outputClass, $referenceType, - $shortName + $shortName, + $operation ), '@type' => $shortName, ]; @@ -148,6 +149,7 @@ public function getAnonymousResourceContext(object $object, array $context = [], private function getResourceContextWithShortname(string $resourceClass, int $referenceType, string $shortName, ?HttpOperation $operation = null): array { $context = $this->getBaseContext($referenceType); + $hydraPrefix = $operation->getNormalizationContext()[self::HYDRA_CONTEXT_HAS_PREFIX] ?? $this->defaultContext[self::HYDRA_CONTEXT_HAS_PREFIX] ?? true; $propertyContext = $operation ? ['normalization_groups' => $operation->getNormalizationContext()['groups'] ?? null, 'denormalization_groups' => $operation->getDenormalizationContext()['groups'] ?? null] : ['normalization_groups' => [], 'denormalization_groups' => []]; foreach ($this->propertyNameCollectionFactory->create($resourceClass) as $propertyName) { @@ -184,10 +186,6 @@ private function getResourceContextWithShortname(string $resourceClass, int $ref } } - if (false === ($this->defaultContext[self::HYDRA_CONTEXT_HAS_PREFIX] ?? true)) { - return [HYDRA_CONTEXT, $context]; - } - return $context; } } diff --git a/src/JsonLd/Serializer/JsonLdContextTrait.php b/src/JsonLd/Serializer/JsonLdContextTrait.php index 2e0cef8a402..7b2d98b2cc7 100644 --- a/src/JsonLd/Serializer/JsonLdContextTrait.php +++ b/src/JsonLd/Serializer/JsonLdContextTrait.php @@ -14,6 +14,7 @@ namespace ApiPlatform\JsonLd\Serializer; use ApiPlatform\JsonLd\AnonymousContextBuilderInterface; +use ApiPlatform\JsonLd\ContextBuilder; use ApiPlatform\JsonLd\ContextBuilderInterface; /** @@ -49,13 +50,19 @@ private function addJsonLdContext(ContextBuilderInterface $contextBuilder, strin private function createJsonLdContext(AnonymousContextBuilderInterface $contextBuilder, $object, array &$context): array { + $anonymousContext = ($context['output'] ?? []) + ['api_resource' => $context['api_resource'] ?? null]; + + if (isset($context[ContextBuilder::HYDRA_CONTEXT_HAS_PREFIX])) { + $anonymousContext[ContextBuilder::HYDRA_CONTEXT_HAS_PREFIX] = $context[ContextBuilder::HYDRA_CONTEXT_HAS_PREFIX]; + } + // We're in a collection, don't add the @context part if (isset($context['jsonld_has_context'])) { - return $contextBuilder->getAnonymousResourceContext($object, ($context['output'] ?? []) + ['api_resource' => $context['api_resource'] ?? null, 'has_context' => true]); + return $contextBuilder->getAnonymousResourceContext($object, ['has_context' => true] + $anonymousContext); } $context['jsonld_has_context'] = true; - return $contextBuilder->getAnonymousResourceContext($object, ($context['output'] ?? []) + ['api_resource' => $context['api_resource'] ?? null]); + return $contextBuilder->getAnonymousResourceContext($object, $anonymousContext); } } diff --git a/tests/Fixtures/TestBundle/ApiResource/Issue6810/JsonLdContextOutput.php b/tests/Fixtures/TestBundle/ApiResource/Issue6810/JsonLdContextOutput.php new file mode 100644 index 00000000000..fe1ee0d5548 --- /dev/null +++ b/tests/Fixtures/TestBundle/ApiResource/Issue6810/JsonLdContextOutput.php @@ -0,0 +1,30 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace ApiPlatform\Tests\Fixtures\TestBundle\ApiResource\Issue6810; + +use ApiPlatform\Metadata\Get; +use ApiPlatform\Metadata\Operation; + +#[Get('/json_ld_context_output', provider: [self::class, 'getData'], output: Output::class, normalizationContext: ['hydra_prefix' => false])] +class JsonLdContextOutput +{ + public function __construct(public string $id) + { + } + + public static function getData(Operation $operation, array $uriVariables = [], array $context = []): Output + { + return new Output(); + } +} diff --git a/tests/Fixtures/TestBundle/ApiResource/Issue6810/Output.php b/tests/Fixtures/TestBundle/ApiResource/Issue6810/Output.php new file mode 100644 index 00000000000..0a3a8d89ab4 --- /dev/null +++ b/tests/Fixtures/TestBundle/ApiResource/Issue6810/Output.php @@ -0,0 +1,19 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace ApiPlatform\Tests\Fixtures\TestBundle\ApiResource\Issue6810; + +class Output +{ + public string $foo = 'bar'; +} diff --git a/tests/Functional/JsonLdTest.php b/tests/Functional/JsonLdTest.php index 4cb0b978027..3be83e5c057 100644 --- a/tests/Functional/JsonLdTest.php +++ b/tests/Functional/JsonLdTest.php @@ -14,6 +14,7 @@ namespace ApiPlatform\Tests\Functional; use ApiPlatform\Symfony\Bundle\Test\ApiTestCase; +use ApiPlatform\Tests\Fixtures\TestBundle\ApiResource\Issue6810\JsonLdContextOutput; use ApiPlatform\Tests\Fixtures\TestBundle\Entity\Issue6465\Bar; use ApiPlatform\Tests\Fixtures\TestBundle\Entity\Issue6465\Foo; use ApiPlatform\Tests\SetupClassResourcesTrait; @@ -29,7 +30,7 @@ class JsonLdTest extends ApiTestCase */ public static function getResources(): array { - return [Foo::class, Bar::class]; + return [Foo::class, Bar::class, JsonLdContextOutput::class]; } /** @@ -50,6 +51,20 @@ public function testIssue6465(): void $this->assertEquals('Bar two', $res['title']); } + public function testContextWithOutput(): void + { + $response = self::createClient()->request( + 'GET', + '/json_ld_context_output', + ); + $res = $response->toArray(); + $this->assertEquals($res['@context'], [ + '@vocab' => 'http://localhost/docs.jsonld#', + 'hydra' => 'http://www.w3.org/ns/hydra/core#', + 'foo' => 'Output/foo', + ]); + } + protected function setUp(): void { self::bootKernel(); @@ -66,8 +81,12 @@ protected function setUp(): void $classes[] = $manager->getClassMetadata($entityClass); } - $schemaTool = new SchemaTool($manager); - @$schemaTool->createSchema($classes); + try { + $schemaTool = new SchemaTool($manager); + @$schemaTool->createSchema($classes); + } catch (\Exception $e) { + return; + } $foo = new Foo(); $foo->title = 'Foo';