Skip to content

Commit 57ffe41

Browse files
committed
Merge 3.4
2 parents 754e499 + fc94db0 commit 57ffe41

File tree

6 files changed

+100
-42
lines changed

6 files changed

+100
-42
lines changed

features/openapi/docs.feature

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -153,7 +153,7 @@ Feature: Documentation support
153153
And the JSON node "paths./related_dummies/{id}/related_to_dummy_friends.get.parameters" should have 6 elements
154154

155155
# Subcollection - check schema
156-
And the JSON node "paths./related_dummies/{id}/related_to_dummy_friends.get.responses.200.content.application/ld+json.schema.properties.hydra:member.items.$ref" should be equal to "#/components/schemas/RelatedToDummyFriend.jsonld-fakemanytomany"
156+
And the JSON node "paths./related_dummies/{id}/related_to_dummy_friends.get.responses.200.content.application/ld+json.schema.properties.hydra:member.items.$ref" should be equal to "#/components/schemas/RelatedToDummyFriend.jsonld-fakemanytomany.output"
157157

158158
# Deprecations
159159
And the JSON node "paths./dummies.get.deprecated" should be false
@@ -165,8 +165,8 @@ Feature: Documentation support
165165
And the JSON node "paths./deprecated_resources/{id}.patch.deprecated" should be true
166166

