Completed
Pull Request — master (#65)
by John
02:46
created

SwaggerRouteLoader::resolveControllerKey()   B

Complexity

Conditions 5
Paths 7

Size

Total Lines 24
Code Lines 17

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 24
rs 8.5125
cc 5
eloc 17
nc 7
nop 5
1
<?php
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\Routing;
10
11
use KleijnWeb\SwaggerBundle\Document\DocumentRepository;
12
use KleijnWeb\SwaggerBundle\Document\OperationObject;
13
use KleijnWeb\SwaggerBundle\Document\SwaggerDocument;
14
use Symfony\Component\Config\Loader\Loader;
15
use Symfony\Component\Routing\Route;
16
use Symfony\Component\Routing\RouteCollection;
17
18
/**
19
 * @author John Kleijn <[email protected]>
20
 */
21
class SwaggerRouteLoader extends Loader
22
{
23
    /**
24
     * @var array
25
     */
26
    private $loadedSpecs = [];
27
28
    /**
29
     * @var DocumentRepository
30
     */
31
    private $documentRepository;
32
33
    /**
34
     * @param DocumentRepository $documentRepository
35
     */
36
    public function __construct(DocumentRepository $documentRepository)
37
    {
38
        $this->documentRepository = $documentRepository;
39
    }
40
41
    /**
42
     * @SuppressWarnings(PHPMD.UnusedFormalParameter)
43
     *
44
     * @param mixed  $resource
45
     * @param string $type
46
     *
47
     * @return bool
48
     */
49
    public function supports($resource, $type = null)
50
    {
51
        return 'swagger' === $type;
52
    }
53
54
    /**
55
     * @SuppressWarnings(PHPMD.UnusedFormalParameter)
56
     *
57
     * @param mixed $resource
58
     * @param null  $type
59
     *
60
     * @return RouteCollection
61
     */
62
    public function load($resource, $type = null)
63
    {
64
        $resource = (string)$resource;
65
        if (in_array($resource, $this->loadedSpecs)) {
66
            throw new \RuntimeException("Resource '$resource' was already loaded");
67
        }
68
69
        $document = $this->documentRepository->get($resource);
70
71
        $routes = new RouteCollection();
72
73
        $paths = $document->getPathDefinitions();
74
        $router = 'swagger.controller';
75
        foreach ($paths as $path => $pathSpec) {
76
            if ($path === 'x-router') {
77
                $router = $pathSpec;
78
                unset($paths->$path);
79
            }
80
        }
81
        foreach ($paths as $path => $methods) {
82
            $relativePath = ltrim($path, '/');
83
            $resourceName = strpos($relativePath, '/')
84
                ? substr($relativePath, 0, strpos($relativePath, '/'))
85
                : $relativePath;
86
            $routerController = null;
87
            foreach ($methods as $methodName => $operationSpec) {
88
                if ($methodName === 'x-router-controller') {
89
                    $routerController = $operationSpec;
90
                    unset($methods->$methodName);
91
                }
92
            }
93
            foreach ($methods as $methodName => $operationSpec) {
94
                $controllerKey = $this->resolveControllerKey(
95
                    $operationSpec,
96
                    $methodName,
97
                    $resourceName,
98
                    $router,
99
                    $routerController
100
                );
101
                $defaults = [
102
                    '_controller'   => $controllerKey,
103
                    '_definition'   => $resource,
104
                    '_swagger_path' => $path
105
                ];
106
107
                $route = new Route($path, $defaults, $this->resolveRequirements($document, $path, $methodName));
108
                $route->setMethods($methodName);
109
                $routes->add($this->createRouteId($resource, $path, $controllerKey), $route);
110
            }
111
        }
112
113
        $this->loadedSpecs[] = $resource;
114
115
        return $routes;
116
    }
117
118
    /**
119
     * @param SwaggerDocument $document
120
     * @param                 $path
121
     * @param                 $methodName
122
     *
123
     * @return array
124
     */
125
    private function resolveRequirements(SwaggerDocument $document, $path, $methodName)
126
    {
127
        $operationObject = $document->getOperationObject($path, $methodName);
128
129
        $requirements = [];
130
131
        foreach ($operationObject->getParameters() as $paramDefinition) {
132
            if ($paramDefinition->in === 'path' && isset($paramDefinition->type)) {
133
                switch ($paramDefinition->type) {
134
                    case 'integer':
135
                        $requirements[$paramDefinition->name] = '\d+';
136
                        break;
137
                    case 'string':
138
                        if (isset($paramDefinition->pattern)) {
139
                            $requirements[$paramDefinition->name] = $paramDefinition->pattern;
140
                            break;
141
                        }
142
                        if (isset($paramDefinition->enum)) {
143
                            $requirements[$paramDefinition->name] = '(' .
144
                                implode('|', $paramDefinition->enum)
145
                                . ')';
146
                            break;
147
                        }
148
                        break;
149
                    default:
150
                        //NOOP
151
                }
152
            }
153
        }
154
155
        return $requirements;
156
    }
157
158
    /**
159
     * @param        $operationSpec
160
     * @param        $methodName
161
     * @param        $resourceName
162
     * @param string $router
163
     * @param null   $routerController
164
     *
165
     * @return string
166
     */
167
    private function resolveControllerKey(
168
        $operationSpec,
169
        $methodName,
170
        $resourceName,
171
        $router,
172
        $routerController = null
173
    ) {
174
        $operationName = $methodName;
175
        $diKey = "$router.$resourceName";
176
        if (isset($operationSpec->operationId)) {
177
            if (false !== strpos($operationSpec->operationId, ':')) {
178
                return $operationSpec->operationId;
179
            }
180
            $operationName = $operationSpec->operationId;
181
        }
182
183
        if (property_exists($operationSpec, 'x-router-controller')) {
184
            $diKey = $operationSpec->{'x-router-controller'};
185
        } elseif ($routerController) {
186
            $diKey = $routerController;
187
        }
188
189
        return "$diKey:$operationName";
190
    }
191
192
    /**
193
     * @param string $resource
194
     * @param string $path
195
     *
196
     * @param string $controllerKey
197
     *
198
     * @return string
199
     */
200
    private function createRouteId($resource, $path, $controllerKey)
201
    {
202
        list(, $operationName) = explode(':', $controllerKey);
203
        $fileName = pathinfo($resource, PATHINFO_FILENAME);
204
        $normalizedPath = strtolower(trim(preg_replace('/\W+/', '.', $path), '.'));
205
        $routeName = "swagger.{$fileName}.$normalizedPath.$operationName";
206
207
        return $routeName;
208
    }
209
}
210