From 0b0c8b85d904304148f27796ec7a5a3f47b19ac4 Mon Sep 17 00:00:00 2001 From: Vincent Langlet Date: Mon, 10 Nov 2025 14:14:18 +0100 Subject: [PATCH 1/2] fix(jsonSchema): use different cache key for schema definition name --- src/Hal/JsonSchema/SchemaFactory.php | 7 ++++- src/Hydra/JsonSchema/SchemaFactory.php | 13 ++++++-- src/JsonApi/JsonSchema/SchemaFactory.php | 7 ++++- src/JsonSchema/DefinitionNameFactory.php | 6 ++-- src/JsonSchema/SchemaFactory.php | 6 +++- ...ypeAwareDefinitionNameFactoryInterface.php | 31 +++++++++++++++++++ 6 files changed, 63 insertions(+), 7 deletions(-) create mode 100644 src/JsonSchema/TypeAwareDefinitionNameFactoryInterface.php diff --git a/src/Hal/JsonSchema/SchemaFactory.php b/src/Hal/JsonSchema/SchemaFactory.php index 8075bd40f81..76c93c70cae 100644 --- a/src/Hal/JsonSchema/SchemaFactory.php +++ b/src/Hal/JsonSchema/SchemaFactory.php @@ -20,6 +20,7 @@ use ApiPlatform\JsonSchema\SchemaFactoryAwareInterface; use ApiPlatform\JsonSchema\SchemaFactoryInterface; use ApiPlatform\JsonSchema\SchemaUriPrefixTrait; +use ApiPlatform\JsonSchema\TypeAwareDefinitionNameFactoryInterface; use ApiPlatform\Metadata\Operation; use ApiPlatform\Metadata\Resource\Factory\ResourceMetadataCollectionFactoryInterface; @@ -91,7 +92,11 @@ public function buildSchema(string $className, string $format = 'jsonhal', strin $schema = $this->schemaFactory->buildSchema($className, 'json', $type, $operation, $schema, $serializerContext, $forceCollection); $definitions = $schema->getDefinitions(); - $definitionName = $this->definitionNameFactory->create($className, $format, $className, $operation, $serializerContext); + if ($this->definitionNameFactory instanceof TypeAwareDefinitionNameFactoryInterface) { + $definitionName = $this->definitionNameFactory->create($className, $format, $className, $operation, $serializerContext, $type); + } else { + $definitionName = $this->definitionNameFactory->create($className, $format, $className, $operation, $serializerContext); + } $prefix = $this->getSchemaUriPrefix($schema->getVersion()); $collectionKey = $schema->getItemsDefinitionKey(); diff --git a/src/Hydra/JsonSchema/SchemaFactory.php b/src/Hydra/JsonSchema/SchemaFactory.php index 4dd3e78047c..6a37c3ab1af 100644 --- a/src/Hydra/JsonSchema/SchemaFactory.php +++ b/src/Hydra/JsonSchema/SchemaFactory.php @@ -22,6 +22,7 @@ use ApiPlatform\JsonSchema\SchemaFactoryAwareInterface; use ApiPlatform\JsonSchema\SchemaFactoryInterface; use ApiPlatform\JsonSchema\SchemaUriPrefixTrait; +use ApiPlatform\JsonSchema\TypeAwareDefinitionNameFactoryInterface; use ApiPlatform\Metadata\Operation; use ApiPlatform\Metadata\Resource\Factory\ResourceMetadataCollectionFactoryInterface; @@ -136,7 +137,11 @@ public function buildSchema(string $className, string $format = 'jsonld', string $collectionKey = $schema->getItemsDefinitionKey(); if (!$collectionKey) { - $definitionName = $schema->getRootDefinitionKey() ?? $this->definitionNameFactory->create($className, $format, $inputOrOutputClass, $operation, $serializerContext); + if ($this->definitionNameFactory instanceof TypeAwareDefinitionNameFactoryInterface) { + $definitionName = $schema->getRootDefinitionKey() ?? $this->definitionNameFactory->create($className, $format, $inputOrOutputClass, $operation, $serializerContext, $type); + } else { + $definitionName = $schema->getRootDefinitionKey() ?? $this->definitionNameFactory->create($className, $format, $inputOrOutputClass, $operation, $serializerContext); + } $this->decorateItemDefinition($definitionName, $definitions, $prefix, $type, $serializerContext); if (isset($definitions[$definitionName])) { @@ -235,7 +240,11 @@ public function buildSchema(string $className, string $format = 'jsonld', string ]; } - $definitionName = $this->definitionNameFactory->create($className, $format, $inputOrOutputClass, $operation, $serializerContext); + if ($this->definitionNameFactory instanceof TypeAwareDefinitionNameFactoryInterface) { + $definitionName = $this->definitionNameFactory->create($className, $format, $inputOrOutputClass, $operation, $serializerContext, $type); + } else { + $definitionName = $this->definitionNameFactory->create($className, $format, $inputOrOutputClass, $operation, $serializerContext); + } $schema['type'] = 'object'; $schema['description'] = "$definitionName collection."; $schema['allOf'] = [ diff --git a/src/JsonApi/JsonSchema/SchemaFactory.php b/src/JsonApi/JsonSchema/SchemaFactory.php index 1189c34888d..100fbd8559f 100644 --- a/src/JsonApi/JsonSchema/SchemaFactory.php +++ b/src/JsonApi/JsonSchema/SchemaFactory.php @@ -20,6 +20,7 @@ use ApiPlatform\JsonSchema\SchemaFactoryAwareInterface; use ApiPlatform\JsonSchema\SchemaFactoryInterface; use ApiPlatform\JsonSchema\SchemaUriPrefixTrait; +use ApiPlatform\JsonSchema\TypeAwareDefinitionNameFactoryInterface; use ApiPlatform\Metadata\Operation; use ApiPlatform\Metadata\Property\Factory\PropertyMetadataFactoryInterface; use ApiPlatform\Metadata\Resource\Factory\ResourceMetadataCollectionFactoryInterface; @@ -173,7 +174,11 @@ public function buildSchema(string $className, string $format = 'jsonapi', strin } $schema = $this->schemaFactory->buildSchema($className, 'json', $type, $operation, $schema, $jsonApiSerializerContext, $forceCollection); - $definitionName = $this->definitionNameFactory->create($inputOrOutputClass, $format, $className, $operation, $jsonApiSerializerContext); + if ($this->definitionNameFactory instanceof TypeAwareDefinitionNameFactoryInterface) { + $definitionName = $this->definitionNameFactory->create($inputOrOutputClass, $format, $className, $operation, $serializerContext, $type); + } else { + $definitionName = $this->definitionNameFactory->create($inputOrOutputClass, $format, $className, $operation, $serializerContext); + } $prefix = $this->getSchemaUriPrefix($schema->getVersion()); $definitions = $schema->getDefinitions(); $collectionKey = $schema->getItemsDefinitionKey(); diff --git a/src/JsonSchema/DefinitionNameFactory.php b/src/JsonSchema/DefinitionNameFactory.php index 0d1948742a0..cce5c176996 100644 --- a/src/JsonSchema/DefinitionNameFactory.php +++ b/src/JsonSchema/DefinitionNameFactory.php @@ -17,7 +17,7 @@ use ApiPlatform\Metadata\Util\ResourceClassInfoTrait; use Symfony\Component\Serializer\Normalizer\AbstractNormalizer; -final class DefinitionNameFactory implements DefinitionNameFactoryInterface +final class DefinitionNameFactory implements TypeAwareDefinitionNameFactoryInterface { use ResourceClassInfoTrait; @@ -33,7 +33,7 @@ public function __construct(private ?array $distinctFormats = null) } } - public function create(string $className, string $format = 'json', ?string $inputOrOutputClass = null, ?Operation $operation = null, array $serializerContext = []): string + public function create(string $className, string $format = 'json', ?string $inputOrOutputClass = null, ?Operation $operation = null, array $serializerContext = [], string $type = Schema::TYPE_OUTPUT): string { if ($operation) { $prefix = $operation->getShortName(); @@ -58,6 +58,8 @@ public function create(string $className, string $format = 'json', ?string $inpu $prefix .= self::GLUE.$format; } + $prefix .= self::GLUE.$type; + $definitionName = $serializerContext[SchemaFactory::OPENAPI_DEFINITION_NAME] ?? null; if (null !== $definitionName) { $name = \sprintf('%s%s', $prefix, $definitionName ? '-'.$definitionName : $definitionName); diff --git a/src/JsonSchema/SchemaFactory.php b/src/JsonSchema/SchemaFactory.php index 87c6fb736f2..9dff1438f1f 100644 --- a/src/JsonSchema/SchemaFactory.php +++ b/src/JsonSchema/SchemaFactory.php @@ -97,7 +97,11 @@ public function buildSchema(string $className, string $format = 'json', string $ $isJsonMergePatch => 'merge-patch+json', }; - $definitionName = $this->definitionNameFactory->create($className, $definitionFormat, $inputOrOutputClass, $operation, $serializerContext); + if ($this->definitionNameFactory instanceof TypeAwareDefinitionNameFactoryInterface) { + $definitionName = $this->definitionNameFactory->create($className, $definitionFormat, $inputOrOutputClass, $operation, $serializerContext, $type); + } else { + $definitionName = $this->definitionNameFactory->create($className, $definitionFormat, $inputOrOutputClass, $operation, $serializerContext); + } if (!isset($schema['$ref']) && !isset($schema['type'])) { $ref = $this->getSchemaUriPrefix($version).$definitionName; diff --git a/src/JsonSchema/TypeAwareDefinitionNameFactoryInterface.php b/src/JsonSchema/TypeAwareDefinitionNameFactoryInterface.php new file mode 100644 index 00000000000..b84c0aabbbf --- /dev/null +++ b/src/JsonSchema/TypeAwareDefinitionNameFactoryInterface.php @@ -0,0 +1,31 @@ + + * + * 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\JsonSchema; + +use ApiPlatform\Metadata\Operation; + +/** + * Factory for creating definition names for resources in a JSON Schema document. + */ +interface TypeAwareDefinitionNameFactoryInterface extends DefinitionNameFactoryInterface +{ + /** + * Creates a resource definition name. + * + * @param class-string $className + * + * @return string the definition name + */ + public function create(string $className, string $format = 'json', ?string $inputOrOutputClass = null, ?Operation $operation = null, array $serializerContext = [], string $type = Schema::TYPE_OUTPUT): string; +} From 63345d9c3fca15fb0434d705b7a7bb92615aa19d Mon Sep 17 00:00:00 2001 From: Vincent Langlet Date: Mon, 10 Nov 2025 15:50:32 +0100 Subject: [PATCH 2/2] Update tests --- features/hydra/docs.feature | 2 +- .../JsonSchema/JsonApiJsonSchemaTest.php | 28 ++-- .../JsonSchema/JsonLdJsonSchemaTest.php | 36 ++--- .../Functional/JsonSchema/JsonSchemaTest.php | 4 +- tests/Functional/OpenApiTest.php | 139 ++++++++++-------- .../Command/JsonSchemaGenerateCommandTest.php | 24 +-- tests/OpenApi/Command/OpenApiCommandTest.php | 8 +- 7 files changed, 128 insertions(+), 113 deletions(-) diff --git a/features/hydra/docs.feature b/features/hydra/docs.feature index de6da5aae8d..224a5fe05ef 100644 --- a/features/hydra/docs.feature +++ b/features/hydra/docs.feature @@ -13,7 +13,7 @@ Feature: Documentation support And the response should be in JSON And the header "Content-Type" should be equal to "application/ld+json; charset=utf-8" # Context - And the Hydra context matches the online resource "http://www.w3.org/ns/hydra/context.jsonld" + And the Hydra context matches the online resource "http://www.w3.org/ns/hydra/context.jsonld.output" And the JSON node "@context[1].@vocab" should be equal to "http://example.com/docs.jsonld#" And the JSON node "@context[1].hydra" should be equal to "http://www.w3.org/ns/hydra/core#" And the JSON node "@context[1].rdf" should be equal to "http://www.w3.org/1999/02/22-rdf-syntax-ns#" diff --git a/tests/Functional/JsonSchema/JsonApiJsonSchemaTest.php b/tests/Functional/JsonSchema/JsonApiJsonSchemaTest.php index a9c3f3533af..d2b5e705a07 100644 --- a/tests/Functional/JsonSchema/JsonApiJsonSchemaTest.php +++ b/tests/Functional/JsonSchema/JsonApiJsonSchemaTest.php @@ -58,7 +58,7 @@ public static function getResources(): array public function testJsonApi(): void { $speciesSchema = $this->schemaFactory->buildSchema(Issue6317::class, 'jsonapi', Schema::TYPE_OUTPUT); - $this->assertEquals('#/definitions/Issue6317.jsonapi', $speciesSchema['$ref']); + $this->assertEquals('#/definitions/Issue6317.jsonapi.output', $speciesSchema['$ref']); $this->assertEquals([ 'properties' => [ 'data' => [ @@ -71,7 +71,7 @@ public function testJsonApi(): void 'type' => 'string', ], 'attributes' => [ - '$ref' => '#/definitions/Issue6317', + '$ref' => '#/definitions/Issue6317.output', ], ], 'required' => [ @@ -80,7 +80,7 @@ public function testJsonApi(): void ], ], ], - ], $speciesSchema['definitions']['Issue6317.jsonapi']); + ], $speciesSchema['definitions']['Issue6317.jsonapi.output']); } public function testJsonApiIncludesSchemaForQuestion(): void @@ -91,40 +91,40 @@ public function testJsonApiIncludesSchemaForQuestion(): void $questionSchema = $this->schemaFactory->buildSchema(Question::class, 'jsonapi', Schema::TYPE_OUTPUT); $json = $questionSchema->getDefinitions(); - $properties = $json['Question.jsonapi']['properties']['data']['properties']; - $included = $json['Question.jsonapi']['properties']['included']; + $properties = $json['Question.jsonapi.output']['properties']['data']['properties']; + $included = $json['Question.jsonapi.output']['properties']['included']; $this->assertArrayHasKey('answer', $properties['relationships']['properties']); $this->assertArrayHasKey('anyOf', $included['items']); $this->assertCount(1, $included['items']['anyOf']); $this->assertArrayHasKey('$ref', $included['items']['anyOf'][0]); - $this->assertSame('#/definitions/Answer.jsonapi', $included['items']['anyOf'][0]['$ref']); + $this->assertSame('#/definitions/Answer.jsonapi.output', $included['items']['anyOf'][0]['$ref']); } public function testJsonApiIncludesSchemaForAnimalObservation(): void { $animalObservationSchema = $this->schemaFactory->buildSchema(AnimalObservation::class, 'jsonapi', Schema::TYPE_OUTPUT); $json = $animalObservationSchema->getDefinitions(); - $properties = $json['AnimalObservation.jsonapi']['properties']['data']['properties']; - $included = $json['AnimalObservation.jsonapi']['properties']['included']; + $properties = $json['AnimalObservation.jsonapi.output']['properties']['data']['properties']; + $included = $json['AnimalObservation.jsonapi.output']['properties']['included']; $this->assertArrayHasKey('individuals', $properties['relationships']['properties']); $this->assertArrayHasKey('anyOf', $included['items']); $this->assertCount(1, $included['items']['anyOf']); - $this->assertSame('#/definitions/Animal.jsonapi', $included['items']['anyOf'][0]['$ref']); + $this->assertSame('#/definitions/Animal.jsonapi.output', $included['items']['anyOf'][0]['$ref']); } public function testJsonApiIncludesSchemaForAnimal(): void { $animalSchema = $this->schemaFactory->buildSchema(Animal::class, 'jsonapi', Schema::TYPE_OUTPUT); $json = $animalSchema->getDefinitions(); - $properties = $json['Animal.jsonapi']['properties']['data']['properties']; - $included = $json['Animal.jsonapi']['properties']['included']; + $properties = $json['Animal.jsonapi.output']['properties']['data']['properties']; + $included = $json['Animal.jsonapi.output']['properties']['included']; $this->assertArrayHasKey('species', $properties['relationships']['properties']); $this->assertArrayHasKey('anyOf', $included['items']); $this->assertCount(1, $included['items']['anyOf']); - $this->assertSame('#/definitions/Species.jsonapi', $included['items']['anyOf'][0]['$ref']); + $this->assertSame('#/definitions/Species.jsonapi.output', $included['items']['anyOf'][0]['$ref']); } public function testJsonApiIncludesSchemaForSpecies(): void @@ -132,7 +132,7 @@ public function testJsonApiIncludesSchemaForSpecies(): void $speciesSchema = $this->schemaFactory->buildSchema(Species::class, 'jsonapi', Schema::TYPE_OUTPUT, forceCollection: true); $this->assertArraySubset( [ - 'description' => 'Species.jsonapi collection.', + 'description' => 'Species.jsonapi.output collection.', 'allOf' => [ ['$ref' => '#/definitions/JsonApiCollectionBaseSchema'], [ @@ -150,7 +150,7 @@ public function testJsonApiIncludesSchemaForSpecies(): void 'type' => 'string', ], 'attributes' => [ - '$ref' => '#/definitions/Species', + '$ref' => '#/definitions/Species.output', ], ], 'required' => [ diff --git a/tests/Functional/JsonSchema/JsonLdJsonSchemaTest.php b/tests/Functional/JsonSchema/JsonLdJsonSchemaTest.php index b1380c4ec7d..08e06eaa180 100644 --- a/tests/Functional/JsonSchema/JsonLdJsonSchemaTest.php +++ b/tests/Functional/JsonSchema/JsonLdJsonSchemaTest.php @@ -74,11 +74,11 @@ public function testSubSchemaJsonLd(): void 'nonResourceTests' => new \ArrayObject([ 'type' => 'array', 'items' => [ - '$ref' => '#/definitions/NonResourceTestEntity.jsonld-read', + '$ref' => '#/definitions/NonResourceTestEntity.jsonld.output-read', ], ]), 'type' => new \ArrayObject([ - '$ref' => '#/definitions/TestEntity.jsonld-read', + '$ref' => '#/definitions/TestEntity.jsonld.output-read', ]), ], ]), @@ -137,15 +137,15 @@ public function testSubSchemaJsonLd(): void ]); $this->assertArrayHasKey('definitions', $schema); - $this->assertArrayHasKey('BagOfTests.jsonld-read', $schema['definitions']); - $this->assertArrayHasKey('NonResourceTestEntity.jsonld-read', $schema['definitions']); - $this->assertArrayHasKey('TestEntity.jsonld-read', $schema['definitions']); + $this->assertArrayHasKey('BagOfTests.jsonld.output-read', $schema['definitions']); + $this->assertArrayHasKey('NonResourceTestEntity.jsonld.output-read', $schema['definitions']); + $this->assertArrayHasKey('TestEntity.jsonld.output-read', $schema['definitions']); - $this->assertEquals($expectedBagOfTestsSchema, $schema['definitions']['BagOfTests.jsonld-read']); - $this->assertEquals($expectedNonResourceTestEntitySchema, $schema['definitions']['NonResourceTestEntity.jsonld-read']); - $this->assertEquals($expectedTestEntitySchema, $schema['definitions']['TestEntity.jsonld-read']); + $this->assertEquals($expectedBagOfTestsSchema, $schema['definitions']['BagOfTests.jsonld.output-read']); + $this->assertEquals($expectedNonResourceTestEntitySchema, $schema['definitions']['NonResourceTestEntity.jsonld.output-read']); + $this->assertEquals($expectedTestEntitySchema, $schema['definitions']['TestEntity.jsonld.output-read']); - $this->assertEquals('#/definitions/BagOfTests.jsonld-read', $schema['$ref']); + $this->assertEquals('#/definitions/BagOfTests.jsonld.output-read', $schema['$ref']); } public function testSchemaJsonLdCollection(): void @@ -153,31 +153,31 @@ public function testSchemaJsonLdCollection(): void $schema = $this->schemaFactory->buildSchema(BagOfTests::class, 'jsonld', forceCollection: true); $this->assertArrayHasKey('definitions', $schema); - $this->assertArrayHasKey('BagOfTests.jsonld-read', $schema['definitions']); - $this->assertArrayHasKey('NonResourceTestEntity.jsonld-read', $schema['definitions']); - $this->assertArrayHasKey('TestEntity.jsonld-read', $schema['definitions']); + $this->assertArrayHasKey('BagOfTests.jsonld.output-read', $schema['definitions']); + $this->assertArrayHasKey('NonResourceTestEntity.jsonld.output-read', $schema['definitions']); + $this->assertArrayHasKey('TestEntity.jsonld.output-read', $schema['definitions']); $this->assertArrayHasKey('HydraItemBaseSchema', $schema['definitions']); $this->assertArrayHasKey('HydraCollectionBaseSchema', $schema['definitions']); $this->assertEquals(['$ref' => '#/definitions/HydraCollectionBaseSchema'], $schema['allOf'][0]); - $this->assertEquals(['$ref' => '#/definitions/BagOfTests.jsonld-read'], $schema['allOf'][1]['properties']['hydra:member']['items']); + $this->assertEquals(['$ref' => '#/definitions/BagOfTests.jsonld.output-read'], $schema['allOf'][1]['properties']['hydra:member']['items']); } public function testArraySchemaWithMultipleUnionTypes(): void { $schema = $this->schemaFactory->buildSchema(Nest::class, 'jsonld', 'output'); - $this->assertContains(['$ref' => '#/definitions/Robin.jsonld'], $schema['definitions']['Nest.jsonld']['allOf'][1]['properties']['owner']['anyOf']); - $this->assertContains(['$ref' => '#/definitions/Wren.jsonld'], $schema['definitions']['Nest.jsonld']['allOf'][1]['properties']['owner']['anyOf']); - $this->assertContains(['type' => 'null'], $schema['definitions']['Nest.jsonld']['allOf'][1]['properties']['owner']['anyOf']); + $this->assertContains(['$ref' => '#/definitions/Robin.jsonld.output'], $schema['definitions']['Nest.jsonld.output']['allOf'][1]['properties']['owner']['anyOf']); + $this->assertContains(['$ref' => '#/definitions/Wren.jsonld.output'], $schema['definitions']['Nest.jsonld.output']['allOf'][1]['properties']['owner']['anyOf']); + $this->assertContains(['type' => 'null'], $schema['definitions']['Nest.jsonld.output']['allOf'][1]['properties']['owner']['anyOf']); - $this->assertArrayHasKey('Nest.jsonld', $schema['definitions']); + $this->assertArrayHasKey('Nest.jsonld.output', $schema['definitions']); } public function testSchemaWithoutGetOperation(): void { $schema = $this->schemaFactory->buildSchema(Boat::class, 'jsonld', 'output', $this->operationMetadataFactory->create('_api_/boats{._format}_get_collection')); - $this->assertEquals(['$ref' => '#/definitions/HydraItemBaseSchema'], $schema->getDefinitions()['Boat.jsonld-boat.read']['allOf'][0]); + $this->assertEquals(['$ref' => '#/definitions/HydraItemBaseSchema'], $schema->getDefinitions()['Boat.jsonld.output-boat.read']['allOf'][0]); } } diff --git a/tests/Functional/JsonSchema/JsonSchemaTest.php b/tests/Functional/JsonSchema/JsonSchemaTest.php index 00887fd563e..d1efb0fbde1 100644 --- a/tests/Functional/JsonSchema/JsonSchemaTest.php +++ b/tests/Functional/JsonSchema/JsonSchemaTest.php @@ -204,8 +204,8 @@ public function testResourceWithEnumPropertiesSchema(): void public function testSchemaWithUnknownType(): void { $schema = $this->schemaFactory->buildSchema(Book::class, 'json', Schema::TYPE_OUTPUT, $this->operationMetadataFactory->create('_api_/issue-5452/books{._format}_get_collection')); - $this->assertContains(['$ref' => '#/definitions/ActivableInterface'], $schema['definitions']['Book']['properties']['library']['anyOf']); - $this->assertContains(['$ref' => '#/definitions/TimestampableInterface'], $schema['definitions']['Book']['properties']['library']['anyOf']); + $this->assertContains(['$ref' => '#/definitions/ActivableInterface.output'], $schema['definitions']['Book.output']['properties']['library']['anyOf']); + $this->assertContains(['$ref' => '#/definitions/TimestampableInterface.output'], $schema['definitions']['Book.output']['properties']['library']['anyOf']); } public function testReadOnlySchema(): void diff --git a/tests/Functional/OpenApiTest.php b/tests/Functional/OpenApiTest.php index 6912d7154d9..87fc3206372 100644 --- a/tests/Functional/OpenApiTest.php +++ b/tests/Functional/OpenApiTest.php @@ -130,7 +130,7 @@ public function testErrorsAreDocumented(): void // problem detail https://datatracker.ietf.org/doc/html/rfc7807#section-3.1 foreach (['title', 'detail', 'instance', 'type', 'status'] as $key) { - $this->assertArrayHasKey($key, $res['components']['schemas']['Error']['properties']); + $this->assertArrayHasKey($key, $res['components']['schemas']['Error.output']['properties']); } foreach (['title', 'detail', 'instance', 'type', 'status', '@id', '@type', '@context'] as $key) { @@ -184,12 +184,12 @@ public function testErrorsAreDocumented(): void ], ], ], - ], 'description' => 'A representation of common errors.'], $res['components']['schemas']['Error.jsonld']); + ], 'description' => 'A representation of common errors.'], $res['components']['schemas']['Error.jsonld.output']); } foreach (['id', 'title', 'detail', 'instance', 'type', 'status', 'meta', 'source'] as $key) { $this->assertSame(['allOf' => [ - ['$ref' => '#/components/schemas/Error'], + ['$ref' => '#/components/schemas/Error.output'], ['type' => 'object', 'properties' => [ 'source' => [ 'type' => 'object', @@ -198,7 +198,7 @@ public function testErrorsAreDocumented(): void 'type' => 'string', ], ]], - ]], $res['components']['schemas']['Error.jsonapi']['properties']['errors']['items']); + ]], $res['components']['schemas']['Error.jsonapi.output']['properties']['errors']['items']); } } @@ -284,40 +284,55 @@ public function testRetrieveTheOpenApiDocumentation(): void ], $json['components']['securitySchemes']); // Supported classes - $this->assertArrayHasKey('AbstractDummy', $json['components']['schemas']); - $this->assertArrayHasKey('CircularReference', $json['components']['schemas']); - $this->assertArrayHasKey('CircularReference-circular', $json['components']['schemas']); - $this->assertArrayHasKey('CompositeItem', $json['components']['schemas']); - $this->assertArrayHasKey('CompositeLabel', $json['components']['schemas']); - $this->assertArrayHasKey('ConcreteDummy', $json['components']['schemas']); - $this->assertArrayHasKey('CustomIdentifierDummy', $json['components']['schemas']); - $this->assertArrayHasKey('CustomNormalizedDummy-input', $json['components']['schemas']); - $this->assertArrayHasKey('CustomNormalizedDummy-output', $json['components']['schemas']); - $this->assertArrayHasKey('CustomWritableIdentifierDummy', $json['components']['schemas']); - $this->assertArrayHasKey('Dummy', $json['components']['schemas']); - $this->assertArrayHasKey('DummyBoolean', $json['components']['schemas']); - $this->assertArrayHasKey('RelatedDummy', $json['components']['schemas']); - $this->assertArrayHasKey('DummyTableInheritance', $json['components']['schemas']); - $this->assertArrayHasKey('DummyTableInheritanceChild', $json['components']['schemas']); - $this->assertArrayHasKey('OverriddenOperationDummy-overridden_operation_dummy_get', $json['components']['schemas']); - $this->assertArrayHasKey('OverriddenOperationDummy-overridden_operation_dummy_put', $json['components']['schemas']); - $this->assertArrayHasKey('OverriddenOperationDummy-overridden_operation_dummy_read', $json['components']['schemas']); - $this->assertArrayHasKey('OverriddenOperationDummy-overridden_operation_dummy_write', $json['components']['schemas']); - $this->assertArrayHasKey('Person', $json['components']['schemas']); - $this->assertArrayHasKey('NoCollectionDummy', $json['components']['schemas']); - $this->assertArrayHasKey('RelatedToDummyFriend', $json['components']['schemas']); - $this->assertArrayHasKey('RelatedToDummyFriend-fakemanytomany', $json['components']['schemas']); - $this->assertArrayHasKey('DummyFriend', $json['components']['schemas']); - $this->assertArrayHasKey('RelationEmbedder-barcelona', $json['components']['schemas']); - $this->assertArrayHasKey('RelationEmbedder-chicago', $json['components']['schemas']); - $this->assertArrayHasKey('User-user_user-read', $json['components']['schemas']); - $this->assertArrayHasKey('User-user_user-write', $json['components']['schemas']); - $this->assertArrayHasKey('UuidIdentifierDummy', $json['components']['schemas']); - $this->assertArrayHasKey('ThirdLevel', $json['components']['schemas']); - $this->assertArrayHasKey('DummyCar', $json['components']['schemas']); - $this->assertArrayHasKey('DummyWebhook', $json['components']['schemas']); - $this->assertArrayNotHasKey('ParentDummy', $json['components']['schemas']); - $this->assertArrayNotHasKey('UnknownDummy', $json['components']['schemas']); + $this->assertArrayHasKey('AbstractDummy.input', $json['components']['schemas']); + $this->assertArrayHasKey('AbstractDummy.output', $json['components']['schemas']); + $this->assertArrayHasKey('CircularReference.input', $json['components']['schemas']); + $this->assertArrayHasKey('CircularReference.output', $json['components']['schemas']); + $this->assertArrayHasKey('CircularReference.output-circular', $json['components']['schemas']); + $this->assertArrayHasKey('CompositeItem.output', $json['components']['schemas']); + $this->assertArrayHasKey('CompositeLabel.input', $json['components']['schemas']); + $this->assertArrayHasKey('CompositeLabel.output', $json['components']['schemas']); + $this->assertArrayHasKey('ConcreteDummy.input', $json['components']['schemas']); + $this->assertArrayHasKey('ConcreteDummy.output', $json['components']['schemas']); + $this->assertArrayHasKey('CustomIdentifierDummy.input', $json['components']['schemas']); + $this->assertArrayHasKey('CustomIdentifierDummy.output', $json['components']['schemas']); + $this->assertArrayHasKey('CustomNormalizedDummy.input-input', $json['components']['schemas']); + $this->assertArrayHasKey('CustomNormalizedDummy.output-output', $json['components']['schemas']); + $this->assertArrayHasKey('CustomWritableIdentifierDummy.input', $json['components']['schemas']); + $this->assertArrayHasKey('CustomWritableIdentifierDummy.output', $json['components']['schemas']); + $this->assertArrayHasKey('Dummy.input', $json['components']['schemas']); + $this->assertArrayHasKey('Dummy.output', $json['components']['schemas']); + $this->assertArrayHasKey('DummyBoolean.input', $json['components']['schemas']); + $this->assertArrayHasKey('DummyBoolean.output', $json['components']['schemas']); + $this->assertArrayHasKey('RelatedDummy.input', $json['components']['schemas']); + $this->assertArrayHasKey('RelatedDummy.output', $json['components']['schemas']); + $this->assertArrayHasKey('DummyTableInheritance.input', $json['components']['schemas']); + $this->assertArrayHasKey('DummyTableInheritance.output', $json['components']['schemas']); + $this->assertArrayHasKey('DummyTableInheritanceChild.input', $json['components']['schemas']); + $this->assertArrayHasKey('DummyTableInheritanceChild.output', $json['components']['schemas']); + $this->assertArrayHasKey('OverriddenOperationDummy.output-overridden_operation_dummy_get', $json['components']['schemas']); + $this->assertArrayHasKey('OverriddenOperationDummy.input-overridden_operation_dummy_put', $json['components']['schemas']); + $this->assertArrayHasKey('OverriddenOperationDummy.output-overridden_operation_dummy_read', $json['components']['schemas']); + $this->assertArrayHasKey('OverriddenOperationDummy.input-overridden_operation_dummy_write', $json['components']['schemas']); + $this->assertArrayHasKey('Person.input', $json['components']['schemas']); + $this->assertArrayHasKey('Person.output', $json['components']['schemas']); + $this->assertArrayHasKey('NoCollectionDummy.input', $json['components']['schemas']); + $this->assertArrayHasKey('NoCollectionDummy.output', $json['components']['schemas']); + $this->assertArrayHasKey('RelatedToDummyFriend.input', $json['components']['schemas']); + $this->assertArrayHasKey('RelatedToDummyFriend.output', $json['components']['schemas']); + $this->assertArrayHasKey('RelatedToDummyFriend.output-fakemanytomany', $json['components']['schemas']); + $this->assertArrayHasKey('DummyFriend.input', $json['components']['schemas']); + $this->assertArrayHasKey('DummyFriend.output', $json['components']['schemas']); + $this->assertArrayHasKey('RelationEmbedder.output-barcelona', $json['components']['schemas']); + $this->assertArrayHasKey('RelationEmbedder.input-chicago', $json['components']['schemas']); + $this->assertArrayHasKey('User.output-user_user-read', $json['components']['schemas']); + $this->assertArrayHasKey('User.input-user_user-write', $json['components']['schemas']); + $this->assertArrayHasKey('UuidIdentifierDummy.output', $json['components']['schemas']); + $this->assertArrayHasKey('ThirdLevel.output', $json['components']['schemas']); + $this->assertArrayHasKey('DummyCar.output', $json['components']['schemas']); + $this->assertArrayHasKey('DummyWebhook.output', $json['components']['schemas']); + $this->assertArrayNotHasKey('ParentDummy.output', $json['components']['schemas']); + $this->assertArrayNotHasKey('UnknownDummy.output', $json['components']['schemas']); $this->assertArrayHasKey('/relation_embedders/{id}/custom', $json['paths']); $this->assertArrayHasKey('/override/swagger', $json['paths']); @@ -325,9 +340,9 @@ public function testRetrieveTheOpenApiDocumentation(): void $this->assertArrayHasKey('get', $json['paths']['/api/custom-call/{id}']); $this->assertArrayHasKey('put', $json['paths']['/api/custom-call/{id}']); - $this->assertArrayHasKey('id', $json['components']['schemas']['Dummy']['properties']); - $this->assertSame(['name'], $json['components']['schemas']['Dummy']['required']); - $this->assertArrayHasKey('genderType', $json['components']['schemas']['Person']['properties']); + $this->assertArrayHasKey('id', $json['components']['schemas']['Dummy.output']['properties']); + $this->assertSame(['name'], $json['components']['schemas']['Dummy.output']['required']); + $this->assertArrayHasKey('genderType', $json['components']['schemas']['Person.output']['properties']); $this->assertEquals([ 'default' => 'male', 'type' => ['string', 'null'], @@ -336,13 +351,13 @@ public function testRetrieveTheOpenApiDocumentation(): void 'female', null, ], - ], $json['components']['schemas']['Person']['properties']['genderType']); - $this->assertArrayHasKey('playMode', $json['components']['schemas']['VideoGame']['properties']); + ], $json['components']['schemas']['Person.output']['properties']['genderType']); + $this->assertArrayHasKey('playMode', $json['components']['schemas']['VideoGame.output']['properties']); $this->assertEquals([ 'default' => 'SinglePlayer', 'enum' => ['CoOp', 'MultiPlayer', 'SinglePlayer'], 'type' => 'string', - ], $json['components']['schemas']['VideoGame']['properties']['playMode']); + ], $json['components']['schemas']['VideoGame.output']['properties']['playMode']); // Filters $this->assertSame('dummyBoolean', $json['paths']['/dummies']['get']['parameters'][4]['name']); @@ -390,7 +405,7 @@ public function testRetrieveTheOpenApiDocumentation(): void $this->assertCount(7, $json['paths']['/related_dummies/{id}/related_to_dummy_friends']['get']['parameters']); // Subcollection - check schema - $this->assertSame('#/components/schemas/RelatedToDummyFriend.jsonld-fakemanytomany', $json['paths']['/related_dummies/{id}/related_to_dummy_friends']['get']['responses']['200']['content']['application/ld+json']['schema']['allOf'][1]['properties']['hydra:member']['items']['$ref']); + $this->assertSame('#/components/schemas/RelatedToDummyFriend.jsonld.output-fakemanytomany', $json['paths']['/related_dummies/{id}/related_to_dummy_friends']['get']['responses']['200']['content']['application/ld+json']['schema']['allOf'][1]['properties']['hydra:member']['items']['$ref']); // Deprecations $this->assertTrue($json['paths']['/deprecated_resources']['get']['deprecated']); @@ -401,7 +416,7 @@ public function testRetrieveTheOpenApiDocumentation(): void $this->assertTrue($json['paths']['/deprecated_resources/{id}']['patch']['deprecated']); // Formats - $this->assertArrayHasKey('Dummy.jsonld', $json['components']['schemas']); + $this->assertArrayHasKey('Dummy.jsonld.output', $json['components']['schemas']); $this->assertEquals([ '204' => [ 'description' => 'User activated', @@ -450,9 +465,9 @@ public function testRetrieveTheOpenApiDocumentationWithApiGatewayCompatibility() $json = $response->toArray(); $this->assertSame('/', $json['basePath']); - $this->assertSame('The dummy id.', $json['components']['schemas']['RamseyUuidDummy']['properties']['id']['description']); - $this->assertArrayNotHasKey('RelatedDummy-barcelona', $json['components']['schemas']); - $this->assertArrayHasKey('RelatedDummybarcelona', $json['components']['schemas']); + $this->assertSame('The dummy id.', $json['components']['schemas']['RamseyUuidDummyinput']['properties']['id']['description']); + $this->assertArrayNotHasKey('RelatedDummy.output-barcelona', $json['components']['schemas']); + $this->assertArrayHasKey('RelatedDummyoutputbarcelona', $json['components']['schemas']); } public function testRetrieveTheOpenApiDocumentationToSeeIfShortNamePropertyIsUsed(): void @@ -466,19 +481,19 @@ public function testRetrieveTheOpenApiDocumentationToSeeIfShortNamePropertyIsUse $this->assertResponseIsSuccessful(); $json = $response->toArray(); - $this->assertArrayHasKey('Resource', $json['components']['schemas']); - $this->assertArrayHasKey('ResourceRelated', $json['components']['schemas']); + $this->assertArrayHasKey('Resource.output', $json['components']['schemas']); + $this->assertArrayHasKey('ResourceRelated.output', $json['components']['schemas']); $this->assertEquals([ 'readOnly' => true, 'anyOf' => [ [ - '$ref' => '#/components/schemas/ResourceRelated', + '$ref' => '#/components/schemas/ResourceRelated.output', ], [ 'type' => 'null', ], ], - ], $json['components']['schemas']['Resource']['properties']['resourceRelated']); + ], $json['components']['schemas']['Resource.output']['properties']['resourceRelated']); } public function testRetrieveTheJsonOpenApiDocumentation(): void @@ -546,12 +561,12 @@ public function testRetrieveTheOpenApiDocumentationForEntityDtoWrappers(): void $this->assertResponseIsSuccessful(); $json = $response->toArray(); - $this->assertArrayHasKey('WrappedResponseEntity-read', $json['components']['schemas']); - $this->assertArrayHasKey('id', $json['components']['schemas']['WrappedResponseEntity-read']['properties']); - $this->assertEquals(['type' => 'string'], $json['components']['schemas']['WrappedResponseEntity-read']['properties']['id']); - $this->assertArrayHasKey('WrappedResponseEntity.CustomOutputEntityWrapperDto-read', $json['components']['schemas']); - $this->assertArrayHasKey('data', $json['components']['schemas']['WrappedResponseEntity.CustomOutputEntityWrapperDto-read']['properties']); - $this->assertEquals(['$ref' => '#/components/schemas/WrappedResponseEntity-read'], $json['components']['schemas']['WrappedResponseEntity.CustomOutputEntityWrapperDto-read']['properties']['data']); + $this->assertArrayHasKey('WrappedResponseEntity.output-read', $json['components']['schemas']); + $this->assertArrayHasKey('id', $json['components']['schemas']['WrappedResponseEntity.output-read']['properties']); + $this->assertEquals(['type' => 'string'], $json['components']['schemas']['WrappedResponseEntity.output-read']['properties']['id']); + $this->assertArrayHasKey('WrappedResponseEntity.CustomOutputEntityWrapperDto.output-read', $json['components']['schemas']); + $this->assertArrayHasKey('data', $json['components']['schemas']['WrappedResponseEntity.CustomOutputEntityWrapperDto.output-read']['properties']); + $this->assertEquals(['$ref' => '#/components/schemas/WrappedResponseEntity.output-read'], $json['components']['schemas']['WrappedResponseEntity.CustomOutputEntityWrapperDto.output-read']['properties']['data']); } public function testRetrieveTheOpenApiDocumentationWith30Specification(): void @@ -564,12 +579,12 @@ public function testRetrieveTheOpenApiDocumentationWith30Specification(): void $this->assertEquals([ ['type' => 'integer'], ['type' => 'null'], - ], $json['components']['schemas']['DummyBoolean']['properties']['id']['anyOf']); + ], $json['components']['schemas']['DummyBoolean.output']['properties']['id']['anyOf']); $this->assertEquals([ ['type' => 'boolean'], ['type' => 'null'], - ], $json['components']['schemas']['DummyBoolean']['properties']['isDummyBoolean']['anyOf']); - $this->assertArrayNotHasKey('owl:maxCardinality', $json['components']['schemas']['DummyBoolean']['properties']['isDummyBoolean']); + ], $json['components']['schemas']['DummyBoolean.output']['properties']['isDummyBoolean']['anyOf']); + $this->assertArrayNotHasKey('owl:maxCardinality', $json['components']['schemas']['DummyBoolean.output']['properties']['isDummyBoolean']); } public function testRetrieveTheOpenApiDocumentationInJson(): void diff --git a/tests/JsonSchema/Command/JsonSchemaGenerateCommandTest.php b/tests/JsonSchema/Command/JsonSchemaGenerateCommandTest.php index d369de61793..73a0e9910d8 100644 --- a/tests/JsonSchema/Command/JsonSchemaGenerateCommandTest.php +++ b/tests/JsonSchema/Command/JsonSchemaGenerateCommandTest.php @@ -128,8 +128,8 @@ public function testExecuteWithJsonMergePatchTypeInput(): void $json = json_decode($result, associative: true); $this->assertArrayNotHasKey('Dummy', $json['definitions']); - $this->assertArrayHasKey('Dummy.jsonMergePatch', $json['definitions']); - $this->assertArrayNotHasKey('required', $json['definitions']['Dummy.jsonMergePatch']); + $this->assertArrayHasKey('Dummy.input.jsonMergePatch', $json['definitions']); + $this->assertArrayNotHasKey('required', $json['definitions']['Dummy.input.jsonMergePatch']); } /** @@ -141,7 +141,7 @@ public function testWritableNonResourceRef(): void $result = $this->tester->getDisplay(); $json = json_decode($result, associative: true); - $this->assertEquals($json['definitions']['SaveProduct']['properties']['codes']['items']['$ref'], '#/definitions/ProductCode'); + $this->assertEquals($json['definitions']['SaveProduct.input']['properties']['codes']['items']['$ref'], '#/definitions/ProductCode.input'); } /** @@ -153,8 +153,8 @@ public function testOpenApiResourceRefIsNotOverwritten(): void $result = $this->tester->getDisplay(); $json = json_decode($result, associative: true); - $this->assertEquals('#/definitions/DummyFriend', $json['definitions']['Issue6299.Issue6299OutputDto.jsonld']['allOf'][1]['properties']['itemDto']['$ref']); - $this->assertEquals('#/definitions/DummyDate', $json['definitions']['Issue6299.Issue6299OutputDto.jsonld']['allOf'][1]['properties']['collectionDto']['items']['$ref']); + $this->assertEquals('#/definitions/DummyFriend', $json['definitions']['Issue6299.Issue6299OutputDto.jsonld.output']['allOf'][1]['properties']['itemDto']['$ref']); + $this->assertEquals('#/definitions/DummyDate', $json['definitions']['Issue6299.Issue6299OutputDto.jsonld.output']['allOf'][1]['properties']['collectionDto']['items']['$ref']); } /** @@ -165,7 +165,7 @@ public function testBackedEnumExamplesAreNotLost(): void $this->tester->run(['command' => 'api:json-schema:generate', 'resource' => Issue6317::class, '--type' => 'output', '--format' => 'jsonld']); $result = $this->tester->getDisplay(); $json = json_decode($result, associative: true); - $properties = $json['definitions']['Issue6317.jsonld']['allOf'][1]['properties']; + $properties = $json['definitions']['Issue6317.jsonld.output']['allOf'][1]['properties']; $this->assertArrayHasKey('example', $properties['id']); $this->assertArrayHasKey('example', $properties['name']); $this->assertArrayNotHasKey('example', $properties['ordinal']); @@ -181,7 +181,7 @@ public function testGenId(): void $this->tester->run(['command' => 'api:json-schema:generate', 'resource' => 'ApiPlatform\Tests\Fixtures\TestBundle\Entity\DisableIdGeneration', '--type' => 'output', '--format' => 'jsonld']); $result = $this->tester->getDisplay(); $json = json_decode($result, associative: true); - $this->assertArrayNotHasKey('@id', $json['definitions']['DisableIdGenerationItem.jsonld_noid']['properties']); + $this->assertArrayNotHasKey('@id', $json['definitions']['DisableIdGenerationItem.jsonld.output_noid']['properties']); } #[DataProvider('arrayPropertyTypeSyntaxProvider')] @@ -199,9 +199,9 @@ public function testOpenApiSchemaGenerationForArrayProperty(string $propertyName $json = json_decode($result, true); $definitions = $json['definitions']; - $this->assertArrayHasKey('TestApiDocHashmapArrayObjectIssue.jsonld', $definitions); + $this->assertArrayHasKey('TestApiDocHashmapArrayObjectIssue.jsonld.output', $definitions); - $ressourceDefinitions = $definitions['TestApiDocHashmapArrayObjectIssue.jsonld']['allOf'][1]; + $ressourceDefinitions = $definitions['TestApiDocHashmapArrayObjectIssue.jsonld.output']['allOf'][1]; $this->assertEquals('object', $ressourceDefinitions['type']); $this->assertEquals($expectedProperties, $ressourceDefinitions['properties'][$propertyName]); @@ -214,7 +214,7 @@ public static function arrayPropertyTypeSyntaxProvider(): \Generator [ 'type' => 'array', 'items' => [ - '$ref' => '#/definitions/Foo.jsonld', + '$ref' => '#/definitions/Foo.jsonld.output', ], ], ]; @@ -223,7 +223,7 @@ public static function arrayPropertyTypeSyntaxProvider(): \Generator [ 'type' => 'array', 'items' => [ - '$ref' => '#/definitions/Foo.jsonld', + '$ref' => '#/definitions/Foo.jsonld.output', ], ], ]; @@ -232,7 +232,7 @@ public static function arrayPropertyTypeSyntaxProvider(): \Generator [ 'type' => 'object', 'additionalProperties' => [ - '$ref' => '#/definitions/Foo.jsonld', + '$ref' => '#/definitions/Foo.jsonld.output', ], ], ]; diff --git a/tests/OpenApi/Command/OpenApiCommandTest.php b/tests/OpenApi/Command/OpenApiCommandTest.php index 6e9619c7df4..be6812cffa9 100644 --- a/tests/OpenApi/Command/OpenApiCommandTest.php +++ b/tests/OpenApi/Command/OpenApiCommandTest.php @@ -146,9 +146,9 @@ public function testBackedEnumExamplesAreNotLost(): void $this->assertArrayNotHasKey('example', $properties['ordinal']); // jsonldContext }; - $assertExample($json['components']['schemas']['Issue6317']['properties'], 'id'); - $assertExample($json['components']['schemas']['Issue6317.jsonld']['allOf'][1]['properties'], 'id'); - $this->assertEquals($json['components']['schemas']['Issue6317.jsonhal']['allOf'][1]['$ref'], '#/components/schemas/Issue6317'); + $assertExample($json['components']['schemas']['Issue6317.output']['properties'], 'id'); + $assertExample($json['components']['schemas']['Issue6317.jsonld.output']['allOf'][1]['properties'], 'id'); + $this->assertEquals($json['components']['schemas']['Issue6317.jsonhal.output']['allOf'][1]['$ref'], '#/components/schemas/Issue6317.output'); } private function assertYaml(string $data): void @@ -167,7 +167,7 @@ public function testFilterXApiPlatformTag(): void $result = $this->tester->getDisplay(); $res = json_decode($result, true, 512, \JSON_THROW_ON_ERROR); - $this->assertArrayHasKey('Crud', $res['components']['schemas']); + $this->assertArrayHasKey('Crud.output', $res['components']['schemas']); $this->assertArrayNotHasKey('/cruds/{id}', $res['paths']); $this->assertArrayHasKey('/cruds', $res['paths']); $this->assertArrayNotHasKey('post', $res['paths']['/cruds']);