Skip to content

Commit 7807301

Browse files
committed
1 parent 47828e9 commit 7807301

File tree

5 files changed

+63
-51
lines changed

5 files changed

+63
-51
lines changed

src/Hydra/JsonSchema/SchemaFactory.php

+29-22
Original file line numberDiff line numberDiff line change
@@ -37,37 +37,43 @@ final class SchemaFactory implements SchemaFactoryInterface, SchemaFactoryAwareI
3737
use SchemaUriPrefixTrait;
3838

3939
private const ITEM_BASE_SCHEMA_NAME = 'HydraItemBaseSchema';
40+
private const ITEM_BASE_SCHEMA_OUTPUT_NAME = 'HydraOutputBaseSchema';
4041
private const COLLECTION_BASE_SCHEMA_NAME = 'HydraCollectionBaseSchema';
4142
private const BASE_PROP = [
42-
'readOnly' => true,
4343
'type' => 'string',
4444
];
4545
private const BASE_PROPS = [
4646
'@id' => self::BASE_PROP,
4747
'@type' => self::BASE_PROP,
4848
];
49-
private const BASE_ROOT_PROPS = [
50-
'@context' => [
51-
'readOnly' => true,
52-
'oneOf' => [
53-
['type' => 'string'],
54-
[
55-
'type' => 'object',
56-
'properties' => [
57-
'@vocab' => [
58-
'type' => 'string',
59-
],
60-
'hydra' => [
61-
'type' => 'string',
62-
'enum' => [ContextBuilder::HYDRA_NS],
49+
private const ITEM_BASE_SCHEMA = [
50+
'type' => 'object',
51+
'properties' => [
52+
'@context' => [
53+
'oneOf' => [
54+
['type' => 'string'],
55+
[
56+
'type' => 'object',
57+
'properties' => [
58+
'@vocab' => [
59+
'type' => 'string',
60+
],
61+
'hydra' => [
62+
'type' => 'string',
63+
'enum' => [ContextBuilder::HYDRA_NS],
64+
],
6365
],
66+
'required' => ['@vocab', 'hydra'],
67+
'additionalProperties' => true,
6468
],
65-
'required' => ['@vocab', 'hydra'],
66-
'additionalProperties' => true,
6769
],
68-
],
70+
] + self::BASE_PROPS,
6971
],
70-
] + self::BASE_PROPS;
72+
];
73+
74+
private const ITEM_BASE_SCHEMA_OUTPUT = [
75+
'required' => ['@id', '@type'],
76+
] + self::ITEM_BASE_SCHEMA;
7177

7278
/**
7379
* @param array<string, mixed> $defaultContext
@@ -126,13 +132,14 @@ public function buildSchema(string $className, string $format = 'jsonld', string
126132

127133
$key = $schema->getRootDefinitionKey() ?? $collectionKey;
128134

129-
if (!isset($definitions[self::ITEM_BASE_SCHEMA_NAME])) {
130-
$definitions[self::ITEM_BASE_SCHEMA_NAME] = ['type' => 'object', 'properties' => self::BASE_ROOT_PROPS];
135+
$name = Schema::TYPE_OUTPUT === $type ? self::ITEM_BASE_SCHEMA_NAME : self::ITEM_BASE_SCHEMA_OUTPUT_NAME;
136+
if (!isset($definitions[$name])) {
137+
$definitions[$name] = Schema::TYPE_OUTPUT === $type ? self::ITEM_BASE_SCHEMA_OUTPUT : self::ITEM_BASE_SCHEMA;
131138
}
132139

133140
$definitions[$definitionName] = [
134141
'allOf' => [
135-
['$ref' => $prefix.self::ITEM_BASE_SCHEMA_NAME],
142+
['$ref' => $prefix.$name],
136143
['$ref' => $prefix.$key],
137144
],
138145
];

src/JsonApi/JsonSchema/SchemaFactory.php

+10-5
Original file line numberDiff line numberDiff line change
@@ -317,15 +317,20 @@ private function buildDefinitionPropertiesSchema(string $key, string $className,
317317
}
318318

319319
if ($required = $definitions[$key]['required'] ?? null) {
320-
foreach ($required as $require) {
321-
if (isset($replacement['attributes']['properties'][$require])) {
322-
$replacement['attributes']['required'][] = $require;
323-
continue;
324-
}
320+
foreach ($required as $i => $require) {
325321
if (isset($relationships[$require])) {
326322
$replacement['relationships']['required'][] = $require;
323+
unset($required[$i]);
327324
}
328325
}
326+
327+
$replacement['attributes'] = [
328+
'allOf' => [
329+
$replacement['attributes'],
330+
['type' => 'object', 'required' => $required],
331+
],
332+
];
333+
329334
unset($definitions[$key]['required']);
330335
}
331336

src/JsonSchema/SchemaFactory.php

+4-2
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ final class SchemaFactory implements SchemaFactoryInterface, SchemaFactoryAwareI
4040
public const FORCE_SUBSCHEMA = '_api_subschema_force_readable_link';
4141
public const OPENAPI_DEFINITION_NAME = 'openapi_definition_name';
4242

43-
public function __construct(ResourceMetadataCollectionFactoryInterface $resourceMetadataFactory, private readonly PropertyNameCollectionFactoryInterface $propertyNameCollectionFactory, private readonly PropertyMetadataFactoryInterface $propertyMetadataFactory, private readonly ?NameConverterInterface $nameConverter = null, ?ResourceClassResolverInterface $resourceClassResolver = null, private readonly ?array $distinctFormats = null, private ?DefinitionNameFactoryInterface $definitionNameFactory = null)
43+
public function __construct(ResourceMetadataCollectionFactoryInterface $resourceMetadataFactory, private readonly PropertyNameCollectionFactoryInterface $propertyNameCollectionFactory, private readonly PropertyMetadataFactoryInterface $propertyMetadataFactory, private readonly ?NameConverterInterface $nameConverter = null, ?ResourceClassResolverInterface $resourceClassResolver = null, ?array $distinctFormats = null, private ?DefinitionNameFactoryInterface $definitionNameFactory = null)
4444
{
4545
if (!$definitionNameFactory) {
4646
$this->definitionNameFactory = new DefinitionNameFactory($distinctFormats);
@@ -104,7 +104,9 @@ public function buildSchema(string $className, string $format = 'json', string $
104104
/** @var \ArrayObject<string, mixed> $definition */
105105
$definition = new \ArrayObject(['type' => 'object']);
106106
$definitions[$definitionName] = $definition;
107-
$definition['description'] = $operation ? ($operation->getDescription() ?? '') : '';
107+
if ($description = $operation->getDescription()) {
108+
$definition['description'] = $description;
109+
}
108110

109111
// additionalProperties are allowed by default, so it does not need to be set explicitly, unless allow_extra_attributes is false
110112
// See https://json-schema.org/understanding-json-schema/reference/object.html#properties

tests/Hal/JsonSchema/SchemaFactoryTest.php

+19-22
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ protected function setUp(): void
5050
$propertyNameCollectionFactory->create(Dummy::class, ['enable_getter_setter_extraction' => true, 'schema_type' => Schema::TYPE_OUTPUT])->willReturn(new PropertyNameCollection());
5151
$propertyMetadataFactory = $this->prophesize(PropertyMetadataFactoryInterface::class);
5252

53-
$definitionNameFactory = new DefinitionNameFactory(['jsonapi' => true, 'jsonhal' => true, 'jsonld' => true]);
53+
$definitionNameFactory = new DefinitionNameFactory();
5454

5555
$baseSchemaFactory = new BaseSchemaFactory(
5656
resourceMetadataFactory: $resourceMetadataFactory->reveal(),
@@ -87,8 +87,8 @@ public function testHasRootDefinitionKeyBuildSchema(): void
8787
$rootDefinitionKey = $resultSchema->getRootDefinitionKey();
8888

8989
$this->assertTrue(isset($definitions[$rootDefinitionKey]));
90-
$this->assertTrue(isset($definitions[$rootDefinitionKey]['properties']));
91-
$properties = $resultSchema['definitions'][$rootDefinitionKey]['properties'];
90+
$this->assertTrue(isset($definitions[$rootDefinitionKey]['allOf'][0]['properties']));
91+
$properties = $resultSchema['definitions'][$rootDefinitionKey]['allOf'][0]['properties'];
9292
$this->assertArrayHasKey('_links', $properties);
9393
$this->assertEquals(
9494
[
@@ -109,29 +109,26 @@ public function testHasRootDefinitionKeyBuildSchema(): void
109109
);
110110
}
111111

112-
public function testSchemaTypeBuildSchema(): void
112+
public function testCollection(): void
113113
{
114114
$resultSchema = $this->schemaFactory->buildSchema(Dummy::class, 'jsonhal', Schema::TYPE_OUTPUT, new GetCollection());
115-
$definitionName = 'Dummy.jsonhal';
116-
117115
$this->assertNull($resultSchema->getRootDefinitionKey());
118-
$this->assertTrue(isset($resultSchema['properties']));
119-
$this->assertArrayHasKey('_embedded', $resultSchema['properties']);
120-
$this->assertArrayHasKey('totalItems', $resultSchema['properties']);
121-
$this->assertArrayHasKey('itemsPerPage', $resultSchema['properties']);
122-
$this->assertArrayHasKey('_links', $resultSchema['properties']);
123-
$properties = $resultSchema['definitions'][$definitionName]['properties'];
124-
$this->assertArrayHasKey('_links', $properties);
125116

126-
$resultSchema = $this->schemaFactory->buildSchema(Dummy::class, 'jsonhal', Schema::TYPE_OUTPUT, null, null, null, true);
117+
$this->assertTrue(isset($resultSchema['definitions']['Dummy.jsonhal']));
118+
$this->assertTrue(isset($resultSchema['definitions']['HalCollectionBaseSchema']));
119+
$this->assertTrue(isset($resultSchema['definitions']['Dummy.jsonhal']));
127120

128-
$this->assertNull($resultSchema->getRootDefinitionKey());
129-
$this->assertTrue(isset($resultSchema['properties']));
130-
$this->assertArrayHasKey('_embedded', $resultSchema['properties']);
131-
$this->assertArrayHasKey('totalItems', $resultSchema['properties']);
132-
$this->assertArrayHasKey('itemsPerPage', $resultSchema['properties']);
133-
$this->assertArrayHasKey('_links', $resultSchema['properties']);
134-
$properties = $resultSchema['definitions'][$definitionName]['properties'];
135-
$this->assertArrayHasKey('_links', $properties);
121+
foreach ($resultSchema['allOf'] as $schema) {
122+
if (isset($schema['$ref'])) {
123+
$this->assertEquals($schema['$ref'], '#/definitions/HalCollectionBaseSchema');
124+
continue;
125+
}
126+
127+
$this->assertArrayHasKey('_embedded', $schema['properties']);
128+
$this->assertEquals('#/definitions/Dummy.jsonhal', $schema['properties']['_embedded']['additionalProperties']['items']['$ref']);
129+
}
130+
131+
$forceCollectionSchema = $this->schemaFactory->buildSchema(Dummy::class, 'jsonhal', Schema::TYPE_OUTPUT, null, null, null, true);
132+
$this->assertEquals($forceCollectionSchema, $resultSchema);
136133
}
137134
}

tests/Symfony/Bundle/DependencyInjection/ConfigurationTest.php

+1
Original file line numberDiff line numberDiff line change
@@ -216,6 +216,7 @@ private function runDefaultConfigTests(array $doctrineIntegrationsToLoad = ['orm
216216
'license' => [
217217
'name' => null,
218218
'url' => null,
219+
'identifier' => null,
219220
],
220221
'swagger_ui_extra_configuration' => [],
221222
'overrideResponses' => true,

0 commit comments

Comments
 (0)