Passed
Push — master ( 449842...b125b6 )
by Kévin
04:31 queued 15s
created

ApiGatewayNormalizer::isLocalRef()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 1
nc 1
nop 1
dl 0
loc 3
rs 10
c 0
b 0
f 0
1
<?php
2
3
/*
4
 * This file is part of the API Platform project.
5
 *
6
 * (c) Kévin Dunglas <[email protected]>
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\Core\Swagger\Serializer;
15
16
use Symfony\Component\Serializer\Exception\UnexpectedValueException;
17
use Symfony\Component\Serializer\Normalizer\CacheableSupportsMethodInterface;
18
use Symfony\Component\Serializer\Normalizer\NormalizerInterface;
19
20
/**
21
 * Removes features unsupported by Amazon API Gateway.
22
 *
23
 * @see https://docs.aws.amazon.com/apigateway/latest/developerguide/api-gateway-known-issues.html
24
 *
25
 * @internal
26
 *
27
 * @author Vincent Chalamon <[email protected]>
28
 */
29
final class ApiGatewayNormalizer implements NormalizerInterface, CacheableSupportsMethodInterface
30
{
31
    public const API_GATEWAY = 'api_gateway';
32
33
    private $documentationNormalizer;
34
    private $defaultContext = [
35
        self::API_GATEWAY => false,
36
    ];
37
38
    public function __construct(NormalizerInterface $documentationNormalizer, $defaultContext = [])
39
    {
40
        $this->documentationNormalizer = $documentationNormalizer;
41
        $this->defaultContext = array_merge($this->defaultContext, $defaultContext);
42
    }
43
44
    /**
45
     * {@inheritdoc}
46
     *
47
     * @throws UnexpectedValueException
48
     */
49
    public function normalize($object, $format = null, array $context = [])
50
    {
51
        $data = $this->documentationNormalizer->normalize($object, $format, $context);
52
        if (!\is_array($data)) {
53
            throw new UnexpectedValueException('Expected data to be an array');
54
        }
55
56
        if (!($context[self::API_GATEWAY] ?? $this->defaultContext[self::API_GATEWAY])) {
57
            return $data;
58
        }
59
60
        if (empty($data['basePath'])) {
61
            $data['basePath'] = '/';
62
        }
63
64
        foreach ($data['paths'] as $path => $operations) {
65
            foreach ($operations as $operation => $options) {
66
                if (isset($options['parameters'])) {
67
                    foreach ($options['parameters'] as $key => $parameter) {
68
                        if (!preg_match('/^[a-zA-Z0-9._$-]+$/', $parameter['name'])) {
69
                            unset($data['paths'][$path][$operation]['parameters'][$key]);
70
                        }
71
                        if (isset($parameter['schema']['$ref']) && $this->isLocalRef($parameter['schema']['$ref'])) {
72
                            $data['paths'][$path][$operation]['parameters'][$key]['schema']['$ref'] = $this->normalizeRef($parameter['schema']['$ref']);
73
                        }
74
                    }
75
                    $data['paths'][$path][$operation]['parameters'] = array_values($data['paths'][$path][$operation]['parameters']);
76
                }
77
                if (isset($options['responses'])) {
78
                    foreach ($options['responses'] as $statusCode => $response) {
79
                        if (isset($response['schema']['items']['$ref']) && $this->isLocalRef($response['schema']['items']['$ref'])) {
80
                            $data['paths'][$path][$operation]['responses'][$statusCode]['schema']['items']['$ref'] = $this->normalizeRef($response['schema']['items']['$ref']);
81
                        }
82
                        if (isset($response['schema']['$ref']) && $this->isLocalRef($response['schema']['$ref'])) {
83
                            $data['paths'][$path][$operation]['responses'][$statusCode]['schema']['$ref'] = $this->normalizeRef($response['schema']['$ref']);
84
                        }
85
                    }
86
                }
87
            }
88
        }
89
90
        foreach ($data['definitions'] as $definition => $options) {
91
            if (!isset($options['properties'])) {
92
                continue;
93
            }
94
            foreach ($options['properties'] as $property => $propertyOptions) {
95
                if (isset($propertyOptions['readOnly'])) {
96
                    unset($data['definitions'][$definition]['properties'][$property]['readOnly']);
97
                }
98
                if (isset($propertyOptions['$ref']) && $this->isLocalRef($propertyOptions['$ref'])) {
99
                    $data['definitions'][$definition]['properties'][$property]['$ref'] = $this->normalizeRef($propertyOptions['$ref']);
100
                }
101
                if (isset($propertyOptions['items']['$ref']) && $this->isLocalRef($propertyOptions['items']['$ref'])) {
102
                    $data['definitions'][$definition]['properties'][$property]['items']['$ref'] = $this->normalizeRef($propertyOptions['items']['$ref']);
103
                }
104
            }
105
        }
106
107
        // $data['definitions'] is an instance of \ArrayObject
108
        foreach (array_keys($data['definitions']->getArrayCopy()) as $definition) {
109
            if (!preg_match('/^[0-9A-Za-z]+$/', (string) $definition)) {
110
                $data['definitions'][preg_replace('/[^0-9A-Za-z]/', '', (string) $definition)] = $data['definitions'][$definition];
111
                unset($data['definitions'][$definition]);
112
            }
113
        }
114
115
        return $data;
116
    }
117
118
    /**
119
     * {@inheritdoc}
120
     */
121
    public function supportsNormalization($data, $format = null)
122
    {
123
        return $this->documentationNormalizer->supportsNormalization($data, $format);
124
    }
125
126
    /**
127
     * {@inheritdoc}
128
     */
129
    public function hasCacheableSupportsMethod(): bool
130
    {
131
        return $this->documentationNormalizer instanceof CacheableSupportsMethodInterface && $this->documentationNormalizer->hasCacheableSupportsMethod();
0 ignored issues
show
Bug introduced by Kévin Dunglas
The method hasCacheableSupportsMethod() does not exist on Symfony\Component\Serial...zer\NormalizerInterface. It seems like you code against a sub-type of said class. However, the method does not exist in Symfony\Component\Serial...er\SerializerNormalizer or ApiPlatform\Core\Tests\F...DocumentationNormalizer or Symfony\Component\Serial...rmalizer\TestNormalizer or Symfony\Component\Serial...sonSerializerNormalizer or Symfony\Component\Serial...wareNormalizerInterface or Symfony\Component\Serial...ectSerializerNormalizer or Symfony\Component\Serializer\Serializer. Are you sure you never get one of those? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

131
        return $this->documentationNormalizer instanceof CacheableSupportsMethodInterface && $this->documentationNormalizer->/** @scrutinizer ignore-call */ hasCacheableSupportsMethod();
Loading history...
132
    }
133
134
    private function isLocalRef(string $ref): bool
135
    {
136
        return '#/' === substr($ref, 0, 2);
137
    }
138
139
    private function normalizeRef(string $ref): string
140
    {
141
        $refParts = explode('/', $ref);
142
143
        $schemaName = array_pop($refParts);
144
        $schemaName = preg_replace('/[^0-9A-Za-z]/', '', $schemaName);
145
        $refParts[] = $schemaName;
146
147
        return implode('/', $refParts);
148
    }
149
}
150