Skip to content

Commit

Permalink
fix(jsonapi): handle multiple relation classes, unrelated unions
Browse files Browse the repository at this point in the history
  • Loading branch information
GwendolenLynch committed Apr 16, 2024
1 parent af61482 commit ed67674
Show file tree
Hide file tree
Showing 2 changed files with 51 additions and 16 deletions.
31 changes: 17 additions & 14 deletions src/JsonApi/JsonSchema/SchemaFactory.php
Expand Up @@ -184,17 +184,21 @@ private function buildDefinitionPropertiesSchema(string $key, string $className,
$relatedDefinitions = [];
foreach ($properties as $propertyName => $property) {
if ($relation = $this->getRelationship($className, $propertyName, $serializerContext)) {
[$isOne, $hasOperations, $relatedClassName] = $relation;
if (false === $hasOperations) {
continue;
}
[$isOne, $relatedClasses] = $relation;
$refs = [];
foreach ($relatedClasses as $relatedClassName => $hasOperations) {
if (false === $hasOperations) {
continue;
}

$operation = $this->findOperation($relatedClassName, $type, $operation, $serializerContext);
$inputOrOutputClass = $this->findOutputClass($relatedClassName, $type, $operation, $serializerContext);
$serializerContext ??= $this->getSerializerContext($operation, $type);
$definitionName = $this->definitionNameFactory->create($relatedClassName, $format, $inputOrOutputClass, $operation, $serializerContext);
$ref = Schema::VERSION_OPENAPI === $schema->getVersion() ? '#/components/schemas/'.$definitionName : '#/definitions/'.$definitionName;
$relatedDefinitions[$propertyName] = ['$ref' => $ref];
$operation = $this->findOperation($relatedClassName, $type, $operation, $serializerContext);
$inputOrOutputClass = $this->findOutputClass($relatedClassName, $type, $operation, $serializerContext);
$serializerContext ??= $this->getSerializerContext($operation, $type);
$definitionName = $this->definitionNameFactory->create($relatedClassName, $format, $inputOrOutputClass, $operation, $serializerContext);
$ref = Schema::VERSION_OPENAPI === $schema->getVersion() ? '#/components/schemas/'.$definitionName : '#/definitions/'.$definitionName;
$refs[$ref] = '$ref';
}
$relatedDefinitions[$propertyName] = array_flip($refs);
if ($isOne) {
$relationships[$propertyName]['properties']['data'] = self::RELATION_PROPS;
continue;
Expand All @@ -203,7 +207,6 @@ private function buildDefinitionPropertiesSchema(string $key, string $className,
'type' => 'array',
'items' => self::RELATION_PROPS,
];
continue;
}
if ('id' === $propertyName) {
$attributes['_id'] = $property;
Expand Down Expand Up @@ -264,7 +267,7 @@ private function getRelationship(string $resourceClass, string $property, ?array
$types = $propertyMetadata->getBuiltinTypes() ?? [];
$isRelationship = false;
$isOne = $isMany = false;
$className = $hasOperations = null;
$relatedClasses = [];

foreach ($types as $type) {
if ($type->isCollection()) {
Expand All @@ -281,9 +284,9 @@ private function getRelationship(string $resourceClass, string $property, ?array
$operation = $resourceMetadata->getOperation();
// @see https://github.com/api-platform/core/issues/5501
// @see https://github.com/api-platform/core/pull/5722
$hasOperations ??= $operation->canRead();
$relatedClasses[$className] = $operation->canRead();
}

return $isRelationship ? [$isOne, $hasOperations, $className] : null;
return $isRelationship ? [$isOne, $relatedClasses] : null;
}
}
36 changes: 34 additions & 2 deletions tests/JsonSchema/Command/JsonSchemaGenerateCommandTest.php
Expand Up @@ -124,9 +124,9 @@ public function testArraySchemaWithReference(): void
]);
}

public function testArraySchemaWithMultipleUnionTypes(): void
public function testArraySchemaWithMultipleUnionTypesJsonLd(): void
{
$this->tester->run(['command' => 'api:json-schema:generate', 'resource' => 'ApiPlatform\Tests\Fixtures\TestBundle\Entity\Issue6212\Nest', '--type' => 'output']);
$this->tester->run(['command' => 'api:json-schema:generate', 'resource' => 'ApiPlatform\Tests\Fixtures\TestBundle\Entity\Issue6212\Nest', '--type' => 'output', '--format' => 'jsonld']);
$result = $this->tester->getDisplay();
$json = json_decode($result, associative: true);

Expand All @@ -140,6 +140,38 @@ public function testArraySchemaWithMultipleUnionTypes(): void
$this->assertArrayHasKey('Robin.jsonld', $json['definitions']);
}

public function testArraySchemaWithMultipleUnionTypesJsonApi(): void
{
$this->tester->run(['command' => 'api:json-schema:generate', 'resource' => 'ApiPlatform\Tests\Fixtures\TestBundle\Entity\Issue6212\Nest', '--type' => 'output', '--format' => 'jsonapi']);
$result = $this->tester->getDisplay();
$json = json_decode($result, associative: true);

$this->assertEquals($json['definitions']['Nest.jsonapi']['properties']['data']['properties']['attributes']['properties']['owner']['anyOf'], [
['$ref' => '#/definitions/Wren.jsonapi'],
['$ref' => '#/definitions/Robin.jsonapi'],
['type' => 'null'],
]);

$this->assertArrayHasKey('Wren.jsonapi', $json['definitions']);
$this->assertArrayHasKey('Robin.jsonapi', $json['definitions']);
}

public function testArraySchemaWithMultipleUnionTypesJsonHal(): void
{
$this->tester->run(['command' => 'api:json-schema:generate', 'resource' => 'ApiPlatform\Tests\Fixtures\TestBundle\Entity\Issue6212\Nest', '--type' => 'output', '--format' => 'jsonhal']);
$result = $this->tester->getDisplay();
$json = json_decode($result, associative: true);

$this->assertEquals($json['definitions']['Nest.jsonhal']['properties']['owner']['anyOf'], [
['$ref' => '#/definitions/Wren.jsonhal'],
['$ref' => '#/definitions/Robin.jsonhal'],
['type' => 'null'],
]);

$this->assertArrayHasKey('Wren.jsonhal', $json['definitions']);
$this->assertArrayHasKey('Robin.jsonhal', $json['definitions']);
}

/**
* TODO: add deprecation (TypeFactory will be deprecated in api platform 3.3).
*
Expand Down

0 comments on commit ed67674

Please sign in to comment.