Completed
Push — master ( e625a1...0d2a97 )
by Veaceslav
02:33
created

Schema::getDefinitionNames()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 0
Metric Value
dl 0
loc 4
ccs 2
cts 2
cp 1
rs 10
c 0
b 0
f 0
cc 1
nc 1
nop 0
crap 1
1
<?php
2
/**
3
 * This file is part of Rebilly.
4
 *
5
 * For the full copyright and license information, please view the LICENSE
6
 * file that was distributed with this source code.
7
 *
8
 * @see http://rebilly.com
9
 */
10
11
namespace Rebilly\OpenAPI;
12
13
use InvalidArgumentException;
14
use JsonSchema\Exception\UnresolvableJsonPointerException;
15
use JsonSchema\SchemaStorage;
16
use JsonSchema\Uri\UriResolver;
17
use JsonSchema\Uri\UriRetriever;
18
use stdClass;
19
20
final class Schema
21
{
22
    private $schemaStorage;
23
24
    private $uri;
25
26 10
    public function __construct(string $uri)
27
    {
28 10
        if (strpos($uri, '//') === false) {
29 10
            $uri = "file://{$uri}";
30
        }
31
32 10
        $schemaStorage = new SchemaStorage(new UriRetriever(), new UriResolver());
33 10
        $schemaStorage->addSchema($uri);
34
35 10
        $this->schemaStorage = $schemaStorage;
36 10
        $this->uri = $uri;
37
38 10
        if (!preg_match('|3\.0(\.\d+)?|', $this->getVersion())) {
39 1
            throw new UnexpectedValueException('Unsupported OpenAPI Specification schema');
40
        }
41
    }
42
43 10
    public function getVersion(): string
44
    {
45 10
        return $this->fetch('#/openapi');
46
    }
47
48 4
    public function getServers(): array
49
    {
50 4
        return array_column($this->fetch('#/servers'), 'url');
51
    }
52
53 2
    public function getDefinition(string $name): stdClass
54
    {
55 2
        return $this->fetch("#/components/schemas/{$name}");
56
    }
57
58 1
    public function getDefinitionNames(): array
59
    {
60 1
        return array_keys((array) $this->fetch('#/components/schemas'));
61
    }
62
63 6
    public function getPathSchema(string $path): stdClass
64
    {
65 6
        return $this->fetch("#/paths/{$this->encodePath($path)}");
66
    }
67
68 1
    public function getAvailablePaths(): array
69
    {
70 1
        return array_keys((array) $this->fetch('#/paths'));
71
    }
72
73 6
    public function getAllowedMethods(string $path): array
74
    {
75 6
        $schema = $this->getPathSchema($path);
76
        $methods = [
77 6
            'OPTIONS' => true,
78 6
            'HEAD' => isset($schema->get),
79 6
            'GET' => isset($schema->get),
80 6
            'POST' => isset($schema->post),
81 6
            'PUT' => isset($schema->put),
82 6
            'DELETE' => isset($schema->delete),
83 6
            'PATCH' => isset($schema->patch),
84
        ];
85
86 6
        return array_keys(array_filter($methods));
87
    }
88
89 3
    public function getRequestHeaderSchemas(string $path, string $method): array
90
    {
91 3
        $parameters = $this->getRequestParameters($path, $method, 'header');
92 3
        $headers = [];
93
94 3
        foreach ($parameters as $parameter) {
95 1
            $headers[] = $parameter->schema;
96
        }
97
98 3
        return $headers;
99
    }
100
101 3
    public function isRequestBodyDefined(string $path, string $method): bool
102
    {
103 3
        return $this->fetch("#/paths/{$this->encodePath($path)}/{$this->normalizeMethod($method)}/requestBody/content") !== null;
104
    }
105
106 3
    public function getRequestBodySchema(string $path, string $method, string $contentType = null): ?stdClass
107
    {
108 3
        $schemas = $this->fetch("#/paths/{$this->encodePath($path)}/{$this->normalizeMethod($method)}/requestBody/content");
109
110 3
        if (empty($schemas)) {
111 2
            return null;
112
        }
113
114 2
        if (!$contentType) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $contentType of type null|string is loosely compared to false; this is ambiguous if the string can be empty. You might want to explicitly use === null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
115 2
            return reset($schemas)->schema;
116
        }
117
118 1
        if (!isset($schemas->{$contentType})) {
119 1
            throw new InvalidArgumentException('Unsupported request content type');
120
        }
121
122 1
        return $schemas->{$contentType}->schema;
123
    }
124
125 4
    public function getRequestPathParameters(string $path, string $method): array
126
    {
127 4
        return $this->getRequestParameters($path, $method, 'path');
128
    }
129
130 4
    public function getRequestQueryParameters(string $path, string $method): array
