Completed
Push — feature/http204_for_options_re... ( 6bdeb4...dce5f4 )
by Bastian
233:11 queued 227:03
created

RestUtils::serializeContent()   A

Complexity

Conditions 3
Paths 2

Size

Total Lines 19
Code Lines 14

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 6
CRAP Score 4.944

Importance

Changes 2
Bugs 0 Features 1
Metric Value
dl 0
loc 19
ccs 6
cts 15
cp 0.4
rs 9.4285
c 2
b 0
f 1
cc 3
eloc 14
nc 2
nop 2
crap 4.944
1
<?php
2
/**
3
 * service for RESTy stuff
4
 */
5
6
namespace Graviton\RestBundle\Service;
7
8
use Psr\Log\LoggerInterface;
9
use Symfony\Component\DependencyInjection\ContainerInterface;
10
use Symfony\Component\Routing\Route;
11
use Symfony\Component\Routing\Router;
12
use JMS\Serializer\Serializer;
13
use JMS\Serializer\SerializationContext;
14
use Graviton\RestBundle\Controller\RestController;
15
16
/**
17
 * A service (meaning symfony service) providing some convenience stuff when dealing with our RestController
18
 * based services (meaning rest services).
19
 *
20
 * @author   List of contributors <https://github.com/libgraviton/graviton/graphs/contributors>
21
 * @license  http://opensource.org/licenses/gpl-license.php GNU Public License
22
 * @link     http://swisscom.ch
23
 */
