Skip to content

Commit 0e5991f

Browse files
committed
json-ld: correct embeded context
1 parent f6e4c59 commit 0e5991f

File tree

7 files changed

+88
-59
lines changed

7 files changed

+88
-59
lines changed

features/jsonld/context.feature

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -27,10 +27,7 @@ Feature: JSON-LD contexts generation
2727
"description": "https://schema.org/description",
2828
"dummy": "Dummy/dummy",
2929
"dummyBoolean": "Dummy/dummyBoolean",
30-
"dummyDate": {
31-
"@id": "http://schema.org/DateTime",
32-
"@type": "@id"
33-
},
30+
"dummyDate": "http://schema.org/DateTime",
3431
"dummyFloat": "Dummy/dummyFloat",
3532
"dummyPrice": "Dummy/dummyPrice",
3633
"relatedDummy": {

features/main/input_output.feature

Lines changed: 32 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -35,22 +35,16 @@ Feature: DTO input and output
3535
Then the response status code should be 200
3636
And the response should be in JSON
3737
And the header "Content-Type" should be equal to "application/ld+json; charset=utf-8"
38-
And the JSON should be a superset of:
38+
And the JSON should be equal to:
3939
"""
4040
{
4141
"@context": {
4242
"@vocab": "http://example.com/docs.jsonld#",
4343
"hydra": "http://www.w3.org/ns/hydra/core#",
44-
"foo": {
45-
"@id": "/dummy_dto_customs/1/foo",
46-
"@type": "@id"
47-
},
48-
"bar": {
49-
"@id": "/dummy_dto_customs/1/bar",
50-
"@type": "@id"
51-
}
44+
"foo": "CustomOutputDto/foo",
45+
"bar": "CustomOutputDto/bar"
5246
},
53-
"@type": "CustomOutputDto",
47+
"@type": "DummyDtoCustom",
5448
"@id": "/dummy_dto_customs/1",
5549
"foo": "test",
5650
"bar": 1
@@ -64,7 +58,7 @@ Feature: DTO input and output
6458
Then the response status code should be 200
6559
And the response should be in JSON
6660
And the header "Content-Type" should be equal to "application/ld+json; charset=utf-8"
67-
And the JSON should be a superset of:
61+
And the JSON should be equal to:
6862
"""
6963
{
7064
"@context": "/contexts/DummyDtoCustom",
@@ -110,20 +104,19 @@ Feature: DTO input and output
110104
}
111105
"""
112106
Then the response status code should be 201
113-
And the JSON should be a superset of:
107+
And the JSON should be equal to:
114108
"""
115109
{
116110
"@context": {
117111
"@vocab": "http://example.com/docs.jsonld#",
118112
"hydra": "http://www.w3.org/ns/hydra/core#",
119-
"baz": {
120-
"@type": "@id"
121-
},
122-
"bat": {
123-
"@type": "@id"
124-
}
113+
"id": "OutputDto/id",
114+
"baz": "OutputDto/baz",
115+
"bat": "OutputDto/bat"
125116
},
126-
"@type": "OutputDto",
117+
"@type": "DummyDtoInputOutput",
118+
"@id": "/dummy_dto_input_outputs/1",
119+
"id": 1,
127120
"baz": 1,
128121
"bat": "test"
129122
}
@@ -137,20 +130,19 @@ Feature: DTO input and output
137130
}
138131
"""
139132
Then the response status code should be 200
140-
And the JSON should be a superset of:
133+
And the JSON should be equal to:
141134
"""
142135
{
143136
"@context": {
144-
"@vocab": "http:\/\/example.com\/docs.jsonld#",
145-
"hydra": "http:\/\/www.w3.org\/ns\/hydra\/core#",
146-
"baz": {
147-
"@type": "@id"
148-
},
149-
"bat": {
150-
"@type": "@id"
151-
}
137+
"@vocab": "http://example.com/docs.jsonld#",
138+
"hydra": "http://www.w3.org/ns/hydra/core#",
139+
"id": "OutputDto/id",
140+
"baz": "OutputDto/baz",
141+
"bat": "OutputDto/bat"
152142
},
153-
"@type": "OutputDto",
143+
"@type": "DummyDtoInputOutput",
144+
"@id": "/dummy_dto_input_outputs/1",
145+
"id": 1,
154146
"baz": 2,
155147
"bat": "test"
156148
}
@@ -177,17 +169,16 @@ Feature: DTO input and output
177169
}
178170
"""
179171
Then the response status code should be 200
180-
And the JSON should be a superset of:
172+
And the JSON should be equal to:
181173
"""
182174
{
183175
"@context": {
184176
"@vocab": "http://example.com/docs.jsonld#",
185177
"hydra": "http://www.w3.org/ns/hydra/core#",
186-
"dummy": {
187-
"@type": "@id"
188-
}
178+
"dummy": "RecoverPasswordOutput/dummy"
189179
},
190-
"@type": "RecoverPasswordOutput",
180+
"@type": "User",
181+
"@id": "/users/1",
191182
"dummy": "/dummies/1"
192183
}
193184
"""
@@ -203,20 +194,19 @@ Feature: DTO input and output
203194
}
204195
"""
205196
Then the response status code should be 201
206-
And the JSON should be a superset of:
197+
And the JSON should be equal to:
207198
"""
208199
{
209200
"@context": {
210201
"@vocab": "http://example.com/docs.jsonld#",
211202
"hydra": "http://www.w3.org/ns/hydra/core#",
212-
"baz": {
213-
"@type": "@id"
214-
},
215-
"bat": {
216-
"@type": "@id"
217-
}
203+
"id": "OutputDto/id",
204+
"baz": "OutputDto/baz",
205+
"bat": "OutputDto/bat"
218206
},
219-
"@type": "OutputDto",
207+
"@type": "DummyDtoInputOutput",
208+
"@id": "/dummy_dto_input_outputs/1",
209+
"id": 1,
220210
"baz": 1,
221211
"bat": "test"
222212
}

src/GraphQl/Serializer/ItemNormalizer.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ public function normalize($object, $format = null, array $context = [])
5959
if (isset($context['api_resource']) && isset($data['id'])) {
6060
$data['_id'] = $data['id'];
6161
$data['id'] = $this->iriConverter->getIriFromItem($context['api_resource']);
62+
unset($context['api_resource']);
6263
}
6364
}
6465

src/JsonLd/ContextBuilder.php

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
use ApiPlatform\Core\Metadata\Property\Factory\PropertyNameCollectionFactoryInterface;
1919
use ApiPlatform\Core\Metadata\Resource\Factory\ResourceMetadataFactoryInterface;
2020
use ApiPlatform\Core\Metadata\Resource\Factory\ResourceNameCollectionFactoryInterface;
21+
use ApiPlatform\Core\Util\ClassInfoTrait;
2122
use Symfony\Component\Serializer\NameConverter\NameConverterInterface;
2223

2324
/**
@@ -27,6 +28,8 @@
2728
*/
2829
final class ContextBuilder implements AnonymousContextBuilderInterface
2930
{
31+
use ClassInfoTrait;
32+
3033
public const FORMAT = 'jsonld';
3134

3235
private $resourceNameCollectionFactory;
@@ -110,14 +113,22 @@ public function getResourceContextUri(string $resourceClass, int $referenceType
110113
*/
111114
public function getAnonymousResourceContext($object, array $context = [], int $referenceType = UrlGeneratorInterface::ABS_PATH): array
112115
{
113-
$id = $context['iri'] ?? '_:'.(\function_exists('spl_object_id') ? spl_object_id($object) : spl_object_hash($object));
116+
$outputClass = $this->getObjectClass($object);
117+
$shortName = (new \ReflectionClass($outputClass))->getShortName();
118+
114119
$jsonLdContext = [
115-
'@context' => $this->getResourceContextWithShortname(\get_class($object), $referenceType, $id),
116-
'@id' => $id,
120+
'@context' => $this->getResourceContextWithShortname(
121+
$outputClass,
122+
$referenceType,
123+
$shortName
124+
),
125+
'@type' => $shortName,
126+
'@id' => $context['iri'] ?? '_:'.(\function_exists('spl_object_id') ? spl_object_id($object) : spl_object_hash($object)),
117127
];
118128

119-
if ($context['name'] ?? false) {
120-
$jsonLdContext['@type'] = $context['name'];
129+
// here the object can be different from the resource given by the $context['api_resource'] value
130+
if (isset($context['api_resource'])) {
131+
$jsonLdContext['@type'] = $this->resourceMetadataFactory->create($this->getObjectClass($context['api_resource']))->getShortName();
121132
}
122133

123134
return $jsonLdContext;
@@ -141,7 +152,7 @@ private function getResourceContextWithShortname(string $resourceClass, int $ref
141152
$id = sprintf('%s/%s', $shortName, $convertedName);
142153
}
143154

144-
if (true !== $propertyMetadata->isReadableLink()) {
155+
if (false === $propertyMetadata->isReadableLink()) {
145156
$jsonldContext += [
146157
'@id' => $id,
147158
'@type' => '@id',

src/JsonLd/Serializer/ItemNormalizer.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,11 @@ public function normalize($object, $format = null, array $context = [])
7979
}
8080

8181
$data = $this->createJsonLdContext($this->contextBuilder, $object, $context);
82+
83+
if (isset($context['api_resource'])) {
84+
unset($context['api_resource']);
85+
}
86+
8287
$rawData = parent::normalize($object, $format, $context);
8388
if (!\is_array($rawData)) {
8489
return $rawData;

src/JsonLd/Serializer/JsonLdContextTrait.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,6 @@ private function createJsonLdContext(AnonymousContextBuilderInterface $contextBu
5656

5757
$context['jsonld_has_context'] = true;
5858

59-
return $contextBuilder->getAnonymousResourceContext($object, $context['output'] ?? []);
59+
return $contextBuilder->getAnonymousResourceContext($object, ($context['output'] ?? []) + ['api_resource' => $context['api_resource'] ?? null]);
6060
}
6161
}

tests/JsonLd/ContextBuilderTest.php

Lines changed: 31 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
use ApiPlatform\Core\Metadata\Resource\Factory\ResourceNameCollectionFactoryInterface;
2424
use ApiPlatform\Core\Metadata\Resource\ResourceMetadata;
2525
use ApiPlatform\Core\Metadata\Resource\ResourceNameCollection;
26+
use ApiPlatform\Core\Tests\Fixtures\TestBundle\Dto\OutputDto;
2627
use ApiPlatform\Core\Tests\Fixtures\TestBundle\Entity\Dummy;
2728
use PHPUnit\Framework\TestCase;
2829
use Symfony\Component\PropertyInfo\Type;
@@ -141,32 +142,56 @@ public function testAnonymousResourceContext()
141142
'@context' => [
142143
'@vocab' => '#',
143144
'hydra' => 'http://www.w3.org/ns/hydra/core#',
144-
'dummyPropertyA' => $iri.'/dummyPropertyA',
145+
'dummyPropertyA' => 'Dummy/dummyPropertyA',
145146
],
146147
'@id' => $iri,
148+
'@type' => 'Dummy',
147149
];
148150

149151
$this->assertEquals($expected, $contextBuilder->getAnonymousResourceContext($dummy));
150152
}
151153

152154
public function testAnonymousResourceContextWithIri()
153155
{
154-
$dummy = new Dummy();
155-
$this->propertyNameCollectionFactoryProphecy->create(Dummy::class)->willReturn(new PropertyNameCollection(['dummyPropertyA']));
156-
$this->propertyMetadataFactoryProphecy->create(Dummy::class, 'dummyPropertyA')->willReturn(new PropertyMetadata(new Type(Type::BUILTIN_TYPE_STRING), 'Dummy property A', true, true, true, true, false, false, null, null, []));
156+
$output = new OutputDto();
157+
$this->propertyNameCollectionFactoryProphecy->create(OutputDto::class)->willReturn(new PropertyNameCollection(['dummyPropertyA']));
158+
$this->propertyMetadataFactoryProphecy->create(OutputDto::class, 'dummyPropertyA')->willReturn(new PropertyMetadata(new Type(Type::BUILTIN_TYPE_STRING), 'Dummy property A', true, true, true, true, false, false, null, null, []));
159+
160+
$contextBuilder = new ContextBuilder($this->resourceNameCollectionFactoryProphecy->reveal(), $this->resourceMetadataFactoryProphecy->reveal(), $this->propertyNameCollectionFactoryProphecy->reveal(), $this->propertyMetadataFactoryProphecy->reveal(), $this->urlGeneratorProphecy->reveal());
161+
162+
$expected = [
163+
'@context' => [
164+
'@vocab' => '#',
165+
'hydra' => 'http://www.w3.org/ns/hydra/core#',
166+
'dummyPropertyA' => 'OutputDto/dummyPropertyA',
167+
],
168+
'@id' => '/dummies',
169+
'@type' => 'OutputDto',
170+
];
171+
172+
$this->assertEquals($expected, $contextBuilder->getAnonymousResourceContext($output, ['iri' => '/dummies', 'name' => 'Dummy']));
173+
}
174+
175+
public function testAnonymousResourceContextWithApiResource()
176+
{
177+
$output = new OutputDto();
178+
$this->propertyNameCollectionFactoryProphecy->create(OutputDto::class)->willReturn(new PropertyNameCollection(['dummyPropertyA']));
179+
$this->propertyMetadataFactoryProphecy->create(OutputDto::class, 'dummyPropertyA')->willReturn(new PropertyMetadata(new Type(Type::BUILTIN_TYPE_STRING), 'Dummy property A', true, true, true, true, false, false, null, null, []));
180+
181+
$this->resourceMetadataFactoryProphecy->create(Dummy::class)->willReturn(new ResourceMetadata('Dummy'));
157182

158183
$contextBuilder = new ContextBuilder($this->resourceNameCollectionFactoryProphecy->reveal(), $this->resourceMetadataFactoryProphecy->reveal(), $this->propertyNameCollectionFactoryProphecy->reveal(), $this->propertyMetadataFactoryProphecy->reveal(), $this->urlGeneratorProphecy->reveal());
159184

160185
$expected = [
161186
'@context' => [
162187
'@vocab' => '#',
163188
'hydra' => 'http://www.w3.org/ns/hydra/core#',
164-
'dummyPropertyA' => '/dummies/dummyPropertyA',
189+
'dummyPropertyA' => 'OutputDto/dummyPropertyA',
165190
],
166191
'@id' => '/dummies',
167192
'@type' => 'Dummy',
168193
];
169194

170-
$this->assertEquals($expected, $contextBuilder->getAnonymousResourceContext($dummy, ['iri' => '/dummies', 'name' => 'Dummy']));
195+
$this->assertEquals($expected, $contextBuilder->getAnonymousResourceContext($output, ['iri' => '/dummies', 'name' => 'Dummy', 'api_resource' => new Dummy()]));
171196
}
172197
}

0 commit comments

Comments
 (0)