Completed
Pull Request — master (#29)
by Joao
07:15
created

SwaggerSchema::getPathDefinition()   B

Complexity

Conditions 9
Paths 10

Size

Total Lines 50

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 50
rs 7.5353
c 0
b 0
f 0
cc 9
nc 10
nop 2
1
<?php
2
/**
3
 * User: jg
4
 * Date: 22/05/17
5
 * Time: 09:29
6
 */
7
8
namespace ByJG\Swagger;
9
10
use ByJG\Swagger\Exception\DefinitionNotFoundException;
11
use ByJG\Swagger\Exception\HttpMethodNotFoundException;
12
use ByJG\Swagger\Exception\InvalidDefinitionException;
13
use ByJG\Swagger\Exception\NotMatchedException;
14
use ByJG\Swagger\Exception\PathNotFoundException;
15
use ByJG\Util\Uri;
16
17
class SwaggerSchema
18
{
19
    protected $jsonFile;
20
    protected $allowNullValues;
21
    protected $specificationVersion;
22
23
    const SWAGGER_PATHS="paths";
24
    const SWAGGER_PARAMETERS="parameters";
25
    const SWAGGER_COMPONENTS="components";
26
27
    public function __construct($jsonFile, $allowNullValues = false)
28
    {
29
        $this->jsonFile = json_decode($jsonFile, true);
30
        $this->allowNullValues = (bool) $allowNullValues;
31
        $this->specificationVersion = isset($this->jsonFile['swagger']) ? '2' : '3';
32
    }
33
34
    /**
35
     * Returns the major specification version
36
     * @return string
37
     */
38
    public function getSpecificationVersion()
39
    {
40
        return $this->specificationVersion;
41
    }
42
43
    public function getServerUrl()
44
    {
45
        return isset($this->jsonFile['servers']) ? $this->jsonFile['servers'][0]['url'] : '';
46
    }
47
48
    public function getHttpSchema()
49
    {
50
        return isset($this->jsonFile['schemes']) ? $this->jsonFile['schemes'][0] : '';
51
    }
52
53
    public function getHost()
54
    {
55
        return isset($this->jsonFile['host']) ? $this->jsonFile['host'] : '';
56
    }
57
58
    public function getBasePath()
59
    {
60
        if ($this->getSpecificationVersion() === '3') {
61
            $basePath =isset($this->jsonFile['servers']) ? explode('/', $this->jsonFile['servers'][0]['url']) : '';
62
            return is_array($basePath) ? '/' . end($basePath) : $basePath;
63
        }
64
65
        return isset($this->jsonFile['basePath']) ? $this->jsonFile['basePath'] : '';
66
    }
67
68
    /**
69
     * @param $path
70
     * @param $method
71
     * @return mixed
72
     * @throws DefinitionNotFoundException
73
     * @throws HttpMethodNotFoundException
74
     * @throws InvalidDefinitionException
75
     * @throws NotMatchedException
76
     * @throws PathNotFoundException
77
     */
78
    public function getPathDefinition($path, $method)
79
    {
80
        $method = strtolower($method);
81
82
        $path = preg_replace('~^' . $this->getBasePath() . '~', '', $path);
83
84
        $uri = new Uri($path);
85
86
        // Try direct match
87
        if (isset($this->jsonFile[self::SWAGGER_PATHS][$uri->getPath()])) {
88
            if (isset($this->jsonFile[self::SWAGGER_PATHS][$uri->getPath()][$method])) {
89
                return $this->jsonFile[self::SWAGGER_PATHS][$uri->getPath()][$method];
90
            }
91
            throw new HttpMethodNotFoundException("The http method '$method' not found in '$path'");
92
        }
93
94
        // Try inline parameter
95
        foreach (array_keys($this->jsonFile[self::SWAGGER_PATHS]) as $pathItem) {
96
            if (strpos($pathItem, '{') === false) {
97
                continue;
98
            }
99
100
            $pathItemPattern = '~^' . preg_replace('~\{(.*?)\}~', '(?<\1>[^/]+)', $pathItem) . '$~';
101
102
            $matches = [];
103
            if (preg_match($pathItemPattern, $uri->getPath(), $matches)) {
104
                $pathDef = $this->jsonFile[self::SWAGGER_PATHS][$pathItem];
105
                if (!isset($pathDef[$method])) {
106
                    throw new HttpMethodNotFoundException("The http method '$method' not found in '$path'");
107
                }
108
109
                $parametersPathMethod = [];
110
                $parametersPath = [];
111
112
                if (isset($pathDef[$method][self::SWAGGER_PARAMETERS])) {
113
                    $parametersPathMethod = $pathDef[$method][self::SWAGGER_PARAMETERS];
114
                }
115
116
                if (isset($pathDef[self::SWAGGER_PARAMETERS])) {
117
                    $parametersPath = $pathDef[self::SWAGGER_PARAMETERS];
118
                }
119
120
                $this->validateArguments('path', array_merge($parametersPathMethod, $parametersPath), $matches);
121
122
                return $pathDef[$method];
123
            }
124
        }
125
126
        throw new PathNotFoundException('Path "' . $path . '" not found');
127
    }
128
129
    /**
130
     * @param $parameterIn
131
     * @param $parameters
132
     * @param $arguments
133
     * @throws DefinitionNotFoundException
134
     * @throws InvalidDefinitionException
135
     * @throws NotMatchedException
136
     */
137
    private function validateArguments($parameterIn, $parameters, $arguments)
138
    {
139
        if ($this->getSpecificationVersion() === '3') {
140
            foreach ($parameters as $parameter) {
141
                if (isset($parameter['$ref'])) {
142
                    $paramParts = explode("/", $parameter['$ref']);
143
                    if (count($paramParts) != 4 || $paramParts[0] != "#" || $paramParts[1] != self::SWAGGER_COMPONENTS || $paramParts[2] != self::SWAGGER_PARAMETERS) {
144
                        throw new InvalidDefinitionException(
145
                            "Not get the reference in the expected format #/components/parameters/<NAME>"
146
                        );
147
                    }
148
                    if (!isset($this->jsonFile[self::SWAGGER_COMPONENTS][self::SWAGGER_PARAMETERS][$paramParts[3]])) {
149
                        throw new DefinitionNotFoundException(
150
                            "Not find reference #/components/parameters/${paramParts[3]}"
151
                        );
152
                    }
153
                    $parameter = $this->jsonFile[self::SWAGGER_COMPONENTS][self::SWAGGER_PARAMETERS][$paramParts[3]];
154
                }
155 View Code Duplication
                if ($parameter['in'] === $parameterIn &&
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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.

Loading history...
156
                    $parameter['schema']['type'] === "integer"
157
                    && filter_var($arguments[$parameter['name']], FILTER_VALIDATE_INT) === false) {
158
                    throw new NotMatchedException('Path expected an integer value');
159
                }
160
            }
161
            return;
162
        }
163
164
        foreach ($parameters as $parameter) {
165 View Code Duplication
            if ($parameter['in'] === $parameterIn
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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.

Loading history...
166
                && $parameter['type'] === "integer"
167
                && filter_var($arguments[$parameter['name']], FILTER_VALIDATE_INT) === false) {
168
                throw new NotMatchedException('Path expected an integer value');
169
            }
170
        }
171
    }
172
173
    /**
174
     * @param $name
175
     * @return mixed
176
     * @throws DefinitionNotFoundException
177
     * @throws InvalidDefinitionException
178
     */
179
    public function getDefintion($name)
180
    {
181
        $nameParts = explode('/', $name);
182
183
        if ($this->getSpecificationVersion() === '3') {
184 View Code Duplication
            if (count($nameParts) < 4 || $nameParts[0] !== '#') {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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.

Loading history...
185
                throw new InvalidDefinitionException('Invalid Component');
186
            }
187
188 View Code Duplication
            if (!isset($this->jsonFile[$nameParts[1]][$nameParts[2]][$nameParts[3]])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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.

Loading history...
189
                throw new DefinitionNotFoundException("Component'$name' not found");
190
            }
191
192
            return $this->jsonFile[$nameParts[1]][$nameParts[2]][$nameParts[3]];
193
        }
194
195 View Code Duplication
        if (count($nameParts) < 3 || $nameParts[0] !== '#') {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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.

Loading history...
196
            throw new InvalidDefinitionException('Invalid Definition');
197
        }
198
199 View Code Duplication
        if (!isset($this->jsonFile[$nameParts[1]][$nameParts[2]])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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.

Loading history...
200
            throw new DefinitionNotFoundException("Definition '$name' not found");
201
        }
202
203
        return $this->jsonFile[$nameParts[1]][$nameParts[2]];
204
    }
205
206
    /**
207
     * @param $path
208
     * @param $method
209
     * @return \ByJG\Swagger\SwaggerRequestBody
210
     * @throws DefinitionNotFoundException
211
     * @throws HttpMethodNotFoundException
212
     * @throws InvalidDefinitionException
213
     * @throws NotMatchedException
214
     * @throws PathNotFoundException
215
     */
216
    public function getRequestParameters($path, $method)
217
    {
218
        $structure = $this->getPathDefinition($path, $method);
219
220
        if($this->getSpecificationVersion() === '3') {
221
            if (!isset($structure['requestBody'])) {
222
                return new SwaggerRequestBody($this, "$method $path", []);
223
            }
224
            return new SwaggerRequestBody($this, "$method $path", $structure['requestBody']);
225
        }
226
227
        if (!isset($structure[self::SWAGGER_PARAMETERS])) {
228
            return new SwaggerRequestBody($this, "$method $path", []);
229
        }
230
        return new SwaggerRequestBody($this, "$method $path", $structure[self::SWAGGER_PARAMETERS]);
231
    }
232
233
    /**
234
     * @param $path
235
     * @param $method
236
     * @param $status
237
     * @return \ByJG\Swagger\SwaggerResponseBody
238
     * @throws \ByJG\Swagger\Exception\HttpMethodNotFoundException
239
     * @throws \ByJG\Swagger\Exception\InvalidDefinitionException
240
     * @throws \ByJG\Swagger\Exception\NotMatchedException
241
     * @throws \ByJG\Swagger\Exception\PathNotFoundException
242
     * @throws DefinitionNotFoundException
243
     */
244
    public function getResponseParameters($path, $method, $status)
245
    {
246
        $structure = $this->getPathDefinition($path, $method);
247
248
        if (!isset($structure['responses'][$status])) {
249
            throw new InvalidDefinitionException("Could not found status code '$status' in '$path' and '$method'");
250
        }
251
252
        return new SwaggerResponseBody($this, "$method $status $path", $structure['responses'][$status]);
253
    }
254
255
    /**
256
     * OpenApi 2.0 doesn't describe null values, so this flag defines,
257
     * if match is ok when one of property
258
     *
259
     * @return bool
260
     */
261
    public function isAllowNullValues()
262
    {
263
        return $this->allowNullValues;
264
    }
265
266
    /**
267
     * OpenApi 2.0 doesn't describe null values, so this flag defines,
268
     * if match is ok when one of property
269
     *
270
     * @param $value
271
     */
272
    public function setAllowNullValues($value)
273
    {
274
        $this->allowNullValues = (bool) $value;
275
    }
276
}
277