diff --git a/src/Hydra/JsonSchema/SchemaFactory.php b/src/Hydra/JsonSchema/SchemaFactory.php index 83a1b15e6c..55f443ba2e 100644 --- a/src/Hydra/JsonSchema/SchemaFactory.php +++ b/src/Hydra/JsonSchema/SchemaFactory.php @@ -109,7 +109,7 @@ public function buildSchema(string $className, string $format = 'jsonld', string // Strictly speaking, it is possible to include `@id` or `@context` in the input, // but the generated JSON Schema does not include `"additionalProperties": false` by default, // so it is possible to include `@id` or `@context` in the input even if the input schema is a JSON schema. - if (Schema::TYPE_INPUT === $type) { + if (Schema::TYPE_INPUT === $type && 'jsonapi' !== $format) { $format = 'json'; } diff --git a/src/JsonApi/JsonSchema/SchemaFactory.php b/src/JsonApi/JsonSchema/SchemaFactory.php index d4b0ab154f..141feb286f 100644 --- a/src/JsonApi/JsonSchema/SchemaFactory.php +++ b/src/JsonApi/JsonSchema/SchemaFactory.php @@ -128,6 +128,9 @@ public function buildSchema(string $className, string $format = 'jsonapi', strin $schema = $this->schemaFactory->buildSchema($className, 'json', $type, $operation, $schema, $jsonApiSerializerContext, $forceCollection); $definitionName = $this->definitionNameFactory->create($className, $format, $inputOrOutputClass, $operation, $jsonApiSerializerContext + ['schema_type' => $type]); + if (Schema::TYPE_INPUT === $type) { + $definitionName .= '.input'; + } $prefix = $this->getSchemaUriPrefix($schema->getVersion()); $definitions = $schema->getDefinitions(); $collectionKey = $schema->getItemsDefinitionKey(); @@ -295,7 +298,7 @@ private function buildDefinitionPropertiesSchema(string $key, string $className, if ($relation = $this->getRelationship($className, $propertyName, $serializerContext)) { [$isOne, $relatedClasses] = $relation; $refs = []; - foreach ($relatedClasses as $relatedClassName => $hasOperations) { + foreach (Schema::TYPE_OUTPUT === $type ? $relatedClasses : [] as $relatedClassName => $hasOperations) { if (false === $hasOperations) { continue; } @@ -358,6 +361,9 @@ private function buildDefinitionPropertiesSchema(string $key, string $className, 'type' => 'object', 'properties' => $relationships, ]; + } + + if (Schema::TYPE_OUTPUT === $type && \count($relationships) > 0) { $included = [ 'included' => [ 'description' => 'Related resources requested via the "include" query parameter.', diff --git a/tests/Functional/OpenApiTest.php b/tests/Functional/OpenApiTest.php index 7ae7dee890..fc63a7f685 100644 --- a/tests/Functional/OpenApiTest.php +++ b/tests/Functional/OpenApiTest.php @@ -138,6 +138,44 @@ public function testOpenApiReferenceInResponsesUsesDollarRef(): void $this->assertArrayNotHasKey('ref', $responses['401']); } + public function testNonJsonLdRequestBodiesKeepTheirFormatDefinition(): void + { + $json = self::createClient()->request('GET', '/docs', [ + 'headers' => ['Accept' => 'application/vnd.openapi+json'], + ])->toArray(); + + $content = $json['paths']['/dummies']['post']['requestBody']['content']; + + $this->assertSame('#/components/schemas/Dummy', $content['application/xml']['schema']['$ref']); + $this->assertSame('#/components/schemas/Dummy', $content['application/ld+json']['schema']['$ref']); + } + + public function testJsonApiRequestBodyUsesItsOwnInputDefinition(): void + { + $json = self::createClient()->request('GET', '/docs', [ + 'headers' => ['Accept' => 'application/vnd.openapi+json'], + ])->toArray(); + $schemas = $json['components']['schemas']; + + $inputName = $this->definitionName($json['paths']['/dummies']['post']['requestBody']['content']['application/vnd.api+json']['schema']['$ref']); + $outputName = $this->definitionName($json['paths']['/dummies/{id}']['get']['responses']['200']['content']['application/vnd.api+json']['schema']['$ref']); + + $this->assertSame('Dummy.jsonapi', $outputName); + $this->assertNotSame($outputName, $inputName); + + $input = $schemas[$inputName]; + $this->assertSame(['type'], $input['properties']['data']['required']); + $this->assertArrayHasKey('attributes', $input['properties']['data']['properties']); + $this->assertArrayNotHasKey('included', $input['properties']); + + $this->assertSame(['type', 'id'], $schemas[$outputName]['properties']['data']['required']); + } + + private function definitionName(string $ref): string + { + return substr($ref, \strlen('#/components/schemas/')); + } + public function testDeprecatedPutDoesNotLeakIntoNestedResourceSchema(): void { $response = self::createClient()->request('GET', '/docs', [