Completed
Push — master ( 9c5fdf...27a5d2 )
by John
7s
created

Operation::assembleRequestSchema()   D

Complexity

Conditions 9
Paths 22

Size

Total Lines 44
Code Lines 29

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 0
loc 44
rs 4.909
cc 9
eloc 29
nc 22
nop 0
1
<?php declare(strict_types = 1);
2
/*
3
 * This file is part of the KleijnWeb\SwaggerBundle package.
4
 *
5
 * For the full copyright and license information, please view the LICENSE
6
 * file that was distributed with this source code.
7
 */
8
9
namespace KleijnWeb\SwaggerBundle\Document\Specification;
10
11
use KleijnWeb\SwaggerBundle\Document\Specification;
12
13
/**
14
 * @author John Kleijn <[email protected]>
15
 */
16
class Operation
17
{
18
    /**
19
     * @var \stdClass
20
     */
21
    private $definition;
22
23
    /**
24
     * @var string
25
     */
26
    private $path;
27
28
    /**
29
     * @var string
30
     */
31
    private $method;
32
33
    /**
34
     * @param Specification $document
35
     * @param string        $path
36
     * @param string        $method
37
     */
38
    public function __construct(Specification $document, string $path, string $method)
39
    {
40
        $paths = $document->getPaths();
41
42
        if (!property_exists($paths, $path)) {
43
            throw new \InvalidArgumentException("Path '$path' not in Swagger document");
44
        }
45
        $method = strtolower($method);
46
        if (!property_exists($paths->$path, $method)) {
47
            throw new \InvalidArgumentException("Method '$method' not supported for path '$path'");
48
        }
49
50
        $this->path                             = $path;
51
        $this->method                           = $method;
52
        $this->definition                       = $paths->$path->$method;
53
        $this->definition->{'x-request-schema'} = $this->assembleRequestSchema();
54
    }
55
56
    /**
57
     * @param \stdClass $definition
58
     * @param string    $path
59
     * @param string    $method
60
     *
61
     * @return static
62
     */
63
    public static function createFromOperationDefinition(
64
        \stdClass $definition,
65
        string $path = '/',
66
        string $method = 'GET'
67
    ) {
68
69
        $method = strtolower($method);
70
71
        $documentDefinition = (object)[
72
            'paths' => (object)[
73
                $path => (object)[
74
                    $method => $definition
75
                ]
76
            ]
77
        ];
78
79
        return new static(new Specification($documentDefinition), $path, $method);
80
    }
81
82
    /**
83
     * @return object
84
     */
85
    public function getRequestSchema()
86
    {
87
        return $this->definition->{'x-request-schema'};
88
    }
89
90
    /**
91
     * @return bool
92
     */
93
    public function hasParameters(): bool
94
    {
95
        return property_exists($this->definition, 'parameters');
96
    }
97
98
    /**
99
     * @return array
100
     */
101
    public function getParameters(): array
102
    {
103
        return $this->hasParameters() ? $this->definition->parameters : [];
104
    }
105
106
    /**
107
     * @return string
108
     */
109
    public function getPath(): string
110
    {
111
        return $this->path;
112
    }
113
114
    /**
115
     * @return string
116
     */
117
    public function getMethod(): string
118
    {
119
        return $this->method;
120
    }
121
122
    /**
123
     * @return \stdClass
124
     */
125
    public function getDefinition(): \stdClass
126
    {
127
        return $this->definition;
128
    }
129
130
    /**
131
     * @param string $parameterName
132
     *
133
     * @return string
134
     */
135
    public function createParameterPointer(string $parameterName): string
136
    {
137
        foreach ($this->definition->parameters as $i => $paramDefinition) {
138
            if ($paramDefinition->name === $parameterName) {
139
                return '/' . implode('/', [
140
                    'paths',
141
                    str_replace(['~', '/'], ['~0', '~1'], $this->getPath()),
142
                    $this->getMethod(),
143
                    'parameters',
144
                    $i
145
                ]);
146
            }
147
        }
148
        throw new \InvalidArgumentException("Parameter '$parameterName' not in document");
149
    }
150
151
    /**
152
     * @param string $parameterName
153
     *
154
     * @return string
155
     */
156
    public function createParameterSchemaPointer(string $parameterName): string
157
    {
158
        $segments = explode('.', $parameterName);
159
160
        $pointer = '/'
161
            . implode(
162
                '/',
163
                [
164
                    'paths',
165
                    str_replace(['~', '/'], ['~0', '~1'], $this->getPath()),
166
                    $this->getMethod(),
167
                    'x-request-schema',
168
                    'properties'
169
                ]
170
            );
171
172
        return self::resolvePointerRecursively(
173
            $pointer,
174
            $segments,
175
            $this->definition->{'x-request-schema'}->properties
176
        );
177
    }
178
179
    /**
180
     * @param string    $pointer
181
     * @param array     $segments
182
     * @param \stdClass $context
183
     *
184
     * @return mixed
185
     */
186
    public static function resolvePointerRecursively(string $pointer, array $segments, \stdClass $context)
187
    {
188
        $segment = str_replace(['~0', '~1'], ['~', '/'], array_shift($segments));
189
        if (property_exists($context, $segment)) {
190
            $pointer .= '/' . $segment;
191
            if (!count($segments)) {
192
                return $pointer;
193
            }
194
195
            return self::resolvePointerRecursively($pointer, $segments, $context->$segment);
196
        }
197
198
        throw new \InvalidArgumentException("Segment '$segment' not found in context '$pointer'");
199
    }
200
201
    /**
202
     * @return \stdClass
203
     */
204
    private function assembleRequestSchema(): \stdClass
205
    {
206
        if (!isset($this->definition->parameters)) {
207
            return new \stdClass;
208
        }
209
        $schema             = new \stdClass;
210
        $schema->type       = 'object';
211
        $schema->required   = [];
212
        $schema->properties = new \stdClass;
213
214
        foreach ($this->definition->parameters as $paramDefinition) {
215
            $isRequired = isset($paramDefinition->required) && $paramDefinition->required;
216
            if ($isRequired) {
217
                $schema->required[] = $paramDefinition->name;
218
            }
219
            if ($paramDefinition->in === 'body') {
220
                $schema->properties->{$paramDefinition->name}
221
                    = property_exists($paramDefinition, 'schema')
222
                    ? $paramDefinition->schema
223
                    : (object)['type' => 'object'];
224
                continue;
225
            }
226
227
            $propertyDefinition = clone $paramDefinition;
228
229
            // Remove non-JSON-Schema properties
230
            $swaggerPropertyNames = [
231
                'name',
232
                'in',
233
                'description',
234
                'required',
235
                'allowEmptyValue'
236
            ];
237
            foreach ($swaggerPropertyNames as $propertyName) {
238
                if (property_exists($propertyDefinition, $propertyName)) {
239
                    unset($propertyDefinition->$propertyName);
240
                }
241
            }
242
243
            $schema->properties->{$paramDefinition->name} = $propertyDefinition;
244
        }
245
246
        return $schema;
247
    }
248
}
249