diff --git a/CHANGELOG.md b/CHANGELOG.md index 2f739b4f4..43452e922 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,7 @@ The major version bump is due to upping the required PHP version from `8.1` to ` * It is now possible to customize how JSON request bodies are decoded using the following methods: - `Body::setJsonMaxDepth()` to set the maximum JSON nesting level. - `Body::setJsonFlags()` to set the JSON decoding options. +* Added `Deprecated` middleware that allows you to easily set the `Deprecation` and `Sunset` HTTP headers. #### Improvements diff --git a/src/mako/http/routing/middleware/Deprecated.php b/src/mako/http/routing/middleware/Deprecated.php new file mode 100644 index 000000000..6f10be039 --- /dev/null +++ b/src/mako/http/routing/middleware/Deprecated.php @@ -0,0 +1,65 @@ +deprecationDate = $deprecationDate instanceof DateTimeInterface ? $deprecationDate : new DateTime($deprecationDate); + } + + if ($sunsetDate !== null) { + $this->sunsetDate = $sunsetDate instanceof DateTimeInterface ? $sunsetDate : new DateTime($sunsetDate); + } + + if ($deprecationDate !== null && $sunsetDate !== null && $this->deprecationDate->getTimestamp() >= $this->sunsetDate->getTimestamp()) { + throw new RuntimeException('The deprecation date must be earlier than the sunset date.'); + } + } + /** + * {@inheritDoc} + */ + public function execute(Request $request, Response $response, Closure $next): Response + { + if ($this->deprecationDate !== null) { + $response->headers->add('Deprecation', "@{$this->deprecationDate->getTimestamp()}"); + } + + if ($this->sunsetDate !== null) { + $response->headers->add('Sunset', $this->sunsetDate->format(DateTime::RFC7231)); + } + + return $next($request, $response); + } +} diff --git a/tests/unit/http/routing/middleware/DeprecatedTest.php b/tests/unit/http/routing/middleware/DeprecatedTest.php new file mode 100644 index 000000000..3f66cb465 --- /dev/null +++ b/tests/unit/http/routing/middleware/DeprecatedTest.php @@ -0,0 +1,119 @@ +expectExceptionMessage('You must specify either a deprecation date or a sunset date or both.'); + + new Deprecated; + } + + /** + * + */ + public function testConstructorWithSunsetBeforeDeprecation(): void + { + $this->expectExceptionMessage('The deprecation date must be earlier than the sunset date.'); + + new Deprecated('2021-02-01', '2020-01-01'); + } + + /** + * + */ + public function testDeprecatedWithDeprecationDate(): void + { + $middleware = new Deprecated('2021-02-01'); + + /** @var \mako\http\Request|\Mockery\MockInterface $request */ + $request = Mockery::mock(Request::class); + + /** @var \mako\http\Response|\Mockery\MockInterface $response */ + $response = Mockery::mock(Response::class); + + /** @var \mako\http\response\Headers|\Mockery\MockInterface $headers */ + $headers = Mockery::mock(Headers::class); + + (function () use ($headers): void { + $this->headers = $headers; + })->bindTo($response, Response::class)(); + + $headers->shouldReceive('add')->once()->with('Deprecation', '@1612137600'); + + $middleware->execute($request, $response, fn ($request, $response) => $response); + + } + + /** + * + */ + public function testDeprecatedWithSunsetDate(): void + { + $middleware = new Deprecated(null, '2021-02-01'); + + /** @var \mako\http\Request|\Mockery\MockInterface $request */ + $request = Mockery::mock(Request::class); + + /** @var \mako\http\Response|\Mockery\MockInterface $response */ + $response = Mockery::mock(Response::class); + + /** @var \mako\http\response\Headers|\Mockery\MockInterface $headers */ + $headers = Mockery::mock(Headers::class); + + (function () use ($headers): void { + $this->headers = $headers; + })->bindTo($response, Response::class)(); + + $headers->shouldReceive('add')->once()->with('Sunset', 'Mon, 01 Feb 2021 00:00:00 GMT'); + + $middleware->execute($request, $response, fn ($request, $response) => $response); + } + + /** + * + */ + public function testDeprecatedWithDeprecationAndSunsetDate(): void + { + $middleware = new Deprecated('2020-01-01', '2021-02-01'); + + /** @var \mako\http\Request|\Mockery\MockInterface $request */ + $request = Mockery::mock(Request::class); + + /** @var \mako\http\Response|\Mockery\MockInterface $response */ + $response = Mockery::mock(Response::class); + + /** @var \mako\http\response\Headers|\Mockery\MockInterface $headers */ + $headers = Mockery::mock(Headers::class); + + (function () use ($headers): void { + $this->headers = $headers; + })->bindTo($response, Response::class)(); + + $headers->shouldReceive('add')->once()->with('Deprecation', '@1577836800'); + + $headers->shouldReceive('add')->once()->with('Sunset', 'Mon, 01 Feb 2021 00:00:00 GMT'); + + $middleware->execute($request, $response, fn ($request, $response) => $response); + } +}