Passed
Pull Request — master (#3041)
by
unknown
04:27
created

EntrypointAction::parseRequest()   B

Complexity

Conditions 8
Paths 20

Size

Total Lines 31
Code Lines 16

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 8
eloc 16
nc 20
nop 1
dl 0
loc 31
rs 8.4444
c 0
b 0
f 0
1
<?php
2
3
/*
4
 * This file is part of the API Platform project.
5
 *
6
 * (c) Kévin Dunglas <[email protected]>
7
 *
8
 * For the full copyright and license information, please view the LICENSE
9
 * file that was distributed with this source code.
10
 */
11
12
declare(strict_types=1);
13
14
namespace ApiPlatform\Core\GraphQl\Action;
15
16
use ApiPlatform\Core\GraphQl\ExecutorInterface;
17
use ApiPlatform\Core\GraphQl\Type\SchemaBuilderInterface;
18
use GraphQL\Error\Debug;
19
use GraphQL\Error\Error;
20
use GraphQL\Executor\ExecutionResult;
21
use Symfony\Component\HttpFoundation\JsonResponse;
22
use Symfony\Component\HttpFoundation\Request;
23
use Symfony\Component\HttpFoundation\Response;
24
use Symfony\Component\PropertyAccess\PropertyAccess;
25
26
/**
27
 * GraphQL API entrypoint.
28
 *
29
 * @author Alan Poulain <[email protected]>
30
 */
31
final class EntrypointAction
32
{
33
    private $schemaBuilder;
34
    private $executor;
35
    private $graphiQlAction;
36
    private $graphQlPlaygroundAction;
37
    private $debug;
38
    private $graphiqlEnabled;
39
    private $graphQlPlaygroundEnabled;
40
    private $defaultIde;
41
42
    public function __construct(SchemaBuilderInterface $schemaBuilder, ExecutorInterface $executor, GraphiQlAction $graphiQlAction, GraphQlPlaygroundAction $graphQlPlaygroundAction, bool $debug = false, bool $graphiqlEnabled = false, bool $graphQlPlaygroundEnabled = false, $defaultIde = false)
43
    {
44
        $this->schemaBuilder = $schemaBuilder;
45
        $this->executor = $executor;
46
        $this->graphiQlAction = $graphiQlAction;
47
        $this->graphQlPlaygroundAction = $graphQlPlaygroundAction;
48
        $this->debug = $debug ? Debug::INCLUDE_DEBUG_MESSAGE | Debug::INCLUDE_TRACE : false;
49
        $this->graphiqlEnabled = $graphiqlEnabled;
50
        $this->graphQlPlaygroundEnabled = $graphQlPlaygroundEnabled;
51
        $this->defaultIde = $defaultIde;
52
    }
53
54
    public function __invoke(Request $request): Response
55
    {
56
        if ($request->isMethod('GET') && 'html' === $request->getRequestFormat()) {
57
            if ('graphiql' === $this->defaultIde && $this->graphiqlEnabled) {
58
                return ($this->graphiQlAction)($request);
59
            }
60
61
            if ('graphql-playground' === $this->defaultIde && $this->graphQlPlaygroundEnabled) {
62
                return ($this->graphQlPlaygroundAction)($request);
63
            }
64
        }
65
66
        [$query, $operation, $variables] = $this->parseRequest($request);
67
68
        if (null === $query) {
69
            return new JsonResponse(new ExecutionResult(null, [new Error('GraphQL query is not valid')]), Response::HTTP_BAD_REQUEST);
70
        }
71
72
        if (null === $variables) {
73
            return new JsonResponse(new ExecutionResult(null, [new Error('GraphQL variables are not valid JSON')]), Response::HTTP_BAD_REQUEST);
74
        }
75
76
        try {
77
            $executionResult = $this->executor->executeQuery($this->schemaBuilder->getSchema(), $query, null, null, $variables, $operation);
78
        } catch (\Exception $e) {
79
            $executionResult = new ExecutionResult(null, [new Error($e->getMessage(), null, null, null, null, $e)]);
80
        }
81
82
        return new JsonResponse($executionResult->toArray($this->debug));
83
    }
84
85
    private function parseRequest(Request $request): array
86
    {
87
        $query = $request->query->get('query');
88
        $operation = $request->query->get('operation');
89
        if ($variables = $request->query->get('variables', [])) {
90
            $variables = json_decode($variables, true);
91
        }
92
93
        if (!$request->isMethod('POST')) {
94
            return [$query, $operation, $variables];
95
        }
96
97
        if ('json' === $request->getContentType()) {
98
            return $this->parseInput($query, $variables, $operation, $request->getContent());
99
        }
100
101
        if (false !== mb_stripos($request->headers->get('CONTENT_TYPE'), 'multipart/form-data')) {
102
            if ($request->request->has('operations')) {
103
                [$query, $operation, $variables] = $this->parseInput($query, $variables, $operation, $request->request->get('operations'));
104
105
                if ($request->request->has('map')) {
106
                    $variables = $this->applyMapToVariables($request, $variables);
107
                }
108
            }
109
        }
110
111
        if ('application/graphql' === $request->headers->get('CONTENT_TYPE')) {
112
            $query = $request->getContent();
113
        }
114
115
        return [$query, $operation, $variables];
116
    }
117
118
    private function parseInput(?string $query, ?array $variables, ?string $operation, string $jsonContent): array
119
    {
120
        $input = json_decode($jsonContent, true);
121
122
        if (isset($input['query'])) {
123
            $query = $input['query'];
124
        }
125
126
        if (isset($input['variables'])) {
127
            $variables = \is_array($input['variables']) ? $input['variables'] : json_decode($input['variables'], true);
128
        }
129
130
        if (isset($input['operation'])) {
131
            $operation = $input['operation'];
132
        }
133
134
        return [$query, $operation, $variables];
135
    }
136
137
    private function applyMapToVariables(Request $request, array $variables): array
138
    {
139
        $mapValues = json_decode($request->request->get('map'), true);
140
        if (!$mapValues) {
141
            return $variables;
142
        }
143
        $propertyAccessor = PropertyAccess::createPropertyAccessor();
144
        foreach ($mapValues as $key => $value) {
145
            if ($request->files->has($key)) {
146
                foreach ($mapValues[$key] as $mapValue) {
147
                    $path = explode('.', $mapValue);
148
                    if ('variables' === $path[0]) {
149
                        unset($path[0]);
150
                        $propertyAccessor->setValue($variables, '['.implode('][', $path).']', $request->files->get($key));
151
                    }
152
                }
153
            }
154
        }
155
156
        return $variables;
157
    }
158
}
159