Completed
Pull Request — develop (#349)
by Bastian
07:34
created

FileController::validateRequest()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 13
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 7
CRAP Score 2.0078
Metric Value
dl 0
loc 13
ccs 7
cts 8
cp 0.875
rs 9.4286
cc 2
eloc 9
nc 2
nop 3
crap 2.0078
1
<?php
2
/**
3
 * controller for gaufrette based file store
4
 */
5
6
namespace Graviton\FileBundle\Controller;
7
8
use Graviton\FileBundle\FileManager;
9
use Graviton\RestBundle\Controller\RestController;
10
use Graviton\RestBundle\Service\RestUtilsInterface;
11
use Graviton\SchemaBundle\SchemaUtils;
12
use GravitonDyn\FileBundle\Document\File;
13
use Symfony\Component\DependencyInjection\ContainerInterface;
14
use Symfony\Component\HttpFoundation\FileBag;
15
use Symfony\Component\HttpFoundation\ParameterBag;
16
use Symfony\Component\HttpFoundation\Request;
17
use Symfony\Component\HttpFoundation\Response;
18
use Symfony\Bundle\FrameworkBundle\Routing\Router;
19
use Symfony\Component\Validator\Validator\ValidatorInterface;
20
use Symfony\Bundle\FrameworkBundle\Templating\EngineInterface;
21
use Symfony\Component\Form\FormFactory;
22
use Graviton\DocumentBundle\Form\Type\DocumentType;
23
24
/**
25
 * @author   List of contributors <https://github.com/libgraviton/graviton/graphs/contributors>
26
 * @license  http://opensource.org/licenses/gpl-license.php GNU Public License
27
 * @link     http://swisscom.ch
28
 */
29
class FileController extends RestController
30
{
31
    /**
32
     * @var FileManager
33
     */
34
    private $fileManager;
35
36
    /**
37
     * @param Response           $response    Response
38
     * @param RestUtilsInterface $restUtils   Rest utils
39
     * @param Router             $router      Router
40
     * @param ValidatorInterface $validator   Validator
41
     * @param EngineInterface    $templating  Templating
42
     * @param FormFactory        $formFactory form factory
43
     * @param DocumentType       $formType    generic form
44
     * @param ContainerInterface $container   Container
45
     * @param SchemaUtils        $schemaUtils schema utils
46
     * @param FileManager        $fileManager Handles file specific tasks
47
     */
48 1 View Code Duplication
    public function __construct(
1 ignored issue
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
49
        Response $response,
50
        RestUtilsInterface $restUtils,
51
        Router $router,
52
        ValidatorInterface $validator,
53
        EngineInterface $templating,
54
        FormFactory $formFactory,
55
        DocumentType $formType,
56
        ContainerInterface $container,
57
        SchemaUtils $schemaUtils,
58
        FileManager $fileManager
59
    ) {
60 1
        parent::__construct(
61
            $response,
62
            $restUtils,
63
            $router,
64
            $validator,
65
            $templating,
66
            $formFactory,
67
            $formType,
68
            $container,
69
            $schemaUtils
70
        );
71 1
        $this->fileManager = $fileManager;
72 1
    }
73
74
    /**
75
     * Writes a new Entry to the database
76
     *
77
     * @param Request $request Current http request
78
     *
79
     * @return Response $response Result of action with data (if successful)
80
     */
81 1
    public function postAction(Request $request)
82
    {
83 1
        $response = $this->getResponse();
84 1
        $fileData = $this->validateRequest($request, $response, $request->get('metadata'));
85 1
        $files = $this->fileManager->saveFiles($request, $this->getModel(), $fileData);
86
87
        // store id of new record so we don't need to re-parse body later when needed
88 1
        $request->attributes->set('id', $files[0]);
89
90
        // Set status code and content
91 1
        $response->setStatusCode(Response::HTTP_CREATED);
92
93
        // TODO: this not is correct for multiple uploaded files!!
94
        // TODO: Probably use "Link" header to address this.
95 1
        $locations = $this->determineRoutes($request->get('_route'), $files, ['post', 'postNoSlash']);
96 1
        $response->headers->set(
97 1
            'Location',
98 1
            $locations[0]
99
        );
100
101 1
        return $response;
102
    }
103
104
    /**
105
     * respond with document if non json mime-type is requested
106
     *
107
     * @param Request $request Current http request
108
     * @param string  $id      id of file
109
     *
110
     * @return Response
111
     */
112 1
    public function getAction(Request $request, $id)
113
    {
114 1
        $accept = $request->headers->get('accept');
115 1
        if (substr(strtolower($accept), 0, 16) === 'application/json') {
116 1
            return parent::getAction($request, $id);
117
        }
118
        $response = $this->getResponse();
119
120
        if (!$this->fileManager->has($id)) {
121
            $response->setStatusCode(Response::HTTP_NOT_FOUND);
122
123
            return $response;
124
        }
125
126
        $record = $this->findRecord($id);
127
        $data = $this->fileManager->read($id);
128
129
        $response->setStatusCode(Response::HTTP_OK);
130
        $response->headers->set('Content-Type', $record->getMetadata()->getMime());
131
132
        return $this->render(
133
            'GravitonFileBundle:File:index.raw.twig',
134
            ['data' => $data],
135
            $response
136
        );
137
    }
138
139
    /**
140
     * Update a record
141
     *
142
     * @param Number  $id      ID of record
143
     * @param Request $request Current http request
144
     *
145
     * @return Response $response Result of action with data (if successful)
146
     */
147
    public function putAction($id, Request $request)
148
    {
149
        $contentType = $request->headers->get('Content-Type');
150
        if (substr(strtolower($contentType), 0, 16) === 'application/json') {
151
            return parent::putAction($id, $request);
152
        }
153
        if (0 === strpos($contentType, 'multipart/form-data')) {
154
            $request = $this->normalizeRequest($request);
155
        }
156
157
        $response = $this->getResponse();
158
        $fileData = $this->validateRequest($request, $response, $request->get('metadata'));
159
        $files = $this->fileManager->saveFiles($request, $this->getModel(), $fileData);
160
161
        // store id of new record so we don't need to re-parse body later when needed
162
        $request->attributes->set('id', $files[0]);
163
164
        $response->setStatusCode(Response::HTTP_NO_CONTENT);
165
166
        // no service sends Location headers on PUT - /file shouldn't as well
167
168
        return $response;
169
    }
170
171
    /**
172
     * Deletes a record
173
     *
174
     * @param Number $id ID of record
175
     *
176
     * @return Response $response Result of the action
177
     */
178 1
    public function deleteAction($id)
179
    {
180 1
        if ($this->fileManager->has($id)) {
181 1
            $this->fileManager->delete($id);
182
        }
183
184 1
        return parent::deleteAction($id);
185
    }
186
187
    /**
188
     * Determines the routes and replaces the http method
189
     *
190
     * @param string $routeName  Name of the route to be generated
191
     * @param array  $files      Set of uploaded files
192
     * @param array  $routeTypes Set of route types to be recognized
193
     *
194
     * @return array
195
     */
196 1
    private function determineRoutes($routeName, array $files, array $routeTypes)
197
    {
198 1
        $locations = [];
199 1
        $newRouteName = '';
200 1
        foreach ($routeTypes as $routeType) {
201 1
            $routeParts = explode('.', $routeName);
202
203 1
            if ($routeType == array_pop($routeParts)) {
204 1
                $reduce = (-1) * strlen($routeType);
205 1
                $newRouteName = substr($routeName, 0, $reduce).'get';
206 1
                break;
207
            }
208
        }
209
210 1
        if (!empty($newRouteName)) {
211 1
            foreach ($files as $id) {
212 1
                $locations[] = $this->getRouter()->generate($newRouteName, array('id' => $id));
213
            }
214
        }
215
216 1
        return $locations;
217
    }
218
219
    /**
220
     * Validates the provided request
221
     *
222
     * @param Request  $request  Http request to be validated
223
     * @param Response $response Http response to be returned in case of an error
224
     * @param string   $fileData Alternative content to be validated
225
     *
226
     * @throws \Exception
227
     * @return File|null
228
     */
229 1
    private function validateRequest(Request $request, Response $response, $fileData = '')
230
    {
231 1
        if (!empty($fileData)) {
232 1
            $this->formValidator->checkJsonRequest($request, $response, $fileData);
233 1
            $model = $this->getModel();
234 1
            return $this->formValidator->checkForm(
235 1
                $this->formValidator->getForm($request, $model),
236
                $model,
237 1
                $this->formDataMapper,
238
                $fileData
239
            );
240
        }
241
    }
242
243
    /**
244
     * Gathers information into a request
245
     *
246
     * @param Request $request master request sent by client.
247
     *
248
     * @return Request
249
     */
250
    private function normalizeRequest(Request $request)
251
    {
252
        $contentData = $this->fileManager->extractDataFromRequestContent($request);
253
        $normalized = $request->duplicate(
254
            null,
255
            null,
256
            $contentData['attributes'],
257
            null,
258
            $contentData['files']
259
        );
260
261
        return $normalized;
262
    }
263
}
264