Skip to content

Commit 1e9ca54

Browse files
authored
Merge pull request #22 from 123inkt/Add-support-for-json-body
Add support for json body
2 parents 096cd31 + d40879c commit 1e9ca54

File tree

3 files changed

+101
-29
lines changed

3 files changed

+101
-29
lines changed

src/Constraint/RequestConstraint.php

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,14 @@ class RequestConstraint extends Constraint
1010
public const WRONG_VALUE_TYPE = '08937cc5-9ea6-460c-9917-d3f6ba912998';
1111
public const MISSING_QUERY_CONSTRAINT = 'b62ab5ca-ee6f-4baf-bdef-ffbe14f674d6';
1212
public const MISSING_REQUEST_CONSTRAINT = 'c3990dad-3638-449b-9dd3-4dd42e90c52f';
13+
public const INVALID_BODY_CONTENT = '3b41f393-5f46-471e-8a2e-c4035d5fb3cb';
1314

1415
/** @var array<string, string> */
1516
protected static $errorNames = [
1617
self::WRONG_VALUE_TYPE => 'WRONG_VALUE_TYPE',
1718
self::MISSING_QUERY_CONSTRAINT => 'MISSING_QUERY_CONSTRAINT',
1819
self::MISSING_REQUEST_CONSTRAINT => 'MISSING_REQUEST_CONSTRAINT',
20+
self::INVALID_BODY_CONTENT => 'INVALID_BODY_CONTENT',
1921
];
2022

2123
/** @var string */
@@ -24,6 +26,8 @@ class RequestConstraint extends Constraint
2426
public $queryMessage = 'Request::query is not empty, but there is no constraint configured.';
2527
/** @var string */
2628
public $requestMessage = 'Request::request is not empty, but there is no constraint configured.';
29+
/** @var string */
30+
public $invalidBodyMessage = 'Request::content cant be decoded';
2731

2832
/** @var Constraint|Constraint[]|null */
2933
public $query;

src/Constraint/RequestConstraintValidator.php

Lines changed: 46 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
namespace DigitalRevolution\SymfonyRequestValidation\Constraint;
55

6+
use Symfony\Component\HttpFoundation\Exception\JsonException;
67
use Symfony\Component\HttpFoundation\Request;
78
use Symfony\Component\Validator\Constraint;
89
use Symfony\Component\Validator\ConstraintValidator;
@@ -12,6 +13,7 @@ class RequestConstraintValidator extends ConstraintValidator
1213
{
1314
/**
1415
* @param mixed $value
16+
*
1517
* @inheritDoc
1618
*/
1719
public function validate($value, Constraint $constraint): void
@@ -38,43 +40,73 @@ public function validate($value, Constraint $constraint): void
3840
$this->validateAttributes($constraint, $value);
3941
}
4042

41-
private function validateQuery(RequestConstraint $constraint, Request $value): void
43+
private function validateQuery(RequestConstraint $constraint, Request $request): void
4244
{
4345
if ($constraint->query !== null) {
4446
$this->context->getValidator()
4547
->inContext($this->context)
4648
->atPath('[query]')
47-
->validate($value->query->all(), $constraint->query);
48-
} elseif ($constraint->allowExtraFields === false && count($value->query) > 0) {
49+
->validate($request->query->all(), $constraint->query);
50+
} elseif ($constraint->allowExtraFields === false && count($request->query) > 0) {
4951
$this->context->buildViolation($constraint->queryMessage)
5052
->atPath('[query]')
5153
->setCode($constraint::MISSING_QUERY_CONSTRAINT)
5254
->addViolation();
5355
}
5456
}
5557

