• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

api-platform / core / 14836358929

05 May 2025 12:24PM UTC coverage: 8.396% (-15.0%) from 23.443%
14836358929

push

github

soyuka
test: property info deprecation

0 of 300 new or added lines in 4 files covered. (0.0%)

2655 existing lines in 165 files now uncovered.

13444 of 160118 relevant lines covered (8.4%)

22.88 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

0.0
/src/JsonSchema/Tests/SchemaFactoryTest.php
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;
15

16
use ApiPlatform\JsonSchema\DefinitionNameFactory;
17
use ApiPlatform\JsonSchema\Schema;
18
use ApiPlatform\JsonSchema\SchemaFactory;
19
use ApiPlatform\JsonSchema\Tests\Fixtures\ApiResource\OverriddenOperationDummy;
20
use ApiPlatform\JsonSchema\Tests\Fixtures\DummyResourceInterface;
21
use ApiPlatform\JsonSchema\Tests\Fixtures\Enum\GenderTypeEnum;
22
use ApiPlatform\JsonSchema\Tests\Fixtures\NotAResource;
23
use ApiPlatform\JsonSchema\Tests\Fixtures\NotAResourceWithUnionIntersectTypes;
24
use ApiPlatform\JsonSchema\Tests\Fixtures\Serializable;
25
use ApiPlatform\Metadata\ApiProperty;
26
use ApiPlatform\Metadata\ApiResource;
27
use ApiPlatform\Metadata\Operations;
28
use ApiPlatform\Metadata\Property\Factory\PropertyMetadataFactoryInterface;
29
use ApiPlatform\Metadata\Property\Factory\PropertyNameCollectionFactoryInterface;
30
use ApiPlatform\Metadata\Property\PropertyNameCollection;
31
use ApiPlatform\Metadata\Put;
32
use ApiPlatform\Metadata\Resource\Factory\ResourceMetadataCollectionFactoryInterface;
33
use ApiPlatform\Metadata\Resource\ResourceMetadataCollection;
34
use ApiPlatform\Metadata\ResourceClassResolverInterface;
35
use PHPUnit\Framework\Attributes\IgnoreDeprecations;
36
use PHPUnit\Framework\TestCase;
37
use Prophecy\Argument;
38
use Prophecy\PhpUnit\ProphecyTrait;
39
use Symfony\Component\PropertyInfo\Type as LegacyType;
40
use Symfony\Component\Serializer\Normalizer\AbstractNormalizer;
41
use Symfony\Component\TypeInfo\Type;
42