167167
# Formats
168-
And the OpenAPI class "Dummy.jsonld" exists
169-
And the "@id" property exists for the OpenAPI class "Dummy.jsonld"
168+
And the OpenAPI class "Dummy.jsonld.output" exists
169+
And the "@id" property exists for the OpenAPI class "Dummy.jsonld.output"
170170
And the JSON node "paths./dummies.get.responses.200.content.application/ld+json" should be equal to:
171171
"""
172172
{
@@ -176,7 +176,7 @@ Feature: Documentation support
176176
"hydra:member": {
177177
"type": "array",
178178
"items": {
179-
"$ref": "#/components/schemas/Dummy.jsonld"
179+
"$ref": "#/components/schemas/Dummy.jsonld.output"
180180
}
181181
},
182182
"hydra:totalItems": {

src/Hydra/JsonSchema/SchemaFactory.php

Lines changed: 27 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,6 @@
2727
final class SchemaFactory implements SchemaFactoryInterface, SchemaFactoryAwareInterface
2828
{
2929
private const BASE_PROP = [
30-
'readOnly' => true,
3130
'type' => 'string',
3231
];
3332
private const BASE_PROPS = [
@@ -36,7 +35,6 @@ final class SchemaFactory implements SchemaFactoryInterface, SchemaFactoryAwareI
3635
];
3736
private const BASE_ROOT_PROPS = [
3837
'@context' => [
39-
'readOnly' => true,
4038
'oneOf' => [
4139
['type' => 'string'],
4240
[
@@ -74,18 +72,43 @@ public function buildSchema(string $className, string $format = 'jsonld', string
7472
return $schema;
7573
}
7674

77-
if ('input' === $type) {
78-
return $schema;
75+
if (($key = $schema->getRootDefinitionKey() ?? $schema->getItemsDefinitionKey()) !== null) {
76+
$postfix = '.'.$type;
77+
$definitions = $schema->getDefinitions();
78+
$definitions[$key.$postfix] = $definitions[$key];
79+
unset($definitions[$key]);
80+
81+
if (($schema['type'] ?? '') === 'array') {
82+
$schema['items']['$ref'] .= $postfix;
83+
} else {
84+
$schema['$ref'] .= $postfix;
85+
}
7986
}
8087

8188
$definitions = $schema->getDefinitions();
8289
if ($key = $schema->getRootDefinitionKey()) {
8390
$definitions[$key]['properties'] = self::BASE_ROOT_PROPS + ($definitions[$key]['properties'] ?? []);
91+
if (Schema::TYPE_OUTPUT === $type) {
92+
foreach (array_keys(self::BASE_ROOT_PROPS) as $property) {
93+
$definitions[$key]['required'] ??= [];
94+
if (!\in_array($property, $definitions[$key]['required'], true)) {
95+
$definitions[$key]['required'][] = $property;
96+
}
97+
}
98+
}
8499

85100
return $schema;
86101
}
87102
if ($key = $schema->getItemsDefinitionKey()) {
88103
$definitions[$key]['properties'] = self::BASE_PROPS + ($definitions[$key]['properties'] ?? []);
104+
if (Schema::TYPE_OUTPUT === $type) {
105+
foreach (array_keys(self::BASE_PROPS) as $property) {
106+
$definitions[$key]['required'] ??= [];
107+
if (!\in_array($property, $definitions[$key]['required'], true)) {
108+
$definitions[$key]['required'][] = $property;
109+
}
110+
}
111+
}
89112
}
90113

91114
if (($schema['type'] ?? '') === 'array') {

src/Hydra/Tests/JsonSchema/SchemaFactoryTest.php

Lines changed: 47 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
use ApiPlatform\Metadata\ApiResource;
2323
use ApiPlatform\Metadata\Get;
2424
use ApiPlatform\Metadata\GetCollection;
25+
use ApiPlatform\Metadata\Post;
2526
use ApiPlatform\Metadata\Property\Factory\PropertyMetadataFactoryInterface;
2627
use ApiPlatform\Metadata\Property\Factory\PropertyNameCollectionFactoryInterface;
2728
use ApiPlatform\Metadata\Property\PropertyNameCollection;
@@ -49,6 +50,7 @@ protected function setUp(): void
4950

5051
$propertyNameCollectionFactory = $this->prophesize(PropertyNameCollectionFactoryInterface::class);
5152
$propertyNameCollectionFactory->create(Dummy::class, ['enable_getter_setter_extraction' => true, 'schema_type' => Schema::TYPE_OUTPUT])->willReturn(new PropertyNameCollection());
53+
$propertyNameCollectionFactory->create(Dummy::class, ['enable_getter_setter_extraction' => true, 'schema_type' => Schema::TYPE_INPUT])->willReturn(new PropertyNameCollection());
5254
$propertyMetadataFactory = $this->prophesize(PropertyMetadataFactoryInterface::class);
5355

5456
$definitionNameFactory = new DefinitionNameFactory(['jsonapi' => true, 'jsonhal' => true, 'jsonld' => true]);
@@ -68,7 +70,12 @@ public function testBuildSchema(): void
6870
$resultSchema = $this->schemaFactory->buildSchema(Dummy::class);
6971

7072
$this->assertTrue($resultSchema->isDefined());
71-
$this->assertSame('Dummy.jsonld', $resultSchema->getRootDefinitionKey());
73+
$this->assertSame('Dummy.jsonld.output', $resultSchema->getRootDefinitionKey());
74+
75+
$resultSchema = $this->schemaFactory->buildSchema(Dummy::class, 'jsonld', Schema::TYPE_INPUT, new Post());
76+
77+
$this->assertTrue($resultSchema->isDefined());
78+
$this->assertSame('Dummy.jsonld.input', $resultSchema->getRootDefinitionKey());
7279
}
7380

7481
public function testCustomFormatBuildSchema(): void
@@ -93,7 +100,6 @@ public function testHasRootDefinitionKeyBuildSchema(): void
93100
$this->assertArrayHasKey('@context', $properties);
94101
$this->assertEquals(
95102
[
96-
'readOnly' => true,
97103
'oneOf' => [
98104
['type' => 'string'],
99105
[
@@ -121,7 +127,7 @@ public function testHasRootDefinitionKeyBuildSchema(): void
121127
public function testSchemaTypeBuildSchema(): void
122128
{
123129
$resultSchema = $this->schemaFactory->buildSchema(Dummy::class, 'jsonld', Schema::TYPE_OUTPUT, new GetCollection());
124-
$definitionName = 'Dummy.jsonld';
130+
$definitionName = 'Dummy.jsonld.output';
125131

126132
$this->assertNull($resultSchema->getRootDefinitionKey());
127133
// @noRector
@@ -150,6 +156,12 @@ public function testSchemaTypeBuildSchema(): void
150156
$this->assertArrayNotHasKey('@context', $properties);
151157
$this->assertArrayHasKey('@type', $properties);
152158
$this->assertArrayHasKey('@id', $properties);
159+
160+
$resultSchema = $this->schemaFactory->buildSchema(Dummy::class, 'jsonld', Schema::TYPE_INPUT, new Post());
161+
$definitionName = 'Dummy.jsonld.input';
162+
163+
$this->assertSame($definitionName, $resultSchema->getRootDefinitionKey());
164+
$this->assertFalse(isset($resultSchema['properties']));
153165
}
154166

155167
public function testHasHydraViewNavigationBuildSchema(): void
@@ -167,4 +179,36 @@ public function testHasHydraViewNavigationBuildSchema(): void
167179
$this->assertArrayHasKey('hydra:previous', $resultSchema['properties']['hydra:view']['properties']);
168180
$this->assertArrayHasKey('hydra:next', $resultSchema['properties']['hydra:view']['properties']);
169181
}
182+
183+
public function testRequiredBasePropertiesBuildSchema(): void
184+
{
185+
$resultSchema = $this->schemaFactory->buildSchema(Dummy::class);
186+
$definitions = $resultSchema->getDefinitions();
187+
$rootDefinitionKey = $resultSchema->getRootDefinitionKey();
188+
189+
$this->assertTrue(isset($definitions[$rootDefinitionKey]));
190+
$this->assertTrue(isset($definitions[$rootDefinitionKey]['required']));
191+
$requiredProperties = $resultSchema['definitions'][$rootDefinitionKey]['required'];
192+
$this->assertContains('@context', $requiredProperties);
193+
$this->assertContains('@id', $requiredProperties);
194+
$this->assertContains('@type', $requiredProperties);
195+
196+
$resultSchema = $this->schemaFactory->buildSchema(Dummy::class, 'jsonld', Schema::TYPE_OUTPUT, new GetCollection());
197+
$definitions = $resultSchema->getDefinitions();
198+
$itemsDefinitionKey = array_key_first($definitions->getArrayCopy());
199+
200+
$this->assertTrue(isset($definitions[$itemsDefinitionKey]));
201+
$this->assertTrue(isset($definitions[$itemsDefinitionKey]['required']));
202+
$requiredProperties = $resultSchema['definitions'][$itemsDefinitionKey]['required'];
203+
$this->assertNotContains('@context', $requiredProperties);
204+
$this->assertContains('@id', $requiredProperties);
205+
$this->assertContains('@type', $requiredProperties);
206+
207+
$resultSchema = $this->schemaFactory->buildSchema(Dummy::class, 'jsonld', Schema::TYPE_INPUT, new Post());
208+
$definitions = $resultSchema->getDefinitions();
209+
$itemsDefinitionKey = array_key_first($definitions->getArrayCopy());
210+
211+
$this->assertTrue(isset($definitions[$itemsDefinitionKey]));
212+
$this->assertFalse(isset($definitions[$itemsDefinitionKey]['required']));
213+
}
170214
}

src/JsonSchema/SchemaFactory.php

Lines changed: 1 addition & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@
1818
use ApiPlatform\Metadata\CollectionOperationInterface;
1919
use ApiPlatform\Metadata\HttpOperation;
2020
use ApiPlatform\Metadata\Operation;
21-
use ApiPlatform\Metadata\Patch;
2221
use ApiPlatform\Metadata\Property\Factory\PropertyMetadataFactoryInterface;
2322
use ApiPlatform\Metadata\Property\Factory\PropertyNameCollectionFactoryInterface;
2423
use ApiPlatform\Metadata\Resource\Factory\ResourceMetadataCollectionFactoryInterface;
@@ -35,8 +34,6 @@ final class SchemaFactory implements SchemaFactoryInterface, SchemaFactoryAwareI
3534
{
3635
use ResourceMetadataTrait;
3736

38-
private const PATCH_SCHEMA_POSTFIX = '.patch';
39-
4037
private ?SchemaFactoryInterface $schemaFactory = null;
4138
// Edge case where the related resource is not readable (for example: NotExposed) but we have groups to read the whole related object
4239
public const FORCE_SUBSCHEMA = '_api_subschema_force_readable_link';
@@ -88,12 +85,6 @@ public function buildSchema(string $className, string $format = 'json', string $
8885
return $schema;
8986
}
9087

91-
$isJsonMergePatch = 'json' === $format && $operation instanceof Patch && Schema::TYPE_INPUT === $type;
92-
93-
if ($isJsonMergePatch) {
94-
$definitionName .= self::PATCH_SCHEMA_POSTFIX;
95-
}
96-
9788
if (!isset($schema['$ref']) && !isset($schema['type'])) {
9889
$ref = Schema::VERSION_OPENAPI === $version ? '#/components/schemas/'.$definitionName : '#/definitions/'.$definitionName;
9990
if ($forceCollection || ('POST' !== $method && $operation instanceof CollectionOperationInterface)) {
@@ -142,7 +133,7 @@ public function buildSchema(string $className, string $format = 'json', string $
142133
}
143134

144135
$normalizedPropertyName = $this->nameConverter ? $this->nameConverter->normalize($propertyName, $inputOrOutputClass, $format, $serializerContext) : $propertyName;
145-
if ($propertyMetadata->isRequired() && !$isJsonMergePatch) {
136+
if ($propertyMetadata->isRequired()) {
146137
$definition['required'][] = $normalizedPropertyName;
147138
}
148139

tests/JsonSchema/Command/JsonSchemaGenerateCommandTest.php

Lines changed: 20 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -120,9 +120,9 @@ public function testExecuteWithJsonldTypeInput(): void
120120
$this->tester->run(['command' => 'api:json-schema:generate', 'resource' => $this->entityClass, '--operation' => '_api_/dummies{._format}_post', '--format' => 'jsonld', '--type' => 'input']);
121121
$result = $this->tester->getDisplay();
122122

123-
$this->assertStringNotContainsString('@id', $result);
124-
$this->assertStringNotContainsString('@context', $result);
125-
$this->assertStringNotContainsString('@type', $result);
123+
$this->assertStringContainsString('@id', $result);
124+
$this->assertStringContainsString('@context', $result);
125+
$this->assertStringContainsString('@type', $result);
126126
}
127127

128128
/**
@@ -148,24 +148,24 @@ public function testArraySchemaWithReference(): void
148148
$result = $this->tester->getDisplay();
149149
$json = json_decode($result, associative: true);
150150

151-
$this->assertEquals($json['definitions']['BagOfTests.jsonld-write']['properties']['tests'], [
151+
$this->assertEquals($json['definitions']['BagOfTests.jsonld-write.input']['properties']['tests'], [
152152
'type' => 'string',
153153
'foo' => 'bar',
154154
]);
155155

156-
$this->assertEquals($json['definitions']['BagOfTests.jsonld-write']['properties']['nonResourceTests'], [
156+
$this->assertEquals($json['definitions']['BagOfTests.jsonld-write.input']['properties']['nonResourceTests'], [
157157
'type' => 'array',
158158
'items' => [
159-
'$ref' => '#/definitions/NonResourceTestEntity.jsonld-write',
159+
'$ref' => '#/definitions/NonResourceTestEntity.jsonld-write.input',
160160
],
161161
]);
162162

163-
$this->assertEquals($json['definitions']['BagOfTests.jsonld-write']['properties']['description'], [
163+
$this->assertEquals($json['definitions']['BagOfTests.jsonld-write.input']['properties']['description'], [
164164
'maxLength' => 255,
165165
]);
166166

167-
$this->assertEquals($json['definitions']['BagOfTests.jsonld-write']['properties']['type'], [
168-
'$ref' => '#/definitions/TestEntity.jsonld-write',
167+
$this->assertEquals($json['definitions']['BagOfTests.jsonld-write.input']['properties']['type'], [
168+
'$ref' => '#/definitions/TestEntity.jsonld-write.input',
169169
]);
170170
}
171171

@@ -175,14 +175,14 @@ public function testArraySchemaWithMultipleUnionTypesJsonLd(): void
175175
$result = $this->tester->getDisplay();
176176
$json = json_decode($result, associative: true);
177177

178-
$this->assertEquals($json['definitions']['Nest.jsonld']['properties']['owner']['anyOf'], [
179-
['$ref' => '#/definitions/Wren.jsonld'],
180-
['$ref' => '#/definitions/Robin.jsonld'],
178+
$this->assertEquals($json['definitions']['Nest.jsonld.output']['properties']['owner']['anyOf'], [
179+
['$ref' => '#/definitions/Wren.jsonld.output'],
180+
['$ref' => '#/definitions/Robin.jsonld.output'],
181181
['type' => 'null'],
182182
]);
183183

184-
$this->assertArrayHasKey('Wren.jsonld', $json['definitions']);
185-
$this->assertArrayHasKey('Robin.jsonld', $json['definitions']);
184+
$this->assertArrayHasKey('Wren.jsonld.output', $json['definitions']);
185+
$this->assertArrayHasKey('Robin.jsonld.output', $json['definitions']);
186186
}
187187

188188
public function testArraySchemaWithMultipleUnionTypesJsonApi(): void
@@ -226,7 +226,7 @@ public function testWritableNonResourceRef(): void
226226
$result = $this->tester->getDisplay();
227227
$json = json_decode($result, associative: true);
228228

229-
$this->assertEquals($json['definitions']['SaveProduct.jsonld']['properties']['codes']['items']['$ref'], '#/definitions/ProductCode.jsonld');
229+
$this->assertEquals($json['definitions']['SaveProduct.jsonld.input']['properties']['codes']['items']['$ref'], '#/definitions/ProductCode.jsonld.input');
230230
}
231231

232232
/**
@@ -238,8 +238,8 @@ public function testOpenApiResourceRefIsNotOverwritten(): void
238238
$result = $this->tester->getDisplay();
239239
$json = json_decode($result, associative: true);
240240

241-
$this->assertEquals('#/definitions/DummyFriend', $json['definitions']['Issue6299.Issue6299OutputDto.jsonld']['properties']['itemDto']['$ref']);
242-
$this->assertEquals('#/definitions/DummyDate', $json['definitions']['Issue6299.Issue6299OutputDto.jsonld']['properties']['collectionDto']['items']['$ref']);
241+
$this->assertEquals('#/definitions/DummyFriend', $json['definitions']['Issue6299.Issue6299OutputDto.jsonld.output']['properties']['itemDto']['$ref']);
242+
$this->assertEquals('#/definitions/DummyDate', $json['definitions']['Issue6299.Issue6299OutputDto.jsonld.output']['properties']['collectionDto']['items']['$ref']);
243243
}
244244

245245
/**
@@ -252,7 +252,7 @@ public function testSubSchemaJsonLd(): void
252252
$result = $this->tester->getDisplay();
253253
$json = json_decode($result, associative: true);
254254

255-
$this->assertArrayHasKey('@id', $json['definitions']['ThirdLevel.jsonld-friends']['properties']);
255+
$this->assertArrayHasKey('@id', $json['definitions']['ThirdLevel.jsonld-friends.output']['properties']);
256256
}
257257

258258
#[\PHPUnit\Framework\Attributes\Group('orm')]
@@ -311,7 +311,7 @@ public function testBackedEnumExamplesAreNotLost(): void
311311
$this->tester->run(['command' => 'api:json-schema:generate', 'resource' => Issue6317::class, '--type' => 'output', '--format' => 'jsonld']);
312312
$result = $this->tester->getDisplay();
313313
$json = json_decode($result, associative: true);
314-
$properties = $json['definitions']['Issue6317.jsonld']['properties'];
314+
$properties = $json['definitions']['Issue6317.jsonld.output']['properties'];
315315

316316
$this->assertArrayHasKey('example', $properties['id']);
317317
$this->assertArrayHasKey('example', $properties['name']);
@@ -326,7 +326,7 @@ public function testResourceWithEnumPropertiesSchema(): void
326326
$this->tester->run(['command' => 'api:json-schema:generate', 'resource' => ResourceWithEnumProperty::class, '--type' => 'output', '--format' => 'jsonld']);
327327
$result = $this->tester->getDisplay();
328328
$json = json_decode($result, associative: true);
329-
$properties = $json['definitions']['ResourceWithEnumProperty.jsonld']['properties'];
329+
$properties = $json['definitions']['ResourceWithEnumProperty.jsonld.output']['properties'];
330330

331331
$this->assertSame(
332332
[

tests/OpenApi/Command/OpenApiCommandTest.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -147,7 +147,7 @@ public function testBackedEnumExamplesAreNotLost(): void
147147
};
148148

149149
$assertExample($json['components']['schemas']['Issue6317']['properties'], 'id');
150-
$assertExample($json['components']['schemas']['Issue6317.jsonld']['properties'], 'id');
150+
$assertExample($json['components']['schemas']['Issue6317.jsonld.output']['properties'], 'id');
151151
$assertExample($json['components']['schemas']['Issue6317.jsonapi']['properties']['data']['properties']['attributes']['properties'], '_id');
152152
$assertExample($json['components']['schemas']['Issue6317.jsonhal']['properties'], 'id');
153153
}

0 commit comments

Comments
 (0)