diff --git a/features/hydra/error.feature b/features/hydra/error.feature index 689558730f5..9af57ddb9c7 100644 --- a/features/hydra/error.feature +++ b/features/hydra/error.feature @@ -42,13 +42,12 @@ Feature: Error handling """ Then the response status code should be 400 And the response should be in JSON - And the header "Content-Type" should be equal to "application/problem+json; charset=utf-8" + And the header "Content-Type" should be equal to "application/ld+json; charset=utf-8" And the JSON node "@context" should be equal to "/contexts/Error" And the JSON node "@type" should be equal to "hydra:Error" And the JSON node "hydra:title" should be equal to "An error occurred" And the JSON node "hydra:description" should be equal to 'Nested documents for attribute "relatedDummy" are not allowed. Use IRIs instead.' And the JSON node "trace" should exist - And the header "Link" should contain '; rel="http://www.w3.org/ns/json-ld#error"' Scenario: Get an error during deserialization of collection When I add "Content-Type" header equal to "application/ld+json" @@ -63,7 +62,7 @@ Feature: Error handling """ Then the response status code should be 400 And the response should be in JSON - And the header "Content-Type" should be equal to "application/problem+json; charset=utf-8" + And the header "Content-Type" should be equal to "application/ld+json; charset=utf-8" And the JSON node "@context" should be equal to "/contexts/Error" And the JSON node "@type" should be equal to "hydra:Error" And the JSON node "hydra:title" should be equal to "An error occurred" @@ -80,7 +79,7 @@ Feature: Error handling """ Then the response status code should be 400 And the response should be in JSON - And the header "Content-Type" should be equal to "application/problem+json; charset=utf-8" + And the header "Content-Type" should be equal to "application/ld+json; charset=utf-8" And the JSON node "@context" should be equal to "/contexts/Error" And the JSON node "@type" should be equal to "hydra:Error" And the JSON node "hydra:title" should be equal to "An error occurred" @@ -98,7 +97,7 @@ Feature: Error handling """ Then the response status code should be 400 And the response should be in JSON - And the header "Content-Type" should be equal to "application/problem+json; charset=utf-8" + And the header "Content-Type" should be equal to "application/ld+json; charset=utf-8" And the JSON node "@context" should be equal to "/contexts/Error" And the JSON node "@type" should be equal to "hydra:Error" And the JSON node "hydra:title" should be equal to "An error occurred" @@ -152,3 +151,39 @@ Feature: Error handling Then the response status code should be 201 And the response should be in JSON And the header "Content-Type" should be equal to "application/ld+json; charset=utf-8" + + Scenario: Get an rfc 7807 validation error + When I add "Content-Type" header equal to "application/ld+json" + And I send a "POST" request to "/validation_exception_problems" with body: + """ + {} + """ + Then the response status code should be 422 + And the response should be in JSON + And the header "Content-Type" should be equal to "application/problem+json; charset=utf-8" + And the header "Link" should contain '; rel="http://www.w3.org/ns/json-ld#error"' + And the JSON node "@context" should not exist + + Scenario: Get an rfc 7807 error + When I add "Content-Type" header equal to "application/ld+json" + And I send a "POST" request to "/exception_problems" with body: + """ + {} + """ + Then the response status code should be 400 + And the response should be in JSON + And the header "Content-Type" should be equal to "application/problem+json; charset=utf-8" + And the header "Link" should contain '; rel="http://www.w3.org/ns/json-ld#error"' + And the JSON node "@context" should not exist + + Scenario: Get an rfc 7807 error with backward compatibility + When I add "Content-Type" header equal to "application/ld+json" + And I send a "POST" request to "/exception_problems_with_compatibility" with body: + """ + {} + """ + Then the response status code should be 400 + And the response should be in JSON + And the header "Content-Type" should be equal to "application/ld+json; charset=utf-8" + And the header "Link" should not contain '; rel="http://www.w3.org/ns/json-ld#error"' + And the JSON node "@context" should exist diff --git a/features/main/not_exposed.feature b/features/main/not_exposed.feature index 6c4b206c1ac..a0ece2ee3ec 100644 --- a/features/main/not_exposed.feature +++ b/features/main/not_exposed.feature @@ -171,7 +171,7 @@ Feature: Expose only a collection of objects When I send a "GET" request to "" Then the response status code should be 404 And the response should be in JSON - And the header "Content-Type" should be equal to "application/problem+json; charset=utf-8" + And the header "Content-Type" should be equal to "application/ld+json; charset=utf-8" And the JSON node "hydra:description" should be equal to "" Examples: | uri | hydra:description | diff --git a/features/main/relation.feature b/features/main/relation.feature index fcc89f1b97c..d406f1d3788 100644 --- a/features/main/relation.feature +++ b/features/main/relation.feature @@ -484,15 +484,12 @@ Feature: Relations support Then the response status code should be 400 And the response should be in JSON And the header "Content-Type" should be equal to "application/problem+json; charset=utf-8" + And the header "Link" should contain '; rel="http://www.w3.org/ns/json-ld#error"' And the JSON should be valid according to this schema: """ { "type": "object", "properties": { - "@context": { - "type": "string", - "pattern": "^/contexts/Error$" - }, "@type": { "type": "string", "pattern": "^hydra:Error$" @@ -506,7 +503,6 @@ Feature: Relations support } }, "required": [ - "@context", "@type", "hydra:title", "hydra:description" diff --git a/features/mongodb/filters.feature b/features/mongodb/filters.feature index aad34147c69..9e89239215d 100644 --- a/features/mongodb/filters.feature +++ b/features/mongodb/filters.feature @@ -10,7 +10,7 @@ Feature: Filters on collections When I send a "GET" request to "/dummies?relatedDummy.thirdLevel.badFourthLevel.level=4" Then the response status code should be 500 And the response should be in JSON - And the header "Content-Type" should be equal to "application/problem+json; charset=utf-8" + And the header "Content-Type" should be equal to "application/ld+json; charset=utf-8" And the JSON node "@context" should be equal to "/contexts/Error" And the JSON node "@type" should be equal to "hydra:Error" And the JSON node "hydra:title" should be equal to "An error occurred" @@ -21,7 +21,7 @@ Feature: Filters on collections When I send a "GET" request to "/dummies?relatedDummy.thirdLevel.fourthLevel.badThirdLevel.level=3" Then the response status code should be 500 And the response should be in JSON - And the header "Content-Type" should be equal to "application/problem+json; charset=utf-8" + And the header "Content-Type" should be equal to "application/ld+json; charset=utf-8" And the JSON node "@context" should be equal to "/contexts/Error" And the JSON node "@type" should be equal to "hydra:Error" And the JSON node "hydra:title" should be equal to "An error occurred" diff --git a/features/security/send_security_headers.feature b/features/security/send_security_headers.feature index 41dc0bd4efb..7d38893bf01 100644 --- a/features/security/send_security_headers.feature +++ b/features/security/send_security_headers.feature @@ -17,7 +17,6 @@ Feature: Send security header {"name": 1} """ Then the response status code should be 400 - And the header "Content-Type" should be equal to "application/problem+json; charset=utf-8" And the header "X-Content-Type-Options" should be equal to "nosniff" And the header "X-Frame-Options" should be equal to "deny" diff --git a/features/security/strong_typing.feature b/features/security/strong_typing.feature index d69c670b3fe..3d4b7599a62 100644 --- a/features/security/strong_typing.feature +++ b/features/security/strong_typing.feature @@ -52,7 +52,7 @@ Feature: Handle properly invalid data submitted to the API """ Then the response status code should be 400 And the response should be in JSON - And the header "Content-Type" should be equal to "application/problem+json; charset=utf-8" + And the header "Content-Type" should be equal to "application/ld+json; charset=utf-8" And the JSON node "@context" should be equal to "/contexts/Error" And the JSON node "@type" should be equal to "hydra:Error" And the JSON node "hydra:title" should be equal to "An error occurred" @@ -69,7 +69,7 @@ Feature: Handle properly invalid data submitted to the API """ Then the response status code should be 400 And the response should be in JSON - And the header "Content-Type" should be equal to "application/problem+json; charset=utf-8" + And the header "Content-Type" should be equal to "application/ld+json; charset=utf-8" And the JSON node "@context" should be equal to "/contexts/Error" And the JSON node "@type" should be equal to "hydra:Error" And the JSON node "hydra:title" should be equal to "An error occurred" @@ -87,7 +87,7 @@ Feature: Handle properly invalid data submitted to the API """ Then the response status code should be 400 And the response should be in JSON - And the header "Content-Type" should be equal to "application/problem+json; charset=utf-8" + And the header "Content-Type" should be equal to "application/ld+json; charset=utf-8" Scenario: Send non-array data when an array is expected When I add "Content-Type" header equal to "application/ld+json" @@ -100,7 +100,7 @@ Feature: Handle properly invalid data submitted to the API """ Then the response status code should be 400 And the response should be in JSON - And the header "Content-Type" should be equal to "application/problem+json; charset=utf-8" + And the header "Content-Type" should be equal to "application/ld+json; charset=utf-8" And the JSON node "@context" should be equal to "/contexts/Error" And the JSON node "@type" should be equal to "hydra:Error" And the JSON node "hydra:title" should be equal to "An error occurred" @@ -118,7 +118,7 @@ Feature: Handle properly invalid data submitted to the API """ Then the response status code should be 400 And the response should be in JSON - And the header "Content-Type" should be equal to "application/problem+json; charset=utf-8" + And the header "Content-Type" should be equal to "application/ld+json; charset=utf-8" And the JSON node "@context" should be equal to "/contexts/Error" And the JSON node "@type" should be equal to "hydra:Error" And the JSON node "hydra:title" should be equal to "An error occurred" @@ -134,7 +134,7 @@ Feature: Handle properly invalid data submitted to the API """ Then the response status code should be 400 And the response should be in JSON - And the header "Content-Type" should be equal to "application/problem+json; charset=utf-8" + And the header "Content-Type" should be equal to "application/ld+json; charset=utf-8" And the JSON node "@context" should be equal to "/contexts/Error" And the JSON node "@type" should be equal to "hydra:Error" And the JSON node "hydra:title" should be equal to "An error occurred" diff --git a/features/security/validate_incoming_content-types.feature b/features/security/validate_incoming_content-types.feature index bc947705479..993352428cc 100644 --- a/features/security/validate_incoming_content-types.feature +++ b/features/security/validate_incoming_content-types.feature @@ -13,5 +13,5 @@ Feature: Validate incoming content type something """ Then the response status code should be 415 - And the header "Content-Type" should be equal to "application/problem+json; charset=utf-8" + And the header "Content-Type" should be equal to "application/ld+json; charset=utf-8" And the JSON node "hydra:description" should be equal to 'The content-type "text/plain" is not supported. Supported MIME types are "application/ld+json", "application/hal+json", "application/vnd.api+json", "application/xml", "text/xml", "application/json", "text/html", "application/graphql", "multipart/form-data".' diff --git a/features/serializer/vo_relations.feature b/features/serializer/vo_relations.feature index 30a50f7206b..c2cdcd5a7d1 100644 --- a/features/serializer/vo_relations.feature +++ b/features/serializer/vo_relations.feature @@ -140,15 +140,12 @@ Feature: Value object as ApiResource """ Then the response status code should be 400 And the header "Content-Type" should be equal to "application/problem+json; charset=utf-8" + And the header "Link" should contain '; rel="http://www.w3.org/ns/json-ld#error"' And the JSON should be valid according to this schema: """ { "type": "object", "properties": { - "@context": { - "type": "string", - "pattern": "^/contexts/Error$" - }, "@type": { "type": "string", "pattern": "^hydra:Error$" @@ -162,7 +159,6 @@ Feature: Value object as ApiResource } }, "required": [ - "@context", "@type", "hydra:title", "hydra:description" diff --git a/src/Hydra/EventListener/AddLinkHeaderListener.php b/src/Hydra/EventListener/AddLinkHeaderListener.php index e542211c8cc..f8d158d3352 100644 --- a/src/Hydra/EventListener/AddLinkHeaderListener.php +++ b/src/Hydra/EventListener/AddLinkHeaderListener.php @@ -51,7 +51,7 @@ public function onKernelResponse(ResponseEvent $event): void $apiDocUrl = $this->urlGenerator->generate('api_doc', ['_format' => 'jsonld'], UrlGeneratorInterface::ABS_URL); $apiDocLink = new Link(ContextBuilder::HYDRA_NS.'apiDocumentation', $apiDocUrl); - $linkProvider = $request->attributes->get('_links', new GenericLinkProvider()); + $linkProvider = $request->attributes->get('_api_platform_links', new GenericLinkProvider()); if (!$linkProvider instanceof EvolvableLinkProviderInterface) { return; @@ -63,6 +63,6 @@ public function onKernelResponse(ResponseEvent $event): void } } - $request->attributes->set('_links', $linkProvider->withLink($apiDocLink)); + $request->attributes->set('_api_platform_links', $linkProvider->withLink($apiDocLink)); } } diff --git a/src/Hydra/State/HydraLinkProcessor.php b/src/Hydra/State/HydraLinkProcessor.php index 9204a842c8b..0adff87a970 100644 --- a/src/Hydra/State/HydraLinkProcessor.php +++ b/src/Hydra/State/HydraLinkProcessor.php @@ -40,14 +40,14 @@ public function process(mixed $data, Operation $operation, array $uriVariables = } $apiDocUrl = $this->urlGenerator->generate('api_doc', ['_format' => 'jsonld'], UrlGeneratorInterface::ABS_URL); - $linkProvider = $request->attributes->get('_links') ?? new GenericLinkProvider(); + $linkProvider = $request->attributes->get('_api_platform_links') ?? new GenericLinkProvider(); foreach ($operation->getLinks() ?? [] as $link) { $linkProvider = $linkProvider->withLink($link); } $link = new Link(ContextBuilder::HYDRA_NS.'apiDocumentation', $apiDocUrl); - $request->attributes->set('_links', $linkProvider->withLink($link)); + $request->attributes->set('_api_platform_links', $linkProvider->withLink($link)); return $this->decorated->process($data, $operation, $uriVariables, $context); } diff --git a/src/JsonLd/Serializer/JsonLdContextTrait.php b/src/JsonLd/Serializer/JsonLdContextTrait.php index 2e0cef8a402..edae4b21d10 100644 --- a/src/JsonLd/Serializer/JsonLdContextTrait.php +++ b/src/JsonLd/Serializer/JsonLdContextTrait.php @@ -15,6 +15,7 @@ use ApiPlatform\JsonLd\AnonymousContextBuilderInterface; use ApiPlatform\JsonLd\ContextBuilderInterface; +use ApiPlatform\Metadata\Error; /** * Creates and manipulates the Serializer context. @@ -42,6 +43,10 @@ private function addJsonLdContext(ContextBuilderInterface $contextBuilder, strin return $data; } + if (($operation = $context['operation'] ?? null) && ($operation->getExtraProperties()['rfc_7807_compliant_errors'] ?? false) && $operation instanceof Error) { + return $data; + } + $data['@context'] = $contextBuilder->getResourceContextUri($resourceClass); return $data; diff --git a/src/State/Processor/AddLinkHeaderProcessor.php b/src/State/Processor/AddLinkHeaderProcessor.php index 59b6f859e9d..27b7330d8c8 100644 --- a/src/State/Processor/AddLinkHeaderProcessor.php +++ b/src/State/Processor/AddLinkHeaderProcessor.php @@ -36,11 +36,9 @@ public function process(mixed $data, Operation $operation, array $uriVariables = } // We add our header here as Symfony does it only for the main Request and we want it to be done on errors (sub-request) as well - $linksProvider = $request->attributes->get('_links'); + $linksProvider = $request->attributes->get('_api_platform_links'); if ($this->serializer && ($links = $linksProvider->getLinks())) { $response->headers->set('Link', $this->serializer->serialize($links)); - // We don't want Symfony WebLink component do add links twice - $request->attributes->set('_links', []); } return $response; diff --git a/src/Symfony/EventListener/AddLinkHeaderListener.php b/src/Symfony/EventListener/AddLinkHeaderListener.php index 6223a839530..2a41c2d53ba 100644 --- a/src/Symfony/EventListener/AddLinkHeaderListener.php +++ b/src/Symfony/EventListener/AddLinkHeaderListener.php @@ -17,8 +17,10 @@ use ApiPlatform\State\Util\CorsTrait; use ApiPlatform\State\Util\OperationRequestInitiatorTrait; use ApiPlatform\Symfony\Util\RequestAttributesExtractor; +use Psr\Link\LinkProviderInterface; use Symfony\Component\HttpKernel\Event\ResponseEvent; use Symfony\Component\Mercure\Discovery; +use Symfony\Component\WebLink\HttpHeaderSerializer; /** * Adds the HTTP Link header pointing to the Mercure hub for resources having their updates dispatched. @@ -30,8 +32,11 @@ final class AddLinkHeaderListener use CorsTrait; use OperationRequestInitiatorTrait; - public function __construct(private readonly Discovery $discovery, ResourceMetadataCollectionFactoryInterface $resourceMetadataCollectionFactory = null) - { + public function __construct( + private readonly Discovery $discovery, + ResourceMetadataCollectionFactoryInterface $resourceMetadataCollectionFactory = null, + private readonly HttpHeaderSerializer $serializer = new HttpHeaderSerializer() + ) { $this->resourceMetadataCollectionFactory = $resourceMetadataCollectionFactory; } @@ -48,6 +53,13 @@ public function onKernelResponse(ResponseEvent $event): void return; } + // Does the same as the web-link AddLinkHeaderListener as we want to use `_api_platform_links` not `_links`, + // note that the AddLinkHeaderProcessor is doing it with the MainController + $linkProvider = $event->getRequest()->attributes->get('_api_platform_links'); + if ($operation && $linkProvider instanceof LinkProviderInterface && $links = $linkProvider->getLinks()) { + $event->getResponse()->headers->set('Link', $this->serializer->serialize($links), false); + } + if ( null === $request->attributes->get('_api_resource_class') || !($attributes = RequestAttributesExtractor::extractAttributes($request)) diff --git a/src/Symfony/EventListener/ErrorListener.php b/src/Symfony/EventListener/ErrorListener.php index dad2a70f394..d689e6b861e 100644 --- a/src/Symfony/EventListener/ErrorListener.php +++ b/src/Symfony/EventListener/ErrorListener.php @@ -18,7 +18,6 @@ use ApiPlatform\Metadata\Error as ErrorOperation; use ApiPlatform\Metadata\Exception\ProblemExceptionInterface; use ApiPlatform\Metadata\HttpOperation; -use ApiPlatform\Metadata\Operation; use ApiPlatform\Metadata\Resource\Factory\ResourceMetadataCollectionFactoryInterface; use ApiPlatform\Metadata\ResourceClassResolverInterface; use ApiPlatform\Metadata\Util\ContentNegotiationTrait; @@ -107,7 +106,7 @@ protected function duplicateRequest(\Throwable $exception, Request $request): Re } } else { /** @var HttpOperation $operation */ - $operation = new ErrorOperation(name: '_api_errors_problem', class: Error::class, outputFormats: ['jsonld' => ['application/ld+json']], normalizationContext: ['groups' => ['jsonld'], 'skip_null_values' => true]); + $operation = new ErrorOperation(name: '_api_errors_problem', class: Error::class, outputFormats: ['jsonld' => ['application/problem+json']], normalizationContext: ['groups' => ['jsonld'], 'skip_null_values' => true]); $operation = $operation->withStatus($this->getStatusCode($apiOperation, $request, $operation, $exception)); $errorResource = Error::createFromException($exception, $operation->getStatus()); } @@ -128,13 +127,17 @@ protected function duplicateRequest(\Throwable $exception, Request $request): Re } catch (\Exception $e) { } - if ($exception instanceof ValidationException) { - if (!($apiOperation?->getExtraProperties()['rfc_7807_compliant_errors'] ?? false)) { - $operation = $operation->withNormalizationContext([ - 'groups' => ['legacy_'.$format], - 'force_iri_generation' => false, - ]); - } + if ($exception instanceof ValidationException && !($apiOperation?->getExtraProperties()['rfc_7807_compliant_errors'] ?? false)) { + $operation = $operation->withNormalizationContext([ + 'groups' => ['legacy_'.$format], + 'force_iri_generation' => false, + ]); + } + + if ('jsonld' === $format && !($apiOperation?->getExtraProperties()['rfc_7807_compliant_errors'] ?? false)) { + $operation = $operation->withOutputFormats(['jsonld' => ['application/ld+json']]) + ->withLinks([]) + ->withExtraProperties(['rfc_7807_compliant_errors' => false] + $operation->getExtraProperties()); } $dup->attributes->set('_api_resource_class', $operation->getClass()); diff --git a/src/Symfony/EventListener/SerializeListener.php b/src/Symfony/EventListener/SerializeListener.php index a258ad3260b..71c9510713e 100644 --- a/src/Symfony/EventListener/SerializeListener.php +++ b/src/Symfony/EventListener/SerializeListener.php @@ -142,11 +142,11 @@ public function onKernelView(ViewEvent $event): void return; } - $linkProvider = $request->attributes->get('_links', new GenericLinkProvider()); + $linkProvider = $request->attributes->get('_api_platform_links', new GenericLinkProvider()); foreach ($resourcesToPush as $resourceToPush) { $linkProvider = $linkProvider->withLink((new Link('preload', $resourceToPush))->withAttribute('as', 'fetch')); } - $request->attributes->set('_links', $linkProvider); + $request->attributes->set('_api_platform_links', $linkProvider); } /** diff --git a/src/Symfony/State/SerializeProcessor.php b/src/Symfony/State/SerializeProcessor.php index ce9b4e8a765..f47b6a619dd 100644 --- a/src/Symfony/State/SerializeProcessor.php +++ b/src/Symfony/State/SerializeProcessor.php @@ -65,11 +65,11 @@ public function process(mixed $data, Operation $operation, array $uriVariables = $serialized = $this->serializer->serialize($data, $request->getRequestFormat(), $serializerContext); $request->attributes->set('_resources', $request->attributes->get('_resources', []) + (array) $resources); if (\count($resourcesToPush)) { - $linkProvider = $request->attributes->get('_links', new GenericLinkProvider()); + $linkProvider = $request->attributes->get('_api_platform_links', new GenericLinkProvider()); foreach ($resourcesToPush as $resourceToPush) { $linkProvider = $linkProvider->withLink((new Link('preload', $resourceToPush))->withAttribute('as', 'fetch')); } - $request->attributes->set('_links', $linkProvider); + $request->attributes->set('_api_platform_links', $linkProvider); } return $this->processor->process($serialized, $operation, $uriVariables, $context); diff --git a/src/Symfony/Validator/Exception/ValidationException.php b/src/Symfony/Validator/Exception/ValidationException.php index 70800ab7677..e08f3d1a05f 100644 --- a/src/Symfony/Validator/Exception/ValidationException.php +++ b/src/Symfony/Validator/Exception/ValidationException.php @@ -13,6 +13,7 @@ namespace ApiPlatform\Symfony\Validator\Exception; +use ApiPlatform\JsonLd\ContextBuilderInterface; use ApiPlatform\Metadata\Error as ErrorOperation; use ApiPlatform\Metadata\ErrorResource; use ApiPlatform\Metadata\Exception\ProblemExceptionInterface; @@ -21,6 +22,7 @@ use Symfony\Component\Serializer\Annotation\Groups; use Symfony\Component\Serializer\Annotation\SerializedName; use Symfony\Component\Validator\ConstraintViolationListInterface; +use Symfony\Component\WebLink\Link; /** * Thrown when a validation error occurs. @@ -34,8 +36,16 @@ uriVariables: ['id'], shortName: 'ConstraintViolationList', operations: [ - new ErrorOperation(name: '_api_validation_errors_hydra', outputFormats: ['jsonld' => ['application/ld+json']], normalizationContext: ['groups' => ['jsonld'], 'skip_null_values' => true]), - new ErrorOperation(name: '_api_validation_errors_problem', outputFormats: ['jsonproblem' => ['application/problem+json'], 'json' => ['application/problem+json']], normalizationContext: ['groups' => ['json'], 'skip_null_values' => true]), + new ErrorOperation(name: '_api_validation_errors_problem', outputFormats: ['json' => ['application/problem+json']], normalizationContext: ['groups' => ['json'], 'skip_null_values' => true]), + new ErrorOperation( + name: '_api_validation_errors_hydra', + outputFormats: ['jsonld' => ['application/problem+json']], + links: [new Link(rel: ContextBuilderInterface::JSONLD_NS.'error', href: 'http://www.w3.org/ns/hydra/error')], + normalizationContext: [ + 'groups' => ['jsonld'], + 'skip_null_values' => true, + ] + ), new ErrorOperation(name: '_api_validation_errors_jsonapi', outputFormats: ['jsonapi' => ['application/vnd.api+json']], normalizationContext: ['groups' => ['jsonapi'], 'skip_null_values' => true]), ] )] diff --git a/tests/Fixtures/TestBundle/ApiResource/ValidationExceptionProblem.php b/tests/Fixtures/TestBundle/ApiResource/ValidationExceptionProblem.php new file mode 100644 index 00000000000..6684b2d130c --- /dev/null +++ b/tests/Fixtures/TestBundle/ApiResource/ValidationExceptionProblem.php @@ -0,0 +1,35 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace ApiPlatform\Tests\Fixtures\TestBundle\ApiResource; + +use ApiPlatform\Metadata\Post; +use ApiPlatform\Symfony\Validator\Exception\ValidationException; +use Symfony\Component\HttpKernel\Exception\BadRequestHttpException; +use Symfony\Component\Validator\ConstraintViolationList; + +#[Post(processor: [ValidationExceptionProblem::class, 'provide'])] +#[Post(uriTemplate: '/exception_problems', processor: [ValidationExceptionProblem::class, 'provideException'])] +#[Post(uriTemplate: '/exception_problems_with_compatibility', processor: [ValidationExceptionProblem::class, 'provideException'], extraProperties: ['rfc_7807_compliant_errors' => false])] +class ValidationExceptionProblem +{ + public static function provide(): void + { + throw new ValidationException(new ConstraintViolationList()); + } + + public static function provideException(): void + { + throw new BadRequestHttpException(); + } +} diff --git a/tests/Hydra/EventListener/AddLinkHeaderListenerTest.php b/tests/Hydra/EventListener/AddLinkHeaderListenerTest.php index 2d69c112492..c7b5ac4ee84 100644 --- a/tests/Hydra/EventListener/AddLinkHeaderListenerTest.php +++ b/tests/Hydra/EventListener/AddLinkHeaderListenerTest.php @@ -21,9 +21,7 @@ use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpKernel\Event\ResponseEvent; use Symfony\Component\HttpKernel\HttpKernelInterface; -use Symfony\Component\WebLink\GenericLinkProvider; use Symfony\Component\WebLink\HttpHeaderSerializer; -use Symfony\Component\WebLink\Link; /** * @author Kévin Dunglas @@ -49,13 +47,12 @@ public function testAddLinkHeader(string $expected, Request $request): void $listener = new AddLinkHeaderListener($urlGenerator->reveal()); $listener->onKernelResponse($event); - $this->assertSame($expected, (new HttpHeaderSerializer())->serialize($request->attributes->get('_links')->getLinks())); + $this->assertSame($expected, (new HttpHeaderSerializer())->serialize($request->attributes->get('_api_platform_links')->getLinks())); } public static function provider(): \Iterator { yield ['; rel="http://www.w3.org/ns/hydra/core#apiDocumentation"', new Request()]; - yield ['; rel="mercure",; rel="http://www.w3.org/ns/hydra/core#apiDocumentation"', new Request([], [], ['_links' => new GenericLinkProvider([new Link('mercure', 'https://demo.mercure.rocks')])])]; } public function testSkipWhenPreflightRequest(): void @@ -75,6 +72,6 @@ public function testSkipWhenPreflightRequest(): void $listener = new AddLinkHeaderListener($urlGenerator->reveal()); $listener->onKernelResponse($event); - $this->assertFalse($request->attributes->has('_links')); + $this->assertFalse($request->attributes->has('_api_platform_links')); } } diff --git a/tests/Hydra/State/HydraLinkProcessorTest.php b/tests/Hydra/State/HydraLinkProcessorTest.php index 658cd93d853..299d1560e0e 100644 --- a/tests/Hydra/State/HydraLinkProcessorTest.php +++ b/tests/Hydra/State/HydraLinkProcessorTest.php @@ -33,7 +33,7 @@ public function testProcess(): void $request = $this->createMock(Request::class); $request->attributes = $this->createMock(ParameterBag::class); - $request->attributes->expects($this->once())->method('set')->with('_links', $this->callback(function ($linkProvider) { + $request->attributes->expects($this->once())->method('set')->with('_api_platform_links', $this->callback(function ($linkProvider) { $this->assertInstanceOf(GenericLinkProvider::class, $linkProvider); $this->assertEquals($linkProvider->getLinks(), [new Link('a', 'b'), new Link(ContextBuilder::HYDRA_NS.'apiDocumentation', '/docs')]);