Completed
Pull Request — develop (#337)
by Bastian
06:24
created

FileController::validateRequest()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 13
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 10
CRAP Score 2

Importance

Changes 1
Bugs 0 Features 0
Metric Value
dl 0
loc 13
ccs 10
cts 10
cp 1
rs 9.4286
c 1
b 0
f 0
cc 2
eloc 9
nc 2
nop 3
crap 2
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 7 View Code Duplication
    public function __construct(
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 7
        parent::__construct(
61 7
            $response,
62 7
            $restUtils,
63 7
            $router,
64 7
            $validator,
65 7
            $templating,
66 7
            $formFactory,
67 7
            $formType,
68 7
            $container,
69
            $schemaUtils
70 7
        );
71 7
        $this->fileManager = $fileManager;
72 7
    }
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 4
    public function postAction(Request $request)
82
    {
83 4
        $response = $this->getResponse();
84 4
        $fileData = $this->validateRequest($request, $response, $request->get('metadata'));
85 4
        $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 4
        $request->attributes->set('id', $files[0]);
89
90
        // Set status code and content
91 4
        $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 4
        $locations = $this->determineRoutes($request->get('_route'), $files, ['post', 'postNoSlash']);
96 4
        $response->headers->set(
97 4
            'Location',
98 4
            $locations[0]
99 4
        );
100
101 4
        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 4
    public function getAction(Request $request, $id)
113
    {
114 4
        $accept = $request->headers->get('accept');
115 4
        if (substr(strtolower($accept), 0, 16) === 'application/json') {
116 4
            return parent::getAction($request, $id);
117
        }
118 1
        $response = $this->getResponse();
119
120 1
        if (!$this->fileManager->has($id)) {
121
            $response->setStatusCode(Response::HTTP_NOT_FOUND);
122
123
            return $response;
124
        }
125
126 1
        $record = $this->findRecord($id);
127 1
        $data = $this->fileManager->read($id);
128
129 1
        $response->setStatusCode(Response::HTTP_OK);
130 1
        $response->headers->set('Content-Type', $record->getMetadata()->getMime());
131
132 1
        return $this->render(
133 1
            'GravitonFileBundle:File:index.raw.twig',
134 1
            ['data' => $data],
135
            $response
136 1
        );
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 3
    public function putAction($id, Request $request)
148
    {
149 3
        $contentType = $request->headers->get('Content-Type');
150 3
        if (substr(strtolower($contentType), 0, 16) === 'application/json') {
151 1
            return parent::putAction($id, $request);
152
        }
153 3
        if (0 === strpos($contentType, 'multipart/form-data')) {
154
            $request = $this->normalizeRequest($request);
155
        }
156
157 3
        $response = $this->getResponse();
158 3
        $fileData = $this->validateRequest($request, $response, $request->get('metadata'));
159 3
        $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 3
        $request->attributes->set('id', $files[0]);
163
164 3
        $response->setStatusCode(Response::HTTP_NO_CONTENT);
165
166
        // no service sends Location headers on PUT - /file shouldn't as well
167
168 3
        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 1
        }
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 4
    private function determineRoutes($routeName, array $files, array $routeTypes)
197
    {
198 4
        $locations = [];
199 4
        $newRouteName = '';
200 4
        foreach ($routeTypes as $routeType) {
201 4
            $routeParts = explode('.', $routeName);
202
203 4
            if ($routeType == array_pop($routeParts)) {
204 4
                $reduce = (-1) * strlen($routeType);
205 4
                $newRouteName = substr($routeName, 0, $reduce).'get';
206 4
                break;
207
            }
208 4
        }
209
210 4
        if (!empty($newRouteName)) {
211 4
            foreach ($files as $id) {
212 4
                $locations[] = $this->getRouter()->generate($newRouteName, array('id' => $id));
213 4
            }
214 4
        }
215
216 4
        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 5
    private function validateRequest(Request $request, Response $response, $fileData = '')
230
    {
231 5
        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 1
                $model,
237 1
                $this->formDataMapper,
238
                $fileData
239 1
            );
240
        }
241 5
    }
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