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

api-platform / core / 6067528200

04 Sep 2023 12:12AM UTC coverage: 36.875% (-21.9%) from 58.794%
6067528200

Pull #5791

github

web-flow
Merge 64157e578 into d09cfc9d2
Pull Request #5791: fix: strip down any sql function name

3096 of 3096 new or added lines in 205 files covered. (100.0%)

9926 of 26918 relevant lines covered (36.87%)

6.5 hits per line

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

0.0
/src/OpenApi/Tests/Serializer/OpenApiNormalizerTest.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\OpenApi\Tests\Serializer;
15

16
use ApiPlatform\JsonSchema\SchemaFactory;
17
use ApiPlatform\JsonSchema\TypeFactory;
18
use ApiPlatform\Metadata\ApiProperty;
19
use ApiPlatform\Metadata\ApiResource;
20
use ApiPlatform\Metadata\Delete;
21
use ApiPlatform\Metadata\Get;
22
use ApiPlatform\Metadata\GetCollection;
23
use ApiPlatform\Metadata\HttpOperation;
24
use ApiPlatform\Metadata\Operations;
25
use ApiPlatform\Metadata\Post;
26
use ApiPlatform\Metadata\Property\Factory\PropertyMetadataFactoryInterface;
27
use ApiPlatform\Metadata\Property\Factory\PropertyNameCollectionFactoryInterface;
28
use ApiPlatform\Metadata\Property\PropertyNameCollection;
29
use ApiPlatform\Metadata\Put;
30
use ApiPlatform\Metadata\Resource\Factory\ResourceMetadataCollectionFactoryInterface;
31
use ApiPlatform\Metadata\Resource\Factory\ResourceNameCollectionFactoryInterface;
32
use ApiPlatform\Metadata\Resource\ResourceMetadataCollection;
33
use ApiPlatform\Metadata\Resource\ResourceNameCollection;
34
use ApiPlatform\OpenApi\Factory\OpenApiFactory;
35
use ApiPlatform\OpenApi\Model\Components;
36
use ApiPlatform\OpenApi\Model\Info;
37
use ApiPlatform\OpenApi\Model\Operation as OpenApiOperation;
38
use ApiPlatform\OpenApi\Model\Parameter;
39
use ApiPlatform\OpenApi\Model\Paths;
40
use ApiPlatform\OpenApi\Model\Schema;
41
use ApiPlatform\OpenApi\Model\Server;
42
use ApiPlatform\OpenApi\OpenApi;
43
use ApiPlatform\OpenApi\Options;
44
use ApiPlatform\OpenApi\Serializer\OpenApiNormalizer;
45
use ApiPlatform\State\Pagination\PaginationOptions;
46
use ApiPlatform\Tests\Fixtures\TestBundle\Entity\Dummy;
47
use PHPUnit\Framework\TestCase;
48
use Prophecy\Argument;
49
use Prophecy\PhpUnit\ProphecyTrait;
50
use Psr\Container\ContainerInterface;
51
use Symfony\Component\PropertyInfo\Type;
52
use Symfony\Component\Serializer\Encoder\JsonEncoder;
53
use Symfony\Component\Serializer\NameConverter\CamelCaseToSnakeCaseNameConverter;
54
use Symfony\Component\Serializer\Normalizer\ObjectNormalizer;
55
use Symfony\Component\Serializer\Serializer;
56

