Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(jsonschema) don't skip remaining multiple union types #6223

Merged
merged 1 commit into from Mar 19, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
25 changes: 17 additions & 8 deletions src/JsonSchema/SchemaFactory.php
Expand Up @@ -196,10 +196,13 @@
// property schema is created in SchemaPropertyMetadataFactory, but it cannot build resource reference ($ref)
// complete property schema with resource reference ($ref) only if it's related to an object
$version = $schema->getVersion();
$subSchema = new Schema($version);
$subSchema->setDefinitions($schema->getDefinitions()); // Populate definitions of the main schema
$refs = [];
$isNullable = null;

foreach ($types as $type) {
$subSchema = new Schema($version);
$subSchema->setDefinitions($schema->getDefinitions()); // Populate definitions of the main schema

// TODO: in 3.3 add trigger_deprecation() as type factories are not used anymore, we moved this logic to SchemaPropertyMetadataFactory so that it gets cached
if ($typeFromFactory = $this->typeFactory?->getType($type, 'jsonschema', $propertyMetadata->isReadableLink(), $serializerContext)) {
$propertySchema = $typeFromFactory;
Expand Down Expand Up @@ -230,14 +233,20 @@
break;
}

if ($type->isNullable()) {
$propertySchema['anyOf'] = [['$ref' => $subSchema['$ref']], ['type' => 'null']];
} else {
$propertySchema['$ref'] = $subSchema['$ref'];
}
$refs[] = ['$ref' => $subSchema['$ref']];
$isNullable = $isNullable ?? $type->isNullable();

Check warning on line 237 in src/JsonSchema/SchemaFactory.php

View check run for this annotation

Codecov / codecov/patch

src/JsonSchema/SchemaFactory.php#L236-L237

Added lines #L236 - L237 were not covered by tests
}

if ($isNullable) {
$refs[] = ['type' => 'null'];

Check warning on line 241 in src/JsonSchema/SchemaFactory.php

View check run for this annotation

Codecov / codecov/patch

src/JsonSchema/SchemaFactory.php#L241

Added line #L241 was not covered by tests
}

if (($c = \count($refs)) > 1) {
$propertySchema['anyOf'] = $refs;
unset($propertySchema['type']);

Check warning on line 246 in src/JsonSchema/SchemaFactory.php

View check run for this annotation

Codecov / codecov/patch

src/JsonSchema/SchemaFactory.php#L245-L246

Added lines #L245 - L246 were not covered by tests
vincentchalamon marked this conversation as resolved.
Show resolved Hide resolved
} elseif (1 === $c) {
$propertySchema['$ref'] = $refs[0]['$ref'];

Check warning on line 248 in src/JsonSchema/SchemaFactory.php

View check run for this annotation

Codecov / codecov/patch

src/JsonSchema/SchemaFactory.php#L248

Added line #L248 was not covered by tests
unset($propertySchema['type']);
break;
}

$schema->getDefinitions()[$definitionName]['properties'][$normalizedPropertyName] = new \ArrayObject($propertySchema);
Expand Down
21 changes: 21 additions & 0 deletions tests/Fixtures/TestBundle/ApiResource/Issue6212/Bird.php
@@ -0,0 +1,21 @@
<?php

/*
* This file is part of the API Platform project.
*
* (c) Kévin Dunglas <dunglas@gmail.com>
*
* 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\Tests\Fixtures\TestBundle\ApiResource\Issue6212;

interface Bird extends \JsonSerializable
{
public function getName(): ?string;

public function getAge(): ?int;
}
35 changes: 35 additions & 0 deletions tests/Fixtures/TestBundle/ApiResource/Issue6212/Robin.php
@@ -0,0 +1,35 @@
<?php

/*
* This file is part of the API Platform project.
*
* (c) Kévin Dunglas <dunglas@gmail.com>
*
* 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\Tests\Fixtures\TestBundle\ApiResource\Issue6212;

final class Robin implements Bird
{
public ?string $name = null;
public ?int $age = null;

public function getName(): ?string
{
return $this->name;
}

public function getAge(): ?int
{
return $this->age;
}

public function jsonSerialize(): array
{
return get_object_vars($this);
}
}
36 changes: 36 additions & 0 deletions tests/Fixtures/TestBundle/ApiResource/Issue6212/Wren.php
@@ -0,0 +1,36 @@
<?php

/*
* This file is part of the API Platform project.
*
* (c) Kévin Dunglas <dunglas@gmail.com>
*
* 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\Tests\Fixtures\TestBundle\ApiResource\Issue6212;

final class Wren implements Bird
{
public ?string $name = null;
public ?int $age = null;
public ?int $weight = null;

public function getName(): ?string
{
return $this->name;
}

public function getAge(): ?int
{
return $this->age;
}

public function jsonSerialize(): array
{
return get_object_vars($this);
}
}
50 changes: 50 additions & 0 deletions tests/Fixtures/TestBundle/Entity/Issue6212/Nest.php
@@ -0,0 +1,50 @@
<?php

/*
* This file is part of the API Platform project.
*
* (c) Kévin Dunglas <dunglas@gmail.com>
*
* 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\Tests\Fixtures\TestBundle\Entity\Issue6212;

use ApiPlatform\Metadata\ApiResource;
use ApiPlatform\Tests\Fixtures\TestBundle\ApiResource\Issue6212\Bird;
use ApiPlatform\Tests\Fixtures\TestBundle\ApiResource\Issue6212\Robin;
use ApiPlatform\Tests\Fixtures\TestBundle\ApiResource\Issue6212\Wren;
use Doctrine\ORM\Mapping as ORM;

#[ApiResource]
#[ORM\Entity]
class Nest
{
#[ORM\Id]
#[ORM\GeneratedValue]
#[ORM\Column]
private ?int $id = null;

#[ORM\Column(type: 'json')]
private ?Bird $owner;

public function getId(): ?int
{
return $this->id;
}

public function getOwner(): ?Bird
{
return $this->owner;
}

public function setOwner(Wren|Robin|null $owner): static
vincentchalamon marked this conversation as resolved.
Show resolved Hide resolved
{
$this->owner = $owner;

return $this;
}
}
16 changes: 16 additions & 0 deletions tests/JsonSchema/Command/JsonSchemaGenerateCommandTest.php
Expand Up @@ -124,6 +124,22 @@ public function testArraySchemaWithReference(): void
]);
}

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

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

$this->assertArrayHasKey('Wren.jsonld', $json['definitions']);
$this->assertArrayHasKey('Robin.jsonld', $json['definitions']);
vincentchalamon marked this conversation as resolved.
Show resolved Hide resolved
}

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