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
|
|
|
/** |
14
|
|
|
* Schema representation. |
15
|
|
|
*/ |
16
|
|
|
final class Schema |
17
|
|
|
{ |
18
|
|
|
/** |
19
|
|
|
* Schema definition. |
20
|
|
|
* |
21
|
|
|
* @var object |
22
|
|
|
*/ |
23
|
|
|
private $schema; |
24
|
|
|
|
25
|
|
|
/** |
26
|
|
|
* @param object $schema |
27
|
|
|
*/ |
28
|
15 |
|
public function __construct($schema) |
29
|
|
|
{ |
30
|
15 |
|
if (!(isset($schema->swagger) && $schema->swagger === '2.0')) { |
31
|
1 |
|
throw new UnexpectedValueException('Unsupported OpenAPI Specification schema'); |
32
|
|
|
} |
33
|
|
|
|
34
|
14 |
|
$this->schema = $schema; |
35
|
|
|
} |
36
|
|
|
|
37
|
|
|
/** |
38
|
|
|
* @return string |
39
|
|
|
*/ |
40
|
5 |
|
public function getHost() |
41
|
|
|
{ |
42
|
5 |
|
return $this->fetch($this->schema, 'host'); |
43
|
|
|
} |
44
|
|
|
|
45
|
|
|
/** |
46
|
|
|
* @return string |
47
|
|
|
*/ |
48
|
4 |
|
public function getBasePath() |
49
|
|
|
{ |
50
|
4 |
|
return $this->fetch($this->schema, 'basePath'); |
51
|
|
|
} |
52
|
|
|
|
53
|
|
|
/** |
54
|
|
|
* @param string $name |
55
|
|
|
* |
56
|
|
|
* @return object |
57
|
|
|
*/ |
58
|
2 |
|
public function getDefinition($name) |
59
|
|
|
{ |
60
|
2 |
|
return $this->fetch($this->schema, 'definitions', $name); |
61
|
|
|
} |
62
|
|
|
|
63
|
|
|
/** |
64
|
|
|
* @return string[] |
65
|
|
|
*/ |
66
|
1 |
|
public function getDefinitionNames() |
67
|
|
|
{ |
68
|
1 |
|
return array_keys((array) $this->fetch($this->schema, 'definitions')); |
69
|
|
|
} |
70
|
|
|
|
71
|
|
|
/** |
72
|
|
|
* @param string $template |
73
|
|
|
* |
74
|
|
|
* @return object |
75
|
|
|
*/ |
76
|
6 |
|
public function getPathSchema($template) |
77
|
|
|
{ |
78
|
6 |
|
return $this->fetch($this->schema, 'paths', $template); |
79
|
|
|
} |
80
|
|
|
|
81
|
|
|
/** |
82
|
|
|
* @return string[] |
83
|
|
|
*/ |
84
|
1 |
|
public function getAvailablePaths() |
85
|
|
|
{ |
86
|
1 |
|
return array_keys((array) $this->fetch($this->schema, 'paths')); |
87
|
|
|
} |
88
|
|
|
|
89
|
|
|
/** |
90
|
|
|
* @param string $template Schema path template. |
91
|
|
|
* |
92
|
|
|
* @return string[] |
93
|
|
|
*/ |
94
|
6 |
|
public function getAllowedMethods($template) |
95
|
|
|
{ |
96
|
6 |
|
$schema = $this->getPathSchema($template); |
97
|
|
|
$methods = [ |
98
|
6 |
|
'OPTIONS' => true, |
99
|
6 |
|
'HEAD' => isset($schema->get), |
100
|
6 |
|
'GET' => isset($schema->get), |
101
|
6 |
|
'POST' => isset($schema->post), |
102
|
6 |
|
'PUT' => isset($schema->put), |
103
|
6 |
|
'DELETE' => isset($schema->delete), |
104
|
6 |
|
'PATCH' => isset($schema->patch), |
105
|
|
|
]; |
106
|
|
|
|
107
|
6 |
|
return array_keys(array_filter($methods)); |
108
|
|
|
} |
109
|
|
|
|
110
|
|
|
/** |
111
|
|
|
* The transfer protocol for the operation. |
112
|
|
|
* The value overrides the top-level schemes definition. |
113
|
|
|
* |
114
|
|
|
* @see https://github.com/OAI/OpenAPI-Specification/blob/master/versions/2.0.md#operationObject |
115
|
|
|
* |
116
|
|
|
* @param string $template |
117
|
|
|
* @param string $method |
118
|
|
|
* |
119
|
|
|
* @return string[] |
120
|
|
|
*/ |
121
|
4 |
View Code Duplication |
public function getSupportedSchemes($template, $method) |
|
|
|
|
122
|
|
|
{ |
123
|
4 |
|
$schemes = $this->fetch( |
124
|
4 |
|
$this->schema, |
125
|
4 |
|
'paths', |
126
|
|
|
$template, |
127
|
|
|
$method, |
128
|
4 |
|
'schemes' |
129
|
|
|
); |
130
|
|
|
|
131
|
4 |
|
if (!$schemes) { |
132
|
4 |
|
$schemes = $this->fetch($this->schema, 'schemes'); |
133
|
|
|
} |
134
|
|
|
|
135
|
4 |
|
return (array) $schemes; |
136
|
|
|
} |
137
|
|
|
|
138
|
|
|
/** |
139
|
|
|
* @param string $template |
140
|
|
|
* @param string $method |
141
|
|
|
* |
142
|
|
|
* @return object[] |
143
|
|
|
*/ |
144
|
6 |
|
public function getRequestHeaderSchemas($template, $method) |
145
|
|
|
{ |
146
|
6 |
|
return $this->getRequestParameters($template, $method, 'header'); |
147
|
|
|
} |
148
|
|
|
|
149
|
|
|
/** |
150
|
|
|
* @param string $template |
151
|
|
|
* @param string $method |
152
|
|
|
* |
153
|
|
|
* @return object|null |
154
|
|
|
*/ |
155
|
5 |
|
public function getRequestBodySchema($template, $method) |
156
|
|
|
{ |
157
|
5 |
|
$parameters = $this->getRequestParameters($template, $method, 'body'); |
158
|
5 |
|
$count = count($parameters); |
159
|
|
|
|
160
|
5 |
|
if ($count === 0) { |
161
|
2 |
|
return null; |
162
|
|
|
} |
163
|
|
|
|
164
|
3 |
|
if ($count > 1) { |
165
|
1 |
|
throw new UnexpectedValueException('Multiple body parameters found'); |
166
|
|
|
} |
167
|
|
|
|
168
|
2 |
|
$body = reset($parameters); |
169
|
|
|
|
170
|
2 |
|
if (!isset($body->schema)) { |
171
|
1 |
|
throw new UnexpectedValueException('Invalid body parameter definition'); |
172
|
|
|
} |
173
|
|
|
|
174
|
1 |
|
return $body->schema; |
175
|
|
|
} |
176
|
|
|
|
177
|
|
|
/** |
178
|
|
|
* @param string $template |
179
|
|
|
* @param string $method |
180
|
|
|
* |
181
|
|
|
* @return object[] |
182
|
|
|
*/ |
183
|
4 |
|
public function getRequestPathParameters($template, $method) |
184
|
|
|
{ |
185
|
4 |
|
return $this->getRequestParameters($template, $method, 'path'); |
186
|
|
|
} |
187
|
|
|
|
188
|
|
|
/** |
189
|
|
|
* @param string $template |
190
|
|
|
* @param string $method |
191
|
|
|
* |
192
|
|
|
* @return object[] |
193
|
|
|
*/ |
194
|
4 |
|
public function getRequestQueryParameters($template, $method) |
195
|
|
|
{ |
196
|
4 |
|
return $this->getRequestParameters($template, $method, 'query'); |
197
|
|
|
} |
198
|
|
|
|
199
|
|
|
/** |
200
|
|
|
* @param string $template |
201
|
|
|
* @param string $method |
202
|
|
|
* |
203
|
|
|
* @return string[] |
204
|
|
|
*/ |
205
|
4 |
View Code Duplication |
public function getRequestContentTypes($template, $method) |
|
|
|
|
206
|
|
|
{ |
207
|
4 |
|
$items = $this->fetch($this->schema, 'paths', $template, $method, 'consumes'); |
208
|
|
|
|
209
|
4 |
|
if (!$items) { |
210
|
4 |
|
$items = $this->fetch($this->schema, 'consumes'); |
211
|
|
|
} |
212
|
|
|
|
213
|
4 |
|
return (array) $items; |
214
|
|
|
} |
215
|
|
|
|
216
|
|
|
/** |
217
|
|
|
* @param string $template |
218
|
|
|
* @param string $method |
219
|
|
|
* |
220
|
|
|
* @return string[] |
221
|
|
|
*/ |
222
|
4 |
|
public function getResponseCodes($template, $method) |
223
|
|
|
{ |
224
|
4 |
|
return array_filter( |
225
|
4 |
|
array_keys((array) $this->fetch($this->schema, 'paths', $template, $method, 'responses')), |
226
|
4 |
|
'is_numeric' |
227
|
|
|
); |
228
|
|
|
} |
229
|
|
|
|
230
|
|
|
/** |
231
|
|
|
* @param string $template |
232
|
|
|
* @param string $method |
233
|
|
|
* |
234
|
|
|
* @return string[] |
235
|
|
|
*/ |
236
|
4 |
View Code Duplication |
public function getResponseContentTypes($template, $method) |
|
|
|
|
237
|
|
|
{ |
238
|
4 |
|
$items = $this->fetch($this->schema, 'paths', $template, $method, 'produces'); |
239
|
|
|
|
240
|
4 |
|
if (!$items) { |
241
|
4 |
|
$items = $this->fetch($this->schema, 'produces'); |
242
|
|
|
} |
243
|
|
|
|
244
|
4 |
|
return (array) $items; |
245
|
|
|
} |
246
|
|
|
|
247
|
|
|
/** |
248
|
|
|
* @param string $template |
249
|
|
|
* @param string $method |
250
|
|
|
* @param string $status |
251
|
|
|
* |
252
|
|
|
* TODO: Normalize headers list to JSON schema (seems it is validator deals) |
253
|
|
|
* TODO: If status does not defined, check default response declaration |
254
|
|
|
* |
255
|
|
|
* @return object[] |
256
|
|
|
*/ |
257
|
4 |
|
public function getResponseHeaderSchemas($template, $method, $status) |
258
|
|
|
{ |
259
|
4 |
|
return (array) $this->fetch( |
260
|
4 |
|
$this->schema, |
261
|
4 |
|
'paths', |
262
|
|
|
$template, |
263
|
|
|
$method, |
264
|
4 |
|
'responses', |
265
|
|
|
$status, |
266
|
4 |
|
'headers' |
267
|
|
|
); |
268
|
|
|
} |
269
|
|
|
|
270
|
|
|
/** |
271
|
|
|
* Returns body schema. |
272
|
|
|
* |
273
|
|
|
* @param string $template |
274
|
|
|
* @param string $method |
275
|
|
|
* @param string $status |
276
|
|
|
* |
277
|
|
|
* @return object|null |
278
|
|
|
*/ |
279
|
4 |
|
public function getResponseBodySchema($template, $method, $status) |
280
|
|
|
{ |
281
|
4 |
|
return $this->fetch( |
282
|
4 |
|
$this->schema, |
283
|
4 |
|
'paths', |
284
|
|
|
$template, |
285
|
|
|
$method, |
286
|
4 |
|
'responses', |
287
|
|
|
$status, |
288
|
4 |
|
'schema' |
289
|
|
|
); |
290
|
|
|
} |
291
|
|
|
|
292
|
|
|
/** |
293
|
|
|
* @param $schema |
294
|
|
|
* @param array ...$paths |
295
|
|
|
* |
296
|
|
|
* @return mixed |
297
|
|
|
*/ |
298
|
14 |
|
private static function fetch($schema, ...$paths) |
299
|
|
|
{ |
300
|
14 |
|
foreach ($paths as $path) { |
301
|
14 |
|
if (!isset($schema->{$path})) { |
302
|
10 |
|
return null; |
303
|
|
|
} |
304
|
|
|
|
305
|
14 |
|
$schema = $schema->{$path}; |
306
|
|
|
} |
307
|
|
|
|
308
|
14 |
|
return $schema; |
309
|
|
|
} |
310
|
|
|
|
311
|
|
|
/** |
312
|
|
|
* A list of parameters that are applicable for this operation. |
313
|
|
|
* |
314
|
|
|
* If a parameter is already defined at the Path Item, |
315
|
|
|
* the new definition will override it, but can never remove it. |
316
|
|
|
* |
317
|
|
|
* @param string $template |
318
|
|
|
* @param string $method |
319
|
|
|
* @param string $location |
320
|
|
|
* |
321
|
|
|
* @return object[] |
322
|
|
|
*/ |
323
|
8 |
|
private function getRequestParameters($template, $method, $location) |
324
|
|
|
{ |
325
|
8 |
|
$path = $this->fetch($this->schema, 'paths', $template); |
326
|
|
|
|
327
|
8 |
|
$operationParameters = $this->normalizeRequestParameters( |
328
|
8 |
|
(array) $this->fetch($path, $method, 'parameters'), |
329
|
|
|
$location |
330
|
|
|
); |
331
|
|
|
|
332
|
8 |
|
$pathParameters = $this->normalizeRequestParameters( |
333
|
8 |
|
(array) $this->fetch($path, 'parameters'), |
334
|
|
|
$location |
335
|
|
|
); |
336
|
|
|
|
337
|
7 |
|
return $operationParameters + $pathParameters; |
338
|
|
|
} |
339
|
|
|
|
340
|
|
|
/** |
341
|
|
|
* Normalizes parameters definitions. |
342
|
|
|
* |
343
|
|
|
* Filter parameters by location, and use name as list index. |
344
|
|
|
* |
345
|
|
|
* @param array $parameters |
346
|
|
|
* @param string $location |
347
|
|
|
* |
348
|
|
|
* @return object[] |
349
|
|
|
*/ |
350
|
8 |
|
private function normalizeRequestParameters(array $parameters, $location) |
351
|
|
|
{ |
352
|
8 |
|
$schemas = []; |
353
|
|
|
|
354
|
8 |
|
foreach ($parameters as $parameter) { |
355
|
8 |
|
if ($parameter->in !== $location) { |
356
|
4 |
|
continue; |
357
|
|
|
} |
358
|
|
|
|
359
|
8 |
|
if (isset($schemas[$parameter->name])) { |
360
|
1 |
|
throw new UnexpectedValueException('Multiple parameters found'); |
361
|
|
|
} |
362
|
|
|
|
363
|
8 |
|
$schemas[$parameter->name] = clone $parameter; |
364
|
8 |
|
unset($schemas[$parameter->name]->name, $schemas[$parameter->name]->in); |
365
|
|
|
} |
366
|
|
|
|
367
|
8 |
|
return $schemas; |
368
|
|
|
} |
369
|
|
|
} |
370
|
|
|
|
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.
You can also find more detailed suggestions in the “Code” section of your repository.