56-
private function validateRequest(RequestConstraint $constraint, Request $value): void
58+
private function validateRequest(RequestConstraint $constraint, Request $request): void
5759
{
58-
if ($constraint->request !== null) {
59-
$this->context->getValidator()
60-
->inContext($this->context)
61-
->atPath('[request]')
62-
->validate($value->request->all(), $constraint->request);
63-
} elseif ($constraint->allowExtraFields === false && count($value->request) > 0) {
64-
$this->context->buildViolation($constraint->requestMessage)
60+
if ($constraint->request === null) {
61+
if ($constraint->allowExtraFields === false && count($request->request) > 0) {
62+
$this->context->buildViolation($constraint->requestMessage)
63+
->atPath('[request]')
64+
->setCode($constraint::MISSING_REQUEST_CONSTRAINT)
65+
->addViolation();
66+
}
67+
68+
return;
69+
}
70+
71+
if (in_array($request->getContentType(), ['json', 'jsonld'], true)) {
72+
$data = $this->validateAndGetJsonBody($constraint, $request);
73+
if ($data === null) {
74+
return;
75+
}
76+
} else {
77+
$data = $request->request->all();
78+
}
79+
80+
$this->context->getValidator()
81+
->inContext($this->context)
82+
->atPath('[request]')
83+
->validate($data, $constraint->request);
84+
}
85+
86+
/**
87+
* @return mixed[]|null
88+
*/
89+
private function validateAndGetJsonBody(RequestConstraint $constraint, Request $request): ?array
90+
{
91+
try {
92+
return $request->toArray();
93+
} catch (JsonException $exception) {
94+
$this->context->buildViolation($constraint->invalidBodyMessage)
6595
->atPath('[request]')
66-
->setCode($constraint::MISSING_REQUEST_CONSTRAINT)
96+
->setCode($constraint::INVALID_BODY_CONTENT)
6797
->addViolation();
98+
99+
return null;
68100
}
69101
}
70102

71-
private function validateAttributes(RequestConstraint $constraint, Request $value): void
103+
private function validateAttributes(RequestConstraint $constraint, Request $request): void
72104
{
73105
if ($constraint->attributes !== null) {
74106
$this->context->getValidator()
75107
->inContext($this->context)
76108
->atPath('[attributes]')
77-
->validate($value->attributes->all(), $constraint->attributes);
109+
->validate($request->attributes->all(), $constraint->attributes);
78110
}
79111
}
80112
}

tests/Unit/Constraint/RequestConstraintValidatorTest.php

Lines changed: 51 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
use DigitalRevolution\SymfonyRequestValidation\Constraint\RequestConstraint;
77
use DigitalRevolution\SymfonyRequestValidation\Constraint\RequestConstraintValidator;
88
use PHPUnit\Framework\TestCase;
9+
use Symfony\Component\HttpFoundation\Exception\JsonException;
910
use Symfony\Component\HttpFoundation\Request;
1011
use Symfony\Component\Validator\Constraints as Assert;
1112
use Symfony\Component\Validator\Context\ExecutionContext;
@@ -45,6 +46,7 @@ public function testValidateUnexpectedTypeException(): void
4546

