Explorar o código

Support types in URL and query parameters

shalvah %!s(int64=4) %!d(string=hai) anos
pai
achega
edfda67ac1

+ 6 - 6
src/Extracting/ParamHelpers.php

@@ -59,7 +59,7 @@ trait ParamHelpers
             },
         ];
 
-        $fakeFactory = $fakeFactories[$this->normalizeParameterType($baseType)] ?? $fakeFactories['string'];
+        $fakeFactory = $fakeFactories[$baseType] ?? $fakeFactories['string'];
 
         return $fakeFactory();
     }
@@ -79,7 +79,7 @@ trait ParamHelpers
             'array', // todo remove this
             'object',
         ];
-        return in_array(preg_replace('/\[]$/', '', $type), $types);
+        return in_array(str_replace('[]', '', $type), $types);
     }
 
     /**
@@ -148,15 +148,15 @@ trait ParamHelpers
             return 'string';
         }
 
-        $base = preg_replace('/\[]/', '', strtolower($typeName));
+        $base = str_replace('[]', '', strtolower($typeName));
         switch ($base) {
             case 'int':
-                return preg_replace("/$base/", 'integer', $typeName);
+                return str_replace($base, 'integer', $typeName);
             case 'float':
             case 'double':
-                return preg_replace("/$base/", 'number', $typeName);
+                return str_replace($base, 'number', $typeName);
             case 'bool':
-                return preg_replace("/$base/", 'boolean', $typeName);
+                return str_replace($base, 'boolean', $typeName);
             default:
                 return $typeName;
         }

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

@@ -61,41 +61,41 @@ class GetFromBodyParamTag extends Strategy
 
     public function getBodyParametersFromDocBlock($tags)
     {
-        $parameters = collect($tags)
-            ->filter(function ($tag) {
-                return $tag instanceof Tag && $tag->getName() === 'bodyParam';
-            })
-            ->mapWithKeys(function (Tag $tag) {
-                // Format:
-                // @bodyParam <name> <type> <"required" (optional)> <description>
-                // Examples:
-                // @bodyParam text string required The text.
-                // @bodyParam user_id integer The ID of the user.
-                preg_match('/(.+?)\s+(.+?)\s+(required\s+)?([\s\S]*)/', trim($tag->getContent()), $content);
-                $content = preg_replace('/\s+No-example.?/', '', $content);
-                if (empty($content)) {
-                    // this means only name and type were supplied
-                    [$name, $type] = preg_split('/\s+/', $tag->getContent());
-                    $required = false;
+        $parameters = [];
+
+        foreach ($tags as $tag) {
+            if ($tag->getName() !== 'bodyParam') continue;
+
+            $tagContent = trim($tag->getContent());
+            // Format:
+            // @bodyParam <name> <type> <"required" (optional)> <description>
+            // Examples:
+            // @bodyParam text string required The text.
+            // @bodyParam user_id integer The ID of the user.
+            preg_match('/(.+?)\s+(.+?)\s+(required\s+)?([\s\S]*)/', $tagContent, $content);
+            if (empty($content)) {
+                // this means only name and type were supplied
+                [$name, $type] = preg_split('/\s+/', $tagContent);
+                $required = false;
+                $description = '';
+            } else {
+                [$_, $name, $type, $required, $description] = $content;
+                $description = trim(str_replace(['No-example.', 'No-example'], '', $description));
+                if ($description == 'required') {
+                    $required = $description;
                     $description = '';
-                } else {
-                    [$_, $name, $type, $required, $description] = $content;
-                    $description = trim($description);
-                    if ($description == 'required' && empty(trim($required))) {
-                        $required = $description;
-                        $description = '';
-                    }
-                    $required = trim($required) == 'required' ? true : false;
                 }
+                $required = trim($required) === 'required';
+            }
 
-                $type = $this->normalizeParameterType($type);
-                [$description, $example] = $this->parseExampleFromParamDescription($description, $type);
-                $value = is_null($example) && ! $this->shouldExcludeExample($tag->getContent())
-                    ? $this->generateDummyValue($type)
-                    : $example;
+            $type = $this->normalizeParameterType($type);
+            [$description, $example] = $this->parseExampleFromParamDescription($description, $type);
+            $value = is_null($example) && !$this->shouldExcludeExample($tagContent)
+                ? $this->generateDummyValue($type)
+                : $example;
 
-                return [$name => compact('type', 'description', 'required', 'value')];
-            })->toArray();
+            $parameters[$name] = compact('type', 'description', 'required', 'value');
+        }
 
         return $parameters;
     }

+ 66 - 31
src/Extracting/Strategies/QueryParameters/GetFromQueryParamTag.php

@@ -59,44 +59,79 @@ class GetFromQueryParamTag extends Strategy
         return $this->getQueryParametersFromDocBlock($methodDocBlock->getTags());
     }
 
-    public function getQueryParametersFromDocBlock($tags)
+    /**
+     * @param Tag[] $tags
+     *
+     * @return <string, array>[]
+     */
+    public function getQueryParametersFromDocBlock(array $tags)
     {
-        $parameters = collect($tags)
-            ->filter(function ($tag) {
-                return $tag instanceof Tag && $tag->getName() === 'queryParam';
-            })
-            ->mapWithKeys(function (Tag $tag) {
-                // Format:
-                // @queryParam <name> <"required" (optional)> <description>
-                // Examples:
-                // @queryParam text string required The text.
-                // @queryParam user_id The ID of the user.
-                preg_match('/(.+?)\s+(required\s+)?([\s\S]*)/', $tag->getContent(), $content);
-                $content = preg_replace('/\s?No-example.?/', '', $content);
-                if (empty($content)) {
-                    // this means only name was supplied
-                    [$name] = preg_split('/\s+/', $tag->getContent());
-                    $required = false;
+        $parameters = [];
+
+        foreach ($tags as $tag) {
+            if ($tag->getName() !== 'queryParam') continue;
+
+            // Format:
+            // @queryParam <name> <type (optional)> <"required" (optional)> <description>
+            // Examples:
+            // @queryParam text required The text.
+            // @queryParam user_id integer The ID of the user.
+
+            $tagContent = trim($tag->getContent());
+            preg_match('/(.+?)\s+([a-zA-Z\[\]]+\s+)?(required\s+)?([\s\S]*)/', $tagContent, $content);
+
+            if (empty($content)) {
+                // this means only name was supplied
+                $name = $tagContent;
+                $required = false;
+                $description = '';
+                $type = 'string';
+            } else {
+                [$_, $name, $type, $required, $description] = $content;
+
+                $description = trim(str_replace(['No-example.', 'No-example'], '', $description));
+                if ($description === 'required') {
+                    // No description was supplied
+                    $required = true;
                     $description = '';
                 } else {
-                    [$_, $name, $required, $description] = $content;
-                    $description = trim($description);
-                    if ($description == 'required' && empty(trim($required))) {
-                        $required = $description;
-                        $description = '';
-                    }
-                    $required = trim($required) == 'required' ? true : false;
+                    $required = trim($required) === 'required';
                 }
 
-                [$description, $value] = $this->parseExampleFromParamDescription($description, 'string');
-                if (is_null($value) && ! $this->shouldExcludeExample($tag->getContent())) {
-                    $value = Str::contains($description, ['number', 'count', 'page'])
-                        ? $this->generateDummyValue('integer')
-                        : $this->generateDummyValue('string');
+                $type = trim($type);
+                if ($type) {
+                    if ($type === 'required') {
+                        // Type wasn't supplied
+                        $type = 'string';
+                        $required = true;
+                    } else {
+                        $type = $this->normalizeParameterType($type);
+                        // Type in annotation is optional
+                        if (!$this->isSupportedTypeInDocBlocks($type)) {
+                            // Then that wasn't a type, but part of the description
+                            $description = trim("$type $description");
+                            $type = '';
+                        }
+                    }
+                } else if ($this->isSupportedTypeInDocBlocks($description)) {
+                    // Only type was supplied
+                    $type = $description;
+                    $description = '';
                 }
 
-                return [$name => compact('description', 'required', 'value')];
-            })->toArray();
+                $type = empty($type)
+                    ? (Str::contains($description, ['number', 'count', 'page']) ? 'integer' : 'string')
+                    : $this->normalizeParameterType($type);
+
+            }
+
+            [$description, $value] = $this->parseExampleFromParamDescription($description, $type);
+            if (is_null($value) && !$this->shouldExcludeExample($tagContent)) {
+                $value = $this->generateDummyValue($type);
+            }
+
+            $parameters[$name] = compact('description', 'required', 'value', 'type');
+        }
 
         return $parameters;
     }

+ 1 - 1
src/Extracting/Strategies/ResponseFields/GetFromResponseFieldTag.php

@@ -58,7 +58,7 @@ class GetFromResponseFieldTag extends Strategy
                 // Support optional type in annotation
                 if (!$this->isSupportedTypeInDocBlocks($type)) {
                     // Then that wasn't a type, but part of the description
-                    $description = "$type $description";
+                    $description = trim("$type $description");
 
                     // Try to get a type from first 2xx response
                     $validResponse = collect($responses)->first(function ($r) {

+ 47 - 30
src/Extracting/Strategies/UrlParameters/GetFromUrlParamTag.php

@@ -54,44 +54,61 @@ class GetFromUrlParamTag extends Strategy
         return $this->getUrlParametersFromDocBlock($methodDocBlock->getTags());
     }
 
+    /**
+     * @param Tag[] $tags
+     *
+     * @return <string, array>[]
+     */
     public function getUrlParametersFromDocBlock($tags)
     {
-        $parameters = collect($tags)
-            ->filter(function ($tag) {
-                return $tag instanceof Tag && $tag->getName() === 'urlParam';
-            })
-            ->mapWithKeys(function (Tag $tag) {
-                // Format:
-                // @urlParam <name> <"required" (optional)> <description>
-                // Examples:
-                // @urlParam id required The id of the post.
-                // @urlParam user_id The ID of the user.
-                preg_match('/(.+?)\s+(required\s+)?([\s\S]*)/', $tag->getContent(), $content);
-                $content = preg_replace('/\s?No-example.?/', '', $content);
-                if (empty($content)) {
-                    // This means only name was supplied
-                    [$name] = preg_split('/\s+/', $tag->getContent());
-                    $required = false;
+        $parameters = [];
+
+        foreach ($tags as $tag) {
+            if ($tag->getName() !== 'urlParam') continue;
+
+            $tagContent = trim($tag->getContent());
+            // Format:
+            // @urlParam <name> <type (optional)> <"required" (optional)> <description>
+            // Examples:
+            // @urlParam id string required The id of the post.
+            // @urlParam user_id The ID of the user.
+
+            // We match on all the possible types for URL parameters. It's a limited range, so no biggie.
+            preg_match('/(\w+?)\s+((int|integer|string|float|double|number)\s+)?(required\s+)?([\s\S]*)/', $tagContent, $content);
+            if (empty($content)) {
+                // This means only name was supplied
+                $name = trim($tagContent);
+                $required = false;
+                $description = '';
+                $type = 'string';
+            } else {
+                [$_, $name, $__, $type, $required, $description] = $content;
+                $description = trim(str_replace(['No-example.', 'No-example'], '', $description));
+                if ($description === 'required') {
+                    $required = true;
                     $description = '';
                 } else {
-                    [$_, $name, $required, $description] = $content;
-                    $description = trim($description);
-                    if ($description == 'required' && empty(trim($required))) {
-                        $required = $description;
-                        $description = '';
-                    }
-                    $required = trim($required) == 'required' ? true : false;
+                    $required = trim($required) === 'required';
                 }
 
-                [$description, $value] = $this->parseExampleFromParamDescription($description, 'string');
-                if (is_null($value) && ! $this->shouldExcludeExample($tag->getContent())) {
-                    $value = Str::contains($description, ['number', 'count', 'page'])
-                        ? $this->generateDummyValue('integer')
-                        : $this->generateDummyValue('string');
+                if (empty($type) && $this->isSupportedTypeInDocBlocks($description)) {
+                    // Only type was supplied
+                    $type = $description;
+                    $description = '';
                 }
 
-                return [$name => compact('description', 'required', 'value')];
-            })->toArray();
+                $type = empty($type)
+                    ? (Str::contains($description, ['number', 'count', 'page']) ? 'integer' : 'string')
+                    : $this->normalizeParameterType($type);
+            }
+
+            [$description, $value] = $this->parseExampleFromParamDescription($description, $type);
+            if (is_null($value) && !$this->shouldExcludeExample($tagContent)) {
+                $value = $this->generateDummyValue($type);
+            }
+
+            $parameters[$name] = compact('description', 'required', 'value', 'type');
+        }
 
         return $parameters;
     }

+ 1 - 1
tests/Fixtures/TestController.php

@@ -131,7 +131,7 @@ class TestController extends Controller
      * @queryParam location_id required The id of the location.
      * @queryParam user_id required The id of the user. Example: me
      * @queryParam page required The page number. Example: 4
-     * @queryParam filters.* The filters.
+     * @queryParam filters The filters.
      * @queryParam url_encoded  Used for testing that URL parameters will be URL-encoded where needed. Example: + []&=
      */
     public function withQueryParameters()

+ 0 - 1
tests/Fixtures/TestRequest.php

@@ -8,7 +8,6 @@ use Illuminate\Foundation\Http\FormRequest;
  * @queryParam location_id required The id of the location.
  * @queryParam user_id required The id of the user. Example: me
  * @queryParam page required The page number. Example: 4
- * @queryParam filters.* The filters.
  * @queryParam url_encoded  Used for testing that URL parameters will be URL-encoded where needed. Example: + []&=
  * @bodyParam user_id int required The id of the user. Example: 9
  * @bodyParam room_id string The id of the room.

+ 56 - 10
tests/Strategies/QueryParameters/GetFromQueryParamTagTest.php

@@ -21,31 +21,81 @@ class GetFromQueryParamTagTest extends TestCase
         $tags = [
             new Tag('queryParam', 'location_id required The id of the location.'),
             new Tag('queryParam', 'user_id required The id of the user. Example: me'),
-            new Tag('queryParam', 'page required The page number. Example: 4'),
-            new Tag('queryParam', 'filters.* The filters.'),
-            new Tag('queryParam', 'url_encoded Used for testing that URL parameters will be URL-encoded where needed. Example: + []&='),
+            new Tag('queryParam', 'page The page number. Example: 4'),
+            new Tag('queryParam', 'with_type number Example: 13'),
+            new Tag('queryParam', 'with_list_type int[]'),
+            new Tag('queryParam', 'fields string[] The fields. Example: ["age", "name"]'),
+            new Tag('queryParam', 'filters object The filters. '),
+            new Tag('queryParam', 'filters.class double Class. Example: 11'),
+            new Tag('queryParam', 'filters.other string required Other things.'),
+            new Tag('queryParam', 'noExampleNoDescription No-example.'),
+            new Tag('queryParam', 'noExample Something No-example'),
         ];
         $results = $strategy->getQueryParametersFromDocBlock($tags);
 
         $this->assertArraySubset([
             'location_id' => [
+                'type' => 'string',
                 'required' => true,
                 'description' => 'The id of the location.',
             ],
             'user_id' => [
+                'type' => 'string',
                 'required' => true,
                 'description' => 'The id of the user.',
                 'value' => 'me',
             ],
             'page' => [
-                'required' => true,
+                'type' => 'integer',
+                'required' => false,
                 'description' => 'The page number.',
-                'value' => '4',
+                'value' => 4,
+            ],
+            'with_type' => [
+                'type' => 'number',
+                'required' => false,
+                'description' => '',
+                'value' => 13.0,
             ],
-            'filters.*' => [
+            'with_list_type' => [
+                'type' => 'integer[]',
+                'required' => false,
+                'description' => '',
+            ],
+            'fields' => [
+                'type' => 'string[]',
+                'required' => false,
+                'description' => 'The fields.',
+                'value' => ['age', 'name']
+            ],
+            'filters' => [
+                'type' => 'object',
                 'required' => false,
                 'description' => 'The filters.',
             ],
+            'filters.class' => [
+                'type' => 'number',
+                'required' => false,
+                'description' => 'Class.',
+                'value' => 11.0
+            ],
+            'filters.other' => [
+                'type' => 'string',
+                'required' => true,
+                'description' => 'Other things.',
+            ],
+            'noExampleNoDescription' => [
+                'type' => 'string',
+                'required' => false,
+                'description' => '',
+                'value' => null
+            ],
+            'noExample' => [
+                'type' => 'string',
+                'required' => false,
+                'description' => 'Something',
+                'value' => null
+            ],
         ], $results);
     }
 
@@ -74,10 +124,6 @@ class GetFromQueryParamTagTest extends TestCase
                 'description' => 'The page number.',
                 'value' => '4',
             ],
-            'filters.*' => [
-                'required' => false,
-                'description' => 'The filters.',
-            ],
         ], $results);
     }
 

+ 60 - 0
tests/Strategies/UrlParameters/GetFromUrlParamTagTest.php

@@ -19,18 +19,78 @@ class GetFromUrlParamTagTest extends TestCase
         $tags = [
             new Tag('urlParam', 'id required The id of the order.'),
             new Tag('urlParam', 'lang The language to serve in.'),
+            new Tag('urlParam', 'withType number With type, maybe.'),
+            new Tag('urlParam', 'withTypeDefinitely integer required With type.'),
+            new Tag('urlParam', 'barebones'),
+            new Tag('urlParam', 'barebonesType number'),
+            new Tag('urlParam', 'barebonesRequired required'),
+            new Tag('urlParam', 'withExampleOnly Example: 12'),
+            new Tag('urlParam', 'withExampleOnlyButTyped int Example: 12'),
+            new Tag('urlParam', 'noExampleNoDescription No-example.'),
+            new Tag('urlParam', 'noExample Something No-example'),
         ];
         $results = $strategy->getUrlParametersFromDocBlock($tags);
 
         $this->assertArraySubset([
             'id' => [
+                'type' => 'string',
                 'required' => true,
                 'description' => 'The id of the order.',
             ],
             'lang' => [
+                'type' => 'string',
                 'required' => false,
                 'description' => 'The language to serve in.',
             ],
+            'withType' => [
+                'type' => 'number',
+                'required' => false,
+                'description' => 'With type, maybe.',
+            ],
+            'withTypeDefinitely' => [
+                'type' => 'integer',
+                'required' => true,
+                'description' => 'With type.',
+            ],
+            'barebones' => [
+                'type' => 'string',
+                'required' => false,
+                'description' => '',
+            ],
+            'barebonesType' => [
+                'type' => 'number',
+                'required' => false,
+                'description' => '',
+            ],
+            'barebonesRequired' => [
+                'type' => 'string',
+                'required' => true,
+                'description' => '',
+            ],
+            'withExampleOnly' => [
+                'type' => 'string',
+                'required' => false,
+                'description' => '',
+                'value' => '12',
+            ],
+            'withExampleOnlyButTyped' => [
+                'type' => 'integer',
+                'required' => false,
+                'description' => '',
+                'value' => 12
+            ],
+            'noExampleNoDescription' => [
+                'type' => 'string',
+                'required' => false,
+                'description' => '',
+                'value' => null
+            ],
+            'noExample' => [
+                'type' => 'string',
+                'required' => false,
+                'description' => 'Something',
+                'value' => null
+            ],
         ], $results);
     }