diff --git a/src/Hydra/JsonSchema/SchemaFactory.php b/src/Hydra/JsonSchema/SchemaFactory.php index 4328d52f47..4f7cebee57 100644 --- a/src/Hydra/JsonSchema/SchemaFactory.php +++ b/src/Hydra/JsonSchema/SchemaFactory.php @@ -36,9 +36,9 @@ final class SchemaFactory implements SchemaFactoryInterface, SchemaFactoryAwareI use ResourceMetadataTrait; use SchemaUriPrefixTrait; - private const ITEM_BASE_SCHEMA_NAME = 'HydraItemBaseSchema'; - private const ITEM_BASE_SCHEMA_OUTPUT_NAME = 'HydraOutputBaseSchema'; - private const COLLECTION_BASE_SCHEMA_NAME = 'HydraCollectionBaseSchema'; + private const INPUT_ITEM_SCHEMA_NAME = 'HydraInputItemBaseSchema'; + private const OUTPUT_ITEM_SCHEMA_NAME = 'HydraItemBaseSchema'; + private const COLLECTION_SCHEMA_NAME = 'HydraCollectionBaseSchema'; private const BASE_PROP = [ 'type' => 'string', ]; @@ -46,7 +46,8 @@ final class SchemaFactory implements SchemaFactoryInterface, SchemaFactoryAwareI '@id' => self::BASE_PROP, '@type' => self::BASE_PROP, ]; - private const ITEM_BASE_SCHEMA = [ + // This is the base for both input and output, and serves as the complete definition for an INPUT schema. + private const BASE_ITEM_SCHEMA = [ 'type' => 'object', 'properties' => [ '@context' => [ @@ -70,10 +71,10 @@ final class SchemaFactory implements SchemaFactoryInterface, SchemaFactoryAwareI ], ] + self::BASE_PROPS, ]; - - private const ITEM_BASE_SCHEMA_OUTPUT = [ + // The OUTPUT schema is the base schema with the addition of required fields. + private const OUTPUT_ITEM_SCHEMA = [ 'required' => ['@id', '@type'], - ] + self::ITEM_BASE_SCHEMA; + ] + self::BASE_ITEM_SCHEMA; /** * @var array @@ -104,9 +105,10 @@ public function __construct( */ public function buildSchema(string $className, string $format = 'jsonld', string $type = Schema::TYPE_OUTPUT, ?Operation $operation = null, ?Schema $schema = null, ?array $serializerContext = null, bool $forceCollection = false): Schema { - if ('jsonld' !== $format || 'input' === $type) { + if ('jsonld' !== $format) { return $this->schemaFactory->buildSchema($className, $format, $type, $operation, $schema, $serializerContext, $forceCollection); } + if (!$this->isResourceClass($className)) { $operation = null; $inputOrOutputClass = null; @@ -140,10 +142,11 @@ public function buildSchema(string $className, string $format = 'jsonld', string return $schema; } - $baseName = Schema::TYPE_OUTPUT === $type ? self::ITEM_BASE_SCHEMA_NAME : self::ITEM_BASE_SCHEMA_OUTPUT_NAME; + $isOutput = Schema::TYPE_OUTPUT === $type; + $baseName = $isOutput ? self::OUTPUT_ITEM_SCHEMA_NAME : self::INPUT_ITEM_SCHEMA_NAME; if (!isset($definitions[$baseName])) { - $definitions[$baseName] = Schema::TYPE_OUTPUT === $type ? self::ITEM_BASE_SCHEMA_OUTPUT : self::ITEM_BASE_SCHEMA; + $definitions[$baseName] = $isOutput ? self::OUTPUT_ITEM_SCHEMA : self::BASE_ITEM_SCHEMA; } $allOf = new \ArrayObject(['allOf' => [ @@ -171,7 +174,7 @@ public function buildSchema(string $className, string $format = 'jsonld', string $hydraPrefix = $this->getHydraPrefix($serializerContext + $this->defaultContext); - if (!isset($definitions[self::COLLECTION_BASE_SCHEMA_NAME])) { + if (!isset($definitions[self::COLLECTION_SCHEMA_NAME])) { switch ($schema->getVersion()) { // JSON Schema + OpenAPI 3.1 case Schema::VERSION_OPENAPI: @@ -184,7 +187,7 @@ public function buildSchema(string $className, string $format = 'jsonld', string break; } - $definitions[self::COLLECTION_BASE_SCHEMA_NAME] = [ + $definitions[self::COLLECTION_SCHEMA_NAME] = [ 'type' => 'object', 'required' => [ $hydraPrefix.'member', @@ -261,7 +264,7 @@ public function buildSchema(string $className, string $format = 'jsonld', string $schema['type'] = 'object'; $schema['description'] = "$definitionName collection."; $schema['allOf'] = [ - ['$ref' => $prefix.self::COLLECTION_BASE_SCHEMA_NAME], + ['$ref' => $prefix.self::COLLECTION_SCHEMA_NAME], [ 'type' => 'object', 'properties' => [ diff --git a/tests/Functional/JsonSchema/JsonSchemaTest.php b/tests/Functional/JsonSchema/JsonSchemaTest.php index 4145c1ecbb..dc74e6204e 100644 --- a/tests/Functional/JsonSchema/JsonSchemaTest.php +++ b/tests/Functional/JsonSchema/JsonSchemaTest.php @@ -134,23 +134,24 @@ public function testArraySchemaWithReference(): void $schema = $this->schemaFactory->buildSchema(BagOfTests::class, 'jsonld', Schema::TYPE_INPUT); - $this->assertEquals($schema['definitions']['BagOfTests.jsonld-write']['properties']['tests'], new \ArrayObject([ + $base = $schema['definitions']['BagOfTests.jsonld-write']['allOf'][1]; + $this->assertEquals($base['properties']['tests'], new \ArrayObject([ 'type' => 'string', 'foo' => 'bar', ])); - $this->assertEquals($schema['definitions']['BagOfTests.jsonld-write']['properties']['nonResourceTests'], new \ArrayObject([ + $this->assertEquals($base['properties']['nonResourceTests'], new \ArrayObject([ 'type' => 'array', 'items' => [ '$ref' => '#/definitions/NonResourceTestEntity.jsonld-write', ], ])); - $this->assertEquals($schema['definitions']['BagOfTests.jsonld-write']['properties']['description'], new \ArrayObject([ + $this->assertEquals($base['properties']['description'], new \ArrayObject([ 'maxLength' => 255, ])); - $this->assertEquals($schema['definitions']['BagOfTests.jsonld-write']['properties']['type'], new \ArrayObject([ + $this->assertEquals($base['properties']['type'], new \ArrayObject([ '$ref' => '#/definitions/TestEntity.jsonld-write', ])); } diff --git a/tests/JsonSchema/Command/JsonSchemaGenerateCommandTest.php b/tests/JsonSchema/Command/JsonSchemaGenerateCommandTest.php index da735dd744..8103049ea8 100644 --- a/tests/JsonSchema/Command/JsonSchemaGenerateCommandTest.php +++ b/tests/JsonSchema/Command/JsonSchemaGenerateCommandTest.php @@ -13,6 +13,7 @@ namespace ApiPlatform\Tests\JsonSchema\Command; +use ApiPlatform\Symfony\Bundle\Test\Constraint\MatchesJsonSchema; use ApiPlatform\Tests\Fixtures\TestBundle\ApiResource\Animal; use ApiPlatform\Tests\Fixtures\TestBundle\ApiResource\AnimalObservation; use ApiPlatform\Tests\Fixtures\TestBundle\ApiResource\BackedEnumIntegerResource; @@ -116,9 +117,8 @@ public function testExecuteWithJsonldTypeInput(): void $this->tester->run(['command' => 'api:json-schema:generate', 'resource' => $this->entityClass, '--operation' => '_api_/dummies{._format}_post', '--format' => 'jsonld', '--type' => 'input']); $result = $this->tester->getDisplay(); - $this->assertStringNotContainsString('@id', $result); - $this->assertStringNotContainsString('@context', $result); - $this->assertStringNotContainsString('@type', $result); + $constraint = new MatchesJsonSchema(json_decode($result)); + static::assertThat(['name' => 'test'], $constraint); } public function testExecuteWithJsonMergePatchTypeInput(): void