Passed
Pull Request — master (#11)
by Joao
07:11
created

OpenApiRouteDefinition::withCache()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

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