Passed
Push — master ( d9a9b3...d856e5 )
by Joao
10:51 queued 13s
created

OpenApiRouteList::withOutputProcessorForRoute()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
eloc 2
nc 1
nop 3
dl 0
loc 4
ccs 3
cts 3
cp 1
crap 1
rs 10
c 0
b 0
f 0
1
<?php
2
3
4
namespace ByJG\RestServer\Route;
5
6
use ByJG\Cache\Psr16\NoCacheEngine;
7
use ByJG\RestServer\Exception\OperationIdInvalidException;
8
use ByJG\RestServer\Exception\SchemaInvalidException;
9
use ByJG\RestServer\Exception\SchemaNotFoundException;
10
use ByJG\RestServer\OutputProcessor\BaseOutputProcessor;
11
use ByJG\RestServer\OutputProcessor\JsonOutputProcessor;
12
use ByJG\Serializer\SerializerObject;
13
use ByJG\Util\Uri;
14
use Psr\SimpleCache\CacheInterface;
15
use Psr\SimpleCache\InvalidArgumentException;
16
17
class OpenApiRouteList extends RouteList
18
{
19
    protected $cache;
20
    protected $schema;
21
    protected $defaultProcessor;
22
    protected $overrideOutputProcessor = [];
23
24
    /**
25
     * @param $openApiDefinition
26
     * @param string $defaultProcessor
27
     * @param CacheInterface|null $cache
28
     * @throws InvalidArgumentException
29
     * @throws OperationIdInvalidException
30
     * @throws SchemaInvalidException
31
     * @throws SchemaNotFoundException
32
     */
33 5
    public function __construct($openApiDefinition)
34
    {
35 5
        if (!file_exists($openApiDefinition)) {
36
            throw new SchemaNotFoundException("Schema '$openApiDefinition' not found");
37
        }
38
39 5
        $ext = substr(strrchr($openApiDefinition, "."), 1);
40 5
        $contents = file_get_contents($openApiDefinition);
41
42 5
        if ($ext == "json") {
43 3
            $this->schema = SerializerObject::instance($contents)->fromJson()->serialize();
44 2
        } elseif ($ext == "yaml" || $ext == "yml") {
45 2
            $this->schema = SerializerObject::instance($contents)->fromYaml()->serialize();
46
        } else {
47
            throw new SchemaInvalidException(
48
                "Cannot determine file type. Valids extensions are 'json', 'yaml' or 'yml'"
49
            );
50
        }
51
52 5
        if (!isset($this->schema['paths'])) {
53
            throw new SchemaInvalidException("Schema '$openApiDefinition' is invalid");
54
        }
55
56 5
        $this->cache = new NoCacheEngine();
57
58 5
        $this->defaultProcessor = JsonOutputProcessor::class;
59
    }
60
61
    /**
62
     * @param $method
63
     * @param $path
64
     * @param string $processor
65
     * @return $this
66
     */
67 2
    public function withOutputProcessorForRoute($method, $path, $processor)
68
    {
69 2
        $this->overrideOutputProcessor[strtoupper($method) . " " . $path] = $processor;
70 2
        return $this;
71
    }
72
73 2
    public function withOutputProcessorForMimeType($mimeType, $processor)
74
    {
75 2
        $this->overrideOutputProcessor[$mimeType] = $processor;
76 2
        return $this;
77
    }
78
79 2
    public function withDefaultProcessor($processor)
80
    {
81 2
        $this->defaultProcessor = $processor;
82 2
        return $this;
83
    }
84
85
    public function withCache(CacheInterface $cache)
86
    {
87
        $this->cache = $cache;
88
        return $this;
89
    }
90
91 4
    public function getRoutes()
92
    {
93 4
        if (empty($this->routes)) {
94 4
            $routePattern = $this->cache->get('SERVERHANDLERROUTES', false);
95 4
            if ($routePattern === false) {
96 4
                $routePattern = $this->generateRoutes();
97 4
                $this->cache->set('SERVERHANDLERROUTES', $routePattern);
98
            }
99 4
            $this->setRoutes($routePattern);
100
        }
101
102 4
        return parent::getRoutes();
103
    }
104
105
    /**
106
     * @return array
107
     * @throws OperationIdInvalidException
108
     */
109 4
    protected function generateRoutes()
110
    {
111 4
        $basePath = isset($this->schema["basePath"]) ? $this->schema["basePath"] : "";
112 4
        if (empty($basePath) && isset($this->schema["servers"])) {
113 2
            $uri = new Uri($this->schema["servers"][0]["url"]);
114 2
            $basePath = $uri->getPath();
115
        }
116
117 4
        $pathList = $this->sortPaths(array_keys($this->schema['paths']));
118
119 4
        $routes = [];
120 4
        foreach ($pathList as $path) {
121 4
            foreach ($this->schema['paths'][$path] as $method => $properties) {
122 4
                if (!isset($properties['operationId'])) {
123
                    throw new OperationIdInvalidException('OperationId was not found');
124
                }
125
126 4
                $parts = explode('::', $properties['operationId']);
127 4
                if (count($parts) !== 2 && count($parts) !== 4) {
128
                    throw new OperationIdInvalidException(
129
                        'OperationId needs to be in the format Namespace\\class::method or Method::Path::Namespace\\class::method'
130
                    );
131
                }
132
133 4
                $outputProcessor = $this->getMethodOutputProcessor($method, $basePath. $path, $properties);
134
135 4
                $routes[] = (new Route(strtoupper($method), $basePath . $path))
136 4
                    ->withOutputProcessor($outputProcessor)
137 4
                    ->withClass($parts[count($parts)-2], $parts[count($parts)-1]);
138
            }
139
        }
140
141 4
        return $routes;
142
    }
143
144 5
    protected function sortPaths($pathList)
145
    {
146 5
        usort($pathList, function ($left, $right) {
147 5
            if (strpos($left, '{') === false && strpos($right, '{') !== false) {
148 5
                return -16384;
149
            }
150 5
            if (strpos($left, '{') !== false && strpos($right, '{') === false) {
151 5
                return 16384;
152
            }
153 5
            if (strpos($left, $right) !== false) {
154 1
                return -16384;
155
            }
156 5
            if (strpos($right, $left) !== false) {
157 5
                return 16384;
158
            }
159 5
            return strcmp($left, $right);
160 5
        });
161
162 5
        return $pathList;
163
    }
164
165
    /**
166
     * @param $method
167
     * @param $path
168
     * @param $properties
169
     * @return string
170
     * @throws OperationIdInvalidException
171
     */
172 4
    protected function getMethodOutputProcessor($method, $path, $properties)
173
    {
174 4
        $key = strtoupper($method) . " " . $path;
175 4
        if (isset($this->overrideOutputProcessor[$key])) {
176 2
            return $this->overrideOutputProcessor[$key];
177
        }
178
179 4
        $produces = null;
180 4
        if (isset($properties['produces'])) {
181 2
            $produces = (array) $properties['produces'];
182
        }
183 4
        if (empty($produces) && isset($properties["responses"]["200"]["content"])) {
184 2
            $produces = array_keys($properties["responses"]["200"]["content"]);
185
        }
186
187 4
        if (empty($produces)) {
188 2
            return $this->defaultProcessor;
189
        }
190
191 4
        $produces = $produces[0];
192
193 4
        if (isset($this->overrideOutputProcessor[$produces])) {
194 2
            return $this->overrideOutputProcessor[$produces];
195
        }
196
197 4
        return BaseOutputProcessor::getFromContentType($produces);
198
    }
199
200
    public function getSchema()
201
    {
202
        return $this->schema;
203
    }
204
}