|
21 | 21 | use Symfony\Component\HttpKernel\Event\ControllerArgumentsEvent;
|
22 | 22 | use Symfony\Component\HttpKernel\Exception\HttpException;
|
23 | 23 | use Symfony\Component\HttpKernel\HttpKernelInterface;
|
24 |
| -use Symfony\Component\PropertyAccess\Exception\InvalidTypeException; |
25 | 24 | use Symfony\Component\Serializer\Encoder\JsonEncoder;
|
26 | 25 | use Symfony\Component\Serializer\Encoder\XmlEncoder;
|
27 | 26 | use Symfony\Component\Serializer\Exception\PartialDenormalizationException;
|
@@ -285,6 +284,70 @@ public function testValidationNotPerformedWhenPartialDenormalizationReturnsViola
|
285 | 284 | }
|
286 | 285 | }
|
287 | 286 |
|
| 287 | + public function testNestedPayloadErrorReportingWhenPartialDenormalizationReturnsViolation() |
| 288 | + { |
| 289 | + $content = '{ |
| 290 | + "name": "john doe", |
| 291 | + "address": { |
| 292 | + "address": "2332 street", |
| 293 | + "zipcode": "20220", |
| 294 | + "city": "Paris", |
| 295 | + "country": "75000", |
| 296 | + "geolocalization": { |
| 297 | + "lng": 32.423 |
| 298 | + } |
| 299 | + } |
| 300 | + }'; |
| 301 | + $serializer = new Serializer([new ObjectNormalizer()], ['json' => new JsonEncoder()]); |
| 302 | + |
| 303 | + $validator = $this->createMock(ValidatorInterface::class); |
| 304 | + $validator->expects($this->never()) |
| 305 | + ->method('validate'); |
| 306 | + |
| 307 | + $resolver = new RequestPayloadValueResolver($serializer, $validator); |
| 308 | + $request = Request::create('/', 'POST', server: ['CONTENT_TYPE' => 'application/json'], content: $content); |
| 309 | + $kernel = $this->createMock(HttpKernelInterface::class); |
| 310 | + |
| 311 | + |
| 312 | + // Test using use_class_as_default_expected_type = false context |
| 313 | + $argument = new ArgumentMetadata('invalid-nested-payload', Employee::class, false, false, null, false, [ |
| 314 | + MapRequestPayload::class => new MapRequestPayload(serializationContext: ['use_class_as_default_expected_type' => false]), |
| 315 | + ]); |
| 316 | + $arguments = $resolver->resolve($request, $argument); |
| 317 | + $event = new ControllerArgumentsEvent($kernel, function () {}, $arguments, $request, HttpKernelInterface::MAIN_REQUEST); |
| 318 | + |
| 319 | + try { |
| 320 | + $resolver->onKernelControllerArguments($event); |
| 321 | + $this->fail(sprintf('Expected "%s" to be thrown.', HttpException::class)); |
| 322 | + } catch (HttpException $e) { |
| 323 | + $validationFailedException = $e->getPrevious(); |
| 324 | + $this->assertInstanceOf(ValidationFailedException::class, $validationFailedException); |
| 325 | + $this->assertSame( |
| 326 | + sprintf('This value should be of type %s.', 'unknown'), |
| 327 | + $validationFailedException->getViolations()[0]->getMessage() |
| 328 | + ); |
| 329 | + } |
| 330 | + |
| 331 | + // Test using use_class_as_default_expected_type context |
| 332 | + $argument = new ArgumentMetadata('invalid-nested-payload', Employee::class, false, false, null, false, [ |
| 333 | + MapRequestPayload::class => new MapRequestPayload(serializationContext: ['use_class_as_default_expected_type' => true]), |
| 334 | + ]); |
| 335 | + $arguments = $resolver->resolve($request, $argument); |
| 336 | + $event = new ControllerArgumentsEvent($kernel, function () {}, $arguments, $request, HttpKernelInterface::MAIN_REQUEST); |
| 337 | + |
| 338 | + try { |
| 339 | + $resolver->onKernelControllerArguments($event); |
| 340 | + $this->fail(sprintf('Expected "%s" to be thrown.', HttpException::class)); |
| 341 | + } catch (HttpException $e) { |
| 342 | + $validationFailedException = $e->getPrevious(); |
| 343 | + $this->assertInstanceOf(ValidationFailedException::class, $validationFailedException); |
| 344 | + $this->assertSame( |
| 345 | + sprintf('This value should be of type %s.', Geolocalization::class), |
| 346 | + $validationFailedException->getViolations()[0]->getMessage() |
| 347 | + ); |
| 348 | + } |
| 349 | + } |
| 350 | + |
288 | 351 | public function testUnsupportedMedia()
|
289 | 352 | {
|
290 | 353 | $serializer = new Serializer();
|
@@ -731,3 +794,34 @@ public function getPassword(): string
|
731 | 794 | return $this->password;
|
732 | 795 | }
|
733 | 796 | }
|
| 797 | + |
| 798 | +class Employee |
| 799 | +{ |
| 800 | + public function __construct( |
| 801 | + public string $name, |
| 802 | + #[Assert\Valid] |
| 803 | + public ?Address $address = null, |
| 804 | + ) { |
| 805 | + } |
| 806 | +} |
| 807 | + |
| 808 | +class Address |
| 809 | +{ |
| 810 | + public function __construct( |
| 811 | + public string $address, |
| 812 | + public string $zipcode, |
| 813 | + public string $city, |
| 814 | + public string $country, |
| 815 | + public Geolocalization $geolocalization, |
| 816 | + ) { |
| 817 | + } |
| 818 | +} |
| 819 | + |
| 820 | +class Geolocalization |
| 821 | +{ |
| 822 | + public function __construct( |
| 823 | + public string $lat, |
| 824 | + public string $lng, |
| 825 | + ) { |
| 826 | + } |
| 827 | +} |
0 commit comments