瀏覽代碼

Add ability to automatically extract fields from response

shalvah 5 年之前
父節點
當前提交
afccb86451

+ 1 - 1
composer.json

@@ -33,7 +33,7 @@
         "knuckleswtf/pastel": "dev-master",
         "league/flysystem": "^1.0",
         "mpociot/reflection-docblock": "^1.0.1",
-        "nunomaduro/collision": "^3.0|^4.0",
+        "nunomaduro/collision": "^4.0",
         "ramsey/uuid": "^3.8|^4.0",
         "shalvah/clara": "^2.6",
         "symfony/var-exporter": "^4.0|^5.0"

+ 3 - 0
config/scribe.php

@@ -214,6 +214,9 @@ return [
             \Knuckles\Scribe\Extracting\Strategies\Responses\UseApiResourceTags::class,
             \Knuckles\Scribe\Extracting\Strategies\Responses\ResponseCalls::class,
         ],
+        'responseFields' => [
+            \Knuckles\Scribe\Extracting\Strategies\ResponseFields\UseParsedResponses::class,
+        ],
     ],
 
     /*

+ 4 - 4
resources/views/partials/route.blade.php

@@ -71,12 +71,12 @@ Binary data - {{ str_replace("<<binary>>","",$response['content']) }}
 
 @if(count($route['responseFields'] ?? []))
 <h4 class="fancy-heading-panel"><b>Response Fields</b></h4>
-@foreach($route['responseFields'] as $attribute => $parameter)
+@foreach($route['responseFields'] as $field)
 @component('scribe::components.field-description', [
-  'name' => $attribute,
-  'type' => null,
+  'name' => $field['name'],
+  'type' => $field['type'],
   'required' => true,
-  'description' => $parameter['description'],
+  'description' => $field['description'],
 ])
 @endcomponent
 @endforeach

+ 3 - 3
src/Commands/GenerateDocumentation.php

@@ -25,9 +25,9 @@ class GenerateDocumentation extends Command
      *
      * @var string
      */
-    protected $signature = 'scribe:generate
-                            {--force : Force rewriting of existing routes}
-    ';
+    protected $signature = "scribe:generate
+                            {--force : Discard any changes you've made to the Markdown files}
+    ";
 
     /**
      * The console command description.

+ 12 - 1
src/Extracting/Generator.php

@@ -85,6 +85,9 @@ class Generator
         $parsedRoute['responses'] = $responses;
         $parsedRoute['showresponse'] = ! empty($responses);
 
+        $responseFields = $this->fetchResponseFields($controller, $method, $route, $routeRules, $parsedRoute);
+        $parsedRoute['responseFields'] = $responseFields;
+
         return $parsedRoute;
     }
 
@@ -128,6 +131,11 @@ class Generator
         return [];
     }
 
+    protected function fetchResponseFields(ReflectionClass $controller, ReflectionFunctionAbstract $method, Route $route, array $rulesToApply, array $context = [])
+    {
+        return $this->iterateThroughStrategies('responseFields', $context, [$route, $controller, $method, $rulesToApply]);
+    }
+
     protected function fetchRequestHeaders(ReflectionClass $controller, ReflectionFunctionAbstract $method, Route $route, array $rulesToApply, array $context = [])
     {
         $headers = $this->iterateThroughStrategies('headers', $context, [$route, $controller, $method, $rulesToApply]);
@@ -160,6 +168,9 @@ class Generator
                 \Knuckles\Scribe\Extracting\Strategies\Responses\UseApiResourceTags::class,
                 \Knuckles\Scribe\Extracting\Strategies\Responses\ResponseCalls::class,
             ],
+            'responseFields' => [
+                \Knuckles\Scribe\Extracting\Strategies\ResponseFields\UseParsedResponses::class,
+            ],
         ];
 
         // Use the default strategies for the stage, unless they were explicitly set
@@ -173,7 +184,7 @@ class Generator
             if (! is_null($results)) {
                 foreach ($results as $index => $item) {
                     if ($stage == 'responses') {
-                        // Responses are additive
+                        // Responses from different strategies are all added, not overwritten
                         $context[$stage][] = $item;
                         continue;
                     }

+ 65 - 0
src/Extracting/Strategies/ResponseFields/UseParsedResponses.php

@@ -0,0 +1,65 @@
+<?php
+
+namespace Knuckles\Scribe\Extracting\Strategies\ResponseFields;
+
+use Exception;
+use Illuminate\Routing\Route;
+use Illuminate\Support\Arr;
+use Knuckles\Scribe\Extracting\Strategies\Strategy;
+use ReflectionClass;
+use ReflectionFunctionAbstract;
+
+class UseParsedResponses extends Strategy
+{
+    /**
+     * @param Route $route
+     * @param ReflectionClass $controller
+     * @param ReflectionFunctionAbstract $method
+     * @param array $rulesToApply
+     * @param array $context
+     *
+     * @return array|null
+     *@throws Exception
+     *
+     */
+    public function __invoke(Route $route, ReflectionClass $controller, ReflectionFunctionAbstract $method, array $rulesToApply, array $context = [])
+    {
+        $responses = $context['responses'];
+
+        $successfulResponse = json_decode(Arr::first($responses, function ($response) {
+           return $response['status'] >= 200 && $response['status'] < 300;
+        })['content'] ?? null, true);
+
+        if (! is_array($successfulResponse)) {
+            return [];
+        }
+
+        // If the first key is 0, we assume it's a numeric array, ergo a list of items
+        $responseWithFields = key($successfulResponse) === 0
+            ? $successfulResponse[0] : $successfulResponse;
+
+        $fields = [];
+
+        foreach ($responseWithFields as $field => $value) {
+            $type = gettype($value);
+
+            if ($type == "double") {
+                $type = "float";
+            }
+
+            if ($type == "array" && key($successfulResponse) !== 0) {
+                $type = "object";
+            }
+
+            $fields[] = [
+                'name' => $field,
+                'value' => $value,
+                'type' => $type,
+                'description' => "",
+            ];
+        }
+
+        return $fields;
+    }
+
+}

+ 3 - 2
src/Extracting/Strategies/Responses/UseApiResourceTags.php

@@ -120,6 +120,7 @@ class UseApiResourceTags extends Strategy
      * @param array $tags
      *
      * @return string
+     * @throws Exception
      */
     private function getClassToBeTransformed(array $tags): string
     {
@@ -127,10 +128,10 @@ class UseApiResourceTags extends Strategy
             return ($tag instanceof Tag) && strtolower($tag->getName()) == 'apiresourcemodel';
         }));
 
-        $type = $modelTag->getContent();
+        $type = $modelTag ? $modelTag->getContent() : null;
 
         if (empty($type)) {
-            throw new Exception('Failed to detect an Eloquent API resource model. Please specify a model using @apiResourceModel.');
+            throw new Exception("Couldn't detect an Eloquent API resource model from your docblock. Did you remember to specify a model using @apiResourceModel?");
         }
 
         return $type;

+ 1 - 1
src/Extracting/Strategies/Responses/UseTransformerTags.php

@@ -137,7 +137,7 @@ class UseTransformerTags extends Strategy
         }
 
         if ($type == null) {
-            throw new Exception('Failed to detect a transformer model. Please specify a model using @transformerModel.');
+            throw new Exception("Couldn't detect a transformer model from your docblock. Did you remember to specify a model using @transformerModel?");
         }
 
         return $type;