Переглянути джерело

Support merging of parameter data from successive stages

shalvah 2 роки тому
батько
коміт
d29718c9a2

+ 1 - 0
CHANGELOG.md

@@ -20,6 +20,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
 - Nested response fields are now collapsed ([00b09bb](https://github.com/knuckleswtf/scribe/commit/00b09bbea8ec64006db864bf807004d48926c6d3))
 - Inlined routes (no more Scribe/Controller class)
 - Changed signature of Strategy#invoke ($routeRules is now optional)
+- Parameter data from successive stages is now merged
 
 ## 3.35.0 (27 July 2022)
 ### Modified

+ 8 - 2
camel/BaseDTO.php

@@ -14,13 +14,19 @@ class BaseDTO extends DataTransferObject implements Arrayable
      */
     public array $custom = [];
 
-    public static function create(BaseDTO|array $data): static
+    public static function create(BaseDTO|array $data, BaseDTO|array $inheritFrom = []): static
     {
         if ($data instanceof static) {
             return $data;
         }
 
-        return new static($data);
+        $mergedData = $inheritFrom instanceof static ? $inheritFrom->toArray() : $inheritFrom;
+
+        foreach ($data as $property => $value) {
+            $mergedData[$property] = $value;
+        }
+
+        return new static($mergedData);
     }
 
     protected function parseArray(array $array): array

+ 4 - 4
src/Extracting/Extractor.php

@@ -115,7 +115,7 @@ class Extractor
                 if (empty($item['name'])) {
                     $item['name'] = $key;
                 }
-                $endpointData->urlParameters[$key] = Parameter::create($item);
+                $endpointData->urlParameters[$key] = Parameter::create($item, $endpointData->urlParameters[$key] ?? []);
             }
         });
     }
@@ -127,7 +127,7 @@ class Extractor
                 if (empty($item['name'])) {
                     $item['name'] = $key;
                 }
-                $endpointData->queryParameters[$key] = Parameter::create($item);
+                $endpointData->queryParameters[$key] = Parameter::create($item, $endpointData->queryParameters[$key] ?? []);
             }
         });
     }
@@ -139,7 +139,7 @@ class Extractor
                 if (empty($item['name'])) {
                     $item['name'] = $key;
                 }
-                $endpointData->bodyParameters[$key] = Parameter::create($item);
+                $endpointData->bodyParameters[$key] = Parameter::create($item, $endpointData->bodyParameters[$key] ?? []);
             }
         });
     }
@@ -158,7 +158,7 @@ class Extractor
     {
         $this->iterateThroughStrategies('responseFields', $endpointData, $rulesToApply, function ($results) use ($endpointData) {
             foreach ($results as $key => $item) {
-                $endpointData->responseFields[$key] = ResponseField::create($item);
+                $endpointData->responseFields[$key] = Parameter::create($item, $endpointData->responseFields[$key] ?? []);
             }
         });
     }

+ 2 - 0
src/Extracting/Strategies/UrlParameters/GetFromLaravelAPI.php