4647
/**
4748
* @param array<mixed> $data
49+
*
4850
* @dataProvider \DigitalRevolution\SymfonyRequestValidation\Tests\DataProvider\Constraint\RequestConstraintValidatorDataProvider::dataProvider
4951
* @covers ::validate
5052
* @covers ::validateQuery
@@ -60,6 +62,7 @@ public function testValidateQuery(array $data, bool $success): void
6062

6163
/**
6264
* @param array<mixed> $data
65+
*
6366
* @dataProvider \DigitalRevolution\SymfonyRequestValidation\Tests\DataProvider\Constraint\RequestConstraintValidatorDataProvider::dataProvider
6467
* @covers ::validate
6568
* @covers ::validateRequest
@@ -75,6 +78,42 @@ public function testValidateRequest(array $data, bool $success): void
7578

7679
/**
7780
* @param array<mixed> $data
81+
*
82+
* @dataProvider \DigitalRevolution\SymfonyRequestValidation\Tests\DataProvider\Constraint\RequestConstraintValidatorDataProvider::dataProvider
83+
* @covers ::validate
84+
* @covers ::validateRequest
85+
* @covers ::validateAndGetJsonBody
86+
* @throws JsonException
87+
*/
88+
public function testValidateJson(array $data, bool $success): void
89+
{
90+
$request = new Request([], [], [], [], [], ['HTTP_CONTENT_TYPE' => 'application/json'], json_encode($data, JSON_THROW_ON_ERROR));
91+
$constraint = new RequestConstraint(['request' => new Assert\Collection(['email' => new Assert\Required(new Assert\Email())])]);
92+
$this->context->setConstraint($constraint);
93+
$this->validator->validate($request, $constraint);
94+
static::assertCount($success ? 0 : 1, $this->context->getViolations());
95+
}
96+
97+
/**
98+
* @covers ::validate
99+
* @covers ::validateRequest
100+
* @covers ::validateAndGetJsonBody
101+
*/
102+
public function testValidateInvalidJson(): void
103+
{
104+
$request = new Request([], [], [], [], [], ['HTTP_CONTENT_TYPE' => 'application/json'], '{invalid');
105+
$constraint = new RequestConstraint(['request' => new Assert\Collection(['email' => new Assert\Required(new Assert\Email())])]);
106+
$this->context->setConstraint($constraint);
107+
$this->validator->validate($request, $constraint);
108+
109+
$violations = $this->context->getViolations();
110+
static::assertCount(1, $violations);
111+
static::assertSame('Request::content cant be decoded', $violations->get(0)->getMessageTemplate());
112+
}
113+
114+
/**
115+
* @param array<mixed> $data
116+
*
78117
* @dataProvider \DigitalRevolution\SymfonyRequestValidation\Tests\DataProvider\Constraint\RequestConstraintValidatorDataProvider::dataProvider
79118
* @covers ::validate
80119
* @covers ::validateAttributes
@@ -90,6 +129,7 @@ public function testValidateAttributes(array $data, bool $success): void
90129

91130
/**
92131
* @param array<mixed> $data
132+
*
93133
* @dataProvider \DigitalRevolution\SymfonyRequestValidation\Tests\DataProvider\Constraint\RequestConstraintValidatorDataProvider::dataProvider
94134
* @covers ::validate
95135
* @covers ::validateQuery
@@ -99,19 +139,20 @@ public function testValidateAttributes(array $data, bool $success): void
99139
public function testValidateQueryRequestAttributes(array $data, bool $success): void
100140
{
101141
$request = new Request($data, $data, $data);
102-
$constraint = new RequestConstraint([
103-
'query' => new Assert\Collection(['email' => new Assert\Required(new Assert\Email())]),
104-
'request' => new Assert\Collection(['email' => new Assert\Required(new Assert\Email())]),
105-
'attributes' => new Assert\Collection(['email' => new Assert\Required(new Assert\Email())])
106-
]);
142+
$constraint = new RequestConstraint(
143+
[
144+
'query' => new Assert\Collection(['email' => new Assert\Required(new Assert\Email())]),
145+
'request' => new Assert\Collection(['email' => new Assert\Required(new Assert\Email())]),
146+
'attributes' => new Assert\Collection(['email' => new Assert\Required(new Assert\Email())])
147+
]
148+
);
107149
$this->context->setConstraint($constraint);
108150
$this->validator->validate($request, $constraint);
109151
static::assertCount($success ? 0 : 3, $this->context->getViolations());
110152
}
111153

112154
/**
113155
* Test that 'null' request should be ignored
114-
*
115156
* @covers ::validate
116157
*/
117158
public function testValidateNullRequest(): void
@@ -125,7 +166,6 @@ public function testValidateNullRequest(): void
125166

126167
/**
127168
* Test that 'null' request should be ignored
128-
*
129169
* @covers ::validate
130170
*/
131171
public function testValidateWrongTypeViolation(): void
@@ -141,7 +181,6 @@ public function testValidateWrongTypeViolation(): void
141181

142182
/**
143183
* Test that if no constraints have been specified. the request's query _must_ be empty
144-
*
145184
* @covers ::validate
146185
* @covers ::validateQuery
147186
*/
@@ -158,14 +197,13 @@ public function testValidateEmptyConstraintsFilledQuery(): void
158197

159198
/**
160199
* Test that if no constraints have been specified. the request's query _must_ not be empty
161-
*
162200
* @covers ::validate
163201
* @covers ::validateQuery
164202
*/
165203
public function testValidateEmptyConstraintsFilledQueryAllowed(): void
166204
{
167-
$request = new Request(['a']);
168-
$constraint = new RequestConstraint();
205+
$request = new Request(['a']);
206+
$constraint = new RequestConstraint();
169207
$constraint->allowExtraFields = true;
170208
$this->context->setConstraint($constraint);
171209
$this->validator->validate($request, $constraint);
@@ -175,7 +213,6 @@ public function testValidateEmptyConstraintsFilledQueryAllowed(): void
175213

176214
/**
177215
* Test that if no constraints have been specified. the request's request _must_ be empty
178-
*
179216
* @covers ::validate
180217
* @covers ::validateRequest
181218
*/
@@ -192,14 +229,13 @@ public function testValidateEmptyConstraintsFilledRequest(): void
192229

193230
/**
194231
* Test that if no constraints have been specified, and extra fields are allowed. the request's request _must_ not be empty
195-
*
196232
* @covers ::validate
197233
* @covers ::validateRequest
198234
*/
199235
public function testValidateEmptyConstraintsFilledRequestAllowed(): void
200236
{
201-
$request = new Request([], ['b']);
202-
$constraint = new RequestConstraint();
237+
$request = new Request([], ['b']);
238+
$constraint = new RequestConstraint();
203239
$constraint->allowExtraFields = true;
204240

205241
$this->context->setConstraint($constraint);

0 commit comments

Comments
 (0)