Test Failed
Push — v2 ( ccc702...50c01b )
by Daniel
04:58
created

FileUploadAction::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 16
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
eloc 7
c 1
b 0
f 0
nc 1
nop 7
dl 0
loc 16
rs 10
1
<?php
2
3
namespace Silverback\ApiComponentBundle\Controller;
4
5
use ApiPlatform\Core\DataProvider\ItemDataProviderInterface;
6
use ApiPlatform\Core\Metadata\Resource\Factory\ResourceMetadataFactoryInterface;
7
use InvalidArgumentException;
8
use RuntimeException;
9
use Silverback\ApiComponentBundle\Entity\Component\FileInterface;
0 ignored issues
show
Bug introduced by
The type Silverback\ApiComponentB...Component\FileInterface was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
10
use Silverback\ApiComponentBundle\File\Uploader\FileUploader;
0 ignored issues
show
Bug introduced by
The type Silverback\ApiComponentB...e\Uploader\FileUploader was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
11
use Silverback\ApiComponentBundle\Security\RestrictedResourceVoter;
0 ignored issues
show
Bug introduced by
The type Silverback\ApiComponentB...RestrictedResourceVoter was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
12
use Silverback\ApiComponentBundle\Serializer\ApiContextBuilder;
0 ignored issues
show
Bug introduced by
The type Silverback\ApiComponentB...lizer\ApiContextBuilder was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
13
use Symfony\Component\HttpFoundation\BinaryFileResponse;
14
use Symfony\Component\HttpFoundation\Request;
15
use Symfony\Component\HttpFoundation\Response;
16
use Symfony\Component\PropertyAccess\PropertyAccess;
17
use Symfony\Component\Routing\Annotation\Route;
18
use Symfony\Component\Routing\Matcher\UrlMatcherInterface;
19
use Symfony\Component\Routing\RequestContext;
20
use Symfony\Component\Security\Core\Exception\AccessDeniedException;
21
use Symfony\Component\Serializer\SerializerInterface;
22
23
class FileUploadAction
24
{
25
    private $urlMatcher;
26
    private $itemDataProvider;
27
    private $uploader;
28
    private $serializer;
29
    private $resourceMetadataFactory;
30
    private $apiContextBuilder;
31
    private $restrictedResourceVoter;
32
33
    public function __construct(
34
        UrlMatcherInterface $urlMatcher,
35
        ItemDataProviderInterface $itemDataProvider,
36
        FileUploader $uploader,
37
        SerializerInterface $serializer,
38
        ResourceMetadataFactoryInterface $resourceMetadataFactory,
39
        ApiContextBuilder $apiContextBuilder,
40
        RestrictedResourceVoter $restrictedResourceVoter
41
    ) {
42
        $this->urlMatcher = $urlMatcher;
43
        $this->itemDataProvider = $itemDataProvider;
44
        $this->uploader = $uploader;
45
        $this->serializer = $serializer;
46
        $this->resourceMetadataFactory = $resourceMetadataFactory;
47
        $this->apiContextBuilder = $apiContextBuilder;
48
        $this->restrictedResourceVoter = $restrictedResourceVoter;
49
    }
50
51
    /**
52
     * @param Request $request
53
     * @param string $field
54
     * @param string $id
55
     * @Route(
56
     *     name="files_upload",
57
     *     path="/files/{field}/{id}.{_format}",
58
     *     requirements={"field"="\w+", "id"=".+"},
59
     *     defaults={"_format"="jsonld"},
60
     *     methods={"POST", "PUT", "GET"}
61
     * )
62
     * @return Response
63
     */
64
    public function __invoke(Request $request, string $field, string $id)
65
    {
66
        $contentType = $request->headers->get('CONTENT_TYPE');
67
        $_format = $request->attributes->get('_format') ?: $request->getFormat($contentType);
68
69
        /**
70
         * MATCH THE ID TO A ROUTE TO FIND RESOURCE CLASS AND ID
71
         * @var array|null $route
72
         */
73
        $ctx = new RequestContext();
74
        $ctx->fromRequest($request);
75
        $ctx->setMethod('GET');
76
        $this->urlMatcher->setContext($ctx);
77
        $route = $this->urlMatcher->match($id);
78
        if (empty($route) || !isset($route['_api_resource_class'])) {
79
            return new Response(sprintf('No route/resource found for id %s', $id), Response::HTTP_BAD_REQUEST);
80
        }
81
82
        /**
83
         * GET THE ENTITY
84
         */
85
        $entity = $this->itemDataProvider->getItem($route['_api_resource_class'], $route['id']);
86
        if (!$entity) {
87
            return new Response(sprintf('Entity not found from provider %s (ID: %s)', $route['_api_resource_class'], $route['id']), Response::HTTP_BAD_REQUEST);
88
        }
89
        if (!($entity instanceof FileInterface)) {
90
            return new Response(sprintf('Provider %s does not implement %s', $route['_api_resource_class'], FileInterface::class), Response::HTTP_BAD_REQUEST);
91
        }
92
        $method = strtolower($request->getMethod());
93
94
        if ($method === 'get') {
95
            $propertyAccessor = PropertyAccess::createPropertyAccessor();
96
            if (!$this->restrictedResourceVoter->vote($entity)) {
97
                throw new AccessDeniedException('You are not permitted to download this file');
98
            }
99
            $filePath = $propertyAccessor->getValue($entity, $field);
100
            return new BinaryFileResponse($filePath);
101
        }
102
103
        /**
104
         * CHECK WE HAVE A FILE - WASTE OF TIME DOING ANYTHING ELSE OTHERWISE
105
         */
106
        if (!$request->files->count()) {
107
            return new Response('No files have been submitted', Response::HTTP_BAD_REQUEST);
108
        }
109
110
        /**
111
         * UPLOAD THE FILE
112
         */
113
        $files = $request->files->all();
114
        try {
115
            $entity = $this->uploader->upload($entity, $field, reset($files), $method);
116
        } catch (InvalidArgumentException $exception) {
117
            return new Response($exception->getMessage(), Response::HTTP_BAD_REQUEST);
118
        } catch (RuntimeException $exception) {
119
            return new Response($exception->getMessage(), Response::HTTP_INTERNAL_SERVER_ERROR);
120
        }
121
122
        /**
123
         * Return the entity back in the format requested
124
         */
125
        $resourceMetadata = $this->resourceMetadataFactory->create($route['_api_resource_class']);
126
        $serializerGroups = $resourceMetadata->getOperationAttribute(
127
            ['item_operation_name' => $method],
128
            'serializer_groups',
129
            [],
130
            true
131
        );
132
        $customGroups = $this->apiContextBuilder->getGroups($route['_api_resource_class'], true);
133
        if (\count($customGroups)) {
134
            $serializerGroups = array_merge($serializerGroups ?? [], ...$customGroups);
135
        }
136
        $serializedData = $this->serializer->serialize($entity, $_format, ['groups' => $serializerGroups]);
0 ignored issues
show
Bug introduced by
It seems like $_format can also be of type null; however, parameter $format of Symfony\Component\Serial...rInterface::serialize() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

136
        $serializedData = $this->serializer->serialize($entity, /** @scrutinizer ignore-type */ $_format, ['groups' => $serializerGroups]);
Loading history...
137
        return new Response($serializedData, Response::HTTP_OK);
138
    }
139
}
140