SwaggerStrategy::__construct()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 1

Importance

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