From d4d0d6bec705eb8a110c5d5531a3bb3ecb50cd04 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 | 18 +++++++---- src/JsonLd/Serializer/JsonLdContextTrait.php | 11 +++++-- .../Issue6810/JsonLdContextOutput.php | 30 +++++++++++++++++++ .../ApiResource/Issue6810/Output.php | 19 ++++++++++++ tests/Functional/JsonLdTest.php | 25 ++++++++++++++-- tests/JsonLd/ContextBuilderTest.php | 11 ++----- 6 files changed, 95 insertions(+), 19 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..4dc9dda405f 100644 --- a/src/JsonLd/ContextBuilder.php +++ b/src/JsonLd/ContextBuilder.php @@ -115,14 +115,24 @@ 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, + 'groups' => [], + ], + denormalizationContext: [ + 'groups' => [], + ] + ); $shortName = $operation->getShortName(); $jsonLdContext = [ '@context' => $this->getResourceContextWithShortname( $outputClass, $referenceType, - $shortName + $shortName, + $operation ), '@type' => $shortName, ]; @@ -184,10 +194,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'; diff --git a/tests/JsonLd/ContextBuilderTest.php b/tests/JsonLd/ContextBuilderTest.php index e5ba3a59326..7a64f7d3beb 100644 --- a/tests/JsonLd/ContextBuilderTest.php +++ b/tests/JsonLd/ContextBuilderTest.php @@ -35,8 +35,6 @@ use Prophecy\Prophecy\ObjectProphecy; use Symfony\Component\PropertyInfo\Type; -use const ApiPlatform\JsonLd\HYDRA_CONTEXT; - /** * @author Markus Mächler */ @@ -293,12 +291,9 @@ public function testResourceContextWithoutHydraPrefix(): void $contextBuilder = new ContextBuilder($this->resourceNameCollectionFactoryProphecy->reveal(), $this->resourceMetadataCollectionFactoryProphecy->reveal(), $this->propertyNameCollectionFactoryProphecy->reveal(), $this->propertyMetadataFactoryProphecy->reveal(), $this->urlGeneratorProphecy->reveal(), null, null, [ContextBuilder::HYDRA_CONTEXT_HAS_PREFIX => false]); $expected = [ - HYDRA_CONTEXT, - [ - '@vocab' => '#', - 'hydra' => 'http://www.w3.org/ns/hydra/core#', - 'dummyPropertyA' => 'DummyEntity/dummyPropertyA', - ], + '@vocab' => '#', + 'hydra' => 'http://www.w3.org/ns/hydra/core#', + 'dummyPropertyA' => 'DummyEntity/dummyPropertyA', ]; $this->assertEquals($expected, $contextBuilder->getResourceContext($this->entityClass));