Skip to content

Commit dfe4fd7

Browse files
elliotbruneelElliot Bruneel
andauthored
fix: handle generic array in JsonSchema (#7414)
Co-authored-by: Elliot Bruneel <elliot.bruneel@wizbii.com>
1 parent 6fe6795 commit dfe4fd7

File tree

4 files changed

+68
-3
lines changed

4 files changed

+68
-3
lines changed

src/JsonSchema/SchemaFactory.php

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
use Symfony\Component\TypeInfo\Type\BuiltinType;
3030
use Symfony\Component\TypeInfo\Type\CollectionType;
3131
use Symfony\Component\TypeInfo\Type\CompositeTypeInterface;
32+
use Symfony\Component\TypeInfo\Type\GenericType;
3233
use Symfony\Component\TypeInfo\Type\ObjectType;
3334
use Symfony\Component\TypeInfo\TypeIdentifier;
3435

@@ -349,11 +350,17 @@ private function buildPropertySchema(Schema $schema, string $definitionName, str
349350
$valueType = TypeHelper::getCollectionValueType($t);
350351
}
351352

352-
if (!$valueType instanceof ObjectType) {
353+
if (!$valueType instanceof ObjectType && !$valueType instanceof GenericType) {
353354
continue;
354355
}
355356

356-
$className = $valueType->getClassName();
357+
if ($valueType instanceof ObjectType) {
358+
$className = $valueType->getClassName();
359+
} else {
360+
// GenericType
361+
$className = $valueType->getWrappedType()->getClassName();
362+
}
363+
357364
$subSchemaInstance = new Schema($version);
358365
$subSchemaInstance->setDefinitions($schema->getDefinitions());
359366
$subSchemaFactory = $this->schemaFactory ?: $this;
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the API Platform project.
5+
*
6+
* (c) Kévin Dunglas <dunglas@gmail.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
declare(strict_types=1);
13+
14+
namespace ApiPlatform\JsonSchema\Tests\Fixtures;
15+
16+
/**
17+
* @template T of object
18+
*/
19+
class GenericChild
20+
{
21+
public string $property;
22+
}

src/JsonSchema/Tests/Fixtures/NotAResource.php

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,11 +22,15 @@
2222
*/
2323
class NotAResource
2424
{
25+
/**
26+
* @param array<GenericChild<object>> $items
27+
*/
2528
public function __construct(
2629
#[Groups('contain_non_resource')]
2730
private $foo,
2831
#[Groups('contain_non_resource')]
2932
private $bar,
33+
private array $items,
3034
) {
3135
}
3236

@@ -39,4 +43,12 @@ public function getBar()
3943
{
4044
return $this->bar;
4145
}
46+
47+
/**
48+
* @return array<GenericChild<object>>
49+
*/
50+
public function getItems()
51+
{
52+
return $this->items;
53+
}
4254
}

src/JsonSchema/Tests/SchemaFactoryTest.php

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
use ApiPlatform\JsonSchema\Tests\Fixtures\ApiResource\OverriddenOperationDummy;
2020
use ApiPlatform\JsonSchema\Tests\Fixtures\DummyResourceInterface;
2121
use ApiPlatform\JsonSchema\Tests\Fixtures\Enum\GenderTypeEnum;
22+
use ApiPlatform\JsonSchema\Tests\Fixtures\GenericChild;
2223
use ApiPlatform\JsonSchema\Tests\Fixtures\NotAResource;
2324
use ApiPlatform\JsonSchema\Tests\Fixtures\NotAResourceWithUnionIntersectTypes;
2425
use ApiPlatform\JsonSchema\Tests\Fixtures\Serializable;
@@ -126,7 +127,7 @@ public function testBuildSchemaForNonResourceClass(): void
126127
$resourceMetadataFactoryProphecy = $this->prophesize(ResourceMetadataCollectionFactoryInterface::class);
127128

128129
$propertyNameCollectionFactoryProphecy = $this->prophesize(PropertyNameCollectionFactoryInterface::class);
129-
$propertyNameCollectionFactoryProphecy->create(NotAResource::class, Argument::cetera())->willReturn(new PropertyNameCollection(['foo', 'bar', 'genderType']));
130+
$propertyNameCollectionFactoryProphecy->create(NotAResource::class, Argument::cetera())->willReturn(new PropertyNameCollection(['foo', 'bar', 'genderType', 'items']));
130131

131132
$propertyMetadataFactoryProphecy = $this->prophesize(PropertyMetadataFactoryInterface::class);
132133
$propertyMetadataFactoryProphecy->create(NotAResource::class, 'foo', Argument::cetera())->willReturn(
@@ -150,9 +151,28 @@ public function testBuildSchemaForNonResourceClass(): void
150151
->withDefault('male')
151152
->withSchema(['type' => 'object', 'default' => 'male', 'example' => 'male'])
152153
);
154+
$propertyMetadataFactoryProphecy->create(NotAResource::class, 'items', Argument::cetera())->willReturn(
155+
(new ApiProperty())
156+
->withNativeType(
157+
Type::generic(Type::object(GenericChild::class), Type::int()),
158+
)
159+
->withReadable(true)
160+
->withSchema(['type' => Schema::UNKNOWN_TYPE])
161+
);
162+
163+
$propertyNameCollectionFactoryProphecy->create(GenericChild::class, Argument::cetera())
164+
->willReturn(new PropertyNameCollection(['property']));
165+
$propertyMetadataFactoryProphecy->create(GenericChild::class, 'property', Argument::cetera())
166+
->willReturn(
167+
(new ApiProperty())
168+
->withNativeType(Type::string())
169+
->withReadable(true)
170+
->withSchema(['type' => 'string'])
171+
);
153172

154173
$resourceClassResolverProphecy = $this->prophesize(ResourceClassResolverInterface::class);
155174
$resourceClassResolverProphecy->isResourceClass(NotAResource::class)->willReturn(false);
175+
$resourceClassResolverProphecy->isResourceClass(GenericChild::class)->willReturn(false);
156176

157177
$definitionNameFactory = new DefinitionNameFactory();
158178

@@ -194,6 +214,10 @@ public function testBuildSchemaForNonResourceClass(): void
194214
$this->assertSame('object', $definitions[$rootDefinitionKey]['properties']['genderType']['type']);
195215
$this->assertSame('male', $definitions[$rootDefinitionKey]['properties']['genderType']['default']);
196216
$this->assertSame('male', $definitions[$rootDefinitionKey]['properties']['genderType']['example']);
217+
218+
$this->assertArrayHasKey('items', $definitions[$rootDefinitionKey]['properties']);
219+
$this->assertArrayHasKey('$ref', $definitions[$rootDefinitionKey]['properties']['items']);
220+
$this->assertSame('#/definitions/GenericChild', $definitions[$rootDefinitionKey]['properties']['items']['$ref']);
197221
}
198222

199223
#[IgnoreDeprecations]

0 commit comments

Comments
 (0)