diff --git a/module/Rest/src/Middleware/BackwardsCompatibleProblemDetailsMiddleware.php b/module/Rest/src/Middleware/BackwardsCompatibleProblemDetailsMiddleware.php index 9857465b..0812c7e0 100644 --- a/module/Rest/src/Middleware/BackwardsCompatibleProblemDetailsMiddleware.php +++ b/module/Rest/src/Middleware/BackwardsCompatibleProblemDetailsMiddleware.php @@ -36,7 +36,6 @@ class BackwardsCompatibleProblemDetailsMiddleware implements MiddlewareInterface public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface { $resp = $handler->handle($request); - if ($resp->getHeaderLine('Content-type') !== 'application/problem+json') { return $resp; } @@ -70,9 +69,7 @@ class BackwardsCompatibleProblemDetailsMiddleware implements MiddlewareInterface /** @deprecated When Shlink 2 is released, do not chekc the version */ private function isVersionOne(ServerRequestInterface $request): bool { - $uri = $request->getUri(); - $path = $uri->getPath(); - + $path = $request->getUri()->getPath(); return strpos($path, '/v') === false || strpos($path, '/v1') === 0; } diff --git a/module/Rest/test/Middleware/BackwardsCompatibleProblemDetailsMiddlewareTest.php b/module/Rest/test/Middleware/BackwardsCompatibleProblemDetailsMiddlewareTest.php new file mode 100644 index 00000000..4d47c4cb --- /dev/null +++ b/module/Rest/test/Middleware/BackwardsCompatibleProblemDetailsMiddlewareTest.php @@ -0,0 +1,122 @@ +handler = $this->prophesize(RequestHandlerInterface::class); + $this->middleware = new BackwardsCompatibleProblemDetailsMiddleware([ + 404 => 'NOT_FOUND', + 500 => 'INTERNAL_SERVER_ERROR', + ], 0); + } + + /** + * @test + * @dataProvider provideNonProcessableResponses + */ + public function nonProblemDetailsOrInvalidResponsesAreReturnedAsTheyAre(Response $response): void + { + $request = ServerRequestFactory::fromGlobals(); + $response = new Response(); + $handle = $this->handler->handle($request)->willReturn($response); + + $result = $this->middleware->process($request, $this->handler->reveal()); + + $this->assertSame($response, $result); + $handle->shouldHaveBeenCalledOnce(); + } + + public function provideNonProcessableResponses(): iterable + { + yield 'no problem details' => [new Response()]; + yield 'invalid JSON' => [(new Response('data://text/plain,{invalid-json'))->withHeader( + 'Content-Type', + 'application/problem+json' + )]; + } + + /** + * @test + * @dataProvider provideStatusAndTypes + */ + public function properlyMapsTypesBasedOnResponseStatus(Response\JsonResponse $response, string $expectedType): void + { + $request = ServerRequestFactory::fromGlobals()->withUri(new Uri('/v2/something')); + $handle = $this->handler->handle($request)->willReturn($response); + + /** @var Response\JsonResponse $result */ + $result = $this->middleware->process($request, $this->handler->reveal()); + $payload = $result->getPayload(); + + $this->assertEquals($expectedType, $payload['type']); + $this->assertArrayNotHasKey('error', $payload); + $this->assertArrayNotHasKey('message', $payload); + $handle->shouldHaveBeenCalledOnce(); + } + + public function provideStatusAndTypes(): iterable + { + yield [$this->jsonResponse(['type' => 'https://httpstatus.es/404'], 404), 'NOT_FOUND']; + yield [$this->jsonResponse(['type' => 'https://httpstatus.es/500'], 500), 'INTERNAL_SERVER_ERROR']; + yield [$this->jsonResponse(['type' => 'https://httpstatus.es/504'], 504), 'https://httpstatus.es/504']; + yield [$this->jsonResponse(['type' => 'something_else'], 404), 'something_else']; + yield [$this->jsonResponse(['type' => 'something_else'], 500), 'something_else']; + yield [$this->jsonResponse(['type' => 'something_else'], 504), 'something_else']; + } + + /** + * @test + * @dataProvider provideRequestPath + */ + public function mapsDeprecatedPropertiesWhenRequestIsPerformedToVersionOne(string $requestPath): void + { + $request = ServerRequestFactory::fromGlobals()->withUri(new Uri($requestPath)); + $response = $this->jsonResponse([ + 'type' => 'the_type', + 'detail' => 'the_detail', + ]); + $handle = $this->handler->handle($request)->willReturn($response); + + /** @var Response\JsonResponse $result */ + $result = $this->middleware->process($request, $this->handler->reveal()); + $payload = $result->getPayload(); + + $this->assertEquals([ + 'type' => 'the_type', + 'detail' => 'the_detail', + 'error' => 'the_type', + 'message' => 'the_detail', + ], $payload); + $handle->shouldHaveBeenCalledOnce(); + } + + public function provideRequestPath(): iterable + { + yield 'no version' => ['/foo']; + yield 'version one' => ['/v1/foo']; + } + + private function jsonResponse(array $payload, int $status = 200): Response\JsonResponse + { + $headers = ['Content-Type' => 'application/problem+json']; + return new Response\JsonResponse($payload, $status, $headers); + } +}