24
final class RestUtils implements RestUtilsInterface
25
{
26
    /**
27
     * @var ContainerInterface
28
     */
29
    private $container;
30
31
    /**
32
     * @var Serializer
33
     */
34
    private $serializer;
35
36
    /**
37
     * @var null|SerializationContext
38
     */
39
    private $serializerContext;
40
41
    /**
42
     * @var Router
43
     */
44
    private $router;
45
46
    /** @var LoggerInterface  */
47
    private $logger;
48
49
    /**
50
     * @param ContainerInterface   $container         container
51
     * @param Router               $router            router
52
     * @param Serializer           $serializer        serializer
53
     * @param LoggerInterface      $logger            PSR logger (e.g. Monolog)
54
     * @param SerializationContext $serializerContext context for serializer
0 ignored issues
show
Documentation introduced by
Should the type for parameter $serializerContext not be null|SerializationContext?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
55
     */
56 6
    public function __construct(
57
        ContainerInterface $container,
58
        Router $router,
59
        Serializer $serializer,
60
        LoggerInterface $logger,
61
        SerializationContext $serializerContext = null
62
    ) {
63 6
        $this->container = $container;
64 6
        $this->serializer = $serializer;
65 6
        $this->serializerContext = $serializerContext;
66 6
        $this->router = $router;
67 6
        $this->logger = $logger;
68 6
    }
69
70
    /**
71
     * Builds a map of baseroutes (controllers) to its relevant route to the actions.
72
     * ignores schema stuff.
73
     *
74
     * @return array grouped array of basenames and actions..
75
     */
76
    public function getServiceRoutingMap()
77
    {
78
        $ret = array();
79
        $optionRoutes = $this->getOptionRoutes();
80
81
        foreach ($optionRoutes as $routeName => $optionRoute) {
82
            // get base name from options action
83
            $routeParts = explode('.', $routeName);
84
            array_pop($routeParts); // get rid of last part
85
            $baseName = implode('.', $routeParts);
86
87
            // get routes from same controller
88
            foreach ($this->getRoutesByBasename($baseName) as $routeName => $route) {
89
                // don't put schema stuff
90
                if (strpos('schema', strtolower($routeName)) === false) {
91
                    $ret[$baseName][$routeName] = $route;
92
                }
93
            }
94
        }
95
96
        return $ret;
97
    }
98
99
    /**
100
     * Public function to serialize stuff according to the serializer rules.
101
     *
102
     * @param object $content Any content to serialize
103
     * @param string $format  Which format to serialize into
104
     *
105
     * @throws \Exception
106
     *
107
     * @return string $content Json content
108
     */
109 6
    public function serializeContent($content, $format = 'json')
110
    {
111
        try {
112 6
            return $this->getSerializer()->serialize(
113 3
                $content,
114 3
                $format,
115 6
                $this->getSerializerContext()
116 3
            );
117
        } catch (\Exception $e) {
118
            $msg = sprintf(
119
                'Cannot serialize content class: %s; with id: %s; Message: %s',
120
                get_class($content),
121
                method_exists($content, 'getId') ? $content->getId() : '-no id-',
122
                str_replace('MongoDBODMProxies\__CG__\GravitonDyn', '', $e->getMessage())
123
            );
124
            $this->logger->alert($msg);
125
            throw new \Exception($msg, $e->getCode());
126
        }
127
    }
128
129
    /**
130
     * Deserialize the given content throw an exception if something went wrong
131
     *
132
     * @param string $content       Request content
133
     * @param string $documentClass Document class
134
     * @param string $format        Which format to deserialize from
135
     *
136
     * @throws \Exception
137
     *
138
     * @return object|array|integer|double|string|boolean
139
     */
140
    public function deserializeContent($content, $documentClass, $format = 'json')
141
    {
142
        $record = $this->getSerializer()->deserialize(
143
            $content,
144
            $documentClass,
145
            $format
146
        );
147
148
        return $record;
149
    }
150
151
    /**
152
     * Get the serializer
153
     *
154
     * @return Serializer
155
     */
156 6
    public function getSerializer()
157
    {
158 6
        return $this->serializer;
159
    }
160
161
    /**
162
     * Get the serializer context
163
     *
164
     * @return SerializationContext
165
     */
166 6
    public function getSerializerContext()
167
    {
168 6
        return clone $this->serializerContext;
169
    }
170
171
    /**
172
     * It has been deemed that we search for OPTION routes in order to detect our
173
     * service routes and then derive the rest from them.
174
     *
175
     * @return array An array with option routes
176
     */
177
    public function getOptionRoutes()
178
    {
179
        $router = $this->router;
180
        $ret = array_filter(
181
            $router->getRouteCollection()
182
                   ->all(),
183
            function ($route) {
184
                if (!in_array('OPTIONS', $route->getMethods())) {
185
                    return false;
186
                }
187
                // ignore all schema routes
188
                if (strpos($route->getPath(), '/schema') === 0) {
189
                    return false;
190
                }
191
                if ($route->getPath() == '/' || $route->getPath() == '/core/version') {
192
                    return false;
193
                }
194
195
                return is_null($route->getRequirement('id'));
196
            }
197
        );
198
199
        return $ret;
200
    }
201
202
    /**
203
     * Based on $baseName, this function returns all routes that match this basename..
204
     * So if you pass graviton.cont.action; it will return all route names that start with the same.
205
     * In our routing naming schema, this means all the routes from the same controller.
206
     *
207
     * @param string $baseName basename
208
     *
209
     * @return array array with matching routes
210
     */
211
    public function getRoutesByBasename($baseName)
212
    {
213
        $ret = array();
214
        foreach ($this->router
215
                      ->getRouteCollection()
216
                      ->all() as $routeName => $route) {
217
            if (preg_match('/^' . $baseName . '/', $routeName)) {
218
                $ret[$routeName] = $route;
219
            }
220
        }
221
222
        return $ret;
223
    }
224
225
    /**
226
     * Gets the Model assigned to the RestController
227
     *
228
     * @param Route $route Route
229
     *
230
     * @return bool|object The model or false
0 ignored issues
show
Documentation introduced by
Consider making the return type a bit more specific; maybe use \Graviton\RestBundle\Model\DocumentModel|false.

This check looks for the generic type array as a return type and suggests a more specific type. This type is inferred from the actual code.

Loading history...
231
     * @throws \Exception
232
     */
233
    public function getModelFromRoute(Route $route)
234
    {
235
        $ret = false;
236
        $controller = $this->getControllerFromRoute($route);
237
238
        if ($controller instanceof RestController) {
239
            $ret = $controller->getModel();
240
        }
241
242
        return $ret;
243
    }
244
245
    /**
246
     * Gets the controller from a Route
247
     *
248
     * @param Route $route Route
249
     *
250
     * @return bool|object The controller or false
0 ignored issues
show
Documentation introduced by
Consider making the return type a bit more specific; maybe use object|false.

This check looks for the generic type array as a return type and suggests a more specific type. This type is inferred from the actual code.

Loading history...
251
     */
252
    public function getControllerFromRoute(Route $route)
253
    {
254
        $ret = false;
255
        $actionParts = explode(':', $route->getDefault('_controller'));
256
257
        if (count($actionParts) == 2) {
258
            $ret = $this->container->get($actionParts[0]);
259
        }
260
261
        return $ret;
262
    }
263
}
264