131
    {
132 4
        return $this->getRequestParameters($path, $method, 'query');
133
    }
134
135 3
    public function getRequestContentTypes(string $path, string $method): array
136
    {
137 3
        return array_keys((array) $this->fetch("#/paths/{$this->encodePath($path)}/{$this->normalizeMethod($method)}/requestBody/content"));
138
    }
139
140 4
    public function isResponseDefined(string $path, string $method, string $status): bool
141
    {
142 4
        return $this->fetch("#/paths/{$this->encodePath($path)}/{$this->normalizeMethod($method)}/responses/{$status}") !== null;
143
    }
144
145 3
    public function isResponseBodyDefined(string $path, string $method, string $status): bool
146
    {
147 3
        return $this->fetch("#/paths/{$this->encodePath($path)}/{$this->normalizeMethod($method)}/responses/{$status}/content") !== null;
148
    }
149
150 3
    public function getResponseContentTypes(string $path, string $method, string $status): array
151
    {
152 3
        return array_keys((array) $this->fetch("#/paths/{$this->encodePath($path)}/{$this->normalizeMethod($method)}/responses/{$status}/content"));
153
    }
154
155 4
    public function getResponseHeaderSchemas(string $path, string $method, string $status): array
156
    {
157 4
        $headers = (array) $this->fetch("#/paths/{$this->encodePath($path)}/{$this->normalizeMethod($method)}/responses/{$status}/headers");
158
159 4
        foreach ($headers as &$header) {
160 4
            if (isset($header->{'$ref'})) {
161 4
                $header = $this->schemaStorage->resolveRef($header->{'$ref'});
162
            }
163
164 4
            if (isset($header->schema->{'$ref'})) {
165 4
                $header->schema = $this->schemaStorage->resolveRef($header->schema->{'$ref'});
166
            }
167
168 4
            $header = $header->schema;
169
        }
170
171 4
        return $headers;
172
    }
173
174 4
    public function getResponseBodySchema(string $path, string $method, string $status, string $contentType = null): ?stdClass
175
    {
176 4
        $schemas = $this->fetch("#/paths/{$this->encodePath($path)}/{$this->normalizeMethod($method)}/responses/{$status}/content");
177
178 4
        if (empty($schemas)) {
179 1
            return null;
180
        }
181
182 3
        if (!$contentType) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $contentType of type null|string is loosely compared to false; this is ambiguous if the string can be empty. You might want to explicitly use === null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
183 3
            return reset($schemas)->schema;
184
        }
185
186 1
        if (!isset($schemas->{$contentType})) {
187 1
            throw new InvalidArgumentException('Unsupported response content type');
188
        }
189
190 1
        return $schemas->{$contentType}->schema;
191
    }
192
193 11
    private function fetch(string $path)
194
    {
195
        try {
196 11
            return $this->schemaStorage->resolveRef(sprintf("%s%s", $this->uri, $path));
197 5
        } catch (UnresolvableJsonPointerException $e) {
198 5
            return null;
199
        }
200
    }
201
202 8
    private function encodePath(string $path): string
203
    {
204 8
        return strtr($path, ['/' => '~1', '~' => '~0', '%' => '%25']);
205
    }
206
207 7
    private function normalizeMethod(string $method): string
208
    {
209 7
        return strtolower($method);
210
    }
211
212 4
    private function getRequestParameters(string $path, string $method, string $location): array
213
    {
214 4
        $operationParameters = $this->normalizeRequestParameters(
215 4
            (array) $this->fetch("#/paths/{$this->encodePath($path)}/{$this->normalizeMethod($method)}/parameters"),
216 4
            $location
217
        );
218
219 4
        $pathParameters = $this->normalizeRequestParameters(
220 4
            (array) $this->fetch("#/paths/{$this->encodePath($path)}/parameters"),
221 4
            $location
222
        );
223
224 4
        return $operationParameters + $pathParameters;
225
    }
226
227 4
    private function normalizeRequestParameters(array $parameters, string $location): array
228
    {
229 4
        $schemas = [];
230
231 4
        foreach ($parameters as &$parameter) {
232 4
            if (isset($parameter->{'$ref'})) {
233 4
                $parameter = $this->schemaStorage->resolveRef($parameter->{'$ref'});
234
            }
235
236 4
            if ($parameter->in !== $location) {
237 4
                continue;
238
            }
239
240 4
            if (isset($parameter->schema->{'$ref'})) {
241 1
                $parameter->schema = $this->schemaStorage->resolveRef($parameter->schema->{'$ref'});
242
            }
243
244 4
            $schemas[$parameter->name] = clone $parameter;
245 4
            unset($schemas[$parameter->name]->name, $schemas[$parameter->name]->in);
246
        }
247
248 4
        return $schemas;
249
    }
250
}
251