From aafa14fce77612bdefc6c18cc1611afcb80426ac Mon Sep 17 00:00:00 2001 From: Shalvah Date: Mon, 25 Dec 2023 06:35:10 +0100 Subject: [PATCH] Support `except` and `only` universally for strategies --- src/Extracting/Extractor.php | 13 +++++ .../Strategies/Responses/ResponseCalls.php | 35 +++++++++++- src/Extracting/Strategies/Strategy.php | 30 +++++++++- .../ExtractorStrategiesInvocationTest.php | 55 +++++++++++++++++-- 4 files changed, 124 insertions(+), 9 deletions(-) diff --git a/src/Extracting/Extractor.php b/src/Extracting/Extractor.php index ad38009f..762a90f9 100644 --- a/src/Extracting/Extractor.php +++ b/src/Extracting/Extractor.php @@ -214,6 +214,19 @@ protected function iterateThroughStrategies( continue; } $settings = $strategyClassOrTuple[1]; + + $routesToSkip = $settings['except'] ?? []; + $routesToInclude = $settings['only'] ?? []; + + if (!empty($routesToSkip)) { + if (RoutePatternMatcher::matches($endpointData->route, $routesToSkip)) { + continue; + } + } elseif (!empty($routesToInclude)) { + if (!RoutePatternMatcher::matches($endpointData->route, $routesToInclude)) { + continue; + } + } } else { $strategyClass = $strategyClassOrTuple; $settings = $rulesToApply; diff --git a/src/Extracting/Strategies/Responses/ResponseCalls.php b/src/Extracting/Strategies/Responses/ResponseCalls.php index ade9367d..625f2427 100644 --- a/src/Extracting/Strategies/Responses/ResponseCalls.php +++ b/src/Extracting/Strategies/Responses/ResponseCalls.php @@ -37,7 +37,7 @@ public function __invoke(ExtractedEndpointData $endpointData, array $routeRules public function makeResponseCallIfConditionsPass(ExtractedEndpointData $endpointData, array $routeRules): ?array { - $rulesToApply = $routeRules['response_calls'] ?? []; + $rulesToApply = $routeRules['response_calls'] ?? $routeRules; if (!$this->shouldMakeApiCall($endpointData, $rulesToApply)) { return null; } @@ -132,8 +132,10 @@ private function configureEnvironment(array $rulesToApply) * * @return Request */ - protected function prepareRequest(Route $route, string $url, array $rulesToApply, array $urlParams, - array $bodyParams, array $queryParams, array $fileParameters, array $headers): Request + protected function prepareRequest( + Route $route, string $url, array $rulesToApply, array $urlParams, + array $bodyParams, array $queryParams, array $fileParameters, array $headers + ): Request { $uri = Utils::getUrlWithBoundParameters($url, $urlParams); $routeMethods = $this->getMethods($route); @@ -368,4 +370,31 @@ protected function getResponseHeaders($response): array return $formattedHeaders; } + + /** + * @param array $only The routes which this strategy should be applied to. Can not be specified with $except. + * Specify route names ("users.index", "users.*"), or method and path ("GET *", "POST /safe/*"). + * @param array $except The routes which this strategy should be applied to. Can not be specified with $only. + * Specify route names ("users.index", "users.*"), or method and path ("GET *", "POST /safe/*"). + * @param array $config Any extra Laravel config() values to before starting the response call. + * @param array $queryParams Query params to always send with the response call. Key-value array. + * @param array $bodyParams Body params to always send with the response call. Key-value array. + * @param array $fileParams File params to always send with the response call. Key-value array. Key is param name, value is file path. + * @param array $cookies Cookies to always send with the response call. Key-value array. + * @return array + */ + public static function withSettings( + array $only = ['GET *'], + array $except = [], + array $config = [], + array $queryParams = [], + array $bodyParams = [], + array $fileParams = [ + // 'key' => 'storage/app/image.png', + ], + array $cookies = [], + ): array + { + return static::wrapWithSettings(...get_defined_vars()); + } } diff --git a/src/Extracting/Strategies/Strategy.php b/src/Extracting/Strategies/Strategy.php index 062a9b83..614305a0 100644 --- a/src/Extracting/Strategies/Strategy.php +++ b/src/Extracting/Strategies/Strategy.php @@ -25,9 +25,35 @@ public function getConfig(): DocumentationConfig /** * @param ExtractedEndpointData $endpointData - * @param array $routeRules Array of rules for the ruleset which this route belongs to. + * @param array $settings Settings to be applied to this strategy while processing this route. + * In the past, this was "routeRules". * * @return array|null */ - abstract public function __invoke(ExtractedEndpointData $endpointData, array $routeRules = []): ?array; + abstract public function __invoke(ExtractedEndpointData $endpointData, array $settings = []): ?array; + + /** + * @param array $only The routes which this strategy should be applied to. Can not be specified with $except. + * Specify route names ("users.index", "users.*"), or method and path ("GET *", "POST /safe/*"). + * @param array $except The routes which this strategy should be applied to. Can not be specified with $only. + * Specify route names ("users.index", "users.*"), or method and path ("GET *", "POST /safe/*"). + * @return array{string,array} Tuple of strategy class FQN and specified settings. + */ + public static function wrapWithSettings( + array $only = ['*'], + array $except = [], + ...$settings + ): array + { + if (!empty($only) && !empty($except)) { + throw new \InvalidArgumentException( + "Both \$only and \$except cannot be specified in your ".static::class." settings" + ); + } + + return [ + static::class, + $settings, + ]; + } } diff --git a/tests/Unit/ExtractorStrategiesInvocationTest.php b/tests/Unit/ExtractorStrategiesInvocationTest.php index 67842e36..9b85bc71 100644 --- a/tests/Unit/ExtractorStrategiesInvocationTest.php +++ b/tests/Unit/ExtractorStrategiesInvocationTest.php @@ -44,7 +44,7 @@ public function only_specified_strategies_are_loaded() } /** @test */ - public function supports_overrides() + public function supports_overrides_tuples() { $config = [ 'strategies' => [ @@ -70,7 +70,7 @@ public function supports_overrides() /** @test */ - public function supports_strategy_tuples() + public function supports_strategy_settings_tuples() { $config = [ 'strategies' => [ @@ -93,6 +93,51 @@ public function supports_strategy_tuples() ], $endpointData->headers); } + /** @test */ + public function respects_strategy_s_only_setting() + { + $config = [ + 'strategies' => [ + 'bodyParameters' => [ + [EmptyStrategy1::class, ['only' => 'GET /test']] + ], + 'responses' => [], + ], + ]; + $this->processRoute($config); + $this->assertFalse(EmptyStrategy1::$called); + + $config['strategies']['bodyParameters'][0] = + [EmptyStrategy1::class, ['only' => ['GET api/*']]]; + $this->processRoute($config); + $this->assertTrue(EmptyStrategy1::$called); + } + + /** @test */ + public function respects_strategy_s_except_setting() + { + $config = [ + 'strategies' => [ + 'bodyParameters' => [ + [EmptyStrategy1::class, ['except' => 'GET /api*']] + ], + 'responses' => [], + ], + ]; + $this->processRoute($config); + $this->assertFalse(EmptyStrategy1::$called); + + $config['strategies']['bodyParameters'][0] = + [EmptyStrategy1::class, ['except' => ['*']]]; + $this->processRoute($config); + $this->assertFalse(EmptyStrategy1::$called); + + $config['strategies']['bodyParameters'][0] = + [EmptyStrategy1::class, ['except' => []]]; + $this->processRoute($config); + $this->assertTrue(EmptyStrategy1::$called); + } + /** @test */ public function responses_from_different_strategies_get_added() { @@ -216,9 +261,11 @@ public function overwrites_metadata_from_previous_strategies_in_same_stage() $this->assertArraySubset($expectedMetadata, $parsed->metadata->toArray()); } - protected function processRoute(array $config): ExtractedEndpointData + protected function processRoute( + array $config, $routeMethod = "GET", $routePath = "/api/test", $routeName = "dummy" + ): ExtractedEndpointData { - $route = $this->createRoute('GET', '/api/test', 'dummy'); + $route = $this->createRoute($routeMethod, $routePath, $routeName); $extractor = new Extractor(new DocumentationConfig($config)); return $extractor->processRoute($route); }