@@ -187,6 +187,8 @@ class GetFromLaravelAPI extends Strategy
     protected function getNameOfUrlThing(string $url, string $paramName, string $alternateParamName = null): ?string
     {
         $parts = explode("/", $url);
+        if (count($parts) === 1) return null; // URL was "/{thing}"
+
         $paramIndex = array_search("{{$paramName}}", $parts);
 
         if ($paramIndex === false) {

+ 16 - 15
tests/Unit/ExtractorTest.php

@@ -5,6 +5,7 @@ namespace Knuckles\Scribe\Tests\Unit;
 use DMS\PHPUnitExtensions\ArraySubset\ArraySubsetAsserts;
 use Illuminate\Routing\Route;
 use Knuckles\Camel\Extraction\Parameter;
+use Knuckles\Scribe\Attributes\UrlParam;
 use Knuckles\Scribe\Extracting\Extractor;
 use Knuckles\Scribe\Tests\Fixtures\TestController;
 use Knuckles\Scribe\Tools\DocumentationConfig;
@@ -133,7 +134,7 @@ class ExtractorTest extends TestCase
     /** @test */
     public function does_not_generate_values_for_excluded_params_and_excludes_them_from_clean_params()
     {
-        $route = $this->createRoute('POST', '/api/test', 'withExcludedExamples');
+        $route = $this->createRouteOldSyntax('POST', '/api/test', 'withExcludedExamples');
         $parsed = $this->extractor->processRoute($route)->toArray();
         $cleanBodyParameters = $parsed['cleanBodyParameters'];
         $cleanQueryParameters = $parsed['cleanQueryParameters'];
@@ -166,19 +167,19 @@ class ExtractorTest extends TestCase
     /** @test */
     public function can_parse_route_methods()
     {
-        $route = $this->createRoute('GET', '/get', 'withEndpointDescription');
+        $route = $this->createRouteOldSyntax('GET', '/get', 'withEndpointDescription');
         $parsed = $this->extractor->processRoute($route);
         $this->assertEquals(['GET'], $parsed->httpMethods);
 
-        $route = $this->createRoute('POST', '/post', 'withEndpointDescription');
+        $route = $this->createRouteOldSyntax('POST', '/post', 'withEndpointDescription');
         $parsed = $this->extractor->processRoute($route);
         $this->assertEquals(['POST'], $parsed->httpMethods);
 
-        $route = $this->createRoute('PUT', '/put', 'withEndpointDescription');
+        $route = $this->createRouteOldSyntax('PUT', '/put', 'withEndpointDescription');
         $parsed = $this->extractor->processRoute($route);
         $this->assertEquals(['PUT'], $parsed->httpMethods);
 
-        $route = $this->createRoute('DELETE', '/delete', 'withEndpointDescription');
+        $route = $this->createRouteOldSyntax('DELETE', '/delete', 'withEndpointDescription');
         $parsed = $this->extractor->processRoute($route);
         $this->assertEquals(['DELETE'], $parsed->httpMethods);
     }
@@ -189,7 +190,7 @@ class ExtractorTest extends TestCase
      */
     public function adds_appropriate_field_based_on_configured_auth_type($config, $expected)
     {
-        $route = $this->createRoute('POST', '/withAuthenticatedTag', 'withAuthenticatedTag', true);
+        $route = $this->createRouteOldSyntax('POST', '/withAuthenticatedTag', 'withAuthenticatedTag');
         $generator = new Extractor(new DocumentationConfig(array_merge($this->config, $config)));
         $parsed = $generator->processRoute($route, [])->toArray();
         $this->assertNotNull($parsed[$expected['where']][$expected['name']]);
@@ -200,7 +201,7 @@ class ExtractorTest extends TestCase
     /** @test */
     public function generates_consistent_examples_when_faker_seed_is_set()
     {
-        $route = $this->createRoute('POST', '/withBodyParameters', 'withBodyParameters');
+        $route = $this->createRouteOldSyntax('POST', '/withBodyParameters', 'withBodyParameters');
 
         $paramName = 'room_id';
         $results = [];
@@ -225,7 +226,7 @@ class ExtractorTest extends TestCase
     /** @test */
     public function can_use_arrays_in_routes_uses()
     {
-        $route = $this->createRouteUsesArray('GET', '/api/array/test', 'withEndpointDescription');
+        $route = $this->createRoute('GET', '/api/array/test', 'withEndpointDescription');
 
         $parsed = $this->extractor->processRoute($route);
 
@@ -245,7 +246,7 @@ class ExtractorTest extends TestCase
          * @bodyParam name required Name of the location
          */
         $handler = fn () =>  'hi';
-        $route = $this->createRouteUsesCallable('POST', '/api/closure/test', $handler);
+        $route = $this->createClosureRoute('POST', '/api/closure/test', $handler);
 
         $parsed = $this->extractor->processRoute($route);
 
@@ -260,22 +261,22 @@ class ExtractorTest extends TestCase
     /** @test */
     public function endpoint_metadata_supports_custom_declarations()
     {
-        $route = $this->createRoute('POST', '/api/test', 'dummy');
+        $route = $this->createRouteOldSyntax('POST', '/api/test', 'dummy');
         $parsed = $this->extractor->processRoute($route);
         $this->assertSame('some custom metadata', $parsed->metadata->custom['myProperty']);
     }
 
-    public function createRoute(string $httpMethod, string $path, string $controllerMethod, $register = false, $class = TestController::class)
+    public function createRoute(string $httpMethod, string $path, string $controllerMethod, $class = TestController::class)
     {
-        return new Route([$httpMethod], $path, ['uses' => $class . "@$controllerMethod"]);
+        return new Route([$httpMethod], $path, ['uses' => [$class, $controllerMethod]]);
     }
 
-    public function createRouteUsesArray(string $httpMethod, string $path, string $controllerMethod, $register = false, $class = TestController::class)
+    public function createRouteOldSyntax(string $httpMethod, string $path, string $controllerMethod, $class = TestController::class)
     {
-        return new Route([$httpMethod], $path, ['uses' => [$class, $controllerMethod]]);
+        return new Route([$httpMethod], $path, ['uses' => $class . "@$controllerMethod"]);
     }
 
-    public function createRouteUsesCallable(string $httpMethod, string $path, callable $handler, $register = false)
+    public function createClosureRoute(string $httpMethod, string $path, callable $handler)
     {
         return new Route([$httpMethod], $path, ['uses' => $handler]);
     }