浏览代码

Handle explicit binding fields in URL params

shalvah 3 年之前
父节点
当前提交
b0b89195e6

+ 4 - 0
CHANGELOG.md

@@ -12,6 +12,10 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
 
 ### Removals
 
+## 3.3.3 (Thursday, 1 July 2021)
+### Fixed
+- Try It Out: Spoof HTTP method for PUT/PATCH requests (https://github.com/knuckleswtf/scribe/pull/)
+
 ## 3.3.2 (Wednesday, 30 June 2021)
 ### Fixed
 - Try It Out: Add cancellable requests (https://github.com/knuckleswtf/scribe/commit/816e6fbd37ead033ca58bad048f38455622cb0b9)

+ 16 - 5
src/Extracting/Strategies/UrlParameters/GetFromLaravelAPI.php

@@ -27,9 +27,12 @@ class GetFromLaravelAPI extends Strategy
         foreach ($matches[1] as $match) {
             $optional = Str::endsWith($match, '?');
             $name = rtrim($match, '?');
+
+            // In case of /users/{user:id}, make the param {user}, but
+            $binding = $endpointData->route->bindingFieldFor($name);
             $parameters[$name] = [
                 'name' => $name,
-                'description' => $this->inferUrlParamDescription($endpointData->uri, $name),
+                'description' => $this->inferUrlParamDescription($endpointData->uri, $binding ?: $name, $name),
                 'required' => !$optional,
             ];
         }
@@ -103,17 +106,20 @@ class GetFromLaravelAPI extends Strategy
         return $parameters;
     }
 
-    protected function inferUrlParamDescription(string $url, string $paramName): string
+    protected function inferUrlParamDescription(string $url, string $paramName, string $originalBindingName = null): string
     {
         if ($paramName == "id") {
-            // If $url is sth like /users/{id}, return "The ID of the user."
+            // If $url is sth like /users/{id} or /users/{user}, return "The ID of the user."
             // Make sure to replace underscores, so "side_projects" becomes "side project"
-            $thing = str_replace(["_", "-"], " ",$this->getNameOfUrlThing($url, $paramName));
+            $thing = str_replace(["_", "-"], " ",$this->getNameOfUrlThing($url, $paramName, $originalBindingName));
             return "The ID of the $thing.";
         } else if (Str::is("*_id", $paramName)) {
             // If $url is sth like /something/{user_id}, return "The ID of the user."
             $parts = explode("_", $paramName);
             return "The ID of the $parts[0].";
+        } else if ($paramName && $originalBindingName) {
+            // A case like /posts/{post:slug} -> The slug of the post
+            return "The $paramName of the $originalBindingName.";
         }
 
         return '';
@@ -122,11 +128,16 @@ class GetFromLaravelAPI extends Strategy
     /**
      * Extract "thing" in the URL /<whatever>/things/{paramName}
      */
-    protected function getNameOfUrlThing(string $url, string $paramName): ?string
+    protected function getNameOfUrlThing(string $url, string $paramName, string $alternateParamName = null): ?string
     {
         try {
             $parts = explode("/", $url);
             $paramIndex = array_search("{{$paramName}}", $parts);
+
+            if ($paramIndex === false) {
+                // Try with the other param name
+                $paramIndex = array_search("{{$alternateParamName}}", $parts);
+            }
             $things = $parts[$paramIndex - 1];
             return Str::singular($things);
         } catch (\Throwable $e) {

+ 82 - 4
tests/Strategies/UrlParameters/GetFromLaravelAPITest.php

@@ -2,6 +2,8 @@
 
 namespace Knuckles\Scribe\Tests\Strategies\UrlParameters;
 
+use Illuminate\Routing\Route;
+use Illuminate\Routing\Router;
 use Knuckles\Camel\Extraction\ExtractedEndpointData;
 use Knuckles\Scribe\Extracting\Strategies\UrlParameters\GetFromLaravelAPI;
 use Knuckles\Scribe\Tests\BaseLaravelTest;
@@ -19,8 +21,9 @@ class GetFromLaravelAPITest extends BaseLaravelTest
         $endpoint = new class extends ExtractedEndpointData {
             public function __construct(array $parameters = [])
             {
-                $this->uri = 'users/{id}';
                 $this->method = new \ReflectionMethod(TestController::class, 'withInjectedModel');
+                $this->route = app(Router::class)->addRoute(['GET'], "users/{id}", ['uses' => [TestController::class, 'withInjectedModel']]);
+                $this->uri = $this->route->uri;
             }
         };
 
@@ -37,13 +40,14 @@ class GetFromLaravelAPITest extends BaseLaravelTest
     }
 
     /** @test */
-    public function can_infer_description()
+    public function can_infer_description_from_url()
     {
         $endpoint = new class extends ExtractedEndpointData {
             public function __construct(array $parameters = [])
             {
-                $this->uri = 'everything/{cat_id}';
                 $this->method = new \ReflectionMethod(TestController::class, 'dummy');
+                $this->route = app(Router::class)->addRoute(['GET'], "everything/{cat_id}", ['uses' => [TestController::class, 'dummy']]);
+                $this->uri = $this->route->uri;
             }
         };
 
@@ -57,7 +61,8 @@ class GetFromLaravelAPITest extends BaseLaravelTest
             "type" => "string",
         ], $results['cat_id']);
 
-        $endpoint->uri = 'dogs/{id}';
+        $endpoint->route = app(Router::class)->addRoute(['GET'], 'dogs/{id}', ['uses' => [TestController::class, 'dummy']]);;
+        $endpoint->uri = $endpoint->route->uri;
         $results = $strategy($endpoint, []);
 
         $this->assertArraySubset([
@@ -67,4 +72,77 @@ class GetFromLaravelAPITest extends BaseLaravelTest
             "type" => "string",
         ], $results['id']);
     }
+
+    /** @test */
+    public function can_infer_example_from_wheres()
+    {
+        $endpoint = new class extends ExtractedEndpointData {
+            public function __construct(array $parameters = [])
+            {
+                $this->method = new \ReflectionMethod(TestController::class, 'dummy');
+
+                $route =  app(Router::class)->addRoute(['GET'], "everything/{cat_id}", ['uses' => [TestController::class, 'dummy']]);
+                $this->regex = '/catz\d+-\d/';
+                $this->route = $route->where('cat_id', $this->regex);
+                $this->uri = $this->route->uri;
+            }
+        };
+
+        $strategy = new GetFromLaravelAPI(new DocumentationConfig([]));
+        $results = $strategy($endpoint, []);
+
+        $this->assertArraySubset([
+            "name" => "cat_id",
+            "description" => "The ID of the cat.",
+            "required" => true,
+            "type" => "string",
+        ], $results['cat_id']);
+        $this->assertMatchesRegularExpression($endpoint->regex, $results['cat_id']['example']);
+    }
+
+    /** @test */
+    public function can_infer_data_from_field_bindings()
+    {
+        $strategy = new GetFromLaravelAPI(new DocumentationConfig([]));
+
+        $endpoint = new class extends ExtractedEndpointData {
+            public function __construct(array $parameters = [])
+            {
+                $this->method = new \ReflectionMethod(TestController::class, 'dummy');
+
+                $route = app(Router::class)->addRoute(['GET'], "audio/{audio:slug}", ['uses' => [TestController::class, 'dummy']]);
+                $this->route = $route;
+                $this->uri = $route->uri;
+            }
+        };
+
+        $results = $strategy($endpoint, []);
+
+        $this->assertArraySubset([
+            "name" => "audio",
+            "description" => "The slug of the audio.",
+            "required" => true,
+            "type" => "string",
+        ], $results['audio']);
+
+        $endpoint = new class extends ExtractedEndpointData {
+            public function __construct(array $parameters = [])
+            {
+                $this->method = new \ReflectionMethod(TestController::class, 'withInjectedModel');
+
+                $route = app(Router::class)->addRoute(['GET'], "users/{user:id}", ['uses' => [TestController::class, 'withInjectedModel']]);
+                $this->route = $route;
+                $this->uri = $route->uri;
+            }
+        };
+
+        $results = $strategy($endpoint, []);
+
+        $this->assertArraySubset([
+            "name" => "user",
+            "description" => "The ID of the user.",
+            "required" => true,
+            "type" => "integer",
+        ], $results['user']);
+    }
 }