-
-
Notifications
You must be signed in to change notification settings - Fork 966
Expand file tree
/
Copy pathOpenApiNormalizer.php
More file actions
128 lines (107 loc) · 4.16 KB
/
Copy pathOpenApiNormalizer.php
File metadata and controls
128 lines (107 loc) · 4.16 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
<?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\OpenApi\Serializer;
use ApiPlatform\OpenApi\Model\Paths;
use ApiPlatform\OpenApi\OpenApi;
use Symfony\Component\Serializer\Normalizer\AbstractNormalizer;
use Symfony\Component\Serializer\Normalizer\AbstractObjectNormalizer;
use Symfony\Component\Serializer\Normalizer\NormalizerInterface;
/**
* Generates an OpenAPI v3 specification.
*/
final class OpenApiNormalizer implements NormalizerInterface
{
public const FORMAT = 'json';
public const JSON_FORMAT = 'jsonopenapi';
public const YAML_FORMAT = 'yamlopenapi';
private const EXTENSION_PROPERTIES_KEY = 'extensionProperties';
public function __construct(private readonly NormalizerInterface $decorated)
{
}
/**
* {@inheritdoc}
*/
public function normalize(mixed $data, ?string $format = null, array $context = []): array
{
$pathsCallback = $this->getPathsCallBack();
$context[AbstractObjectNormalizer::PRESERVE_EMPTY_OBJECTS] = true;
$context[AbstractObjectNormalizer::SKIP_NULL_VALUES] = true;
$context[AbstractNormalizer::CALLBACKS] = [
'paths' => $pathsCallback,
];
return $this->recursiveClean($this->decorated->normalize($data, $format, $context));
}
private function recursiveClean(array $data): array
{
foreach ($data as $key => $value) {
if (self::EXTENSION_PROPERTIES_KEY === $key) {
foreach ($data[self::EXTENSION_PROPERTIES_KEY] as $extensionPropertyKey => $extensionPropertyValue) {
$data[$extensionPropertyKey] = $extensionPropertyValue;
}
continue;
}
if (\is_array($value)) {
$data[$key] = $this->recursiveClean($value);
}
}
unset($data[self::EXTENSION_PROPERTIES_KEY]);
return $data;
}
/**
* {@inheritdoc}
*/
public function supportsNormalization(mixed $data, ?string $format = null, array $context = []): bool
{
return (self::FORMAT === $format || self::JSON_FORMAT === $format || self::YAML_FORMAT === $format) && $data instanceof OpenApi;
}
/**
* {@inheritdoc}
*/
public function getSupportedTypes(?string $format): array
{
return (self::FORMAT === $format || self::JSON_FORMAT === $format || self::YAML_FORMAT === $format) ? [OpenApi::class => true] : [];
}
private function getPathsCallBack(): \Closure
{
return static function ($decoratedObject): array {
if ($decoratedObject instanceof Paths) {
$paths = $decoratedObject->getPaths();
// sort paths by tags, then by path for each tag
uksort($paths, static function ($keyA, $keyB) use ($paths) {
$a = $paths[$keyA];
$b = $paths[$keyB];
$tagsA = [
...($a->getGet()?->getTags() ?? []),
...($a->getPost()?->getTags() ?? []),
...($a->getPatch()?->getTags() ?? []),
...($a->getPut()?->getTags() ?? []),
...($a->getDelete()?->getTags() ?? []),
];
sort($tagsA);
$tagsB = [
...($b->getGet()?->getTags() ?? []),
...($b->getPost()?->getTags() ?? []),
...($b->getPatch()?->getTags() ?? []),
...($b->getPut()?->getTags() ?? []),
...($b->getDelete()?->getTags() ?? []),
];
sort($tagsB);
return match (true) {
current($tagsA) === current($tagsB) => $keyA <=> $keyB,
default => current($tagsA) <=> current($tagsB),
};
});
return $paths;
}
return [];
};
}
}