Passed
Pull Request — main (#104)
by
unknown
11:58
created

validateHeaderParameter()   A

Complexity

Conditions 4
Paths 3

Size

Total Lines 20
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 4
eloc 11
c 0
b 0
f 0
nc 3
nop 3
dl 0
loc 20
rs 9.9
1
<?php
2
3
declare(strict_types=1);
4
5
/*
6
 * This file is part of the OpenapiBundle package.
7
 *
8
 * (c) Niels Nijens <[email protected]>
9
 *
10
 * For the full copyright and license information, please view the LICENSE
11
 * file that was distributed with this source code.
12
 */
13
14
namespace Nijens\OpenapiBundle\Validation\RequestValidator;
15
16
use JsonSchema\Constraints\Constraint;
17
use JsonSchema\Validator;
18
use Nijens\OpenapiBundle\ExceptionHandling\Exception\InvalidRequestParameterProblemException;
19
use Nijens\OpenapiBundle\ExceptionHandling\Exception\ProblemException;
20
use Nijens\OpenapiBundle\ExceptionHandling\Exception\RequestProblemExceptionInterface;
21
use Nijens\OpenapiBundle\ExceptionHandling\Exception\Violation;
22
use Nijens\OpenapiBundle\Routing\RouteContext;
23
use stdClass;
24
use Symfony\Component\HttpFoundation\Request;
25
use Symfony\Component\HttpFoundation\Response;
26
27
/**
28
 * Validates the query parameters of the request with the schema from the OpenAPI document.
29
 *
30
 * @experimental
31
 *
32
 * @author Niels Nijens <[email protected]>
33
 */
34
final class RequestParameterValidator implements ValidatorInterface
35
{
36
    /**
37
     * @var Validator
38
     */
39
    private $jsonValidator;
40
41
    public function __construct(Validator $jsonValidator)
42
    {
43
        $this->jsonValidator = $jsonValidator;
44
    }
45
46
    public function validate(Request $request): ?RequestProblemExceptionInterface
47
    {
48
        $validateQueryParameters = $this->getValidateQueryParametersFromRequest($request);
49
        $validateHeaderParameters = $this->getValidateHeaderParametersFromRequest($request);
50
51
        $violations = [];
52
        foreach ($validateQueryParameters as $parameterName => $parameter) {
53
            $violations = array_merge(
54
                $violations,
55
                $this->validateQueryParameter($request, $parameterName, json_decode($parameter))
56
            );
57
        }
58
59
        foreach ($validateHeaderParameters as $parameterName => $parameter) {
60
            $violations = array_merge(
61
                $violations,
62
                $this->validateHeaderParameter($request, $parameterName, json_decode($parameter))
63
            );
64
        }
65
66
        if (count($violations) > 0) {
67
            $exception = new InvalidRequestParameterProblemException(
68
                ProblemException::DEFAULT_TYPE_URI,
69
                'The request contains errors.',
70
                Response::HTTP_BAD_REQUEST,
71
                'Validation of query parameters failed.'
72
            );
73
74
            return $exception->withViolations($violations);
75
        }
76
77
        return null;
78
    }
79
80
    private function getValidateQueryParametersFromRequest(Request $request): array
81
    {
82
        return $request->attributes
83
            ->get(RouteContext::REQUEST_ATTRIBUTE)[RouteContext::REQUEST_VALIDATE_QUERY_PARAMETERS] ?? [];
84
    }
85
86
    private function validateQueryParameter(Request $request, string $parameterName, stdClass $parameter): array
87
    {
88
        $violations = [];
89
        if ($request->query->has($parameterName) === false && $parameter->required ?? false) {
90
            $violations[] = new Violation(
91
                'required_query_parameter',
92
                sprintf('Query parameter %s is required.', $parameterName),
93
                $parameterName
94
            );
95
96
            return $violations;
97
        }
98
99
        if ($request->query->has($parameterName) === false) {
100
            return $violations;
101
        }
102
103
        $parameterValue = $request->query->get($parameterName);
104
105
        return $this->validateParameterValue($parameterName, $parameter, $parameterValue, $violations);
106
    }
107
108
    private function getValidateHeaderParametersFromRequest(Request $request): array
109
    {
110
        return $request->attributes
111
            ->get(RouteContext::REQUEST_ATTRIBUTE)[RouteContext::REQUEST_VALIDATE_HEADER_PARAMETERS] ?? [];
112
    }
113
114
    private function validateHeaderParameter(Request $request, string $parameterName, stdClass $parameter): array
115
    {
116
        $violations = [];
117
        if ($request->headers->has($parameterName) === false && $parameter->required ?? false) {
118
            $violations[] = new Violation(
119
                'required_header_parameter',
120
                sprintf('Header parameter %s is required.', $parameterName),
121
                $parameterName
122
            );
123
124
            return $violations;
125
        }
126
127
        if ($request->headers->has($parameterName) === false) {
128
            return $violations;
129
        }
130
131
        $parameterValue = $request->headers->get($parameterName);
132
133
        return $this->validateParameterValue($parameterName, $parameter, $parameterValue, $violations);
134
    }
135
136
    private function validateParameterValue(string $parameterName, stdClass $parameter, mixed $parameterValue, array $currentViolations): array
137
    {
138
        $this->jsonValidator->validate($parameterValue, $parameter->schema, Constraint::CHECK_MODE_COERCE_TYPES);
139
        if ($this->jsonValidator->isValid()) {
140
            return $currentViolations;
141
        }
142
143
        $validationErrors = $this->jsonValidator->getErrors();
144
        $this->jsonValidator->reset();
145
146
        return array_map(
147
            function (array $validationError) use ($parameterName): Violation {
148
                $validationError['property'] = $parameterName;
149
150
                return Violation::fromArray($validationError);
151
            },
152
            $validationErrors
153
        );
154
    }
155
}
156