Completed
Pull Request — develop (#352)
by Samuel
06:45 queued 10s
created

FileController::putAction()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 23
Code Lines 12

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 12

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 23
ccs 0
cts 13
cp 0
rs 9.0856
cc 3
eloc 12
nc 3
nop 2
crap 12
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\Request;
15
use Symfony\Component\HttpFoundation\Response;
16
use Symfony\Bundle\FrameworkBundle\Routing\Router;
17
use Symfony\Component\Validator\Validator\ValidatorInterface;
18
use Symfony\Bundle\FrameworkBundle\Templating\EngineInterface;
19
use Symfony\Component\Form\FormFactory;
20
use Graviton\DocumentBundle\Form\Type\DocumentType;
21
22
/**
23
 * @author   List of contributors <https://github.com/libgraviton/graviton/graphs/contributors>
24
 * @license  http://opensource.org/licenses/gpl-license.php GNU Public License
25
 * @link     http://swisscom.ch
26
 */
27
class FileController extends RestController
28
{
29
    /**
30
     * @var FileManager
31
     */
32
    private $fileManager;
33
34
    /**
35
     * @param Response           $response    Response
36
     * @param RestUtilsInterface $restUtils   Rest utils
37
     * @param Router             $router      Router
38
     * @param ValidatorInterface $validator   Validator
39
     * @param EngineInterface    $templating  Templating
40
     * @param FormFactory        $formFactory form factory
41
     * @param DocumentType       $formType    generic form
42
     * @param ContainerInterface $container   Container
43
     * @param SchemaUtils        $schemaUtils schema utils
44
     * @param FileManager        $fileManager Handles file specific tasks
45
     */
46 1 View Code Duplication
    public function __construct(
47
        Response $response,
48
        RestUtilsInterface $restUtils,
49
        Router $router,
50
        ValidatorInterface $validator,
51
        EngineInterface $templating,
52
        FormFactory $formFactory,
53
        DocumentType $formType,
54
        ContainerInterface $container,
55
        SchemaUtils $schemaUtils,
56
        FileManager $fileManager
57
    ) {
58 1
        parent::__construct(
59 1
            $response,
60 1
            $restUtils,
61 1
            $router,
62 1
            $validator,
63 1
            $templating,
64 1
            $formFactory,
65 1
            $formType,
66 1
            $container,
67
            $schemaUtils
68 1
        );
69 1
        $this->fileManager = $fileManager;
70 1
    }
71
72
    /**
73
     * Writes a new Entry to the database
74
     *
75
     * @param Request $request Current http request
76
     *
77
     * @return Response $response Result of action with data (if successful)
78
     */
79 1
    public function postAction(Request $request)
80
    {
81 1
        $response = $this->getResponse();
82 1
        $fileData = $this->validateRequest($request, $response, $request->get('metadata'));
83 1
        $files = $this->fileManager->saveFiles($request, $this->getModel(), $fileData);
84
85
        // store id of new record so we don't need to re-parse body later when needed
86 1
        $request->attributes->set('id', $files[0]);
87
88
        // Set status code and content
89 1
        $response->setStatusCode(Response::HTTP_CREATED);
90
91
        // TODO: this not is correct for multiple uploaded files!!
92
        // TODO: Probably use "Link" header to address this.
93 1
        $locations = $this->determineRoutes($request->get('_route'), $files, ['post', 'postNoSlash']);
94 1
        $response->headers->set(
95 1
            'Location',
96 1
            $locations[0]
97 1
        );
98
99 1
        return $response;
100
    }
101
102
    /**
103
     * respond with document if non json mime-type is requested
104
     *
105
     * @param Request $request Current http request
106
     * @param string  $id      id of file
107
     *
108
     * @return Response
109
     */
110 1
    public function getAction(Request $request, $id)
111
    {
112 1
        $accept = $request->headers->get('accept');
113 1
        if (substr(strtolower($accept), 0, 16) === 'application/json') {
114 1
            return parent::getAction($request, $id);
115
        }
116
        $response = $this->getResponse();
117
118
        if (!$this->fileManager->has($id)) {
119
            $response->setStatusCode(Response::HTTP_NOT_FOUND);
120
121
            return $response;
122
        }
123
124
        $record = $this->findRecord($id);
125
        $data = $this->fileManager->read($id);
126
127
        $response->setStatusCode(Response::HTTP_OK);
128
        $response->headers->set('Content-Type', $record->getMetadata()->getMime());
129
130
        return $this->render(
131
            'GravitonFileBundle:File:index.raw.twig',
132
            ['data' => $data],
133
            $response
134
        );
135
    }
136
137
    /**
138
     * Update a record
139
     *
140
     * @param Number  $id      ID of record
141
     * @param Request $request Current http request
142
     *
143
     * @return Response $response Result of action with data (if successful)
144
     */
145
    public function putAction($id, Request $request)
146
    {
147
        $contentType = $request->headers->get('Content-Type');
148
        if (substr(strtolower($contentType), 0, 16) === 'application/json') {
149
            return parent::putAction($id, $request);
150
        }
151
        if (0 === strpos($contentType, 'multipart/form-data')) {
152
            $request = $this->normalizeRequest($request);
153
        }
154
155
        $response = $this->getResponse();
156
        $fileData = $this->validateRequest($request, $response, $request->get('metadata'));
157
        $files = $this->fileManager->saveFiles($request, $this->getModel(), $fileData);
158
159
        // store id of new record so we don't need to re-parse body later when needed
160
        $request->attributes->set('id', $files[0]);
161
162
        $response->setStatusCode(Response::HTTP_NO_CONTENT);
163
164
        // no service sends Location headers on PUT - /file shouldn't as well
165
166
        return $response;
167
    }
168
169
    /**
170
     * Deletes a record
171
     *
172
     * @param Number $id ID of record
173
     *
174
     * @return Response $response Result of the action
175
     */
176 1
    public function deleteAction($id)
177
    {
178 1
        if ($this->fileManager->has($id)) {
179 1
            $this->fileManager->delete($id);
180 1
        }
181
182 1
        return parent::deleteAction($id);
183
    }
184
185
    /**
186
     * Determines the routes and replaces the http method
187
     *
188
     * @param string $routeName  Name of the route to be generated
189
     * @param array  $files      Set of uploaded files
190
     * @param array  $routeTypes Set of route types to be recognized
191
     *
192
     * @return array
193
     */
194 1
    private function determineRoutes($routeName, array $files, array $routeTypes)
195
    {
196 1
        $locations = [];
197 1
        $newRouteName = '';
198 1
        foreach ($routeTypes as $routeType) {
199 1
            $routeParts = explode('.', $routeName);
200
201 1
            if ($routeType == array_pop($routeParts)) {
202 1
                $reduce = (-1) * strlen($routeType);
203 1
                $newRouteName = substr($routeName, 0, $reduce).'get';
204 1
                break;
205
            }
206 1
        }
207
208 1
        if (!empty($newRouteName)) {
209 1
            foreach ($files as $id) {
210 1
                $locations[] = $this->getRouter()->generate($newRouteName, array('id' => $id));
211 1
            }
212 1
        }
213
214 1
        return $locations;
215
    }
216
217
    /**
218
     * Validates the provided request
219
     *
220
     * @param Request  $request  Http request to be validated
221
     * @param Response $response Http response to be returned in case of an error
222
     * @param string   $fileData Alternative content to be validated
223
     *
224
     * @throws \Exception
225
     * @return File|null
226
     */
227 1
    private function validateRequest(Request $request, Response $response, $fileData = '')
228
    {
229 1
        if (!empty($fileData)) {
230 1
            $this->formValidator->checkJsonRequest($request, $response, $fileData);
231 1
            $model = $this->getModel();
232 1
            return $this->formValidator->checkForm(
233 1
                $this->formValidator->getForm($request, $model),
234 1
                $model,
235 1
                $this->formDataMapper,
236
                $fileData
237 1
            );
238
        }
239
    }
240
241
    /**
242
     * Gathers information into a request
243
     *
244
     * @param Request $request master request sent by client.
245
     *
246
     * @return Request
247
     */
248
    private function normalizeRequest(Request $request)
249
    {
250
        $contentData = $this->fileManager->extractDataFromRequestContent($request);
251
        $normalized = $request->duplicate(
252
            null,
253
            null,
254
            $contentData['attributes'],
255
            null,
256
            $contentData['files']
257
        );
258
259
        return $normalized;
260
    }
261
}
262