57
class OpenApiNormalizerTest extends TestCase
58
{
59
    use ProphecyTrait;
60

61
    private const OPERATION_FORMATS = [
62
        'input_formats' => ['jsonld' => ['application/ld+json']],
63
        'output_formats' => ['jsonld' => ['application/ld+json']],
64
    ];
65

66
    public function testNormalizeWithSchemas(): void
67
    {
68
        $openApi = new OpenApi(new Info('My API', '1.0.0', 'An amazing API'), [new Server('https://example.com')], new Paths(), new Components(new \ArrayObject(['z' => new Schema(), 'b' => new Schema()])));
×
69
        $encoders = [new JsonEncoder()];
×
70
        $normalizers = [new ObjectNormalizer()];
×
71

72
        $serializer = new Serializer($normalizers, $encoders);
×
73
        $normalizers[0]->setSerializer($serializer);
×
74

75
        $normalizer = new OpenApiNormalizer($normalizers[0]);
×
76

77
        $array = $normalizer->normalize($openApi);
×
78

79
        $this->assertSame(array_keys($array['components']['schemas']), ['b', 'z']);
×
80
    }
81

82
    public function testNormalizeWithEmptySchemas(): void
83
    {
84
        $openApi = new OpenApi(new Info('My API', '1.0.0', 'An amazing API'), [new Server('https://example.com')], new Paths(), new Components(new \ArrayObject()));
×
85
        $encoders = [new JsonEncoder()];
×
86
        $normalizers = [new ObjectNormalizer()];
×
87

88
        $serializer = new Serializer($normalizers, $encoders);
×
89
        $normalizers[0]->setSerializer($serializer);
×
90

91
        $normalizer = new OpenApiNormalizer($normalizers[0]);
×
92

93
        $array = $normalizer->normalize($openApi);
×
94
        $this->assertCount(0, $array['components']['schemas']);
×
95
    }
96

97
    public function testNormalize(): void
98
    {
99
        $resourceNameCollectionFactoryProphecy = $this->prophesize(ResourceNameCollectionFactoryInterface::class);
×
100
        $resourceNameCollectionFactoryProphecy->create()->shouldBeCalled()->willReturn(new ResourceNameCollection([Dummy::class, 'Zorro']));
×
101
        $propertyNameCollectionFactoryProphecy = $this->prophesize(PropertyNameCollectionFactoryInterface::class);
×
102
        $propertyNameCollectionFactoryProphecy->create(Dummy::class, Argument::any())->shouldBeCalled()->willReturn(new PropertyNameCollection(['id', 'name', 'description', 'dummyDate']));
×
103
        $propertyNameCollectionFactoryProphecy->create('Zorro', Argument::any())->shouldBeCalled()->willReturn(new PropertyNameCollection(['id']));
×
104

105
        $baseOperation = (new HttpOperation())->withTypes(['http://schema.example.com/Dummy'])
×
106
                                              ->withInputFormats(self::OPERATION_FORMATS['input_formats'])->withOutputFormats(self::OPERATION_FORMATS['output_formats'])
×
107
                                              ->withClass(Dummy::class)
×
108
                                              ->withShortName('Dummy')
×
109
                                              ->withDescription('This is a dummy.');
×
110

111
        $dummyMetadata = new ResourceMetadataCollection(Dummy::class, [
×
112
            (new ApiResource())->withOperations(new Operations(
×
113
                [
×
114
                    'get' => (new Get())->withUriTemplate('/dummies/{id}')->withOperation($baseOperation),
×
115
                    'put' => (new Put())->withUriTemplate('/dummies/{id}')->withOperation($baseOperation),
×
116
                    'delete' => (new Delete())->withUriTemplate('/dummies/{id}')->withOperation($baseOperation),
×
117
                    'get_collection' => (new GetCollection())->withUriTemplate('/dummies')->withOperation($baseOperation),
×
118
                    'post' => (new Post())->withUriTemplate('/dummies')->withOpenapi(new OpenApiOperation(
×
119
                        security: [],
×
120
                        servers: ['url' => '/test'],
×
121
                    ))->withOperation($baseOperation),
×
122
                ]
×
123
            )),
×
124
        ]);
×
125

126
        $zorroBaseOperation = (new HttpOperation())
×
127
            ->withTypes(['http://schema.example.com/Zorro'])
×
128
            ->withInputFormats(self::OPERATION_FORMATS['input_formats'])->withOutputFormats(self::OPERATION_FORMATS['output_formats'])
×
129
            ->withClass('Zorro')
×
130
            ->withShortName('Zorro')
×
131
            ->withDescription('This is zorro.');
×
132

133
        $zorroMetadata = new ResourceMetadataCollection(Dummy::class, [
×
134
            (new ApiResource())->withOperations(new Operations(
×
135
                [
×
136
                    'get' => (new Get())->withUriTemplate('/zorros/{id}')->withOperation($zorroBaseOperation),
×
137
                    'get_collection' => (new GetCollection())->withUriTemplate('/zorros')->withOperation($zorroBaseOperation),
×
138
                ]
×
139
            )),
×
140
        ]);
×
141

142
        $resourceCollectionMetadataFactoryProphecy = $this->prophesize(ResourceMetadataCollectionFactoryInterface::class);
×
143
        $resourceCollectionMetadataFactoryProphecy->create(Dummy::class)->shouldBeCalled()->willReturn($dummyMetadata);
×
144
        $resourceCollectionMetadataFactoryProphecy->create('Zorro')->shouldBeCalled()->willReturn($zorroMetadata);
×
145

146
        $propertyMetadataFactoryProphecy = $this->prophesize(PropertyMetadataFactoryInterface::class);
×
147
        $propertyMetadataFactoryProphecy->create(Dummy::class, 'id', Argument::any())->shouldBeCalled()->willReturn(
×
148
            (new ApiProperty())
×
149
                ->withBuiltinTypes([new Type(Type::BUILTIN_TYPE_INT)])
×
150
                ->withDescription('This is an id.')
×
151
                ->withReadable(true)
×
152
                ->withWritable(false)
×
153
                ->withIdentifier(true)
×
154
                ->withSchema(['type' => 'integer', 'description' => 'This is an id.', 'readOnly' => true])
×
155
        );
×
156
        $propertyMetadataFactoryProphecy->create(Dummy::class, 'name', Argument::any())->shouldBeCalled()->willReturn(
×
157
            (new ApiProperty())
×
158
                ->withBuiltinTypes([new Type(Type::BUILTIN_TYPE_STRING)])
×
159
                ->withDescription('This is a name.')
×
160
                ->withReadable(true)
×
161
                ->withWritable(true)
×
162
                ->withReadableLink(true)
×
163
                ->withWritableLink(true)
×
164
                ->withRequired(false)
×
165
                ->withIdentifier(false)
×
166
                ->withSchema(['type' => 'string', 'description' => 'This is a name.', 'minLength' => 3, 'maxLength' => 20, 'pattern' => '^dummyPattern$'])
×
167
        );
×
168
        $propertyMetadataFactoryProphecy->create(Dummy::class, 'description', Argument::any())->shouldBeCalled()->willReturn(
×
169
            (new ApiProperty())
×
170
                ->withBuiltinTypes([new Type(Type::BUILTIN_TYPE_STRING)])
×
171
                ->withDescription('This is an initializable but not writable property.')
×
172
                ->withReadable(true)
×
173
                ->withWritable(false)
×
174
                ->withReadableLink(true)
×
175
                ->withWritableLink(true)
×
176
                ->withRequired(false)
×
177
                ->withIdentifier(false)
×
178
                ->withSchema(['type' => 'string', 'readOnly' => true, 'description' => 'This is an initializable but not writable property.'])
×
179
        );
×
180
        $propertyMetadataFactoryProphecy->create(Dummy::class, 'dummyDate', Argument::any())->shouldBeCalled()->willReturn(
×
181
            (new ApiProperty())
×
182
                ->withBuiltinTypes([new Type(Type::BUILTIN_TYPE_OBJECT, true, \DateTime::class)])
×
183
                ->withDescription('This is a \DateTimeInterface object.')
×
184
                ->withReadable(true)
×
185
                ->withWritable(true)
×
186
                ->withReadableLink(true)
×
187
                ->withWritableLink(true)
×
188
                ->withRequired(false)
×
189
                ->withIdentifier(false)
×
190
                ->withSchema(['type' => 'string', 'format' => 'date-time', 'description' => 'This is a \DateTimeInterface object.'])
×
191
        );
×
192

193
        $propertyMetadataFactoryProphecy->create('Zorro', 'id', Argument::any())->shouldBeCalled()->willReturn(
×
194
            (new ApiProperty())
×
195
                ->withBuiltinTypes([new Type(Type::BUILTIN_TYPE_INT)])
×
196
                ->withDescription('This is an id.')
×
197
                ->withReadable(true)
×
198
                ->withWritable(false)
×
199
                ->withIdentifier(true)
×
200
                ->withSchema(['type' => 'integer', 'description' => 'This is an id.', 'readOnly' => true])
×
201
        );
×
202

203
        $filterLocatorProphecy = $this->prophesize(ContainerInterface::class);
×
204
        $resourceMetadataFactory = $resourceCollectionMetadataFactoryProphecy->reveal();
×
205
        $propertyNameCollectionFactory = $propertyNameCollectionFactoryProphecy->reveal();
×
206
        $propertyMetadataFactory = $propertyMetadataFactoryProphecy->reveal();
×
207

208
        $schemaFactory = new SchemaFactory(null, $resourceMetadataFactory, $propertyNameCollectionFactory, $propertyMetadataFactory, new CamelCaseToSnakeCaseNameConverter());
×
209

210
        $typeFactory = new TypeFactory();
×
211
        $typeFactory->setSchemaFactory($schemaFactory);
×
212

213
        $factory = new OpenApiFactory(
×
214
            $resourceNameCollectionFactoryProphecy->reveal(),
×
215
            $resourceMetadataFactory,
×
216
            $propertyNameCollectionFactory,
×
217
            $propertyMetadataFactory,
×
218
            $schemaFactory,
×
219
            $typeFactory,
×
220
            $filterLocatorProphecy->reveal(),
×
221
            [],
×
222
            new Options('Test API', 'This is a test API.', '1.2.3', true, 'oauth2', 'authorizationCode', '/oauth/v2/token', '/oauth/v2/auth', '/oauth/v2/refresh', ['scope param'], [
×
223
                'header' => [
×
224
                    'type' => 'header',
×
225
                    'name' => 'Authorization',
×
226
                ],
×
227
                'query' => [
×
228
                    'type' => 'query',
×
229
                    'name' => 'key',
×
230
                ],
×
231
            ]),
×
232
            new PaginationOptions(true, 'page', true, 'itemsPerPage', true, 'pagination')
×
233
        );
×
234

235
        $openApi = $factory(['base_url' => '/app_dev.php/']);
×
236

237
        $pathItem = $openApi->getPaths()->getPath('/dummies/{id}');
×
238
        $operation = $pathItem->getGet();
×
239

240
        $openApi->getPaths()->addPath('/dummies/{id}', $pathItem->withGet(
×
241
            $operation->withParameters(array_merge(
×
242
                $operation->getParameters(),
×
243
                [new Parameter('fields', 'query', 'Fields to remove of the output')]
×
244
            ))
×
245
        ));
×
246

247
        $openApi = $openApi->withInfo((new Info('New Title', 'v2', 'Description of my custom API'))->withExtensionProperty('info-key', 'Info value'));
×
248
        $openApi = $openApi->withExtensionProperty('key', 'Custom x-key value');
×
249
        $openApi = $openApi->withExtensionProperty('x-value', 'Custom x-value value');
×
250

251
        $encoders = [new JsonEncoder()];
×
252
        $normalizers = [new ObjectNormalizer()];
×
253

254
        $serializer = new Serializer($normalizers, $encoders);
×
255
        $normalizers[0]->setSerializer($serializer);
×
256

257
        $normalizer = new OpenApiNormalizer($normalizers[0]);
×
258

259
        $openApiAsArray = $normalizer->normalize($openApi);
×
260

261
        // Just testing normalization specifics
262
        $this->assertSame($openApiAsArray['x-key'], 'Custom x-key value');
×
263
        $this->assertSame($openApiAsArray['x-value'], 'Custom x-value value');
×
264
        $this->assertSame($openApiAsArray['info']['x-info-key'], 'Info value');
×
265
        $this->assertArrayNotHasKey('extensionProperties', $openApiAsArray);
×
266
        // this key is null, should not be in the output
267
        $this->assertArrayNotHasKey('termsOfService', $openApiAsArray['info']);
×
268
        $this->assertArrayNotHasKey('paths', $openApiAsArray['paths']);
×
269
        $this->assertArrayHasKey('/dummies/{id}', $openApiAsArray['paths']);
×
270
        $this->assertArrayNotHasKey('servers', $openApiAsArray['paths']['/dummies/{id}']['get']);
×
271
        $this->assertArrayNotHasKey('security', $openApiAsArray['paths']['/dummies/{id}']['get']);
×
272

273
        // Security can be disabled per-operation using an empty array
274
        $this->assertEquals([], $openApiAsArray['paths']['/dummies']['post']['security']);
×
275
        $this->assertEquals(['url' => '/test'], $openApiAsArray['paths']['/dummies']['post']['servers']);
×
276

277
        // Make sure things are sorted
278
        $this->assertSame(array_keys($openApiAsArray['paths']), ['/dummies', '/dummies/{id}', '/zorros', '/zorros/{id}']);
×
279
        // Test name converter doesn't rename this property
280
        $this->assertArrayHasKey('requestBody', $openApiAsArray['paths']['/dummies']['post']);
×
281
    }
282
}
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