Completed
Push — feature/evo-2891-remove-thirdp... ( 0fdd1b )
by Adrian
12:07
created

SwaggerStrategy::normalizePath()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1
Metric Value
dl 0
loc 4
ccs 2
cts 2
cp 1
rs 10
cc 1
eloc 2
nc 1
nop 1
crap 1
1
<?php
2
/**
3
 * SwaggerStrategy
4
 */
5
6
namespace Graviton\ProxyBundle\Definition\Loader\DispersalStrategy;
7
8
use Graviton\ProxyBundle\Definition\ApiDefinition;
9
use Swagger\Document;
10
use Swagger\Exception\MissingDocumentPropertyException;
11
use Swagger\Object\AbstractObject;
12
use Swagger\Object\Parameter;
13
use Swagger\Object\Reference;
14
use Swagger\OperationReference;
15
16
/**
17
 * process a swagger.json file and return an APi definition
18
 *
19
 * @author  List of contributors <https://github.com/libgraviton/graviton/graphs/contributors>
20
 * @license http://opensource.org/licenses/gpl-license.php GNU Public License
21
 * @link    http://swisscom.ch
22
 */
23
class SwaggerStrategy implements DispersalStrategyInterface
24
{
25
    /**
26
     * @var array
27
     */
28
    private $fallbackData = [];
29
30
    /**
31
     *
32
     */
33
    private $document;
34
35
    /**
36
     * constructor
37
     *
38
     * @param Document $document Swagger document parser
39
     */
40
    public function __construct(Document $document)
41
    {
42
        $this->document = $document;
43
    }
44
45
    /**
46
     * process data
47
     *
48
     * @param string $input        JSON information about the swagger service.
49
     * @param array  $fallbackData Set of information to be registered in case the swagger info is not complete.
50
     *
51
     * @return ApiDefinition
52
     */
53
    public function process($input, array $fallbackData = [])
54
    {
55
        $this->registerFallbackData($fallbackData);
56
        $apiDef = new ApiDefinition();
57
58
        /**
59
         * @var \stdClass $swagger
60
         */
61
        $swagger = $this->decodeJson($input);
62
        if (is_object($swagger)) {
63
            $this->document->setDocument($swagger);
64
            $this->setBaseValues($apiDef);
65
66
            $operations = $this->document->getOperationsById();
67
            foreach ($operations as $service) {
68
                $path = $service->getPath();
69
70
                if (in_array(strtolower($service->getMethod()), ['delete', 'patch']) || $apiDef->hasEndpoint($path)) {
71
                    continue;
72
                }
73
                $apiDef->addEndpoint($path);
74
                $apiDef->addSchema(
75
                    $path,
76
                    $this->getServiceSchema($service)
77
                );
78
                $apiDef->setOrigin($this->document);
79
            }
80
        }
81
82
        return $apiDef;
83
    }
84
85
    /**
86
     * is input data valid json
87
     *
88
     * @param string $input json string
89
     *
90
     * @return boolean
91
     */
92
    public function supports($input)
93
    {
94
        /**
95
         * @var array $swagger
96
         */
97
        $swagger = $this->decodeJson($input, true);
98
99
        $mandatoryFields = ['swagger', 'info', 'paths', 'version', 'title', 'definitions'];
100
        $fields = array_merge(array_keys($swagger), array_keys($swagger['info']));
101
        $intersect = array_intersect($mandatoryFields, $fields);
102
103
        // every mandatory field was found in provided json definition.
104
        return empty(array_diff($mandatoryFields, $intersect));
105
    }
106
107
    /**
108
     * decode a json string
109
     *
110
     * @param string $input json string
111
     * @param bool   $assoc Force the encoded result to be a hash.
112
     *
113
     * @return array|\stdClass
114
     */
115
    private function decodeJson($input, $assoc = false)
116
    {
117
        $input = trim($input);
118
119
        return json_decode($input, $assoc);
120
    }
121
122
    /**
123
     * set base values
124
     *
125
     * @param ApiDefinition $apiDef API definition
126
     *
127
     * @return void
128
     */
129
    private function setBaseValues(ApiDefinition $apiDef)
130
    {
131
        $this->registerHost($apiDef);
132
        $basePath = $this->document->getBasePath();
133
        if (isset($basePath)) {
134
            $apiDef->setBasePath($basePath);
135
        }
136
    }
137
138
    /**
139
     * get the schema
140
     *
141
     * @param OperationReference $service service endpoint
142
     *
143
     * @return \stdClass
144
     */
145
    private function getServiceSchema($service)
146
    {
147
        $operation = $service->getOperation();
148
        $schema = new \stdClass();
149
        switch (strtolower($service->getMethod())) {
150
            case "post":
151
            case "put":
152
                try {
153
                    $parameters = $operation->getDocumentObjectProperty('parameters', Parameter\Body::class, true);
154
                } catch (MissingDocumentPropertyException $e) {
155
                    // request has no params
156
                    break;
157
                }
158
                foreach ($parameters as $parameter) {
159
                    /**
160
                     * there is no schema information available, if $action->parameters[0]->in != 'body'
161
                     *
162
                     * @link http://swagger.io/specification/#parameterObject
163
                     */
164
                    if ($parameter instanceof Parameter\Body && $parameter->getIn() === 'body') {
165
                        $ref = $parameter->getDocumentObjectProperty('schema', Reference::class)->getDocument();
166
                        $schema = $this->resolveSchema($ref);
167
                        break;
168
                    }
169
                }
170
                break;
171
            case "get":
172
                try {
173
                    $response = $operation->getResponses()->getHttpStatusCode(200);
174
                } catch (MissingDocumentPropertyException $e) {
175
                    // no response with status code 200 is defined
176
                    break;
177
                }
178
                $schema = $this->resolveSchema($response->getSchema()->getDocument());
179
                break;
180
        }
181
182
        return $schema;
183
    }
184
185
    /**
186
     * resolve schema
187
     *
188
     * @param \stdClass $reference reference
189
     *
190
     * @return \stdClass
191
     */
192
    private function resolveSchema($reference)
193
    {
194
        $schema = $reference;
195
        if (property_exists($reference, '$ref')) {
196
            $schemaResolver = $this->document->getSchemaResolver();
197
            $ref = new Reference($reference);
198
            $schema = $schemaResolver->resolveReference($ref)->getDocument();
199
        } elseif ($reference->type === 'array' && !empty($reference->items)) {
200
            $schema->items = $this->resolveSchema($reference->items);
201
        }
202
203
        // resolve properties
204
        if (!empty($schema->properties)) {
205
            $properties = (array) $schema->properties;
206
            foreach ($properties as $name => $property) {
207
                if (isset($property->type)
208
                    && $property->type === 'array'
209
                    && isset($property->items)
210
                    && property_exists($property->items, '$ref')
211
                    && !isset($schema->properties->$name->items)) {
212
                    $schema->properties->$name->items = $this->resolveSchema($property->items);
213
                } elseif (property_exists($property, '$ref') && !isset($schema->properties->$name)) {
214
                    $schema->properties->$name = $this->resolveSchema($property);
215
                }
216
            }
217
        }
218
219
        return $schema;
220
    }
221
222
    /**
223
     * Sets the destination host for the api definition.
224
     *
225
     * @param ApiDefinition $apiDef Configuration for the swagger api to be recognized.
226
     *
227
     * @return void
228
     */
229
    private function registerHost(ApiDefinition $apiDef)
230
    {
231
        try {
232
            $host = $this->document->getHost();
233
        } catch (MissingDocumentPropertyException $e) {
234
            $host = $this->fallbackData['host'];
235
        }
236
        $apiDef->setHost($host);
237
    }
238
239
    /**
240
     * Set of information to be used as default if not defined by the swagger configuration.
241
     *
242
     * @param array $fallbackData Set of default information (e.g. host)
243
     *
244
     * @return void
245
     */
246
    private function registerFallbackData(array $fallbackData)
247
    {
248
        if (!array_key_exists('host', $fallbackData)) {
249
            throw new \RuntimeException('Missing mandatory key (host) in fallback data set.');
250
        }
251
252
        $this->fallbackData = $fallbackData;
253
    }
254
}
255