43
class SchemaFactoryTest extends TestCase
44
{
45
    use ProphecyTrait;
46

47
    #[IgnoreDeprecations]
48
    public function testBuildSchemaForNonResourceClassLegacy(): void
49
    {
NEW
50
        $this->expectUserDeprecationMessage('Since api-platform/metadata 4.2: The "ApiPlatform\Metadata\ApiProperty::withBuiltinTypes()" method is deprecated, use "ApiPlatform\Metadata\ApiProperty::withNativeType()" instead.');
×
NEW
51
        $resourceMetadataFactoryProphecy = $this->prophesize(ResourceMetadataCollectionFactoryInterface::class);
×
52

NEW
53
        $propertyNameCollectionFactoryProphecy = $this->prophesize(PropertyNameCollectionFactoryInterface::class);
×
NEW
54
        $propertyNameCollectionFactoryProphecy->create(NotAResource::class, Argument::cetera())->willReturn(new PropertyNameCollection(['foo', 'bar', 'genderType']));
×
55

NEW
56
        $propertyMetadataFactoryProphecy = $this->prophesize(PropertyMetadataFactoryInterface::class);
×
NEW
57
        $propertyMetadataFactoryProphecy->create(NotAResource::class, 'foo', Argument::cetera())->willReturn(
×
NEW
58
            (new ApiProperty())
×
NEW
59
                ->withBuiltinTypes([new LegacyType(LegacyType::BUILTIN_TYPE_STRING)])
×
NEW
60
                ->withReadable(true)
×
NEW
61
                ->withSchema(['type' => 'string'])
×
NEW
62
        );
×
NEW
63
        $propertyMetadataFactoryProphecy->create(NotAResource::class, 'bar', Argument::cetera())->willReturn(
×
NEW
64
            (new ApiProperty())
×
NEW
65
                ->withBuiltinTypes([new LegacyType(LegacyType::BUILTIN_TYPE_INT)])
×
NEW
66
                ->withReadable(true)
×
NEW
67
                ->withDefault('default_bar')
×
NEW
68
                ->withExample('example_bar')
×
NEW
69
                ->withSchema(['type' => 'integer', 'default' => 'default_bar', 'example' => 'example_bar'])
×
NEW
70
        );
×
NEW
71
        $propertyMetadataFactoryProphecy->create(NotAResource::class, 'genderType', Argument::cetera())->willReturn(
×
NEW
72
            (new ApiProperty())
×
NEW
73
                ->withBuiltinTypes([new LegacyType(LegacyType::BUILTIN_TYPE_OBJECT)])
×
NEW
74
                ->withReadable(true)
×
NEW
75
                ->withDefault('male')
×
NEW
76
                ->withSchema(['type' => 'object', 'default' => 'male', 'example' => 'male'])
×
NEW
77
        );
×
78

NEW
79
        $resourceClassResolverProphecy = $this->prophesize(ResourceClassResolverInterface::class);
×
NEW
80
        $resourceClassResolverProphecy->isResourceClass(NotAResource::class)->willReturn(false);
×
81

NEW
82
        $definitionNameFactory = new DefinitionNameFactory(['jsonapi' => true, 'jsonhal' => true, 'jsonld' => true]);
×
83

NEW
84
        $schemaFactory = new SchemaFactory(
×
NEW
85
            resourceMetadataFactory: $resourceMetadataFactoryProphecy->reveal(),
×
NEW
86
            propertyNameCollectionFactory: $propertyNameCollectionFactoryProphecy->reveal(),
×
NEW
87
            propertyMetadataFactory: $propertyMetadataFactoryProphecy->reveal(),
×
NEW
88
            resourceClassResolver: $resourceClassResolverProphecy->reveal(),
×
NEW
89
            definitionNameFactory: $definitionNameFactory,
×
NEW
90
        );
×
NEW
91
        $resultSchema = $schemaFactory->buildSchema(NotAResource::class);
×
92

NEW
93
        $rootDefinitionKey = $resultSchema->getRootDefinitionKey();
×
NEW
94
        $definitions = $resultSchema->getDefinitions();
×
95

NEW
96
        $this->assertSame((new \ReflectionClass(NotAResource::class))->getShortName(), $rootDefinitionKey);
×
NEW
97
        $this->assertTrue(isset($definitions[$rootDefinitionKey]));
×
NEW
98
        $this->assertArrayHasKey('type', $definitions[$rootDefinitionKey]);
×
NEW
99
        $this->assertSame('object', $definitions[$rootDefinitionKey]['type']);
×
NEW
100
        $this->assertArrayNotHasKey('additionalProperties', $definitions[$rootDefinitionKey]);
×
NEW
101
        $this->assertArrayHasKey('properties', $definitions[$rootDefinitionKey]);
×
NEW
102
        $this->assertArrayHasKey('foo', $definitions[$rootDefinitionKey]['properties']);
×
NEW
103
        $this->assertArrayHasKey('type', $definitions[$rootDefinitionKey]['properties']['foo']);
×
NEW
104
        $this->assertArrayNotHasKey('default', $definitions[$rootDefinitionKey]['properties']['foo']);
×
NEW
105
        $this->assertArrayNotHasKey('example', $definitions[$rootDefinitionKey]['properties']['foo']);
×
NEW
106
        $this->assertSame('string', $definitions[$rootDefinitionKey]['properties']['foo']['type']);
×
NEW
107
        $this->assertArrayHasKey('bar', $definitions[$rootDefinitionKey]['properties']);
×
NEW
108
        $this->assertArrayHasKey('type', $definitions[$rootDefinitionKey]['properties']['bar']);
×
NEW
109
        $this->assertArrayHasKey('default', $definitions[$rootDefinitionKey]['properties']['bar']);
×
NEW
110
        $this->assertArrayHasKey('example', $definitions[$rootDefinitionKey]['properties']['bar']);
×
NEW
111
        $this->assertSame('integer', $definitions[$rootDefinitionKey]['properties']['bar']['type']);
×
NEW
112
        $this->assertSame('default_bar', $definitions[$rootDefinitionKey]['properties']['bar']['default']);
×
NEW
113
        $this->assertSame('example_bar', $definitions[$rootDefinitionKey]['properties']['bar']['example']);
×
114

NEW
115
        $this->assertArrayHasKey('genderType', $definitions[$rootDefinitionKey]['properties']);
×
NEW
116
        $this->assertArrayHasKey('type', $definitions[$rootDefinitionKey]['properties']['genderType']);
×
NEW
117
        $this->assertArrayHasKey('default', $definitions[$rootDefinitionKey]['properties']['genderType']);
×
NEW
118
        $this->assertArrayHasKey('example', $definitions[$rootDefinitionKey]['properties']['genderType']);
×
NEW
119
        $this->assertSame('object', $definitions[$rootDefinitionKey]['properties']['genderType']['type']);
×
NEW
120
        $this->assertSame('male', $definitions[$rootDefinitionKey]['properties']['genderType']['default']);
×
NEW
121
        $this->assertSame('male', $definitions[$rootDefinitionKey]['properties']['genderType']['example']);
×
122
    }
123

124
    public function testBuildSchemaForNonResourceClass(): void
125
    {
126
        $resourceMetadataFactoryProphecy = $this->prophesize(ResourceMetadataCollectionFactoryInterface::class);
×
127

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

131
        $propertyMetadataFactoryProphecy = $this->prophesize(PropertyMetadataFactoryInterface::class);
×
132
        $propertyMetadataFactoryProphecy->create(NotAResource::class, 'foo', Argument::cetera())->willReturn(
×
133
            (new ApiProperty())
×
NEW
134
                ->withNativeType(Type::string())
×
135
                ->withReadable(true)
×
136
                ->withSchema(['type' => 'string'])
×
137
        );
×
138
        $propertyMetadataFactoryProphecy->create(NotAResource::class, 'bar', Argument::cetera())->willReturn(
×
139
            (new ApiProperty())
×
NEW
140
                ->withNativeType(Type::int())
×
141
                ->withReadable(true)
×
142
                ->withDefault('default_bar')
×
143
                ->withExample('example_bar')
×
144
                ->withSchema(['type' => 'integer', 'default' => 'default_bar', 'example' => 'example_bar'])
×
145
        );
×
146
        $propertyMetadataFactoryProphecy->create(NotAResource::class, 'genderType', Argument::cetera())->willReturn(
×
147
            (new ApiProperty())
×
NEW
148
                ->withNativeType(Type::object())
×
149
                ->withReadable(true)
×
150
                ->withDefault('male')
×
151
                ->withSchema(['type' => 'object', 'default' => 'male', 'example' => 'male'])
×
152
        );
×
153

154
        $resourceClassResolverProphecy = $this->prophesize(ResourceClassResolverInterface::class);
×
155
        $resourceClassResolverProphecy->isResourceClass(NotAResource::class)->willReturn(false);
×
156

157
        $definitionNameFactory = new DefinitionNameFactory(['jsonapi' => true, 'jsonhal' => true, 'jsonld' => true]);
×
158

159
        $schemaFactory = new SchemaFactory(
×
160
            resourceMetadataFactory: $resourceMetadataFactoryProphecy->reveal(),
×
161
            propertyNameCollectionFactory: $propertyNameCollectionFactoryProphecy->reveal(),
×
162
            propertyMetadataFactory: $propertyMetadataFactoryProphecy->reveal(),
×
163
            resourceClassResolver: $resourceClassResolverProphecy->reveal(),
×
164
            definitionNameFactory: $definitionNameFactory,
×
165
        );
×
166
        $resultSchema = $schemaFactory->buildSchema(NotAResource::class);
×
167

168
        $rootDefinitionKey = $resultSchema->getRootDefinitionKey();
×
169
        $definitions = $resultSchema->getDefinitions();
×
170

171
        $this->assertSame((new \ReflectionClass(NotAResource::class))->getShortName(), $rootDefinitionKey);
×
172
        $this->assertTrue(isset($definitions[$rootDefinitionKey]));
×
173
        $this->assertArrayHasKey('type', $definitions[$rootDefinitionKey]);
×
174
        $this->assertSame('object', $definitions[$rootDefinitionKey]['type']);
×
175
        $this->assertArrayNotHasKey('additionalProperties', $definitions[$rootDefinitionKey]);
×
176
        $this->assertArrayHasKey('properties', $definitions[$rootDefinitionKey]);
×
177
        $this->assertArrayHasKey('foo', $definitions[$rootDefinitionKey]['properties']);
×
178
        $this->assertArrayHasKey('type', $definitions[$rootDefinitionKey]['properties']['foo']);
×
179
        $this->assertArrayNotHasKey('default', $definitions[$rootDefinitionKey]['properties']['foo']);
×
180
        $this->assertArrayNotHasKey('example', $definitions[$rootDefinitionKey]['properties']['foo']);
×
181
        $this->assertSame('string', $definitions[$rootDefinitionKey]['properties']['foo']['type']);
×
182
        $this->assertArrayHasKey('bar', $definitions[$rootDefinitionKey]['properties']);
×
183
        $this->assertArrayHasKey('type', $definitions[$rootDefinitionKey]['properties']['bar']);
×
184
        $this->assertArrayHasKey('default', $definitions[$rootDefinitionKey]['properties']['bar']);
×
185
        $this->assertArrayHasKey('example', $definitions[$rootDefinitionKey]['properties']['bar']);
×
186
        $this->assertSame('integer', $definitions[$rootDefinitionKey]['properties']['bar']['type']);
×
187
        $this->assertSame('default_bar', $definitions[$rootDefinitionKey]['properties']['bar']['default']);
×
188
        $this->assertSame('example_bar', $definitions[$rootDefinitionKey]['properties']['bar']['example']);
×
189

190
        $this->assertArrayHasKey('genderType', $definitions[$rootDefinitionKey]['properties']);
×
191
        $this->assertArrayHasKey('type', $definitions[$rootDefinitionKey]['properties']['genderType']);
×
192
        $this->assertArrayHasKey('default', $definitions[$rootDefinitionKey]['properties']['genderType']);
×
193
        $this->assertArrayHasKey('example', $definitions[$rootDefinitionKey]['properties']['genderType']);
×
194
        $this->assertSame('object', $definitions[$rootDefinitionKey]['properties']['genderType']['type']);
×
195
        $this->assertSame('male', $definitions[$rootDefinitionKey]['properties']['genderType']['default']);
×
196
        $this->assertSame('male', $definitions[$rootDefinitionKey]['properties']['genderType']['example']);
×
197
    }
198

199
    #[IgnoreDeprecations]
200
    public function testBuildSchemaForNonResourceClassWithUnionIntersectTypesLegacy(): void
201
    {
NEW
202
        $this->expectUserDeprecationMessage('Since api-platform/metadata 4.2: The "ApiPlatform\Metadata\ApiProperty::withBuiltinTypes()" method is deprecated, use "ApiPlatform\Metadata\ApiProperty::withNativeType()" instead.');
×
NEW
203
        $resourceMetadataFactoryProphecy = $this->prophesize(ResourceMetadataCollectionFactoryInterface::class);
×
204

NEW
205
        $propertyNameCollectionFactoryProphecy = $this->prophesize(PropertyNameCollectionFactoryInterface::class);
×
NEW
206
        $propertyNameCollectionFactoryProphecy->create(NotAResourceWithUnionIntersectTypes::class, Argument::cetera())->willReturn(new PropertyNameCollection(['ignoredProperty', 'unionType', 'intersectType']));
×
207

NEW
208
        $propertyMetadataFactoryProphecy = $this->prophesize(PropertyMetadataFactoryInterface::class);
×
NEW
209
        $propertyMetadataFactoryProphecy->create(NotAResourceWithUnionIntersectTypes::class, 'ignoredProperty', Argument::cetera())->willReturn(
×
NEW
210
            (new ApiProperty())
×
NEW
211
                ->withBuiltinTypes([new LegacyType(LegacyType::BUILTIN_TYPE_STRING, nullable: true)])
×
NEW
212
                ->withReadable(true)
×
NEW
213
                ->withSchema(['type' => ['string', 'null']])
×
NEW
214
        );
×
NEW
215
        $propertyMetadataFactoryProphecy->create(NotAResourceWithUnionIntersectTypes::class, 'unionType', Argument::cetera())->willReturn(
×
NEW
216
            (new ApiProperty())
×
NEW
217
                ->withBuiltinTypes([new LegacyType(LegacyType::BUILTIN_TYPE_STRING, nullable: true), new LegacyType(LegacyType::BUILTIN_TYPE_INT, nullable: true), new LegacyType(LegacyType::BUILTIN_TYPE_FLOAT, nullable: true)])
×
NEW
218
                ->withReadable(true)
×
NEW
219
                ->withSchema(['oneOf' => [
×
NEW
220
                    ['type' => ['string', 'null']],
×
NEW
221
                    ['type' => ['integer', 'null']],
×
NEW
222
                ]])
×
NEW
223
        );
×
NEW
224
        $propertyMetadataFactoryProphecy->create(NotAResourceWithUnionIntersectTypes::class, 'intersectType', Argument::cetera())->willReturn(
×
NEW
225
            (new ApiProperty())
×
NEW
226
                ->withBuiltinTypes([new LegacyType(LegacyType::BUILTIN_TYPE_OBJECT, class: Serializable::class), new LegacyType(LegacyType::BUILTIN_TYPE_OBJECT, class: DummyResourceInterface::class)])
×
NEW
227
                ->withReadable(true)
×
NEW
228
                ->withSchema(['type' => 'object'])
×
NEW
229
        );
×
230

NEW
231
        $resourceClassResolverProphecy = $this->prophesize(ResourceClassResolverInterface::class);
×
NEW
232
        $resourceClassResolverProphecy->isResourceClass(NotAResourceWithUnionIntersectTypes::class)->willReturn(false);
×
233

NEW
234
        $definitionNameFactory = new DefinitionNameFactory(['jsonapi' => true, 'jsonhal' => true, 'jsonld' => true]);
×
235

NEW
236
        $schemaFactory = new SchemaFactory(
×
NEW
237
            resourceMetadataFactory: $resourceMetadataFactoryProphecy->reveal(),
×
NEW
238
            propertyNameCollectionFactory: $propertyNameCollectionFactoryProphecy->reveal(),
×
NEW
239
            propertyMetadataFactory: $propertyMetadataFactoryProphecy->reveal(),
×
NEW
240
            resourceClassResolver: $resourceClassResolverProphecy->reveal(),
×
NEW
241
            definitionNameFactory: $definitionNameFactory,
×
NEW
242
        );
×
NEW
243
        $resultSchema = $schemaFactory->buildSchema(NotAResourceWithUnionIntersectTypes::class);
×
244

NEW
245
        $rootDefinitionKey = $resultSchema->getRootDefinitionKey();
×
NEW
246
        $definitions = $resultSchema->getDefinitions();
×
247

NEW
248
        $this->assertSame((new \ReflectionClass(NotAResourceWithUnionIntersectTypes::class))->getShortName(), $rootDefinitionKey);
×
NEW
249
        $this->assertTrue(isset($definitions[$rootDefinitionKey]));
×
NEW
250
        $this->assertArrayHasKey('type', $definitions[$rootDefinitionKey]);
×
NEW
251
        $this->assertSame('object', $definitions[$rootDefinitionKey]['type']);
×
NEW
252
        $this->assertArrayNotHasKey('additionalProperties', $definitions[$rootDefinitionKey]);
×
NEW
253
        $this->assertArrayHasKey('properties', $definitions[$rootDefinitionKey]);
×
254

NEW
255
        $this->assertArrayHasKey('ignoredProperty', $definitions[$rootDefinitionKey]['properties']);
×
NEW
256
        $this->assertArrayHasKey('type', $definitions[$rootDefinitionKey]['properties']['ignoredProperty']);
×
NEW
257
        $this->assertSame(['string', 'null'], $definitions[$rootDefinitionKey]['properties']['ignoredProperty']['type']);
×
258

NEW
259
        $this->assertArrayHasKey('unionType', $definitions[$rootDefinitionKey]['properties']);
×
NEW
260
        $this->assertArrayHasKey('oneOf', $definitions[$rootDefinitionKey]['properties']['unionType']);
×
NEW
261
        $this->assertCount(2, $definitions[$rootDefinitionKey]['properties']['unionType']['oneOf']);
×
NEW
262
        $this->assertArrayHasKey('type', $definitions[$rootDefinitionKey]['properties']['unionType']['oneOf'][0]);
×
NEW
263
        $this->assertSame(['string', 'null'], $definitions[$rootDefinitionKey]['properties']['unionType']['oneOf'][0]['type']);
×
NEW
264
        $this->assertSame(['integer', 'null'], $definitions[$rootDefinitionKey]['properties']['unionType']['oneOf'][1]['type']);
×
265

NEW
266
        $this->assertArrayHasKey('intersectType', $definitions[$rootDefinitionKey]['properties']);
×
NEW
267
        $this->assertArrayHasKey('type', $definitions[$rootDefinitionKey]['properties']['intersectType']);
×
NEW
268
        $this->assertSame('object', $definitions[$rootDefinitionKey]['properties']['intersectType']['type']);
×
269
    }
270

271
    public function testBuildSchemaForNonResourceClassWithUnionIntersectTypes(): void
272
    {
273
        $resourceMetadataFactoryProphecy = $this->prophesize(ResourceMetadataCollectionFactoryInterface::class);
×
274

275
        $propertyNameCollectionFactoryProphecy = $this->prophesize(PropertyNameCollectionFactoryInterface::class);
×
276
        $propertyNameCollectionFactoryProphecy->create(NotAResourceWithUnionIntersectTypes::class, Argument::cetera())->willReturn(new PropertyNameCollection(['ignoredProperty', 'unionType', 'intersectType']));
×
277

278
        $propertyMetadataFactoryProphecy = $this->prophesize(PropertyMetadataFactoryInterface::class);
×
279
        $propertyMetadataFactoryProphecy->create(NotAResourceWithUnionIntersectTypes::class, 'ignoredProperty', Argument::cetera())->willReturn(
×
280
            (new ApiProperty())
×
NEW
281
                ->withNativeType(Type::nullable(Type::string())) // @phpstan-ignore-line
×
282
                ->withReadable(true)
×
283
                ->withSchema(['type' => ['string', 'null']])
×
284
        );
×
285
        $propertyMetadataFactoryProphecy->create(NotAResourceWithUnionIntersectTypes::class, 'unionType', Argument::cetera())->willReturn(
×
286
            (new ApiProperty())
×
NEW
287
                ->withNativeType(Type::union(Type::string(), Type::int(), Type::float(), Type::null()))
×
288
                ->withReadable(true)
×
289
                ->withSchema(['oneOf' => [
×
290
                    ['type' => ['string', 'null']],
×
291
                    ['type' => ['integer', 'null']],
×
292
                ]])
×
293
        );
×
294
        $propertyMetadataFactoryProphecy->create(NotAResourceWithUnionIntersectTypes::class, 'intersectType', Argument::cetera())->willReturn(
×
295
            (new ApiProperty())
×
NEW
296
                ->withNativeType(Type::intersection(Type::object(Serializable::class), Type::object(DummyResourceInterface::class)))
×
297
                ->withReadable(true)
×
298
                ->withSchema(['type' => 'object'])
×
299
        );
×
300

301
        $resourceClassResolverProphecy = $this->prophesize(ResourceClassResolverInterface::class);
×
302
        $resourceClassResolverProphecy->isResourceClass(NotAResourceWithUnionIntersectTypes::class)->willReturn(false);
×
303

304
        $definitionNameFactory = new DefinitionNameFactory(['jsonapi' => true, 'jsonhal' => true, 'jsonld' => true]);
×
305

306
        $schemaFactory = new SchemaFactory(
×
307
            resourceMetadataFactory: $resourceMetadataFactoryProphecy->reveal(),
×
308
            propertyNameCollectionFactory: $propertyNameCollectionFactoryProphecy->reveal(),
×
309
            propertyMetadataFactory: $propertyMetadataFactoryProphecy->reveal(),
×
310
            resourceClassResolver: $resourceClassResolverProphecy->reveal(),
×
311
            definitionNameFactory: $definitionNameFactory,
×
312
        );
×
313
        $resultSchema = $schemaFactory->buildSchema(NotAResourceWithUnionIntersectTypes::class);
×
314

315
        $rootDefinitionKey = $resultSchema->getRootDefinitionKey();
×
316
        $definitions = $resultSchema->getDefinitions();
×
317

318
        $this->assertSame((new \ReflectionClass(NotAResourceWithUnionIntersectTypes::class))->getShortName(), $rootDefinitionKey);
×
319
        $this->assertTrue(isset($definitions[$rootDefinitionKey]));
×
320
        $this->assertArrayHasKey('type', $definitions[$rootDefinitionKey]);
×
321
        $this->assertSame('object', $definitions[$rootDefinitionKey]['type']);
×
322
        $this->assertArrayNotHasKey('additionalProperties', $definitions[$rootDefinitionKey]);
×
323
        $this->assertArrayHasKey('properties', $definitions[$rootDefinitionKey]);
×
324

325
        $this->assertArrayHasKey('ignoredProperty', $definitions[$rootDefinitionKey]['properties']);
×
326
        $this->assertArrayHasKey('type', $definitions[$rootDefinitionKey]['properties']['ignoredProperty']);
×
327
        $this->assertSame(['string', 'null'], $definitions[$rootDefinitionKey]['properties']['ignoredProperty']['type']);
×
328

329
        $this->assertArrayHasKey('unionType', $definitions[$rootDefinitionKey]['properties']);
×
330
        $this->assertArrayHasKey('oneOf', $definitions[$rootDefinitionKey]['properties']['unionType']);
×
331
        $this->assertCount(2, $definitions[$rootDefinitionKey]['properties']['unionType']['oneOf']);
×
332
        $this->assertArrayHasKey('type', $definitions[$rootDefinitionKey]['properties']['unionType']['oneOf'][0]);
×
333
        $this->assertSame(['string', 'null'], $definitions[$rootDefinitionKey]['properties']['unionType']['oneOf'][0]['type']);
×
334
        $this->assertSame(['integer', 'null'], $definitions[$rootDefinitionKey]['properties']['unionType']['oneOf'][1]['type']);
×
335

336
        $this->assertArrayHasKey('intersectType', $definitions[$rootDefinitionKey]['properties']);
×
337
        $this->assertArrayHasKey('type', $definitions[$rootDefinitionKey]['properties']['intersectType']);
×
338
        $this->assertSame('object', $definitions[$rootDefinitionKey]['properties']['intersectType']['type']);
×
339
    }
340

341
    #[IgnoreDeprecations]
342
    public function testBuildSchemaWithSerializerGroupsLegacy(): void
343
    {
NEW
344
        $this->expectUserDeprecationMessage('Since api-platform/metadata 4.2: The "ApiPlatform\Metadata\ApiProperty::withBuiltinTypes()" method is deprecated, use "ApiPlatform\Metadata\ApiProperty::withNativeType()" instead.');
×
345
        $shortName = (new \ReflectionClass(OverriddenOperationDummy::class))->getShortName();
×
346
        $resourceMetadataFactoryProphecy = $this->prophesize(ResourceMetadataCollectionFactoryInterface::class);
×
347
        $operation = (new Put())->withName('put')->withNormalizationContext([
×
348
            'groups' => 'overridden_operation_dummy_put',
×
349
            AbstractNormalizer::ALLOW_EXTRA_ATTRIBUTES => false,
×
350
        ])->withShortName($shortName)->withValidationContext(['groups' => ['validation_groups_dummy_put']]);
×
351
        $resourceMetadataFactoryProphecy->create(OverriddenOperationDummy::class)
×
352
            ->willReturn(
×
353
                new ResourceMetadataCollection(OverriddenOperationDummy::class, [
×
354
                    (new ApiResource())->withOperations(new Operations(['put' => $operation])),
×
355
                ])
×
356
            );
×
357

358
        $serializerGroup = 'custom_operation_dummy';
×
359

360
        $propertyNameCollectionFactoryProphecy = $this->prophesize(PropertyNameCollectionFactoryInterface::class);
×
361
        $propertyNameCollectionFactoryProphecy->create(OverriddenOperationDummy::class, Argument::type('array'))->willReturn(new PropertyNameCollection(['alias', 'description', 'genderType']));
×
362

363
        $propertyMetadataFactoryProphecy = $this->prophesize(PropertyMetadataFactoryInterface::class);
×
364
        $propertyMetadataFactoryProphecy->create(OverriddenOperationDummy::class, 'alias', Argument::type('array'))->willReturn(
×
365
            (new ApiProperty())
×
NEW
366
                ->withBuiltinTypes([new LegacyType(LegacyType::BUILTIN_TYPE_STRING)])
×
367
                ->withReadable(true)
×
368
                ->withSchema(['type' => 'string'])
×
369
        );
×
370
        $propertyMetadataFactoryProphecy->create(OverriddenOperationDummy::class, 'description', Argument::type('array'))->willReturn(
×
371
            (new ApiProperty())
×
NEW
372
                ->withBuiltinTypes([new LegacyType(LegacyType::BUILTIN_TYPE_STRING)])
×
373
                ->withReadable(true)
×
374
                ->withSchema(['type' => 'string'])
×
375
        );
×
376
        $propertyMetadataFactoryProphecy->create(OverriddenOperationDummy::class, 'genderType', Argument::type('array'))->willReturn(
×
377
            (new ApiProperty())
×
NEW
378
                ->withBuiltinTypes([new LegacyType(LegacyType::BUILTIN_TYPE_OBJECT, false, GenderTypeEnum::class)])
×
379
                ->withReadable(true)
×
380
                ->withDefault(GenderTypeEnum::MALE)
×
381
                ->withSchema(['type' => 'object'])
×
382
        );
×
383

384
        $resourceClassResolverProphecy = $this->prophesize(ResourceClassResolverInterface::class);
×
385
        $resourceClassResolverProphecy->isResourceClass(OverriddenOperationDummy::class)->willReturn(true);
×
386
        $resourceClassResolverProphecy->isResourceClass(GenderTypeEnum::class)->willReturn(true);
×
387

388
        $definitionNameFactory = new DefinitionNameFactory(['jsonapi' => true, 'jsonhal' => true, 'jsonld' => true]);
×
389

390
        $schemaFactory = new SchemaFactory(
×
391
            resourceMetadataFactory: $resourceMetadataFactoryProphecy->reveal(),
×
392
            propertyNameCollectionFactory: $propertyNameCollectionFactoryProphecy->reveal(),
×
393
            propertyMetadataFactory: $propertyMetadataFactoryProphecy->reveal(),
×
394
            resourceClassResolver: $resourceClassResolverProphecy->reveal(),
×
395
            definitionNameFactory: $definitionNameFactory,
×
396
        );
×
397
        $resultSchema = $schemaFactory->buildSchema(OverriddenOperationDummy::class, 'json', Schema::TYPE_OUTPUT, null, null, ['groups' => $serializerGroup, AbstractNormalizer::ALLOW_EXTRA_ATTRIBUTES => false]);
×
398

399
        $rootDefinitionKey = $resultSchema->getRootDefinitionKey();
×
400
        $definitions = $resultSchema->getDefinitions();
×
401

402
        $this->assertSame((new \ReflectionClass(OverriddenOperationDummy::class))->getShortName().'-'.$serializerGroup, $rootDefinitionKey);
×
403
        $this->assertTrue(isset($definitions[$rootDefinitionKey]));
×
404
        $this->assertArrayHasKey('type', $definitions[$rootDefinitionKey]);
×
405
        $this->assertSame('object', $definitions[$rootDefinitionKey]['type']);
×
406
        $this->assertFalse($definitions[$rootDefinitionKey]['additionalProperties']);
×
407
        $this->assertArrayHasKey('properties', $definitions[$rootDefinitionKey]);
×
408
        $this->assertArrayHasKey('alias', $definitions[$rootDefinitionKey]['properties']);
×
409
        $this->assertArrayHasKey('type', $definitions[$rootDefinitionKey]['properties']['alias']);
×
410
        $this->assertSame('string', $definitions[$rootDefinitionKey]['properties']['alias']['type']);
×
411
        $this->assertArrayHasKey('description', $definitions[$rootDefinitionKey]['properties']);
×
412
        $this->assertArrayHasKey('type', $definitions[$rootDefinitionKey]['properties']['description']);
×
413
        $this->assertSame('string', $definitions[$rootDefinitionKey]['properties']['description']['type']);
×
414
        $this->assertArrayHasKey('genderType', $definitions[$rootDefinitionKey]['properties']);
×
415
        $this->assertArrayHasKey('type', $definitions[$rootDefinitionKey]['properties']['genderType']);
×
416
        $this->assertArrayNotHasKey('default', $definitions[$rootDefinitionKey]['properties']['genderType']);
×
417
        $this->assertArrayNotHasKey('example', $definitions[$rootDefinitionKey]['properties']['genderType']);
×
418
        $this->assertSame('object', $definitions[$rootDefinitionKey]['properties']['genderType']['type']);
×
419
    }
420

421
    public function testBuildSchemaWithSerializerGroups(): void
422
    {
NEW
423
        $shortName = (new \ReflectionClass(OverriddenOperationDummy::class))->getShortName();
×
NEW
424
        $resourceMetadataFactoryProphecy = $this->prophesize(ResourceMetadataCollectionFactoryInterface::class);
×
NEW
425
        $operation = (new Put())->withName('put')->withNormalizationContext([
×
NEW
426
            'groups' => 'overridden_operation_dummy_put',
×
NEW
427
            AbstractNormalizer::ALLOW_EXTRA_ATTRIBUTES => false,
×
NEW
428
        ])->withShortName($shortName)->withValidationContext(['groups' => ['validation_groups_dummy_put']]);
×
NEW
429
        $resourceMetadataFactoryProphecy->create(OverriddenOperationDummy::class)
×
NEW
430
            ->willReturn(
×
NEW
431
                new ResourceMetadataCollection(OverriddenOperationDummy::class, [
×
NEW
432
                    (new ApiResource())->withOperations(new Operations(['put' => $operation])),
×
NEW
433
                ])
×
NEW
434
            );
×
435

NEW
436
        $serializerGroup = 'custom_operation_dummy';
×
437

NEW
438
        $propertyNameCollectionFactoryProphecy = $this->prophesize(PropertyNameCollectionFactoryInterface::class);
×
NEW
439
        $propertyNameCollectionFactoryProphecy->create(OverriddenOperationDummy::class, Argument::type('array'))->willReturn(new PropertyNameCollection(['alias', 'description', 'genderType']));
×
440

NEW
441
        $propertyMetadataFactoryProphecy = $this->prophesize(PropertyMetadataFactoryInterface::class);
×
NEW
442
        $propertyMetadataFactoryProphecy->create(OverriddenOperationDummy::class, 'alias', Argument::type('array'))->willReturn(
×
NEW
443
            (new ApiProperty())
×
NEW
444
                ->withNativeType(Type::string())
×
NEW
445
                ->withReadable(true)
×
NEW
446
                ->withSchema(['type' => 'string'])
×
NEW
447
        );
×
NEW
448
        $propertyMetadataFactoryProphecy->create(OverriddenOperationDummy::class, 'description', Argument::type('array'))->willReturn(
×
NEW
449
            (new ApiProperty())
×
NEW
450
                ->withNativeType(Type::string())
×
NEW
451
                ->withReadable(true)
×
NEW
452
                ->withSchema(['type' => 'string'])
×
NEW
453
        );
×
NEW
454
        $propertyMetadataFactoryProphecy->create(OverriddenOperationDummy::class, 'genderType', Argument::type('array'))->willReturn(
×
NEW
455
            (new ApiProperty())
×
NEW
456
                ->withNativeType(Type::enum(GenderTypeEnum::class))
×
NEW
457
                ->withReadable(true)
×
NEW
458
                ->withDefault(GenderTypeEnum::MALE)
×
NEW
459
                ->withSchema(['type' => 'object'])
×
NEW
460
        );
×
NEW
461
        $resourceClassResolverProphecy = $this->prophesize(ResourceClassResolverInterface::class);
×
NEW
462
        $resourceClassResolverProphecy->isResourceClass(OverriddenOperationDummy::class)->willReturn(true);
×
NEW
463
        $resourceClassResolverProphecy->isResourceClass(GenderTypeEnum::class)->willReturn(true);
×
464

NEW
465
        $definitionNameFactory = new DefinitionNameFactory(['jsonapi' => true, 'jsonhal' => true, 'jsonld' => true]);
×
466

NEW
467
        $schemaFactory = new SchemaFactory(
×
NEW
468
            resourceMetadataFactory: $resourceMetadataFactoryProphecy->reveal(),
×
NEW
469
            propertyNameCollectionFactory: $propertyNameCollectionFactoryProphecy->reveal(),
×
NEW
470
            propertyMetadataFactory: $propertyMetadataFactoryProphecy->reveal(),
×
NEW
471
            resourceClassResolver: $resourceClassResolverProphecy->reveal(),
×
NEW
472
            definitionNameFactory: $definitionNameFactory,
×
NEW
473
        );
×
NEW
474
        $resultSchema = $schemaFactory->buildSchema(OverriddenOperationDummy::class, 'json', Schema::TYPE_OUTPUT, null, null, ['groups' => $serializerGroup, AbstractNormalizer::ALLOW_EXTRA_ATTRIBUTES => false]);
×
475

NEW
476
        $rootDefinitionKey = $resultSchema->getRootDefinitionKey();
×
NEW
477
        $definitions = $resultSchema->getDefinitions();
×
478

NEW
479
        $this->assertSame((new \ReflectionClass(OverriddenOperationDummy::class))->getShortName().'-'.$serializerGroup, $rootDefinitionKey);
×
NEW
480
        $this->assertTrue(isset($definitions[$rootDefinitionKey]));
×
NEW
481
        $this->assertArrayHasKey('type', $definitions[$rootDefinitionKey]);
×
NEW
482
        $this->assertSame('object', $definitions[$rootDefinitionKey]['type']);
×
NEW
483
        $this->assertFalse($definitions[$rootDefinitionKey]['additionalProperties']);
×
NEW
484
        $this->assertArrayHasKey('properties', $definitions[$rootDefinitionKey]);
×
NEW
485
        $this->assertArrayHasKey('alias', $definitions[$rootDefinitionKey]['properties']);
×
NEW
486
        $this->assertArrayHasKey('type', $definitions[$rootDefinitionKey]['properties']['alias']);
×
NEW
487
        $this->assertSame('string', $definitions[$rootDefinitionKey]['properties']['alias']['type']);
×
NEW
488
        $this->assertArrayHasKey('description', $definitions[$rootDefinitionKey]['properties']);
×
NEW
489
        $this->assertArrayHasKey('type', $definitions[$rootDefinitionKey]['properties']['description']);
×
NEW
490
        $this->assertSame('string', $definitions[$rootDefinitionKey]['properties']['description']['type']);
×
NEW
491
        $this->assertArrayHasKey('genderType', $definitions[$rootDefinitionKey]['properties']);
×
NEW
492
        $this->assertArrayHasKey('type', $definitions[$rootDefinitionKey]['properties']['genderType']);
×
NEW
493
        $this->assertArrayNotHasKey('default', $definitions[$rootDefinitionKey]['properties']['genderType']);
×
NEW
494
        $this->assertArrayNotHasKey('example', $definitions[$rootDefinitionKey]['properties']['genderType']);
×
NEW
495
        $this->assertSame('object', $definitions[$rootDefinitionKey]['properties']['genderType']['type']);
×
496
    }
497

498
    #[IgnoreDeprecations]
499
    public function testBuildSchemaForAssociativeArrayLegacy(): void
500
    {
NEW
501
        $this->expectUserDeprecationMessage('Since api-platform/metadata 4.2: The "ApiPlatform\Metadata\ApiProperty::withBuiltinTypes()" method is deprecated, use "ApiPlatform\Metadata\ApiProperty::withNativeType()" instead.');
×
NEW
502
        $resourceMetadataFactoryProphecy = $this->prophesize(ResourceMetadataCollectionFactoryInterface::class);
×
503

NEW
504
        $propertyNameCollectionFactoryProphecy = $this->prophesize(PropertyNameCollectionFactoryInterface::class);
×
NEW
505
        $propertyNameCollectionFactoryProphecy->create(NotAResource::class, Argument::cetera())->willReturn(new PropertyNameCollection(['foo', 'bar']));
×
506

NEW
507
        $propertyMetadataFactoryProphecy = $this->prophesize(PropertyMetadataFactoryInterface::class);
×
NEW
508
        $propertyMetadataFactoryProphecy->create(NotAResource::class, 'foo', Argument::cetera())->willReturn(
×
NEW
509
            (new ApiProperty())
×
NEW
510
                ->withBuiltinTypes([new LegacyType(LegacyType::BUILTIN_TYPE_ARRAY, false, null, true, new LegacyType(LegacyType::BUILTIN_TYPE_INT), new LegacyType(LegacyType::BUILTIN_TYPE_STRING))])
×
NEW
511
                ->withReadable(true)
×
NEW
512
                ->withSchema(['type' => 'array', 'items' => ['string', 'int']])
×
NEW
513
        );
×
NEW
514
        $propertyMetadataFactoryProphecy->create(NotAResource::class, 'bar', Argument::cetera())->willReturn(
×
NEW
515
            (new ApiProperty())
×
NEW
516
                ->withBuiltinTypes([new LegacyType(LegacyType::BUILTIN_TYPE_ARRAY, false, null, true, new LegacyType(LegacyType::BUILTIN_TYPE_STRING), new LegacyType(LegacyType::BUILTIN_TYPE_STRING))])
×
NEW
517
                ->withReadable(true)
×
NEW
518
                ->withSchema(['type' => 'object', 'additionalProperties' => 'string'])
×
NEW
519
        );
×
520

NEW
521
        $resourceClassResolverProphecy = $this->prophesize(ResourceClassResolverInterface::class);
×
NEW
522
        $resourceClassResolverProphecy->isResourceClass(NotAResource::class)->willReturn(false);
×
523

NEW
524
        $definitionNameFactory = new DefinitionNameFactory(['jsonapi' => true, 'jsonhal' => true, 'jsonld' => true]);
×
525

NEW
526
        $schemaFactory = new SchemaFactory(
×
NEW
527
            resourceMetadataFactory: $resourceMetadataFactoryProphecy->reveal(),
×
NEW
528
            propertyNameCollectionFactory: $propertyNameCollectionFactoryProphecy->reveal(),
×
NEW
529
            propertyMetadataFactory: $propertyMetadataFactoryProphecy->reveal(),
×
NEW
530
            resourceClassResolver: $resourceClassResolverProphecy->reveal(),
×
NEW
531
            definitionNameFactory: $definitionNameFactory,
×
NEW
532
        );
×
NEW
533
        $resultSchema = $schemaFactory->buildSchema(NotAResource::class);
×
534

NEW
535
        $rootDefinitionKey = $resultSchema->getRootDefinitionKey();
×
NEW
536
        $definitions = $resultSchema->getDefinitions();
×
537

NEW
538
        $this->assertSame((new \ReflectionClass(NotAResource::class))->getShortName(), $rootDefinitionKey);
×
NEW
539
        $this->assertTrue(isset($definitions[$rootDefinitionKey]));
×
NEW
540
        $this->assertArrayHasKey('properties', $definitions[$rootDefinitionKey]);
×
NEW
541
        $this->assertArrayHasKey('foo', $definitions[$rootDefinitionKey]['properties']);
×
NEW
542
        $this->assertArrayHasKey('type', $definitions[$rootDefinitionKey]['properties']['foo']);
×
NEW
543
        $this->assertArrayNotHasKey('additionalProperties', $definitions[$rootDefinitionKey]['properties']['foo']);
×
NEW
544
        $this->assertSame('array', $definitions[$rootDefinitionKey]['properties']['foo']['type']);
×
NEW
545
        $this->assertArrayHasKey('bar', $definitions[$rootDefinitionKey]['properties']);
×
NEW
546
        $this->assertArrayHasKey('type', $definitions[$rootDefinitionKey]['properties']['bar']);
×
NEW
547
        $this->assertArrayHasKey('additionalProperties', $definitions[$rootDefinitionKey]['properties']['bar']);
×
NEW
548
        $this->assertSame('object', $definitions[$rootDefinitionKey]['properties']['bar']['type']);
×
NEW
549
        $this->assertSame('string', $definitions[$rootDefinitionKey]['properties']['bar']['additionalProperties']);
×
550
    }
551

552
    public function testBuildSchemaForAssociativeArray(): void
553
    {
554
        $resourceMetadataFactoryProphecy = $this->prophesize(ResourceMetadataCollectionFactoryInterface::class);
×
555

556
        $propertyNameCollectionFactoryProphecy = $this->prophesize(PropertyNameCollectionFactoryInterface::class);
×
557
        $propertyNameCollectionFactoryProphecy->create(NotAResource::class, Argument::cetera())->willReturn(new PropertyNameCollection(['foo', 'bar']));
×
558

559
        $propertyMetadataFactoryProphecy = $this->prophesize(PropertyMetadataFactoryInterface::class);
×
560
        $propertyMetadataFactoryProphecy->create(NotAResource::class, 'foo', Argument::cetera())->willReturn(
×
561
            (new ApiProperty())
×
NEW
562
                ->withNativeType(Type::list(Type::string()))
×
563
                ->withReadable(true)
×
564
                ->withSchema(['type' => 'array', 'items' => ['string', 'int']])
×
565
        );
×
566
        $propertyMetadataFactoryProphecy->create(NotAResource::class, 'bar', Argument::cetera())->willReturn(
×
567
            (new ApiProperty())
×
NEW
568
                ->withNativeType(Type::dict(Type::string()))
×
569
                ->withReadable(true)
×
570
                ->withSchema(['type' => 'object', 'additionalProperties' => 'string'])
×
571
        );
×
572

573
        $resourceClassResolverProphecy = $this->prophesize(ResourceClassResolverInterface::class);
×
574
        $resourceClassResolverProphecy->isResourceClass(NotAResource::class)->willReturn(false);
×
575

576
        $definitionNameFactory = new DefinitionNameFactory(['jsonapi' => true, 'jsonhal' => true, 'jsonld' => true]);
×
577

578
        $schemaFactory = new SchemaFactory(
×
579
            resourceMetadataFactory: $resourceMetadataFactoryProphecy->reveal(),
×
580
            propertyNameCollectionFactory: $propertyNameCollectionFactoryProphecy->reveal(),
×
581
            propertyMetadataFactory: $propertyMetadataFactoryProphecy->reveal(),
×
582
            resourceClassResolver: $resourceClassResolverProphecy->reveal(),
×
583
            definitionNameFactory: $definitionNameFactory,
×
584
        );
×
585
        $resultSchema = $schemaFactory->buildSchema(NotAResource::class);
×
586

587
        $rootDefinitionKey = $resultSchema->getRootDefinitionKey();
×
588
        $definitions = $resultSchema->getDefinitions();
×
589

590
        $this->assertSame((new \ReflectionClass(NotAResource::class))->getShortName(), $rootDefinitionKey);
×
591
        $this->assertTrue(isset($definitions[$rootDefinitionKey]));
×
592
        $this->assertArrayHasKey('properties', $definitions[$rootDefinitionKey]);
×
593
        $this->assertArrayHasKey('foo', $definitions[$rootDefinitionKey]['properties']);
×
594
        $this->assertArrayHasKey('type', $definitions[$rootDefinitionKey]['properties']['foo']);
×
595
        $this->assertArrayNotHasKey('additionalProperties', $definitions[$rootDefinitionKey]['properties']['foo']);
×
596
        $this->assertSame('array', $definitions[$rootDefinitionKey]['properties']['foo']['type']);
×
597
        $this->assertArrayHasKey('bar', $definitions[$rootDefinitionKey]['properties']);
×
598
        $this->assertArrayHasKey('type', $definitions[$rootDefinitionKey]['properties']['bar']);
×
599
        $this->assertArrayHasKey('additionalProperties', $definitions[$rootDefinitionKey]['properties']['bar']);
×
600
        $this->assertSame('object', $definitions[$rootDefinitionKey]['properties']['bar']['type']);
×
601
        $this->assertSame('string', $definitions[$rootDefinitionKey]['properties']['bar']['additionalProperties']);
×
602
    }
603
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc