diff --git a/src/php/src/Api/Documentation/Extensions/SunsetExtension.php b/src/php/src/Api/Documentation/Extensions/SunsetExtension.php index ae73c15..d20709e 100644 --- a/src/php/src/Api/Documentation/Extensions/SunsetExtension.php +++ b/src/php/src/Api/Documentation/Extensions/SunsetExtension.php @@ -60,7 +60,9 @@ class SunsetExtension implements Extension */ public function extendOperation(array $operation, Route $route, string $method, array $config): array { - if (! $this->hasSunsetMiddleware($route)) { + $sunset = $this->sunsetMiddlewareArguments($route); + + if ($sunset === null) { return $operation; } @@ -76,14 +78,20 @@ class SunsetExtension implements Extension $response['headers']['Deprecation'] = [ '$ref' => '#/components/headers/deprecation', ]; - $response['headers']['Sunset'] = [ - '$ref' => '#/components/headers/sunset', - ]; + if ($sunset['sunsetDate'] !== null && $sunset['sunsetDate'] !== '') { + $response['headers']['Sunset'] = [ + '$ref' => '#/components/headers/sunset', + ]; + } $response['headers']['X-API-Warn'] = [ '$ref' => '#/components/headers/xapiwarn', ]; - if (! isset($response['headers']['Link'])) { + if ( + $sunset['replacement'] !== null + && $sunset['replacement'] !== '' + && ! isset($response['headers']['Link']) + ) { $response['headers']['Link'] = [ '$ref' => '#/components/headers/link', ]; @@ -95,16 +103,45 @@ class SunsetExtension implements Extension } /** - * Determine whether the route uses the sunset middleware. + * Extract the configured sunset middleware arguments from a route. + * + * Returns null when the route does not use the sunset middleware. + * + * @return array{sunsetDate:?string,replacement:?string}|null */ - protected function hasSunsetMiddleware(Route $route): bool + protected function sunsetMiddlewareArguments(Route $route): ?array { foreach ($route->middleware() as $middleware) { - if (str_starts_with($middleware, 'api.sunset') || str_contains($middleware, 'ApiSunset')) { - return true; + if (! str_starts_with($middleware, 'api.sunset') && ! str_contains($middleware, 'ApiSunset')) { + continue; } + + $arguments = null; + + if (str_contains($middleware, ':')) { + [, $arguments] = explode(':', $middleware, 2); + } + + if ($arguments === null || $arguments === '') { + return [ + 'sunsetDate' => null, + 'replacement' => null, + ]; + } + + $parts = explode(',', $arguments, 2); + $sunsetDate = trim($parts[0] ?? ''); + $replacement = isset($parts[1]) ? trim($parts[1]) : null; + if ($replacement === '') { + $replacement = null; + } + + return [ + 'sunsetDate' => $sunsetDate !== '' ? $sunsetDate : null, + 'replacement' => $replacement, + ]; } - return false; + return null; } } diff --git a/src/php/src/Api/Tests/Feature/OpenApiDocumentationComprehensiveTest.php b/src/php/src/Api/Tests/Feature/OpenApiDocumentationComprehensiveTest.php index 8c61808..1713550 100644 --- a/src/php/src/Api/Tests/Feature/OpenApiDocumentationComprehensiveTest.php +++ b/src/php/src/Api/Tests/Feature/OpenApiDocumentationComprehensiveTest.php @@ -825,6 +825,29 @@ describe('Sunset Documentation', function () { ->toHaveKey('X-API-Warn') ->toHaveKey('Link'); }); + + it('only documents the sunset headers that the middleware will emit', function () { + RouteFacade::prefix('api') + ->middleware(['api', 'api.sunset']) + ->group(function () { + RouteFacade::get('/sunset-test/plain', fn () => response()->json(['ok' => true])) + ->name('sunset-test.plain'); + }); + + config(['api-docs.routes.include' => ['api/*']]); + + $builder = new OpenApiBuilder; + $spec = $builder->build(); + + $operation = $spec['paths']['/api/sunset-test/plain']['get']; + $headers = $operation['responses']['200']['headers']; + + expect($operation['deprecated'])->toBeTrue(); + expect($headers)->toHaveKey('Deprecation') + ->toHaveKey('X-API-Warn'); + expect($headers)->not->toHaveKey('Sunset'); + expect($headers)->not->toHaveKey('Link'); + }); }); // ─────────────────────────────────────────────────────────────────────────────