Просмотр исходного кода

Support specifying `Example: null` in annotations (#755)

liverocaisson 1 год назад
Родитель
Сommit
6ecd6a8626

+ 1 - 0
camel/Extraction/Parameter.php

@@ -13,6 +13,7 @@ class Parameter extends BaseDTO
     public mixed $example = null;
     public string $type = 'string';
     public array $enumValues = [];
+    public bool $exampleWasSpecified = false;
 
     public function __construct(array $parameters = [])
     {

+ 6 - 3
src/Extracting/Extractor.php

@@ -238,9 +238,12 @@ class Extractor
          * @var Parameter $details
          */
         foreach ($parameters as $paramName => $details) {
-            // Remove params which have no examples and are optional.
-            if (is_null($details->example) && $details->required === false) {
-                continue;
+            
+            // Remove params which have no intentional examples and are optional.
+            if (!$details->exampleWasSpecified) {
+                if (is_null($details->example) && $details->required === false) {
+                    continue;
+                }
             }
 
             if ($details->type === 'file') {

+ 10 - 3
src/Extracting/ParamHelpers.php

@@ -236,14 +236,21 @@ trait ParamHelpers
      */
     protected function parseExampleFromParamDescription(string $description, string $type): array
     {
+        $exampleWasSpecified = false;
         $example = null;
         $enumValues = [];
 
         if (preg_match('/(.*)\bExample:\s*([\s\S]+)\s*/s', $description, $content)) {
+            $exampleWasSpecified = true;
             $description = trim($content[1]);
 
-            // Examples are parsed as strings by default, we need to cast them properly
-            $example = $this->castToType($content[2], $type);
+            if ($content[2] == 'null') {
+                // If we intentionally put null as example we return null as example
+                $example = null;
+            } else {
+                // Examples are parsed as strings by default, we need to cast them properly
+                $example = $this->castToType($content[2], $type);
+            }
         }
 
         if (preg_match('/(.*)\bEnum:\s*([\s\S]+)\s*/s', $description, $content)) {
@@ -255,6 +262,6 @@ trait ParamHelpers
             );
         }
 
-        return [$description, $example, $enumValues];
+        return [$description, $example, $enumValues, $exampleWasSpecified];
     }
 }

+ 2 - 2
src/Extracting/ParsesValidationRules.php

@@ -660,14 +660,14 @@ trait ParsesValidationRules
         foreach ($parameters as $name => $details) {
             if (Str::endsWith($name, '.*')) {
                 // The user might have set the example via bodyParameters()
-                $hasExample = $this->examplePresent($details);
+                $exampleWasSpecified = $this->examplePresent($details);
 
                 // Change cars.*.dogs.things.*.* with type X to cars.*.dogs.things with type X[][]
                 while (Str::endsWith($name, '.*')) {
                     $details['type'] .= '[]';
                     $name = substr($name, 0, -2);
 
-                    if ($hasExample) {
+                    if ($exampleWasSpecified) {
                         $details['example'] = [$details['example']];
                     } else if (isset($details['setter'])) {
                         $previousSetter = $details['setter'];

+ 2 - 2
src/Extracting/Strategies/BodyParameters/GetFromBodyParamTag.php

@@ -33,9 +33,9 @@ class GetFromBodyParamTag extends GetFieldsFromTagStrategy
         }
 
         $type = static::normalizeTypeName($type);
-        [$description, $example, $enumValues] =
+        [$description, $example, $enumValues, $exampleWasSpecified] =
             $this->getDescriptionAndExample($description, $type, $tagContent, $name);
 
-        return compact('name', 'type', 'description', 'required', 'example', 'enumValues');
+        return compact('name', 'type', 'description', 'required', 'example', 'enumValues', 'exampleWasSpecified');
     }
 }

+ 9 - 3
src/Extracting/Strategies/GetFieldsFromTagStrategy.php

@@ -37,9 +37,15 @@ abstract class GetFieldsFromTagStrategy extends TagStrategyWithFormRequestFallba
         string $description, string $type, string $tagContent, string $fieldName
     ): array
     {
-        [$description, $example, $enumValues] = $this->parseExampleFromParamDescription($description, $type);
-        $example = $this->setExampleIfNeeded($example, $type, $tagContent, $fieldName, $enumValues);
-        return [$description, $example, $enumValues];
+        [$description, $example, $enumValues, $exampleWasSpecified] = $this->parseExampleFromParamDescription($description, $type);
+
+        if($exampleWasSpecified && $example === null) {
+            $example = null;
+        } else {
+            $example = $this->setExampleIfNeeded($example, $type, $tagContent, $fieldName, $enumValues);
+        }
+        
+        return [$description, $example, $enumValues, $exampleWasSpecified];
     }
 
     protected function setExampleIfNeeded(

+ 2 - 2
src/Extracting/Strategies/QueryParameters/GetFromQueryParamTag.php

@@ -63,9 +63,9 @@ class GetFromQueryParamTag extends GetFieldsFromTagStrategy
 
         }
 
-        [$description, $example, $enumValues] =
+        [$description, $example, $enumValues, $exampleWasSpecified] =
             $this->getDescriptionAndExample($description, $type, $tagContent, $name);
 
-        return compact('name', 'description', 'required', 'example', 'type', 'enumValues');
+        return compact('name', 'description', 'required', 'example', 'type', 'enumValues', 'exampleWasSpecified');
     }
 }

+ 2 - 2
src/Extracting/Strategies/UrlParameters/GetFromUrlParamTag.php

@@ -46,9 +46,9 @@ class GetFromUrlParamTag extends GetFieldsFromTagStrategy
                 : static::normalizeTypeName($type);
         }
 
-        [$description, $example, $enumValues] =
+        [$description, $example, $enumValues, $exampleWasSpecified] =
             $this->getDescriptionAndExample($description, $type, $tagContent, $name);
 
-        return compact('name', 'description', 'required', 'example', 'type', 'enumValues');
+        return compact('name', 'description', 'required', 'example', 'type', 'enumValues', 'exampleWasSpecified');
     }
 }

+ 1 - 0
tests/GenerateDocumentation/OutputTest.php

@@ -478,6 +478,7 @@ class OutputTest extends BaseLaravelTest
             'type' => 'integer',
             'enumValues' => [],
             'custom' => [],
+            'exampleWasSpecified' => false,
         ];
         $group['endpoints'][0]['urlParameters']['a_param'] = $extraParam;
         file_put_contents($firstGroupFilePath, Yaml::dump(

+ 25 - 0
tests/Strategies/BodyParameters/GetFromBodyParamTagTest.php

@@ -126,6 +126,31 @@ class GetFromBodyParamTagTest extends TestCase
         ], $results);
     }
 
+    /** @test */
+    public function retains_null_as_example_if_specified()
+    {
+        $tags = [
+            new Tag('bodyParam', 'id int required The id to use. Leave null to autogenerate. Example: null'),
+            new Tag('bodyParam', 'key string A key. Example: null'),
+        ];
+        $results = $this->strategy->getFromTags($tags);
+
+        $this->assertArraySubset([
+            'id' => [
+                'type' => 'integer',
+                'required' => true,
+                'description' => 'The id to use. Leave null to autogenerate.',
+                'example' => null,
+            ],
+            'key' => [
+                'type' => 'string',
+                'required' => false,
+                'description' => 'A key.',
+                'example' => null,
+            ],
+        ], $results);
+    }
+
     /** @test */
     public function can_fetch_from_bodyparam_tag_for_array_body()
     {