From d31ce7f8f7973ceffe64cbd7c8df762e4273286e Mon Sep 17 00:00:00 2001 From: Lucas Michot Date: Tue, 24 May 2016 00:27:51 +0200 Subject: [PATCH 001/780] Sort imports so that they match Laravel code styles --- src/Mpociot/ApiDoc/ApiDocGenerator.php | 8 ++++---- src/Mpociot/ApiDoc/ApiDocGeneratorServiceProvider.php | 2 +- src/Mpociot/ApiDoc/Commands/GenerateDocumentation.php | 2 +- tests/ApiDocGeneratorTest.php | 4 ++-- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/Mpociot/ApiDoc/ApiDocGenerator.php b/src/Mpociot/ApiDoc/ApiDocGenerator.php index f6b4cf27..b060cffb 100644 --- a/src/Mpociot/ApiDoc/ApiDocGenerator.php +++ b/src/Mpociot/ApiDoc/ApiDocGenerator.php @@ -3,14 +3,14 @@ namespace Mpociot\ApiDoc; use Faker\Factory; -use Illuminate\Foundation\Http\FormRequest; +use ReflectionClass; +use Illuminate\Support\Str; use Illuminate\Routing\Route; use Illuminate\Support\Facades\App; +use phpDocumentor\Reflection\DocBlock; use Illuminate\Support\Facades\Request; use Illuminate\Support\Facades\Validator; -use Illuminate\Support\Str; -use phpDocumentor\Reflection\DocBlock; -use ReflectionClass; +use Illuminate\Foundation\Http\FormRequest; class ApiDocGenerator { diff --git a/src/Mpociot/ApiDoc/ApiDocGeneratorServiceProvider.php b/src/Mpociot/ApiDoc/ApiDocGeneratorServiceProvider.php index 7ecf6c55..9684e5e2 100644 --- a/src/Mpociot/ApiDoc/ApiDocGeneratorServiceProvider.php +++ b/src/Mpociot/ApiDoc/ApiDocGeneratorServiceProvider.php @@ -3,8 +3,8 @@ namespace Mpociot\ApiDoc; use Illuminate\Support\ServiceProvider; -use Mpociot\ApiDoc\Commands\GenerateDocumentation; use Mpociot\ApiDoc\Commands\UpdateDocumentation; +use Mpociot\ApiDoc\Commands\GenerateDocumentation; class ApiDocGeneratorServiceProvider extends ServiceProvider { diff --git a/src/Mpociot/ApiDoc/Commands/GenerateDocumentation.php b/src/Mpociot/ApiDoc/Commands/GenerateDocumentation.php index 5a94a985..e7c73a27 100644 --- a/src/Mpociot/ApiDoc/Commands/GenerateDocumentation.php +++ b/src/Mpociot/ApiDoc/Commands/GenerateDocumentation.php @@ -3,8 +3,8 @@ namespace Mpociot\ApiDoc\Commands; use Illuminate\Console\Command; -use Illuminate\Support\Facades\Route; use Mpociot\ApiDoc\ApiDocGenerator; +use Illuminate\Support\Facades\Route; use Mpociot\Documentarian\Documentarian; class GenerateDocumentation extends Command diff --git a/tests/ApiDocGeneratorTest.php b/tests/ApiDocGeneratorTest.php index 24b4a45d..632c9b86 100644 --- a/tests/ApiDocGeneratorTest.php +++ b/tests/ApiDocGeneratorTest.php @@ -1,9 +1,9 @@ Date: Tue, 24 May 2016 00:59:12 +0200 Subject: [PATCH 002/780] Fix some docblocks --- src/Mpociot/ApiDoc/ApiDocGenerator.php | 6 +++--- src/Mpociot/ApiDoc/Commands/GenerateDocumentation.php | 2 +- src/Mpociot/ApiDoc/Commands/UpdateDocumentation.php | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/Mpociot/ApiDoc/ApiDocGenerator.php b/src/Mpociot/ApiDoc/ApiDocGenerator.php index f6b4cf27..28980eb0 100644 --- a/src/Mpociot/ApiDoc/ApiDocGenerator.php +++ b/src/Mpociot/ApiDoc/ApiDocGenerator.php @@ -114,9 +114,9 @@ private function getRouteRules($route) } /** - * @param $arr - * @param $first - * @param $last + * @param array $arr + * @param string $first + * @param string $last * * @return string */ diff --git a/src/Mpociot/ApiDoc/Commands/GenerateDocumentation.php b/src/Mpociot/ApiDoc/Commands/GenerateDocumentation.php index 5a94a985..6801720f 100644 --- a/src/Mpociot/ApiDoc/Commands/GenerateDocumentation.php +++ b/src/Mpociot/ApiDoc/Commands/GenerateDocumentation.php @@ -41,7 +41,7 @@ public function __construct() /** * Execute the console command. * - * @return mixed + * @return false|null */ public function handle() { diff --git a/src/Mpociot/ApiDoc/Commands/UpdateDocumentation.php b/src/Mpociot/ApiDoc/Commands/UpdateDocumentation.php index 8e25ea6d..00883314 100644 --- a/src/Mpociot/ApiDoc/Commands/UpdateDocumentation.php +++ b/src/Mpociot/ApiDoc/Commands/UpdateDocumentation.php @@ -36,7 +36,7 @@ public function __construct() /** * Execute the console command. * - * @return mixed + * @return false|null */ public function handle() { From eef611ad5dc093e24c31ec476f97688eafd09255 Mon Sep 17 00:00:00 2001 From: Lucas Michot Date: Tue, 24 May 2016 01:07:14 +0200 Subject: [PATCH 003/780] Fix docblocks spacing too and use FQN --- src/Mpociot/ApiDoc/ApiDocGenerator.php | 36 ++++++++++--------- .../ApiDoc/ApiDocGeneratorServiceProvider.php | 2 ++ .../ApiDoc/Commands/GenerateDocumentation.php | 2 +- 3 files changed, 22 insertions(+), 18 deletions(-) diff --git a/src/Mpociot/ApiDoc/ApiDocGenerator.php b/src/Mpociot/ApiDoc/ApiDocGenerator.php index 28980eb0..c915de04 100644 --- a/src/Mpociot/ApiDoc/ApiDocGenerator.php +++ b/src/Mpociot/ApiDoc/ApiDocGenerator.php @@ -15,7 +15,7 @@ class ApiDocGenerator { /** - * @param Route $route + * @param \Illuminate\Routing\Route $route * * @return array */ @@ -52,7 +52,7 @@ public function processRoute(Route $route) } /** - * @param \Illuminate\Routing\Route $route + * @param \Illuminate\Routing\Route $route * * @return \Illuminate\Http\Response */ @@ -65,7 +65,7 @@ private function getRouteResponse(Route $route) } /** - * @param $route + * @param \Illuminate\Routing\Route $route * * @return string */ @@ -85,7 +85,7 @@ private function getRouteDescription($route) } /** - * @param $route + * @param \Illuminate\Routing\Route $route * * @return array */ @@ -131,8 +131,10 @@ protected function fancyImplode($arr, $first, $last) } /** - * @param $rule - * @param $attributeData + * @param string $rule + * @param array $attributeData + * + * @return void */ protected function parseRule($rule, &$attributeData) { @@ -304,13 +306,13 @@ protected function parseRule($rule, &$attributeData) /** * Call the given URI and return the Response. * - * @param string $method - * @param string $uri - * @param array $parameters - * @param array $cookies - * @param array $files - * @param array $server - * @param string $content + * @param string $method + * @param string $uri + * @param array $parameters + * @param array $cookies + * @param array $files + * @param array $server + * @param string $content * * @return \Illuminate\Http\Response */ @@ -339,7 +341,7 @@ public function callRoute($method, $uri, $parameters = [], $cookies = [], $files /** * Transform headers array to array of $_SERVER vars with HTTP_* format. * - * @param array $headers + * @param array $headers * * @return array */ @@ -364,7 +366,7 @@ protected function transformHeadersToServerVars(array $headers) /** * Parse a string based rule. * - * @param string $rules + * @param string $rules * * @return array */ @@ -387,8 +389,8 @@ protected function parseStringRule($rules) /** * Parse a parameter list. * - * @param string $rule - * @param string $parameter + * @param string $rule + * @param string $parameter * * @return array */ diff --git a/src/Mpociot/ApiDoc/ApiDocGeneratorServiceProvider.php b/src/Mpociot/ApiDoc/ApiDocGeneratorServiceProvider.php index 7ecf6c55..8b2744c7 100644 --- a/src/Mpociot/ApiDoc/ApiDocGeneratorServiceProvider.php +++ b/src/Mpociot/ApiDoc/ApiDocGeneratorServiceProvider.php @@ -20,6 +20,8 @@ public function boot() /** * Register the API doc commands. + * + * @return void */ public function register() { diff --git a/src/Mpociot/ApiDoc/Commands/GenerateDocumentation.php b/src/Mpociot/ApiDoc/Commands/GenerateDocumentation.php index 6801720f..a36ad291 100644 --- a/src/Mpociot/ApiDoc/Commands/GenerateDocumentation.php +++ b/src/Mpociot/ApiDoc/Commands/GenerateDocumentation.php @@ -83,7 +83,7 @@ public function handle() } /** - * @param $parsedRoutes + * @param $parsedRoutes */ private function writeMarkdown($parsedRoutes) { From a78775f62b8d922169ed23bbf6f5d649225287be Mon Sep 17 00:00:00 2001 From: Lucas Michot Date: Tue, 24 May 2016 01:08:44 +0200 Subject: [PATCH 004/780] More docblocks fixes --- src/Mpociot/ApiDoc/Commands/GenerateDocumentation.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/Mpociot/ApiDoc/Commands/GenerateDocumentation.php b/src/Mpociot/ApiDoc/Commands/GenerateDocumentation.php index a36ad291..5d203985 100644 --- a/src/Mpociot/ApiDoc/Commands/GenerateDocumentation.php +++ b/src/Mpociot/ApiDoc/Commands/GenerateDocumentation.php @@ -83,7 +83,9 @@ public function handle() } /** - * @param $parsedRoutes + * @param array $parsedRoutes + * + * @return void */ private function writeMarkdown($parsedRoutes) { From c0eb4712bfb9f69e803dea74705fe98497d29c45 Mon Sep 17 00:00:00 2001 From: Lucas Michot Date: Tue, 24 May 2016 01:11:20 +0200 Subject: [PATCH 005/780] Use stricter assertions --- tests/ApiDocGeneratorTest.php | 142 +++++++++++++++++----------------- 1 file changed, 71 insertions(+), 71 deletions(-) diff --git a/tests/ApiDocGeneratorTest.php b/tests/ApiDocGeneratorTest.php index 24b4a45d..21787554 100644 --- a/tests/ApiDocGeneratorTest.php +++ b/tests/ApiDocGeneratorTest.php @@ -28,8 +28,8 @@ public function testCanParseMethodDescription() $route = new Route(['GET'], '/api/test', ['uses' => 'TestController@parseMethodDescription']); $parsed = $this->generator->processRoute($route); - $this->assertEquals('Example title.', $parsed['title']); - $this->assertEquals("This will be the long description.\nIt can also be multiple lines long.", $parsed['description']); + $this->assertSame('Example title.', $parsed['title']); + $this->assertSame("This will be the long description.\nIt can also be multiple lines long.", $parsed['description']); } public function testCanParseRouteMethods() @@ -41,19 +41,19 @@ public function testCanParseRouteMethods() $route = new Route(['GET'], '/get', ['uses' => 'TestController@parseMethodDescription']); $parsed = $this->generator->processRoute($route); - $this->assertEquals(['GET', 'HEAD'], $parsed['methods']); + $this->assertSame(['GET', 'HEAD'], $parsed['methods']); $route = new Route(['POST'], '/post', ['uses' => 'TestController@parseMethodDescription']); $parsed = $this->generator->processRoute($route); - $this->assertEquals(['POST'], $parsed['methods']); + $this->assertSame(['POST'], $parsed['methods']); $route = new Route(['PUT'], '/put', ['uses' => 'TestController@parseMethodDescription']); $parsed = $this->generator->processRoute($route); - $this->assertEquals(['PUT'], $parsed['methods']); + $this->assertSame(['PUT'], $parsed['methods']); $route = new Route(['DELETE'], '/delete', ['uses' => 'TestController@parseMethodDescription']); $parsed = $this->generator->processRoute($route); - $this->assertEquals(['DELETE'], $parsed['methods']); + $this->assertSame(['DELETE'], $parsed['methods']); } public function testCanParseFormRequestRules() @@ -73,219 +73,219 @@ public function testCanParseFormRequestRules() case 'required': $this->assertTrue($attribute['required']); - $this->assertEquals('string', $attribute['type']); + $this->assertSame('string', $attribute['type']); $this->assertCount(0, $attribute['description']); break; case 'accepted': $this->assertTrue($attribute['required']); - $this->assertEquals('boolean', $attribute['type']); + $this->assertSame('boolean', $attribute['type']); $this->assertCount(0, $attribute['description']); break; case 'active_url': $this->assertFalse($attribute['required']); - $this->assertEquals('url', $attribute['type']); + $this->assertSame('url', $attribute['type']); $this->assertCount(0, $attribute['description']); break; case 'alpha': $this->assertFalse($attribute['required']); - $this->assertEquals('string', $attribute['type']); + $this->assertSame('string', $attribute['type']); $this->assertCount(1, $attribute['description']); - $this->assertEquals('Only alphabetic characters allowed', $attribute['description'][0]); + $this->assertSame('Only alphabetic characters allowed', $attribute['description'][0]); break; case 'alpha_dash': $this->assertFalse($attribute['required']); - $this->assertEquals('string', $attribute['type']); + $this->assertSame('string', $attribute['type']); $this->assertCount(1, $attribute['description']); - $this->assertEquals('Allowed: alpha-numeric characters, as well as dashes and underscores.', $attribute['description'][0]); + $this->assertSame('Allowed: alpha-numeric characters, as well as dashes and underscores.', $attribute['description'][0]); break; case 'alpha_num': $this->assertFalse($attribute['required']); - $this->assertEquals('string', $attribute['type']); + $this->assertSame('string', $attribute['type']); $this->assertCount(1, $attribute['description']); - $this->assertEquals('Only alpha-numeric characters allowed', $attribute['description'][0]); + $this->assertSame('Only alpha-numeric characters allowed', $attribute['description'][0]); break; case 'array': $this->assertFalse($attribute['required']); - $this->assertEquals('array', $attribute['type']); + $this->assertSame('array', $attribute['type']); $this->assertCount(0, $attribute['description']); break; case 'between': $this->assertFalse($attribute['required']); - $this->assertEquals('numeric', $attribute['type']); + $this->assertSame('numeric', $attribute['type']); $this->assertCount(1, $attribute['description']); - $this->assertEquals('Between: `5` and `200`', $attribute['description'][0]); + $this->assertSame('Between: `5` and `200`', $attribute['description'][0]); break; case 'before': $this->assertFalse($attribute['required']); - $this->assertEquals('date', $attribute['type']); + $this->assertSame('date', $attribute['type']); $this->assertCount(1, $attribute['description']); - $this->assertEquals('Must be a date preceding: `Saturday, 23-Apr-16 14:31:00 UTC`', $attribute['description'][0]); + $this->assertSame('Must be a date preceding: `Saturday, 23-Apr-16 14:31:00 UTC`', $attribute['description'][0]); break; case 'boolean': $this->assertFalse($attribute['required']); - $this->assertEquals('boolean', $attribute['type']); + $this->assertSame('boolean', $attribute['type']); $this->assertCount(0, $attribute['description']); break; case 'date': $this->assertFalse($attribute['required']); - $this->assertEquals('date', $attribute['type']); + $this->assertSame('date', $attribute['type']); $this->assertCount(0, $attribute['description']); break; case 'date_format': $this->assertFalse($attribute['required']); - $this->assertEquals('date', $attribute['type']); + $this->assertSame('date', $attribute['type']); $this->assertCount(1, $attribute['description']); - $this->assertEquals('Date format: `j.n.Y H:iP`', $attribute['description'][0]); + $this->assertSame('Date format: `j.n.Y H:iP`', $attribute['description'][0]); break; case 'different': $this->assertFalse($attribute['required']); - $this->assertEquals('string', $attribute['type']); + $this->assertSame('string', $attribute['type']); $this->assertCount(1, $attribute['description']); - $this->assertEquals('Must have a different value than parameter: `alpha_num`', $attribute['description'][0]); + $this->assertSame('Must have a different value than parameter: `alpha_num`', $attribute['description'][0]); break; case 'digits': $this->assertFalse($attribute['required']); - $this->assertEquals('numeric', $attribute['type']); + $this->assertSame('numeric', $attribute['type']); $this->assertCount(1, $attribute['description']); - $this->assertEquals('Must have an exact length of `2`', $attribute['description'][0]); + $this->assertSame('Must have an exact length of `2`', $attribute['description'][0]); break; case 'digits_between': $this->assertFalse($attribute['required']); - $this->assertEquals('numeric', $attribute['type']); + $this->assertSame('numeric', $attribute['type']); $this->assertCount(1, $attribute['description']); - $this->assertEquals('Must have a length between `2` and `10`', $attribute['description'][0]); + $this->assertSame('Must have a length between `2` and `10`', $attribute['description'][0]); break; case 'email': $this->assertFalse($attribute['required']); - $this->assertEquals('email', $attribute['type']); + $this->assertSame('email', $attribute['type']); $this->assertCount(0, $attribute['description']); break; case 'exists': $this->assertFalse($attribute['required']); - $this->assertEquals('string', $attribute['type']); + $this->assertSame('string', $attribute['type']); $this->assertCount(1, $attribute['description']); - $this->assertEquals('Valid user firstname', $attribute['description'][0]); + $this->assertSame('Valid user firstname', $attribute['description'][0]); break; case 'image': $this->assertFalse($attribute['required']); - $this->assertEquals('image', $attribute['type']); + $this->assertSame('image', $attribute['type']); $this->assertCount(1, $attribute['description']); - $this->assertEquals('Must be an image (jpeg, png, bmp, gif, or svg)', $attribute['description'][0]); + $this->assertSame('Must be an image (jpeg, png, bmp, gif, or svg)', $attribute['description'][0]); break; case 'in': $this->assertFalse($attribute['required']); - $this->assertEquals('string', $attribute['type']); + $this->assertSame('string', $attribute['type']); $this->assertCount(1, $attribute['description']); - $this->assertEquals('`jpeg`, `png`, `bmp`, `gif` or `svg`', $attribute['description'][0]); + $this->assertSame('`jpeg`, `png`, `bmp`, `gif` or `svg`', $attribute['description'][0]); break; case 'integer': $this->assertFalse($attribute['required']); - $this->assertEquals('integer', $attribute['type']); + $this->assertSame('integer', $attribute['type']); $this->assertCount(0, $attribute['description']); break; case 'ip': $this->assertFalse($attribute['required']); - $this->assertEquals('ip', $attribute['type']); + $this->assertSame('ip', $attribute['type']); $this->assertCount(0, $attribute['description']); break; case 'json': $this->assertFalse($attribute['required']); - $this->assertEquals('string', $attribute['type']); + $this->assertSame('string', $attribute['type']); $this->assertCount(1, $attribute['description']); - $this->assertEquals('Must be a valid JSON string.', $attribute['description'][0]); + $this->assertSame('Must be a valid JSON string.', $attribute['description'][0]); break; case 'max': $this->assertFalse($attribute['required']); - $this->assertEquals('string', $attribute['type']); + $this->assertSame('string', $attribute['type']); $this->assertCount(1, $attribute['description']); - $this->assertEquals('Maximum: `10`', $attribute['description'][0]); + $this->assertSame('Maximum: `10`', $attribute['description'][0]); break; case 'min': $this->assertFalse($attribute['required']); - $this->assertEquals('string', $attribute['type']); + $this->assertSame('string', $attribute['type']); $this->assertCount(1, $attribute['description']); - $this->assertEquals('Minimum: `20`', $attribute['description'][0]); + $this->assertSame('Minimum: `20`', $attribute['description'][0]); break; case 'mimes': $this->assertFalse($attribute['required']); - $this->assertEquals('string', $attribute['type']); + $this->assertSame('string', $attribute['type']); $this->assertCount(1, $attribute['description']); - $this->assertEquals('Allowed mime types: `jpeg`, `bmp` or `png`', $attribute['description'][0]); + $this->assertSame('Allowed mime types: `jpeg`, `bmp` or `png`', $attribute['description'][0]); break; case 'not_in': $this->assertFalse($attribute['required']); - $this->assertEquals('string', $attribute['type']); + $this->assertSame('string', $attribute['type']); $this->assertCount(1, $attribute['description']); - $this->assertEquals('Not in: `foo` or `bar`', $attribute['description'][0]); + $this->assertSame('Not in: `foo` or `bar`', $attribute['description'][0]); break; case 'numeric': $this->assertFalse($attribute['required']); - $this->assertEquals('numeric', $attribute['type']); + $this->assertSame('numeric', $attribute['type']); $this->assertCount(0, $attribute['description']); break; case 'regex': $this->assertFalse($attribute['required']); - $this->assertEquals('string', $attribute['type']); + $this->assertSame('string', $attribute['type']); $this->assertCount(1, $attribute['description']); - $this->assertEquals('Must match this regular expression: `(.*)`', $attribute['description'][0]); + $this->assertSame('Must match this regular expression: `(.*)`', $attribute['description'][0]); break; case 'required_if': $this->assertFalse($attribute['required']); - $this->assertEquals('string', $attribute['type']); + $this->assertSame('string', $attribute['type']); $this->assertCount(1, $attribute['description']); - $this->assertEquals('Required if `foo` is `bar`', $attribute['description'][0]); + $this->assertSame('Required if `foo` is `bar`', $attribute['description'][0]); break; case 'required_unless': $this->assertFalse($attribute['required']); - $this->assertEquals('string', $attribute['type']); + $this->assertSame('string', $attribute['type']); $this->assertCount(1, $attribute['description']); - $this->assertEquals('Required unless `foo` is `bar`', $attribute['description'][0]); + $this->assertSame('Required unless `foo` is `bar`', $attribute['description'][0]); break; case 'required_with': $this->assertFalse($attribute['required']); - $this->assertEquals('string', $attribute['type']); + $this->assertSame('string', $attribute['type']); $this->assertCount(1, $attribute['description']); - $this->assertEquals('Required if the parameters `foo`, `bar` or `baz` are present.', $attribute['description'][0]); + $this->assertSame('Required if the parameters `foo`, `bar` or `baz` are present.', $attribute['description'][0]); break; case 'required_with_all': $this->assertFalse($attribute['required']); - $this->assertEquals('string', $attribute['type']); + $this->assertSame('string', $attribute['type']); $this->assertCount(1, $attribute['description']); - $this->assertEquals('Required if the parameters `foo`, `bar` and `baz` are present.', $attribute['description'][0]); + $this->assertSame('Required if the parameters `foo`, `bar` and `baz` are present.', $attribute['description'][0]); break; case 'required_without': $this->assertFalse($attribute['required']); - $this->assertEquals('string', $attribute['type']); + $this->assertSame('string', $attribute['type']); $this->assertCount(1, $attribute['description']); - $this->assertEquals('Required if the parameters `foo`, `bar` or `baz` are not present.', $attribute['description'][0]); + $this->assertSame('Required if the parameters `foo`, `bar` or `baz` are not present.', $attribute['description'][0]); break; case 'required_without_all': $this->assertFalse($attribute['required']); - $this->assertEquals('string', $attribute['type']); + $this->assertSame('string', $attribute['type']); $this->assertCount(1, $attribute['description']); - $this->assertEquals('Required if the parameters `foo`, `bar` and `baz` are not present.', $attribute['description'][0]); + $this->assertSame('Required if the parameters `foo`, `bar` and `baz` are not present.', $attribute['description'][0]); break; case 'same': $this->assertFalse($attribute['required']); - $this->assertEquals('string', $attribute['type']); + $this->assertSame('string', $attribute['type']); $this->assertCount(1, $attribute['description']); - $this->assertEquals('Must be the same as `foo`', $attribute['description'][0]); + $this->assertSame('Must be the same as `foo`', $attribute['description'][0]); break; case 'size': $this->assertFalse($attribute['required']); - $this->assertEquals('string', $attribute['type']); + $this->assertSame('string', $attribute['type']); $this->assertCount(1, $attribute['description']); - $this->assertEquals('Must have the size of `51`', $attribute['description'][0]); + $this->assertSame('Must have the size of `51`', $attribute['description'][0]); break; case 'timezone': $this->assertFalse($attribute['required']); - $this->assertEquals('string', $attribute['type']); + $this->assertSame('string', $attribute['type']); $this->assertCount(1, $attribute['description']); - $this->assertEquals('Must be a valid timezone identifier', $attribute['description'][0]); + $this->assertSame('Must be a valid timezone identifier', $attribute['description'][0]); break; case 'url': $this->assertFalse($attribute['required']); - $this->assertEquals('url', $attribute['type']); + $this->assertSame('url', $attribute['type']); $this->assertCount(0, $attribute['description']); break; From 00eb039b59c9de38a8c99f1df3265e3546a69de3 Mon Sep 17 00:00:00 2001 From: Lucas Michot Date: Tue, 24 May 2016 01:28:14 +0200 Subject: [PATCH 006/780] Shorten a bvery long statement --- src/Mpociot/ApiDoc/ApiDocGenerator.php | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/Mpociot/ApiDoc/ApiDocGenerator.php b/src/Mpociot/ApiDoc/ApiDocGenerator.php index f6b4cf27..adc6c61d 100644 --- a/src/Mpociot/ApiDoc/ApiDocGenerator.php +++ b/src/Mpociot/ApiDoc/ApiDocGenerator.php @@ -24,13 +24,20 @@ public function processRoute(Route $route) $routeAction = $route->getAction(); $response = $this->getRouteResponse($route); $routeDescription = $this->getRouteDescription($routeAction['uses']); + if ($response->headers->get('Content-Type') === 'application/json') + { + $content = json_encode(json_decode($response->getContent()), JSON_PRETTY_PRINT); + } else + { + $content = $response->getContent(); + } $routeData = [ 'title' => $routeDescription['short'], 'description' => $routeDescription['long'], 'methods' => $route->getMethods(), 'uri' => $route->getUri(), 'parameters' => [], - 'response' => ($response->headers->get('Content-Type') === 'application/json') ? json_encode(json_decode($response->getContent()), JSON_PRETTY_PRINT) : $response->getContent(), + 'response' => $content, ]; $validator = Validator::make([], $this->getRouteRules($routeAction['uses'])); From 1421255747acc58b802d6ea375cdacaa81116cbd Mon Sep 17 00:00:00 2001 From: Lucas Michot Date: Tue, 24 May 2016 01:29:17 +0200 Subject: [PATCH 007/780] Fix style --- src/Mpociot/ApiDoc/ApiDocGenerator.php | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/Mpociot/ApiDoc/ApiDocGenerator.php b/src/Mpociot/ApiDoc/ApiDocGenerator.php index adc6c61d..57bc8ecf 100644 --- a/src/Mpociot/ApiDoc/ApiDocGenerator.php +++ b/src/Mpociot/ApiDoc/ApiDocGenerator.php @@ -24,11 +24,9 @@ public function processRoute(Route $route) $routeAction = $route->getAction(); $response = $this->getRouteResponse($route); $routeDescription = $this->getRouteDescription($routeAction['uses']); - if ($response->headers->get('Content-Type') === 'application/json') - { + if ($response->headers->get('Content-Type') === 'application/json') { $content = json_encode(json_decode($response->getContent()), JSON_PRETTY_PRINT); - } else - { + } else { $content = $response->getContent(); } $routeData = [ From 61d5b947a65fe1425bb8d626b98214c06df2a58b Mon Sep 17 00:00:00 2001 From: Lucas Michot Date: Tue, 24 May 2016 01:35:26 +0200 Subject: [PATCH 008/780] Small readme adjustments --- README.md | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index 3808e116..3f4d5431 100644 --- a/README.md +++ b/README.md @@ -16,20 +16,20 @@ Automatically generate your API documentation from your existing Laravel routes. Require this package with composer using the following command: -```bash +```sh $ composer require mpociot/laravel-apidoc-generator ``` Go to your `config/app.php` and add the service provider: ```php -Mpociot\ApiDoc\ApiDocGeneratorServiceProvider::class +Mpociot\ApiDoc\ApiDocGeneratorServiceProvider::class, ``` ## Usage To generate your API documentation, use the `api:generate` artisan command. -```bash +```sh $ php artisan api:generate --routePrefix=api/v1/* ``` @@ -38,11 +38,11 @@ This command will scan your applications routes for the URIs matching `api/v1/*` ### Available command options: Option | Description ---------- | ------- | ------- | ------- | ----------- +--------- | ------- `output` | The output path used for the generated documentation. Default: `public/docs` `routePrefix` | The route prefix to use for generation - `*` can be used as a wildcard -`routes ` | The route names to use for generation - Required if no routePrefix is provided -`actAsUserId ` | The user ID to use for authenticated API response calls +`routes` | The route names to use for generation - Required if no routePrefix is provided +`actAsUserId` | The user ID to use for authenticated API response calls ### How does it work? @@ -76,7 +76,7 @@ public function rules() 'title' => 'required|max:255', 'body' => 'required', 'type' => 'in:foo,bar', - 'thumbnail' => 'required_if:type,foo|image' + 'thumbnail' => 'required_if:type,foo|image', ]; } ``` @@ -89,7 +89,7 @@ If your API route accepts a `GET` method, this package tries to call the API rou If your API needs an authenticated user, you can use the `actAsUserId` option to specify a user ID that will be used for making these API calls: -```bash +```sh $ php artisan api:generate --routePrefix=api/* --actAsUserId=1 ``` @@ -103,7 +103,7 @@ The default location of this file is: `public/docs/source/index.md`. After editing the markdown file, use the `api:update` command to rebuild your documentation as a static HTML file. -```bash +```sh $ php artisan api:update ``` From b9d832936107ab70e54b274d25ea1422df5e2906 Mon Sep 17 00:00:00 2001 From: Lucas Michot Date: Tue, 24 May 2016 01:44:24 +0200 Subject: [PATCH 009/780] Exclude files from code coverage --- phpunit.xml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/phpunit.xml b/phpunit.xml index bf98672b..d81ca523 100644 --- a/phpunit.xml +++ b/phpunit.xml @@ -12,6 +12,10 @@ tests/ + + src/Mpociot/ApiDoc/ApiDocGeneratorServiceProvider.php + src/resources/views/documentarian.blade.php + From afb0f4c40f8012910cbc863bcbd2136f690117f6 Mon Sep 17 00:00:00 2001 From: Lucas Michot Date: Tue, 24 May 2016 01:52:33 +0200 Subject: [PATCH 010/780] Only instantiate \Mpociot\ApiDoc\Commands\generator if necessary --- src/Mpociot/ApiDoc/Commands/GenerateDocumentation.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Mpociot/ApiDoc/Commands/GenerateDocumentation.php b/src/Mpociot/ApiDoc/Commands/GenerateDocumentation.php index 5a94a985..b569131b 100644 --- a/src/Mpociot/ApiDoc/Commands/GenerateDocumentation.php +++ b/src/Mpociot/ApiDoc/Commands/GenerateDocumentation.php @@ -45,7 +45,6 @@ public function __construct() */ public function handle() { - $generator = new ApiDocGenerator(); $allowedRoutes = $this->option('routes'); $routePrefix = $this->option('routePrefix'); $actAs = $this->option('actAsUserId'); @@ -70,6 +69,8 @@ public function handle() $routes = Route::getRoutes(); + $generator = new ApiDocGenerator(); + /* @var \Illuminate\Routing\Route $route */ $parsedRoutes = []; foreach ($routes as $route) { From 358a0760076cd9272455b483a15995cfd43d0fee Mon Sep 17 00:00:00 2001 From: Lucas Michot Date: Tue, 24 May 2016 12:36:46 +0200 Subject: [PATCH 011/780] Add Versioneye Dependency Status --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 3f4d5431..46794732 100644 --- a/README.md +++ b/README.md @@ -10,6 +10,7 @@ Automatically generate your API documentation from your existing Laravel routes. [![Scrutinizer Code Quality](https://scrutinizer-ci.com/g/mpociot/laravel-apidoc-generator/badges/quality-score.png?b=master)](https://scrutinizer-ci.com/g/mpociot/laravel-apidoc-generator/?branch=master) [![Build Status](https://travis-ci.org/mpociot/laravel-apidoc-generator.svg?branch=master)](https://travis-ci.org/mpociot/laravel-apidoc-generator) [![StyleCI](https://styleci.io/repos/57999295/shield)](https://styleci.io/repos/57999295) +[![Dependency Status](https://www.versioneye.com/php/mpociot:laravel-apidoc-generator/dev-master/badge?style=flat)](https://www.versioneye.com/php/mpociot:laravel-apidoc-generator/dev-master) ## Installation From 90399b50e9b378935d2a404761c8701aa15b2091 Mon Sep 17 00:00:00 2001 From: Lucas Michot Date: Tue, 24 May 2016 13:31:32 +0200 Subject: [PATCH 012/780] Use PSR4 for tests --- composer.json | 5 ++ tests/ApiDocGeneratorTest.php | 104 ++++++------------------------ tests/Fixtures/TestController.php | 28 ++++++++ tests/Fixtures/TestRequest.php | 51 +++++++++++++++ 4 files changed, 103 insertions(+), 85 deletions(-) create mode 100644 tests/Fixtures/TestController.php create mode 100644 tests/Fixtures/TestRequest.php diff --git a/composer.json b/composer.json index 7b5e6737..bad021f0 100644 --- a/composer.json +++ b/composer.json @@ -29,5 +29,10 @@ "psr-0": { "Mpociot\\ApiDoc": "src/" } + }, + "autoload-dev": { + "psr-4": { + "Mpociot\\ApiDoc\\Tests\\": "tests/" + } } } diff --git a/tests/ApiDocGeneratorTest.php b/tests/ApiDocGeneratorTest.php index 2ca6bbe4..0ed9c382 100644 --- a/tests/ApiDocGeneratorTest.php +++ b/tests/ApiDocGeneratorTest.php @@ -1,11 +1,15 @@ 'TestController@parseMethodDescription']); + RouteFacade::get('/api/test', TestController::class.'@parseMethodDescription'); + $route = new Route(['GET'], '/api/test', ['uses' => TestController::class.'@parseMethodDescription']); $parsed = $this->generator->processRoute($route); $this->assertSame('Example title.', $parsed['title']); @@ -34,32 +38,32 @@ public function testCanParseMethodDescription() public function testCanParseRouteMethods() { - \Illuminate\Support\Facades\Route::get('/get', 'TestController@dummy'); - \Illuminate\Support\Facades\Route::post('/post', 'TestController@dummy'); - \Illuminate\Support\Facades\Route::put('/put', 'TestController@dummy'); - \Illuminate\Support\Facades\Route::delete('/delete', 'TestController@dummy'); + RouteFacade::get('/get', TestController::class.'@dummy'); + RouteFacade::post('/post', TestController::class.'@dummy'); + RouteFacade::put('/put', TestController::class.'@dummy'); + RouteFacade::delete('/delete', TestController::class.'@dummy'); - $route = new Route(['GET'], '/get', ['uses' => 'TestController@parseMethodDescription']); + $route = new Route(['GET'], '/get', ['uses' => TestController::class.'@parseMethodDescription']); $parsed = $this->generator->processRoute($route); $this->assertSame(['GET', 'HEAD'], $parsed['methods']); - $route = new Route(['POST'], '/post', ['uses' => 'TestController@parseMethodDescription']); + $route = new Route(['POST'], '/post', ['uses' => TestController::class.'@parseMethodDescription']); $parsed = $this->generator->processRoute($route); $this->assertSame(['POST'], $parsed['methods']); - $route = new Route(['PUT'], '/put', ['uses' => 'TestController@parseMethodDescription']); + $route = new Route(['PUT'], '/put', ['uses' => TestController::class.'@parseMethodDescription']); $parsed = $this->generator->processRoute($route); $this->assertSame(['PUT'], $parsed['methods']); - $route = new Route(['DELETE'], '/delete', ['uses' => 'TestController@parseMethodDescription']); + $route = new Route(['DELETE'], '/delete', ['uses' => TestController::class.'@parseMethodDescription']); $parsed = $this->generator->processRoute($route); $this->assertSame(['DELETE'], $parsed['methods']); } public function testCanParseFormRequestRules() { - \Illuminate\Support\Facades\Route::post('/post', 'TestController@parseFormRequestRules'); - $route = new Route(['POST'], '/post', ['uses' => 'TestController@parseFormRequestRules']); + RouteFacade::post('/post', TestController::class.'@parseFormRequestRules'); + $route = new Route(['POST'], '/post', ['uses' => TestController::class.'@parseFormRequestRules']); $parsed = $this->generator->processRoute($route); $parameters = $parsed['parameters']; @@ -293,73 +297,3 @@ public function testCanParseFormRequestRules() } } } - -class TestController extends Controller -{ - public function dummy() - { - return ''; - } - - /** - * Example title. - * - * This will be the long description. - * It can also be multiple lines long. - */ - public function parseMethodDescription() - { - return ''; - } - - public function parseFormRequestRules(TestRequest $request) - { - return ''; - } -} - -class TestRequest extends FormRequest -{ - public function rules() - { - return [ - 'required' => 'required', - 'accepted' => 'accepted', - 'after' => 'after:2016-04-23 14:31:00', - 'active_url' => 'active_url', - 'alpha' => 'alpha', - 'alpha_dash' => 'alpha_dash', - 'alpha_num' => 'alpha_num', - 'array' => 'array', - 'before' => 'before:2016-04-23 14:31:00', - 'between' => 'between:5,200', - 'boolean' => 'boolean', - 'date' => 'date', - 'date_format' => 'date_format:j.n.Y H:iP', - 'different' => 'different:alpha_num', - 'digits' => 'digits:2', - 'digits_between' => 'digits_between:2,10', - 'exists' => 'exists:users,firstname', - 'in' => 'in:jpeg,png,bmp,gif,svg', - 'integer' => 'integer', - 'ip' => 'ip', - 'json' => 'json', - 'min' => 'min:20', - 'max' => 'max:10', - 'mimes' => 'mimes:jpeg,bmp,png', - 'not_in' => 'not_in:foo,bar', - 'numeric' => 'numeric', - 'regex' => 'regex:(.*)', - 'required_if' => 'required_if:foo,bar', - 'required_unless' => 'required_unless:foo,bar', - 'required_with' => 'required_with:foo,bar,baz', - 'required_with_all' => 'required_with_all:foo,bar,baz', - 'required_without' => 'required_without:foo,bar,baz', - 'required_without_all' => 'required_without_all:foo,bar,baz', - 'same' => 'same:foo', - 'size' => 'size:51', - 'timezone' => 'timezone', - 'url' => 'url', - ]; - } -} diff --git a/tests/Fixtures/TestController.php b/tests/Fixtures/TestController.php new file mode 100644 index 00000000..fc349675 --- /dev/null +++ b/tests/Fixtures/TestController.php @@ -0,0 +1,28 @@ + 'required', + 'accepted' => 'accepted', + 'after' => 'after:2016-04-23 14:31:00', + 'active_url' => 'active_url', + 'alpha' => 'alpha', + 'alpha_dash' => 'alpha_dash', + 'alpha_num' => 'alpha_num', + 'array' => 'array', + 'before' => 'before:2016-04-23 14:31:00', + 'between' => 'between:5,200', + 'boolean' => 'boolean', + 'date' => 'date', + 'date_format' => 'date_format:j.n.Y H:iP', + 'different' => 'different:alpha_num', + 'digits' => 'digits:2', + 'digits_between' => 'digits_between:2,10', + 'exists' => 'exists:users,firstname', + 'in' => 'in:jpeg,png,bmp,gif,svg', + 'integer' => 'integer', + 'ip' => 'ip', + 'json' => 'json', + 'min' => 'min:20', + 'max' => 'max:10', + 'mimes' => 'mimes:jpeg,bmp,png', + 'not_in' => 'not_in:foo,bar', + 'numeric' => 'numeric', + 'regex' => 'regex:(.*)', + 'required_if' => 'required_if:foo,bar', + 'required_unless' => 'required_unless:foo,bar', + 'required_with' => 'required_with:foo,bar,baz', + 'required_with_all' => 'required_with_all:foo,bar,baz', + 'required_without' => 'required_without:foo,bar,baz', + 'required_without_all' => 'required_without_all:foo,bar,baz', + 'same' => 'same:foo', + 'size' => 'size:51', + 'timezone' => 'timezone', + 'url' => 'url', + ]; + } +} From b297bc10c16da4096bf2840ef0c14d54055394d1 Mon Sep 17 00:00:00 2001 From: Lucas Michot Date: Tue, 24 May 2016 14:52:49 +0200 Subject: [PATCH 013/780] Use strict comparisons --- src/Mpociot/ApiDoc/ApiDocGenerator.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Mpociot/ApiDoc/ApiDocGenerator.php b/src/Mpociot/ApiDoc/ApiDocGenerator.php index 107817da..0879761d 100644 --- a/src/Mpociot/ApiDoc/ApiDocGenerator.php +++ b/src/Mpociot/ApiDoc/ApiDocGenerator.php @@ -358,7 +358,7 @@ protected function transformHeadersToServerVars(array $headers) foreach ($headers as $name => $value) { $name = strtr(strtoupper($name), '-', '_'); - if (! starts_with($name, $prefix) && $name != 'CONTENT_TYPE') { + if (! starts_with($name, $prefix) && $name !== 'CONTENT_TYPE') { $name = $prefix.$name; } @@ -401,7 +401,7 @@ protected function parseStringRule($rules) */ protected function parseParameters($rule, $parameter) { - if (strtolower($rule) == 'regex') { + if (strtolower($rule) === 'regex') { return [$parameter]; } From 3342e989caa2575cf89f3cccd52b63f9e05d9b9d Mon Sep 17 00:00:00 2001 From: Lucas Michot Date: Tue, 24 May 2016 14:56:13 +0200 Subject: [PATCH 014/780] No need to create a variable --- src/Mpociot/ApiDoc/ApiDocGenerator.php | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/Mpociot/ApiDoc/ApiDocGenerator.php b/src/Mpociot/ApiDoc/ApiDocGenerator.php index 107817da..e283306c 100644 --- a/src/Mpociot/ApiDoc/ApiDocGenerator.php +++ b/src/Mpociot/ApiDoc/ApiDocGenerator.php @@ -64,9 +64,8 @@ public function processRoute(Route $route) private function getRouteResponse(Route $route) { $methods = $route->getMethods(); - $response = $this->callRoute(array_shift($methods), $route->getUri()); - return $response; + return $this->callRoute(array_shift($methods), $route->getUri()); } /** From e5c28445fe4f4eefef5ffed57c399818522c1b9b Mon Sep 17 00:00:00 2001 From: Marcel Pociot Date: Tue, 24 May 2016 23:43:21 +0200 Subject: [PATCH 015/780] Move exclude inside of whitelist --- phpunit.xml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/phpunit.xml b/phpunit.xml index d81ca523..1458c056 100644 --- a/phpunit.xml +++ b/phpunit.xml @@ -12,15 +12,15 @@ tests/ - - src/Mpociot/ApiDoc/ApiDocGeneratorServiceProvider.php - src/resources/views/documentarian.blade.php - src/Mpociot/ + + src/Mpociot/ApiDoc/ApiDocGeneratorServiceProvider.php + src/resources/views/documentarian.blade.php + From 3593f0d17ee7fc10809671df99cf8c2b6a9d8565 Mon Sep 17 00:00:00 2001 From: Lucas Michot Date: Tue, 24 May 2016 23:52:28 +0200 Subject: [PATCH 016/780] Upgrade phpunit --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index bad021f0..86f8f9cc 100644 --- a/composer.json +++ b/composer.json @@ -23,7 +23,7 @@ }, "require-dev": { "orchestra/testbench": "~3.0", - "phpunit/phpunit": "~4.0" + "phpunit/phpunit": "~4.0 || ~5.0" }, "autoload": { "psr-0": { From 426ac81190cba68080c6b17951d552a8f0421a0f Mon Sep 17 00:00:00 2001 From: Lucas Michot Date: Wed, 25 May 2016 13:36:46 +0200 Subject: [PATCH 017/780] Directly call \Illuminate\Support\Str::startsWith --- src/Mpociot/ApiDoc/ApiDocGenerator.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Mpociot/ApiDoc/ApiDocGenerator.php b/src/Mpociot/ApiDoc/ApiDocGenerator.php index 5a52430f..8c49eda1 100644 --- a/src/Mpociot/ApiDoc/ApiDocGenerator.php +++ b/src/Mpociot/ApiDoc/ApiDocGenerator.php @@ -357,7 +357,7 @@ protected function transformHeadersToServerVars(array $headers) foreach ($headers as $name => $value) { $name = strtr(strtoupper($name), '-', '_'); - if (! starts_with($name, $prefix) && $name !== 'CONTENT_TYPE') { + if (! Str::startsWith($name, $prefix) && $name !== 'CONTENT_TYPE') { $name = $prefix.$name; } From 303c412bccfe1fcbfa17351d03291219fc3add28 Mon Sep 17 00:00:00 2001 From: Marcel Pociot Date: Fri, 27 May 2016 14:52:46 +0200 Subject: [PATCH 018/780] Update README.md --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 46794732..25214d79 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ Automatically generate your API documentation from your existing Laravel routes. Take a look at the [example documentation](http://marcelpociot.com/whiteboard/). -`php artisan api:gen --routePrefix=settings/api/*` +`php artisan api:gen --routePrefix="settings/api/*"` ![image](http://img.shields.io/packagist/v/mpociot/laravel-apidoc-generator.svg?style=flat) ![image](http://img.shields.io/packagist/l/mpociot/laravel-apidoc-generator.svg?style=flat) @@ -31,7 +31,7 @@ Mpociot\ApiDoc\ApiDocGeneratorServiceProvider::class, To generate your API documentation, use the `api:generate` artisan command. ```sh -$ php artisan api:generate --routePrefix=api/v1/* +$ php artisan api:generate --routePrefix="api/v1/*" ``` This command will scan your applications routes for the URIs matching `api/v1/*` and will parse these controller methods and form requests. From e819439ba92fbfd587dc9901e1ceb793777b0718 Mon Sep 17 00:00:00 2001 From: Marcel Pociot Date: Sat, 28 May 2016 23:41:18 +0200 Subject: [PATCH 019/780] Added Dingo support Added support for route model bindings Added grouping of controllers --- .../ApiDoc/Commands/GenerateDocumentation.php | 136 ++++++++++++++---- .../AbstractGenerator.php} | 110 +++++++------- .../ApiDoc/Generators/DingoGenerator.php | 60 ++++++++ .../ApiDoc/Generators/LaravelGenerator.php | 87 +++++++++++ src/resources/views/documentarian.blade.php | 15 +- tests/ApiDocGeneratorTest.php | 7 +- 6 files changed, 331 insertions(+), 84 deletions(-) rename src/Mpociot/ApiDoc/{ApiDocGenerator.php => Generators/AbstractGenerator.php} (87%) create mode 100644 src/Mpociot/ApiDoc/Generators/DingoGenerator.php create mode 100644 src/Mpociot/ApiDoc/Generators/LaravelGenerator.php diff --git a/src/Mpociot/ApiDoc/Commands/GenerateDocumentation.php b/src/Mpociot/ApiDoc/Commands/GenerateDocumentation.php index c5212d95..bb4faccd 100644 --- a/src/Mpociot/ApiDoc/Commands/GenerateDocumentation.php +++ b/src/Mpociot/ApiDoc/Commands/GenerateDocumentation.php @@ -3,8 +3,11 @@ namespace Mpociot\ApiDoc\Commands; use Illuminate\Console\Command; -use Mpociot\ApiDoc\ApiDocGenerator; +use Illuminate\Support\Collection; use Illuminate\Support\Facades\Route; +use Mpociot\ApiDoc\Generators\AbstractGenerator; +use Mpociot\ApiDoc\Generators\DingoGenerator; +use Mpociot\ApiDoc\Generators\LaravelGenerator; use Mpociot\Documentarian\Documentarian; class GenerateDocumentation extends Command @@ -19,6 +22,8 @@ class GenerateDocumentation extends Command {--routePrefix= : The route prefix to use for generation} {--routes=* : The route names to use for generation} {--actAsUserId= : The user ID to use for API response calls} + {--router=laravel : The router to be used (Laravel or Dingo)} + {--bindings= : Route Model Bindings} '; /** @@ -45,9 +50,16 @@ public function __construct() */ public function handle() { + if ($this->option('router') === 'laravel') { + $generator = new LaravelGenerator(); + } else { + $generator = new DingoGenerator(); + } + $allowedRoutes = $this->option('routes'); $routePrefix = $this->option('routePrefix'); - $actAs = $this->option('actAsUserId'); + + $this->setUserToBeImpersonated($this->option('actAsUserId')); if ($routePrefix === null && ! count($allowedRoutes)) { $this->error('You must provide either a route prefix or a route to generate the documentation.'); @@ -55,36 +67,18 @@ public function handle() return false; } - if ($actAs !== null) { - if (version_compare($this->laravel->version(), '5.2.0', '<')) { - $userModel = config('auth.model'); - $user = $userModel::find($actAs); - $this->laravel['auth']->setUser($user); - } else { - $userModel = config('auth.providers.users.model'); - $user = $userModel::find($actAs); - $this->laravel['auth']->guard()->setUser($user); - } - } - - $routes = Route::getRoutes(); - - $generator = new ApiDocGenerator(); - - /* @var \Illuminate\Routing\Route $route */ - $parsedRoutes = []; - foreach ($routes as $route) { - if (in_array($route->getName(), $allowedRoutes) || str_is($routePrefix, $route->getUri())) { - $parsedRoutes[] = $generator->processRoute($route); - $this->info('Processed route: '.$route->getUri()); - } + if ($this->option('router') === 'laravel') { + $parsedRoutes = $this->processLaravelRoutes($generator, $allowedRoutes, $routePrefix); + } else { + $parsedRoutes = $this->processDingoRoutes($generator, $allowedRoutes, $routePrefix); } + $parsedRoutes = collect($parsedRoutes)->sortBy('resource')->groupBy('resource'); $this->writeMarkdown($parsedRoutes); } /** - * @param array $parsedRoutes + * @param Collection $parsedRoutes * * @return void */ @@ -94,7 +88,7 @@ private function writeMarkdown($parsedRoutes) $documentarian = new Documentarian(); - $markdown = view('apidoc::documentarian')->with('parsedRoutes', $parsedRoutes); + $markdown = view('apidoc::documentarian')->with('parsedRoutes', $parsedRoutes->all()); if (! is_dir($outputPath)) { $documentarian->create($outputPath); @@ -110,4 +104,92 @@ private function writeMarkdown($parsedRoutes) $this->info('Wrote HTML documentation to: '.$outputPath.'/public/index.html'); } + + /** + * @return array + */ + private function getBindings() + { + $bindings = $this->option('bindings'); + if (empty($bindings)) { + return []; + } + $bindings = explode('|', $bindings); + $resultBindings = []; + foreach ($bindings as $binding) { + list($name, $id) = explode(',', $binding); + $resultBindings[$name] = $id; + } + return $resultBindings; + } + + /** + * @param $actAs + */ + private function setUserToBeImpersonated($actAs) + { + if (!empty($actAs)) { + if (version_compare($this->laravel->version(), '5.2.0', '<')) { + $userModel = config('auth.model'); + $user = $userModel::find($actAs); + $this->laravel['auth']->setUser($user); + } else { + $userModel = config('auth.providers.users.model'); + $user = $userModel::find($actAs); + $this->laravel['auth']->guard()->setUser($user); + } + } + } + + /** + * @return mixed + */ + private function getRoutes() + { + if ($this->option('router') === 'laravel') { + return Route::getRoutes(); + } else { + return app('Dingo\Api\Routing\Router')->getRoutes()[$this->option('routePrefix')]; + } + } + + /** + * @param AbstractGenerator $generator + * @param $allowedRoutes + * @param $routePrefix + * @return array + */ + private function processLaravelRoutes(AbstractGenerator $generator, $allowedRoutes, $routePrefix) + { + $routes = $this->getRoutes(); + $bindings = $this->getBindings(); + $parsedRoutes = []; + foreach ($routes as $route) { + if (in_array($route->getName(), $allowedRoutes) || str_is($routePrefix, $route->getUri())) { + $parsedRoutes[] = $generator->processRoute($route, $bindings); + $this->info('Processed route: '.$route->getUri()); + } + } + return $parsedRoutes; + } + + /** + * @param AbstractGenerator $generator + * @param $allowedRoutes + * @param $routePrefix + * @return array + */ + private function processDingoRoutes(AbstractGenerator $generator, $allowedRoutes, $routePrefix) + { + $routes = $this->getRoutes(); + $bindings = $this->getBindings(); + $parsedRoutes = []; + foreach ($routes as $route) { + if (empty($allowedRoutes) || in_array($route->getName(), $allowedRoutes) || str_is($routePrefix, $route->uri())) { + $parsedRoutes[] = $generator->processRoute($route, $bindings); + $this->info('Processed route: '.$route->uri()); + } + } + return $parsedRoutes; + } } diff --git a/src/Mpociot/ApiDoc/ApiDocGenerator.php b/src/Mpociot/ApiDoc/Generators/AbstractGenerator.php similarity index 87% rename from src/Mpociot/ApiDoc/ApiDocGenerator.php rename to src/Mpociot/ApiDoc/Generators/AbstractGenerator.php index 5a52430f..41760c57 100644 --- a/src/Mpociot/ApiDoc/ApiDocGenerator.php +++ b/src/Mpociot/ApiDoc/Generators/AbstractGenerator.php @@ -1,43 +1,38 @@ getAction(); - $response = $this->getRouteResponse($route); - $routeDescription = $this->getRouteDescription($routeAction['uses']); - if ($response->headers->get('Content-Type') === 'application/json') { - $content = json_encode(json_decode($response->getContent()), JSON_PRETTY_PRINT); - } else { - $content = $response->getContent(); - } - $routeData = [ - 'title' => $routeDescription['short'], - 'description' => $routeDescription['long'], - 'methods' => $route->getMethods(), - 'uri' => $route->getUri(), - 'parameters' => [], - 'response' => $content, - ]; + abstract public function processRoute(Route $route, $bindings = []); + /** + * @param array $routeData + * @param array $routeAction + * @return mixed + */ + protected function getParameters($routeData, $routeAction) { $validator = Validator::make([], $this->getRouteRules($routeAction['uses'])); foreach ($validator->getRules() as $attribute => $rules) { $attributeData = [ @@ -61,11 +56,28 @@ public function processRoute(Route $route) * * @return \Illuminate\Http\Response */ - private function getRouteResponse(Route $route) + protected function getRouteResponse(Route $route, $bindings) { + $uri = $this->addRouteModelBindings($route, $bindings); + $methods = $route->getMethods(); - return $this->callRoute(array_shift($methods), $route->getUri()); + return $this->callRoute(array_shift($methods), $uri); + } + + + /** + * @param Route $route + * @param array $bindings + * @return mixed + */ + protected function addRouteModelBindings(Route $route, $bindings) + { + $uri = $this->getUri($route); + foreach ($bindings as $model => $id) { + $uri = str_replace('{'.$model.'}', $id, $uri); + } + return $uri; } /** @@ -73,7 +85,7 @@ private function getRouteResponse(Route $route) * * @return string */ - private function getRouteDescription($route) + protected function getRouteDescription($route) { list($class, $method) = explode('@', $route); $reflection = new ReflectionClass($class); @@ -88,12 +100,32 @@ private function getRouteDescription($route) ]; } + /** - * @param \Illuminate\Routing\Route $route + * @param string $route + * + * @return string + */ + protected function getRouteGroup($route) + { + list($class, $method) = explode('@', $route); + $reflection = new ReflectionClass($class); + $comment = $reflection->getDocComment(); + $phpdoc = new DocBlock($comment); + foreach ($phpdoc->getTags() as $tag) { + if ($tag->getName() === 'resource') { + return $tag->getContent(); + } + } + return 'general'; + } + + /** + * @param $route * * @return array */ - private function getRouteRules($route) + protected function getRouteRules($route) { list($class, $method) = explode('@', $route); $reflection = new ReflectionClass($class); @@ -320,27 +352,7 @@ protected function parseRule($rule, &$attributeData) * * @return \Illuminate\Http\Response */ - public function callRoute($method, $uri, $parameters = [], $cookies = [], $files = [], $server = [], $content = null) - { - $kernel = App::make('Illuminate\Contracts\Http\Kernel'); - App::instance('middleware.disable', true); - - $server = [ - 'CONTENT_TYPE' => 'application/json', - 'Accept' => 'application/json', - ]; - - $request = Request::create( - $uri, $method, $parameters, - $cookies, $files, $this->transformHeadersToServerVars($server), $content - ); - - $response = $kernel->handle($request); - - $kernel->terminate($request, $response); - - return $response; - } + abstract public function callRoute($method, $uri, $parameters = [], $cookies = [], $files = [], $server = [], $content = null); /** * Transform headers array to array of $_SERVER vars with HTTP_* format. diff --git a/src/Mpociot/ApiDoc/Generators/DingoGenerator.php b/src/Mpociot/ApiDoc/Generators/DingoGenerator.php new file mode 100644 index 00000000..691f1cde --- /dev/null +++ b/src/Mpociot/ApiDoc/Generators/DingoGenerator.php @@ -0,0 +1,60 @@ +getRouteResponse($route, $bindings); + + $routeAction = $route->getAction(); + $routeGroup = $this->getRouteGroup($routeAction['uses']); + $routeDescription = $this->getRouteDescription($routeAction['uses']); + + if ($response->headers->get('Content-Type') === 'application/json') { + $content = json_encode(json_decode($response->getContent()), JSON_PRETTY_PRINT); + } else { + $content = $response->getContent(); + } + + return $this->getParameters([ + 'resource' => $routeGroup, + 'title' => $routeDescription['short'], + 'description' => $routeDescription['long'], + 'methods' => $route->getMethods(), + 'uri' => $route->getUri(), + 'parameters' => [], + 'response' => $content, + ], $routeAction); + } + + + /** + * {@inheritdoc} + */ + public function callRoute($method, $uri, $parameters = [], $cookies = [], $files = [], $server = [], $content = null) + { + return call_user_func_array([app('Dingo\Api\Dispatcher'), strtolower($method)], [$uri]); + } + + /** + * {@inheritdoc} + */ + protected function getUri(Route $route) + { + return $route->uri(); + } + +} \ No newline at end of file diff --git a/src/Mpociot/ApiDoc/Generators/LaravelGenerator.php b/src/Mpociot/ApiDoc/Generators/LaravelGenerator.php new file mode 100644 index 00000000..997c10d9 --- /dev/null +++ b/src/Mpociot/ApiDoc/Generators/LaravelGenerator.php @@ -0,0 +1,87 @@ +getUri(); + } + + /** + * @param \Illuminate\Routing\Route $route + * @param array $bindings + * + * @return array + */ + public function processRoute(Route $route, $bindings = []) + { + $response = $this->getRouteResponse($route, $bindings); + + $routeAction = $route->getAction(); + $routeGroup = $this->getRouteGroup($routeAction['uses']); + $routeDescription = $this->getRouteDescription($routeAction['uses']); + + if ($response->headers->get('Content-Type') === 'application/json') { + $content = json_encode(json_decode($response->getContent()), JSON_PRETTY_PRINT); + } else { + $content = $response->getContent(); + } + + return $this->getParameters([ + 'resource' => $routeGroup, + 'title' => $routeDescription['short'], + 'description' => $routeDescription['long'], + 'methods' => $route->getMethods(), + 'uri' => $route->getUri(), + 'parameters' => [], + 'response' => $content, + ], $routeAction); + } + + /** + * Call the given URI and return the Response. + * + * @param string $method + * @param string $uri + * @param array $parameters + * @param array $cookies + * @param array $files + * @param array $server + * @param string $content + * + * @return \Illuminate\Http\Response + */ + public function callRoute($method, $uri, $parameters = [], $cookies = [], $files = [], $server = [], $content = null) + { + $kernel = App::make('Illuminate\Contracts\Http\Kernel'); + App::instance('middleware.disable', true); + + $server = [ + 'CONTENT_TYPE' => 'application/json', + 'Accept' => 'application/json', + ]; + + $request = Request::create( + $uri, $method, $parameters, + $cookies, $files, $this->transformHeadersToServerVars($server), $content + ); + + $response = $kernel->handle($request); + + $kernel->terminate($request, $response); + + return $response; + } + +} \ No newline at end of file diff --git a/src/resources/views/documentarian.blade.php b/src/resources/views/documentarian.blade.php index af78011a..25f60dc6 100644 --- a/src/resources/views/documentarian.blade.php +++ b/src/resources/views/documentarian.blade.php @@ -18,7 +18,11 @@ Welcome to the generated API reference. # Available routes -@foreach($parsedRoutes as $parsedRoute) +@foreach($parsedRoutes as $group => $routes) +@if($group) +#{{$group}} +@endif +@foreach($routes as $parsedRoute) @if($parsedRoute['title'] != '')## {{ $parsedRoute['title']}} @else## {{$parsedRoute['uri']}} @endif @@ -59,11 +63,11 @@ ``` @if(in_array('GET',$parsedRoute['methods'])) - > Example response: +> Example response: - ```json - {!! $parsedRoute['response'] !!} - ``` +```json +{!! str_limit(json_encode(json_decode($parsedRoute['response']), JSON_PRETTY_PRINT)) !!} +``` @endif ### HTTP Request @@ -82,3 +86,4 @@ @endif @endforeach +@endforeach diff --git a/tests/ApiDocGeneratorTest.php b/tests/ApiDocGeneratorTest.php index 0ed9c382..1590e28c 100644 --- a/tests/ApiDocGeneratorTest.php +++ b/tests/ApiDocGeneratorTest.php @@ -3,8 +3,9 @@ namespace Mpociot\ApiDoc\Tests; use Illuminate\Routing\Route; +use Mpociot\ApiDoc\Generators\LaravelGenerator; use Orchestra\Testbench\TestCase; -use Mpociot\ApiDoc\ApiDocGenerator; +use Mpociot\ApiDoc\AbstractGenerator; use Mpociot\ApiDoc\Tests\Fixtures\TestRequest; use Mpociot\ApiDoc\Tests\Fixtures\TestController; use Illuminate\Support\Facades\Route as RouteFacade; @@ -12,7 +13,7 @@ class ApiDocGeneratorTest extends TestCase { /** - * @var \Mpociot\ApiDoc\ApiDocGenerator + * @var \Mpociot\ApiDoc\AbstractGenerator */ protected $generator; @@ -23,7 +24,7 @@ public function setUp() { parent::setUp(); - $this->generator = new ApiDocGenerator(); + $this->generator = new LaravelGenerator(); } public function testCanParseMethodDescription() From 3ba920c92c842ba4eb2228ce6acd856c842ff67a Mon Sep 17 00:00:00 2001 From: Marcel Pociot Date: Sat, 28 May 2016 17:43:06 -0400 Subject: [PATCH 020/780] Applied fixes from StyleCI --- src/Mpociot/ApiDoc/Commands/GenerateDocumentation.php | 7 ++++++- src/Mpociot/ApiDoc/Generators/AbstractGenerator.php | 11 +++++++---- src/Mpociot/ApiDoc/Generators/DingoGenerator.php | 10 +++------- src/Mpociot/ApiDoc/Generators/LaravelGenerator.php | 11 +++++------ tests/ApiDocGeneratorTest.php | 1 - 5 files changed, 21 insertions(+), 19 deletions(-) diff --git a/src/Mpociot/ApiDoc/Commands/GenerateDocumentation.php b/src/Mpociot/ApiDoc/Commands/GenerateDocumentation.php index bb4faccd..63e0da8c 100644 --- a/src/Mpociot/ApiDoc/Commands/GenerateDocumentation.php +++ b/src/Mpociot/ApiDoc/Commands/GenerateDocumentation.php @@ -120,6 +120,7 @@ private function getBindings() list($name, $id) = explode(',', $binding); $resultBindings[$name] = $id; } + return $resultBindings; } @@ -128,7 +129,7 @@ private function getBindings() */ private function setUserToBeImpersonated($actAs) { - if (!empty($actAs)) { + if (! empty($actAs)) { if (version_compare($this->laravel->version(), '5.2.0', '<')) { $userModel = config('auth.model'); $user = $userModel::find($actAs); @@ -157,6 +158,7 @@ private function getRoutes() * @param AbstractGenerator $generator * @param $allowedRoutes * @param $routePrefix + * * @return array */ private function processLaravelRoutes(AbstractGenerator $generator, $allowedRoutes, $routePrefix) @@ -170,6 +172,7 @@ private function processLaravelRoutes(AbstractGenerator $generator, $allowedRout $this->info('Processed route: '.$route->getUri()); } } + return $parsedRoutes; } @@ -177,6 +180,7 @@ private function processLaravelRoutes(AbstractGenerator $generator, $allowedRout * @param AbstractGenerator $generator * @param $allowedRoutes * @param $routePrefix + * * @return array */ private function processDingoRoutes(AbstractGenerator $generator, $allowedRoutes, $routePrefix) @@ -190,6 +194,7 @@ private function processDingoRoutes(AbstractGenerator $generator, $allowedRoutes $this->info('Processed route: '.$route->uri()); } } + return $parsedRoutes; } } diff --git a/src/Mpociot/ApiDoc/Generators/AbstractGenerator.php b/src/Mpociot/ApiDoc/Generators/AbstractGenerator.php index 03100808..718751c0 100644 --- a/src/Mpociot/ApiDoc/Generators/AbstractGenerator.php +++ b/src/Mpociot/ApiDoc/Generators/AbstractGenerator.php @@ -12,9 +12,9 @@ abstract class AbstractGenerator { - /** * @param Route $route + * * @return mixed */ abstract protected function getUri(Route $route); @@ -30,9 +30,11 @@ abstract public function processRoute(Route $route, $bindings = []); /** * @param array $routeData * @param array $routeAction + * * @return mixed */ - protected function getParameters($routeData, $routeAction) { + protected function getParameters($routeData, $routeAction) + { $validator = Validator::make([], $this->getRouteRules($routeAction['uses'])); foreach ($validator->getRules() as $attribute => $rules) { $attributeData = [ @@ -65,10 +67,10 @@ protected function getRouteResponse(Route $route, $bindings) return $this->callRoute(array_shift($methods), $uri); } - /** * @param Route $route * @param array $bindings + * * @return mixed */ protected function addRouteModelBindings(Route $route, $bindings) @@ -77,6 +79,7 @@ protected function addRouteModelBindings(Route $route, $bindings) foreach ($bindings as $model => $id) { $uri = str_replace('{'.$model.'}', $id, $uri); } + return $uri; } @@ -100,7 +103,6 @@ protected function getRouteDescription($route) ]; } - /** * @param string $route * @@ -117,6 +119,7 @@ protected function getRouteGroup($route) return $tag->getContent(); } } + return 'general'; } diff --git a/src/Mpociot/ApiDoc/Generators/DingoGenerator.php b/src/Mpociot/ApiDoc/Generators/DingoGenerator.php index 691f1cde..938ae422 100644 --- a/src/Mpociot/ApiDoc/Generators/DingoGenerator.php +++ b/src/Mpociot/ApiDoc/Generators/DingoGenerator.php @@ -4,11 +4,9 @@ use Illuminate\Routing\Route; use Illuminate\Support\Facades\App; -use Illuminate\Support\Facades\Request; class DingoGenerator extends AbstractGenerator { - /** * @param \Illuminate\Routing\Route $route * @param array $bindings @@ -22,7 +20,7 @@ public function processRoute(Route $route, $bindings = []) $routeAction = $route->getAction(); $routeGroup = $this->getRouteGroup($routeAction['uses']); $routeDescription = $this->getRouteDescription($routeAction['uses']); - + if ($response->headers->get('Content-Type') === 'application/json') { $content = json_encode(json_decode($response->getContent()), JSON_PRETTY_PRINT); } else { @@ -30,7 +28,7 @@ public function processRoute(Route $route, $bindings = []) } return $this->getParameters([ - 'resource' => $routeGroup, + 'resource' => $routeGroup, 'title' => $routeDescription['short'], 'description' => $routeDescription['long'], 'methods' => $route->getMethods(), @@ -40,7 +38,6 @@ public function processRoute(Route $route, $bindings = []) ], $routeAction); } - /** * {@inheritdoc} */ @@ -56,5 +53,4 @@ protected function getUri(Route $route) { return $route->uri(); } - -} \ No newline at end of file +} diff --git a/src/Mpociot/ApiDoc/Generators/LaravelGenerator.php b/src/Mpociot/ApiDoc/Generators/LaravelGenerator.php index 997c10d9..c4b40307 100644 --- a/src/Mpociot/ApiDoc/Generators/LaravelGenerator.php +++ b/src/Mpociot/ApiDoc/Generators/LaravelGenerator.php @@ -8,9 +8,9 @@ class LaravelGenerator extends AbstractGenerator { - /** * @param Route $route + * * @return mixed */ protected function getUri(Route $route) @@ -27,11 +27,11 @@ protected function getUri(Route $route) public function processRoute(Route $route, $bindings = []) { $response = $this->getRouteResponse($route, $bindings); - + $routeAction = $route->getAction(); $routeGroup = $this->getRouteGroup($routeAction['uses']); $routeDescription = $this->getRouteDescription($routeAction['uses']); - + if ($response->headers->get('Content-Type') === 'application/json') { $content = json_encode(json_decode($response->getContent()), JSON_PRETTY_PRINT); } else { @@ -39,7 +39,7 @@ public function processRoute(Route $route, $bindings = []) } return $this->getParameters([ - 'resource' => $routeGroup, + 'resource' => $routeGroup, 'title' => $routeDescription['short'], 'description' => $routeDescription['long'], 'methods' => $route->getMethods(), @@ -83,5 +83,4 @@ public function callRoute($method, $uri, $parameters = [], $cookies = [], $files return $response; } - -} \ No newline at end of file +} diff --git a/tests/ApiDocGeneratorTest.php b/tests/ApiDocGeneratorTest.php index 1590e28c..07f297b5 100644 --- a/tests/ApiDocGeneratorTest.php +++ b/tests/ApiDocGeneratorTest.php @@ -5,7 +5,6 @@ use Illuminate\Routing\Route; use Mpociot\ApiDoc\Generators\LaravelGenerator; use Orchestra\Testbench\TestCase; -use Mpociot\ApiDoc\AbstractGenerator; use Mpociot\ApiDoc\Tests\Fixtures\TestRequest; use Mpociot\ApiDoc\Tests\Fixtures\TestController; use Illuminate\Support\Facades\Route as RouteFacade; From 130806352216cc23202fe9ba99c4c65f9aabc3df Mon Sep 17 00:00:00 2001 From: Marcel Pociot Date: Sun, 29 May 2016 00:03:27 +0200 Subject: [PATCH 021/780] update readme --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index 25214d79..c3ca8281 100644 --- a/README.md +++ b/README.md @@ -43,7 +43,10 @@ Option | Description `output` | The output path used for the generated documentation. Default: `public/docs` `routePrefix` | The route prefix to use for generation - `*` can be used as a wildcard `routes` | The route names to use for generation - Required if no routePrefix is provided +`routes` | The route names to use for generation - Required if no routePrefix is provided `actAsUserId` | The user ID to use for authenticated API response calls +`router` | The router to use, when processing the route files (can be Laravel or Dingo - defaults to Laravel) +`bindings` | List of route bindings that should be replaced when trying to retrieve route results. Syntax format: `binding_one,id|binding_two,id` ### How does it work? From 097525f43baf8395fc8d1c633958e052cff805d8 Mon Sep 17 00:00:00 2001 From: Marcel Pociot Date: Sun, 29 May 2016 14:49:09 +0200 Subject: [PATCH 022/780] Update DingoGenerator.php --- src/Mpociot/ApiDoc/Generators/DingoGenerator.php | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/Mpociot/ApiDoc/Generators/DingoGenerator.php b/src/Mpociot/ApiDoc/Generators/DingoGenerator.php index 938ae422..41f4f515 100644 --- a/src/Mpociot/ApiDoc/Generators/DingoGenerator.php +++ b/src/Mpociot/ApiDoc/Generators/DingoGenerator.php @@ -2,18 +2,17 @@ namespace Mpociot\ApiDoc\Generators; -use Illuminate\Routing\Route; use Illuminate\Support\Facades\App; class DingoGenerator extends AbstractGenerator { /** - * @param \Illuminate\Routing\Route $route + * @param $route * @param array $bindings * * @return array */ - public function processRoute(Route $route, $bindings = []) + public function processRoute($route, $bindings = []) { $response = $this->getRouteResponse($route, $bindings); @@ -49,7 +48,7 @@ public function callRoute($method, $uri, $parameters = [], $cookies = [], $files /** * {@inheritdoc} */ - protected function getUri(Route $route) + protected function getUri($route) { return $route->uri(); } From bf2c3a1fac014f89116bad1e83e9ab50687bb2bc Mon Sep 17 00:00:00 2001 From: Marcel Pociot Date: Mon, 30 May 2016 10:47:54 +0200 Subject: [PATCH 023/780] Added Dingo tests --- composer.json | 3 +- .../ApiDoc/Generators/AbstractGenerator.php | 14 +- .../ApiDoc/Generators/DingoGenerator.php | 19 +- .../ApiDoc/Generators/LaravelGenerator.php | 4 +- tests/DingoGeneratorTest.php | 318 ++++++++++++++++++ tests/Fixtures/DingoTestController.php | 29 ++ tests/Fixtures/DingoTestRequest.php | 52 +++ 7 files changed, 418 insertions(+), 21 deletions(-) create mode 100644 tests/DingoGeneratorTest.php create mode 100644 tests/Fixtures/DingoTestController.php create mode 100644 tests/Fixtures/DingoTestRequest.php diff --git a/composer.json b/composer.json index 86f8f9cc..d02f5b69 100644 --- a/composer.json +++ b/composer.json @@ -23,7 +23,8 @@ }, "require-dev": { "orchestra/testbench": "~3.0", - "phpunit/phpunit": "~4.0 || ~5.0" + "phpunit/phpunit": "~4.0 || ~5.0", + "dingo/api": "1.0.*@dev" }, "autoload": { "psr-0": { diff --git a/src/Mpociot/ApiDoc/Generators/AbstractGenerator.php b/src/Mpociot/ApiDoc/Generators/AbstractGenerator.php index 718751c0..88e2a944 100644 --- a/src/Mpociot/ApiDoc/Generators/AbstractGenerator.php +++ b/src/Mpociot/ApiDoc/Generators/AbstractGenerator.php @@ -13,11 +13,11 @@ abstract class AbstractGenerator { /** - * @param Route $route + * @param $route * * @return mixed */ - abstract protected function getUri(Route $route); + abstract protected function getUri($route); /** * @param \Illuminate\Routing\Route $route @@ -25,7 +25,7 @@ abstract protected function getUri(Route $route); * * @return array */ - abstract public function processRoute(Route $route, $bindings = []); + abstract public function processRoute($route, $bindings = []); /** * @param array $routeData @@ -54,11 +54,11 @@ protected function getParameters($routeData, $routeAction) } /** - * @param \Illuminate\Routing\Route $route + * @param $route * * @return \Illuminate\Http\Response */ - protected function getRouteResponse(Route $route, $bindings) + protected function getRouteResponse($route, $bindings) { $uri = $this->addRouteModelBindings($route, $bindings); @@ -68,12 +68,12 @@ protected function getRouteResponse(Route $route, $bindings) } /** - * @param Route $route + * @param $route * @param array $bindings * * @return mixed */ - protected function addRouteModelBindings(Route $route, $bindings) + protected function addRouteModelBindings($route, $bindings) { $uri = $this->getUri($route); foreach ($bindings as $model => $id) { diff --git a/src/Mpociot/ApiDoc/Generators/DingoGenerator.php b/src/Mpociot/ApiDoc/Generators/DingoGenerator.php index 41f4f515..6af0c09e 100644 --- a/src/Mpociot/ApiDoc/Generators/DingoGenerator.php +++ b/src/Mpociot/ApiDoc/Generators/DingoGenerator.php @@ -2,7 +2,7 @@ namespace Mpociot\ApiDoc\Generators; -use Illuminate\Support\Facades\App; +use Exception; class DingoGenerator extends AbstractGenerator { @@ -14,26 +14,23 @@ class DingoGenerator extends AbstractGenerator */ public function processRoute($route, $bindings = []) { - $response = $this->getRouteResponse($route, $bindings); - + try { + $response = $this->getRouteResponse($route, $bindings); + } catch( Exception $e){ + $response = ''; + } $routeAction = $route->getAction(); $routeGroup = $this->getRouteGroup($routeAction['uses']); $routeDescription = $this->getRouteDescription($routeAction['uses']); - if ($response->headers->get('Content-Type') === 'application/json') { - $content = json_encode(json_decode($response->getContent()), JSON_PRETTY_PRINT); - } else { - $content = $response->getContent(); - } - return $this->getParameters([ 'resource' => $routeGroup, 'title' => $routeDescription['short'], 'description' => $routeDescription['long'], 'methods' => $route->getMethods(), - 'uri' => $route->getUri(), + 'uri' => $route->uri(), 'parameters' => [], - 'response' => $content, + 'response' => $response, ], $routeAction); } diff --git a/src/Mpociot/ApiDoc/Generators/LaravelGenerator.php b/src/Mpociot/ApiDoc/Generators/LaravelGenerator.php index c4b40307..48afbd77 100644 --- a/src/Mpociot/ApiDoc/Generators/LaravelGenerator.php +++ b/src/Mpociot/ApiDoc/Generators/LaravelGenerator.php @@ -13,7 +13,7 @@ class LaravelGenerator extends AbstractGenerator * * @return mixed */ - protected function getUri(Route $route) + protected function getUri($route) { return $route->getUri(); } @@ -24,7 +24,7 @@ protected function getUri(Route $route) * * @return array */ - public function processRoute(Route $route, $bindings = []) + public function processRoute($route, $bindings = []) { $response = $this->getRouteResponse($route, $bindings); diff --git a/tests/DingoGeneratorTest.php b/tests/DingoGeneratorTest.php new file mode 100644 index 00000000..227273d6 --- /dev/null +++ b/tests/DingoGeneratorTest.php @@ -0,0 +1,318 @@ +generator = new DingoGenerator(); + } + + public function testCanParseMethodDescription() + { + $api = app('Dingo\Api\Routing\Router'); + $api->version('v1', function ($api) { + $api->get('/api/test', TestController::class.'@parseMethodDescription'); + }); + $route = app('Dingo\Api\Routing\Router')->getRoutes()['v1']->getRoutes()[0]; + + $parsed = $this->generator->processRoute($route); + + $this->assertSame('Example title.', $parsed['title']); + $this->assertSame("This will be the long description.\nIt can also be multiple lines long.", $parsed['description']); + } + + public function testCanParseRouteMethods() + { + $api = app('Dingo\Api\Routing\Router'); + $api->version('v1', function ($api) { + $api->get('/get', TestController::class.'@dummy'); + $api->post('/post', TestController::class.'@dummy'); + $api->put('/put', TestController::class.'@dummy'); + $api->delete('/delete', TestController::class.'@dummy'); + }); + $route = app('Dingo\Api\Routing\Router')->getRoutes()['v1']->getRoutes()[0]; + $parsed = $this->generator->processRoute($route); + $this->assertSame(['GET', 'HEAD'], $parsed['methods']); + + $route = app('Dingo\Api\Routing\Router')->getRoutes()['v1']->getRoutes()[1]; + $parsed = $this->generator->processRoute($route); + $this->assertSame(['POST'], $parsed['methods']); + + $route = app('Dingo\Api\Routing\Router')->getRoutes()['v1']->getRoutes()[2]; + $parsed = $this->generator->processRoute($route); + $this->assertSame(['PUT'], $parsed['methods']); + + $route = app('Dingo\Api\Routing\Router')->getRoutes()['v1']->getRoutes()[3]; + $parsed = $this->generator->processRoute($route); + $this->assertSame(['DELETE'], $parsed['methods']); + } + + public function testCanParseFormRequestRules() + { + $api = app('Dingo\Api\Routing\Router'); + $api->version('v1', function ($api) { + $api->post('/post', DingoTestController::class.'@parseFormRequestRules'); + }); + + $route = app('Dingo\Api\Routing\Router')->getRoutes()['v1']->getRoutes()[0]; + $parsed = $this->generator->processRoute($route); + $parameters = $parsed['parameters']; + + $testRequest = new TestRequest(); + $rules = $testRequest->rules(); + + foreach ($rules as $name => $rule) { + $attribute = $parameters[$name]; + + switch ($name) { + + case 'required': + $this->assertTrue($attribute['required']); + $this->assertSame('string', $attribute['type']); + $this->assertCount(0, $attribute['description']); + break; + case 'accepted': + $this->assertTrue($attribute['required']); + $this->assertSame('boolean', $attribute['type']); + $this->assertCount(0, $attribute['description']); + break; + case 'active_url': + $this->assertFalse($attribute['required']); + $this->assertSame('url', $attribute['type']); + $this->assertCount(0, $attribute['description']); + break; + case 'alpha': + $this->assertFalse($attribute['required']); + $this->assertSame('string', $attribute['type']); + $this->assertCount(1, $attribute['description']); + $this->assertSame('Only alphabetic characters allowed', $attribute['description'][0]); + break; + case 'alpha_dash': + $this->assertFalse($attribute['required']); + $this->assertSame('string', $attribute['type']); + $this->assertCount(1, $attribute['description']); + $this->assertSame('Allowed: alpha-numeric characters, as well as dashes and underscores.', $attribute['description'][0]); + break; + case 'alpha_num': + $this->assertFalse($attribute['required']); + $this->assertSame('string', $attribute['type']); + $this->assertCount(1, $attribute['description']); + $this->assertSame('Only alpha-numeric characters allowed', $attribute['description'][0]); + break; + case 'array': + $this->assertFalse($attribute['required']); + $this->assertSame('array', $attribute['type']); + $this->assertCount(0, $attribute['description']); + break; + case 'between': + $this->assertFalse($attribute['required']); + $this->assertSame('numeric', $attribute['type']); + $this->assertCount(1, $attribute['description']); + $this->assertSame('Between: `5` and `200`', $attribute['description'][0]); + break; + case 'before': + $this->assertFalse($attribute['required']); + $this->assertSame('date', $attribute['type']); + $this->assertCount(1, $attribute['description']); + $this->assertSame('Must be a date preceding: `Saturday, 23-Apr-16 14:31:00 UTC`', $attribute['description'][0]); + break; + case 'boolean': + $this->assertFalse($attribute['required']); + $this->assertSame('boolean', $attribute['type']); + $this->assertCount(0, $attribute['description']); + break; + case 'date': + $this->assertFalse($attribute['required']); + $this->assertSame('date', $attribute['type']); + $this->assertCount(0, $attribute['description']); + break; + case 'date_format': + $this->assertFalse($attribute['required']); + $this->assertSame('date', $attribute['type']); + $this->assertCount(1, $attribute['description']); + $this->assertSame('Date format: `j.n.Y H:iP`', $attribute['description'][0]); + break; + case 'different': + $this->assertFalse($attribute['required']); + $this->assertSame('string', $attribute['type']); + $this->assertCount(1, $attribute['description']); + $this->assertSame('Must have a different value than parameter: `alpha_num`', $attribute['description'][0]); + break; + case 'digits': + $this->assertFalse($attribute['required']); + $this->assertSame('numeric', $attribute['type']); + $this->assertCount(1, $attribute['description']); + $this->assertSame('Must have an exact length of `2`', $attribute['description'][0]); + break; + case 'digits_between': + $this->assertFalse($attribute['required']); + $this->assertSame('numeric', $attribute['type']); + $this->assertCount(1, $attribute['description']); + $this->assertSame('Must have a length between `2` and `10`', $attribute['description'][0]); + break; + case 'email': + $this->assertFalse($attribute['required']); + $this->assertSame('email', $attribute['type']); + $this->assertCount(0, $attribute['description']); + break; + case 'exists': + $this->assertFalse($attribute['required']); + $this->assertSame('string', $attribute['type']); + $this->assertCount(1, $attribute['description']); + $this->assertSame('Valid user firstname', $attribute['description'][0]); + break; + case 'image': + $this->assertFalse($attribute['required']); + $this->assertSame('image', $attribute['type']); + $this->assertCount(1, $attribute['description']); + $this->assertSame('Must be an image (jpeg, png, bmp, gif, or svg)', $attribute['description'][0]); + break; + case 'in': + $this->assertFalse($attribute['required']); + $this->assertSame('string', $attribute['type']); + $this->assertCount(1, $attribute['description']); + $this->assertSame('`jpeg`, `png`, `bmp`, `gif` or `svg`', $attribute['description'][0]); + break; + case 'integer': + $this->assertFalse($attribute['required']); + $this->assertSame('integer', $attribute['type']); + $this->assertCount(0, $attribute['description']); + break; + case 'ip': + $this->assertFalse($attribute['required']); + $this->assertSame('ip', $attribute['type']); + $this->assertCount(0, $attribute['description']); + break; + case 'json': + $this->assertFalse($attribute['required']); + $this->assertSame('string', $attribute['type']); + $this->assertCount(1, $attribute['description']); + $this->assertSame('Must be a valid JSON string.', $attribute['description'][0]); + break; + case 'max': + $this->assertFalse($attribute['required']); + $this->assertSame('string', $attribute['type']); + $this->assertCount(1, $attribute['description']); + $this->assertSame('Maximum: `10`', $attribute['description'][0]); + break; + case 'min': + $this->assertFalse($attribute['required']); + $this->assertSame('string', $attribute['type']); + $this->assertCount(1, $attribute['description']); + $this->assertSame('Minimum: `20`', $attribute['description'][0]); + break; + case 'mimes': + $this->assertFalse($attribute['required']); + $this->assertSame('string', $attribute['type']); + $this->assertCount(1, $attribute['description']); + $this->assertSame('Allowed mime types: `jpeg`, `bmp` or `png`', $attribute['description'][0]); + break; + case 'not_in': + $this->assertFalse($attribute['required']); + $this->assertSame('string', $attribute['type']); + $this->assertCount(1, $attribute['description']); + $this->assertSame('Not in: `foo` or `bar`', $attribute['description'][0]); + break; + case 'numeric': + $this->assertFalse($attribute['required']); + $this->assertSame('numeric', $attribute['type']); + $this->assertCount(0, $attribute['description']); + break; + case 'regex': + $this->assertFalse($attribute['required']); + $this->assertSame('string', $attribute['type']); + $this->assertCount(1, $attribute['description']); + $this->assertSame('Must match this regular expression: `(.*)`', $attribute['description'][0]); + break; + case 'required_if': + $this->assertFalse($attribute['required']); + $this->assertSame('string', $attribute['type']); + $this->assertCount(1, $attribute['description']); + $this->assertSame('Required if `foo` is `bar`', $attribute['description'][0]); + break; + case 'required_unless': + $this->assertFalse($attribute['required']); + $this->assertSame('string', $attribute['type']); + $this->assertCount(1, $attribute['description']); + $this->assertSame('Required unless `foo` is `bar`', $attribute['description'][0]); + break; + case 'required_with': + $this->assertFalse($attribute['required']); + $this->assertSame('string', $attribute['type']); + $this->assertCount(1, $attribute['description']); + $this->assertSame('Required if the parameters `foo`, `bar` or `baz` are present.', $attribute['description'][0]); + break; + case 'required_with_all': + $this->assertFalse($attribute['required']); + $this->assertSame('string', $attribute['type']); + $this->assertCount(1, $attribute['description']); + $this->assertSame('Required if the parameters `foo`, `bar` and `baz` are present.', $attribute['description'][0]); + break; + case 'required_without': + $this->assertFalse($attribute['required']); + $this->assertSame('string', $attribute['type']); + $this->assertCount(1, $attribute['description']); + $this->assertSame('Required if the parameters `foo`, `bar` or `baz` are not present.', $attribute['description'][0]); + break; + case 'required_without_all': + $this->assertFalse($attribute['required']); + $this->assertSame('string', $attribute['type']); + $this->assertCount(1, $attribute['description']); + $this->assertSame('Required if the parameters `foo`, `bar` and `baz` are not present.', $attribute['description'][0]); + break; + case 'same': + $this->assertFalse($attribute['required']); + $this->assertSame('string', $attribute['type']); + $this->assertCount(1, $attribute['description']); + $this->assertSame('Must be the same as `foo`', $attribute['description'][0]); + break; + case 'size': + $this->assertFalse($attribute['required']); + $this->assertSame('string', $attribute['type']); + $this->assertCount(1, $attribute['description']); + $this->assertSame('Must have the size of `51`', $attribute['description'][0]); + break; + case 'timezone': + $this->assertFalse($attribute['required']); + $this->assertSame('string', $attribute['type']); + $this->assertCount(1, $attribute['description']); + $this->assertSame('Must be a valid timezone identifier', $attribute['description'][0]); + break; + case 'url': + $this->assertFalse($attribute['required']); + $this->assertSame('url', $attribute['type']); + $this->assertCount(0, $attribute['description']); + break; + + } + } + } +} diff --git a/tests/Fixtures/DingoTestController.php b/tests/Fixtures/DingoTestController.php new file mode 100644 index 00000000..d408df8b --- /dev/null +++ b/tests/Fixtures/DingoTestController.php @@ -0,0 +1,29 @@ + 'required', + 'accepted' => 'accepted', + 'after' => 'after:2016-04-23 14:31:00', + 'active_url' => 'active_url', + 'alpha' => 'alpha', + 'alpha_dash' => 'alpha_dash', + 'alpha_num' => 'alpha_num', + 'array' => 'array', + 'before' => 'before:2016-04-23 14:31:00', + 'between' => 'between:5,200', + 'boolean' => 'boolean', + 'date' => 'date', + 'date_format' => 'date_format:j.n.Y H:iP', + 'different' => 'different:alpha_num', + 'digits' => 'digits:2', + 'digits_between' => 'digits_between:2,10', + 'exists' => 'exists:users,firstname', + 'in' => 'in:jpeg,png,bmp,gif,svg', + 'integer' => 'integer', + 'ip' => 'ip', + 'json' => 'json', + 'min' => 'min:20', + 'max' => 'max:10', + 'mimes' => 'mimes:jpeg,bmp,png', + 'not_in' => 'not_in:foo,bar', + 'numeric' => 'numeric', + 'regex' => 'regex:(.*)', + 'required_if' => 'required_if:foo,bar', + 'required_unless' => 'required_unless:foo,bar', + 'required_with' => 'required_with:foo,bar,baz', + 'required_with_all' => 'required_with_all:foo,bar,baz', + 'required_without' => 'required_without:foo,bar,baz', + 'required_without_all' => 'required_without_all:foo,bar,baz', + 'same' => 'same:foo', + 'size' => 'size:51', + 'timezone' => 'timezone', + 'url' => 'url', + ]; + } +} From 16beb5c4d0b9045d08502876be5f56bf0587fd94 Mon Sep 17 00:00:00 2001 From: Marcel Pociot Date: Mon, 30 May 2016 04:50:38 -0400 Subject: [PATCH 024/780] Applied fixes from StyleCI --- src/Mpociot/ApiDoc/Generators/AbstractGenerator.php | 1 - src/Mpociot/ApiDoc/Generators/DingoGenerator.php | 2 +- tests/DingoGeneratorTest.php | 4 +--- tests/Fixtures/DingoTestController.php | 1 - tests/Fixtures/DingoTestRequest.php | 1 - 5 files changed, 2 insertions(+), 7 deletions(-) diff --git a/src/Mpociot/ApiDoc/Generators/AbstractGenerator.php b/src/Mpociot/ApiDoc/Generators/AbstractGenerator.php index 88e2a944..f377ce3e 100644 --- a/src/Mpociot/ApiDoc/Generators/AbstractGenerator.php +++ b/src/Mpociot/ApiDoc/Generators/AbstractGenerator.php @@ -5,7 +5,6 @@ use Faker\Factory; use ReflectionClass; use Illuminate\Support\Str; -use Illuminate\Routing\Route; use phpDocumentor\Reflection\DocBlock; use Illuminate\Support\Facades\Validator; use Illuminate\Foundation\Http\FormRequest; diff --git a/src/Mpociot/ApiDoc/Generators/DingoGenerator.php b/src/Mpociot/ApiDoc/Generators/DingoGenerator.php index 6af0c09e..92d017ea 100644 --- a/src/Mpociot/ApiDoc/Generators/DingoGenerator.php +++ b/src/Mpociot/ApiDoc/Generators/DingoGenerator.php @@ -16,7 +16,7 @@ public function processRoute($route, $bindings = []) { try { $response = $this->getRouteResponse($route, $bindings); - } catch( Exception $e){ + } catch (Exception $e) { $response = ''; } $routeAction = $route->getAction(); diff --git a/tests/DingoGeneratorTest.php b/tests/DingoGeneratorTest.php index 227273d6..2b7a8e71 100644 --- a/tests/DingoGeneratorTest.php +++ b/tests/DingoGeneratorTest.php @@ -3,13 +3,11 @@ namespace Mpociot\ApiDoc\Tests; use Dingo\Api\Provider\LaravelServiceProvider; -use Illuminate\Routing\Route; use Mpociot\ApiDoc\Tests\Fixtures\DingoTestController; use Orchestra\Testbench\TestCase; use Mpociot\ApiDoc\Generators\DingoGenerator; use Mpociot\ApiDoc\Tests\Fixtures\TestRequest; use Mpociot\ApiDoc\Tests\Fixtures\TestController; -use Illuminate\Support\Facades\Route as RouteFacade; class DingoGeneratorTest extends TestCase { @@ -21,7 +19,7 @@ class DingoGeneratorTest extends TestCase protected function getPackageProviders($app) { return [ - LaravelServiceProvider::class + LaravelServiceProvider::class, ]; } diff --git a/tests/Fixtures/DingoTestController.php b/tests/Fixtures/DingoTestController.php index d408df8b..63099d28 100644 --- a/tests/Fixtures/DingoTestController.php +++ b/tests/Fixtures/DingoTestController.php @@ -6,7 +6,6 @@ class DingoTestController extends Controller { - public function dummy() { return ''; diff --git a/tests/Fixtures/DingoTestRequest.php b/tests/Fixtures/DingoTestRequest.php index 284d34bb..c4e7b517 100644 --- a/tests/Fixtures/DingoTestRequest.php +++ b/tests/Fixtures/DingoTestRequest.php @@ -6,7 +6,6 @@ class DingoTestRequest extends FormRequest { - public function rules() { return [ From 5ef2f708f582b401cd3aa1525a0ef82c558f1c3d Mon Sep 17 00:00:00 2001 From: bdgold Date: Tue, 31 May 2016 07:41:32 -0400 Subject: [PATCH 025/780] Update README.md Removes duplicate 'routes' line in "Available command options". --- README.md | 1 - 1 file changed, 1 deletion(-) diff --git a/README.md b/README.md index c3ca8281..5d86bb23 100644 --- a/README.md +++ b/README.md @@ -43,7 +43,6 @@ Option | Description `output` | The output path used for the generated documentation. Default: `public/docs` `routePrefix` | The route prefix to use for generation - `*` can be used as a wildcard `routes` | The route names to use for generation - Required if no routePrefix is provided -`routes` | The route names to use for generation - Required if no routePrefix is provided `actAsUserId` | The user ID to use for authenticated API response calls `router` | The router to use, when processing the route files (can be Laravel or Dingo - defaults to Laravel) `bindings` | List of route bindings that should be replaced when trying to retrieve route results. Syntax format: `binding_one,id|binding_two,id` From 8d3171a2573bef0f327dda42a8cee11da21e2567 Mon Sep 17 00:00:00 2001 From: Marcel Pociot Date: Mon, 6 Jun 2016 13:06:02 +0200 Subject: [PATCH 026/780] Added tests to ensure that the route is no closure - fixes #30 --- .../ApiDoc/Commands/GenerateDocumentation.php | 17 ++++- tests/GenerateDocumentationTest.php | 70 +++++++++++++++++++ 2 files changed, 85 insertions(+), 2 deletions(-) create mode 100644 tests/GenerateDocumentationTest.php diff --git a/src/Mpociot/ApiDoc/Commands/GenerateDocumentation.php b/src/Mpociot/ApiDoc/Commands/GenerateDocumentation.php index 63e0da8c..f22227d9 100644 --- a/src/Mpociot/ApiDoc/Commands/GenerateDocumentation.php +++ b/src/Mpociot/ApiDoc/Commands/GenerateDocumentation.php @@ -168,8 +168,12 @@ private function processLaravelRoutes(AbstractGenerator $generator, $allowedRout $parsedRoutes = []; foreach ($routes as $route) { if (in_array($route->getName(), $allowedRoutes) || str_is($routePrefix, $route->getUri())) { - $parsedRoutes[] = $generator->processRoute($route, $bindings); - $this->info('Processed route: '.$route->getUri()); + if ($this->isValidRoute($route)) { + $parsedRoutes[] = $generator->processRoute($route, $bindings); + $this->info('Processed route: '.$route->getUri()); + } else { + $this->warn('Skipping route: '.$route->getUri().' - contains closure.'); + } } } @@ -197,4 +201,13 @@ private function processDingoRoutes(AbstractGenerator $generator, $allowedRoutes return $parsedRoutes; } + + /** + * @param $route + * @return bool + */ + private function isValidRoute($route) + { + return !is_callable($route->getAction()['uses']); + } } diff --git a/tests/GenerateDocumentationTest.php b/tests/GenerateDocumentationTest.php new file mode 100644 index 00000000..3ae3ed76 --- /dev/null +++ b/tests/GenerateDocumentationTest.php @@ -0,0 +1,70 @@ +generator = new LaravelGenerator(); + } + + /** + * @param \Illuminate\Foundation\Application $app + * @return array + */ + protected function getPackageProviders($app) + { + return [ApiDocGeneratorServiceProvider::class]; + } + + public function testConsoleCommandNeedsAPrefixOrRoute() + { + $output = $this->artisan('api:generate'); + $this->assertEquals('You must provide either a route prefix or a route to generate the documentation.'.PHP_EOL, $output); + } + + public function testConsoleCommandDoesNotWorkWithClosure() + { + RouteFacade::get('/api/closure', function(){ + return 'foo'; + }); + RouteFacade::get('/api/test', TestController::class.'@parseMethodDescription'); + + $output = $this->artisan('api:generate', [ + '--routePrefix' => 'api/*' + ]); + $this->assertContains('Skipping route: api/closure - contains closure.', $output); + $this->assertContains('Processed route: api/test', $output); + } + + /** + * @param string $command + * @param array $parameters + * @return mixed + */ + public function artisan($command, $parameters = []) + { + $this->app[Kernel::class]->call($command, $parameters); + return $this->app[Kernel::class]->output(); + } +} From cbcad1e6fb1ae8d17e322a92abf624c9752f7fa4 Mon Sep 17 00:00:00 2001 From: Marcel Pociot Date: Mon, 6 Jun 2016 07:07:05 -0400 Subject: [PATCH 027/780] Applied fixes from StyleCI --- src/Mpociot/ApiDoc/Commands/GenerateDocumentation.php | 3 ++- tests/GenerateDocumentationTest.php | 8 +++++--- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/src/Mpociot/ApiDoc/Commands/GenerateDocumentation.php b/src/Mpociot/ApiDoc/Commands/GenerateDocumentation.php index f22227d9..3b60dac5 100644 --- a/src/Mpociot/ApiDoc/Commands/GenerateDocumentation.php +++ b/src/Mpociot/ApiDoc/Commands/GenerateDocumentation.php @@ -204,10 +204,11 @@ private function processDingoRoutes(AbstractGenerator $generator, $allowedRoutes /** * @param $route + * * @return bool */ private function isValidRoute($route) { - return !is_callable($route->getAction()['uses']); + return ! is_callable($route->getAction()['uses']); } } diff --git a/tests/GenerateDocumentationTest.php b/tests/GenerateDocumentationTest.php index 3ae3ed76..4ed578fa 100644 --- a/tests/GenerateDocumentationTest.php +++ b/tests/GenerateDocumentationTest.php @@ -7,7 +7,6 @@ use Mpociot\ApiDoc\ApiDocGeneratorServiceProvider; use Mpociot\ApiDoc\Generators\LaravelGenerator; use Orchestra\Testbench\TestCase; -use Mpociot\ApiDoc\Tests\Fixtures\TestRequest; use Mpociot\ApiDoc\Tests\Fixtures\TestController; use Illuminate\Support\Facades\Route as RouteFacade; @@ -30,6 +29,7 @@ public function setUp() /** * @param \Illuminate\Foundation\Application $app + * * @return array */ protected function getPackageProviders($app) @@ -45,13 +45,13 @@ public function testConsoleCommandNeedsAPrefixOrRoute() public function testConsoleCommandDoesNotWorkWithClosure() { - RouteFacade::get('/api/closure', function(){ + RouteFacade::get('/api/closure', function () { return 'foo'; }); RouteFacade::get('/api/test', TestController::class.'@parseMethodDescription'); $output = $this->artisan('api:generate', [ - '--routePrefix' => 'api/*' + '--routePrefix' => 'api/*', ]); $this->assertContains('Skipping route: api/closure - contains closure.', $output); $this->assertContains('Processed route: api/test', $output); @@ -60,11 +60,13 @@ public function testConsoleCommandDoesNotWorkWithClosure() /** * @param string $command * @param array $parameters + * * @return mixed */ public function artisan($command, $parameters = []) { $this->app[Kernel::class]->call($command, $parameters); + return $this->app[Kernel::class]->output(); } } From 51eb39282180d3e51e0dc66bc35907d5658f04c8 Mon Sep 17 00:00:00 2001 From: Marcel Pociot Date: Mon, 6 Jun 2016 22:58:29 +0200 Subject: [PATCH 028/780] Create CONTRIBUTING.md --- CONTRIBUTING.md | 55 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) create mode 100644 CONTRIBUTING.md diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 00000000..4da74e3f --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,55 @@ +# Contributing + +Contributions are **welcome** and will be fully **credited**. + +Please read and understand the contribution guide before creating an issue or pull request. + +## Etiquette + +This project is open source, and as such, the maintainers give their free time to build and maintain the source code +held within. They make the code freely available in the hope that it will be of use to other developers. It would be +extremely unfair for them to suffer abuse or anger for their hard work. + +Please be considerate towards maintainers when raising issues or presenting pull requests. Let's show the +world that developers are civilized and selfless people. + +It's the duty of the maintainer to ensure that all submissions to the project are of sufficient +quality to benefit the project. Many developers have different skillsets, strengths, and weaknesses. Respect the maintainer's decision, and do not be upset or abusive if your submission is not used. + +## Viability + +When requesting or submitting new features, first consider whether it might be useful to others. Open +source projects are used by many developers, who may have entirely different needs to your own. Think about +whether or not your feature is likely to be used by other users of the project. + +## Procedure + +Before filing an issue: + +- Attempt to replicate the problem, to ensure that it wasn't a coincidental incident. +- Check to make sure your feature suggestion isn't already present within the project. +- Check the pull requests tab to ensure that the bug doesn't have a fix in progress. +- Check the pull requests tab to ensure that the feature isn't already in progress. + +Before submitting a pull request: + +- Check the codebase to ensure that your feature doesn't already exist. +- Check the pull requests to ensure that another person hasn't already submitted the feature or fix. + +## Requirements + +If the project maintainer has any additional requirements, you will find them listed here. + +- **[PSR-2 Coding Standard](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-2-coding-style-guide.md)** - The easiest way to apply the conventions is to install [PHP Code Sniffer](http://pear.php.net/package/PHP_CodeSniffer). + +- **Add tests!** - Your patch won't be accepted if it doesn't have tests. + +- **Document any change in behaviour** - Make sure the `README.md` and any other relevant documentation are kept up-to-date. + +- **Consider our release cycle** - We try to follow [SemVer v2.0.0](http://semver.org/). Randomly breaking public APIs is not an option. + +- **One pull request per feature** - If you want to do more than one thing, send multiple pull requests. + +- **Send coherent history** - Make sure each individual commit in your pull request is meaningful. If you had to make multiple intermediate commits while developing, please [squash them](http://www.git-scm.com/book/en/v2/Git-Tools-Rewriting-History#Changing-Multiple-Commit-Messages) before submitting. + +**Happy coding**! From ec0e4240a736baaa968a0062efe5fa6cb5f5412d Mon Sep 17 00:00:00 2001 From: Juan Carlos Orrego Date: Tue, 7 Jun 2016 08:33:37 -0500 Subject: [PATCH 029/780] Composer dependencies for phpdocumentor updated --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index d02f5b69..58eb7c50 100644 --- a/composer.json +++ b/composer.json @@ -19,7 +19,7 @@ "fzaninotto/faker": "^1.6", "laravel/framework": "~5.0", "mpociot/documentarian": "^0.2.0", - "phpdocumentor/reflection-docblock": "~2.0" + "phpdocumentor/reflection-docblock": "^2.0|^3.0.2" }, "require-dev": { "orchestra/testbench": "~3.0", From 74f38a1c21bf7200b2eb94e9c2568e8c029d32ea Mon Sep 17 00:00:00 2001 From: Marcel Pociot Date: Fri, 10 Jun 2016 00:18:45 +0200 Subject: [PATCH 030/780] Update composer.json --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index 58eb7c50..c229c913 100644 --- a/composer.json +++ b/composer.json @@ -19,7 +19,7 @@ "fzaninotto/faker": "^1.6", "laravel/framework": "~5.0", "mpociot/documentarian": "^0.2.0", - "phpdocumentor/reflection-docblock": "^2.0|^3.0.2" + "phpdocumentor/reflection-docblock": "^2.0 || ^3.0.2" }, "require-dev": { "orchestra/testbench": "~3.0", From 62690bfc252b5586b6c614a8f705f96c89da34b3 Mon Sep 17 00:00:00 2001 From: Marcel Pociot Date: Mon, 13 Jun 2016 11:14:30 +0200 Subject: [PATCH 031/780] Check if reflectionclass has a doc block --- src/Mpociot/ApiDoc/Generators/AbstractGenerator.php | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/Mpociot/ApiDoc/Generators/AbstractGenerator.php b/src/Mpociot/ApiDoc/Generators/AbstractGenerator.php index f377ce3e..896dd7d1 100644 --- a/src/Mpociot/ApiDoc/Generators/AbstractGenerator.php +++ b/src/Mpociot/ApiDoc/Generators/AbstractGenerator.php @@ -112,10 +112,12 @@ protected function getRouteGroup($route) list($class, $method) = explode('@', $route); $reflection = new ReflectionClass($class); $comment = $reflection->getDocComment(); - $phpdoc = new DocBlock($comment); - foreach ($phpdoc->getTags() as $tag) { - if ($tag->getName() === 'resource') { - return $tag->getContent(); + if ($comment) { + $phpdoc = new DocBlock($comment); + foreach ($phpdoc->getTags() as $tag) { + if ($tag->getName() === 'resource') { + return $tag->getContent(); + } } } From d37e1c8bfafea868266332b94ef3bd1ded6ae038 Mon Sep 17 00:00:00 2001 From: Marcel Pociot Date: Mon, 13 Jun 2016 11:39:43 +0200 Subject: [PATCH 032/780] Restrict reflection-docblock version --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index c229c913..7075a0ed 100644 --- a/composer.json +++ b/composer.json @@ -19,7 +19,7 @@ "fzaninotto/faker": "^1.6", "laravel/framework": "~5.0", "mpociot/documentarian": "^0.2.0", - "phpdocumentor/reflection-docblock": "^2.0 || ^3.0.2" + "phpdocumentor/reflection-docblock": "^2.0" }, "require-dev": { "orchestra/testbench": "~3.0", From 8b72eae44958e658d49b958891c7a052c3360ac3 Mon Sep 17 00:00:00 2001 From: Marcel Pociot Date: Mon, 13 Jun 2016 13:09:45 +0200 Subject: [PATCH 033/780] Added 'noResponseCalls' option --- README.md | 8 ++++++++ .../ApiDoc/Commands/GenerateDocumentation.php | 7 +++++-- .../ApiDoc/Generators/AbstractGenerator.php | 5 +++-- .../ApiDoc/Generators/DingoGenerator.php | 18 +++++++++++------- .../ApiDoc/Generators/LaravelGenerator.php | 9 +++++++-- 5 files changed, 34 insertions(+), 13 deletions(-) diff --git a/README.md b/README.md index 5d86bb23..da4a0509 100644 --- a/README.md +++ b/README.md @@ -44,6 +44,8 @@ Option | Description `routePrefix` | The route prefix to use for generation - `*` can be used as a wildcard `routes` | The route names to use for generation - Required if no routePrefix is provided `actAsUserId` | The user ID to use for authenticated API response calls +`noResponseCalls` | Disable API response calls +`actAsUserId` | The user ID to use for authenticated API response calls `router` | The router to use, when processing the route files (can be Laravel or Dingo - defaults to Laravel) `bindings` | List of route bindings that should be replaced when trying to retrieve route results. Syntax format: `binding_one,id|binding_two,id` @@ -96,6 +98,12 @@ If your API needs an authenticated user, you can use the `actAsUserId` option to $ php artisan api:generate --routePrefix=api/* --actAsUserId=1 ``` +If you don't want to automatically perform API response calls, use the `noResponseCalls` option. + +```sh +$ php artisan api:generate --routePrefix=api/* --noResponseCalls +``` + > Note: The example API responses work best with seeded data. diff --git a/src/Mpociot/ApiDoc/Commands/GenerateDocumentation.php b/src/Mpociot/ApiDoc/Commands/GenerateDocumentation.php index 3b60dac5..16c3f1bb 100644 --- a/src/Mpociot/ApiDoc/Commands/GenerateDocumentation.php +++ b/src/Mpociot/ApiDoc/Commands/GenerateDocumentation.php @@ -21,6 +21,7 @@ class GenerateDocumentation extends Command {--output=public/docs : The output path for the generated documentation} {--routePrefix= : The route prefix to use for generation} {--routes=* : The route names to use for generation} + {--noResponseCalls : The user ID to use for API response calls} {--actAsUserId= : The user ID to use for API response calls} {--router=laravel : The router to be used (Laravel or Dingo)} {--bindings= : Route Model Bindings} @@ -163,13 +164,14 @@ private function getRoutes() */ private function processLaravelRoutes(AbstractGenerator $generator, $allowedRoutes, $routePrefix) { + $withResponse = $this->option('noResponseCalls') === false; $routes = $this->getRoutes(); $bindings = $this->getBindings(); $parsedRoutes = []; foreach ($routes as $route) { if (in_array($route->getName(), $allowedRoutes) || str_is($routePrefix, $route->getUri())) { if ($this->isValidRoute($route)) { - $parsedRoutes[] = $generator->processRoute($route, $bindings); + $parsedRoutes[] = $generator->processRoute($route, $bindings, $withResponse); $this->info('Processed route: '.$route->getUri()); } else { $this->warn('Skipping route: '.$route->getUri().' - contains closure.'); @@ -189,12 +191,13 @@ private function processLaravelRoutes(AbstractGenerator $generator, $allowedRout */ private function processDingoRoutes(AbstractGenerator $generator, $allowedRoutes, $routePrefix) { + $withResponse = $this->option('noResponseCalls') === false; $routes = $this->getRoutes(); $bindings = $this->getBindings(); $parsedRoutes = []; foreach ($routes as $route) { if (empty($allowedRoutes) || in_array($route->getName(), $allowedRoutes) || str_is($routePrefix, $route->uri())) { - $parsedRoutes[] = $generator->processRoute($route, $bindings); + $parsedRoutes[] = $generator->processRoute($route, $bindings, $withResponse); $this->info('Processed route: '.$route->uri()); } } diff --git a/src/Mpociot/ApiDoc/Generators/AbstractGenerator.php b/src/Mpociot/ApiDoc/Generators/AbstractGenerator.php index 896dd7d1..5bc9ce86 100644 --- a/src/Mpociot/ApiDoc/Generators/AbstractGenerator.php +++ b/src/Mpociot/ApiDoc/Generators/AbstractGenerator.php @@ -21,10 +21,11 @@ abstract protected function getUri($route); /** * @param \Illuminate\Routing\Route $route * @param array $bindings - * + * @param bool $withResponse + * * @return array */ - abstract public function processRoute($route, $bindings = []); + abstract public function processRoute($route, $bindings = [], $withResponse = true); /** * @param array $routeData diff --git a/src/Mpociot/ApiDoc/Generators/DingoGenerator.php b/src/Mpociot/ApiDoc/Generators/DingoGenerator.php index 92d017ea..2800703d 100644 --- a/src/Mpociot/ApiDoc/Generators/DingoGenerator.php +++ b/src/Mpociot/ApiDoc/Generators/DingoGenerator.php @@ -7,18 +7,22 @@ class DingoGenerator extends AbstractGenerator { /** - * @param $route + * @param \Illuminate\Routing\Route $route * @param array $bindings - * + * @param bool $withResponse + * * @return array */ - public function processRoute($route, $bindings = []) + public function processRoute($route, $bindings = [], $withResponse = true) { - try { - $response = $this->getRouteResponse($route, $bindings); - } catch (Exception $e) { - $response = ''; + $response = ''; + + if ($withResponse) { + try { + $response = $this->getRouteResponse($route, $bindings); + } catch (Exception $e) {} } + $routeAction = $route->getAction(); $routeGroup = $this->getRouteGroup($routeAction['uses']); $routeDescription = $this->getRouteDescription($routeAction['uses']); diff --git a/src/Mpociot/ApiDoc/Generators/LaravelGenerator.php b/src/Mpociot/ApiDoc/Generators/LaravelGenerator.php index 48afbd77..0feb7633 100644 --- a/src/Mpociot/ApiDoc/Generators/LaravelGenerator.php +++ b/src/Mpociot/ApiDoc/Generators/LaravelGenerator.php @@ -21,12 +21,17 @@ protected function getUri($route) /** * @param \Illuminate\Routing\Route $route * @param array $bindings + * @param bool $withResponse * * @return array */ - public function processRoute($route, $bindings = []) + public function processRoute($route, $bindings = [], $withResponse = true) { - $response = $this->getRouteResponse($route, $bindings); + $response = ''; + + if ($withResponse) { + $response = $this->getRouteResponse($route, $bindings); + } $routeAction = $route->getAction(); $routeGroup = $this->getRouteGroup($routeAction['uses']); From 7c6c02f4d8f0ae312c411edfc2a8be8142540eaa Mon Sep 17 00:00:00 2001 From: Marcel Pociot Date: Mon, 13 Jun 2016 07:11:01 -0400 Subject: [PATCH 034/780] Applied fixes from StyleCI --- src/Mpociot/ApiDoc/Generators/AbstractGenerator.php | 2 +- src/Mpociot/ApiDoc/Generators/DingoGenerator.php | 9 +++++---- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/Mpociot/ApiDoc/Generators/AbstractGenerator.php b/src/Mpociot/ApiDoc/Generators/AbstractGenerator.php index 5bc9ce86..96b2032a 100644 --- a/src/Mpociot/ApiDoc/Generators/AbstractGenerator.php +++ b/src/Mpociot/ApiDoc/Generators/AbstractGenerator.php @@ -22,7 +22,7 @@ abstract protected function getUri($route); * @param \Illuminate\Routing\Route $route * @param array $bindings * @param bool $withResponse - * + * * @return array */ abstract public function processRoute($route, $bindings = [], $withResponse = true); diff --git a/src/Mpociot/ApiDoc/Generators/DingoGenerator.php b/src/Mpociot/ApiDoc/Generators/DingoGenerator.php index 2800703d..728c5743 100644 --- a/src/Mpociot/ApiDoc/Generators/DingoGenerator.php +++ b/src/Mpociot/ApiDoc/Generators/DingoGenerator.php @@ -10,19 +10,20 @@ class DingoGenerator extends AbstractGenerator * @param \Illuminate\Routing\Route $route * @param array $bindings * @param bool $withResponse - * + * * @return array */ public function processRoute($route, $bindings = [], $withResponse = true) { $response = ''; - + if ($withResponse) { try { $response = $this->getRouteResponse($route, $bindings); - } catch (Exception $e) {} + } catch (Exception $e) { + } } - + $routeAction = $route->getAction(); $routeGroup = $this->getRouteGroup($routeAction['uses']); $routeDescription = $this->getRouteDescription($routeAction['uses']); From be681d29c130c136b3b0a46cc09d3d3d99636139 Mon Sep 17 00:00:00 2001 From: Marcel Pociot Date: Mon, 13 Jun 2016 13:36:55 +0200 Subject: [PATCH 035/780] Added markdown generation test --- tests/Fixtures/index.md | 61 +++++++++++++++++++++++++++++ tests/GenerateDocumentationTest.php | 13 ++++++ 2 files changed, 74 insertions(+) create mode 100644 tests/Fixtures/index.md diff --git a/tests/Fixtures/index.md b/tests/Fixtures/index.md new file mode 100644 index 00000000..51308611 --- /dev/null +++ b/tests/Fixtures/index.md @@ -0,0 +1,61 @@ +--- +title: API Reference + +language_tabs: +- bash +- javascript + +includes: + +search: true + +toc_footers: +- Documentation Powered by Documentarian +--- + +# Info + +Welcome to the generated API reference. + +# Available routes +#general +## Example title. + +This will be the long description. +It can also be multiple lines long. + +> Example request: + +```bash +curl "http://localhost/api/test" \ +-H "Accept: application/json" +``` + +```javascript +var settings = { + "async": true, + "crossDomain": true, + "url": "http://localhost/api/test", + "method": "GET", + "headers": { + "accept": "application/json" + } +} + +$.ajax(settings).done(function (response) { +console.log(response); +}); +``` + +> Example response: + +```json +null +``` + +### HTTP Request +`GET api/test` + +`HEAD api/test` + + diff --git a/tests/GenerateDocumentationTest.php b/tests/GenerateDocumentationTest.php index 4ed578fa..a55bc458 100644 --- a/tests/GenerateDocumentationTest.php +++ b/tests/GenerateDocumentationTest.php @@ -57,6 +57,19 @@ public function testConsoleCommandDoesNotWorkWithClosure() $this->assertContains('Processed route: api/test', $output); } + public function testGeneratedMarkdownFileIsCorrect() + { + RouteFacade::get('/api/test', TestController::class.'@parseMethodDescription'); + + $output = $this->artisan('api:generate', [ + '--routePrefix' => 'api/*', + ]); + + $generatedMarkdown = file_get_contents(__DIR__ . '/../public/docs/source/index.md'); + $fixtureMarkdown = file_get_contents(__DIR__ . '/Fixtures/index.md'); + $this->assertSame($generatedMarkdown, $fixtureMarkdown); + } + /** * @param string $command * @param array $parameters From 59fe456d6889617073dd5715a849268399d8d74a Mon Sep 17 00:00:00 2001 From: Marcel Pociot Date: Mon, 13 Jun 2016 07:37:28 -0400 Subject: [PATCH 036/780] Applied fixes from StyleCI --- tests/GenerateDocumentationTest.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/GenerateDocumentationTest.php b/tests/GenerateDocumentationTest.php index a55bc458..ce7eca22 100644 --- a/tests/GenerateDocumentationTest.php +++ b/tests/GenerateDocumentationTest.php @@ -64,9 +64,9 @@ public function testGeneratedMarkdownFileIsCorrect() $output = $this->artisan('api:generate', [ '--routePrefix' => 'api/*', ]); - - $generatedMarkdown = file_get_contents(__DIR__ . '/../public/docs/source/index.md'); - $fixtureMarkdown = file_get_contents(__DIR__ . '/Fixtures/index.md'); + + $generatedMarkdown = file_get_contents(__DIR__.'/../public/docs/source/index.md'); + $fixtureMarkdown = file_get_contents(__DIR__.'/Fixtures/index.md'); $this->assertSame($generatedMarkdown, $fixtureMarkdown); } From 2d10144a6b9129115eee5502629ddf3a4b3233f2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ferreyra=2C=20Maximiliano=20Gast=C3=B3n?= Date: Sat, 18 Jun 2016 20:22:34 -0300 Subject: [PATCH 037/780] Allow translate rules descriptions. --- README.md | 13 ++- .../ApiDoc/ApiDocGeneratorServiceProvider.php | 5 ++ .../ApiDoc/Generators/AbstractGenerator.php | 63 ++++++------- .../ApiDoc/Parsers/RuleDescriptionParser.php | 75 ++++++++++++++++ src/resources/lang/en/rules.php | 33 +++++++ tests/ApiDocGeneratorTest.php | 14 ++- tests/DingoGeneratorTest.php | 8 +- tests/RuleDescriptionParserTest.php | 90 +++++++++++++++++++ 8 files changed, 263 insertions(+), 38 deletions(-) create mode 100644 src/Mpociot/ApiDoc/Parsers/RuleDescriptionParser.php create mode 100644 src/resources/lang/en/rules.php create mode 100644 tests/RuleDescriptionParserTest.php diff --git a/README.md b/README.md index da4a0509..e4fdd86b 100644 --- a/README.md +++ b/README.md @@ -47,7 +47,18 @@ Option | Description `noResponseCalls` | Disable API response calls `actAsUserId` | The user ID to use for authenticated API response calls `router` | The router to use, when processing the route files (can be Laravel or Dingo - defaults to Laravel) -`bindings` | List of route bindings that should be replaced when trying to retrieve route results. Syntax format: `binding_one,id|binding_two,id` +`bindings` | List of route bindings that should be replaced when trying to retrieve route results. Syntax format: `binding_one,id|binding_two,id` + +## Publish rule descriptions for customisation or translate. + + For default, this package returns the descriptions in the main language of the application. But provides the possibility of publish the language files for customisation or translate. + + ```sh + $ php artisan vendor:publish + ``` + + After to publish you can customize or translate the descriptions in the language you want by editing the files in `public/vendor/apidoc/resources/lang`. + ### How does it work? diff --git a/src/Mpociot/ApiDoc/ApiDocGeneratorServiceProvider.php b/src/Mpociot/ApiDoc/ApiDocGeneratorServiceProvider.php index cd608b79..26565e42 100644 --- a/src/Mpociot/ApiDoc/ApiDocGeneratorServiceProvider.php +++ b/src/Mpociot/ApiDoc/ApiDocGeneratorServiceProvider.php @@ -16,6 +16,11 @@ class ApiDocGeneratorServiceProvider extends ServiceProvider public function boot() { $this->loadViewsFrom(__DIR__.'/../../resources/views/', 'apidoc'); + $this->loadTranslationsFrom(__DIR__.'/../../resources/lang', 'apidoc'); + + $this->publishes([ + __DIR__.'/../../resources/lang' => resource_path('lang/vendor/apidoc'), + ]); } /** diff --git a/src/Mpociot/ApiDoc/Generators/AbstractGenerator.php b/src/Mpociot/ApiDoc/Generators/AbstractGenerator.php index 96b2032a..3cc0ee2f 100644 --- a/src/Mpociot/ApiDoc/Generators/AbstractGenerator.php +++ b/src/Mpociot/ApiDoc/Generators/AbstractGenerator.php @@ -3,11 +3,12 @@ namespace Mpociot\ApiDoc\Generators; use Faker\Factory; -use ReflectionClass; +use Illuminate\Foundation\Http\FormRequest; +use Illuminate\Support\Facades\Validator; use Illuminate\Support\Str; +use Mpociot\ApiDoc\Parsers\RuleDescriptionParser as Description; +use ReflectionClass; use phpDocumentor\Reflection\DocBlock; -use Illuminate\Support\Facades\Validator; -use Illuminate\Foundation\Http\FormRequest; abstract class AbstractGenerator { @@ -196,102 +197,102 @@ protected function parseRule($rule, &$attributeData) break; case 'after': $attributeData['type'] = 'date'; - $attributeData['description'][] = 'Must be a date after: `'.date(DATE_RFC850, strtotime($parameters[0])).'`'; + $attributeData['description'][] = Description::parse($rule)->with(date(DATE_RFC850, strtotime($parameters[0])))->getDescription(); $attributeData['value'] = date(DATE_RFC850, strtotime('+1 day', strtotime($parameters[0]))); break; case 'alpha': - $attributeData['description'][] = 'Only alphabetic characters allowed'; + $attributeData['description'][] = Description::parse($rule)->getDescription(); $attributeData['value'] = $faker->word; break; case 'alpha_dash': - $attributeData['description'][] = 'Allowed: alpha-numeric characters, as well as dashes and underscores.'; + $attributeData['description'][] = Description::parse($rule)->getDescription(); break; case 'alpha_num': - $attributeData['description'][] = 'Only alpha-numeric characters allowed'; + $attributeData['description'][] = Description::parse($rule)->getDescription(); break; case 'in': - $attributeData['description'][] = $this->fancyImplode($parameters, ', ', ' or '); + $attributeData['description'][] = Description::parse($rule)->with($this->fancyImplode($parameters, ', ', ' or '))->getDescription(); $attributeData['value'] = $faker->randomElement($parameters); break; case 'not_in': - $attributeData['description'][] = 'Not in: '.$this->fancyImplode($parameters, ', ', ' or '); + $attributeData['description'][] = Description::parse($rule)->with($this->fancyImplode($parameters, ', ', ' or '))->getDescription(); $attributeData['value'] = $faker->word; break; case 'min': - $attributeData['description'][] = 'Minimum: `'.$parameters[0].'`'; + $attributeData['description'][] = Description::parse($rule)->with($parameters)->getDescription(); break; case 'max': - $attributeData['description'][] = 'Maximum: `'.$parameters[0].'`'; + $attributeData['description'][] = Description::parse($rule)->with($parameters)->getDescription(); break; case 'between': $attributeData['type'] = 'numeric'; - $attributeData['description'][] = 'Between: `'.$parameters[0].'` and `'.$parameters[1].'`'; + $attributeData['description'][] = Description::parse($rule)->with($parameters)->getDescription(); $attributeData['value'] = $faker->numberBetween($parameters[0], $parameters[1]); break; case 'before': $attributeData['type'] = 'date'; - $attributeData['description'][] = 'Must be a date preceding: `'.date(DATE_RFC850, strtotime($parameters[0])).'`'; + $attributeData['description'][] = Description::parse($rule)->with(date(DATE_RFC850, strtotime($parameters[0])))->getDescription(); $attributeData['value'] = date(DATE_RFC850, strtotime('-1 day', strtotime($parameters[0]))); break; case 'date_format': $attributeData['type'] = 'date'; - $attributeData['description'][] = 'Date format: `'.$parameters[0].'`'; + $attributeData['description'][] = Description::parse($rule)->with($parameters)->getDescription(); break; case 'different': - $attributeData['description'][] = 'Must have a different value than parameter: `'.$parameters[0].'`'; + $attributeData['description'][] = Description::parse($rule)->with($parameters)->getDescription(); break; case 'digits': $attributeData['type'] = 'numeric'; - $attributeData['description'][] = 'Must have an exact length of `'.$parameters[0].'`'; + $attributeData['description'][] = Description::parse($rule)->with($parameters)->getDescription(); $attributeData['value'] = $faker->randomNumber($parameters[0], true); break; case 'digits_between': $attributeData['type'] = 'numeric'; - $attributeData['description'][] = 'Must have a length between `'.$parameters[0].'` and `'.$parameters[1].'`'; + $attributeData['description'][] = Description::parse($rule)->with($parameters)->getDescription(); break; case 'image': $attributeData['type'] = 'image'; - $attributeData['description'][] = 'Must be an image (jpeg, png, bmp, gif, or svg)'; + $attributeData['description'][] = Description::parse($rule)->getDescription(); break; case 'json': $attributeData['type'] = 'string'; - $attributeData['description'][] = 'Must be a valid JSON string.'; + $attributeData['description'][] = Description::parse($rule)->getDescription(); $attributeData['value'] = json_encode(['foo', 'bar', 'baz']); break; case 'mimetypes': case 'mimes': - $attributeData['description'][] = 'Allowed mime types: '.$this->fancyImplode($parameters, ', ', ' or '); + $attributeData['description'][] = Description::parse($rule)->with($this->fancyImplode($parameters, ', ', ' or '))->getDescription(); break; case 'required_if': - $attributeData['description'][] = 'Required if `'.$parameters[0].'` is `'.$parameters[1].'`'; + $attributeData['description'][] = Description::parse($rule)->with($parameters)->getDescription(); break; case 'required_unless': - $attributeData['description'][] = 'Required unless `'.$parameters[0].'` is `'.$parameters[1].'`'; + $attributeData['description'][] = Description::parse($rule)->with($parameters)->getDescription(); break; case 'required_with': - $attributeData['description'][] = 'Required if the parameters '.$this->fancyImplode($parameters, ', ', ' or ').' are present.'; + $attributeData['description'][] = Description::parse($rule)->with($this->fancyImplode($parameters, ', ', ' or '))->getDescription(); break; case 'required_with_all': - $attributeData['description'][] = 'Required if the parameters '.$this->fancyImplode($parameters, ', ', ' and ').' are present.'; + $attributeData['description'][] = Description::parse($rule)->with($this->fancyImplode($parameters, ', ', ' and '))->getDescription(); break; case 'required_without': - $attributeData['description'][] = 'Required if the parameters '.$this->fancyImplode($parameters, ', ', ' or ').' are not present.'; + $attributeData['description'][] = Description::parse($rule)->with($this->fancyImplode($parameters, ', ', ' or '))->getDescription(); break; case 'required_without_all': - $attributeData['description'][] = 'Required if the parameters '.$this->fancyImplode($parameters, ', ', ' and ').' are not present.'; + $attributeData['description'][] = Description::parse($rule)->with($this->fancyImplode($parameters, ', ', ' and '))->getDescription(); break; case 'same': - $attributeData['description'][] = 'Must be the same as `'.$parameters[0].'`'; + $attributeData['description'][] = Description::parse($rule)->with($parameters)->getDescription(); break; case 'size': - $attributeData['description'][] = 'Must have the size of `'.$parameters[0].'`'; + $attributeData['description'][] = Description::parse($rule)->with($parameters)->getDescription(); break; case 'timezone': - $attributeData['description'][] = 'Must be a valid timezone identifier'; + $attributeData['description'][] = Description::parse($rule)->getDescription(); $attributeData['value'] = $faker->timezone; break; case 'exists': - $attributeData['description'][] = 'Valid '.Str::singular($parameters[0]).' '.$parameters[1]; + $attributeData['description'][] = Description::parse($rule)->with([Str::singular($parameters[0]), $parameters[1]])->getDescription(); break; case 'active_url': $attributeData['type'] = 'url'; @@ -299,7 +300,7 @@ protected function parseRule($rule, &$attributeData) break; case 'regex': $attributeData['type'] = 'string'; - $attributeData['description'][] = 'Must match this regular expression: `'.$parameters[0].'`'; + $attributeData['description'][] = Description::parse($rule)->with($parameters)->getDescription(); break; case 'boolean': $attributeData['value'] = true; diff --git a/src/Mpociot/ApiDoc/Parsers/RuleDescriptionParser.php b/src/Mpociot/ApiDoc/Parsers/RuleDescriptionParser.php new file mode 100644 index 00000000..79a0a65c --- /dev/null +++ b/src/Mpociot/ApiDoc/Parsers/RuleDescriptionParser.php @@ -0,0 +1,75 @@ +rule = $rule; + } + + /** + * Returns the description in the main language of the application. + * + * @return array|string + */ + public function getDescription() + { + $key = "apidoc::rules.{$this->rule}"; + + $description = $this->parameters ? $this->translateWithAttributes($key) : trans($key); + + return $description != $key ? $description : []; + } + + /** + * Sets the parameters for the description string. + * @param string|array $parameters + * + * @return $this + */ + public function with($parameters) + { + is_array($parameters) ? $this->parameters += $parameters : $this->parameters[] = $parameters; + + return $this; + } + + /** + * Returns the description string with the replaced attributes. + * @param $key + * + * @return string + */ + protected function translateWithAttributes($key) + { + $translate = trans($key); + + foreach ($this->parameters as $parameter) { + $translate = preg_replace('/:attribute/', $parameter, $translate, 1); + } + + return $translate; + } + + /** + * Provides a named constructor. + * @param null $rule + * + * @return static + */ + public static function parse($rule = null) + { + return new static($rule); + } +} diff --git a/src/resources/lang/en/rules.php b/src/resources/lang/en/rules.php new file mode 100644 index 00000000..5c5f7dd0 --- /dev/null +++ b/src/resources/lang/en/rules.php @@ -0,0 +1,33 @@ + 'Must be a date after: `:attribute`', + 'alpha' => 'Only alphabetic characters allowed', + 'alpha_dash' => 'Allowed: alpha-numeric characters, as well as dashes and underscores.', + 'alpha_num' => 'Only alpha-numeric characters allowed', + 'in' => ':attribute', + 'not_in' => 'Not in: :attribute', + 'min' => 'Minimum: `:attribute`', + 'max' => 'Maximum: `:attribute`', + 'between' => 'Between: `:attribute` and `:attribute`', + 'before' => 'Must be a date preceding: `:attribute`', + 'date_format' => 'Date format: `:attribute`', + 'different' => 'Must have a different value than parameter: `:attribute`', + 'digits' => 'Must have an exact length of `:attribute`', + 'digits_between' => 'Must have a length between `:attribute` and `:attribute`', + 'image' => 'Must be an image (jpeg, png, bmp, gif, or svg)', + 'json' => 'Must be a valid JSON string.', + 'mimetypes' => 'Allowed mime types: :attribute', + 'mimes' => 'Allowed mime types: :attribute', + 'required_if' => 'Required if `:attribute` is `:attribute`', + 'required_unless' => 'Required unless `:attribute` is `:attribute`', + 'required_with' => 'Required if the parameters :attribute are present.', + 'required_with_all' => 'Required if the parameters :attribute are present.', + 'required_without' => 'Required if the parameters :attribute are not present.', + 'required_without_all' => 'Required if the parameters :attribute are not present.', + 'same' => 'Must be the same as `:attribute`', + 'size' => 'Must have the size of `:attribute`', + 'timezone' => 'Must be a valid timezone identifier', + 'exists' => 'Valid :attribute :attribute', + 'regex' => 'Must match this regular expression: `:attribute`', +]; diff --git a/tests/ApiDocGeneratorTest.php b/tests/ApiDocGeneratorTest.php index 07f297b5..ff6a738e 100644 --- a/tests/ApiDocGeneratorTest.php +++ b/tests/ApiDocGeneratorTest.php @@ -3,11 +3,12 @@ namespace Mpociot\ApiDoc\Tests; use Illuminate\Routing\Route; +use Illuminate\Support\Facades\Route as RouteFacade; +use Mpociot\ApiDoc\ApiDocGeneratorServiceProvider; use Mpociot\ApiDoc\Generators\LaravelGenerator; -use Orchestra\Testbench\TestCase; -use Mpociot\ApiDoc\Tests\Fixtures\TestRequest; use Mpociot\ApiDoc\Tests\Fixtures\TestController; -use Illuminate\Support\Facades\Route as RouteFacade; +use Mpociot\ApiDoc\Tests\Fixtures\TestRequest; +use Orchestra\Testbench\TestCase; class ApiDocGeneratorTest extends TestCase { @@ -16,6 +17,13 @@ class ApiDocGeneratorTest extends TestCase */ protected $generator; + protected function getPackageProviders($app) + { + return [ + ApiDocGeneratorServiceProvider::class, + ]; + } + /** * Setup the test environment. */ diff --git a/tests/DingoGeneratorTest.php b/tests/DingoGeneratorTest.php index 2b7a8e71..de6bf3c9 100644 --- a/tests/DingoGeneratorTest.php +++ b/tests/DingoGeneratorTest.php @@ -3,11 +3,12 @@ namespace Mpociot\ApiDoc\Tests; use Dingo\Api\Provider\LaravelServiceProvider; -use Mpociot\ApiDoc\Tests\Fixtures\DingoTestController; -use Orchestra\Testbench\TestCase; +use Mpociot\ApiDoc\ApiDocGeneratorServiceProvider; use Mpociot\ApiDoc\Generators\DingoGenerator; -use Mpociot\ApiDoc\Tests\Fixtures\TestRequest; +use Mpociot\ApiDoc\Tests\Fixtures\DingoTestController; use Mpociot\ApiDoc\Tests\Fixtures\TestController; +use Mpociot\ApiDoc\Tests\Fixtures\TestRequest; +use Orchestra\Testbench\TestCase; class DingoGeneratorTest extends TestCase { @@ -20,6 +21,7 @@ protected function getPackageProviders($app) { return [ LaravelServiceProvider::class, + ApiDocGeneratorServiceProvider::class, ]; } diff --git a/tests/RuleDescriptionParserTest.php b/tests/RuleDescriptionParserTest.php new file mode 100644 index 00000000..1c8b0842 --- /dev/null +++ b/tests/RuleDescriptionParserTest.php @@ -0,0 +1,90 @@ +assertEmpty($rule->getDescription()); + } + + public function testProvidesANamedContructor() + { + $this->assertInstanceOf(RuleDescriptionParser::class, RuleDescriptionParser::parse()); + } + + public function testReturnsADescriptionInTheMainLanguageOfTheApplication() + { + $expected = 'Only alphabetic characters allowed'; + $rule = new RuleDescriptionParser('alpha'); + + $this->assertEquals($expected, $rule->getDescription()); + } + + public function testReturnsAnEmptyDescriptionIfNotAvailable() + { + $rule = new RuleDescriptionParser('dummy_rule'); + + $description = $rule->getDescription(); + + $this->assertEmpty($description); + } + + public function testAllowsToPassParametersToTheDescription() + { + $expected = 'Must have an exact length of `2`'; + $rule = new RuleDescriptionParser('digits'); + + $actual = $rule->with(2)->getDescription(); + + $this->assertEquals($expected, $actual); + } + + public function testOnlyPassesParametersIfTheDescriptionAllows() + { + $expected = 'Only alphabetic characters allowed'; + $rule = new RuleDescriptionParser('alpha'); + + $actual = $rule->with("dummy parameter")->getDescription(); + + $this->assertEquals($expected, $actual); + } + + public function testAllowsToPassMultipleParametersToTheDescription() + { + $expected = 'Required if `2 + 2` is `4`'; + $rule = new RuleDescriptionParser('required_if'); + + $actual = $rule->with(['2 + 2', 4])->getDescription(); + + $this->assertEquals($expected, $actual); + } + + /** + * @param \Illuminate\Foundation\Application $app + * + * @return array + */ + protected function getPackageProviders($app) + { + return [ApiDocGeneratorServiceProvider::class]; + } + + /** + * Define environment setup. + * + * @param \Illuminate\Foundation\Application $app + * @return void + */ + protected function getEnvironmentSetUp($app) + { + $app['config']->set('app.locale', 'en'); + } +} From 8ad87d33b5ee45d9e43cb2166a90bc9aee88bbfb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ferreyra=2C=20Maximiliano=20Gast=C3=B3n?= Date: Sun, 19 Jun 2016 21:07:01 -0300 Subject: [PATCH 038/780] Applied fixes from StyleCI --- src/Mpociot/ApiDoc/Parsers/RuleDescriptionParser.php | 7 ------- tests/RuleDescriptionParserTest.php | 2 +- 2 files changed, 1 insertion(+), 8 deletions(-) diff --git a/src/Mpociot/ApiDoc/Parsers/RuleDescriptionParser.php b/src/Mpociot/ApiDoc/Parsers/RuleDescriptionParser.php index 79a0a65c..2f0720ab 100644 --- a/src/Mpociot/ApiDoc/Parsers/RuleDescriptionParser.php +++ b/src/Mpociot/ApiDoc/Parsers/RuleDescriptionParser.php @@ -9,8 +9,6 @@ class RuleDescriptionParser private $parameters = []; /** - * RuleDescriptionParser constructor. - * * @param null $rule */ public function __construct($rule = null) @@ -19,8 +17,6 @@ public function __construct($rule = null) } /** - * Returns the description in the main language of the application. - * * @return array|string */ public function getDescription() @@ -33,7 +29,6 @@ public function getDescription() } /** - * Sets the parameters for the description string. * @param string|array $parameters * * @return $this @@ -46,7 +41,6 @@ public function with($parameters) } /** - * Returns the description string with the replaced attributes. * @param $key * * @return string @@ -63,7 +57,6 @@ protected function translateWithAttributes($key) } /** - * Provides a named constructor. * @param null $rule * * @return static diff --git a/tests/RuleDescriptionParserTest.php b/tests/RuleDescriptionParserTest.php index 1c8b0842..1896486f 100644 --- a/tests/RuleDescriptionParserTest.php +++ b/tests/RuleDescriptionParserTest.php @@ -52,7 +52,7 @@ public function testOnlyPassesParametersIfTheDescriptionAllows() $expected = 'Only alphabetic characters allowed'; $rule = new RuleDescriptionParser('alpha'); - $actual = $rule->with("dummy parameter")->getDescription(); + $actual = $rule->with('dummy parameter')->getDescription(); $this->assertEquals($expected, $actual); } From 1be225674ac2376c90dfe03f0c06edcae75b1ec8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ferreyra=2C=20Maximiliano=20Gast=C3=B3n?= Date: Sun, 19 Jun 2016 21:11:54 -0300 Subject: [PATCH 039/780] Applied fixes from StyleCI --- src/Mpociot/ApiDoc/Parsers/RuleDescriptionParser.php | 6 +++--- tests/RuleDescriptionParserTest.php | 1 + 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/Mpociot/ApiDoc/Parsers/RuleDescriptionParser.php b/src/Mpociot/ApiDoc/Parsers/RuleDescriptionParser.php index 2f0720ab..26934add 100644 --- a/src/Mpociot/ApiDoc/Parsers/RuleDescriptionParser.php +++ b/src/Mpociot/ApiDoc/Parsers/RuleDescriptionParser.php @@ -30,7 +30,7 @@ public function getDescription() /** * @param string|array $parameters - * + * * @return $this */ public function with($parameters) @@ -42,7 +42,7 @@ public function with($parameters) /** * @param $key - * + * * @return string */ protected function translateWithAttributes($key) @@ -58,7 +58,7 @@ protected function translateWithAttributes($key) /** * @param null $rule - * + * * @return static */ public static function parse($rule = null) diff --git a/tests/RuleDescriptionParserTest.php b/tests/RuleDescriptionParserTest.php index 1896486f..47eb20f4 100644 --- a/tests/RuleDescriptionParserTest.php +++ b/tests/RuleDescriptionParserTest.php @@ -81,6 +81,7 @@ protected function getPackageProviders($app) * Define environment setup. * * @param \Illuminate\Foundation\Application $app + * * @return void */ protected function getEnvironmentSetUp($app) From e198f3cf72907c8d7a33ff740f77f4843883cfd1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ferreyra=2C=20Maximiliano=20Gast=C3=B3n?= Date: Sun, 19 Jun 2016 21:12:49 -0300 Subject: [PATCH 040/780] Applied fixes from StyleCI --- tests/RuleDescriptionParserTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/RuleDescriptionParserTest.php b/tests/RuleDescriptionParserTest.php index 47eb20f4..a255db2d 100644 --- a/tests/RuleDescriptionParserTest.php +++ b/tests/RuleDescriptionParserTest.php @@ -76,7 +76,7 @@ protected function getPackageProviders($app) { return [ApiDocGeneratorServiceProvider::class]; } - + /** * Define environment setup. * From 920308ba68b0808a668b5a7f21210184febc81af Mon Sep 17 00:00:00 2001 From: Marcel Pociot Date: Mon, 20 Jun 2016 10:02:12 +0200 Subject: [PATCH 041/780] Use my own fork of the reflection-docblock --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index 7075a0ed..0547330d 100644 --- a/composer.json +++ b/composer.json @@ -19,7 +19,7 @@ "fzaninotto/faker": "^1.6", "laravel/framework": "~5.0", "mpociot/documentarian": "^0.2.0", - "phpdocumentor/reflection-docblock": "^2.0" + "mpociot/reflection-docblock": "^1.0" }, "require-dev": { "orchestra/testbench": "~3.0", From dc755a52389d8a8779f777128432d3fb92c62ceb Mon Sep 17 00:00:00 2001 From: Marcel Pociot Date: Mon, 20 Jun 2016 10:16:56 +0200 Subject: [PATCH 042/780] Fixed typo --- README.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index e4fdd86b..700efe6b 100644 --- a/README.md +++ b/README.md @@ -49,15 +49,16 @@ Option | Description `router` | The router to use, when processing the route files (can be Laravel or Dingo - defaults to Laravel) `bindings` | List of route bindings that should be replaced when trying to retrieve route results. Syntax format: `binding_one,id|binding_two,id` -## Publish rule descriptions for customisation or translate. +## Publish rule descriptions for customisation or translation. - For default, this package returns the descriptions in the main language of the application. But provides the possibility of publish the language files for customisation or translate. + By default, this package returns the descriptions in the main language of the application. + You can publish the packages language files, to customise and translate the documentation output. ```sh $ php artisan vendor:publish ``` - After to publish you can customize or translate the descriptions in the language you want by editing the files in `public/vendor/apidoc/resources/lang`. + After the files are published you can customise or translate the descriptions in the language you want by editing the files in `public/vendor/apidoc/resources/lang`. ### How does it work? From 028fa07e0dc35292a98b01fc4fecffb4d526bb3b Mon Sep 17 00:00:00 2001 From: "Neri J. Jakubowski Junior" Date: Mon, 20 Jun 2016 10:53:21 -0300 Subject: [PATCH 043/780] README.md removed duplicated propertie The line 'actAsUserId' was duplicated --- README.md | 1 - 1 file changed, 1 deletion(-) diff --git a/README.md b/README.md index 700efe6b..61158439 100644 --- a/README.md +++ b/README.md @@ -43,7 +43,6 @@ Option | Description `output` | The output path used for the generated documentation. Default: `public/docs` `routePrefix` | The route prefix to use for generation - `*` can be used as a wildcard `routes` | The route names to use for generation - Required if no routePrefix is provided -`actAsUserId` | The user ID to use for authenticated API response calls `noResponseCalls` | Disable API response calls `actAsUserId` | The user ID to use for authenticated API response calls `router` | The router to use, when processing the route files (can be Laravel or Dingo - defaults to Laravel) From 304472f7b44afd8d1baaed8955aafb483b1ab53f Mon Sep 17 00:00:00 2001 From: Marcel Pociot Date: Mon, 20 Jun 2016 22:57:45 +0200 Subject: [PATCH 044/780] Update namespace --- src/Mpociot/ApiDoc/Generators/AbstractGenerator.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Mpociot/ApiDoc/Generators/AbstractGenerator.php b/src/Mpociot/ApiDoc/Generators/AbstractGenerator.php index 3cc0ee2f..8d75560f 100644 --- a/src/Mpociot/ApiDoc/Generators/AbstractGenerator.php +++ b/src/Mpociot/ApiDoc/Generators/AbstractGenerator.php @@ -8,7 +8,7 @@ use Illuminate\Support\Str; use Mpociot\ApiDoc\Parsers\RuleDescriptionParser as Description; use ReflectionClass; -use phpDocumentor\Reflection\DocBlock; +use Mpociot\Reflection\DocBlock; abstract class AbstractGenerator { From 0b692e3769f19a401c13f0c1e24d1ddd6c347949 Mon Sep 17 00:00:00 2001 From: Marcel Pociot Date: Tue, 21 Jun 2016 12:54:08 +0200 Subject: [PATCH 045/780] Don't limit response JSON --- .gitignore | 3 ++- src/resources/views/documentarian.blade.php | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/.gitignore b/.gitignore index 2a54e344..338893d3 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ .DS_Store composer.lock .php_cs.cache -/vendor/ \ No newline at end of file +/vendor/ +/public diff --git a/src/resources/views/documentarian.blade.php b/src/resources/views/documentarian.blade.php index 25f60dc6..cf00cfba 100644 --- a/src/resources/views/documentarian.blade.php +++ b/src/resources/views/documentarian.blade.php @@ -66,7 +66,7 @@ > Example response: ```json -{!! str_limit(json_encode(json_decode($parsedRoute['response']), JSON_PRETTY_PRINT)) !!} +{!! json_encode(json_decode($parsedRoute['response']), JSON_PRETTY_PRINT) !!} ``` @endif From a73f21aeccb32931b5091c83de188b85691eb319 Mon Sep 17 00:00:00 2001 From: Marcel Pociot Date: Thu, 23 Jun 2016 21:04:18 +0200 Subject: [PATCH 046/780] Added test to see if route responses get fetched correctly (#43) --- tests/Fixtures/TestController.php | 18 ++++++++++++ tests/Fixtures/index.md | 43 +++++++++++++++++++++++++++++ tests/GenerateDocumentationTest.php | 1 + 3 files changed, 62 insertions(+) diff --git a/tests/Fixtures/TestController.php b/tests/Fixtures/TestController.php index fc349675..1810645a 100644 --- a/tests/Fixtures/TestController.php +++ b/tests/Fixtures/TestController.php @@ -25,4 +25,22 @@ public function parseFormRequestRules(TestRequest $request) { return ''; } + + public function fetchRouteResponse() + { + $fixture = new \stdClass(); + $fixture->id = 1; + $fixture->name = 'banana'; + $fixture->color = 'red'; + $fixture->weight = 300; + $fixture->delicious = 1; + + return [ + 'id' => (int) $fixture->id, + 'name' => ucfirst($fixture->name), + 'color' => ucfirst($fixture->color), + 'weight' => $fixture->weight . ' grams', + 'delicious' => (bool) $fixture->delicious, + ]; + } } diff --git a/tests/Fixtures/index.md b/tests/Fixtures/index.md index 51308611..9047985b 100644 --- a/tests/Fixtures/index.md +++ b/tests/Fixtures/index.md @@ -59,3 +59,46 @@ null `HEAD api/test` +## api/fetch + +> Example request: + +```bash +curl "http://localhost/api/fetch" \ +-H "Accept: application/json" +``` + +```javascript +var settings = { + "async": true, + "crossDomain": true, + "url": "http://localhost/api/fetch", + "method": "GET", + "headers": { + "accept": "application/json" + } +} + +$.ajax(settings).done(function (response) { +console.log(response); +}); +``` + +> Example response: + +```json +{ + "id": 1, + "name": "Banana", + "color": "Red", + "weight": "300 grams", + "delicious": true +} +``` + +### HTTP Request +`GET api/fetch` + +`HEAD api/fetch` + + diff --git a/tests/GenerateDocumentationTest.php b/tests/GenerateDocumentationTest.php index ce7eca22..1ce90f9e 100644 --- a/tests/GenerateDocumentationTest.php +++ b/tests/GenerateDocumentationTest.php @@ -60,6 +60,7 @@ public function testConsoleCommandDoesNotWorkWithClosure() public function testGeneratedMarkdownFileIsCorrect() { RouteFacade::get('/api/test', TestController::class.'@parseMethodDescription'); + RouteFacade::get('/api/fetch', TestController::class.'@fetchRouteResponse'); $output = $this->artisan('api:generate', [ '--routePrefix' => 'api/*', From 477dfcf2c0f7b915f77390c68b29a4612aae6e64 Mon Sep 17 00:00:00 2001 From: Marcel Pociot Date: Thu, 23 Jun 2016 15:04:28 -0400 Subject: [PATCH 047/780] Applied fixes from StyleCI --- tests/Fixtures/TestController.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/Fixtures/TestController.php b/tests/Fixtures/TestController.php index 1810645a..99f11c72 100644 --- a/tests/Fixtures/TestController.php +++ b/tests/Fixtures/TestController.php @@ -36,10 +36,10 @@ public function fetchRouteResponse() $fixture->delicious = 1; return [ - 'id' => (int) $fixture->id, - 'name' => ucfirst($fixture->name), - 'color' => ucfirst($fixture->color), - 'weight' => $fixture->weight . ' grams', + 'id' => (int) $fixture->id, + 'name' => ucfirst($fixture->name), + 'color' => ucfirst($fixture->color), + 'weight' => $fixture->weight.' grams', 'delicious' => (bool) $fixture->delicious, ]; } From dc5654281f9a669bb61660bc4428f3edc59b79dd Mon Sep 17 00:00:00 2001 From: vagrant Date: Tue, 5 Jul 2016 08:49:56 +0000 Subject: [PATCH 048/780] Fix failing tests under PHP5 --- src/Mpociot/ApiDoc/Commands/GenerateDocumentation.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Mpociot/ApiDoc/Commands/GenerateDocumentation.php b/src/Mpociot/ApiDoc/Commands/GenerateDocumentation.php index 16c3f1bb..d3d549a0 100644 --- a/src/Mpociot/ApiDoc/Commands/GenerateDocumentation.php +++ b/src/Mpociot/ApiDoc/Commands/GenerateDocumentation.php @@ -73,7 +73,7 @@ public function handle() } else { $parsedRoutes = $this->processDingoRoutes($generator, $allowedRoutes, $routePrefix); } - $parsedRoutes = collect($parsedRoutes)->sortBy('resource')->groupBy('resource'); + $parsedRoutes = collect($parsedRoutes)->groupBy('resource')->sortBy('resource'); $this->writeMarkdown($parsedRoutes); } From a2d1cc713214b1bccd1205a6c995be2963e9028a Mon Sep 17 00:00:00 2001 From: Marcel Pociot Date: Thu, 23 Jun 2016 15:04:28 -0400 Subject: [PATCH 049/780] RuleDescriptionParser: Refactored main Class and Test --- README.md | 5 +- composer.json | 3 +- .../ApiDoc/Parsers/RuleDescriptionParser.php | 44 ++++++++--- tests/Fixtures/TestController.php | 8 +- tests/RuleDescriptionParserTest.php | 79 +++++++++++++------ 5 files changed, 92 insertions(+), 47 deletions(-) diff --git a/README.md b/README.md index 61158439..0c954da9 100644 --- a/README.md +++ b/README.md @@ -50,14 +50,13 @@ Option | Description ## Publish rule descriptions for customisation or translation. - By default, this package returns the descriptions in the main language of the application. - You can publish the packages language files, to customise and translate the documentation output. + By default, this package returns the descriptions in english. You can publish the packages language files, to customise and translate the documentation output. ```sh $ php artisan vendor:publish ``` - After the files are published you can customise or translate the descriptions in the language you want by editing the files in `public/vendor/apidoc/resources/lang`. + After the files are published you can customise or translate the descriptions in the language you want by renaming the `en` folder and editing the files in `public/vendor/apidoc/resources/lang`. ### How does it work? diff --git a/composer.json b/composer.json index 0547330d..04d75dd4 100644 --- a/composer.json +++ b/composer.json @@ -24,7 +24,8 @@ "require-dev": { "orchestra/testbench": "~3.0", "phpunit/phpunit": "~4.0 || ~5.0", - "dingo/api": "1.0.*@dev" + "dingo/api": "1.0.*@dev", + "mockery/mockery": "^0.9.5" }, "autoload": { "psr-0": { diff --git a/src/Mpociot/ApiDoc/Parsers/RuleDescriptionParser.php b/src/Mpociot/ApiDoc/Parsers/RuleDescriptionParser.php index 26934add..06aa335c 100644 --- a/src/Mpociot/ApiDoc/Parsers/RuleDescriptionParser.php +++ b/src/Mpociot/ApiDoc/Parsers/RuleDescriptionParser.php @@ -8,12 +8,14 @@ class RuleDescriptionParser private $parameters = []; + const DEFAULT_LOCALE = 'en'; + /** * @param null $rule */ public function __construct($rule = null) { - $this->rule = $rule; + $this->rule = "apidoc::rules.{$rule}"; } /** @@ -21,11 +23,7 @@ public function __construct($rule = null) */ public function getDescription() { - $key = "apidoc::rules.{$this->rule}"; - - $description = $this->parameters ? $this->translateWithAttributes($key) : trans($key); - - return $description != $key ? $description : []; + return $this->ruleDescriptionExist() ? $this->makeDescription() : []; } /** @@ -35,25 +33,45 @@ public function getDescription() */ public function with($parameters) { - is_array($parameters) ? $this->parameters += $parameters : $this->parameters[] = $parameters; + is_array($parameters) ? + $this->parameters += $parameters : + $this->parameters[] = $parameters; return $this; } /** - * @param $key - * + * @return bool + */ + protected function ruleDescriptionExist() + { + return trans()->hasForLocale($this->rule) || trans()->hasForLocale($this->rule, self::DEFAULT_LOCALE); + } + + /** * @return string */ - protected function translateWithAttributes($key) + protected function makeDescription() { - $translate = trans($key); + $description = trans()->hasForLocale($this->rule) ? + trans()->get($this->rule) : + trans()->get($this->rule, [], self::DEFAULT_LOCALE); + + return $this->replaceAttributes($description); + } + /** + * @param string $description$ + * + * @return string + */ + protected function replaceAttributes($description) + { foreach ($this->parameters as $parameter) { - $translate = preg_replace('/:attribute/', $parameter, $translate, 1); + $description = preg_replace('/:attribute/', $parameter, $description, 1); } - return $translate; + return $description; } /** diff --git a/tests/Fixtures/TestController.php b/tests/Fixtures/TestController.php index 1810645a..99f11c72 100644 --- a/tests/Fixtures/TestController.php +++ b/tests/Fixtures/TestController.php @@ -36,10 +36,10 @@ public function fetchRouteResponse() $fixture->delicious = 1; return [ - 'id' => (int) $fixture->id, - 'name' => ucfirst($fixture->name), - 'color' => ucfirst($fixture->color), - 'weight' => $fixture->weight . ' grams', + 'id' => (int) $fixture->id, + 'name' => ucfirst($fixture->name), + 'color' => ucfirst($fixture->color), + 'weight' => $fixture->weight.' grams', 'delicious' => (bool) $fixture->delicious, ]; } diff --git a/tests/RuleDescriptionParserTest.php b/tests/RuleDescriptionParserTest.php index a255db2d..4ae9859f 100644 --- a/tests/RuleDescriptionParserTest.php +++ b/tests/RuleDescriptionParserTest.php @@ -2,17 +2,37 @@ namespace Mpociot\ApiDoc\Tests; +use Illuminate\Translation\LoaderInterface; +use Illuminate\Translation\Translator; use Mpociot\ApiDoc\ApiDocGeneratorServiceProvider; use Mpociot\ApiDoc\Parsers\RuleDescriptionParser; use Orchestra\Testbench\TestCase; +use Mockery as m; class RuleDescriptionParserTest extends TestCase { + protected $translatorMock; + + public function setUp() + { + parent::setUp(); + $fileLoaderMock = m::mock(LoaderInterface::class); + $this->translatorMock = m::mock(Translator::class, [$fileLoaderMock, 'es']); + $this->app->instance('translator', $this->translatorMock); + } + + public function tearDown() + { + m::close(); + } + public function testReturnsAnEmptyDescriptionIfARuleIsNotParsed() { - $rule = new RuleDescriptionParser(); + $this->translatorMock->shouldReceive('hasForLocale')->twice()->andReturn(false); + + $description = new RuleDescriptionParser(); - $this->assertEmpty($rule->getDescription()); + $this->assertEmpty($description->getDescription()); } public function testProvidesANamedContructor() @@ -20,51 +40,57 @@ public function testProvidesANamedContructor() $this->assertInstanceOf(RuleDescriptionParser::class, RuleDescriptionParser::parse()); } - public function testReturnsADescriptionInTheMainLanguageOfTheApplication() + public function testReturnsADescriptionInMainLanguageIfAvailable() { - $expected = 'Only alphabetic characters allowed'; - $rule = new RuleDescriptionParser('alpha'); + $this->translatorMock->shouldReceive('hasForLocale')->twice()->with('apidoc::rules.alpha')->andReturn(true); + $this->translatorMock->shouldReceive('get')->once()->with('apidoc::rules.alpha')->andReturn('Solo caracteres alfabeticos permitidos'); - $this->assertEquals($expected, $rule->getDescription()); + $description = RuleDescriptionParser::parse('alpha')->getDescription(); + + $this->assertEquals('Solo caracteres alfabeticos permitidos', $description); } - public function testReturnsAnEmptyDescriptionIfNotAvailable() + public function testReturnsDescriptionInDefaultLanguageIfNotAvailableInMainLanguage() { - $rule = new RuleDescriptionParser('dummy_rule'); + $this->translatorMock->shouldReceive('hasForLocale')->twice()->with('apidoc::rules.alpha')->andReturn(false); + $this->translatorMock->shouldReceive('hasForLocale')->once()->with('apidoc::rules.alpha', 'en')->andReturn(true); + $this->translatorMock->shouldReceive('get')->once()->with('apidoc::rules.alpha', [], 'en')->andReturn('Only alphabetic characters allowed'); - $description = $rule->getDescription(); + $description = RuleDescriptionParser::parse('alpha')->getDescription(); - $this->assertEmpty($description); + $this->assertEquals('Only alphabetic characters allowed', $description); } - public function testAllowsToPassParametersToTheDescription() + public function testReturnsAnEmptyDescriptionIfNotAvailable() { - $expected = 'Must have an exact length of `2`'; - $rule = new RuleDescriptionParser('digits'); + $this->translatorMock->shouldReceive('hasForLocale')->once()->with('apidoc::rules.dummy_rule')->andReturn(false); + $this->translatorMock->shouldReceive('hasForLocale')->once()->with('apidoc::rules.dummy_rule', 'en')->andReturn(false); - $actual = $rule->with(2)->getDescription(); + $description = RuleDescriptionParser::parse('dummy_rule')->getDescription(); - $this->assertEquals($expected, $actual); + $this->assertEmpty($description); } - public function testOnlyPassesParametersIfTheDescriptionAllows() + public function testAllowsToPassParametersToTheDescription() { - $expected = 'Only alphabetic characters allowed'; - $rule = new RuleDescriptionParser('alpha'); + $this->translatorMock->shouldReceive('hasForLocale')->twice()->with('apidoc::rules.digits')->andReturn(false); + $this->translatorMock->shouldReceive('hasForLocale')->once()->with('apidoc::rules.digits', 'en')->andReturn(true); + $this->translatorMock->shouldReceive('get')->once()->with('apidoc::rules.digits', [], 'en')->andReturn('Must have an exact length of `:attribute`'); - $actual = $rule->with('dummy parameter')->getDescription(); + $description = RuleDescriptionParser::parse('digits')->with(2)->getDescription(); - $this->assertEquals($expected, $actual); + $this->assertEquals('Must have an exact length of `2`', $description); } public function testAllowsToPassMultipleParametersToTheDescription() { - $expected = 'Required if `2 + 2` is `4`'; - $rule = new RuleDescriptionParser('required_if'); + $this->translatorMock->shouldReceive('hasForLocale')->twice()->with('apidoc::rules.required_if')->andReturn(false); + $this->translatorMock->shouldReceive('hasForLocale')->once()->with('apidoc::rules.required_if', 'en')->andReturn(true); + $this->translatorMock->shouldReceive('get')->once()->with('apidoc::rules.required_if', [], 'en')->andReturn('Required if `:attribute` is `:attribute`'); - $actual = $rule->with(['2 + 2', 4])->getDescription(); + $description = RuleDescriptionParser::parse('required_if')->with(['2 + 2', 4])->getDescription(); - $this->assertEquals($expected, $actual); + $this->assertEquals('Required if `2 + 2` is `4`', $description); } /** @@ -80,12 +106,13 @@ protected function getPackageProviders($app) /** * Define environment setup. * - * @param \Illuminate\Foundation\Application $app + * @param \Illuminate\Foundation\Application $app * * @return void */ protected function getEnvironmentSetUp($app) { - $app['config']->set('app.locale', 'en'); + $app['config']->set('app.locale', 'es'); // Just to be different to default language. + $app['config']->set('app.fallback_locale', 'ch'); // Just to be different to default language. } } From 741d7b9ca6819346149d86ed12b7ae5be1d5a734 Mon Sep 17 00:00:00 2001 From: zakhttp Date: Wed, 6 Jul 2016 23:18:21 +0400 Subject: [PATCH 050/780] Fix: Add resource_path() function to ApiDocGeneratorServiceProvider --- .../ApiDoc/ApiDocGeneratorServiceProvider.php | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/src/Mpociot/ApiDoc/ApiDocGeneratorServiceProvider.php b/src/Mpociot/ApiDoc/ApiDocGeneratorServiceProvider.php index 26565e42..1eb52ee8 100644 --- a/src/Mpociot/ApiDoc/ApiDocGeneratorServiceProvider.php +++ b/src/Mpociot/ApiDoc/ApiDocGeneratorServiceProvider.php @@ -19,7 +19,7 @@ public function boot() $this->loadTranslationsFrom(__DIR__.'/../../resources/lang', 'apidoc'); $this->publishes([ - __DIR__.'/../../resources/lang' => resource_path('lang/vendor/apidoc'), + __DIR__.'/../../resources/lang' => $this->resource_path('lang/vendor/apidoc'), ]); } @@ -42,4 +42,16 @@ public function register() 'apidoc.update', ]); } + + /** + * Return a fully qualified path to a given file. + * + * @param string $path + * + * @return string + */ + public function resource_path($path = '') + { + return app()->basePath().'/resources'.($path ? '/'.$path : $path); + } } From e24a1a1f53dae003a2065910007bbfeb652552fa Mon Sep 17 00:00:00 2001 From: Rick Sharp Date: Thu, 7 Jul 2016 16:38:26 -0600 Subject: [PATCH 051/780] FIX: Fixed "PHP Fatal error: Method Illuminate\View\View::__toString() must not throw an exception" when using with Dingo API. --- src/resources/views/documentarian.blade.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/resources/views/documentarian.blade.php b/src/resources/views/documentarian.blade.php index cf00cfba..92e92fbe 100644 --- a/src/resources/views/documentarian.blade.php +++ b/src/resources/views/documentarian.blade.php @@ -66,7 +66,11 @@ > Example response: ```json +@if(is_object($parsedRoute['response']) || is_array($parsedRoute['response'])) +{!! json_encode($parsedRoute['response'], JSON_PRETTY_PRINT) !!} +@else {!! json_encode(json_decode($parsedRoute['response']), JSON_PRETTY_PRINT) !!} +@endif ``` @endif From 30756137412d395d6bb3a21633cbd3256aa3a7e2 Mon Sep 17 00:00:00 2001 From: Marcel Pociot Date: Mon, 18 Jul 2016 10:46:46 +0200 Subject: [PATCH 052/780] Fixes #51 --- .../ApiDoc/Generators/AbstractGenerator.php | 13 ++++++++++--- src/Mpociot/ApiDoc/Generators/DingoGenerator.php | 2 +- .../ApiDoc/Generators/LaravelGenerator.php | 2 +- tests/Fixtures/DynamicRequest.php | 15 +++++++++++++++ tests/Fixtures/TestController.php | 5 +++++ tests/GenerateDocumentationTest.php | 14 ++++++++++++++ 6 files changed, 46 insertions(+), 5 deletions(-) create mode 100644 tests/Fixtures/DynamicRequest.php diff --git a/src/Mpociot/ApiDoc/Generators/AbstractGenerator.php b/src/Mpociot/ApiDoc/Generators/AbstractGenerator.php index 8d75560f..09a457e9 100644 --- a/src/Mpociot/ApiDoc/Generators/AbstractGenerator.php +++ b/src/Mpociot/ApiDoc/Generators/AbstractGenerator.php @@ -31,12 +31,13 @@ abstract public function processRoute($route, $bindings = [], $withResponse = tr /** * @param array $routeData * @param array $routeAction + * @param array $bindings * * @return mixed */ - protected function getParameters($routeData, $routeAction) + protected function getParameters($routeData, $routeAction, $bindings) { - $validator = Validator::make([], $this->getRouteRules($routeAction['uses'])); + $validator = Validator::make([], $this->getRouteRules($routeAction['uses'], $bindings)); foreach ($validator->getRules() as $attribute => $rules) { $attributeData = [ 'required' => false, @@ -128,10 +129,11 @@ protected function getRouteGroup($route) /** * @param $route + * @param array $bindings * * @return array */ - protected function getRouteRules($route) + protected function getRouteRules($route, $bindings) { list($class, $method) = explode('@', $route); $reflection = new ReflectionClass($class); @@ -141,8 +143,13 @@ protected function getRouteRules($route) $parameterType = $parameter->getClass(); if (! is_null($parameterType) && class_exists($parameterType->name)) { $className = $parameterType->name; + $parameterReflection = new $className; if ($parameterReflection instanceof FormRequest) { + // Add route parameter bindings + $parameterReflection->query->add($bindings); + $parameterReflection->request->add($bindings); + if (method_exists($parameterReflection, 'validator')) { return $parameterReflection->validator()->getRules(); } else { diff --git a/src/Mpociot/ApiDoc/Generators/DingoGenerator.php b/src/Mpociot/ApiDoc/Generators/DingoGenerator.php index 728c5743..59f1218d 100644 --- a/src/Mpociot/ApiDoc/Generators/DingoGenerator.php +++ b/src/Mpociot/ApiDoc/Generators/DingoGenerator.php @@ -36,7 +36,7 @@ public function processRoute($route, $bindings = [], $withResponse = true) 'uri' => $route->uri(), 'parameters' => [], 'response' => $response, - ], $routeAction); + ], $routeAction, $bindings); } /** diff --git a/src/Mpociot/ApiDoc/Generators/LaravelGenerator.php b/src/Mpociot/ApiDoc/Generators/LaravelGenerator.php index 0feb7633..84dbe238 100644 --- a/src/Mpociot/ApiDoc/Generators/LaravelGenerator.php +++ b/src/Mpociot/ApiDoc/Generators/LaravelGenerator.php @@ -51,7 +51,7 @@ public function processRoute($route, $bindings = [], $withResponse = true) 'uri' => $route->getUri(), 'parameters' => [], 'response' => $content, - ], $routeAction); + ], $routeAction, $bindings); } /** diff --git a/tests/Fixtures/DynamicRequest.php b/tests/Fixtures/DynamicRequest.php new file mode 100644 index 00000000..d370d231 --- /dev/null +++ b/tests/Fixtures/DynamicRequest.php @@ -0,0 +1,15 @@ + 'not_in:'.$this->foo, + ]; + } +} diff --git a/tests/Fixtures/TestController.php b/tests/Fixtures/TestController.php index 99f11c72..e0fa8b9c 100644 --- a/tests/Fixtures/TestController.php +++ b/tests/Fixtures/TestController.php @@ -26,6 +26,11 @@ public function parseFormRequestRules(TestRequest $request) return ''; } + public function addRouteBindingsToRequestClass(DynamicRequest $request) + { + return ''; + } + public function fetchRouteResponse() { $fixture = new \stdClass(); diff --git a/tests/GenerateDocumentationTest.php b/tests/GenerateDocumentationTest.php index 1ce90f9e..c99d9117 100644 --- a/tests/GenerateDocumentationTest.php +++ b/tests/GenerateDocumentationTest.php @@ -71,6 +71,20 @@ public function testGeneratedMarkdownFileIsCorrect() $this->assertSame($generatedMarkdown, $fixtureMarkdown); } + public function testAddsBindingsToGetRouteRules() + { + RouteFacade::get('/api/test/{foo}', TestController::class.'@addRouteBindingsToRequestClass'); + + $this->artisan('api:generate', [ + '--routePrefix' => 'api/*', + '--bindings' => 'foo,bar' + ]); + + $generatedMarkdown = file_get_contents(__DIR__.'/../public/docs/source/index.md'); + + $this->assertContains('Not in: `bar`', $generatedMarkdown); + } + /** * @param string $command * @param array $parameters From 4e7061b4c518268b8dbaa25df72669691ff267d6 Mon Sep 17 00:00:00 2001 From: Marcel Pociot Date: Mon, 18 Jul 2016 04:46:55 -0400 Subject: [PATCH 053/780] Applied fixes from StyleCI --- tests/GenerateDocumentationTest.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/GenerateDocumentationTest.php b/tests/GenerateDocumentationTest.php index c99d9117..af86d6e3 100644 --- a/tests/GenerateDocumentationTest.php +++ b/tests/GenerateDocumentationTest.php @@ -77,11 +77,11 @@ public function testAddsBindingsToGetRouteRules() $this->artisan('api:generate', [ '--routePrefix' => 'api/*', - '--bindings' => 'foo,bar' + '--bindings' => 'foo,bar', ]); - + $generatedMarkdown = file_get_contents(__DIR__.'/../public/docs/source/index.md'); - + $this->assertContains('Not in: `bar`', $generatedMarkdown); } From bc7c1e79c894aec15250bb567ec233ab31a21172 Mon Sep 17 00:00:00 2001 From: Marcel Pociot Date: Mon, 18 Jul 2016 22:26:22 +0200 Subject: [PATCH 054/780] Fixes #63 --- README.md | 6 ++ composer.json | 3 +- .../ApiDoc/Commands/GenerateDocumentation.php | 28 +++++++- .../ApiDoc/Postman/CollectionWriter.php | 67 +++++++++++++++++++ src/resources/views/documentarian.blade.php | 3 + tests/Fixtures/collection.json | 1 + tests/Fixtures/index.md | 1 + tests/GenerateDocumentationTest.php | 20 +++++- 8 files changed, 124 insertions(+), 5 deletions(-) create mode 100644 src/Mpociot/ApiDoc/Postman/CollectionWriter.php create mode 100644 tests/Fixtures/collection.json diff --git a/README.md b/README.md index 0c954da9..300fd79a 100644 --- a/README.md +++ b/README.md @@ -44,6 +44,7 @@ Option | Description `routePrefix` | The route prefix to use for generation - `*` can be used as a wildcard `routes` | The route names to use for generation - Required if no routePrefix is provided `noResponseCalls` | Disable API response calls +`noPostmanCollection` | Disable Postman collection creation `actAsUserId` | The user ID to use for authenticated API response calls `router` | The router to use, when processing the route files (can be Laravel or Dingo - defaults to Laravel) `bindings` | List of route bindings that should be replaced when trying to retrieve route results. Syntax format: `binding_one,id|binding_two,id` @@ -116,6 +117,11 @@ $ php artisan api:generate --routePrefix=api/* --noResponseCalls > Note: The example API responses work best with seeded data. +#### Postman collections + +The generator automatically creates a Postman collection file, which you can import to use within your [Postman App](https://www.getpostman.com/apps) for even simpler API testing and usage. + +If you don't want to create a Postman collection, use the `--noPostmanCollection` option, when generating the API documentation. ## Modify the generated documentation diff --git a/composer.json b/composer.json index 04d75dd4..5ecb6f57 100644 --- a/composer.json +++ b/composer.json @@ -19,7 +19,8 @@ "fzaninotto/faker": "^1.6", "laravel/framework": "~5.0", "mpociot/documentarian": "^0.2.0", - "mpociot/reflection-docblock": "^1.0" + "mpociot/reflection-docblock": "^1.0", + "ramsey/uuid": "^3.0" }, "require-dev": { "orchestra/testbench": "~3.0", diff --git a/src/Mpociot/ApiDoc/Commands/GenerateDocumentation.php b/src/Mpociot/ApiDoc/Commands/GenerateDocumentation.php index d3d549a0..e2750a17 100644 --- a/src/Mpociot/ApiDoc/Commands/GenerateDocumentation.php +++ b/src/Mpociot/ApiDoc/Commands/GenerateDocumentation.php @@ -8,6 +8,7 @@ use Mpociot\ApiDoc\Generators\AbstractGenerator; use Mpociot\ApiDoc\Generators\DingoGenerator; use Mpociot\ApiDoc\Generators\LaravelGenerator; +use Mpociot\ApiDoc\Postman\CollectionWriter; use Mpociot\Documentarian\Documentarian; class GenerateDocumentation extends Command @@ -21,7 +22,8 @@ class GenerateDocumentation extends Command {--output=public/docs : The output path for the generated documentation} {--routePrefix= : The route prefix to use for generation} {--routes=* : The route names to use for generation} - {--noResponseCalls : The user ID to use for API response calls} + {--noResponseCalls : Disable API response calls} + {--noPostmanCollection : Disable Postman collection creation} {--actAsUserId= : The user ID to use for API response calls} {--router=laravel : The router to be used (Laravel or Dingo)} {--bindings= : Route Model Bindings} @@ -89,7 +91,10 @@ private function writeMarkdown($parsedRoutes) $documentarian = new Documentarian(); - $markdown = view('apidoc::documentarian')->with('parsedRoutes', $parsedRoutes->all()); + $markdown = view('apidoc::documentarian') + ->with('outputPath', $this->option('output')) + ->with('showPostmanCollectionButton', !$this->option('noPostmanCollection')) + ->with('parsedRoutes', $parsedRoutes->all()); if (! is_dir($outputPath)) { $documentarian->create($outputPath); @@ -104,6 +109,13 @@ private function writeMarkdown($parsedRoutes) $documentarian->generate($outputPath); $this->info('Wrote HTML documentation to: '.$outputPath.'/public/index.html'); + + + if ($this->option('noPostmanCollection') !== true) { + $this->info('Generating Postman collection'); + + file_put_contents($outputPath.DIRECTORY_SEPARATOR.'collection.json', $this->generatePostmanCollection($parsedRoutes)); + } } /** @@ -214,4 +226,16 @@ private function isValidRoute($route) { return ! is_callable($route->getAction()['uses']); } + + /** + * Generate Postman collection JSON file + * + * @param Collection $routes + * @return string + */ + private function generatePostmanCollection(Collection $routes) + { + $writer = new CollectionWriter($routes); + return $writer->getCollection(); + } } diff --git a/src/Mpociot/ApiDoc/Postman/CollectionWriter.php b/src/Mpociot/ApiDoc/Postman/CollectionWriter.php new file mode 100644 index 00000000..9f7ba72c --- /dev/null +++ b/src/Mpociot/ApiDoc/Postman/CollectionWriter.php @@ -0,0 +1,67 @@ +routeGroups = $routeGroups; + } + + public function getCollection() + { + $collection = [ + 'variables' => [], + 'info' => [ + 'name' => '', + '_postman_id' => Uuid::uuid1()->toString(), + 'description' => '', + 'schema' => 'https://schema.getpostman.com/json/collection/v2.0.0/collection.json' + ], + 'item' => $this->routeGroups->map(function ($routes, $groupName) { + return [ + 'name' => $groupName, + 'description' => '', + 'item' => $routes->map(function ($route) { + return [ + 'name' => $route['title'] != '' ? $route['title'] : url(https://github.com/$route['uri']), + 'request' => [ + 'url' => url(https://github.com/$route['uri']), + 'method' => $route['methods'][0], + 'body' => [ + 'mode' => 'formdata', + 'formdata' => collect($route['parameters'])->map(function ($parameter, $key) { + return [ + 'key' => $key, + 'value' => isset($parameter['value']) ? $parameter['value'] : '', + 'type' => 'text', + 'enabled' => true + ]; + })->values()->toArray(), + ], + 'description' => $route['description'], + 'response' => [] + ] + ]; + })->toArray() + ]; + })->values()->toArray() + ]; + + return json_encode($collection); + } + +} \ No newline at end of file diff --git a/src/resources/views/documentarian.blade.php b/src/resources/views/documentarian.blade.php index 92e92fbe..0fd5960d 100644 --- a/src/resources/views/documentarian.blade.php +++ b/src/resources/views/documentarian.blade.php @@ -16,6 +16,9 @@ # Info Welcome to the generated API reference. +@if($showPostmanCollectionButton) +[Get Postman Collection]({{$outputPath}}/collection.json) +@endif # Available routes @foreach($parsedRoutes as $group => $routes) diff --git a/tests/Fixtures/collection.json b/tests/Fixtures/collection.json new file mode 100644 index 00000000..5dfe5567 --- /dev/null +++ b/tests/Fixtures/collection.json @@ -0,0 +1 @@ +{"variables":[],"info":{"name":"","_postman_id":"","description":"","schema":"https:\/\/schema.getpostman.com\/json\/collection\/v2.0.0\/collection.json"},"item":[{"name":"general","description":"","item":[{"name":"Example title.","request":{"url":"http:\/\/localhost\/api\/test","method":"GET","body":{"mode":"formdata","formdata":[]},"description":"This will be the long description.\nIt can also be multiple lines long.","response":[]}},{"name":"http:\/\/localhost\/api\/fetch","request":{"url":"http:\/\/localhost\/api\/fetch","method":"POST","body":{"mode":"formdata","formdata":[]},"description":"","response":[]}}]}]} \ No newline at end of file diff --git a/tests/Fixtures/index.md b/tests/Fixtures/index.md index 9047985b..3accf302 100644 --- a/tests/Fixtures/index.md +++ b/tests/Fixtures/index.md @@ -16,6 +16,7 @@ toc_footers: # Info Welcome to the generated API reference. +[Get Postman Collection](public/docs/collection.json) # Available routes #general diff --git a/tests/GenerateDocumentationTest.php b/tests/GenerateDocumentationTest.php index c99d9117..5c9d30ff 100644 --- a/tests/GenerateDocumentationTest.php +++ b/tests/GenerateDocumentationTest.php @@ -79,12 +79,28 @@ public function testAddsBindingsToGetRouteRules() '--routePrefix' => 'api/*', '--bindings' => 'foo,bar' ]); - + $generatedMarkdown = file_get_contents(__DIR__.'/../public/docs/source/index.md'); - + $this->assertContains('Not in: `bar`', $generatedMarkdown); } + public function testGeneratedPostmanCollectionFileIsCorrect() + { + RouteFacade::get('/api/test', TestController::class.'@parseMethodDescription'); + RouteFacade::post('/api/fetch', TestController::class.'@fetchRouteResponse'); + + $output = $this->artisan('api:generate', [ + '--routePrefix' => 'api/*', + ]); + + $generatedCollection = json_decode(file_get_contents(__DIR__.'/../public/docs/collection.json')); + $generatedCollection->info->_postman_id = ''; + + $fixtureCollection = json_decode(file_get_contents(__DIR__.'/Fixtures/collection.json')); + $this->assertEquals($generatedCollection, $fixtureCollection); + } + /** * @param string $command * @param array $parameters From 28143ca374e89de999769f55d04017a66c42490d Mon Sep 17 00:00:00 2001 From: Marcel Pociot Date: Mon, 18 Jul 2016 16:28:20 -0400 Subject: [PATCH 055/780] Applied fixes from StyleCI --- .../ApiDoc/Commands/GenerateDocumentation.php | 6 ++++-- src/Mpociot/ApiDoc/Postman/CollectionWriter.php | 16 ++++++++-------- 2 files changed, 12 insertions(+), 10 deletions(-) diff --git a/src/Mpociot/ApiDoc/Commands/GenerateDocumentation.php b/src/Mpociot/ApiDoc/Commands/GenerateDocumentation.php index e2750a17..94b169dc 100644 --- a/src/Mpociot/ApiDoc/Commands/GenerateDocumentation.php +++ b/src/Mpociot/ApiDoc/Commands/GenerateDocumentation.php @@ -93,7 +93,7 @@ private function writeMarkdown($parsedRoutes) $markdown = view('apidoc::documentarian') ->with('outputPath', $this->option('output')) - ->with('showPostmanCollectionButton', !$this->option('noPostmanCollection')) + ->with('showPostmanCollectionButton', ! $this->option('noPostmanCollection')) ->with('parsedRoutes', $parsedRoutes->all()); if (! is_dir($outputPath)) { @@ -228,14 +228,16 @@ private function isValidRoute($route) } /** - * Generate Postman collection JSON file + * Generate Postman collection JSON file. * * @param Collection $routes + * * @return string */ private function generatePostmanCollection(Collection $routes) { $writer = new CollectionWriter($routes); + return $writer->getCollection(); } } diff --git a/src/Mpociot/ApiDoc/Postman/CollectionWriter.php b/src/Mpociot/ApiDoc/Postman/CollectionWriter.php index 9f7ba72c..c95486de 100644 --- a/src/Mpociot/ApiDoc/Postman/CollectionWriter.php +++ b/src/Mpociot/ApiDoc/Postman/CollectionWriter.php @@ -14,6 +14,7 @@ class CollectionWriter /** * CollectionWriter constructor. + * * @param Collection $routeGroups */ public function __construct(Collection $routeGroups) @@ -29,7 +30,7 @@ public function getCollection() 'name' => '', '_postman_id' => Uuid::uuid1()->toString(), 'description' => '', - 'schema' => 'https://schema.getpostman.com/json/collection/v2.0.0/collection.json' + 'schema' => 'https://schema.getpostman.com/json/collection/v2.0.0/collection.json', ], 'item' => $this->routeGroups->map(function ($routes, $groupName) { return [ @@ -48,20 +49,19 @@ public function getCollection() 'key' => $key, 'value' => isset($parameter['value']) ? $parameter['value'] : '', 'type' => 'text', - 'enabled' => true + 'enabled' => true, ]; })->values()->toArray(), ], 'description' => $route['description'], - 'response' => [] - ] + 'response' => [], + ], ]; - })->toArray() + })->toArray(), ]; - })->values()->toArray() + })->values()->toArray(), ]; return json_encode($collection); } - -} \ No newline at end of file +} From c5bcdecae790b77de47ed34d93316d4144b95cbc Mon Sep 17 00:00:00 2001 From: Marcel Pociot Date: Tue, 9 Aug 2016 14:31:43 +0200 Subject: [PATCH 056/780] Update composer.json --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index 5ecb6f57..08472589 100644 --- a/composer.json +++ b/composer.json @@ -16,7 +16,7 @@ ], "require": { "php": ">=5.5.0", - "fzaninotto/faker": "^1.6", + "fzaninotto/faker": "~1.0", "laravel/framework": "~5.0", "mpociot/documentarian": "^0.2.0", "mpociot/reflection-docblock": "^1.0", From 74dd9d54ea6b3a5cd559b819b22d3c4f5d8a90e8 Mon Sep 17 00:00:00 2001 From: Marcel Pociot Date: Tue, 9 Aug 2016 15:40:19 +0200 Subject: [PATCH 057/780] Add check for route groups Modify FormRequest detection to allow usage of the IoC container --- .../ApiDoc/Commands/GenerateDocumentation.php | 2 +- .../ApiDoc/Generators/AbstractGenerator.php | 4 ++-- tests/ApiDocGeneratorTest.php | 8 ++++++++ tests/Fixtures/DependencyInjection.php | 18 ++++++++++++++++++ tests/Fixtures/TestController.php | 5 +++++ 5 files changed, 34 insertions(+), 3 deletions(-) create mode 100644 tests/Fixtures/DependencyInjection.php diff --git a/src/Mpociot/ApiDoc/Commands/GenerateDocumentation.php b/src/Mpociot/ApiDoc/Commands/GenerateDocumentation.php index 94b169dc..4a7a2c0e 100644 --- a/src/Mpociot/ApiDoc/Commands/GenerateDocumentation.php +++ b/src/Mpociot/ApiDoc/Commands/GenerateDocumentation.php @@ -224,7 +224,7 @@ private function processDingoRoutes(AbstractGenerator $generator, $allowedRoutes */ private function isValidRoute($route) { - return ! is_callable($route->getAction()['uses']); + return ! is_callable($route->getAction()['uses']) && ! is_null($route->getAction()['uses']); } /** diff --git a/src/Mpociot/ApiDoc/Generators/AbstractGenerator.php b/src/Mpociot/ApiDoc/Generators/AbstractGenerator.php index 09a457e9..cc8bc8b6 100644 --- a/src/Mpociot/ApiDoc/Generators/AbstractGenerator.php +++ b/src/Mpociot/ApiDoc/Generators/AbstractGenerator.php @@ -144,8 +144,8 @@ protected function getRouteRules($route, $bindings) if (! is_null($parameterType) && class_exists($parameterType->name)) { $className = $parameterType->name; - $parameterReflection = new $className; - if ($parameterReflection instanceof FormRequest) { + if (is_subclass_of($className,FormRequest::class)) { + $parameterReflection = new $className; // Add route parameter bindings $parameterReflection->query->add($bindings); $parameterReflection->request->add($bindings); diff --git a/tests/ApiDocGeneratorTest.php b/tests/ApiDocGeneratorTest.php index ff6a738e..c8a99618 100644 --- a/tests/ApiDocGeneratorTest.php +++ b/tests/ApiDocGeneratorTest.php @@ -68,6 +68,14 @@ public function testCanParseRouteMethods() $this->assertSame(['DELETE'], $parsed['methods']); } + public function testCanParseDependencyInjectionInControllerMethods() + { + RouteFacade::post('/post', TestController::class.'@dependencyInjection'); + $route = new Route(['POST'], '/post', ['uses' => TestController::class.'@dependencyInjection']); + $parsed = $this->generator->processRoute($route); + $this->assertTrue(is_array($parsed)); + } + public function testCanParseFormRequestRules() { RouteFacade::post('/post', TestController::class.'@parseFormRequestRules'); diff --git a/tests/Fixtures/DependencyInjection.php b/tests/Fixtures/DependencyInjection.php new file mode 100644 index 00000000..96ee56c0 --- /dev/null +++ b/tests/Fixtures/DependencyInjection.php @@ -0,0 +1,18 @@ +filesystem = $filesystem; + } +} diff --git a/tests/Fixtures/TestController.php b/tests/Fixtures/TestController.php index e0fa8b9c..2b865f6b 100644 --- a/tests/Fixtures/TestController.php +++ b/tests/Fixtures/TestController.php @@ -48,4 +48,9 @@ public function fetchRouteResponse() 'delicious' => (bool) $fixture->delicious, ]; } + + public function dependencyInjection(DependencyInjection $dependency, TestRequest $request) + { + return ''; + } } From ea32280fefb3a2906c72e3f93eb7bcd0573b4814 Mon Sep 17 00:00:00 2001 From: Marcel Pociot Date: Tue, 9 Aug 2016 09:40:59 -0400 Subject: [PATCH 058/780] Applied fixes from StyleCI --- src/Mpociot/ApiDoc/Generators/AbstractGenerator.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Mpociot/ApiDoc/Generators/AbstractGenerator.php b/src/Mpociot/ApiDoc/Generators/AbstractGenerator.php index cc8bc8b6..ca13134b 100644 --- a/src/Mpociot/ApiDoc/Generators/AbstractGenerator.php +++ b/src/Mpociot/ApiDoc/Generators/AbstractGenerator.php @@ -144,7 +144,7 @@ protected function getRouteRules($route, $bindings) if (! is_null($parameterType) && class_exists($parameterType->name)) { $className = $parameterType->name; - if (is_subclass_of($className,FormRequest::class)) { + if (is_subclass_of($className, FormRequest::class)) { $parameterReflection = new $className; // Add route parameter bindings $parameterReflection->query->add($bindings); From 70766385e19744bff191606145ba5b5cecd8cfcc Mon Sep 17 00:00:00 2001 From: Marcel Pociot Date: Wed, 10 Aug 2016 09:38:37 +0200 Subject: [PATCH 059/780] Remove 'Available routes' headline as it's confusing when used with groups --- src/resources/views/documentarian.blade.php | 1 - tests/Fixtures/index.md | 1 - 2 files changed, 2 deletions(-) diff --git a/src/resources/views/documentarian.blade.php b/src/resources/views/documentarian.blade.php index 0fd5960d..941e3e2c 100644 --- a/src/resources/views/documentarian.blade.php +++ b/src/resources/views/documentarian.blade.php @@ -20,7 +20,6 @@ [Get Postman Collection]({{$outputPath}}/collection.json) @endif -# Available routes @foreach($parsedRoutes as $group => $routes) @if($group) #{{$group}} diff --git a/tests/Fixtures/index.md b/tests/Fixtures/index.md index 3accf302..f45de169 100644 --- a/tests/Fixtures/index.md +++ b/tests/Fixtures/index.md @@ -18,7 +18,6 @@ toc_footers: Welcome to the generated API reference. [Get Postman Collection](public/docs/collection.json) -# Available routes #general ## Example title. From ac7513279798a007e1dc0bdd0d1d666f2365ac09 Mon Sep 17 00:00:00 2001 From: Marcel Pociot Date: Wed, 10 Aug 2016 10:10:47 +0200 Subject: [PATCH 060/780] Allow modification of info text --- .../ApiDoc/Commands/GenerateDocumentation.php | 20 ++++++++++++++++++- src/resources/views/documentarian.blade.php | 10 +++------- src/resources/views/partials/info.blade.php | 6 ++++++ tests/Fixtures/index.md | 3 ++- 4 files changed, 30 insertions(+), 9 deletions(-) create mode 100644 src/resources/views/partials/info.blade.php diff --git a/src/Mpociot/ApiDoc/Commands/GenerateDocumentation.php b/src/Mpociot/ApiDoc/Commands/GenerateDocumentation.php index 4a7a2c0e..fa6e8d0f 100644 --- a/src/Mpociot/ApiDoc/Commands/GenerateDocumentation.php +++ b/src/Mpociot/ApiDoc/Commands/GenerateDocumentation.php @@ -88,10 +88,28 @@ public function handle() private function writeMarkdown($parsedRoutes) { $outputPath = $this->option('output'); + $targetFile = $outputPath.DIRECTORY_SEPARATOR.'source'.DIRECTORY_SEPARATOR.'index.md'; + + $infoText = view('apidoc::partials.info') + ->with('outputPath', $this->option('output')) + ->with('showPostmanCollectionButton', ! $this->option('noPostmanCollection')); + + /** + * In case the target file already exists, we should check if the documentation was modified + * and skip the modified parts of the routes. + */ + if (file_exists($targetFile)) { + $generatedDocumentation = file_get_contents($targetFile); + + if (preg_match("/(.*)/is", $generatedDocumentation, $generatedInfoText)) { + $infoText = trim($generatedInfoText[1],"\n"); + } + } $documentarian = new Documentarian(); $markdown = view('apidoc::documentarian') + ->with('infoText', $infoText) ->with('outputPath', $this->option('output')) ->with('showPostmanCollectionButton', ! $this->option('noPostmanCollection')) ->with('parsedRoutes', $parsedRoutes->all()); @@ -100,7 +118,7 @@ private function writeMarkdown($parsedRoutes) $documentarian->create($outputPath); } - file_put_contents($outputPath.DIRECTORY_SEPARATOR.'source'.DIRECTORY_SEPARATOR.'index.md', $markdown); + file_put_contents($targetFile, $markdown); $this->info('Wrote index.md to: '.$outputPath); diff --git a/src/resources/views/documentarian.blade.php b/src/resources/views/documentarian.blade.php index 941e3e2c..9cfa42cc 100644 --- a/src/resources/views/documentarian.blade.php +++ b/src/resources/views/documentarian.blade.php @@ -12,13 +12,9 @@ toc_footers: - Documentation Powered by Documentarian --- - -# Info - -Welcome to the generated API reference. -@if($showPostmanCollectionButton) -[Get Postman Collection]({{$outputPath}}/collection.json) -@endif + +{!! $infoText !!} + @foreach($parsedRoutes as $group => $routes) @if($group) diff --git a/src/resources/views/partials/info.blade.php b/src/resources/views/partials/info.blade.php new file mode 100644 index 00000000..d8cb20ac --- /dev/null +++ b/src/resources/views/partials/info.blade.php @@ -0,0 +1,6 @@ +# Info + +Welcome to the generated API reference. +@if($showPostmanCollectionButton) +[Get Postman Collection]({{$outputPath}}/collection.json) +@endif \ No newline at end of file diff --git a/tests/Fixtures/index.md b/tests/Fixtures/index.md index f45de169..d8922145 100644 --- a/tests/Fixtures/index.md +++ b/tests/Fixtures/index.md @@ -12,11 +12,12 @@ search: true toc_footers: - Documentation Powered by Documentarian --- - + # Info Welcome to the generated API reference. [Get Postman Collection](public/docs/collection.json) + #general ## Example title. From 576ded0dabbc13bc3c12e51e10f301d6e9cb26c9 Mon Sep 17 00:00:00 2001 From: Marcel Pociot Date: Wed, 10 Aug 2016 10:28:18 +0200 Subject: [PATCH 061/780] Allow frontmatter modifications --- .../ApiDoc/Commands/GenerateDocumentation.php | 6 ++++++ src/resources/views/documentarian.blade.php | 13 +------------ src/resources/views/partials/frontmatter.blade.php | 12 ++++++++++++ tests/Fixtures/index.md | 1 + tests/GenerateDocumentationTest.php | 5 +++++ 5 files changed, 25 insertions(+), 12 deletions(-) create mode 100644 src/resources/views/partials/frontmatter.blade.php diff --git a/src/Mpociot/ApiDoc/Commands/GenerateDocumentation.php b/src/Mpociot/ApiDoc/Commands/GenerateDocumentation.php index fa6e8d0f..bf76da13 100644 --- a/src/Mpociot/ApiDoc/Commands/GenerateDocumentation.php +++ b/src/Mpociot/ApiDoc/Commands/GenerateDocumentation.php @@ -94,6 +94,7 @@ private function writeMarkdown($parsedRoutes) ->with('outputPath', $this->option('output')) ->with('showPostmanCollectionButton', ! $this->option('noPostmanCollection')); + $frontmatter = view('apidoc::partials.frontmatter'); /** * In case the target file already exists, we should check if the documentation was modified * and skip the modified parts of the routes. @@ -104,11 +105,16 @@ private function writeMarkdown($parsedRoutes) if (preg_match("/(.*)/is", $generatedDocumentation, $generatedInfoText)) { $infoText = trim($generatedInfoText[1],"\n"); } + + if (preg_match("/---(.*)---\\s/is", $generatedDocumentation, $generatedFrontmatter)) { + $frontmatter = trim($generatedFrontmatter[1],"\n"); + } } $documentarian = new Documentarian(); $markdown = view('apidoc::documentarian') + ->with('frontmatter', $frontmatter) ->with('infoText', $infoText) ->with('outputPath', $this->option('output')) ->with('showPostmanCollectionButton', ! $this->option('noPostmanCollection')) diff --git a/src/resources/views/documentarian.blade.php b/src/resources/views/documentarian.blade.php index 9cfa42cc..378c4ed3 100644 --- a/src/resources/views/documentarian.blade.php +++ b/src/resources/views/documentarian.blade.php @@ -1,16 +1,5 @@ --- -title: API Reference - -language_tabs: -- bash -- javascript - -includes: - -search: true - -toc_footers: -- Documentation Powered by Documentarian +{!! $frontmatter !!} --- {!! $infoText !!} diff --git a/src/resources/views/partials/frontmatter.blade.php b/src/resources/views/partials/frontmatter.blade.php new file mode 100644 index 00000000..c53f337c --- /dev/null +++ b/src/resources/views/partials/frontmatter.blade.php @@ -0,0 +1,12 @@ +title: API Reference + +language_tabs: +- bash +- javascript + +includes: + +search: true + +toc_footers: +- Documentation Powered by Documentarian \ No newline at end of file diff --git a/tests/Fixtures/index.md b/tests/Fixtures/index.md index d8922145..d6c86462 100644 --- a/tests/Fixtures/index.md +++ b/tests/Fixtures/index.md @@ -17,6 +17,7 @@ toc_footers: Welcome to the generated API reference. [Get Postman Collection](public/docs/collection.json) + #general diff --git a/tests/GenerateDocumentationTest.php b/tests/GenerateDocumentationTest.php index 19d7af16..7cf87344 100644 --- a/tests/GenerateDocumentationTest.php +++ b/tests/GenerateDocumentationTest.php @@ -27,6 +27,11 @@ public function setUp() $this->generator = new LaravelGenerator(); } + public function tearDown() + { + exec('rm -rf ' . __DIR__.'/../public/docs'); + } + /** * @param \Illuminate\Foundation\Application $app * From 463be9a8b2680fe0bed69a5de6e65799a8033e81 Mon Sep 17 00:00:00 2001 From: Marcel Pociot Date: Wed, 10 Aug 2016 11:57:08 +0200 Subject: [PATCH 062/780] Allow modification of route texts --- .../ApiDoc/Commands/GenerateDocumentation.php | 19 +++++- .../ApiDoc/Generators/DingoGenerator.php | 1 + .../ApiDoc/Generators/LaravelGenerator.php | 1 + src/resources/views/documentarian.blade.php | 67 +----------------- src/resources/views/partials/route.blade.php | 68 +++++++++++++++++++ tests/Fixtures/index.md | 4 ++ 6 files changed, 93 insertions(+), 67 deletions(-) create mode 100644 src/resources/views/partials/route.blade.php diff --git a/src/Mpociot/ApiDoc/Commands/GenerateDocumentation.php b/src/Mpociot/ApiDoc/Commands/GenerateDocumentation.php index bf76da13..a3529b25 100644 --- a/src/Mpociot/ApiDoc/Commands/GenerateDocumentation.php +++ b/src/Mpociot/ApiDoc/Commands/GenerateDocumentation.php @@ -26,6 +26,7 @@ class GenerateDocumentation extends Command {--noPostmanCollection : Disable Postman collection creation} {--actAsUserId= : The user ID to use for API response calls} {--router=laravel : The router to be used (Laravel or Dingo)} + {--force : Force rewriting of existing routes} {--bindings= : Route Model Bindings} '; @@ -94,6 +95,13 @@ private function writeMarkdown($parsedRoutes) ->with('outputPath', $this->option('output')) ->with('showPostmanCollectionButton', ! $this->option('noPostmanCollection')); + $parsedRouteOutput = $parsedRoutes->map(function ($routeGroup) { + return $routeGroup->map(function($route){ + $route['output'] = (string)view('apidoc::partials.route')->with('parsedRoute', $route); + return $route; + }); + }); + $frontmatter = view('apidoc::partials.frontmatter'); /** * In case the target file already exists, we should check if the documentation was modified @@ -109,6 +117,15 @@ private function writeMarkdown($parsedRoutes) if (preg_match("/---(.*)---\\s/is", $generatedDocumentation, $generatedFrontmatter)) { $frontmatter = trim($generatedFrontmatter[1],"\n"); } + + $parsedRouteOutput->transform(function ($routeGroup) use($generatedDocumentation) { + return $routeGroup->transform(function($route) use($generatedDocumentation) { + if (preg_match("/(.*)/is", $generatedDocumentation, $routeMatch) && !$this->option('force')) { + $route['output'] = $routeMatch[0]; + } + return $route; + }); + }); } $documentarian = new Documentarian(); @@ -118,7 +135,7 @@ private function writeMarkdown($parsedRoutes) ->with('infoText', $infoText) ->with('outputPath', $this->option('output')) ->with('showPostmanCollectionButton', ! $this->option('noPostmanCollection')) - ->with('parsedRoutes', $parsedRoutes->all()); + ->with('parsedRoutes', $parsedRouteOutput); if (! is_dir($outputPath)) { $documentarian->create($outputPath); diff --git a/src/Mpociot/ApiDoc/Generators/DingoGenerator.php b/src/Mpociot/ApiDoc/Generators/DingoGenerator.php index 59f1218d..60cb5d95 100644 --- a/src/Mpociot/ApiDoc/Generators/DingoGenerator.php +++ b/src/Mpociot/ApiDoc/Generators/DingoGenerator.php @@ -29,6 +29,7 @@ public function processRoute($route, $bindings = [], $withResponse = true) $routeDescription = $this->getRouteDescription($routeAction['uses']); return $this->getParameters([ + 'id' => md5($route->uri().':'.implode($route->getMethods())), 'resource' => $routeGroup, 'title' => $routeDescription['short'], 'description' => $routeDescription['long'], diff --git a/src/Mpociot/ApiDoc/Generators/LaravelGenerator.php b/src/Mpociot/ApiDoc/Generators/LaravelGenerator.php index 84dbe238..b024e97e 100644 --- a/src/Mpociot/ApiDoc/Generators/LaravelGenerator.php +++ b/src/Mpociot/ApiDoc/Generators/LaravelGenerator.php @@ -44,6 +44,7 @@ public function processRoute($route, $bindings = [], $withResponse = true) } return $this->getParameters([ + 'id' => md5($route->getUri().':'.implode($route->getMethods())), 'resource' => $routeGroup, 'title' => $routeDescription['short'], 'description' => $routeDescription['long'], diff --git a/src/resources/views/documentarian.blade.php b/src/resources/views/documentarian.blade.php index 378c4ed3..eb4f6df3 100644 --- a/src/resources/views/documentarian.blade.php +++ b/src/resources/views/documentarian.blade.php @@ -10,71 +10,6 @@ #{{$group}} @endif @foreach($routes as $parsedRoute) -@if($parsedRoute['title'] != '')## {{ $parsedRoute['title']}} -@else## {{$parsedRoute['uri']}} -@endif -@if($parsedRoute['description']) - -{{$parsedRoute['description']}} -@endif - -> Example request: - -```bash -curl "{{config('app.url')}}/{{$parsedRoute['uri']}}" \ --H "Accept: application/json"@if(count($parsedRoute['parameters'])) \ -@foreach($parsedRoute['parameters'] as $attribute => $parameter) - -d "{{$attribute}}"="{{$parameter['value']}}" \ -@endforeach -@endif - -``` - -```javascript -var settings = { - "async": true, - "crossDomain": true, - "url": "{{config('app.url')}}/{{$parsedRoute['uri']}}", - "method": "{{$parsedRoute['methods'][0]}}", - @if(count($parsedRoute['parameters'])) -"data": {!! str_replace(' ',' ',json_encode(array_combine(array_keys($parsedRoute['parameters']), array_map(function($param){ return $param['value']; },$parsedRoute['parameters'])), JSON_PRETTY_PRINT)) !!}, - @endif - "headers": { - "accept": "application/json" - } -} - -$.ajax(settings).done(function (response) { -console.log(response); -}); -``` - -@if(in_array('GET',$parsedRoute['methods'])) -> Example response: - -```json -@if(is_object($parsedRoute['response']) || is_array($parsedRoute['response'])) -{!! json_encode($parsedRoute['response'], JSON_PRETTY_PRINT) !!} -@else -{!! json_encode(json_decode($parsedRoute['response']), JSON_PRETTY_PRINT) !!} -@endif -``` -@endif - -### HTTP Request -@foreach($parsedRoute['methods'] as $method) -`{{$method}} {{$parsedRoute['uri']}}` - -@endforeach -@if(count($parsedRoute['parameters'])) -#### Parameters - -Parameter | Type | Status | Description ---------- | ------- | ------- | ------- | ----------- -@foreach($parsedRoute['parameters'] as $attribute => $parameter) - {{$attribute}} | {{$parameter['type']}} | @if($parameter['required']) required @else optional @endif | {!! implode(' ',$parameter['description']) !!} -@endforeach -@endif - +{!! $parsedRoute['output'] !!} @endforeach @endforeach diff --git a/src/resources/views/partials/route.blade.php b/src/resources/views/partials/route.blade.php new file mode 100644 index 00000000..78f1bd13 --- /dev/null +++ b/src/resources/views/partials/route.blade.php @@ -0,0 +1,68 @@ + +@if($parsedRoute['title'] != '')## {{ $parsedRoute['title']}} +@else## {{$parsedRoute['uri']}} +@endif +@if($parsedRoute['description']) + +{{$parsedRoute['description']}} +@endif + +> Example request: + +```bash +curl "{{config('app.url')}}/{{$parsedRoute['uri']}}" \ +-H "Accept: application/json"@if(count($parsedRoute['parameters'])) \ +@foreach($parsedRoute['parameters'] as $attribute => $parameter) + -d "{{$attribute}}"="{{$parameter['value']}}" \ +@endforeach +@endif + +``` + +```javascript +var settings = { + "async": true, + "crossDomain": true, + "url": "{{config('app.url')}}/{{$parsedRoute['uri']}}", + "method": "{{$parsedRoute['methods'][0]}}", + @if(count($parsedRoute['parameters'])) +"data": {!! str_replace(' ',' ',json_encode(array_combine(array_keys($parsedRoute['parameters']), array_map(function($param){ return $param['value']; },$parsedRoute['parameters'])), JSON_PRETTY_PRINT)) !!}, + @endif + "headers": { + "accept": "application/json" + } +} + +$.ajax(settings).done(function (response) { +console.log(response); +}); +``` + +@if(in_array('GET',$parsedRoute['methods'])) +> Example response: + +```json +@if(is_object($parsedRoute['response']) || is_array($parsedRoute['response'])) +{!! json_encode($parsedRoute['response'], JSON_PRETTY_PRINT) !!} +@else +{!! json_encode(json_decode($parsedRoute['response']), JSON_PRETTY_PRINT) !!} +@endif +``` +@endif + +### HTTP Request +@foreach($parsedRoute['methods'] as $method) +`{{$method}} {{$parsedRoute['uri']}}` + +@endforeach +@if(count($parsedRoute['parameters'])) +#### Parameters + +Parameter | Type | Status | Description +--------- | ------- | ------- | ------- | ----------- +@foreach($parsedRoute['parameters'] as $attribute => $parameter) + {{$attribute}} | {{$parameter['type']}} | @if($parameter['required']) required @else optional @endif | {!! implode(' ',$parameter['description']) !!} +@endforeach +@endif + + \ No newline at end of file diff --git a/tests/Fixtures/index.md b/tests/Fixtures/index.md index d6c86462..08658562 100644 --- a/tests/Fixtures/index.md +++ b/tests/Fixtures/index.md @@ -21,6 +21,7 @@ Welcome to the generated API reference. #general + ## Example title. This will be the long description. @@ -61,6 +62,8 @@ null `HEAD api/test` + + ## api/fetch > Example request: @@ -104,3 +107,4 @@ console.log(response); `HEAD api/fetch` + From d513baa63a5e24c2076eb636dedd2bd140a54f4e Mon Sep 17 00:00:00 2001 From: Marcel Pociot Date: Wed, 10 Aug 2016 11:58:04 +0200 Subject: [PATCH 063/780] Update README --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 300fd79a..fbbb376a 100644 --- a/README.md +++ b/README.md @@ -48,6 +48,7 @@ Option | Description `actAsUserId` | The user ID to use for authenticated API response calls `router` | The router to use, when processing the route files (can be Laravel or Dingo - defaults to Laravel) `bindings` | List of route bindings that should be replaced when trying to retrieve route results. Syntax format: `binding_one,id|binding_two,id` +`force` | Force the re-generation of existing/modified API routes ## Publish rule descriptions for customisation or translation. From f00d211049e670342a3b555d00a483b3f5b91fdc Mon Sep 17 00:00:00 2001 From: Marcel Pociot Date: Wed, 10 Aug 2016 06:01:20 -0400 Subject: [PATCH 064/780] Applied fixes from StyleCI --- .../ApiDoc/Commands/GenerateDocumentation.php | 22 ++++++++++--------- tests/GenerateDocumentationTest.php | 2 +- 2 files changed, 13 insertions(+), 11 deletions(-) diff --git a/src/Mpociot/ApiDoc/Commands/GenerateDocumentation.php b/src/Mpociot/ApiDoc/Commands/GenerateDocumentation.php index a3529b25..22d7dfa6 100644 --- a/src/Mpociot/ApiDoc/Commands/GenerateDocumentation.php +++ b/src/Mpociot/ApiDoc/Commands/GenerateDocumentation.php @@ -96,33 +96,35 @@ private function writeMarkdown($parsedRoutes) ->with('showPostmanCollectionButton', ! $this->option('noPostmanCollection')); $parsedRouteOutput = $parsedRoutes->map(function ($routeGroup) { - return $routeGroup->map(function($route){ - $route['output'] = (string)view('apidoc::partials.route')->with('parsedRoute', $route); + return $routeGroup->map(function ($route) { + $route['output'] = (string) view('apidoc::partials.route')->with('parsedRoute', $route); + return $route; }); }); $frontmatter = view('apidoc::partials.frontmatter'); - /** + /* * In case the target file already exists, we should check if the documentation was modified * and skip the modified parts of the routes. */ if (file_exists($targetFile)) { $generatedDocumentation = file_get_contents($targetFile); - if (preg_match("/(.*)/is", $generatedDocumentation, $generatedInfoText)) { - $infoText = trim($generatedInfoText[1],"\n"); + if (preg_match('/(.*)/is', $generatedDocumentation, $generatedInfoText)) { + $infoText = trim($generatedInfoText[1], "\n"); } - if (preg_match("/---(.*)---\\s/is", $generatedDocumentation, $generatedFrontmatter)) { - $frontmatter = trim($generatedFrontmatter[1],"\n"); + if (preg_match('/---(.*)---\\s/is', $generatedDocumentation, $generatedFrontmatter)) { + $frontmatter = trim($generatedFrontmatter[1], "\n"); } - $parsedRouteOutput->transform(function ($routeGroup) use($generatedDocumentation) { - return $routeGroup->transform(function($route) use($generatedDocumentation) { - if (preg_match("/(.*)/is", $generatedDocumentation, $routeMatch) && !$this->option('force')) { + $parsedRouteOutput->transform(function ($routeGroup) use ($generatedDocumentation) { + return $routeGroup->transform(function ($route) use ($generatedDocumentation) { + if (preg_match('/(.*)/is', $generatedDocumentation, $routeMatch) && ! $this->option('force')) { $route['output'] = $routeMatch[0]; } + return $route; }); }); diff --git a/tests/GenerateDocumentationTest.php b/tests/GenerateDocumentationTest.php index 7cf87344..2ee014de 100644 --- a/tests/GenerateDocumentationTest.php +++ b/tests/GenerateDocumentationTest.php @@ -29,7 +29,7 @@ public function setUp() public function tearDown() { - exec('rm -rf ' . __DIR__.'/../public/docs'); + exec('rm -rf '.__DIR__.'/../public/docs'); } /** From 1b5501ee05bc24e47183ccf46a03c20cfba72709 Mon Sep 17 00:00:00 2001 From: Marcel Pociot Date: Wed, 10 Aug 2016 12:46:40 +0200 Subject: [PATCH 065/780] Don't escape description --- src/resources/views/partials/route.blade.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/resources/views/partials/route.blade.php b/src/resources/views/partials/route.blade.php index 78f1bd13..04f396e8 100644 --- a/src/resources/views/partials/route.blade.php +++ b/src/resources/views/partials/route.blade.php @@ -4,7 +4,7 @@ @endif @if($parsedRoute['description']) -{{$parsedRoute['description']}} +{!! $parsedRoute['description'] !!} @endif > Example request: From 64714c7cd99a9ee62a47c690f1850accf6b4de2b Mon Sep 17 00:00:00 2001 From: Marcel Pociot Date: Wed, 10 Aug 2016 13:32:05 +0200 Subject: [PATCH 066/780] Added '@hideFromAPIDocumentation' tag --- .../ApiDoc/Commands/GenerateDocumentation.php | 26 +++++++++++++++++-- tests/Fixtures/TestController.php | 8 ++++++ tests/GenerateDocumentationTest.php | 14 +++++++++- 3 files changed, 45 insertions(+), 3 deletions(-) diff --git a/src/Mpociot/ApiDoc/Commands/GenerateDocumentation.php b/src/Mpociot/ApiDoc/Commands/GenerateDocumentation.php index a3529b25..eeb4dbf7 100644 --- a/src/Mpociot/ApiDoc/Commands/GenerateDocumentation.php +++ b/src/Mpociot/ApiDoc/Commands/GenerateDocumentation.php @@ -10,6 +10,8 @@ use Mpociot\ApiDoc\Generators\LaravelGenerator; use Mpociot\ApiDoc\Postman\CollectionWriter; use Mpociot\Documentarian\Documentarian; +use Mpociot\Reflection\DocBlock; +use ReflectionClass; class GenerateDocumentation extends Command { @@ -223,11 +225,11 @@ private function processLaravelRoutes(AbstractGenerator $generator, $allowedRout $parsedRoutes = []; foreach ($routes as $route) { if (in_array($route->getName(), $allowedRoutes) || str_is($routePrefix, $route->getUri())) { - if ($this->isValidRoute($route)) { + if ($this->isValidRoute($route) && $this->isRouteVisibleForDocumentation($route->getAction()['uses'])) { $parsedRoutes[] = $generator->processRoute($route, $bindings, $withResponse); $this->info('Processed route: '.$route->getUri()); } else { - $this->warn('Skipping route: '.$route->getUri().' - contains closure.'); + $this->warn('Skipping route: '.$route->getUri()); } } } @@ -268,6 +270,26 @@ private function isValidRoute($route) return ! is_callable($route->getAction()['uses']) && ! is_null($route->getAction()['uses']); } + /** + * @param $route + * @return boolean + */ + private function isRouteVisibleForDocumentation($route) + { + list($class, $method) = explode('@', $route); + $reflection = new ReflectionClass($class); + $comment = $reflection->getMethod($method)->getDocComment(); + if ($comment) { + $phpdoc = new DocBlock($comment); + return collect($phpdoc->getTags()) + ->filter(function($tag) use ($route){ + return $tag->getName() === 'hideFromAPIDocumentation'; + }) + ->isEmpty(); + } + return true; + } + /** * Generate Postman collection JSON file. * diff --git a/tests/Fixtures/TestController.php b/tests/Fixtures/TestController.php index 2b865f6b..4efe2d24 100644 --- a/tests/Fixtures/TestController.php +++ b/tests/Fixtures/TestController.php @@ -53,4 +53,12 @@ public function dependencyInjection(DependencyInjection $dependency, TestRequest { return ''; } + + /** + * @hideFromAPIDocumentation + */ + public function skip() + { + + } } diff --git a/tests/GenerateDocumentationTest.php b/tests/GenerateDocumentationTest.php index 7cf87344..52f2f136 100644 --- a/tests/GenerateDocumentationTest.php +++ b/tests/GenerateDocumentationTest.php @@ -58,7 +58,19 @@ public function testConsoleCommandDoesNotWorkWithClosure() $output = $this->artisan('api:generate', [ '--routePrefix' => 'api/*', ]); - $this->assertContains('Skipping route: api/closure - contains closure.', $output); + $this->assertContains('Skipping route: api/closure', $output); + $this->assertContains('Processed route: api/test', $output); + } + + public function testCanSkipSingleRoutesCommandDoesNotWorkWithClosure() + { + RouteFacade::get('/api/skip', TestController::class.'@skip'); + RouteFacade::get('/api/test', TestController::class.'@parseMethodDescription'); + + $output = $this->artisan('api:generate', [ + '--routePrefix' => 'api/*', + ]); + $this->assertContains('Skipping route: api/skip', $output); $this->assertContains('Processed route: api/test', $output); } From 0626c807c0fae7b6b589d8bd83a697640999d3e7 Mon Sep 17 00:00:00 2001 From: Marcel Pociot Date: Wed, 10 Aug 2016 07:32:31 -0400 Subject: [PATCH 067/780] Applied fixes from StyleCI --- src/Mpociot/ApiDoc/Commands/GenerateDocumentation.php | 7 +++++-- tests/Fixtures/TestController.php | 1 - 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/Mpociot/ApiDoc/Commands/GenerateDocumentation.php b/src/Mpociot/ApiDoc/Commands/GenerateDocumentation.php index 8e1a0a32..3b7a3b65 100644 --- a/src/Mpociot/ApiDoc/Commands/GenerateDocumentation.php +++ b/src/Mpociot/ApiDoc/Commands/GenerateDocumentation.php @@ -274,7 +274,8 @@ private function isValidRoute($route) /** * @param $route - * @return boolean + * + * @return bool */ private function isRouteVisibleForDocumentation($route) { @@ -283,12 +284,14 @@ private function isRouteVisibleForDocumentation($route) $comment = $reflection->getMethod($method)->getDocComment(); if ($comment) { $phpdoc = new DocBlock($comment); + return collect($phpdoc->getTags()) - ->filter(function($tag) use ($route){ + ->filter(function ($tag) use ($route) { return $tag->getName() === 'hideFromAPIDocumentation'; }) ->isEmpty(); } + return true; } diff --git a/tests/Fixtures/TestController.php b/tests/Fixtures/TestController.php index 4efe2d24..2fa20717 100644 --- a/tests/Fixtures/TestController.php +++ b/tests/Fixtures/TestController.php @@ -59,6 +59,5 @@ public function dependencyInjection(DependencyInjection $dependency, TestRequest */ public function skip() { - } } From 18c36aa6ec9812bd04a7f91fadb611b6ebf54143 Mon Sep 17 00:00:00 2001 From: Marcel Pociot Date: Wed, 10 Aug 2016 13:34:10 +0200 Subject: [PATCH 068/780] Add hideFromAPIDocumentation to readme --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index fbbb376a..461cfa4b 100644 --- a/README.md +++ b/README.md @@ -137,6 +137,10 @@ $ php artisan api:update As an optional parameter, you can use `--location` to tell the update command where your documentation can be found. +## Skip single routes + +If you want to skip a single route from a list of routes that match a given prefix, you can use the `@hideFromAPIDocumentation` tag on the Controller method you do not want to document. + ## Further modification This package uses [Documentarian](https://github.com/mpociot/documentarian) to generate the API documentation. If you want to modify the CSS files of your documentation, or simply want to learn more about what is possible, take a look at the [Documentarian guide](http://marcelpociot.com/documentarian/installation). From 894367bc02dec90164c873f3f76433fb05a8bdc9 Mon Sep 17 00:00:00 2001 From: Marcel Pociot Date: Wed, 10 Aug 2016 16:16:43 +0200 Subject: [PATCH 069/780] Compare generated routes and allow modifications --- .../ApiDoc/Commands/GenerateDocumentation.php | 56 ++++++++++++------- .../ApiDoc/Generators/AbstractGenerator.php | 5 +- tests/GenerateDocumentationTest.php | 4 +- 3 files changed, 43 insertions(+), 22 deletions(-) diff --git a/src/Mpociot/ApiDoc/Commands/GenerateDocumentation.php b/src/Mpociot/ApiDoc/Commands/GenerateDocumentation.php index 3b7a3b65..278f3018 100644 --- a/src/Mpociot/ApiDoc/Commands/GenerateDocumentation.php +++ b/src/Mpociot/ApiDoc/Commands/GenerateDocumentation.php @@ -92,41 +92,49 @@ private function writeMarkdown($parsedRoutes) { $outputPath = $this->option('output'); $targetFile = $outputPath.DIRECTORY_SEPARATOR.'source'.DIRECTORY_SEPARATOR.'index.md'; + $compareFile = $outputPath.DIRECTORY_SEPARATOR.'source'.DIRECTORY_SEPARATOR.'.compare.md'; $infoText = view('apidoc::partials.info') ->with('outputPath', $this->option('output')) ->with('showPostmanCollectionButton', ! $this->option('noPostmanCollection')); $parsedRouteOutput = $parsedRoutes->map(function ($routeGroup) { - return $routeGroup->map(function ($route) { - $route['output'] = (string) view('apidoc::partials.route')->with('parsedRoute', $route); - + return $routeGroup->map(function($route){ + $route['output'] = (string)view('apidoc::partials.route')->with('parsedRoute', $route); return $route; }); }); $frontmatter = view('apidoc::partials.frontmatter'); - /* + /** * In case the target file already exists, we should check if the documentation was modified * and skip the modified parts of the routes. */ - if (file_exists($targetFile)) { + if (file_exists($targetFile) && file_exists($compareFile)) { $generatedDocumentation = file_get_contents($targetFile); + $compareDocumentation = file_get_contents($compareFile); - if (preg_match('/(.*)/is', $generatedDocumentation, $generatedInfoText)) { - $infoText = trim($generatedInfoText[1], "\n"); + if (preg_match("/(.*)/is", $generatedDocumentation, $generatedInfoText)) { + $infoText = trim($generatedInfoText[1],"\n"); } - if (preg_match('/---(.*)---\\s/is', $generatedDocumentation, $generatedFrontmatter)) { - $frontmatter = trim($generatedFrontmatter[1], "\n"); + if (preg_match("/---(.*)---\\s/is", $generatedDocumentation, $generatedFrontmatter)) { + $frontmatter = trim($generatedFrontmatter[1],"\n"); } - $parsedRouteOutput->transform(function ($routeGroup) use ($generatedDocumentation) { - return $routeGroup->transform(function ($route) use ($generatedDocumentation) { - if (preg_match('/(.*)/is', $generatedDocumentation, $routeMatch) && ! $this->option('force')) { - $route['output'] = $routeMatch[0]; + $parsedRouteOutput->transform(function ($routeGroup) use($generatedDocumentation, $compareDocumentation) { + return $routeGroup->transform(function($route) use($generatedDocumentation, $compareDocumentation) { + if (preg_match("/(.*)/is", $generatedDocumentation, $routeMatch)) { + $routeDocumentationChanged = (preg_match("/(.*)/is", $compareDocumentation, $compareMatch) && $compareMatch[1] !== $routeMatch[1]); + if ($routeDocumentationChanged === false || $this->option('force')) { + if ($routeDocumentationChanged) { + $this->warn('Discarded manual changes for route ['.implode(',', $route['methods']).'] '.$route['uri']); + } + } else { + $this->warn('Skipping modified route ['.implode(',', $route['methods']).'] '.$route['uri']); + $route['modified_output'] = $routeMatch[0]; + } } - return $route; }); }); @@ -135,6 +143,7 @@ private function writeMarkdown($parsedRoutes) $documentarian = new Documentarian(); $markdown = view('apidoc::documentarian') + ->with('writeCompareFile', false) ->with('frontmatter', $frontmatter) ->with('infoText', $infoText) ->with('outputPath', $this->option('output')) @@ -145,8 +154,20 @@ private function writeMarkdown($parsedRoutes) $documentarian->create($outputPath); } + // Write output file file_put_contents($targetFile, $markdown); + // Write comparable markdown file + $compareMarkdown = view('apidoc::documentarian') + ->with('writeCompareFile', true) + ->with('frontmatter', $frontmatter) + ->with('infoText', $infoText) + ->with('outputPath', $this->option('output')) + ->with('showPostmanCollectionButton', ! $this->option('noPostmanCollection')) + ->with('parsedRoutes', $parsedRouteOutput); + + file_put_contents($compareFile, $compareMarkdown); + $this->info('Wrote index.md to: '.$outputPath); $this->info('Generating API HTML code'); @@ -274,8 +295,7 @@ private function isValidRoute($route) /** * @param $route - * - * @return bool + * @return boolean */ private function isRouteVisibleForDocumentation($route) { @@ -284,14 +304,12 @@ private function isRouteVisibleForDocumentation($route) $comment = $reflection->getMethod($method)->getDocComment(); if ($comment) { $phpdoc = new DocBlock($comment); - return collect($phpdoc->getTags()) - ->filter(function ($tag) use ($route) { + ->filter(function($tag) use ($route){ return $tag->getName() === 'hideFromAPIDocumentation'; }) ->isEmpty(); } - return true; } diff --git a/src/Mpociot/ApiDoc/Generators/AbstractGenerator.php b/src/Mpociot/ApiDoc/Generators/AbstractGenerator.php index ca13134b..282c3ff1 100644 --- a/src/Mpociot/ApiDoc/Generators/AbstractGenerator.php +++ b/src/Mpociot/ApiDoc/Generators/AbstractGenerator.php @@ -47,7 +47,7 @@ protected function getParameters($routeData, $routeAction, $bindings) 'description' => [], ]; foreach ($rules as $rule) { - $this->parseRule($rule, $attributeData); + $this->parseRule($rule, $attributeData, $routeData['id']); } $routeData['parameters'][$attribute] = $attributeData; } @@ -185,9 +185,10 @@ protected function fancyImplode($arr, $first, $last) * * @return void */ - protected function parseRule($rule, &$attributeData) + protected function parseRule($rule, &$attributeData, $seed) { $faker = Factory::create(); + $faker->seed(crc32($seed)); $parsedRule = $this->parseStringRule($rule); $parsedRule[0] = $this->normalizeRule($parsedRule[0]); diff --git a/tests/GenerateDocumentationTest.php b/tests/GenerateDocumentationTest.php index 6fb9739f..287836fc 100644 --- a/tests/GenerateDocumentationTest.php +++ b/tests/GenerateDocumentationTest.php @@ -84,10 +84,12 @@ public function testGeneratedMarkdownFileIsCorrect() ]); $generatedMarkdown = file_get_contents(__DIR__.'/../public/docs/source/index.md'); + $compareMarkdown = file_get_contents(__DIR__.'/../public/docs/source/.compare.md'); $fixtureMarkdown = file_get_contents(__DIR__.'/Fixtures/index.md'); $this->assertSame($generatedMarkdown, $fixtureMarkdown); + $this->assertSame($compareMarkdown, $fixtureMarkdown); } - + public function testAddsBindingsToGetRouteRules() { RouteFacade::get('/api/test/{foo}', TestController::class.'@addRouteBindingsToRequestClass'); From 8689f9798655ff353e1d7374fddc25b640896ac4 Mon Sep 17 00:00:00 2001 From: Marcel Pociot Date: Wed, 10 Aug 2016 10:17:11 -0400 Subject: [PATCH 070/780] Applied fixes from StyleCI --- .../ApiDoc/Commands/GenerateDocumentation.php | 31 +++++++++++-------- tests/GenerateDocumentationTest.php | 2 +- 2 files changed, 19 insertions(+), 14 deletions(-) diff --git a/src/Mpociot/ApiDoc/Commands/GenerateDocumentation.php b/src/Mpociot/ApiDoc/Commands/GenerateDocumentation.php index 278f3018..a2005f44 100644 --- a/src/Mpociot/ApiDoc/Commands/GenerateDocumentation.php +++ b/src/Mpociot/ApiDoc/Commands/GenerateDocumentation.php @@ -99,14 +99,15 @@ private function writeMarkdown($parsedRoutes) ->with('showPostmanCollectionButton', ! $this->option('noPostmanCollection')); $parsedRouteOutput = $parsedRoutes->map(function ($routeGroup) { - return $routeGroup->map(function($route){ - $route['output'] = (string)view('apidoc::partials.route')->with('parsedRoute', $route); + return $routeGroup->map(function ($route) { + $route['output'] = (string) view('apidoc::partials.route')->with('parsedRoute', $route); + return $route; }); }); $frontmatter = view('apidoc::partials.frontmatter'); - /** + /* * In case the target file already exists, we should check if the documentation was modified * and skip the modified parts of the routes. */ @@ -114,18 +115,18 @@ private function writeMarkdown($parsedRoutes) $generatedDocumentation = file_get_contents($targetFile); $compareDocumentation = file_get_contents($compareFile); - if (preg_match("/(.*)/is", $generatedDocumentation, $generatedInfoText)) { - $infoText = trim($generatedInfoText[1],"\n"); + if (preg_match('/(.*)/is', $generatedDocumentation, $generatedInfoText)) { + $infoText = trim($generatedInfoText[1], "\n"); } - if (preg_match("/---(.*)---\\s/is", $generatedDocumentation, $generatedFrontmatter)) { - $frontmatter = trim($generatedFrontmatter[1],"\n"); + if (preg_match('/---(.*)---\\s/is', $generatedDocumentation, $generatedFrontmatter)) { + $frontmatter = trim($generatedFrontmatter[1], "\n"); } - $parsedRouteOutput->transform(function ($routeGroup) use($generatedDocumentation, $compareDocumentation) { - return $routeGroup->transform(function($route) use($generatedDocumentation, $compareDocumentation) { - if (preg_match("/(.*)/is", $generatedDocumentation, $routeMatch)) { - $routeDocumentationChanged = (preg_match("/(.*)/is", $compareDocumentation, $compareMatch) && $compareMatch[1] !== $routeMatch[1]); + $parsedRouteOutput->transform(function ($routeGroup) use ($generatedDocumentation,$compareDocumentation) { + return $routeGroup->transform(function ($route) use ($generatedDocumentation,$compareDocumentation) { + if (preg_match('/(.*)/is', $generatedDocumentation, $routeMatch)) { + $routeDocumentationChanged = (preg_match('/(.*)/is', $compareDocumentation, $compareMatch) && $compareMatch[1] !== $routeMatch[1]); if ($routeDocumentationChanged === false || $this->option('force')) { if ($routeDocumentationChanged) { $this->warn('Discarded manual changes for route ['.implode(',', $route['methods']).'] '.$route['uri']); @@ -135,6 +136,7 @@ private function writeMarkdown($parsedRoutes) $route['modified_output'] = $routeMatch[0]; } } + return $route; }); }); @@ -295,7 +297,8 @@ private function isValidRoute($route) /** * @param $route - * @return boolean + * + * @return bool */ private function isRouteVisibleForDocumentation($route) { @@ -304,12 +307,14 @@ private function isRouteVisibleForDocumentation($route) $comment = $reflection->getMethod($method)->getDocComment(); if ($comment) { $phpdoc = new DocBlock($comment); + return collect($phpdoc->getTags()) - ->filter(function($tag) use ($route){ + ->filter(function ($tag) use ($route) { return $tag->getName() === 'hideFromAPIDocumentation'; }) ->isEmpty(); } + return true; } diff --git a/tests/GenerateDocumentationTest.php b/tests/GenerateDocumentationTest.php index 287836fc..2e33e1f0 100644 --- a/tests/GenerateDocumentationTest.php +++ b/tests/GenerateDocumentationTest.php @@ -89,7 +89,7 @@ public function testGeneratedMarkdownFileIsCorrect() $this->assertSame($generatedMarkdown, $fixtureMarkdown); $this->assertSame($compareMarkdown, $fixtureMarkdown); } - + public function testAddsBindingsToGetRouteRules() { RouteFacade::get('/api/test/{foo}', TestController::class.'@addRouteBindingsToRequestClass'); From c576dc2ee9d4baa2e5cc4c1b22f595abdc161df7 Mon Sep 17 00:00:00 2001 From: Marcel Pociot Date: Wed, 10 Aug 2016 16:21:31 +0200 Subject: [PATCH 071/780] Add route methods to console output --- src/Mpociot/ApiDoc/Commands/GenerateDocumentation.php | 6 +++--- tests/GenerateDocumentationTest.php | 8 ++++---- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/Mpociot/ApiDoc/Commands/GenerateDocumentation.php b/src/Mpociot/ApiDoc/Commands/GenerateDocumentation.php index a2005f44..281dd9ed 100644 --- a/src/Mpociot/ApiDoc/Commands/GenerateDocumentation.php +++ b/src/Mpociot/ApiDoc/Commands/GenerateDocumentation.php @@ -252,9 +252,9 @@ private function processLaravelRoutes(AbstractGenerator $generator, $allowedRout if (in_array($route->getName(), $allowedRoutes) || str_is($routePrefix, $route->getUri())) { if ($this->isValidRoute($route) && $this->isRouteVisibleForDocumentation($route->getAction()['uses'])) { $parsedRoutes[] = $generator->processRoute($route, $bindings, $withResponse); - $this->info('Processed route: '.$route->getUri()); + $this->info('Processed route: ['.implode(',', $route->getMethods()).'] '.$route->getUri()); } else { - $this->warn('Skipping route: '.$route->getUri()); + $this->warn('Skipping route: ['.implode(',', $route->getMethods()).'] '.$route->getUri()); } } } @@ -278,7 +278,7 @@ private function processDingoRoutes(AbstractGenerator $generator, $allowedRoutes foreach ($routes as $route) { if (empty($allowedRoutes) || in_array($route->getName(), $allowedRoutes) || str_is($routePrefix, $route->uri())) { $parsedRoutes[] = $generator->processRoute($route, $bindings, $withResponse); - $this->info('Processed route: '.$route->uri()); + $this->info('Processed route: ['.implode(',', $route->getMethods()).'] '.$route->uri()); } } diff --git a/tests/GenerateDocumentationTest.php b/tests/GenerateDocumentationTest.php index 2e33e1f0..6dd36259 100644 --- a/tests/GenerateDocumentationTest.php +++ b/tests/GenerateDocumentationTest.php @@ -58,8 +58,8 @@ public function testConsoleCommandDoesNotWorkWithClosure() $output = $this->artisan('api:generate', [ '--routePrefix' => 'api/*', ]); - $this->assertContains('Skipping route: api/closure', $output); - $this->assertContains('Processed route: api/test', $output); + $this->assertContains('Skipping route: [GET,HEAD] api/closure', $output); + $this->assertContains('Processed route: [GET,HEAD] api/test', $output); } public function testCanSkipSingleRoutesCommandDoesNotWorkWithClosure() @@ -70,8 +70,8 @@ public function testCanSkipSingleRoutesCommandDoesNotWorkWithClosure() $output = $this->artisan('api:generate', [ '--routePrefix' => 'api/*', ]); - $this->assertContains('Skipping route: api/skip', $output); - $this->assertContains('Processed route: api/test', $output); + $this->assertContains('Skipping route: [GET,HEAD] api/skip', $output); + $this->assertContains('Processed route: [GET,HEAD] api/test', $output); } public function testGeneratedMarkdownFileIsCorrect() From 7b7da673a03619b9c3da9f4adfd12dbf32144083 Mon Sep 17 00:00:00 2001 From: Marcel Pociot Date: Wed, 10 Aug 2016 10:21:40 -0400 Subject: [PATCH 072/780] Applied fixes from StyleCI --- src/Mpociot/ApiDoc/Commands/GenerateDocumentation.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Mpociot/ApiDoc/Commands/GenerateDocumentation.php b/src/Mpociot/ApiDoc/Commands/GenerateDocumentation.php index 281dd9ed..ac2edd2e 100644 --- a/src/Mpociot/ApiDoc/Commands/GenerateDocumentation.php +++ b/src/Mpociot/ApiDoc/Commands/GenerateDocumentation.php @@ -123,8 +123,8 @@ private function writeMarkdown($parsedRoutes) $frontmatter = trim($generatedFrontmatter[1], "\n"); } - $parsedRouteOutput->transform(function ($routeGroup) use ($generatedDocumentation,$compareDocumentation) { - return $routeGroup->transform(function ($route) use ($generatedDocumentation,$compareDocumentation) { + $parsedRouteOutput->transform(function ($routeGroup) use ($generatedDocumentation, $compareDocumentation) { + return $routeGroup->transform(function ($route) use ($generatedDocumentation, $compareDocumentation) { if (preg_match('/(.*)/is', $generatedDocumentation, $routeMatch)) { $routeDocumentationChanged = (preg_match('/(.*)/is', $compareDocumentation, $compareMatch) && $compareMatch[1] !== $routeMatch[1]); if ($routeDocumentationChanged === false || $this->option('force')) { From 94b85d22054fb2039dedbbcf39b09f31d0fbb545 Mon Sep 17 00:00:00 2001 From: Marcel Pociot Date: Fri, 12 Aug 2016 13:24:06 +0200 Subject: [PATCH 073/780] Update composer.json --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index 08472589..1921dacf 100644 --- a/composer.json +++ b/composer.json @@ -1,7 +1,7 @@ { "name": "mpociot/laravel-apidoc-generator", "license": "MIT", - "description": "Generate beautiful API documentation from your Laravel / Lumen application", + "description": "Generate beautiful API documentation from your Laravel application", "keywords": [ "API", "Documentation", From f200ebaa541d47604c1a4176d2536f820c545406 Mon Sep 17 00:00:00 2001 From: LinkThrow Date: Tue, 16 Aug 2016 12:38:59 +0100 Subject: [PATCH 074/780] Typo in Readme --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 461cfa4b..d298dedb 100644 --- a/README.md +++ b/README.md @@ -63,7 +63,7 @@ Option | Description ### How does it work? -This package uses these resources to generated the API documentation: +This package uses these resources to generate the API documentation: #### Controller doc block From 4977c61f8e8009fac21603d868a4eb62ff3ce6fe Mon Sep 17 00:00:00 2001 From: Marcel Pociot Date: Fri, 19 Aug 2016 16:58:23 +0200 Subject: [PATCH 075/780] Fix error when using 'exists' validation without second argument --- src/Mpociot/ApiDoc/Generators/AbstractGenerator.php | 11 +++++++---- tests/ApiDocGeneratorTest.php | 6 ++++++ tests/DingoGeneratorTest.php | 6 ++++++ tests/Fixtures/DingoTestRequest.php | 1 + tests/Fixtures/TestRequest.php | 1 + 5 files changed, 21 insertions(+), 4 deletions(-) diff --git a/src/Mpociot/ApiDoc/Generators/AbstractGenerator.php b/src/Mpociot/ApiDoc/Generators/AbstractGenerator.php index 282c3ff1..7db3c0ff 100644 --- a/src/Mpociot/ApiDoc/Generators/AbstractGenerator.php +++ b/src/Mpociot/ApiDoc/Generators/AbstractGenerator.php @@ -46,8 +46,8 @@ protected function getParameters($routeData, $routeAction, $bindings) 'value' => '', 'description' => [], ]; - foreach ($rules as $rule) { - $this->parseRule($rule, $attributeData, $routeData['id']); + foreach ($rules as $ruleName => $rule) { + $this->parseRule($rule, $attribute, $attributeData, $routeData['id']); } $routeData['parameters'][$attribute] = $attributeData; } @@ -181,11 +181,13 @@ protected function fancyImplode($arr, $first, $last) /** * @param string $rule + * @param string $ruleName * @param array $attributeData + * @param int $seed * * @return void */ - protected function parseRule($rule, &$attributeData, $seed) + protected function parseRule($rule, $ruleName, &$attributeData, $seed) { $faker = Factory::create(); $faker->seed(crc32($seed)); @@ -300,7 +302,8 @@ protected function parseRule($rule, &$attributeData, $seed) $attributeData['value'] = $faker->timezone; break; case 'exists': - $attributeData['description'][] = Description::parse($rule)->with([Str::singular($parameters[0]), $parameters[1]])->getDescription(); + $fieldName = isset($parameters[1]) ? $parameters[1] : $ruleName; + $attributeData['description'][] = Description::parse($rule)->with([Str::singular($parameters[0]), $fieldName])->getDescription(); break; case 'active_url': $attributeData['type'] = 'url'; diff --git a/tests/ApiDocGeneratorTest.php b/tests/ApiDocGeneratorTest.php index c8a99618..b6e5253a 100644 --- a/tests/ApiDocGeneratorTest.php +++ b/tests/ApiDocGeneratorTest.php @@ -186,6 +186,12 @@ public function testCanParseFormRequestRules() $this->assertCount(1, $attribute['description']); $this->assertSame('Valid user firstname', $attribute['description'][0]); break; + case 'single_exists': + $this->assertFalse($attribute['required']); + $this->assertSame('string', $attribute['type']); + $this->assertCount(1, $attribute['description']); + $this->assertSame('Valid user single_exists', $attribute['description'][0]); + break; case 'image': $this->assertFalse($attribute['required']); $this->assertSame('image', $attribute['type']); diff --git a/tests/DingoGeneratorTest.php b/tests/DingoGeneratorTest.php index de6bf3c9..9707d5b2 100644 --- a/tests/DingoGeneratorTest.php +++ b/tests/DingoGeneratorTest.php @@ -189,6 +189,12 @@ public function testCanParseFormRequestRules() $this->assertCount(1, $attribute['description']); $this->assertSame('Valid user firstname', $attribute['description'][0]); break; + case 'single_exists': + $this->assertFalse($attribute['required']); + $this->assertSame('string', $attribute['type']); + $this->assertCount(1, $attribute['description']); + $this->assertSame('Valid user single_exists', $attribute['description'][0]); + break; case 'image': $this->assertFalse($attribute['required']); $this->assertSame('image', $attribute['type']); diff --git a/tests/Fixtures/DingoTestRequest.php b/tests/Fixtures/DingoTestRequest.php index c4e7b517..933d79d6 100644 --- a/tests/Fixtures/DingoTestRequest.php +++ b/tests/Fixtures/DingoTestRequest.php @@ -26,6 +26,7 @@ public function rules() 'digits' => 'digits:2', 'digits_between' => 'digits_between:2,10', 'exists' => 'exists:users,firstname', + 'single_exists' => 'exists:users', 'in' => 'in:jpeg,png,bmp,gif,svg', 'integer' => 'integer', 'ip' => 'ip', diff --git a/tests/Fixtures/TestRequest.php b/tests/Fixtures/TestRequest.php index 7a3c5242..90b7b6fd 100644 --- a/tests/Fixtures/TestRequest.php +++ b/tests/Fixtures/TestRequest.php @@ -26,6 +26,7 @@ public function rules() 'digits' => 'digits:2', 'digits_between' => 'digits_between:2,10', 'exists' => 'exists:users,firstname', + 'single_exists' => 'exists:users', 'in' => 'in:jpeg,png,bmp,gif,svg', 'integer' => 'integer', 'ip' => 'ip', From 7174737bdb9595974e5ca87d9ffbcb6691a01981 Mon Sep 17 00:00:00 2001 From: Marcel Pociot Date: Mon, 22 Aug 2016 14:15:48 +0200 Subject: [PATCH 076/780] Fixes issue #83 --- .../ApiDoc/Generators/LaravelGenerator.php | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/Mpociot/ApiDoc/Generators/LaravelGenerator.php b/src/Mpociot/ApiDoc/Generators/LaravelGenerator.php index b024e97e..14c1e137 100644 --- a/src/Mpociot/ApiDoc/Generators/LaravelGenerator.php +++ b/src/Mpociot/ApiDoc/Generators/LaravelGenerator.php @@ -27,20 +27,20 @@ protected function getUri($route) */ public function processRoute($route, $bindings = [], $withResponse = true) { - $response = ''; - - if ($withResponse) { - $response = $this->getRouteResponse($route, $bindings); - } + $content = ''; $routeAction = $route->getAction(); $routeGroup = $this->getRouteGroup($routeAction['uses']); $routeDescription = $this->getRouteDescription($routeAction['uses']); - if ($response->headers->get('Content-Type') === 'application/json') { - $content = json_encode(json_decode($response->getContent()), JSON_PRETTY_PRINT); - } else { - $content = $response->getContent(); + + if ($withResponse) { + $response = $this->getRouteResponse($route, $bindings); + if ($response->headers->get('Content-Type') === 'application/json') { + $content = json_encode(json_decode($response->getContent()), JSON_PRETTY_PRINT); + } else { + $content = $response->getContent(); + } } return $this->getParameters([ From f4bff24a51534ff81eb1a58bc6a9976c14dd43f4 Mon Sep 17 00:00:00 2001 From: Marcel Pociot Date: Wed, 24 Aug 2016 00:13:04 +0200 Subject: [PATCH 077/780] Cast actAsUser to int --- src/Mpociot/ApiDoc/Commands/GenerateDocumentation.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Mpociot/ApiDoc/Commands/GenerateDocumentation.php b/src/Mpociot/ApiDoc/Commands/GenerateDocumentation.php index ac2edd2e..7fd0b169 100644 --- a/src/Mpociot/ApiDoc/Commands/GenerateDocumentation.php +++ b/src/Mpociot/ApiDoc/Commands/GenerateDocumentation.php @@ -213,11 +213,11 @@ private function setUserToBeImpersonated($actAs) if (! empty($actAs)) { if (version_compare($this->laravel->version(), '5.2.0', '<')) { $userModel = config('auth.model'); - $user = $userModel::find($actAs); + $user = $userModel::find((int)$actAs); $this->laravel['auth']->setUser($user); } else { $userModel = config('auth.providers.users.model'); - $user = $userModel::find($actAs); + $user = $userModel::find((int)$actAs); $this->laravel['auth']->guard()->setUser($user); } } From fbe4c595356ad545328c5817f3d46be1e449d913 Mon Sep 17 00:00:00 2001 From: Marcel Pociot Date: Tue, 23 Aug 2016 22:13:17 +0000 Subject: [PATCH 078/780] Applied fixes from StyleCI --- src/Mpociot/ApiDoc/Commands/GenerateDocumentation.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Mpociot/ApiDoc/Commands/GenerateDocumentation.php b/src/Mpociot/ApiDoc/Commands/GenerateDocumentation.php index 7fd0b169..68f517c8 100644 --- a/src/Mpociot/ApiDoc/Commands/GenerateDocumentation.php +++ b/src/Mpociot/ApiDoc/Commands/GenerateDocumentation.php @@ -213,11 +213,11 @@ private function setUserToBeImpersonated($actAs) if (! empty($actAs)) { if (version_compare($this->laravel->version(), '5.2.0', '<')) { $userModel = config('auth.model'); - $user = $userModel::find((int)$actAs); + $user = $userModel::find((int) $actAs); $this->laravel['auth']->setUser($user); } else { $userModel = config('auth.providers.users.model'); - $user = $userModel::find((int)$actAs); + $user = $userModel::find((int) $actAs); $this->laravel['auth']->guard()->setUser($user); } } From 2238df068b9a0045cbc98339936e5e28d6727401 Mon Sep 17 00:00:00 2001 From: Marcel Pociot Date: Wed, 24 Aug 2016 00:18:21 +0200 Subject: [PATCH 079/780] Allow publishing of views --- src/Mpociot/ApiDoc/ApiDocGeneratorServiceProvider.php | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Mpociot/ApiDoc/ApiDocGeneratorServiceProvider.php b/src/Mpociot/ApiDoc/ApiDocGeneratorServiceProvider.php index 1eb52ee8..ddeb08ef 100644 --- a/src/Mpociot/ApiDoc/ApiDocGeneratorServiceProvider.php +++ b/src/Mpociot/ApiDoc/ApiDocGeneratorServiceProvider.php @@ -20,6 +20,7 @@ public function boot() $this->publishes([ __DIR__.'/../../resources/lang' => $this->resource_path('lang/vendor/apidoc'), + __DIR__.'/../../resources/views' => $this->resource_path('views/vendor/apidoc'), ]); } From 1d1f122017178f4c3a7665c2e09db18cdb9287cd Mon Sep 17 00:00:00 2001 From: Marcel Pociot Date: Fri, 26 Aug 2016 22:48:26 +0200 Subject: [PATCH 080/780] Fix issue #88 --- src/Mpociot/ApiDoc/Generators/AbstractGenerator.php | 10 ++++++++-- tests/ApiDocGeneratorTest.php | 6 ++++++ tests/DingoGeneratorTest.php | 6 ++++++ tests/Fixtures/DingoTestRequest.php | 1 + tests/Fixtures/TestRequest.php | 1 + 5 files changed, 22 insertions(+), 2 deletions(-) diff --git a/src/Mpociot/ApiDoc/Generators/AbstractGenerator.php b/src/Mpociot/ApiDoc/Generators/AbstractGenerator.php index 7db3c0ff..4fc8b87a 100644 --- a/src/Mpociot/ApiDoc/Generators/AbstractGenerator.php +++ b/src/Mpociot/ApiDoc/Generators/AbstractGenerator.php @@ -41,7 +41,7 @@ protected function getParameters($routeData, $routeAction, $bindings) foreach ($validator->getRules() as $attribute => $rules) { $attributeData = [ 'required' => false, - 'type' => 'string', + 'type' => null, 'default' => '', 'value' => '', 'description' => [], @@ -235,7 +235,9 @@ protected function parseRule($rule, $ruleName, &$attributeData, $seed) $attributeData['description'][] = Description::parse($rule)->with($parameters)->getDescription(); break; case 'between': - $attributeData['type'] = 'numeric'; + if (!isset($attributeData['type'])) { + $attributeData['type'] = 'numeric'; + } $attributeData['description'][] = Description::parse($rule)->with($parameters)->getDescription(); $attributeData['value'] = $faker->numberBetween($parameters[0], $parameters[1]); break; @@ -354,6 +356,10 @@ protected function parseRule($rule, $ruleName, &$attributeData, $seed) if ($attributeData['value'] === '') { $attributeData['value'] = $faker->word; } + + if (is_null($attributeData['type'])) { + $attributeData['type'] = 'string'; + } } /** diff --git a/tests/ApiDocGeneratorTest.php b/tests/ApiDocGeneratorTest.php index b6e5253a..b198f2a9 100644 --- a/tests/ApiDocGeneratorTest.php +++ b/tests/ApiDocGeneratorTest.php @@ -135,6 +135,12 @@ public function testCanParseFormRequestRules() $this->assertCount(1, $attribute['description']); $this->assertSame('Between: `5` and `200`', $attribute['description'][0]); break; + case 'string_between': + $this->assertFalse($attribute['required']); + $this->assertSame('string', $attribute['type']); + $this->assertCount(1, $attribute['description']); + $this->assertSame('Between: `5` and `200`', $attribute['description'][0]); + break; case 'before': $this->assertFalse($attribute['required']); $this->assertSame('date', $attribute['type']); diff --git a/tests/DingoGeneratorTest.php b/tests/DingoGeneratorTest.php index 9707d5b2..d638e200 100644 --- a/tests/DingoGeneratorTest.php +++ b/tests/DingoGeneratorTest.php @@ -138,6 +138,12 @@ public function testCanParseFormRequestRules() $this->assertCount(1, $attribute['description']); $this->assertSame('Between: `5` and `200`', $attribute['description'][0]); break; + case 'string_between': + $this->assertFalse($attribute['required']); + $this->assertSame('string', $attribute['type']); + $this->assertCount(1, $attribute['description']); + $this->assertSame('Between: `5` and `200`', $attribute['description'][0]); + break; case 'before': $this->assertFalse($attribute['required']); $this->assertSame('date', $attribute['type']); diff --git a/tests/Fixtures/DingoTestRequest.php b/tests/Fixtures/DingoTestRequest.php index 933d79d6..c3adef0e 100644 --- a/tests/Fixtures/DingoTestRequest.php +++ b/tests/Fixtures/DingoTestRequest.php @@ -19,6 +19,7 @@ public function rules() 'array' => 'array', 'before' => 'before:2016-04-23 14:31:00', 'between' => 'between:5,200', + 'string_between' => 'string|between:5,200', 'boolean' => 'boolean', 'date' => 'date', 'date_format' => 'date_format:j.n.Y H:iP', diff --git a/tests/Fixtures/TestRequest.php b/tests/Fixtures/TestRequest.php index 90b7b6fd..32f606c8 100644 --- a/tests/Fixtures/TestRequest.php +++ b/tests/Fixtures/TestRequest.php @@ -19,6 +19,7 @@ public function rules() 'array' => 'array', 'before' => 'before:2016-04-23 14:31:00', 'between' => 'between:5,200', + 'string_between' => 'string|between:5,200', 'boolean' => 'boolean', 'date' => 'date', 'date_format' => 'date_format:j.n.Y H:iP', From 93887c205517ab9625de494215df4b03b3634c8d Mon Sep 17 00:00:00 2001 From: Marcel Pociot Date: Fri, 26 Aug 2016 20:48:58 +0000 Subject: [PATCH 081/780] Applied fixes from StyleCI --- src/Mpociot/ApiDoc/Generators/AbstractGenerator.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Mpociot/ApiDoc/Generators/AbstractGenerator.php b/src/Mpociot/ApiDoc/Generators/AbstractGenerator.php index 4fc8b87a..7cb70eb5 100644 --- a/src/Mpociot/ApiDoc/Generators/AbstractGenerator.php +++ b/src/Mpociot/ApiDoc/Generators/AbstractGenerator.php @@ -235,7 +235,7 @@ protected function parseRule($rule, $ruleName, &$attributeData, $seed) $attributeData['description'][] = Description::parse($rule)->with($parameters)->getDescription(); break; case 'between': - if (!isset($attributeData['type'])) { + if (! isset($attributeData['type'])) { $attributeData['type'] = 'numeric'; } $attributeData['description'][] = Description::parse($rule)->with($parameters)->getDescription(); From 23fb7a162a176eb79e2db45aea069f512913ea23 Mon Sep 17 00:00:00 2001 From: Marcel Pociot Date: Fri, 26 Aug 2016 22:56:58 +0200 Subject: [PATCH 082/780] Fixes issue #84 --- src/Mpociot/ApiDoc/Commands/GenerateDocumentation.php | 2 +- src/resources/views/partials/info.blade.php | 2 +- tests/Fixtures/index.md | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Mpociot/ApiDoc/Commands/GenerateDocumentation.php b/src/Mpociot/ApiDoc/Commands/GenerateDocumentation.php index 68f517c8..8d0bf567 100644 --- a/src/Mpociot/ApiDoc/Commands/GenerateDocumentation.php +++ b/src/Mpociot/ApiDoc/Commands/GenerateDocumentation.php @@ -95,7 +95,7 @@ private function writeMarkdown($parsedRoutes) $compareFile = $outputPath.DIRECTORY_SEPARATOR.'source'.DIRECTORY_SEPARATOR.'.compare.md'; $infoText = view('apidoc::partials.info') - ->with('outputPath', $this->option('output')) + ->with('outputPath', ltrim($outputPath, 'public/')) ->with('showPostmanCollectionButton', ! $this->option('noPostmanCollection')); $parsedRouteOutput = $parsedRoutes->map(function ($routeGroup) { diff --git a/src/resources/views/partials/info.blade.php b/src/resources/views/partials/info.blade.php index d8cb20ac..4ac1470e 100644 --- a/src/resources/views/partials/info.blade.php +++ b/src/resources/views/partials/info.blade.php @@ -2,5 +2,5 @@ Welcome to the generated API reference. @if($showPostmanCollectionButton) -[Get Postman Collection]({{$outputPath}}/collection.json) +[Get Postman Collection]({{url(https://github.com/$outputPath.'/collection.json')}}) @endif \ No newline at end of file diff --git a/tests/Fixtures/index.md b/tests/Fixtures/index.md index 08658562..192be8c5 100644 --- a/tests/Fixtures/index.md +++ b/tests/Fixtures/index.md @@ -16,7 +16,7 @@ toc_footers: # Info Welcome to the generated API reference. -[Get Postman Collection](public/docs/collection.json) +[Get Postman Collection](http://localhost/docs/collection.json) From 2c4dfb0c96cbcb0367b5f75f02bbb4fb35aee707 Mon Sep 17 00:00:00 2001 From: Marcel Pociot Date: Fri, 26 Aug 2016 23:09:24 +0200 Subject: [PATCH 083/780] Return raw Dingo API results, fixing #43 and #75 --- src/Mpociot/ApiDoc/Generators/DingoGenerator.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Mpociot/ApiDoc/Generators/DingoGenerator.php b/src/Mpociot/ApiDoc/Generators/DingoGenerator.php index 60cb5d95..4d952307 100644 --- a/src/Mpociot/ApiDoc/Generators/DingoGenerator.php +++ b/src/Mpociot/ApiDoc/Generators/DingoGenerator.php @@ -45,7 +45,8 @@ public function processRoute($route, $bindings = [], $withResponse = true) */ public function callRoute($method, $uri, $parameters = [], $cookies = [], $files = [], $server = [], $content = null) { - return call_user_func_array([app('Dingo\Api\Dispatcher'), strtolower($method)], [$uri]); + $dispatcher = app('Dingo\Api\Dispatcher')->raw(); + return call_user_func_array([$dispatcher, strtolower($method)], [$uri]); } /** From 876777ac9f6b2fbb7a53e2137b5935d1adf7c979 Mon Sep 17 00:00:00 2001 From: Marcel Pociot Date: Fri, 26 Aug 2016 21:09:34 +0000 Subject: [PATCH 084/780] Applied fixes from StyleCI --- src/Mpociot/ApiDoc/Generators/DingoGenerator.php | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Mpociot/ApiDoc/Generators/DingoGenerator.php b/src/Mpociot/ApiDoc/Generators/DingoGenerator.php index 4d952307..713f55b3 100644 --- a/src/Mpociot/ApiDoc/Generators/DingoGenerator.php +++ b/src/Mpociot/ApiDoc/Generators/DingoGenerator.php @@ -46,6 +46,7 @@ public function processRoute($route, $bindings = [], $withResponse = true) public function callRoute($method, $uri, $parameters = [], $cookies = [], $files = [], $server = [], $content = null) { $dispatcher = app('Dingo\Api\Dispatcher')->raw(); + return call_user_func_array([$dispatcher, strtolower($method)], [$uri]); } From 976f5ed3a1b37c31d3f31e500d461d244d7525f2 Mon Sep 17 00:00:00 2001 From: Priyabrata Sarkar Date: Tue, 30 Aug 2016 18:43:04 +0530 Subject: [PATCH 085/780] Middleware generate (#92) * added middleware generate support * added middleware generate support * added middleware generate support * added middleware generate support to dingo --- README.md | 1 + .../ApiDoc/Commands/GenerateDocumentation.php | 18 ++++++++++-------- tests/GenerateDocumentationTest.php | 2 +- 3 files changed, 12 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index d298dedb..954ae9fc 100644 --- a/README.md +++ b/README.md @@ -43,6 +43,7 @@ Option | Description `output` | The output path used for the generated documentation. Default: `public/docs` `routePrefix` | The route prefix to use for generation - `*` can be used as a wildcard `routes` | The route names to use for generation - Required if no routePrefix is provided +`middleware` | The middlewares to use for generation `noResponseCalls` | Disable API response calls `noPostmanCollection` | Disable Postman collection creation `actAsUserId` | The user ID to use for authenticated API response calls diff --git a/src/Mpociot/ApiDoc/Commands/GenerateDocumentation.php b/src/Mpociot/ApiDoc/Commands/GenerateDocumentation.php index 8d0bf567..96ec0069 100644 --- a/src/Mpociot/ApiDoc/Commands/GenerateDocumentation.php +++ b/src/Mpociot/ApiDoc/Commands/GenerateDocumentation.php @@ -24,6 +24,7 @@ class GenerateDocumentation extends Command {--output=public/docs : The output path for the generated documentation} {--routePrefix= : The route prefix to use for generation} {--routes=* : The route names to use for generation} + {--middleware= : The middleware to use for generation} {--noResponseCalls : Disable API response calls} {--noPostmanCollection : Disable Postman collection creation} {--actAsUserId= : The user ID to use for API response calls} @@ -64,19 +65,20 @@ public function handle() $allowedRoutes = $this->option('routes'); $routePrefix = $this->option('routePrefix'); + $middleware = $this->option('middleware'); $this->setUserToBeImpersonated($this->option('actAsUserId')); - if ($routePrefix === null && ! count($allowedRoutes)) { - $this->error('You must provide either a route prefix or a route to generate the documentation.'); + if ($routePrefix === null && ! count($allowedRoutes) && $middleware === null) { + $this->error('You must provide either a route prefix or a route or a middleware to generate the documentation.'); return false; } if ($this->option('router') === 'laravel') { - $parsedRoutes = $this->processLaravelRoutes($generator, $allowedRoutes, $routePrefix); + $parsedRoutes = $this->processLaravelRoutes($generator, $allowedRoutes, $routePrefix, $middleware); } else { - $parsedRoutes = $this->processDingoRoutes($generator, $allowedRoutes, $routePrefix); + $parsedRoutes = $this->processDingoRoutes($generator, $allowedRoutes, $routePrefix, $middleware); } $parsedRoutes = collect($parsedRoutes)->groupBy('resource')->sortBy('resource'); @@ -242,14 +244,14 @@ private function getRoutes() * * @return array */ - private function processLaravelRoutes(AbstractGenerator $generator, $allowedRoutes, $routePrefix) + private function processLaravelRoutes(AbstractGenerator $generator, $allowedRoutes, $routePrefix, $middleware) { $withResponse = $this->option('noResponseCalls') === false; $routes = $this->getRoutes(); $bindings = $this->getBindings(); $parsedRoutes = []; foreach ($routes as $route) { - if (in_array($route->getName(), $allowedRoutes) || str_is($routePrefix, $route->getUri())) { + if (in_array($route->getName(), $allowedRoutes) || str_is($routePrefix, $route->getUri()) || in_array($middleware, $route->middleware())) { if ($this->isValidRoute($route) && $this->isRouteVisibleForDocumentation($route->getAction()['uses'])) { $parsedRoutes[] = $generator->processRoute($route, $bindings, $withResponse); $this->info('Processed route: ['.implode(',', $route->getMethods()).'] '.$route->getUri()); @@ -269,14 +271,14 @@ private function processLaravelRoutes(AbstractGenerator $generator, $allowedRout * * @return array */ - private function processDingoRoutes(AbstractGenerator $generator, $allowedRoutes, $routePrefix) + private function processDingoRoutes(AbstractGenerator $generator, $allowedRoutes, $routePrefix, $middleware) { $withResponse = $this->option('noResponseCalls') === false; $routes = $this->getRoutes(); $bindings = $this->getBindings(); $parsedRoutes = []; foreach ($routes as $route) { - if (empty($allowedRoutes) || in_array($route->getName(), $allowedRoutes) || str_is($routePrefix, $route->uri())) { + if (empty($allowedRoutes) || in_array($route->getName(), $allowedRoutes) || str_is($routePrefix, $route->uri()) || in_array($middleware, $route->middleware())) { $parsedRoutes[] = $generator->processRoute($route, $bindings, $withResponse); $this->info('Processed route: ['.implode(',', $route->getMethods()).'] '.$route->uri()); } diff --git a/tests/GenerateDocumentationTest.php b/tests/GenerateDocumentationTest.php index 6dd36259..6eab6285 100644 --- a/tests/GenerateDocumentationTest.php +++ b/tests/GenerateDocumentationTest.php @@ -45,7 +45,7 @@ protected function getPackageProviders($app) public function testConsoleCommandNeedsAPrefixOrRoute() { $output = $this->artisan('api:generate'); - $this->assertEquals('You must provide either a route prefix or a route to generate the documentation.'.PHP_EOL, $output); + $this->assertEquals('You must provide either a route prefix or a route or a middleware to generate the documentation.'.PHP_EOL, $output); } public function testConsoleCommandDoesNotWorkWithClosure() From 03fe9eb072359e68865d8faa11f9e5dcb1448359 Mon Sep 17 00:00:00 2001 From: Marcel Pociot Date: Wed, 14 Sep 2016 00:01:02 +0200 Subject: [PATCH 086/780] Fix issue that ignores manually modified changes --- src/resources/views/documentarian.blade.php | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/resources/views/documentarian.blade.php b/src/resources/views/documentarian.blade.php index eb4f6df3..9e2a136a 100644 --- a/src/resources/views/documentarian.blade.php +++ b/src/resources/views/documentarian.blade.php @@ -10,6 +10,10 @@ #{{$group}} @endif @foreach($routes as $parsedRoute) -{!! $parsedRoute['output'] !!} +@if($writeCompareFile === true) +{!! $parsedRoute['output']!!} +@else +{!! isset($parsedRoute['modified_output']) ? $parsedRoute['modified_output'] : $parsedRoute['output']!!} +@endif @endforeach @endforeach From 79631d0fcb6eb51e5a268da206f7e65f53ed0899 Mon Sep 17 00:00:00 2001 From: Marcel Pociot Date: Wed, 14 Sep 2016 10:38:22 +0200 Subject: [PATCH 087/780] Added 'file' validation rule --- src/Mpociot/ApiDoc/Generators/AbstractGenerator.php | 4 ++++ src/resources/lang/en/rules.php | 1 + tests/ApiDocGeneratorTest.php | 6 ++++++ tests/DingoGeneratorTest.php | 6 ++++++ tests/Fixtures/DingoTestRequest.php | 2 ++ tests/Fixtures/TestRequest.php | 2 ++ 6 files changed, 21 insertions(+) diff --git a/src/Mpociot/ApiDoc/Generators/AbstractGenerator.php b/src/Mpociot/ApiDoc/Generators/AbstractGenerator.php index 7cb70eb5..65870537 100644 --- a/src/Mpociot/ApiDoc/Generators/AbstractGenerator.php +++ b/src/Mpociot/ApiDoc/Generators/AbstractGenerator.php @@ -262,6 +262,10 @@ protected function parseRule($rule, $ruleName, &$attributeData, $seed) $attributeData['type'] = 'numeric'; $attributeData['description'][] = Description::parse($rule)->with($parameters)->getDescription(); break; + case 'file': + $attributeData['type'] = 'file'; + $attributeData['description'][] = Description::parse($rule)->getDescription(); + break; case 'image': $attributeData['type'] = 'image'; $attributeData['description'][] = Description::parse($rule)->getDescription(); diff --git a/src/resources/lang/en/rules.php b/src/resources/lang/en/rules.php index 5c5f7dd0..adad7c79 100644 --- a/src/resources/lang/en/rules.php +++ b/src/resources/lang/en/rules.php @@ -15,6 +15,7 @@ 'different' => 'Must have a different value than parameter: `:attribute`', 'digits' => 'Must have an exact length of `:attribute`', 'digits_between' => 'Must have a length between `:attribute` and `:attribute`', + 'file' => 'Must be a file upload', 'image' => 'Must be an image (jpeg, png, bmp, gif, or svg)', 'json' => 'Must be a valid JSON string.', 'mimetypes' => 'Allowed mime types: :attribute', diff --git a/tests/ApiDocGeneratorTest.php b/tests/ApiDocGeneratorTest.php index b198f2a9..30f0187f 100644 --- a/tests/ApiDocGeneratorTest.php +++ b/tests/ApiDocGeneratorTest.php @@ -198,6 +198,12 @@ public function testCanParseFormRequestRules() $this->assertCount(1, $attribute['description']); $this->assertSame('Valid user single_exists', $attribute['description'][0]); break; + case 'file': + $this->assertFalse($attribute['required']); + $this->assertSame('file', $attribute['type']); + $this->assertCount(1, $attribute['description']); + $this->assertSame('Must be a file upload', $attribute['description'][0]); + break; case 'image': $this->assertFalse($attribute['required']); $this->assertSame('image', $attribute['type']); diff --git a/tests/DingoGeneratorTest.php b/tests/DingoGeneratorTest.php index d638e200..0d0514d8 100644 --- a/tests/DingoGeneratorTest.php +++ b/tests/DingoGeneratorTest.php @@ -201,6 +201,12 @@ public function testCanParseFormRequestRules() $this->assertCount(1, $attribute['description']); $this->assertSame('Valid user single_exists', $attribute['description'][0]); break; + case 'file': + $this->assertFalse($attribute['required']); + $this->assertSame('file', $attribute['type']); + $this->assertCount(1, $attribute['description']); + $this->assertSame('Must be a file upload', $attribute['description'][0]); + break; case 'image': $this->assertFalse($attribute['required']); $this->assertSame('image', $attribute['type']); diff --git a/tests/Fixtures/DingoTestRequest.php b/tests/Fixtures/DingoTestRequest.php index c3adef0e..157bdbfd 100644 --- a/tests/Fixtures/DingoTestRequest.php +++ b/tests/Fixtures/DingoTestRequest.php @@ -28,8 +28,10 @@ public function rules() 'digits_between' => 'digits_between:2,10', 'exists' => 'exists:users,firstname', 'single_exists' => 'exists:users', + 'file' => 'file', 'in' => 'in:jpeg,png,bmp,gif,svg', 'integer' => 'integer', + 'image' => 'image', 'ip' => 'ip', 'json' => 'json', 'min' => 'min:20', diff --git a/tests/Fixtures/TestRequest.php b/tests/Fixtures/TestRequest.php index 32f606c8..e5cf3c8e 100644 --- a/tests/Fixtures/TestRequest.php +++ b/tests/Fixtures/TestRequest.php @@ -27,8 +27,10 @@ public function rules() 'digits' => 'digits:2', 'digits_between' => 'digits_between:2,10', 'exists' => 'exists:users,firstname', + 'file' => 'file', 'single_exists' => 'exists:users', 'in' => 'in:jpeg,png,bmp,gif,svg', + 'image' => 'image', 'integer' => 'integer', 'ip' => 'ip', 'json' => 'json', From 49635dda5919408aadb34e2434db17ab77462731 Mon Sep 17 00:00:00 2001 From: Marcel Pociot Date: Wed, 14 Sep 2016 10:44:07 +0200 Subject: [PATCH 088/780] Add date_format example value #97 --- src/Mpociot/ApiDoc/Generators/AbstractGenerator.php | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Mpociot/ApiDoc/Generators/AbstractGenerator.php b/src/Mpociot/ApiDoc/Generators/AbstractGenerator.php index 65870537..97830de9 100644 --- a/src/Mpociot/ApiDoc/Generators/AbstractGenerator.php +++ b/src/Mpociot/ApiDoc/Generators/AbstractGenerator.php @@ -249,6 +249,7 @@ protected function parseRule($rule, $ruleName, &$attributeData, $seed) case 'date_format': $attributeData['type'] = 'date'; $attributeData['description'][] = Description::parse($rule)->with($parameters)->getDescription(); + $attributeData['value'] = date($parameters[0]); break; case 'different': $attributeData['description'][] = Description::parse($rule)->with($parameters)->getDescription(); From de38dad1e901768096d3b239cc6776711276e3b5 Mon Sep 17 00:00:00 2001 From: Marcel Pociot Date: Wed, 14 Sep 2016 10:48:45 +0200 Subject: [PATCH 089/780] Add numeric and integer min, max example values #97 --- src/Mpociot/ApiDoc/Generators/AbstractGenerator.php | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/Mpociot/ApiDoc/Generators/AbstractGenerator.php b/src/Mpociot/ApiDoc/Generators/AbstractGenerator.php index 97830de9..b4a08ecf 100644 --- a/src/Mpociot/ApiDoc/Generators/AbstractGenerator.php +++ b/src/Mpociot/ApiDoc/Generators/AbstractGenerator.php @@ -4,6 +4,7 @@ use Faker\Factory; use Illuminate\Foundation\Http\FormRequest; +use Illuminate\Support\Arr; use Illuminate\Support\Facades\Validator; use Illuminate\Support\Str; use Mpociot\ApiDoc\Parsers\RuleDescriptionParser as Description; @@ -230,9 +231,15 @@ protected function parseRule($rule, $ruleName, &$attributeData, $seed) break; case 'min': $attributeData['description'][] = Description::parse($rule)->with($parameters)->getDescription(); + if (Arr::get($attributeData, 'type') === 'numeric' || Arr::get($attributeData, 'type') === 'integer') { + $attributeData['value'] = $faker->numberBetween($parameters[0]); + } break; case 'max': $attributeData['description'][] = Description::parse($rule)->with($parameters)->getDescription(); + if (Arr::get($attributeData, 'type') === 'numeric' || Arr::get($attributeData, 'type') === 'integer') { + $attributeData['value'] = $faker->numberBetween(0, $parameters[0]); + } break; case 'between': if (! isset($attributeData['type'])) { From 82368fbd9799e6fc829b9bd9cf4cc377b35f5e6a Mon Sep 17 00:00:00 2001 From: Marcel Pociot Date: Wed, 14 Sep 2016 11:43:19 +0200 Subject: [PATCH 090/780] Add support for custom headers on example calls #76 --- README.md | 1 + .../ApiDoc/Commands/GenerateDocumentation.php | 5 +++-- .../ApiDoc/Generators/AbstractGenerator.php | 12 +++++++++-- .../ApiDoc/Generators/DingoGenerator.php | 9 ++++++-- .../ApiDoc/Generators/LaravelGenerator.php | 9 ++++---- tests/Fixtures/TestController.php | 6 ++++++ tests/GenerateDocumentationTest.php | 21 +++++++++++++++++++ 7 files changed, 53 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index 954ae9fc..71a983c3 100644 --- a/README.md +++ b/README.md @@ -50,6 +50,7 @@ Option | Description `router` | The router to use, when processing the route files (can be Laravel or Dingo - defaults to Laravel) `bindings` | List of route bindings that should be replaced when trying to retrieve route results. Syntax format: `binding_one,id|binding_two,id` `force` | Force the re-generation of existing/modified API routes +`header` | Custom HTTP headers to add to the example requests. Separate the header name and value with ":". For example: `--header 'Authorization: CustomToken'` ## Publish rule descriptions for customisation or translation. diff --git a/src/Mpociot/ApiDoc/Commands/GenerateDocumentation.php b/src/Mpociot/ApiDoc/Commands/GenerateDocumentation.php index 96ec0069..eb2c97d8 100644 --- a/src/Mpociot/ApiDoc/Commands/GenerateDocumentation.php +++ b/src/Mpociot/ApiDoc/Commands/GenerateDocumentation.php @@ -31,6 +31,7 @@ class GenerateDocumentation extends Command {--router=laravel : The router to be used (Laravel or Dingo)} {--force : Force rewriting of existing routes} {--bindings= : Route Model Bindings} + {--header=* : Custom HTTP headers to add to the example requests. Separate the header name and value with ":"} '; /** @@ -253,7 +254,7 @@ private function processLaravelRoutes(AbstractGenerator $generator, $allowedRout foreach ($routes as $route) { if (in_array($route->getName(), $allowedRoutes) || str_is($routePrefix, $route->getUri()) || in_array($middleware, $route->middleware())) { if ($this->isValidRoute($route) && $this->isRouteVisibleForDocumentation($route->getAction()['uses'])) { - $parsedRoutes[] = $generator->processRoute($route, $bindings, $withResponse); + $parsedRoutes[] = $generator->processRoute($route, $bindings, $this->option('header'), $withResponse); $this->info('Processed route: ['.implode(',', $route->getMethods()).'] '.$route->getUri()); } else { $this->warn('Skipping route: ['.implode(',', $route->getMethods()).'] '.$route->getUri()); @@ -279,7 +280,7 @@ private function processDingoRoutes(AbstractGenerator $generator, $allowedRoutes $parsedRoutes = []; foreach ($routes as $route) { if (empty($allowedRoutes) || in_array($route->getName(), $allowedRoutes) || str_is($routePrefix, $route->uri()) || in_array($middleware, $route->middleware())) { - $parsedRoutes[] = $generator->processRoute($route, $bindings, $withResponse); + $parsedRoutes[] = $generator->processRoute($route, $bindings, $this->option('header'), $withResponse); $this->info('Processed route: ['.implode(',', $route->getMethods()).'] '.$route->uri()); } } diff --git a/src/Mpociot/ApiDoc/Generators/AbstractGenerator.php b/src/Mpociot/ApiDoc/Generators/AbstractGenerator.php index b4a08ecf..9231bea1 100644 --- a/src/Mpociot/ApiDoc/Generators/AbstractGenerator.php +++ b/src/Mpociot/ApiDoc/Generators/AbstractGenerator.php @@ -58,16 +58,24 @@ protected function getParameters($routeData, $routeAction, $bindings) /** * @param $route + * @param $bindings + * @param $headers * * @return \Illuminate\Http\Response */ - protected function getRouteResponse($route, $bindings) + protected function getRouteResponse($route, $bindings, $headers = []) { $uri = $this->addRouteModelBindings($route, $bindings); $methods = $route->getMethods(); - return $this->callRoute(array_shift($methods), $uri); + // Split headers into key - value pairs + $headers = collect($headers)->map(function($value) { + $split = explode(':', $value); + return [trim($split[0]) => trim($split[1])]; + })->collapse()->toArray(); + + return $this->callRoute(array_shift($methods), $uri, [], [], [], $headers); } /** diff --git a/src/Mpociot/ApiDoc/Generators/DingoGenerator.php b/src/Mpociot/ApiDoc/Generators/DingoGenerator.php index 713f55b3..4b3a4862 100644 --- a/src/Mpociot/ApiDoc/Generators/DingoGenerator.php +++ b/src/Mpociot/ApiDoc/Generators/DingoGenerator.php @@ -9,17 +9,18 @@ class DingoGenerator extends AbstractGenerator /** * @param \Illuminate\Routing\Route $route * @param array $bindings + * @param array $headers * @param bool $withResponse * * @return array */ - public function processRoute($route, $bindings = [], $withResponse = true) + public function processRoute($route, $bindings = [], $headers = [], $withResponse = true) { $response = ''; if ($withResponse) { try { - $response = $this->getRouteResponse($route, $bindings); + $response = $this->getRouteResponse($route, $bindings, $headers); } catch (Exception $e) { } } @@ -46,6 +47,10 @@ public function processRoute($route, $bindings = [], $withResponse = true) public function callRoute($method, $uri, $parameters = [], $cookies = [], $files = [], $server = [], $content = null) { $dispatcher = app('Dingo\Api\Dispatcher')->raw(); + + collect($server)->map(function($key, $value) use ($dispatcher) { + $dispatcher->header($key, $value); + }); return call_user_func_array([$dispatcher, strtolower($method)], [$uri]); } diff --git a/src/Mpociot/ApiDoc/Generators/LaravelGenerator.php b/src/Mpociot/ApiDoc/Generators/LaravelGenerator.php index 14c1e137..9deba180 100644 --- a/src/Mpociot/ApiDoc/Generators/LaravelGenerator.php +++ b/src/Mpociot/ApiDoc/Generators/LaravelGenerator.php @@ -21,11 +21,12 @@ protected function getUri($route) /** * @param \Illuminate\Routing\Route $route * @param array $bindings + * @param array $headers * @param bool $withResponse * * @return array */ - public function processRoute($route, $bindings = [], $withResponse = true) + public function processRoute($route, $bindings = [], $headers = [], $withResponse = true) { $content = ''; @@ -35,7 +36,7 @@ public function processRoute($route, $bindings = [], $withResponse = true) if ($withResponse) { - $response = $this->getRouteResponse($route, $bindings); + $response = $this->getRouteResponse($route, $bindings, $headers); if ($response->headers->get('Content-Type') === 'application/json') { $content = json_encode(json_decode($response->getContent()), JSON_PRETTY_PRINT); } else { @@ -73,10 +74,10 @@ public function callRoute($method, $uri, $parameters = [], $cookies = [], $files $kernel = App::make('Illuminate\Contracts\Http\Kernel'); App::instance('middleware.disable', true); - $server = [ + $server = collect([ 'CONTENT_TYPE' => 'application/json', 'Accept' => 'application/json', - ]; + ])->merge($server)->toArray(); $request = Request::create( $uri, $method, $parameters, diff --git a/tests/Fixtures/TestController.php b/tests/Fixtures/TestController.php index 2fa20717..14c9675b 100644 --- a/tests/Fixtures/TestController.php +++ b/tests/Fixtures/TestController.php @@ -2,6 +2,7 @@ namespace Mpociot\ApiDoc\Tests\Fixtures; +use Illuminate\Http\Request; use Illuminate\Routing\Controller; class TestController extends Controller @@ -31,6 +32,11 @@ public function addRouteBindingsToRequestClass(DynamicRequest $request) return ''; } + public function checkCustomHeaders(Request $request) + { + return $request->headers->all(); + } + public function fetchRouteResponse() { $fixture = new \stdClass(); diff --git a/tests/GenerateDocumentationTest.php b/tests/GenerateDocumentationTest.php index 6eab6285..44b56cb9 100644 --- a/tests/GenerateDocumentationTest.php +++ b/tests/GenerateDocumentationTest.php @@ -120,6 +120,27 @@ public function testGeneratedPostmanCollectionFileIsCorrect() $this->assertEquals($generatedCollection, $fixtureCollection); } + public function testCanAppendCustomHttpHeaders() + { + RouteFacade::get('/api/headers', TestController::class.'@checkCustomHeaders'); + + $output = $this->artisan('api:generate', [ + '--routePrefix' => 'api/*', + '--header' => [ + 'Authorization: customAuthToken', + 'X-Custom-Header: foobar', + ] + ]); + + $generatedMarkdown = file_get_contents(__DIR__.'/../public/docs/source/index.md'); + $this->assertContains('"authorization": [ + "customAuthToken" + ], + "x-custom-header": [ + "foobar" + ]', $generatedMarkdown); + } + /** * @param string $command * @param array $parameters From ae8994f2fd4e1ad94d28b1f1083634822ed25988 Mon Sep 17 00:00:00 2001 From: Marcel Pociot Date: Wed, 14 Sep 2016 09:43:28 +0000 Subject: [PATCH 091/780] Applied fixes from StyleCI --- src/Mpociot/ApiDoc/Generators/AbstractGenerator.php | 3 ++- src/Mpociot/ApiDoc/Generators/DingoGenerator.php | 4 ++-- tests/GenerateDocumentationTest.php | 2 +- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/Mpociot/ApiDoc/Generators/AbstractGenerator.php b/src/Mpociot/ApiDoc/Generators/AbstractGenerator.php index 9231bea1..94d1bf40 100644 --- a/src/Mpociot/ApiDoc/Generators/AbstractGenerator.php +++ b/src/Mpociot/ApiDoc/Generators/AbstractGenerator.php @@ -70,8 +70,9 @@ protected function getRouteResponse($route, $bindings, $headers = []) $methods = $route->getMethods(); // Split headers into key - value pairs - $headers = collect($headers)->map(function($value) { + $headers = collect($headers)->map(function ($value) { $split = explode(':', $value); + return [trim($split[0]) => trim($split[1])]; })->collapse()->toArray(); diff --git a/src/Mpociot/ApiDoc/Generators/DingoGenerator.php b/src/Mpociot/ApiDoc/Generators/DingoGenerator.php index 4b3a4862..634ed2dd 100644 --- a/src/Mpociot/ApiDoc/Generators/DingoGenerator.php +++ b/src/Mpociot/ApiDoc/Generators/DingoGenerator.php @@ -47,8 +47,8 @@ public function processRoute($route, $bindings = [], $headers = [], $withRespons public function callRoute($method, $uri, $parameters = [], $cookies = [], $files = [], $server = [], $content = null) { $dispatcher = app('Dingo\Api\Dispatcher')->raw(); - - collect($server)->map(function($key, $value) use ($dispatcher) { + + collect($server)->map(function ($key, $value) use ($dispatcher) { $dispatcher->header($key, $value); }); diff --git a/tests/GenerateDocumentationTest.php b/tests/GenerateDocumentationTest.php index 44b56cb9..737f8337 100644 --- a/tests/GenerateDocumentationTest.php +++ b/tests/GenerateDocumentationTest.php @@ -129,7 +129,7 @@ public function testCanAppendCustomHttpHeaders() '--header' => [ 'Authorization: customAuthToken', 'X-Custom-Header: foobar', - ] + ], ]); $generatedMarkdown = file_get_contents(__DIR__.'/../public/docs/source/index.md'); From 181e4adea600e9a6c16cd272a542946b3c90f174 Mon Sep 17 00:00:00 2001 From: Marcel Pociot Date: Wed, 14 Sep 2016 16:08:33 +0200 Subject: [PATCH 092/780] Allow multiple required_if or required_unless rules --- .../ApiDoc/Generators/AbstractGenerator.php | 18 ++++++++++++++++-- src/resources/lang/en/rules.php | 4 ++-- tests/ApiDocGeneratorTest.php | 6 ++++++ tests/DingoGeneratorTest.php | 6 ++++++ tests/Fixtures/DingoTestRequest.php | 1 + tests/Fixtures/TestRequest.php | 1 + 6 files changed, 32 insertions(+), 4 deletions(-) diff --git a/src/Mpociot/ApiDoc/Generators/AbstractGenerator.php b/src/Mpociot/ApiDoc/Generators/AbstractGenerator.php index 9231bea1..b7b21dde 100644 --- a/src/Mpociot/ApiDoc/Generators/AbstractGenerator.php +++ b/src/Mpociot/ApiDoc/Generators/AbstractGenerator.php @@ -188,6 +188,20 @@ protected function fancyImplode($arr, $first, $last) return implode($first, $arr); } + protected function splitValuePairs($parameters, $first = 'is ', $last = 'or ') { + $attribute = ''; + collect($parameters)->map(function($item, $key) use (&$attribute, $first, $last) { + $attribute .= '`'.$item.'` '; + if (($key+1) % 2 === 0) { + $attribute .= $last; + } else { + $attribute .= $first; + } + }); + $attribute = rtrim($attribute, $last); + return $attribute; + } + /** * @param string $rule * @param string $ruleName @@ -296,10 +310,10 @@ protected function parseRule($rule, $ruleName, &$attributeData, $seed) $attributeData['description'][] = Description::parse($rule)->with($this->fancyImplode($parameters, ', ', ' or '))->getDescription(); break; case 'required_if': - $attributeData['description'][] = Description::parse($rule)->with($parameters)->getDescription(); + $attributeData['description'][] = Description::parse($rule)->with($this->splitValuePairs($parameters))->getDescription(); break; case 'required_unless': - $attributeData['description'][] = Description::parse($rule)->with($parameters)->getDescription(); + $attributeData['description'][] = Description::parse($rule)->with($this->splitValuePairs($parameters))->getDescription(); break; case 'required_with': $attributeData['description'][] = Description::parse($rule)->with($this->fancyImplode($parameters, ', ', ' or '))->getDescription(); diff --git a/src/resources/lang/en/rules.php b/src/resources/lang/en/rules.php index adad7c79..c3493267 100644 --- a/src/resources/lang/en/rules.php +++ b/src/resources/lang/en/rules.php @@ -20,8 +20,8 @@ 'json' => 'Must be a valid JSON string.', 'mimetypes' => 'Allowed mime types: :attribute', 'mimes' => 'Allowed mime types: :attribute', - 'required_if' => 'Required if `:attribute` is `:attribute`', - 'required_unless' => 'Required unless `:attribute` is `:attribute`', + 'required_if' => 'Required if :attribute', + 'required_unless' => 'Required unless :attribute', 'required_with' => 'Required if the parameters :attribute are present.', 'required_with_all' => 'Required if the parameters :attribute are present.', 'required_without' => 'Required if the parameters :attribute are not present.', diff --git a/tests/ApiDocGeneratorTest.php b/tests/ApiDocGeneratorTest.php index 30f0187f..fe351a70 100644 --- a/tests/ApiDocGeneratorTest.php +++ b/tests/ApiDocGeneratorTest.php @@ -267,6 +267,12 @@ public function testCanParseFormRequestRules() $this->assertCount(1, $attribute['description']); $this->assertSame('Must match this regular expression: `(.*)`', $attribute['description'][0]); break; + case 'multiple_required_if': + $this->assertFalse($attribute['required']); + $this->assertSame('string', $attribute['type']); + $this->assertCount(1, $attribute['description']); + $this->assertSame('Required if `foo` is `bar` or `baz` is `qux`', $attribute['description'][0]); + break; case 'required_if': $this->assertFalse($attribute['required']); $this->assertSame('string', $attribute['type']); diff --git a/tests/DingoGeneratorTest.php b/tests/DingoGeneratorTest.php index 0d0514d8..1684c2ce 100644 --- a/tests/DingoGeneratorTest.php +++ b/tests/DingoGeneratorTest.php @@ -270,6 +270,12 @@ public function testCanParseFormRequestRules() $this->assertCount(1, $attribute['description']); $this->assertSame('Must match this regular expression: `(.*)`', $attribute['description'][0]); break; + case 'multiple_required_if': + $this->assertFalse($attribute['required']); + $this->assertSame('string', $attribute['type']); + $this->assertCount(1, $attribute['description']); + $this->assertSame('Required if `foo` is `bar` or `baz` is `qux`', $attribute['description'][0]); + break; case 'required_if': $this->assertFalse($attribute['required']); $this->assertSame('string', $attribute['type']); diff --git a/tests/Fixtures/DingoTestRequest.php b/tests/Fixtures/DingoTestRequest.php index 157bdbfd..a929b6c5 100644 --- a/tests/Fixtures/DingoTestRequest.php +++ b/tests/Fixtures/DingoTestRequest.php @@ -41,6 +41,7 @@ public function rules() 'numeric' => 'numeric', 'regex' => 'regex:(.*)', 'required_if' => 'required_if:foo,bar', + 'multiple_required_if' => 'required_if:foo,bar,baz,qux', 'required_unless' => 'required_unless:foo,bar', 'required_with' => 'required_with:foo,bar,baz', 'required_with_all' => 'required_with_all:foo,bar,baz', diff --git a/tests/Fixtures/TestRequest.php b/tests/Fixtures/TestRequest.php index e5cf3c8e..5268030c 100644 --- a/tests/Fixtures/TestRequest.php +++ b/tests/Fixtures/TestRequest.php @@ -41,6 +41,7 @@ public function rules() 'numeric' => 'numeric', 'regex' => 'regex:(.*)', 'required_if' => 'required_if:foo,bar', + 'multiple_required_if' => 'required_if:foo,bar,baz,qux', 'required_unless' => 'required_unless:foo,bar', 'required_with' => 'required_with:foo,bar,baz', 'required_with_all' => 'required_with_all:foo,bar,baz', From 7a50f43c941f70fc119b521d91c81a8930547d15 Mon Sep 17 00:00:00 2001 From: Marcel Pociot Date: Wed, 14 Sep 2016 14:08:52 +0000 Subject: [PATCH 093/780] Applied fixes from StyleCI --- src/Mpociot/ApiDoc/Generators/AbstractGenerator.php | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/Mpociot/ApiDoc/Generators/AbstractGenerator.php b/src/Mpociot/ApiDoc/Generators/AbstractGenerator.php index e629525f..0e44ae8a 100644 --- a/src/Mpociot/ApiDoc/Generators/AbstractGenerator.php +++ b/src/Mpociot/ApiDoc/Generators/AbstractGenerator.php @@ -189,17 +189,19 @@ protected function fancyImplode($arr, $first, $last) return implode($first, $arr); } - protected function splitValuePairs($parameters, $first = 'is ', $last = 'or ') { + protected function splitValuePairs($parameters, $first = 'is ', $last = 'or ') + { $attribute = ''; - collect($parameters)->map(function($item, $key) use (&$attribute, $first, $last) { + collect($parameters)->map(function ($item, $key) use (&$attribute, $first, $last) { $attribute .= '`'.$item.'` '; - if (($key+1) % 2 === 0) { + if (($key + 1) % 2 === 0) { $attribute .= $last; } else { $attribute .= $first; } }); $attribute = rtrim($attribute, $last); + return $attribute; } From f7f840771c65f19d8786340477ed175929f2918f Mon Sep 17 00:00:00 2001 From: Marcel Pociot Date: Thu, 6 Oct 2016 14:43:13 +0200 Subject: [PATCH 094/780] Fixed Dingo part of issue #30 --- .../ApiDoc/Commands/GenerateDocumentation.php | 8 ++++-- tests/GenerateDocumentationTest.php | 25 ++++++++++++++++++- 2 files changed, 30 insertions(+), 3 deletions(-) diff --git a/src/Mpociot/ApiDoc/Commands/GenerateDocumentation.php b/src/Mpociot/ApiDoc/Commands/GenerateDocumentation.php index eb2c97d8..05c73bce 100644 --- a/src/Mpociot/ApiDoc/Commands/GenerateDocumentation.php +++ b/src/Mpociot/ApiDoc/Commands/GenerateDocumentation.php @@ -280,8 +280,12 @@ private function processDingoRoutes(AbstractGenerator $generator, $allowedRoutes $parsedRoutes = []; foreach ($routes as $route) { if (empty($allowedRoutes) || in_array($route->getName(), $allowedRoutes) || str_is($routePrefix, $route->uri()) || in_array($middleware, $route->middleware())) { - $parsedRoutes[] = $generator->processRoute($route, $bindings, $this->option('header'), $withResponse); - $this->info('Processed route: ['.implode(',', $route->getMethods()).'] '.$route->uri()); + if ($this->isValidRoute($route) && $this->isRouteVisibleForDocumentation($route->getAction()['uses'])) { + $parsedRoutes[] = $generator->processRoute($route, $bindings, $this->option('header'), $withResponse); + $this->info('Processed route: [' . implode(',', $route->getMethods()) . '] ' . $route->uri()); + } else { + $this->warn('Skipping route: ['.implode(',', $route->getMethods()).'] '.$route->uri()); + } } } diff --git a/tests/GenerateDocumentationTest.php b/tests/GenerateDocumentationTest.php index 737f8337..008e2010 100644 --- a/tests/GenerateDocumentationTest.php +++ b/tests/GenerateDocumentationTest.php @@ -2,10 +2,12 @@ namespace Mpociot\ApiDoc\Tests; +use Dingo\Api\Provider\LaravelServiceProvider; use Illuminate\Contracts\Console\Kernel; use Illuminate\Routing\Route; use Mpociot\ApiDoc\ApiDocGeneratorServiceProvider; use Mpociot\ApiDoc\Generators\LaravelGenerator; +use Mpociot\ApiDoc\Tests\Fixtures\DingoTestController; use Orchestra\Testbench\TestCase; use Mpociot\ApiDoc\Tests\Fixtures\TestController; use Illuminate\Support\Facades\Route as RouteFacade; @@ -39,7 +41,10 @@ public function tearDown() */ protected function getPackageProviders($app) { - return [ApiDocGeneratorServiceProvider::class]; + return [ + LaravelServiceProvider::class, + ApiDocGeneratorServiceProvider::class + ]; } public function testConsoleCommandNeedsAPrefixOrRoute() @@ -62,6 +67,24 @@ public function testConsoleCommandDoesNotWorkWithClosure() $this->assertContains('Processed route: [GET,HEAD] api/test', $output); } + public function testConsoleCommandDoesNotWorkWithClosureUsingDingo() + { + $api = app('Dingo\Api\Routing\Router'); + $api->version('v1', function ($api) { + $api->get('/closure', function () { + return 'foo'; + }); + $api->get('/test', DingoTestController::class.'@parseMethodDescription'); + + $output = $this->artisan('api:generate', [ + '--router' => 'dingo', + '--routePrefix' => 'v1', + ]); + $this->assertContains('Skipping route: [GET,HEAD] closure', $output); + $this->assertContains('Processed route: [GET,HEAD] test', $output); + }); + } + public function testCanSkipSingleRoutesCommandDoesNotWorkWithClosure() { RouteFacade::get('/api/skip', TestController::class.'@skip'); From 5f37e0c293157280849c052bd40b48474cb7b238 Mon Sep 17 00:00:00 2001 From: Marcel Pociot Date: Thu, 6 Oct 2016 12:43:23 +0000 Subject: [PATCH 095/780] Applied fixes from StyleCI --- src/Mpociot/ApiDoc/Commands/GenerateDocumentation.php | 4 ++-- tests/GenerateDocumentationTest.php | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Mpociot/ApiDoc/Commands/GenerateDocumentation.php b/src/Mpociot/ApiDoc/Commands/GenerateDocumentation.php index 05c73bce..a8d1a2fe 100644 --- a/src/Mpociot/ApiDoc/Commands/GenerateDocumentation.php +++ b/src/Mpociot/ApiDoc/Commands/GenerateDocumentation.php @@ -130,7 +130,7 @@ private function writeMarkdown($parsedRoutes) return $routeGroup->transform(function ($route) use ($generatedDocumentation, $compareDocumentation) { if (preg_match('/(.*)/is', $generatedDocumentation, $routeMatch)) { $routeDocumentationChanged = (preg_match('/(.*)/is', $compareDocumentation, $compareMatch) && $compareMatch[1] !== $routeMatch[1]); - if ($routeDocumentationChanged === false || $this->option('force')) { + if ($routeDocumentationChanged === false || $this->option('force')) { if ($routeDocumentationChanged) { $this->warn('Discarded manual changes for route ['.implode(',', $route['methods']).'] '.$route['uri']); } @@ -282,7 +282,7 @@ private function processDingoRoutes(AbstractGenerator $generator, $allowedRoutes if (empty($allowedRoutes) || in_array($route->getName(), $allowedRoutes) || str_is($routePrefix, $route->uri()) || in_array($middleware, $route->middleware())) { if ($this->isValidRoute($route) && $this->isRouteVisibleForDocumentation($route->getAction()['uses'])) { $parsedRoutes[] = $generator->processRoute($route, $bindings, $this->option('header'), $withResponse); - $this->info('Processed route: [' . implode(',', $route->getMethods()) . '] ' . $route->uri()); + $this->info('Processed route: ['.implode(',', $route->getMethods()).'] '.$route->uri()); } else { $this->warn('Skipping route: ['.implode(',', $route->getMethods()).'] '.$route->uri()); } diff --git a/tests/GenerateDocumentationTest.php b/tests/GenerateDocumentationTest.php index 008e2010..40617426 100644 --- a/tests/GenerateDocumentationTest.php +++ b/tests/GenerateDocumentationTest.php @@ -43,7 +43,7 @@ protected function getPackageProviders($app) { return [ LaravelServiceProvider::class, - ApiDocGeneratorServiceProvider::class + ApiDocGeneratorServiceProvider::class, ]; } From c1a50b523f2558cc9f58c5cfbbbf74ea1fe65f47 Mon Sep 17 00:00:00 2001 From: Marcel Pociot Date: Wed, 19 Oct 2016 20:45:04 +0200 Subject: [PATCH 096/780] Fix issue #99 --- src/Mpociot/ApiDoc/Postman/CollectionWriter.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Mpociot/ApiDoc/Postman/CollectionWriter.php b/src/Mpociot/ApiDoc/Postman/CollectionWriter.php index c95486de..f5933d80 100644 --- a/src/Mpociot/ApiDoc/Postman/CollectionWriter.php +++ b/src/Mpociot/ApiDoc/Postman/CollectionWriter.php @@ -28,7 +28,7 @@ public function getCollection() 'variables' => [], 'info' => [ 'name' => '', - '_postman_id' => Uuid::uuid1()->toString(), + '_postman_id' => Uuid::uuid4()->toString(), 'description' => '', 'schema' => 'https://schema.getpostman.com/json/collection/v2.0.0/collection.json', ], From a1e293968c084fdd7012b1ab236dade398eec5c3 Mon Sep 17 00:00:00 2001 From: JapSeyz Date: Sun, 30 Oct 2016 01:44:09 +0200 Subject: [PATCH 097/780] Allow changes to be made to the info vendor view --- src/Mpociot/ApiDoc/Commands/GenerateDocumentation.php | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/Mpociot/ApiDoc/Commands/GenerateDocumentation.php b/src/Mpociot/ApiDoc/Commands/GenerateDocumentation.php index a8d1a2fe..da9ed004 100644 --- a/src/Mpociot/ApiDoc/Commands/GenerateDocumentation.php +++ b/src/Mpociot/ApiDoc/Commands/GenerateDocumentation.php @@ -118,10 +118,6 @@ private function writeMarkdown($parsedRoutes) $generatedDocumentation = file_get_contents($targetFile); $compareDocumentation = file_get_contents($compareFile); - if (preg_match('/(.*)/is', $generatedDocumentation, $generatedInfoText)) { - $infoText = trim($generatedInfoText[1], "\n"); - } - if (preg_match('/---(.*)---\\s/is', $generatedDocumentation, $generatedFrontmatter)) { $frontmatter = trim($generatedFrontmatter[1], "\n"); } From 0baa6779ae1219690ae6a0803f1e749572de5ebc Mon Sep 17 00:00:00 2001 From: Rytis Date: Wed, 9 Nov 2016 14:27:50 +0200 Subject: [PATCH 098/780] Response example for routes with parameters --- src/Mpociot/ApiDoc/Generators/AbstractGenerator.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/Mpociot/ApiDoc/Generators/AbstractGenerator.php b/src/Mpociot/ApiDoc/Generators/AbstractGenerator.php index 0e44ae8a..0f46c198 100644 --- a/src/Mpociot/ApiDoc/Generators/AbstractGenerator.php +++ b/src/Mpociot/ApiDoc/Generators/AbstractGenerator.php @@ -76,6 +76,9 @@ protected function getRouteResponse($route, $bindings, $headers = []) return [trim($split[0]) => trim($split[1])]; })->collapse()->toArray(); + //Changes url with parameters like /users/{user} to /users/1 + $uri = preg_replace("/{(.*)}/", 1, $uri); + return $this->callRoute(array_shift($methods), $uri, [], [], [], $headers); } From 2a026d7f8254f9b8b6d7e10f93c7e46af2f1baf3 Mon Sep 17 00:00:00 2001 From: Rytis Date: Wed, 9 Nov 2016 14:34:27 +0200 Subject: [PATCH 099/780] Update AbstractGenerator.php --- src/Mpociot/ApiDoc/Generators/AbstractGenerator.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Mpociot/ApiDoc/Generators/AbstractGenerator.php b/src/Mpociot/ApiDoc/Generators/AbstractGenerator.php index 0f46c198..1bc766fb 100644 --- a/src/Mpociot/ApiDoc/Generators/AbstractGenerator.php +++ b/src/Mpociot/ApiDoc/Generators/AbstractGenerator.php @@ -77,7 +77,7 @@ protected function getRouteResponse($route, $bindings, $headers = []) })->collapse()->toArray(); //Changes url with parameters like /users/{user} to /users/1 - $uri = preg_replace("/{(.*)}/", 1, $uri); + $uri = preg_replace('/{(.*)}/', 1, $uri); return $this->callRoute(array_shift($methods), $uri, [], [], [], $headers); } From d2e07d097b8f143780b4d82fae696edb22d903ac Mon Sep 17 00:00:00 2001 From: Rytis Date: Wed, 9 Nov 2016 14:38:21 +0200 Subject: [PATCH 100/780] Update AbstractGenerator.php --- src/Mpociot/ApiDoc/Generators/AbstractGenerator.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Mpociot/ApiDoc/Generators/AbstractGenerator.php b/src/Mpociot/ApiDoc/Generators/AbstractGenerator.php index 1bc766fb..a635eb2f 100644 --- a/src/Mpociot/ApiDoc/Generators/AbstractGenerator.php +++ b/src/Mpociot/ApiDoc/Generators/AbstractGenerator.php @@ -77,7 +77,7 @@ protected function getRouteResponse($route, $bindings, $headers = []) })->collapse()->toArray(); //Changes url with parameters like /users/{user} to /users/1 - $uri = preg_replace('/{(.*)}/', 1, $uri); + $uri = preg_replace('/{(.*)}/', 1, $uri); return $this->callRoute(array_shift($methods), $uri, [], [], [], $headers); } From 4a00e39d7a2ca6a73db5c4d53644e548f26e19a3 Mon Sep 17 00:00:00 2001 From: Rytis Date: Wed, 9 Nov 2016 14:41:13 +0200 Subject: [PATCH 101/780] Update AbstractGenerator.php --- src/Mpociot/ApiDoc/Generators/AbstractGenerator.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Mpociot/ApiDoc/Generators/AbstractGenerator.php b/src/Mpociot/ApiDoc/Generators/AbstractGenerator.php index a635eb2f..118244fc 100644 --- a/src/Mpociot/ApiDoc/Generators/AbstractGenerator.php +++ b/src/Mpociot/ApiDoc/Generators/AbstractGenerator.php @@ -78,7 +78,7 @@ protected function getRouteResponse($route, $bindings, $headers = []) //Changes url with parameters like /users/{user} to /users/1 $uri = preg_replace('/{(.*)}/', 1, $uri); - + return $this->callRoute(array_shift($methods), $uri, [], [], [], $headers); } From eba104355aef8470d51f5a0f0dd33d0d52ecb60d Mon Sep 17 00:00:00 2001 From: Idir Ouhab Meskine Date: Thu, 10 Nov 2016 06:51:56 +0100 Subject: [PATCH 102/780] Update README.md to fix examples (#122) --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 71a983c3..ea574122 100644 --- a/README.md +++ b/README.md @@ -109,13 +109,13 @@ If your API route accepts a `GET` method, this package tries to call the API rou If your API needs an authenticated user, you can use the `actAsUserId` option to specify a user ID that will be used for making these API calls: ```sh -$ php artisan api:generate --routePrefix=api/* --actAsUserId=1 +$ php artisan api:generate --routePrefix="api/*" --actAsUserId=1 ``` If you don't want to automatically perform API response calls, use the `noResponseCalls` option. ```sh -$ php artisan api:generate --routePrefix=api/* --noResponseCalls +$ php artisan api:generate --routePrefix="api/*" --noResponseCalls ``` > Note: The example API responses work best with seeded data. From c2388cba274be5a9af0fa03738568beca585e842 Mon Sep 17 00:00:00 2001 From: Idir Ouhab Meskine Date: Sun, 13 Nov 2016 00:30:25 +0100 Subject: [PATCH 103/780] Fix issue #114 (#123) Adding indentation to js example request --- src/resources/views/partials/route.blade.php | 6 +++--- tests/Fixtures/index.md | 12 ++++++------ 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/resources/views/partials/route.blade.php b/src/resources/views/partials/route.blade.php index 04f396e8..dc150539 100644 --- a/src/resources/views/partials/route.blade.php +++ b/src/resources/views/partials/route.blade.php @@ -28,13 +28,13 @@ @if(count($parsedRoute['parameters'])) "data": {!! str_replace(' ',' ',json_encode(array_combine(array_keys($parsedRoute['parameters']), array_map(function($param){ return $param['value']; },$parsedRoute['parameters'])), JSON_PRETTY_PRINT)) !!}, @endif - "headers": { - "accept": "application/json" +"headers": { + "accept": "application/json" } } $.ajax(settings).done(function (response) { -console.log(response); + console.log(response); }); ``` diff --git a/tests/Fixtures/index.md b/tests/Fixtures/index.md index 192be8c5..6094b590 100644 --- a/tests/Fixtures/index.md +++ b/tests/Fixtures/index.md @@ -40,13 +40,13 @@ var settings = { "crossDomain": true, "url": "http://localhost/api/test", "method": "GET", - "headers": { - "accept": "application/json" + "headers": { + "accept": "application/json" } } $.ajax(settings).done(function (response) { -console.log(response); + console.log(response); }); ``` @@ -79,13 +79,13 @@ var settings = { "crossDomain": true, "url": "http://localhost/api/fetch", "method": "GET", - "headers": { - "accept": "application/json" + "headers": { + "accept": "application/json" } } $.ajax(settings).done(function (response) { -console.log(response); + console.log(response); }); ``` From 9bbc18b769b53e888709d1da204edec56ba59748 Mon Sep 17 00:00:00 2001 From: Rytis Date: Sun, 13 Nov 2016 01:32:47 +0200 Subject: [PATCH 104/780] Fixes issues #65, #84 (#124) * Fixes issue #84 and #65 * tests fix --- src/Mpociot/ApiDoc/Generators/LaravelGenerator.php | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/Mpociot/ApiDoc/Generators/LaravelGenerator.php b/src/Mpociot/ApiDoc/Generators/LaravelGenerator.php index 9deba180..275d5c59 100644 --- a/src/Mpociot/ApiDoc/Generators/LaravelGenerator.php +++ b/src/Mpociot/ApiDoc/Generators/LaravelGenerator.php @@ -88,6 +88,11 @@ public function callRoute($method, $uri, $parameters = [], $cookies = [], $files $kernel->terminate($request, $response); + if (file_exists($file = App::bootstrapPath().'/app.php')) { + $app = require $file; + $app->make('Illuminate\Contracts\Console\Kernel')->bootstrap(); + } + return $response; } } From 7747cd0387e8c470a734bb89734f3079040c6c5e Mon Sep 17 00:00:00 2001 From: f3cp Date: Tue, 22 Nov 2016 09:57:42 +1100 Subject: [PATCH 105/780] Fix so that ordering within groups is preserved (to match ordering of Routes file) while groups are ordered Alphabetically (+1 squashed commit) (#126) Squashed commits: [b52e8e0] Fix so that grouping is sorted alphabetically Changing the order of the sortby and groupby will allow the groups to be sorted in alphabetical order --- src/Mpociot/ApiDoc/Commands/GenerateDocumentation.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/Mpociot/ApiDoc/Commands/GenerateDocumentation.php b/src/Mpociot/ApiDoc/Commands/GenerateDocumentation.php index a8d1a2fe..dc4fd5b7 100644 --- a/src/Mpociot/ApiDoc/Commands/GenerateDocumentation.php +++ b/src/Mpociot/ApiDoc/Commands/GenerateDocumentation.php @@ -81,7 +81,9 @@ public function handle() } else { $parsedRoutes = $this->processDingoRoutes($generator, $allowedRoutes, $routePrefix, $middleware); } - $parsedRoutes = collect($parsedRoutes)->groupBy('resource')->sortBy('resource'); + $parsedRoutes = collect($parsedRoutes)->groupBy('resource')->sort(function ($a, $b) { + return strcmp($a->first()['resource'], $b->first()['resource']); + }); $this->writeMarkdown($parsedRoutes); } From 7dd3f6367ba1fbdedbcaa27a08d3eae6541475fa Mon Sep 17 00:00:00 2001 From: Marcel Pociot Date: Thu, 24 Nov 2016 23:08:52 +0100 Subject: [PATCH 106/780] Added tests --- tests/Fixtures/TestResourceController.php | 99 ++++++++ tests/Fixtures/resource_index.md | 285 ++++++++++++++++++++++ tests/GenerateDocumentationTest.php | 12 + 3 files changed, 396 insertions(+) create mode 100644 tests/Fixtures/TestResourceController.php create mode 100644 tests/Fixtures/resource_index.md diff --git a/tests/Fixtures/TestResourceController.php b/tests/Fixtures/TestResourceController.php new file mode 100644 index 00000000..8d2f521a --- /dev/null +++ b/tests/Fixtures/TestResourceController.php @@ -0,0 +1,99 @@ + true + ]; + } + + /** + * Show the form for creating a new resource. + * + * @return \Illuminate\Http\Response + */ + public function create() + { + return [ + 'create_resource' => true + ]; + } + + /** + * Store a newly created resource in storage. + * + * @param \Illuminate\Http\Request $request + * @return \Illuminate\Http\Response + */ + public function store(Request $request) + { + return [ + 'store_resource' => true + ]; + } + + /** + * Display the specified resource. + * + * @param int $id + * @return \Illuminate\Http\Response + */ + public function show($id) + { + return [ + 'show_resource' => $id + ]; + } + + /** + * Show the form for editing the specified resource. + * + * @param int $id + * @return \Illuminate\Http\Response + */ + public function edit($id) + { + return [ + 'edit_resource' => $id + ]; + } + + /** + * Update the specified resource in storage. + * + * @param \Illuminate\Http\Request $request + * @param int $id + * @return \Illuminate\Http\Response + */ + public function update(Request $request, $id) + { + return [ + 'update_resource' => $id + ]; + } + + /** + * Remove the specified resource from storage. + * + * @param int $id + * @return \Illuminate\Http\Response + */ + public function destroy($id) + { + return [ + 'destroy_resource' => $id + ]; + } +} diff --git a/tests/Fixtures/resource_index.md b/tests/Fixtures/resource_index.md new file mode 100644 index 00000000..a650fefa --- /dev/null +++ b/tests/Fixtures/resource_index.md @@ -0,0 +1,285 @@ +--- +title: API Reference + +language_tabs: +- bash +- javascript + +includes: + +search: true + +toc_footers: +- Documentation Powered by Documentarian +--- + +# Info + +Welcome to the generated API reference. +[Get Postman Collection](http://localhost/docs/collection.json) + + + +#general + +## Display a listing of the resource. + +> Example request: + +```bash +curl "http://localhost/api/user" \ +-H "Accept: application/json" +``` + +```javascript +var settings = { + "async": true, + "crossDomain": true, + "url": "http://localhost/api/user", + "method": "GET", + "headers": { + "accept": "application/json" + } +} + +$.ajax(settings).done(function (response) { + console.log(response); +}); +``` + +> Example response: + +```json +{ + "index_resource": true +} +``` + +### HTTP Request +`GET api/user` + +`HEAD api/user` + + + + +## Show the form for creating a new resource. + +> Example request: + +```bash +curl "http://localhost/api/user/create" \ +-H "Accept: application/json" +``` + +```javascript +var settings = { + "async": true, + "crossDomain": true, + "url": "http://localhost/api/user/create", + "method": "GET", + "headers": { + "accept": "application/json" + } +} + +$.ajax(settings).done(function (response) { + console.log(response); +}); +``` + +> Example response: + +```json +{ + "create_resource": true +} +``` + +### HTTP Request +`GET api/user/create` + +`HEAD api/user/create` + + + + +## Store a newly created resource in storage. + +> Example request: + +```bash +curl "http://localhost/api/user" \ +-H "Accept: application/json" +``` + +```javascript +var settings = { + "async": true, + "crossDomain": true, + "url": "http://localhost/api/user", + "method": "POST", + "headers": { + "accept": "application/json" + } +} + +$.ajax(settings).done(function (response) { + console.log(response); +}); +``` + + +### HTTP Request +`POST api/user` + + + + +## Display the specified resource. + +> Example request: + +```bash +curl "http://localhost/api/user/%7Buser%7D" \ +-H "Accept: application/json" +``` + +```javascript +var settings = { + "async": true, + "crossDomain": true, + "url": "http://localhost/api/user/%7Buser%7D", + "method": "GET", + "headers": { + "accept": "application/json" + } +} + +$.ajax(settings).done(function (response) { + console.log(response); +}); +``` + +> Example response: + +```json +{ + "show_resource": "1" +} +``` + +### HTTP Request +`GET api/user/{user}` + +`HEAD api/user/{user}` + + + + +## Show the form for editing the specified resource. + +> Example request: + +```bash +curl "http://localhost/api/user/%7Buser%7D/edit" \ +-H "Accept: application/json" +``` + +```javascript +var settings = { + "async": true, + "crossDomain": true, + "url": "http://localhost/api/user/%7Buser%7D/edit", + "method": "GET", + "headers": { + "accept": "application/json" + } +} + +$.ajax(settings).done(function (response) { + console.log(response); +}); +``` + +> Example response: + +```json +{ + "edit_resource": "1" +} +``` + +### HTTP Request +`GET api/user/{user}/edit` + +`HEAD api/user/{user}/edit` + + + + +## Update the specified resource in storage. + +> Example request: + +```bash +curl "http://localhost/api/user/%7Buser%7D" \ +-H "Accept: application/json" +``` + +```javascript +var settings = { + "async": true, + "crossDomain": true, + "url": "http://localhost/api/user/%7Buser%7D", + "method": "PUT", + "headers": { + "accept": "application/json" + } +} + +$.ajax(settings).done(function (response) { + console.log(response); +}); +``` + + +### HTTP Request +`PUT api/user/{user}` + +`PATCH api/user/{user}` + + + + +## Remove the specified resource from storage. + +> Example request: + +```bash +curl "http://localhost/api/user/%7Buser%7D" \ +-H "Accept: application/json" +``` + +```javascript +var settings = { + "async": true, + "crossDomain": true, + "url": "http://localhost/api/user/%7Buser%7D", + "method": "DELETE", + "headers": { + "accept": "application/json" + } +} + +$.ajax(settings).done(function (response) { + console.log(response); +}); +``` + + +### HTTP Request +`DELETE api/user/{user}` + + + diff --git a/tests/GenerateDocumentationTest.php b/tests/GenerateDocumentationTest.php index 40617426..cb86eddc 100644 --- a/tests/GenerateDocumentationTest.php +++ b/tests/GenerateDocumentationTest.php @@ -10,6 +10,7 @@ use Mpociot\ApiDoc\Tests\Fixtures\DingoTestController; use Orchestra\Testbench\TestCase; use Mpociot\ApiDoc\Tests\Fixtures\TestController; +use Mpociot\ApiDoc\Tests\Fixtures\TestResourceController; use Illuminate\Support\Facades\Route as RouteFacade; class GenerateDocumentationTest extends TestCase @@ -97,6 +98,17 @@ public function testCanSkipSingleRoutesCommandDoesNotWorkWithClosure() $this->assertContains('Processed route: [GET,HEAD] api/test', $output); } + public function testCanParseResourceRoutes() + { + RouteFacade::resource('/api/user', TestResourceController::class); + $output = $this->artisan('api:generate', [ + '--routePrefix' => 'api/*', + ]); + $generatedMarkdown = file_get_contents(__DIR__.'/../public/docs/source/index.md'); + $fixtureMarkdown = file_get_contents(__DIR__.'/Fixtures/resource_index.md'); + $this->assertSame($generatedMarkdown, $fixtureMarkdown); + } + public function testGeneratedMarkdownFileIsCorrect() { RouteFacade::get('/api/test', TestController::class.'@parseMethodDescription'); From 96c553a16540562f973008f418d075e20df411f4 Mon Sep 17 00:00:00 2001 From: Marcel Pociot Date: Thu, 24 Nov 2016 22:09:31 +0000 Subject: [PATCH 107/780] Applied fixes from StyleCI --- tests/Fixtures/TestResourceController.php | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/tests/Fixtures/TestResourceController.php b/tests/Fixtures/TestResourceController.php index 8d2f521a..aae43b86 100644 --- a/tests/Fixtures/TestResourceController.php +++ b/tests/Fixtures/TestResourceController.php @@ -15,7 +15,7 @@ class TestResourceController extends Controller public function index() { return [ - 'index_resource' => true + 'index_resource' => true, ]; } @@ -27,7 +27,7 @@ public function index() public function create() { return [ - 'create_resource' => true + 'create_resource' => true, ]; } @@ -35,12 +35,13 @@ public function create() * Store a newly created resource in storage. * * @param \Illuminate\Http\Request $request + * * @return \Illuminate\Http\Response */ public function store(Request $request) { return [ - 'store_resource' => true + 'store_resource' => true, ]; } @@ -48,12 +49,13 @@ public function store(Request $request) * Display the specified resource. * * @param int $id + * * @return \Illuminate\Http\Response */ public function show($id) { return [ - 'show_resource' => $id + 'show_resource' => $id, ]; } @@ -61,12 +63,13 @@ public function show($id) * Show the form for editing the specified resource. * * @param int $id + * * @return \Illuminate\Http\Response */ public function edit($id) { return [ - 'edit_resource' => $id + 'edit_resource' => $id, ]; } @@ -75,12 +78,13 @@ public function edit($id) * * @param \Illuminate\Http\Request $request * @param int $id + * * @return \Illuminate\Http\Response */ public function update(Request $request, $id) { return [ - 'update_resource' => $id + 'update_resource' => $id, ]; } @@ -88,12 +92,13 @@ public function update(Request $request, $id) * Remove the specified resource from storage. * * @param int $id + * * @return \Illuminate\Http\Response */ public function destroy($id) { return [ - 'destroy_resource' => $id + 'destroy_resource' => $id, ]; } } From aaa36dcd6cd82f689d8fd3e0ae29e668077a2d85 Mon Sep 17 00:00:00 2001 From: Marcel Pociot Date: Thu, 24 Nov 2016 23:17:57 +0100 Subject: [PATCH 108/780] Use JSON_UNESCAPED_UNICODE - Fixes #102 --- src/resources/views/partials/route.blade.php | 4 ++-- tests/Fixtures/TestController.php | 5 +++++ tests/GenerateDocumentationTest.php | 12 ++++++++++++ 3 files changed, 19 insertions(+), 2 deletions(-) diff --git a/src/resources/views/partials/route.blade.php b/src/resources/views/partials/route.blade.php index dc150539..db85571a 100644 --- a/src/resources/views/partials/route.blade.php +++ b/src/resources/views/partials/route.blade.php @@ -43,9 +43,9 @@ ```json @if(is_object($parsedRoute['response']) || is_array($parsedRoute['response'])) -{!! json_encode($parsedRoute['response'], JSON_PRETTY_PRINT) !!} +{!! json_encode($parsedRoute['response'], JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE) !!} @else -{!! json_encode(json_decode($parsedRoute['response']), JSON_PRETTY_PRINT) !!} +{!! json_encode(json_decode($parsedRoute['response']), JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE) !!} @endif ``` @endif diff --git a/tests/Fixtures/TestController.php b/tests/Fixtures/TestController.php index 14c9675b..521d2394 100644 --- a/tests/Fixtures/TestController.php +++ b/tests/Fixtures/TestController.php @@ -60,6 +60,11 @@ public function dependencyInjection(DependencyInjection $dependency, TestRequest return ''; } + public function utf8() + { + return ['result' => 'Лорем ипсум долор сит амет']; + } + /** * @hideFromAPIDocumentation */ diff --git a/tests/GenerateDocumentationTest.php b/tests/GenerateDocumentationTest.php index cb86eddc..490ddb55 100644 --- a/tests/GenerateDocumentationTest.php +++ b/tests/GenerateDocumentationTest.php @@ -176,6 +176,18 @@ public function testCanAppendCustomHttpHeaders() ]', $generatedMarkdown); } + public function testGeneratesUTF8Responses() + { + RouteFacade::get('/api/utf8', TestController::class.'@utf8'); + + $output = $this->artisan('api:generate', [ + '--routePrefix' => 'api/*' + ]); + + $generatedMarkdown = file_get_contents(__DIR__.'/../public/docs/source/index.md'); + $this->assertContains('Лорем ипсум долор сит амет', $generatedMarkdown); + } + /** * @param string $command * @param array $parameters From 5d1ffe1a78660d4c75a0c6a5224866dc6abb6d8e Mon Sep 17 00:00:00 2001 From: Marcel Pociot Date: Thu, 24 Nov 2016 22:18:24 +0000 Subject: [PATCH 109/780] Applied fixes from StyleCI --- tests/GenerateDocumentationTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/GenerateDocumentationTest.php b/tests/GenerateDocumentationTest.php index 490ddb55..b5e85ec0 100644 --- a/tests/GenerateDocumentationTest.php +++ b/tests/GenerateDocumentationTest.php @@ -181,7 +181,7 @@ public function testGeneratesUTF8Responses() RouteFacade::get('/api/utf8', TestController::class.'@utf8'); $output = $this->artisan('api:generate', [ - '--routePrefix' => 'api/*' + '--routePrefix' => 'api/*', ]); $generatedMarkdown = file_get_contents(__DIR__.'/../public/docs/source/index.md'); From c38692d740b21f4bd77af17b3f5f26a44f4d7302 Mon Sep 17 00:00:00 2001 From: Marcel Pociot Date: Thu, 24 Nov 2016 23:30:48 +0100 Subject: [PATCH 110/780] Added useMiddlewares option - Fixes #112 --- README.md | 1 + .../ApiDoc/Commands/GenerateDocumentation.php | 3 +++ .../ApiDoc/Generators/AbstractGenerator.php | 11 ++++++++++- src/Mpociot/ApiDoc/Generators/DingoGenerator.php | 13 +++++++++++++ .../ApiDoc/Generators/LaravelGenerator.php | 16 +++++++++++++--- 5 files changed, 40 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index ea574122..a64cfb2d 100644 --- a/README.md +++ b/README.md @@ -46,6 +46,7 @@ Option | Description `middleware` | The middlewares to use for generation `noResponseCalls` | Disable API response calls `noPostmanCollection` | Disable Postman collection creation +`useMiddlewares` | Use all configured route middlewares (Needed for Laravel 5.3 `SubstituteBindings` middleware) `actAsUserId` | The user ID to use for authenticated API response calls `router` | The router to use, when processing the route files (can be Laravel or Dingo - defaults to Laravel) `bindings` | List of route bindings that should be replaced when trying to retrieve route results. Syntax format: `binding_one,id|binding_two,id` diff --git a/src/Mpociot/ApiDoc/Commands/GenerateDocumentation.php b/src/Mpociot/ApiDoc/Commands/GenerateDocumentation.php index dc4fd5b7..3f4d40f3 100644 --- a/src/Mpociot/ApiDoc/Commands/GenerateDocumentation.php +++ b/src/Mpociot/ApiDoc/Commands/GenerateDocumentation.php @@ -27,6 +27,7 @@ class GenerateDocumentation extends Command {--middleware= : The middleware to use for generation} {--noResponseCalls : Disable API response calls} {--noPostmanCollection : Disable Postman collection creation} + {--useMiddlewares : Use all configured route middlewares} {--actAsUserId= : The user ID to use for API response calls} {--router=laravel : The router to be used (Laravel or Dingo)} {--force : Force rewriting of existing routes} @@ -76,6 +77,8 @@ public function handle() return false; } + $generator->prepareMiddleware($this->option('useMiddlewares')); + if ($this->option('router') === 'laravel') { $parsedRoutes = $this->processLaravelRoutes($generator, $allowedRoutes, $routePrefix, $middleware); } else { diff --git a/src/Mpociot/ApiDoc/Generators/AbstractGenerator.php b/src/Mpociot/ApiDoc/Generators/AbstractGenerator.php index 118244fc..df49b2e3 100644 --- a/src/Mpociot/ApiDoc/Generators/AbstractGenerator.php +++ b/src/Mpociot/ApiDoc/Generators/AbstractGenerator.php @@ -29,6 +29,15 @@ abstract protected function getUri($route); */ abstract public function processRoute($route, $bindings = [], $withResponse = true); + /** + * Prepares / Disables route middlewares + * + * @param boolean $disable + * + * @return void + */ + abstract public function prepareMiddleware($disable = false); + /** * @param array $routeData * @param array $routeAction @@ -78,7 +87,7 @@ protected function getRouteResponse($route, $bindings, $headers = []) //Changes url with parameters like /users/{user} to /users/1 $uri = preg_replace('/{(.*)}/', 1, $uri); - + return $this->callRoute(array_shift($methods), $uri, [], [], [], $headers); } diff --git a/src/Mpociot/ApiDoc/Generators/DingoGenerator.php b/src/Mpociot/ApiDoc/Generators/DingoGenerator.php index 634ed2dd..0f66cd79 100644 --- a/src/Mpociot/ApiDoc/Generators/DingoGenerator.php +++ b/src/Mpociot/ApiDoc/Generators/DingoGenerator.php @@ -41,6 +41,19 @@ public function processRoute($route, $bindings = [], $headers = [], $withRespons ], $routeAction, $bindings); } + /** + * Prepares / Disables route middlewares + * + * @param boolean $disable + * + * @return void + */ + public function prepareMiddleware($disable = true) + { + // Not needed by Dingo + return false; + } + /** * {@inheritdoc} */ diff --git a/src/Mpociot/ApiDoc/Generators/LaravelGenerator.php b/src/Mpociot/ApiDoc/Generators/LaravelGenerator.php index 275d5c59..9cd80d4e 100644 --- a/src/Mpociot/ApiDoc/Generators/LaravelGenerator.php +++ b/src/Mpociot/ApiDoc/Generators/LaravelGenerator.php @@ -56,6 +56,18 @@ public function processRoute($route, $bindings = [], $headers = [], $withRespons ], $routeAction, $bindings); } + /** + * Prepares / Disables route middlewares + * + * @param boolean $disable + * + * @return void + */ + public function prepareMiddleware($disable = true) + { + App::instance('middleware.disable', true); + } + /** * Call the given URI and return the Response. * @@ -71,9 +83,6 @@ public function processRoute($route, $bindings = [], $headers = [], $withRespons */ public function callRoute($method, $uri, $parameters = [], $cookies = [], $files = [], $server = [], $content = null) { - $kernel = App::make('Illuminate\Contracts\Http\Kernel'); - App::instance('middleware.disable', true); - $server = collect([ 'CONTENT_TYPE' => 'application/json', 'Accept' => 'application/json', @@ -84,6 +93,7 @@ public function callRoute($method, $uri, $parameters = [], $cookies = [], $files $cookies, $files, $this->transformHeadersToServerVars($server), $content ); + $kernel = App::make('Illuminate\Contracts\Http\Kernel'); $response = $kernel->handle($request); $kernel->terminate($request, $response); From 8d8a4af0d3a53b6b03c3921ce8658176f3825b65 Mon Sep 17 00:00:00 2001 From: Marcel Pociot Date: Thu, 24 Nov 2016 22:31:32 +0000 Subject: [PATCH 111/780] Applied fixes from StyleCI --- src/Mpociot/ApiDoc/Generators/AbstractGenerator.php | 8 ++++---- src/Mpociot/ApiDoc/Generators/DingoGenerator.php | 6 +++--- src/Mpociot/ApiDoc/Generators/LaravelGenerator.php | 6 +++--- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/Mpociot/ApiDoc/Generators/AbstractGenerator.php b/src/Mpociot/ApiDoc/Generators/AbstractGenerator.php index df49b2e3..5c4ada8c 100644 --- a/src/Mpociot/ApiDoc/Generators/AbstractGenerator.php +++ b/src/Mpociot/ApiDoc/Generators/AbstractGenerator.php @@ -30,9 +30,9 @@ abstract protected function getUri($route); abstract public function processRoute($route, $bindings = [], $withResponse = true); /** - * Prepares / Disables route middlewares - * - * @param boolean $disable + * Prepares / Disables route middlewares. + * + * @param bool $disable * * @return void */ @@ -87,7 +87,7 @@ protected function getRouteResponse($route, $bindings, $headers = []) //Changes url with parameters like /users/{user} to /users/1 $uri = preg_replace('/{(.*)}/', 1, $uri); - + return $this->callRoute(array_shift($methods), $uri, [], [], [], $headers); } diff --git a/src/Mpociot/ApiDoc/Generators/DingoGenerator.php b/src/Mpociot/ApiDoc/Generators/DingoGenerator.php index 0f66cd79..0b9b92f9 100644 --- a/src/Mpociot/ApiDoc/Generators/DingoGenerator.php +++ b/src/Mpociot/ApiDoc/Generators/DingoGenerator.php @@ -42,9 +42,9 @@ public function processRoute($route, $bindings = [], $headers = [], $withRespons } /** - * Prepares / Disables route middlewares - * - * @param boolean $disable + * Prepares / Disables route middlewares. + * + * @param bool $disable * * @return void */ diff --git a/src/Mpociot/ApiDoc/Generators/LaravelGenerator.php b/src/Mpociot/ApiDoc/Generators/LaravelGenerator.php index 9cd80d4e..7110fa16 100644 --- a/src/Mpociot/ApiDoc/Generators/LaravelGenerator.php +++ b/src/Mpociot/ApiDoc/Generators/LaravelGenerator.php @@ -57,9 +57,9 @@ public function processRoute($route, $bindings = [], $headers = [], $withRespons } /** - * Prepares / Disables route middlewares - * - * @param boolean $disable + * Prepares / Disables route middlewares. + * + * @param bool $disable * * @return void */ From bf6f4648637cb1f3866e500a1cceeb5d2fcccf91 Mon Sep 17 00:00:00 2001 From: Mark Date: Wed, 7 Dec 2016 07:57:13 -0700 Subject: [PATCH 112/780] Allow DI for validator/rules methods (#134) setContainer(app()) dependency injection via app()->call() Follow Style CI Fix unrelated style Use import Style CI Import Style-ci --- .../ApiDoc/Generators/LaravelGenerator.php | 40 ++++++++++++++++++- 1 file changed, 39 insertions(+), 1 deletion(-) diff --git a/src/Mpociot/ApiDoc/Generators/LaravelGenerator.php b/src/Mpociot/ApiDoc/Generators/LaravelGenerator.php index 7110fa16..cb8a8e88 100644 --- a/src/Mpociot/ApiDoc/Generators/LaravelGenerator.php +++ b/src/Mpociot/ApiDoc/Generators/LaravelGenerator.php @@ -2,9 +2,11 @@ namespace Mpociot\ApiDoc\Generators; +use ReflectionClass; use Illuminate\Routing\Route; use Illuminate\Support\Facades\App; use Illuminate\Support\Facades\Request; +use Illuminate\Foundation\Http\FormRequest; class LaravelGenerator extends AbstractGenerator { @@ -34,7 +36,6 @@ public function processRoute($route, $bindings = [], $headers = [], $withRespons $routeGroup = $this->getRouteGroup($routeAction['uses']); $routeDescription = $this->getRouteDescription($routeAction['uses']); - if ($withResponse) { $response = $this->getRouteResponse($route, $bindings, $headers); if ($response->headers->get('Content-Type') === 'application/json') { @@ -105,4 +106,41 @@ public function callRoute($method, $uri, $parameters = [], $cookies = [], $files return $response; } + + /** + * @param string $route + * @param array $bindings + * + * @return array + */ + protected function getRouteRules($route, $bindings) + { + list($class, $method) = explode('@', $route); + $reflection = new ReflectionClass($class); + $reflectionMethod = $reflection->getMethod($method); + + foreach ($reflectionMethod->getParameters() as $parameter) { + $parameterType = $parameter->getClass(); + if (! is_null($parameterType) && class_exists($parameterType->name)) { + $className = $parameterType->name; + + if (is_subclass_of($className, FormRequest::class)) { + $parameterReflection = new $className; + $parameterReflection->setContainer(app()); + // Add route parameter bindings + $parameterReflection->query->add($bindings); + $parameterReflection->request->add($bindings); + + if (method_exists($parameterReflection, 'validator')) { + return app()->call([$parameterReflection, 'validator']) + ->getRules(); + } else { + return app()->call([$parameterReflection, 'rules']); + } + } + } + } + + return []; + } } From 39d4195c0e5287331386ff667cc49bcf5851db6b Mon Sep 17 00:00:00 2001 From: Marcel Pociot Date: Wed, 7 Dec 2016 14:57:18 +0000 Subject: [PATCH 113/780] Apply fixes from StyleCI --- .../ApiDoc/Commands/GenerateDocumentation.php | 11 +++++------ src/Mpociot/ApiDoc/Generators/AbstractGenerator.php | 8 ++++---- src/Mpociot/ApiDoc/Postman/CollectionWriter.php | 2 +- tests/ApiDocGeneratorTest.php | 8 ++++---- tests/DingoGeneratorTest.php | 8 ++++---- tests/GenerateDocumentationTest.php | 12 ++++++------ tests/RuleDescriptionParserTest.php | 8 ++++---- 7 files changed, 28 insertions(+), 29 deletions(-) diff --git a/src/Mpociot/ApiDoc/Commands/GenerateDocumentation.php b/src/Mpociot/ApiDoc/Commands/GenerateDocumentation.php index 3f4d40f3..0fd0a775 100644 --- a/src/Mpociot/ApiDoc/Commands/GenerateDocumentation.php +++ b/src/Mpociot/ApiDoc/Commands/GenerateDocumentation.php @@ -2,16 +2,16 @@ namespace Mpociot\ApiDoc\Commands; +use ReflectionClass; use Illuminate\Console\Command; +use Mpociot\Reflection\DocBlock; use Illuminate\Support\Collection; use Illuminate\Support\Facades\Route; -use Mpociot\ApiDoc\Generators\AbstractGenerator; +use Mpociot\Documentarian\Documentarian; +use Mpociot\ApiDoc\Postman\CollectionWriter; use Mpociot\ApiDoc\Generators\DingoGenerator; use Mpociot\ApiDoc\Generators\LaravelGenerator; -use Mpociot\ApiDoc\Postman\CollectionWriter; -use Mpociot\Documentarian\Documentarian; -use Mpociot\Reflection\DocBlock; -use ReflectionClass; +use Mpociot\ApiDoc\Generators\AbstractGenerator; class GenerateDocumentation extends Command { @@ -186,7 +186,6 @@ private function writeMarkdown($parsedRoutes) $this->info('Wrote HTML documentation to: '.$outputPath.'/public/index.html'); - if ($this->option('noPostmanCollection') !== true) { $this->info('Generating Postman collection'); diff --git a/src/Mpociot/ApiDoc/Generators/AbstractGenerator.php b/src/Mpociot/ApiDoc/Generators/AbstractGenerator.php index 5c4ada8c..20798c16 100644 --- a/src/Mpociot/ApiDoc/Generators/AbstractGenerator.php +++ b/src/Mpociot/ApiDoc/Generators/AbstractGenerator.php @@ -3,13 +3,13 @@ namespace Mpociot\ApiDoc\Generators; use Faker\Factory; -use Illuminate\Foundation\Http\FormRequest; +use ReflectionClass; use Illuminate\Support\Arr; -use Illuminate\Support\Facades\Validator; use Illuminate\Support\Str; -use Mpociot\ApiDoc\Parsers\RuleDescriptionParser as Description; -use ReflectionClass; use Mpociot\Reflection\DocBlock; +use Illuminate\Support\Facades\Validator; +use Illuminate\Foundation\Http\FormRequest; +use Mpociot\ApiDoc\Parsers\RuleDescriptionParser as Description; abstract class AbstractGenerator { diff --git a/src/Mpociot/ApiDoc/Postman/CollectionWriter.php b/src/Mpociot/ApiDoc/Postman/CollectionWriter.php index f5933d80..6bf21865 100644 --- a/src/Mpociot/ApiDoc/Postman/CollectionWriter.php +++ b/src/Mpociot/ApiDoc/Postman/CollectionWriter.php @@ -2,8 +2,8 @@ namespace Mpociot\ApiDoc\Postman; -use Illuminate\Support\Collection; use Ramsey\Uuid\Uuid; +use Illuminate\Support\Collection; class CollectionWriter { diff --git a/tests/ApiDocGeneratorTest.php b/tests/ApiDocGeneratorTest.php index fe351a70..38cf7b54 100644 --- a/tests/ApiDocGeneratorTest.php +++ b/tests/ApiDocGeneratorTest.php @@ -3,12 +3,12 @@ namespace Mpociot\ApiDoc\Tests; use Illuminate\Routing\Route; -use Illuminate\Support\Facades\Route as RouteFacade; -use Mpociot\ApiDoc\ApiDocGeneratorServiceProvider; +use Orchestra\Testbench\TestCase; +use Mpociot\ApiDoc\Tests\Fixtures\TestRequest; use Mpociot\ApiDoc\Generators\LaravelGenerator; use Mpociot\ApiDoc\Tests\Fixtures\TestController; -use Mpociot\ApiDoc\Tests\Fixtures\TestRequest; -use Orchestra\Testbench\TestCase; +use Mpociot\ApiDoc\ApiDocGeneratorServiceProvider; +use Illuminate\Support\Facades\Route as RouteFacade; class ApiDocGeneratorTest extends TestCase { diff --git a/tests/DingoGeneratorTest.php b/tests/DingoGeneratorTest.php index 1684c2ce..3fac1394 100644 --- a/tests/DingoGeneratorTest.php +++ b/tests/DingoGeneratorTest.php @@ -2,13 +2,13 @@ namespace Mpociot\ApiDoc\Tests; +use Orchestra\Testbench\TestCase; +use Mpociot\ApiDoc\Generators\DingoGenerator; use Dingo\Api\Provider\LaravelServiceProvider; +use Mpociot\ApiDoc\Tests\Fixtures\TestRequest; +use Mpociot\ApiDoc\Tests\Fixtures\TestController; use Mpociot\ApiDoc\ApiDocGeneratorServiceProvider; -use Mpociot\ApiDoc\Generators\DingoGenerator; use Mpociot\ApiDoc\Tests\Fixtures\DingoTestController; -use Mpociot\ApiDoc\Tests\Fixtures\TestController; -use Mpociot\ApiDoc\Tests\Fixtures\TestRequest; -use Orchestra\Testbench\TestCase; class DingoGeneratorTest extends TestCase { diff --git a/tests/GenerateDocumentationTest.php b/tests/GenerateDocumentationTest.php index b5e85ec0..ee67ab6f 100644 --- a/tests/GenerateDocumentationTest.php +++ b/tests/GenerateDocumentationTest.php @@ -2,16 +2,16 @@ namespace Mpociot\ApiDoc\Tests; -use Dingo\Api\Provider\LaravelServiceProvider; -use Illuminate\Contracts\Console\Kernel; use Illuminate\Routing\Route; -use Mpociot\ApiDoc\ApiDocGeneratorServiceProvider; -use Mpociot\ApiDoc\Generators\LaravelGenerator; -use Mpociot\ApiDoc\Tests\Fixtures\DingoTestController; use Orchestra\Testbench\TestCase; +use Illuminate\Contracts\Console\Kernel; +use Dingo\Api\Provider\LaravelServiceProvider; +use Mpociot\ApiDoc\Generators\LaravelGenerator; use Mpociot\ApiDoc\Tests\Fixtures\TestController; -use Mpociot\ApiDoc\Tests\Fixtures\TestResourceController; +use Mpociot\ApiDoc\ApiDocGeneratorServiceProvider; use Illuminate\Support\Facades\Route as RouteFacade; +use Mpociot\ApiDoc\Tests\Fixtures\DingoTestController; +use Mpociot\ApiDoc\Tests\Fixtures\TestResourceController; class GenerateDocumentationTest extends TestCase { diff --git a/tests/RuleDescriptionParserTest.php b/tests/RuleDescriptionParserTest.php index 4ae9859f..1281bdaf 100644 --- a/tests/RuleDescriptionParserTest.php +++ b/tests/RuleDescriptionParserTest.php @@ -2,12 +2,12 @@ namespace Mpociot\ApiDoc\Tests; -use Illuminate\Translation\LoaderInterface; +use Mockery as m; +use Orchestra\Testbench\TestCase; use Illuminate\Translation\Translator; -use Mpociot\ApiDoc\ApiDocGeneratorServiceProvider; +use Illuminate\Translation\LoaderInterface; use Mpociot\ApiDoc\Parsers\RuleDescriptionParser; -use Orchestra\Testbench\TestCase; -use Mockery as m; +use Mpociot\ApiDoc\ApiDocGeneratorServiceProvider; class RuleDescriptionParserTest extends TestCase { From 7707cb40fd004bcf4bf4594c5773b407da501d3d Mon Sep 17 00:00:00 2001 From: Marcel Pociot Date: Thu, 8 Dec 2016 10:59:00 +0100 Subject: [PATCH 114/780] Update README.md --- README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index a64cfb2d..f4bb3593 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ ## Laravel API Documentation Generator -Automatically generate your API documentation from your existing Laravel routes. Take a look at the [example documentation](http://marcelpociot.com/whiteboard/). +Automatically generate your API documentation from your existing Laravel routes. Take a look at the [example documentation](http://marcelpociot.de/whiteboard/). `php artisan api:gen --routePrefix="settings/api/*"` @@ -82,7 +82,7 @@ This package uses the HTTP controller doc blocks to create a table of contents a public function foo() ``` -**Result:** ![Doc block result](http://marcelpociot.com/documentarian/doc_block.png) +**Result:** ![Doc block result](http://marcelpociot.de/documentarian/doc_block.png) #### Form request validation rules @@ -101,7 +101,7 @@ public function rules() } ``` -**Result:** ![Form Request](http://marcelpociot.com/documentarian/form_request.png) +**Result:** ![Form Request](http://marcelpociot.de/documentarian/form_request.png) #### API responses @@ -146,7 +146,7 @@ If you want to skip a single route from a list of routes that match a given pref ## Further modification -This package uses [Documentarian](https://github.com/mpociot/documentarian) to generate the API documentation. If you want to modify the CSS files of your documentation, or simply want to learn more about what is possible, take a look at the [Documentarian guide](http://marcelpociot.com/documentarian/installation). +This package uses [Documentarian](https://github.com/mpociot/documentarian) to generate the API documentation. If you want to modify the CSS files of your documentation, or simply want to learn more about what is possible, take a look at the [Documentarian guide](http://marcelpociot.de/documentarian/installation). ### License From 86fc9ecfaf266d8b3a5093996c8229f8ac72d4b8 Mon Sep 17 00:00:00 2001 From: Tim Head Date: Thu, 8 Dec 2016 13:45:57 +0000 Subject: [PATCH 115/780] Readme update (#138) * Readme updates * Added extended DocBlock example image. Postman base URL change explanation given * Formatting changes and small language update --- README.md | 49 +++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 43 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index f4bb3593..cf91e009 100644 --- a/README.md +++ b/README.md @@ -34,7 +34,17 @@ To generate your API documentation, use the `api:generate` artisan command. $ php artisan api:generate --routePrefix="api/v1/*" ``` -This command will scan your applications routes for the URIs matching `api/v1/*` and will parse these controller methods and form requests. +This command will scan your applications routes for the URIs matching `api/v1/*` and will parse these controller methods and form requests. For example: + +```php +// API Group Routes +Route::group(array('prefix' => 'api/v1', 'middleware' => []), function () { + // Custom route added to standard Resource + Route::get('example/foo', 'ExampleController@foo'); + // Standard Resource route + Route::resource('example', 'ExampleController')); +}); +``` ### Available command options: @@ -72,17 +82,32 @@ This package uses these resources to generate the API documentation: This package uses the HTTP controller doc blocks to create a table of contents and show descriptions for your API methods. +Using `@resource` in a doc block prior to each controller is useful as it creates a Group within the API documentation for all methods defined in that controller (rather than listing every method in a single list for all your controllers), but using `@resource` is not required. The short description after the `@resource` should be unique to allow anchor tags to navigate to this section. A longer description can be included below. + +Above each method within the controller you wish to include in your API documentation you should have a doc block. This should include a unique short description as the first entry. An optional second entry can be added with further information. Both descriptions will appear in the API documentation in a different format as shown below. + ```php /** - * This is the short description - * - * This can be an optional longer description of your API call, used within the documentation. + * @resource Example * + * Longer description */ - public function foo() +class ExampleController extends Controller { + + /** + * This is the short description [and should be unique as anchor tags link to this in navigation menu] + * + * This can be an optional longer description of your API call, used within the documentation. + * + */ + public function foo(){ + + } ``` -**Result:** ![Doc block result](http://marcelpociot.de/documentarian/doc_block.png) +**Result:** + +![Doc block result](http://headsquaredsoftware.co.uk/images/api_generator_docblock.png) #### Form request validation rules @@ -127,6 +152,18 @@ The generator automatically creates a Postman collection file, which you can imp If you don't want to create a Postman collection, use the `--noPostmanCollection` option, when generating the API documentation. +As of as of Laravel 5.3, the default base URL added to the Postman collection will be that found in your Laravel `config/app.php` file. This will likely be `http://localhost`. If you wish to change this setting you can directly update the url or link this config value to your environment file to make it more flexible (as shown below): + +```php +'url' => env('APP_URL', 'http://yourappdefault.app/'), +``` + +If you are referring to the environment setting as shown above, then you should ensure that you have updated your `.env` file to set the APP_URL value as appropriate. Otherwise the default value (`http://yourappdefault.app`) will be used in your Postman collection. Example environment value: + +``` +APP_URL=http://yourapp.app +``` + ## Modify the generated documentation If you want to modify the content of your generated documentation, go ahead and edit the generated `index.md` file. From 12abacdb9eb1900bc2646d979771b3725e1cc001 Mon Sep 17 00:00:00 2001 From: Mark Date: Tue, 10 Jan 2017 07:21:05 -0700 Subject: [PATCH 116/780] use -X METHOD for bash example (#136) * use -X METHOD for bash example * Make test pass * That white space * Update route.blade.php --- src/resources/views/partials/route.blade.php | 4 ++-- tests/Fixtures/resource_index.md | 14 +++++++------- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/resources/views/partials/route.blade.php b/src/resources/views/partials/route.blade.php index db85571a..772029e8 100644 --- a/src/resources/views/partials/route.blade.php +++ b/src/resources/views/partials/route.blade.php @@ -10,7 +10,7 @@ > Example request: ```bash -curl "{{config('app.url')}}/{{$parsedRoute['uri']}}" \ +curl -X {{$parsedRoute['methods'][0]}} "{{config('app.url')}}/{{$parsedRoute['uri']}}" \ -H "Accept: application/json"@if(count($parsedRoute['parameters'])) \ @foreach($parsedRoute['parameters'] as $attribute => $parameter) -d "{{$attribute}}"="{{$parameter['value']}}" \ @@ -65,4 +65,4 @@ @endforeach @endif - \ No newline at end of file + diff --git a/tests/Fixtures/resource_index.md b/tests/Fixtures/resource_index.md index a650fefa..b6717b26 100644 --- a/tests/Fixtures/resource_index.md +++ b/tests/Fixtures/resource_index.md @@ -27,7 +27,7 @@ Welcome to the generated API reference. > Example request: ```bash -curl "http://localhost/api/user" \ +curl -X GET "http://localhost/api/user" \ -H "Accept: application/json" ``` @@ -68,7 +68,7 @@ $.ajax(settings).done(function (response) { > Example request: ```bash -curl "http://localhost/api/user/create" \ +curl -X GET "http://localhost/api/user/create" \ -H "Accept: application/json" ``` @@ -109,7 +109,7 @@ $.ajax(settings).done(function (response) { > Example request: ```bash -curl "http://localhost/api/user" \ +curl -X POST "http://localhost/api/user" \ -H "Accept: application/json" ``` @@ -141,7 +141,7 @@ $.ajax(settings).done(function (response) { > Example request: ```bash -curl "http://localhost/api/user/%7Buser%7D" \ +curl -X GET "http://localhost/api/user/%7Buser%7D" \ -H "Accept: application/json" ``` @@ -182,7 +182,7 @@ $.ajax(settings).done(function (response) { > Example request: ```bash -curl "http://localhost/api/user/%7Buser%7D/edit" \ +curl -X GET "http://localhost/api/user/%7Buser%7D/edit" \ -H "Accept: application/json" ``` @@ -223,7 +223,7 @@ $.ajax(settings).done(function (response) { > Example request: ```bash -curl "http://localhost/api/user/%7Buser%7D" \ +curl -X PUT "http://localhost/api/user/%7Buser%7D" \ -H "Accept: application/json" ``` @@ -257,7 +257,7 @@ $.ajax(settings).done(function (response) { > Example request: ```bash -curl "http://localhost/api/user/%7Buser%7D" \ +curl -X DELETE "http://localhost/api/user/%7Buser%7D" \ -H "Accept: application/json" ``` From a4ad23af210073c7da465c2d59fd9ee776052eba Mon Sep 17 00:00:00 2001 From: Marcel Pociot Date: Sun, 29 Jan 2017 22:07:12 +0100 Subject: [PATCH 117/780] Added Laravel 5.4 compatibility --- README.md | 2 ++ composer.json | 2 +- .../ApiDoc/ApiDocGeneratorServiceProvider.php | 4 +-- .../ApiDoc/Commands/GenerateDocumentation.php | 6 ++--- .../ApiDoc/Generators/AbstractGenerator.php | 11 ++++++-- .../ApiDoc/Generators/DingoGenerator.php | 10 ++++++- .../ApiDoc/Generators/LaravelGenerator.php | 26 +++++++++++++++---- tests/DingoGeneratorTest.php | 12 +++++++++ tests/Fixtures/index.md | 6 +++-- tests/Fixtures/resource_index.md | 7 +++++ tests/GenerateDocumentationTest.php | 4 +++ 11 files changed, 74 insertions(+), 16 deletions(-) diff --git a/README.md b/README.md index cf91e009..cdfce3c3 100644 --- a/README.md +++ b/README.md @@ -26,6 +26,8 @@ Go to your `config/app.php` and add the service provider: Mpociot\ApiDoc\ApiDocGeneratorServiceProvider::class, ``` +> Using Laravel < 5.4? Use version 1.0! For Laravel 5.4 and up, use 2.0 instead. + ## Usage To generate your API documentation, use the `api:generate` artisan command. diff --git a/composer.json b/composer.json index 1921dacf..086555c6 100644 --- a/composer.json +++ b/composer.json @@ -17,7 +17,7 @@ "require": { "php": ">=5.5.0", "fzaninotto/faker": "~1.0", - "laravel/framework": "~5.0", + "laravel/framework": "~5.4", "mpociot/documentarian": "^0.2.0", "mpociot/reflection-docblock": "^1.0", "ramsey/uuid": "^3.0" diff --git a/src/Mpociot/ApiDoc/ApiDocGeneratorServiceProvider.php b/src/Mpociot/ApiDoc/ApiDocGeneratorServiceProvider.php index ddeb08ef..125f5f14 100644 --- a/src/Mpociot/ApiDoc/ApiDocGeneratorServiceProvider.php +++ b/src/Mpociot/ApiDoc/ApiDocGeneratorServiceProvider.php @@ -31,10 +31,10 @@ public function boot() */ public function register() { - $this->app['apidoc.generate'] = $this->app->share(function () { + $this->app->singleton('apidoc.generate', function () { return new GenerateDocumentation(); }); - $this->app['apidoc.update'] = $this->app->share(function () { + $this->app->singleton('apidoc.update', function () { return new UpdateDocumentation(); }); diff --git a/src/Mpociot/ApiDoc/Commands/GenerateDocumentation.php b/src/Mpociot/ApiDoc/Commands/GenerateDocumentation.php index 0fd0a775..9f7ae41b 100644 --- a/src/Mpociot/ApiDoc/Commands/GenerateDocumentation.php +++ b/src/Mpociot/ApiDoc/Commands/GenerateDocumentation.php @@ -256,12 +256,12 @@ private function processLaravelRoutes(AbstractGenerator $generator, $allowedRout $bindings = $this->getBindings(); $parsedRoutes = []; foreach ($routes as $route) { - if (in_array($route->getName(), $allowedRoutes) || str_is($routePrefix, $route->getUri()) || in_array($middleware, $route->middleware())) { + if (in_array($route->getName(), $allowedRoutes) || str_is($routePrefix, $generator->getUri($route)) || in_array($middleware, $route->middleware())) { if ($this->isValidRoute($route) && $this->isRouteVisibleForDocumentation($route->getAction()['uses'])) { $parsedRoutes[] = $generator->processRoute($route, $bindings, $this->option('header'), $withResponse); - $this->info('Processed route: ['.implode(',', $route->getMethods()).'] '.$route->getUri()); + $this->info('Processed route: ['.implode(',', $generator->getMethods($route)).'] '.$generator->getUri($route)); } else { - $this->warn('Skipping route: ['.implode(',', $route->getMethods()).'] '.$route->getUri()); + $this->warn('Skipping route: ['.implode(',', $generator->getMethods($route)).'] '.$generator->getUri($route)); } } } diff --git a/src/Mpociot/ApiDoc/Generators/AbstractGenerator.php b/src/Mpociot/ApiDoc/Generators/AbstractGenerator.php index 20798c16..5192b371 100644 --- a/src/Mpociot/ApiDoc/Generators/AbstractGenerator.php +++ b/src/Mpociot/ApiDoc/Generators/AbstractGenerator.php @@ -18,7 +18,14 @@ abstract class AbstractGenerator * * @return mixed */ - abstract protected function getUri($route); + abstract public function getUri($route); + + /** + * @param $route + * + * @return mixed + */ + abstract public function getMethods($route); /** * @param \Illuminate\Routing\Route $route @@ -76,7 +83,7 @@ protected function getRouteResponse($route, $bindings, $headers = []) { $uri = $this->addRouteModelBindings($route, $bindings); - $methods = $route->getMethods(); + $methods = $this->getMethods($route); // Split headers into key - value pairs $headers = collect($headers)->map(function ($value) { diff --git a/src/Mpociot/ApiDoc/Generators/DingoGenerator.php b/src/Mpociot/ApiDoc/Generators/DingoGenerator.php index 0b9b92f9..77252c6f 100644 --- a/src/Mpociot/ApiDoc/Generators/DingoGenerator.php +++ b/src/Mpociot/ApiDoc/Generators/DingoGenerator.php @@ -71,8 +71,16 @@ public function callRoute($method, $uri, $parameters = [], $cookies = [], $files /** * {@inheritdoc} */ - protected function getUri($route) + public function getUri($route) { return $route->uri(); } + + /** + * {@inheritdoc} + */ + public function getMethods($route) + { + return $route->getMethods(); + } } diff --git a/src/Mpociot/ApiDoc/Generators/LaravelGenerator.php b/src/Mpociot/ApiDoc/Generators/LaravelGenerator.php index cb8a8e88..ed934c0d 100644 --- a/src/Mpociot/ApiDoc/Generators/LaravelGenerator.php +++ b/src/Mpociot/ApiDoc/Generators/LaravelGenerator.php @@ -15,9 +15,25 @@ class LaravelGenerator extends AbstractGenerator * * @return mixed */ - protected function getUri($route) + public function getUri($route) { - return $route->getUri(); + if (version_compare(app()->version(), '5.4', '<')) { + return $route->getUri(); + } + return $route->uri(); + } + + /** + * @param Route $route + * + * @return mixed + */ + public function getMethods($route) + { + if (version_compare(app()->version(), '5.4', '<')) { + return $route->getMethods(); + } + return $route->methods(); } /** @@ -46,12 +62,12 @@ public function processRoute($route, $bindings = [], $headers = [], $withRespons } return $this->getParameters([ - 'id' => md5($route->getUri().':'.implode($route->getMethods())), + 'id' => md5($this->getUri($route).':'.implode($this->getMethods($route))), 'resource' => $routeGroup, 'title' => $routeDescription['short'], 'description' => $routeDescription['long'], - 'methods' => $route->getMethods(), - 'uri' => $route->getUri(), + 'methods' => $this->getMethods($route), + 'uri' => $this->getUri($route), 'parameters' => [], 'response' => $content, ], $routeAction, $bindings); diff --git a/tests/DingoGeneratorTest.php b/tests/DingoGeneratorTest.php index 3fac1394..49aa9a2e 100644 --- a/tests/DingoGeneratorTest.php +++ b/tests/DingoGeneratorTest.php @@ -37,6 +37,10 @@ public function setUp() public function testCanParseMethodDescription() { + if (version_compare($this->app->version(), '5.4', '>=')) { + $this->markTestSkipped('Dingo does not support Laravel 5.4'); + } + $api = app('Dingo\Api\Routing\Router'); $api->version('v1', function ($api) { $api->get('/api/test', TestController::class.'@parseMethodDescription'); @@ -51,6 +55,10 @@ public function testCanParseMethodDescription() public function testCanParseRouteMethods() { + if (version_compare($this->app->version(), '5.4', '>=')) { + $this->markTestSkipped('Dingo does not support Laravel 5.4'); + } + $api = app('Dingo\Api\Routing\Router'); $api->version('v1', function ($api) { $api->get('/get', TestController::class.'@dummy'); @@ -77,6 +85,10 @@ public function testCanParseRouteMethods() public function testCanParseFormRequestRules() { + if (version_compare($this->app->version(), '5.4', '>=')) { + $this->markTestSkipped('Dingo does not support Laravel 5.4'); + } + $api = app('Dingo\Api\Routing\Router'); $api->version('v1', function ($api) { $api->post('/post', DingoTestController::class.'@parseFormRequestRules'); diff --git a/tests/Fixtures/index.md b/tests/Fixtures/index.md index 6094b590..2f2effbd 100644 --- a/tests/Fixtures/index.md +++ b/tests/Fixtures/index.md @@ -30,7 +30,7 @@ It can also be multiple lines long. > Example request: ```bash -curl "http://localhost/api/test" \ +curl -X GET "http://localhost/api/test" \ -H "Accept: application/json" ``` @@ -63,13 +63,14 @@ null + ## api/fetch > Example request: ```bash -curl "http://localhost/api/fetch" \ +curl -X GET "http://localhost/api/fetch" \ -H "Accept: application/json" ``` @@ -108,3 +109,4 @@ $.ajax(settings).done(function (response) { + diff --git a/tests/Fixtures/resource_index.md b/tests/Fixtures/resource_index.md index b6717b26..c0cc4451 100644 --- a/tests/Fixtures/resource_index.md +++ b/tests/Fixtures/resource_index.md @@ -62,6 +62,7 @@ $.ajax(settings).done(function (response) { + ## Show the form for creating a new resource. @@ -103,6 +104,7 @@ $.ajax(settings).done(function (response) { + ## Store a newly created resource in storage. @@ -135,6 +137,7 @@ $.ajax(settings).done(function (response) { + ## Display the specified resource. @@ -176,6 +179,7 @@ $.ajax(settings).done(function (response) { + ## Show the form for editing the specified resource. @@ -217,6 +221,7 @@ $.ajax(settings).done(function (response) { + ## Update the specified resource in storage. @@ -251,6 +256,7 @@ $.ajax(settings).done(function (response) { + ## Remove the specified resource from storage. @@ -283,3 +289,4 @@ $.ajax(settings).done(function (response) { + diff --git a/tests/GenerateDocumentationTest.php b/tests/GenerateDocumentationTest.php index ee67ab6f..b538c0ce 100644 --- a/tests/GenerateDocumentationTest.php +++ b/tests/GenerateDocumentationTest.php @@ -70,6 +70,10 @@ public function testConsoleCommandDoesNotWorkWithClosure() public function testConsoleCommandDoesNotWorkWithClosureUsingDingo() { + if (version_compare($this->app->version(), '5.4', '>=')) { + $this->markTestSkipped('Dingo does not support Laravel 5.4'); + } + $api = app('Dingo\Api\Routing\Router'); $api->version('v1', function ($api) { $api->get('/closure', function () { From 8a8d2c28b4f477c21802c4e1c62b89864dce2794 Mon Sep 17 00:00:00 2001 From: Marcel Pociot Date: Sun, 29 Jan 2017 21:07:28 +0000 Subject: [PATCH 118/780] Apply fixes from StyleCI --- src/Mpociot/ApiDoc/Generators/LaravelGenerator.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Mpociot/ApiDoc/Generators/LaravelGenerator.php b/src/Mpociot/ApiDoc/Generators/LaravelGenerator.php index ed934c0d..0c89a37d 100644 --- a/src/Mpociot/ApiDoc/Generators/LaravelGenerator.php +++ b/src/Mpociot/ApiDoc/Generators/LaravelGenerator.php @@ -20,6 +20,7 @@ public function getUri($route) if (version_compare(app()->version(), '5.4', '<')) { return $route->getUri(); } + return $route->uri(); } @@ -33,6 +34,7 @@ public function getMethods($route) if (version_compare(app()->version(), '5.4', '<')) { return $route->getMethods(); } + return $route->methods(); } From d4ca5fce3f3c87e1ae5be9aff45d1eaff017c787 Mon Sep 17 00:00:00 2001 From: Marcel Pociot Date: Tue, 14 Mar 2017 07:29:32 +0100 Subject: [PATCH 119/780] Remove PHP 5.5 --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index b39a1321..dd082e4c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,9 +1,9 @@ language: php php: - - 5.5 - 5.6 - 7.0 + - 7.1 before_script: - travis_retry composer self-update From b764d37952764784cba5a03f1b7df8a74eb0b8f9 Mon Sep 17 00:00:00 2001 From: travoltron Date: Tue, 14 Mar 2017 02:33:09 -0400 Subject: [PATCH 120/780] Update AbstractGenerator.php (#157) The built in faker->randomNumber freaks out on `digits:10`, commonly seen on phone number validation. This is directionally where I'd send it, but maybe not the cleanest execution. But the basic idea is that if the length of digits being randomized is less than 9, proceed as previous, greater than that, use mt_rand() from 1,000,000,000 up to mt_randmax(), which incidentally, is still a 10 digit number... and then take the substr starting at pos{0}, and ending at the desired digit length. Maybe a specific regex is in order? --- src/Mpociot/ApiDoc/Generators/AbstractGenerator.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Mpociot/ApiDoc/Generators/AbstractGenerator.php b/src/Mpociot/ApiDoc/Generators/AbstractGenerator.php index 5192b371..8b07e9ce 100644 --- a/src/Mpociot/ApiDoc/Generators/AbstractGenerator.php +++ b/src/Mpociot/ApiDoc/Generators/AbstractGenerator.php @@ -308,7 +308,7 @@ protected function parseRule($rule, $ruleName, &$attributeData, $seed) case 'digits': $attributeData['type'] = 'numeric'; $attributeData['description'][] = Description::parse($rule)->with($parameters)->getDescription(); - $attributeData['value'] = $faker->randomNumber($parameters[0], true); + $attributeData['value'] = ($parameters[0] < 9) ? $faker->randomNumber($parameters[0], true) : substr(mt_rand(100000000, mt_getrandmax()), 0, $parameters[0]); break; case 'digits_between': $attributeData['type'] = 'numeric'; From 3a0393799d3956e62ac38466987ad1bdfd6d5f2b Mon Sep 17 00:00:00 2001 From: justcodingnobb <815619921@qq.com> Date: Sat, 25 Mar 2017 23:40:41 +0800 Subject: [PATCH 121/780] Fix JWT authenticate and make view bug (#165) * Update DingoGenerator.php * Update GenerateDocumentation.php --- src/Mpociot/ApiDoc/Commands/GenerateDocumentation.php | 2 +- src/Mpociot/ApiDoc/Generators/DingoGenerator.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Mpociot/ApiDoc/Commands/GenerateDocumentation.php b/src/Mpociot/ApiDoc/Commands/GenerateDocumentation.php index 9f7ae41b..f430fc7d 100644 --- a/src/Mpociot/ApiDoc/Commands/GenerateDocumentation.php +++ b/src/Mpociot/ApiDoc/Commands/GenerateDocumentation.php @@ -108,7 +108,7 @@ private function writeMarkdown($parsedRoutes) $parsedRouteOutput = $parsedRoutes->map(function ($routeGroup) { return $routeGroup->map(function ($route) { - $route['output'] = (string) view('apidoc::partials.route')->with('parsedRoute', $route); + $route['output'] = (string) view('apidoc::partials.route')->with('parsedRoute', $route)->render(); return $route; }); diff --git a/src/Mpociot/ApiDoc/Generators/DingoGenerator.php b/src/Mpociot/ApiDoc/Generators/DingoGenerator.php index 77252c6f..d85ac8c4 100644 --- a/src/Mpociot/ApiDoc/Generators/DingoGenerator.php +++ b/src/Mpociot/ApiDoc/Generators/DingoGenerator.php @@ -62,7 +62,7 @@ public function callRoute($method, $uri, $parameters = [], $cookies = [], $files $dispatcher = app('Dingo\Api\Dispatcher')->raw(); collect($server)->map(function ($key, $value) use ($dispatcher) { - $dispatcher->header($key, $value); + $dispatcher->header($value, $key); }); return call_user_func_array([$dispatcher, strtolower($method)], [$uri]); From 585669e8d0ee41a356ef37741b835eaacebc3ff9 Mon Sep 17 00:00:00 2001 From: Rudie Dirkx Date: Fri, 28 Apr 2017 11:55:14 +0200 Subject: [PATCH 122/780] #176 don't document HEAD --- src/Mpociot/ApiDoc/Generators/LaravelGenerator.php | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/Mpociot/ApiDoc/Generators/LaravelGenerator.php b/src/Mpociot/ApiDoc/Generators/LaravelGenerator.php index 0c89a37d..1f59b43e 100644 --- a/src/Mpociot/ApiDoc/Generators/LaravelGenerator.php +++ b/src/Mpociot/ApiDoc/Generators/LaravelGenerator.php @@ -32,10 +32,13 @@ public function getUri($route) public function getMethods($route) { if (version_compare(app()->version(), '5.4', '<')) { - return $route->getMethods(); + $methods = $route->getMethods(); + } + else { + $methods = $route->methods(); } - return $route->methods(); + return array_diff($methods, ['HEAD']); } /** From 8c82799a65a8b7c57f05dd6d7f140191c08a339c Mon Sep 17 00:00:00 2001 From: Rudie Dirkx Date: Fri, 28 Apr 2017 12:03:51 +0200 Subject: [PATCH 123/780] #176 don't document HEAD II --- src/Mpociot/ApiDoc/Generators/DingoGenerator.php | 2 +- src/Mpociot/ApiDoc/Generators/LaravelGenerator.php | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/Mpociot/ApiDoc/Generators/DingoGenerator.php b/src/Mpociot/ApiDoc/Generators/DingoGenerator.php index d85ac8c4..933e639a 100644 --- a/src/Mpociot/ApiDoc/Generators/DingoGenerator.php +++ b/src/Mpociot/ApiDoc/Generators/DingoGenerator.php @@ -81,6 +81,6 @@ public function getUri($route) */ public function getMethods($route) { - return $route->getMethods(); + return array_diff($route->getMethods(), ['HEAD']); } } diff --git a/src/Mpociot/ApiDoc/Generators/LaravelGenerator.php b/src/Mpociot/ApiDoc/Generators/LaravelGenerator.php index 1f59b43e..b2c98dee 100644 --- a/src/Mpociot/ApiDoc/Generators/LaravelGenerator.php +++ b/src/Mpociot/ApiDoc/Generators/LaravelGenerator.php @@ -33,8 +33,7 @@ public function getMethods($route) { if (version_compare(app()->version(), '5.4', '<')) { $methods = $route->getMethods(); - } - else { + } else { $methods = $route->methods(); } From 77cd740135bb6d7c638f2770949f2c1d33c2ea5f Mon Sep 17 00:00:00 2001 From: Rudie Dirkx Date: Mon, 15 May 2017 07:07:48 +0200 Subject: [PATCH 124/780] #177 custom app URL for docs (#181) --- src/resources/views/partials/route.blade.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/resources/views/partials/route.blade.php b/src/resources/views/partials/route.blade.php index 772029e8..0102cc63 100644 --- a/src/resources/views/partials/route.blade.php +++ b/src/resources/views/partials/route.blade.php @@ -10,7 +10,7 @@ > Example request: ```bash -curl -X {{$parsedRoute['methods'][0]}} "{{config('app.url')}}/{{$parsedRoute['uri']}}" \ +curl -X {{$parsedRoute['methods'][0]}} "{{config('app.docs_url') ?: config('app.url')}}/{{$parsedRoute['uri']}}" \ -H "Accept: application/json"@if(count($parsedRoute['parameters'])) \ @foreach($parsedRoute['parameters'] as $attribute => $parameter) -d "{{$attribute}}"="{{$parameter['value']}}" \ @@ -23,7 +23,7 @@ var settings = { "async": true, "crossDomain": true, - "url": "{{config('app.url')}}/{{$parsedRoute['uri']}}", + "url": "{{config('app.docs_url') ?: config('app.url')}}/{{$parsedRoute['uri']}}", "method": "{{$parsedRoute['methods'][0]}}", @if(count($parsedRoute['parameters'])) "data": {!! str_replace(' ',' ',json_encode(array_combine(array_keys($parsedRoute['parameters']), array_map(function($param){ return $param['value']; },$parsedRoute['parameters'])), JSON_PRETTY_PRINT)) !!}, From db46be8edc830252ac4ad12f4e20c9289a4cd770 Mon Sep 17 00:00:00 2001 From: Rudie Dirkx Date: Mon, 15 May 2017 07:08:00 +0200 Subject: [PATCH 125/780] #178 URI bugfix in params in getRouteResponse() (#179) --- src/Mpociot/ApiDoc/Generators/AbstractGenerator.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Mpociot/ApiDoc/Generators/AbstractGenerator.php b/src/Mpociot/ApiDoc/Generators/AbstractGenerator.php index 8b07e9ce..4c649100 100644 --- a/src/Mpociot/ApiDoc/Generators/AbstractGenerator.php +++ b/src/Mpociot/ApiDoc/Generators/AbstractGenerator.php @@ -93,7 +93,7 @@ protected function getRouteResponse($route, $bindings, $headers = []) })->collapse()->toArray(); //Changes url with parameters like /users/{user} to /users/1 - $uri = preg_replace('/{(.*)}/', 1, $uri); + $uri = preg_replace('/{(.*?)}/', 1, $uri); return $this->callRoute(array_shift($methods), $uri, [], [], [], $headers); } From 3fd150e2304bde88d2907063399daed4c9c44e4f Mon Sep 17 00:00:00 2001 From: Rudie Dirkx Date: Thu, 25 May 2017 23:07:21 +0200 Subject: [PATCH 126/780] #176 don't document HEAD III - tests --- tests/ApiDocGeneratorTest.php | 2 +- tests/DingoGeneratorTest.php | 2 +- tests/Fixtures/index.md | 4 ---- tests/Fixtures/resource_index.md | 8 -------- tests/GenerateDocumentationTest.php | 12 ++++++------ 5 files changed, 8 insertions(+), 20 deletions(-) diff --git a/tests/ApiDocGeneratorTest.php b/tests/ApiDocGeneratorTest.php index 38cf7b54..f9c1aead 100644 --- a/tests/ApiDocGeneratorTest.php +++ b/tests/ApiDocGeneratorTest.php @@ -53,7 +53,7 @@ public function testCanParseRouteMethods() $route = new Route(['GET'], '/get', ['uses' => TestController::class.'@parseMethodDescription']); $parsed = $this->generator->processRoute($route); - $this->assertSame(['GET', 'HEAD'], $parsed['methods']); + $this->assertSame(['GET'], $parsed['methods']); $route = new Route(['POST'], '/post', ['uses' => TestController::class.'@parseMethodDescription']); $parsed = $this->generator->processRoute($route); diff --git a/tests/DingoGeneratorTest.php b/tests/DingoGeneratorTest.php index 49aa9a2e..2311498d 100644 --- a/tests/DingoGeneratorTest.php +++ b/tests/DingoGeneratorTest.php @@ -68,7 +68,7 @@ public function testCanParseRouteMethods() }); $route = app('Dingo\Api\Routing\Router')->getRoutes()['v1']->getRoutes()[0]; $parsed = $this->generator->processRoute($route); - $this->assertSame(['GET', 'HEAD'], $parsed['methods']); + $this->assertSame(['GET'], $parsed['methods']); $route = app('Dingo\Api\Routing\Router')->getRoutes()['v1']->getRoutes()[1]; $parsed = $this->generator->processRoute($route); diff --git a/tests/Fixtures/index.md b/tests/Fixtures/index.md index 2f2effbd..4ef3bc0d 100644 --- a/tests/Fixtures/index.md +++ b/tests/Fixtures/index.md @@ -59,8 +59,6 @@ null ### HTTP Request `GET api/test` -`HEAD api/test` - @@ -105,8 +103,6 @@ $.ajax(settings).done(function (response) { ### HTTP Request `GET api/fetch` -`HEAD api/fetch` - diff --git a/tests/Fixtures/resource_index.md b/tests/Fixtures/resource_index.md index c0cc4451..08388f88 100644 --- a/tests/Fixtures/resource_index.md +++ b/tests/Fixtures/resource_index.md @@ -58,8 +58,6 @@ $.ajax(settings).done(function (response) { ### HTTP Request `GET api/user` -`HEAD api/user` - @@ -100,8 +98,6 @@ $.ajax(settings).done(function (response) { ### HTTP Request `GET api/user/create` -`HEAD api/user/create` - @@ -175,8 +171,6 @@ $.ajax(settings).done(function (response) { ### HTTP Request `GET api/user/{user}` -`HEAD api/user/{user}` - @@ -217,8 +211,6 @@ $.ajax(settings).done(function (response) { ### HTTP Request `GET api/user/{user}/edit` -`HEAD api/user/{user}/edit` - diff --git a/tests/GenerateDocumentationTest.php b/tests/GenerateDocumentationTest.php index b538c0ce..41cf492e 100644 --- a/tests/GenerateDocumentationTest.php +++ b/tests/GenerateDocumentationTest.php @@ -64,8 +64,8 @@ public function testConsoleCommandDoesNotWorkWithClosure() $output = $this->artisan('api:generate', [ '--routePrefix' => 'api/*', ]); - $this->assertContains('Skipping route: [GET,HEAD] api/closure', $output); - $this->assertContains('Processed route: [GET,HEAD] api/test', $output); + $this->assertContains('Skipping route: [GET] api/closure', $output); + $this->assertContains('Processed route: [GET] api/test', $output); } public function testConsoleCommandDoesNotWorkWithClosureUsingDingo() @@ -85,8 +85,8 @@ public function testConsoleCommandDoesNotWorkWithClosureUsingDingo() '--router' => 'dingo', '--routePrefix' => 'v1', ]); - $this->assertContains('Skipping route: [GET,HEAD] closure', $output); - $this->assertContains('Processed route: [GET,HEAD] test', $output); + $this->assertContains('Skipping route: [GET] closure', $output); + $this->assertContains('Processed route: [GET] test', $output); }); } @@ -98,8 +98,8 @@ public function testCanSkipSingleRoutesCommandDoesNotWorkWithClosure() $output = $this->artisan('api:generate', [ '--routePrefix' => 'api/*', ]); - $this->assertContains('Skipping route: [GET,HEAD] api/skip', $output); - $this->assertContains('Processed route: [GET,HEAD] api/test', $output); + $this->assertContains('Skipping route: [GET] api/skip', $output); + $this->assertContains('Processed route: [GET] api/test', $output); } public function testCanParseResourceRoutes() From ed92f93d01c406d672806599a404887ceb87dd5d Mon Sep 17 00:00:00 2001 From: Rudie Dirkx Date: Thu, 25 May 2017 23:18:23 +0200 Subject: [PATCH 127/780] #176 don't document HEAD Iv - tests --- tests/Fixtures/index.md | 8 ++++---- tests/Fixtures/resource_index.md | 16 ++++++++-------- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/tests/Fixtures/index.md b/tests/Fixtures/index.md index 4ef3bc0d..f34641d4 100644 --- a/tests/Fixtures/index.md +++ b/tests/Fixtures/index.md @@ -21,7 +21,7 @@ Welcome to the generated API reference. #general - + ## Example title. This will be the long description. @@ -60,9 +60,9 @@ null `GET api/test` - + - + ## api/fetch > Example request: @@ -104,5 +104,5 @@ $.ajax(settings).done(function (response) { `GET api/fetch` - + diff --git a/tests/Fixtures/resource_index.md b/tests/Fixtures/resource_index.md index 08388f88..027c3b6a 100644 --- a/tests/Fixtures/resource_index.md +++ b/tests/Fixtures/resource_index.md @@ -21,7 +21,7 @@ Welcome to the generated API reference. #general - + ## Display a listing of the resource. > Example request: @@ -59,9 +59,9 @@ $.ajax(settings).done(function (response) { `GET api/user` - + - + ## Show the form for creating a new resource. > Example request: @@ -99,7 +99,7 @@ $.ajax(settings).done(function (response) { `GET api/user/create` - + ## Store a newly created resource in storage. @@ -134,7 +134,7 @@ $.ajax(settings).done(function (response) { - + ## Display the specified resource. > Example request: @@ -172,9 +172,9 @@ $.ajax(settings).done(function (response) { `GET api/user/{user}` - + - + ## Show the form for editing the specified resource. > Example request: @@ -212,7 +212,7 @@ $.ajax(settings).done(function (response) { `GET api/user/{user}/edit` - + ## Update the specified resource in storage. From 6b1c5027582392fd418704375f629d403e4bb0bf Mon Sep 17 00:00:00 2001 From: Tobias van Beek Date: Mon, 29 May 2017 17:14:38 +0200 Subject: [PATCH 128/780] Adddocblockhandling (#192) * Add the handling for a @transformer @transformercollection or @result tag in the method docblock * Update the readme to document the @transform, @transformercollection and @response tag * Fix the styleci issues * Apply patch form styleci * Add a transformermodel tag and support PHP 5 * Fix the version without a modeltag * Always use the transformermodel tag and a codestyle issue --- README.md | 52 ++++++++ .../ApiDoc/Generators/AbstractGenerator.php | 26 ++++ .../ApiDoc/Generators/LaravelGenerator.php | 118 +++++++++++++++++- src/resources/views/partials/route.blade.php | 2 +- tests/ApiDocGeneratorTest.php | 75 +++++++++++ tests/Fixtures/TestController.php | 44 +++++++ tests/Fixtures/TestModel.php | 17 +++ tests/Fixtures/TestTransformer.php | 27 ++++ 8 files changed, 359 insertions(+), 2 deletions(-) create mode 100644 tests/Fixtures/TestModel.php create mode 100644 tests/Fixtures/TestTransformer.php diff --git a/README.md b/README.md index cdfce3c3..098fa35e 100644 --- a/README.md +++ b/README.md @@ -130,6 +130,58 @@ public function rules() **Result:** ![Form Request](http://marcelpociot.de/documentarian/form_request.png) +#### Controller method doc block +It is possible to override the results for the response. This will also show the responses for other request methods then GET. + +#### @transformer +With the transformer you can define the transformer that is used for the result of the method. It will try the next parts to get a result if it can find the transformer. The first successfull will be used. + +1. Check if there is a transformermodel tag to define the model +2. Get a model from the modelfactory +2. If the parameter is a Eloquent model it will load the first from the database. +3. A new instance from the class + +```php +/** + * @transformer \Mpociot\ApiDoc\Tests\Fixtures\TestTransformer + */ +public function transformerTag() +{ + return ''; +} +``` + +#### @transformercollection +This is the same idea as the @tranformer tag with one different, instead of the return of an item, it will generate the return of a set with two items + +```php +/** + * @transformercollection \Mpociot\ApiDoc\Tests\Fixtures\TestTransformer + */ +public function transformerCollectionTag() +{ + return ''; +} +``` + +#### @transformermodel +The @transformermodel tag is needed for PHP 5.* to get the model. For PHP 7 is it optional to specify the model that is used for the transformer. + +#### @response +If you expliciet want to specify the result of a function you can set it in the docblock + +```php +/** + * @response { + * data: [], + *} + */ +public function responseTag() +{ + return ''; +} +``` + #### API responses If your API route accepts a `GET` method, this package tries to call the API route with all middleware disabled to fetch an example API response. diff --git a/src/Mpociot/ApiDoc/Generators/AbstractGenerator.php b/src/Mpociot/ApiDoc/Generators/AbstractGenerator.php index 4c649100..b4efdcbc 100644 --- a/src/Mpociot/ApiDoc/Generators/AbstractGenerator.php +++ b/src/Mpociot/ApiDoc/Generators/AbstractGenerator.php @@ -7,6 +7,7 @@ use Illuminate\Support\Arr; use Illuminate\Support\Str; use Mpociot\Reflection\DocBlock; +use Mpociot\Reflection\DocBlock\Tag; use Illuminate\Support\Facades\Validator; use Illuminate\Foundation\Http\FormRequest; use Mpociot\ApiDoc\Parsers\RuleDescriptionParser as Description; @@ -45,6 +46,30 @@ abstract public function processRoute($route, $bindings = [], $withResponse = tr */ abstract public function prepareMiddleware($disable = false); + /** + * Get the response from the docblock if available. + * + * @param array $tags + * + * @return mixed + */ + protected function getDocblockResponse($tags) + { + $responseTags = array_filter($tags, function ($tag) { + if (! ($tag instanceof Tag)) { + return false; + } + + return \strtolower($tag->getName()) == 'response'; + }); + if (empty($responseTags)) { + return; + } + $responseTag = \array_first($responseTags); + + return \response(\json_encode($responseTag->getContent())); + } + /** * @param array $routeData * @param array $routeAction @@ -131,6 +156,7 @@ protected function getRouteDescription($route) return [ 'short' => $phpdoc->getShortDescription(), 'long' => $phpdoc->getLongDescription()->getContents(), + 'tags' => $phpdoc->getTags(), ]; } diff --git a/src/Mpociot/ApiDoc/Generators/LaravelGenerator.php b/src/Mpociot/ApiDoc/Generators/LaravelGenerator.php index 0c89a37d..f86e6f2c 100644 --- a/src/Mpociot/ApiDoc/Generators/LaravelGenerator.php +++ b/src/Mpociot/ApiDoc/Generators/LaravelGenerator.php @@ -3,9 +3,13 @@ namespace Mpociot\ApiDoc\Generators; use ReflectionClass; +use League\Fractal\Manager; use Illuminate\Routing\Route; +use League\Fractal\Resource\Item; use Illuminate\Support\Facades\App; +use Mpociot\Reflection\DocBlock\Tag; use Illuminate\Support\Facades\Request; +use League\Fractal\Resource\Collection; use Illuminate\Foundation\Http\FormRequest; class LaravelGenerator extends AbstractGenerator @@ -53,9 +57,27 @@ public function processRoute($route, $bindings = [], $headers = [], $withRespons $routeAction = $route->getAction(); $routeGroup = $this->getRouteGroup($routeAction['uses']); $routeDescription = $this->getRouteDescription($routeAction['uses']); + $showresponse = null; if ($withResponse) { - $response = $this->getRouteResponse($route, $bindings, $headers); + $response = null; + $docblockResponse = $this->getDocblockResponse($routeDescription['tags']); + if ($docblockResponse) { + // we have a response from the docblock ( @response ) + $response = $docblockResponse; + $showresponse = true; + } + if (! $response) { + $transformerResponse = $this->getTransformerResponse($routeDescription['tags']); + if ($transformerResponse) { + // we have a transformer response from the docblock ( @transformer || @transformercollection ) + $response = $transformerResponse; + $showresponse = true; + } + } + if (! $response) { + $response = $this->getRouteResponse($route, $bindings, $headers); + } if ($response->headers->get('Content-Type') === 'application/json') { $content = json_encode(json_decode($response->getContent()), JSON_PRETTY_PRINT); } else { @@ -72,6 +94,7 @@ public function processRoute($route, $bindings = [], $headers = [], $withRespons 'uri' => $this->getUri($route), 'parameters' => [], 'response' => $content, + 'showresponse' => $showresponse, ], $routeAction, $bindings); } @@ -125,6 +148,99 @@ public function callRoute($method, $uri, $parameters = [], $cookies = [], $files return $response; } + /** + * Get a response from the transformer tags. + * + * @param array $tags + * + * @return mixed + */ + protected function getTransformerResponse($tags) + { + try { + $transFormerTags = array_filter($tags, function ($tag) { + if (! ($tag instanceof Tag)) { + return false; + } + + return \in_array(\strtolower($tag->getName()), ['transformer', 'transformercollection']); + }); + if (empty($transFormerTags)) { + // we didn't have any of the tags so goodbye + return false; + } + + $modelTag = array_first(array_filter($tags, function ($tag) { + if (! ($tag instanceof Tag)) { + return false; + } + + return \in_array(\strtolower($tag->getName()), ['transformermodel']); + })); + $tag = \array_first($transFormerTags); + $transformer = $tag->getContent(); + if (! \class_exists($transformer)) { + // if we can't find the transformer we can't generate a response + return; + } + $demoData = []; + + $reflection = new ReflectionClass($transformer); + $method = $reflection->getMethod('transform'); + $parameter = \array_first($method->getParameters()); + $type = null; + if ($modelTag) { + $type = $modelTag->getContent(); + } + if (version_compare(PHP_VERSION, '7.0.0') >= 0 && \is_null($type)) { + // we can only get the type with reflection for PHP 7 + if ($parameter->hasType() && + ! $parameter->getType()->isBuiltin() && + \class_exists((string) $parameter->getType())) { + //we have a type + $type = (string) $parameter->getType(); + } + } + if ($type) { + // we have a class so we try to create an instance + $demoData = new $type; + try { + // try a factory + $demoData = \factory($type)->make(); + } catch (\Exception $e) { + if ($demoData instanceof \Illuminate\Database\Eloquent\Model) { + // we can't use a factory but can try to get one from the database + try { + // check if we can find one + $newDemoData = $type::first(); + if ($newDemoData) { + $demoData = $newDemoData; + } + } catch (\Exception $e) { + // do nothing + } + } + } + } + + $fractal = new Manager(); + $resource = []; + if ($tag->getName() == 'transformer') { + // just one + $resource = new Item($demoData, new $transformer); + } + if ($tag->getName() == 'transformercollection') { + // a collection + $resource = new Collection([$demoData, $demoData], new $transformer); + } + + return \response($fractal->createData($resource)->toJson()); + } catch (\Exception $e) { + // it isn't possible to parse the transformer + return; + } + } + /** * @param string $route * @param array $bindings diff --git a/src/resources/views/partials/route.blade.php b/src/resources/views/partials/route.blade.php index 0102cc63..f285dc4a 100644 --- a/src/resources/views/partials/route.blade.php +++ b/src/resources/views/partials/route.blade.php @@ -38,7 +38,7 @@ }); ``` -@if(in_array('GET',$parsedRoute['methods'])) +@if(in_array('GET',$parsedRoute['methods']) || isset($parsedRoute['showresponse']) && $parsedRoute['showresponse']) > Example response: ```json diff --git a/tests/ApiDocGeneratorTest.php b/tests/ApiDocGeneratorTest.php index 38cf7b54..e9009399 100644 --- a/tests/ApiDocGeneratorTest.php +++ b/tests/ApiDocGeneratorTest.php @@ -336,4 +336,79 @@ public function testCanParseFormRequestRules() } } } + + public function testCanParseResponseTag() + { + RouteFacade::post('/responseTag', TestController::class.'@responseTag'); + $route = new Route(['GET'], '/responseTag', ['uses' => TestController::class.'@responseTag']); + $parsed = $this->generator->processRoute($route); + $this->assertTrue(is_array($parsed)); + $this->assertArrayHasKey('showresponse', $parsed); + $this->assertTrue($parsed['showresponse']); + $this->assertSame($parsed['response'], '"{\n data: [],\n}"'); + } + + public function testCanParseTransformerTag() + { + if (version_compare(PHP_VERSION, '7.0.0', '<')) { + $this->markTestSkipped('The transformer tag without model need PHP 7'); + } + RouteFacade::post('/transformerTag', TestController::class.'@transformerTag'); + $route = new Route(['GET'], '/transformerTag', ['uses' => TestController::class.'@transformerTag']); + $parsed = $this->generator->processRoute($route); + $this->assertTrue(is_array($parsed)); + $this->assertArrayHasKey('showresponse', $parsed); + $this->assertTrue($parsed['showresponse']); + $this->assertSame( + $parsed['response'], + '{"data":{"id":1,"description":"Welcome on this test versions","name":"TestName"}}' + ); + } + + public function testCanParseTransformerTagWithModel() + { + RouteFacade::post('/transformerTagWithModel', TestController::class.'@transformerTagWithModel'); + $route = new Route(['GET'], '/transformerTagWithModel', ['uses' => TestController::class.'@transformerTagWithModel']); + $parsed = $this->generator->processRoute($route); + $this->assertTrue(is_array($parsed)); + $this->assertArrayHasKey('showresponse', $parsed); + $this->assertTrue($parsed['showresponse']); + $this->assertSame( + $parsed['response'], + '{"data":{"id":1,"description":"Welcome on this test versions","name":"TestName"}}' + ); + } + + public function testCanParseTransformerCollectionTag() + { + if (version_compare(PHP_VERSION, '7.0.0', '<')) { + $this->markTestSkipped('The transformer tag without model need PHP 7'); + } + RouteFacade::post('/transformerCollectionTag', TestController::class.'@transformerCollectionTag'); + $route = new Route(['GET'], '/transformerCollectionTag', ['uses' => TestController::class.'@transformerCollectionTag']); + $parsed = $this->generator->processRoute($route); + $this->assertTrue(is_array($parsed)); + $this->assertArrayHasKey('showresponse', $parsed); + $this->assertTrue($parsed['showresponse']); + $this->assertSame( + $parsed['response'], + '{"data":[{"id":1,"description":"Welcome on this test versions","name":"TestName"},'. + '{"id":1,"description":"Welcome on this test versions","name":"TestName"}]}' + ); + } + + public function testCanParseTransformerCollectionTagWithModel() + { + RouteFacade::post('/transformerCollectionTagWithModel', TestController::class.'@transformerCollectionTagWithModel'); + $route = new Route(['GET'], '/transformerCollectionTagWithModel', ['uses' => TestController::class.'@transformerCollectionTagWithModel']); + $parsed = $this->generator->processRoute($route); + $this->assertTrue(is_array($parsed)); + $this->assertArrayHasKey('showresponse', $parsed); + $this->assertTrue($parsed['showresponse']); + $this->assertSame( + $parsed['response'], + '{"data":[{"id":1,"description":"Welcome on this test versions","name":"TestName"},'. + '{"id":1,"description":"Welcome on this test versions","name":"TestName"}]}' + ); + } } diff --git a/tests/Fixtures/TestController.php b/tests/Fixtures/TestController.php index 521d2394..1f3be0d6 100644 --- a/tests/Fixtures/TestController.php +++ b/tests/Fixtures/TestController.php @@ -71,4 +71,48 @@ public function utf8() public function skip() { } + + /** + * @response { + * data: [], + *} + */ + public function responseTag() + { + return ''; + } + + /** + * @transformer \Mpociot\ApiDoc\Tests\Fixtures\TestTransformer + */ + public function transformerTag() + { + return ''; + } + + /** + * @transformer \Mpociot\ApiDoc\Tests\Fixtures\TestTransformer + * @transformermodel \Mpociot\ApiDoc\Tests\Fixtures\TestModel + */ + public function transformerTagWithModel() + { + return ''; + } + + /** + * @transformercollection \Mpociot\ApiDoc\Tests\Fixtures\TestTransformer + */ + public function transformerCollectionTag() + { + return ''; + } + + /** + * @transformercollection \Mpociot\ApiDoc\Tests\Fixtures\TestTransformer + * @transformermodel \Mpociot\ApiDoc\Tests\Fixtures\TestModel + */ + public function transformerCollectionTagWithModel() + { + return ''; + } } diff --git a/tests/Fixtures/TestModel.php b/tests/Fixtures/TestModel.php new file mode 100644 index 00000000..0d71e046 --- /dev/null +++ b/tests/Fixtures/TestModel.php @@ -0,0 +1,17 @@ + + */ +class TestModel +{ + public $id = 1; + + public $name = 'TestName'; + + public $description = 'Welcome on this test versions'; +} diff --git a/tests/Fixtures/TestTransformer.php b/tests/Fixtures/TestTransformer.php new file mode 100644 index 00000000..11db4bd2 --- /dev/null +++ b/tests/Fixtures/TestTransformer.php @@ -0,0 +1,27 @@ + + */ +class TestTransformer extends TransformerAbstract +{ + /** + * A Fractal transformer. + * + * @return array + */ + public function transform(TestModel $model) + { + return [ + 'id' => $model->id, + 'description' => $model->description, + 'name' => $model->name, + ]; + } +} From 28ff33be15424ff5a5b819dbe88b34468d1752b2 Mon Sep 17 00:00:00 2001 From: Manash Jyoti Sonowal Date: Fri, 16 Jun 2017 23:18:28 +0530 Subject: [PATCH 129/780] added support for multiple prefixes for routes which can be passed as separated by comma --- .../ApiDoc/Commands/GenerateDocumentation.php | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/Mpociot/ApiDoc/Commands/GenerateDocumentation.php b/src/Mpociot/ApiDoc/Commands/GenerateDocumentation.php index f430fc7d..ece88884 100644 --- a/src/Mpociot/ApiDoc/Commands/GenerateDocumentation.php +++ b/src/Mpociot/ApiDoc/Commands/GenerateDocumentation.php @@ -79,10 +79,18 @@ public function handle() $generator->prepareMiddleware($this->option('useMiddlewares')); + $routePrefixes = explode(",", $routePrefix); + + $parsedRoutes = []; + if ($this->option('router') === 'laravel') { - $parsedRoutes = $this->processLaravelRoutes($generator, $allowedRoutes, $routePrefix, $middleware); + foreach ($routePrefixes as $routePrefix) { + $parsedRoutes += $this->processLaravelRoutes($generator, $allowedRoutes, $routePrefix, $middleware); + } } else { - $parsedRoutes = $this->processDingoRoutes($generator, $allowedRoutes, $routePrefix, $middleware); + foreach ($routePrefixes as $routePrefix) { + $parsedRoutes += $this->processDingoRoutes($generator, $allowedRoutes, $routePrefix, $middleware); + } } $parsedRoutes = collect($parsedRoutes)->groupBy('resource')->sort(function ($a, $b) { return strcmp($a->first()['resource'], $b->first()['resource']); From d06292ac3b654f80965df141de1fbef0deec6f26 Mon Sep 17 00:00:00 2001 From: Manash Jyoti Sonowal Date: Fri, 16 Jun 2017 23:37:48 +0530 Subject: [PATCH 130/780] updated the readme with last commit 28ff33be15424ff5a5b819dbe88b34468d1752b2 which adds support for generation when there are multiple prefixes --- README.md | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 098fa35e..ede1f974 100644 --- a/README.md +++ b/README.md @@ -36,6 +36,14 @@ To generate your API documentation, use the `api:generate` artisan command. $ php artisan api:generate --routePrefix="api/v1/*" ``` +After 28ff33be15424ff5a5b819dbe88b34468d1752b2 this commit, it supports passing generation of multiple prefixes by spearating each prefix with comma. + +```sh +$ php artisan api:generate --routePrefix="api/v1/*,api/public/*" +``` +It will generate documentation for all of the routes whose prefixes are `api/v1/` and `api/public/` + + This command will scan your applications routes for the URIs matching `api/v1/*` and will parse these controller methods and form requests. For example: ```php @@ -53,7 +61,7 @@ Route::group(array('prefix' => 'api/v1', 'middleware' => []), function () { Option | Description --------- | ------- `output` | The output path used for the generated documentation. Default: `public/docs` -`routePrefix` | The route prefix to use for generation - `*` can be used as a wildcard +`routePrefix` | The route prefix to use for generation - `*` can be used as a wildcard `routes` | The route names to use for generation - Required if no routePrefix is provided `middleware` | The middlewares to use for generation `noResponseCalls` | Disable API response calls From 08a330c80a287d59ac199f566696db9991b9c11b Mon Sep 17 00:00:00 2001 From: Manash Jyoti Sonowal Date: Fri, 16 Jun 2017 23:42:12 +0530 Subject: [PATCH 131/780] apply patch from StyleCI --- src/Mpociot/ApiDoc/Commands/GenerateDocumentation.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Mpociot/ApiDoc/Commands/GenerateDocumentation.php b/src/Mpociot/ApiDoc/Commands/GenerateDocumentation.php index ece88884..6c97a204 100644 --- a/src/Mpociot/ApiDoc/Commands/GenerateDocumentation.php +++ b/src/Mpociot/ApiDoc/Commands/GenerateDocumentation.php @@ -81,7 +81,7 @@ public function handle() $routePrefixes = explode(",", $routePrefix); - $parsedRoutes = []; + $parsedRoutes = []; if ($this->option('router') === 'laravel') { foreach ($routePrefixes as $routePrefix) { From 03ec9e4c36e625f8ee495d38f5f8252933f24529 Mon Sep 17 00:00:00 2001 From: Manash Jyoti Sonowal Date: Fri, 16 Jun 2017 23:43:15 +0530 Subject: [PATCH 132/780] Apply fixes from StyleCI --- src/Mpociot/ApiDoc/Commands/GenerateDocumentation.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Mpociot/ApiDoc/Commands/GenerateDocumentation.php b/src/Mpociot/ApiDoc/Commands/GenerateDocumentation.php index 6c97a204..d210bf02 100644 --- a/src/Mpociot/ApiDoc/Commands/GenerateDocumentation.php +++ b/src/Mpociot/ApiDoc/Commands/GenerateDocumentation.php @@ -81,7 +81,7 @@ public function handle() $routePrefixes = explode(",", $routePrefix); - $parsedRoutes = []; + $parsedRoutes = []; if ($this->option('router') === 'laravel') { foreach ($routePrefixes as $routePrefix) { From ec2c9590e577c9fcd96aafcdb90171f50d894508 Mon Sep 17 00:00:00 2001 From: Manash Jyoti Sonowal Date: Fri, 16 Jun 2017 23:44:44 +0530 Subject: [PATCH 133/780] apply fixes from StyleCI --- src/Mpociot/ApiDoc/Commands/GenerateDocumentation.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Mpociot/ApiDoc/Commands/GenerateDocumentation.php b/src/Mpociot/ApiDoc/Commands/GenerateDocumentation.php index d210bf02..4ab9e2f4 100644 --- a/src/Mpociot/ApiDoc/Commands/GenerateDocumentation.php +++ b/src/Mpociot/ApiDoc/Commands/GenerateDocumentation.php @@ -79,7 +79,7 @@ public function handle() $generator->prepareMiddleware($this->option('useMiddlewares')); - $routePrefixes = explode(",", $routePrefix); + $routePrefixes = explode(',', $routePrefix); $parsedRoutes = []; From 130b59129e1aa9e9254b42a4257c0d646b80f614 Mon Sep 17 00:00:00 2001 From: Alexander Reichardt Date: Thu, 29 Jun 2017 23:01:36 +0200 Subject: [PATCH 134/780] allow custom description for custom validation rules --- src/Mpociot/ApiDoc/Generators/AbstractGenerator.php | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/Mpociot/ApiDoc/Generators/AbstractGenerator.php b/src/Mpociot/ApiDoc/Generators/AbstractGenerator.php index b4efdcbc..650d17be 100644 --- a/src/Mpociot/ApiDoc/Generators/AbstractGenerator.php +++ b/src/Mpociot/ApiDoc/Generators/AbstractGenerator.php @@ -433,6 +433,12 @@ protected function parseRule($rule, $ruleName, &$attributeData, $seed) $attributeData['value'] = $faker->ipv4; $attributeData['type'] = $rule; break; + default: + $unknownRuleDescription = Description::parse($rule)->getDescription(); + if ($unknownRuleDescription) { + $attributeData['description'][] = $unknownRuleDescription; + } + break; } if ($attributeData['value'] === '') { From cb009caf3b3912e12acbf5fe78c5ba92c5b9176c Mon Sep 17 00:00:00 2001 From: antimech Date: Mon, 24 Jul 2017 21:03:42 +0700 Subject: [PATCH 135/780] Superfluous bracket (#207) --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 098fa35e..57a202d8 100644 --- a/README.md +++ b/README.md @@ -44,7 +44,7 @@ Route::group(array('prefix' => 'api/v1', 'middleware' => []), function () { // Custom route added to standard Resource Route::get('example/foo', 'ExampleController@foo'); // Standard Resource route - Route::resource('example', 'ExampleController')); + Route::resource('example', 'ExampleController'); }); ``` From 2d4cf80ef7371e7d3916d963cbdee7b3692e2da2 Mon Sep 17 00:00:00 2001 From: Jordan Hoff Date: Mon, 24 Jul 2017 09:04:13 -0500 Subject: [PATCH 136/780] Adds authProvider and authGuard Options (#213) * Adds authProvider Option * Add authGuard option too --- src/Mpociot/ApiDoc/Commands/GenerateDocumentation.php | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/Mpociot/ApiDoc/Commands/GenerateDocumentation.php b/src/Mpociot/ApiDoc/Commands/GenerateDocumentation.php index f430fc7d..857be1ca 100644 --- a/src/Mpociot/ApiDoc/Commands/GenerateDocumentation.php +++ b/src/Mpociot/ApiDoc/Commands/GenerateDocumentation.php @@ -20,7 +20,7 @@ class GenerateDocumentation extends Command * * @var string */ - protected $signature = 'api:generate + protected $signature = 'api:generate {--output=public/docs : The output path for the generated documentation} {--routePrefix= : The route prefix to use for generation} {--routes=* : The route names to use for generation} @@ -28,6 +28,8 @@ class GenerateDocumentation extends Command {--noResponseCalls : Disable API response calls} {--noPostmanCollection : Disable Postman collection creation} {--useMiddlewares : Use all configured route middlewares} + {--authProvider=users : The authentication provider to use for API response calls} + {--authGuard=web : The authentication guard to use for API response calls} {--actAsUserId= : The user ID to use for API response calls} {--router=laravel : The router to be used (Laravel or Dingo)} {--force : Force rewriting of existing routes} @@ -223,9 +225,10 @@ private function setUserToBeImpersonated($actAs) $user = $userModel::find((int) $actAs); $this->laravel['auth']->setUser($user); } else { - $userModel = config('auth.providers.users.model'); + $provider = $this->option('authProvider'); + $userModel = config("auth.providers.$provider.model"); $user = $userModel::find((int) $actAs); - $this->laravel['auth']->guard()->setUser($user); + $this->laravel['auth']->guard($this->option('authGuard'))->setUser($user); } } } From fef471296653e335a81f65d1a32ab7226ac5a3fa Mon Sep 17 00:00:00 2001 From: Gustavo Real Date: Tue, 10 Oct 2017 17:21:48 +0100 Subject: [PATCH 137/780] support custom validator method on laravel form request --- .../ApiDoc/Generators/LaravelGenerator.php | 4 ++- tests/ApiDocGeneratorTest.php | 10 +++++++ tests/Fixtures/CustomValidatorRequest.php | 30 +++++++++++++++++++ tests/Fixtures/TestController.php | 5 ++++ 4 files changed, 48 insertions(+), 1 deletion(-) create mode 100644 tests/Fixtures/CustomValidatorRequest.php diff --git a/src/Mpociot/ApiDoc/Generators/LaravelGenerator.php b/src/Mpociot/ApiDoc/Generators/LaravelGenerator.php index f86e6f2c..9faa7485 100644 --- a/src/Mpociot/ApiDoc/Generators/LaravelGenerator.php +++ b/src/Mpociot/ApiDoc/Generators/LaravelGenerator.php @@ -11,6 +11,7 @@ use Illuminate\Support\Facades\Request; use League\Fractal\Resource\Collection; use Illuminate\Foundation\Http\FormRequest; +use Illuminate\Contracts\Validation\Factory as ValidationFactory; class LaravelGenerator extends AbstractGenerator { @@ -266,7 +267,8 @@ protected function getRouteRules($route, $bindings) $parameterReflection->request->add($bindings); if (method_exists($parameterReflection, 'validator')) { - return app()->call([$parameterReflection, 'validator']) + $factory = app()->make(ValidationFactory::class); + return app()->call([$parameterReflection, 'validator'], [$factory]) ->getRules(); } else { return app()->call([$parameterReflection, 'rules']); diff --git a/tests/ApiDocGeneratorTest.php b/tests/ApiDocGeneratorTest.php index e9009399..2d4926c5 100644 --- a/tests/ApiDocGeneratorTest.php +++ b/tests/ApiDocGeneratorTest.php @@ -337,6 +337,16 @@ public function testCanParseFormRequestRules() } } + public function testCustomFormRequestValidatorIsSupported() + { + RouteFacade::post('/post', TestController::class.'@customFormRequestValidator'); + $route = new Route(['POST'], '/post', ['uses' => TestController::class.'@customFormRequestValidator']); + $parsed = $this->generator->processRoute($route); + $parameters = $parsed['parameters']; + + $this->assertNotEmpty($parameters); + } + public function testCanParseResponseTag() { RouteFacade::post('/responseTag', TestController::class.'@responseTag'); diff --git a/tests/Fixtures/CustomValidatorRequest.php b/tests/Fixtures/CustomValidatorRequest.php new file mode 100644 index 00000000..9ce53682 --- /dev/null +++ b/tests/Fixtures/CustomValidatorRequest.php @@ -0,0 +1,30 @@ +make( + $this->validationData(), $this->container->call([$this, 'foo']), + $this->messages(), $this->attributes() + ); + } + + public function foo() + { + return [ + 'required' => 'required', + ]; + } +} diff --git a/tests/Fixtures/TestController.php b/tests/Fixtures/TestController.php index 1f3be0d6..c9abc8b7 100644 --- a/tests/Fixtures/TestController.php +++ b/tests/Fixtures/TestController.php @@ -27,6 +27,11 @@ public function parseFormRequestRules(TestRequest $request) return ''; } + public function customFormRequestValidator(CustomValidatorRequest $request) + { + return ''; + } + public function addRouteBindingsToRequestClass(DynamicRequest $request) { return ''; From bc27ba4de20cebae977be2cc9b772ee6e44c7a48 Mon Sep 17 00:00:00 2001 From: Gustavo Real Date: Tue, 10 Oct 2017 17:38:38 +0100 Subject: [PATCH 138/780] fix code style --- src/Mpociot/ApiDoc/Generators/LaravelGenerator.php | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Mpociot/ApiDoc/Generators/LaravelGenerator.php b/src/Mpociot/ApiDoc/Generators/LaravelGenerator.php index 9faa7485..a6f6ebd2 100644 --- a/src/Mpociot/ApiDoc/Generators/LaravelGenerator.php +++ b/src/Mpociot/ApiDoc/Generators/LaravelGenerator.php @@ -268,6 +268,7 @@ protected function getRouteRules($route, $bindings) if (method_exists($parameterReflection, 'validator')) { $factory = app()->make(ValidationFactory::class); + return app()->call([$parameterReflection, 'validator'], [$factory]) ->getRules(); } else { From f8564b60ff0b54c9a57a4cc943055ec0a13f7e5d Mon Sep 17 00:00:00 2001 From: Bogdan Shtybin Date: Thu, 28 Dec 2017 19:30:25 +0200 Subject: [PATCH 139/780] Added Subdomain API Router Support --- .../ApiDoc/Commands/GenerateDocumentation.php | 17 +++++++---- .../ApiDoc/Generators/AbstractGenerator.php | 7 +++++ .../ApiDoc/Generators/DingoGenerator.php | 8 ++++++ .../ApiDoc/Generators/LaravelGenerator.php | 28 +++++++++++++++---- 4 files changed, 50 insertions(+), 10 deletions(-) diff --git a/src/Mpociot/ApiDoc/Commands/GenerateDocumentation.php b/src/Mpociot/ApiDoc/Commands/GenerateDocumentation.php index 857be1ca..0ff9bb07 100644 --- a/src/Mpociot/ApiDoc/Commands/GenerateDocumentation.php +++ b/src/Mpociot/ApiDoc/Commands/GenerateDocumentation.php @@ -22,6 +22,7 @@ class GenerateDocumentation extends Command */ protected $signature = 'api:generate {--output=public/docs : The output path for the generated documentation} + {--routeDomain= : The route domain to use for generation} {--routePrefix= : The route prefix to use for generation} {--routes=* : The route names to use for generation} {--middleware= : The middleware to use for generation} @@ -68,13 +69,14 @@ public function handle() } $allowedRoutes = $this->option('routes'); + $routeDomain = $this->option('routeDomain'); $routePrefix = $this->option('routePrefix'); $middleware = $this->option('middleware'); $this->setUserToBeImpersonated($this->option('actAsUserId')); - if ($routePrefix === null && ! count($allowedRoutes) && $middleware === null) { - $this->error('You must provide either a route prefix or a route or a middleware to generate the documentation.'); + if ($routePrefix === null && $routeDomain === null && ! count($allowedRoutes) && $middleware === null) { + $this->error('You must provide either a route prefix or a route domain a route or a middleware to generate the documentation.'); return false; } @@ -82,8 +84,9 @@ public function handle() $generator->prepareMiddleware($this->option('useMiddlewares')); if ($this->option('router') === 'laravel') { - $parsedRoutes = $this->processLaravelRoutes($generator, $allowedRoutes, $routePrefix, $middleware); + $parsedRoutes = $this->processLaravelRoutes($generator, $allowedRoutes, $routeDomain, $routePrefix, $middleware); } else { + // TODO: implement Dingo domain route filter $parsedRoutes = $this->processDingoRoutes($generator, $allowedRoutes, $routePrefix, $middleware); } $parsedRoutes = collect($parsedRoutes)->groupBy('resource')->sort(function ($a, $b) { @@ -248,18 +251,22 @@ private function getRoutes() /** * @param AbstractGenerator $generator * @param $allowedRoutes + * @param $routeDomain * @param $routePrefix * * @return array */ - private function processLaravelRoutes(AbstractGenerator $generator, $allowedRoutes, $routePrefix, $middleware) + private function processLaravelRoutes(AbstractGenerator $generator, $allowedRoutes, $routeDomain, $routePrefix, $middleware) { $withResponse = $this->option('noResponseCalls') === false; $routes = $this->getRoutes(); $bindings = $this->getBindings(); $parsedRoutes = []; foreach ($routes as $route) { - if (in_array($route->getName(), $allowedRoutes) || str_is($routePrefix, $generator->getUri($route)) || in_array($middleware, $route->middleware())) { + if (in_array($route->getName(), $allowedRoutes) + || str_is($routeDomain, $generator->getDomain($route)) + || str_is($routePrefix, $generator->getUri($route)) + || in_array($middleware, $route->middleware())) { if ($this->isValidRoute($route) && $this->isRouteVisibleForDocumentation($route->getAction()['uses'])) { $parsedRoutes[] = $generator->processRoute($route, $bindings, $this->option('header'), $withResponse); $this->info('Processed route: ['.implode(',', $generator->getMethods($route)).'] '.$generator->getUri($route)); diff --git a/src/Mpociot/ApiDoc/Generators/AbstractGenerator.php b/src/Mpociot/ApiDoc/Generators/AbstractGenerator.php index b4efdcbc..b1452a91 100644 --- a/src/Mpociot/ApiDoc/Generators/AbstractGenerator.php +++ b/src/Mpociot/ApiDoc/Generators/AbstractGenerator.php @@ -14,6 +14,13 @@ abstract class AbstractGenerator { + /** + * @param $route + * + * @return mixed + */ + abstract public function getDomain($route); + /** * @param $route * diff --git a/src/Mpociot/ApiDoc/Generators/DingoGenerator.php b/src/Mpociot/ApiDoc/Generators/DingoGenerator.php index d85ac8c4..cd741bb5 100644 --- a/src/Mpociot/ApiDoc/Generators/DingoGenerator.php +++ b/src/Mpociot/ApiDoc/Generators/DingoGenerator.php @@ -68,6 +68,14 @@ public function callRoute($method, $uri, $parameters = [], $cookies = [], $files return call_user_func_array([$dispatcher, strtolower($method)], [$uri]); } + /** + * {@inheritdoc} + */ + public function getDomain($route) + { + return $route->uri(); + } + /** * {@inheritdoc} */ diff --git a/src/Mpociot/ApiDoc/Generators/LaravelGenerator.php b/src/Mpociot/ApiDoc/Generators/LaravelGenerator.php index f86e6f2c..92441644 100644 --- a/src/Mpociot/ApiDoc/Generators/LaravelGenerator.php +++ b/src/Mpociot/ApiDoc/Generators/LaravelGenerator.php @@ -2,18 +2,31 @@ namespace Mpociot\ApiDoc\Generators; -use ReflectionClass; -use League\Fractal\Manager; +use Illuminate\Foundation\Http\FormRequest; use Illuminate\Routing\Route; -use League\Fractal\Resource\Item; +use Illuminate\Routing\RouteUrlGenerator; use Illuminate\Support\Facades\App; -use Mpociot\Reflection\DocBlock\Tag; use Illuminate\Support\Facades\Request; +use Illuminate\Support\Facades\URL; +use League\Fractal\Manager; use League\Fractal\Resource\Collection; -use Illuminate\Foundation\Http\FormRequest; +use League\Fractal\Resource\Item; +use Mpociot\Reflection\DocBlock\Tag; +use ReflectionClass; class LaravelGenerator extends AbstractGenerator { + /** + * @param Route $route + * + * @return mixed + */ + public function getDomain($route) + { + return $route->domain(); + } + + /** * @param Route $route * @@ -54,11 +67,16 @@ public function processRoute($route, $bindings = [], $headers = [], $withRespons { $content = ''; + $routeDomain = $route->domain(); $routeAction = $route->getAction(); $routeGroup = $this->getRouteGroup($routeAction['uses']); $routeDescription = $this->getRouteDescription($routeAction['uses']); $showresponse = null; + // set correct route domain + $headers[] = "HTTP_HOST: {$routeDomain}"; + $headers[] = "SERVER_NAME: {$routeDomain}"; + if ($withResponse) { $response = null; $docblockResponse = $this->getDocblockResponse($routeDescription['tags']); From e852f6b439ad0e62dbb91425fae1623f74f2d5a8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Artur=20=C5=9Amiarowski?= Date: Wed, 3 Jan 2018 07:41:32 +0100 Subject: [PATCH 140/780] Memory leak, too many connections to SQL --- src/Mpociot/ApiDoc/Generators/LaravelGenerator.php | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/Mpociot/ApiDoc/Generators/LaravelGenerator.php b/src/Mpociot/ApiDoc/Generators/LaravelGenerator.php index f86e6f2c..fa729b15 100644 --- a/src/Mpociot/ApiDoc/Generators/LaravelGenerator.php +++ b/src/Mpociot/ApiDoc/Generators/LaravelGenerator.php @@ -140,11 +140,6 @@ public function callRoute($method, $uri, $parameters = [], $cookies = [], $files $kernel->terminate($request, $response); - if (file_exists($file = App::bootstrapPath().'/app.php')) { - $app = require $file; - $app->make('Illuminate\Contracts\Console\Kernel')->bootstrap(); - } - return $response; } From 4aab1b4498627d87bad3497f1c3bec95b935513c Mon Sep 17 00:00:00 2001 From: Fongoh Martin Date: Wed, 3 Jan 2018 11:51:07 +0200 Subject: [PATCH 141/780] Removed duplicate words(typo) from the sentence (#249) Initially had As of as of Larave ... so removed one of the as of so that the language is fluent. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 57a202d8..a41cdc2b 100644 --- a/README.md +++ b/README.md @@ -206,7 +206,7 @@ The generator automatically creates a Postman collection file, which you can imp If you don't want to create a Postman collection, use the `--noPostmanCollection` option, when generating the API documentation. -As of as of Laravel 5.3, the default base URL added to the Postman collection will be that found in your Laravel `config/app.php` file. This will likely be `http://localhost`. If you wish to change this setting you can directly update the url or link this config value to your environment file to make it more flexible (as shown below): +As of Laravel 5.3, the default base URL added to the Postman collection will be that found in your Laravel `config/app.php` file. This will likely be `http://localhost`. If you wish to change this setting you can directly update the url or link this config value to your environment file to make it more flexible (as shown below): ```php 'url' => env('APP_URL', 'http://yourappdefault.app/'), From fe58fa263b14165eb4cfcf072ca933e83ac966df Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kov=C3=A1cs=20Lajos?= Date: Wed, 3 Jan 2018 10:51:47 +0100 Subject: [PATCH 142/780] Fixed flat design for StyleCI shield. (#244) --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index a41cdc2b..fb9e8f21 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,7 @@ Automatically generate your API documentation from your existing Laravel routes. [![codecov.io](https://codecov.io/github/mpociot/laravel-apidoc-generator/coverage.svg?branch=master)](https://codecov.io/github/mpociot/laravel-apidoc-generator?branch=master) [![Scrutinizer Code Quality](https://scrutinizer-ci.com/g/mpociot/laravel-apidoc-generator/badges/quality-score.png?b=master)](https://scrutinizer-ci.com/g/mpociot/laravel-apidoc-generator/?branch=master) [![Build Status](https://travis-ci.org/mpociot/laravel-apidoc-generator.svg?branch=master)](https://travis-ci.org/mpociot/laravel-apidoc-generator) -[![StyleCI](https://styleci.io/repos/57999295/shield)](https://styleci.io/repos/57999295) +[![StyleCI](https://styleci.io/repos/57999295/shield?style=flat)](https://styleci.io/repos/57999295) [![Dependency Status](https://www.versioneye.com/php/mpociot:laravel-apidoc-generator/dev-master/badge?style=flat)](https://www.versioneye.com/php/mpociot:laravel-apidoc-generator/dev-master) From 4a78f3db0f5c5d0c87e4a64d33809091f1ec8b56 Mon Sep 17 00:00:00 2001 From: Fabien SOMNIER Date: Wed, 3 Jan 2018 09:52:42 +0000 Subject: [PATCH 143/780] [BUGFIXES] Header containing ":" + invalid "showresponse" condition (#242) * [BUGFIX] Header containing ":" values management * [CHANGE] To pass styleci.io tests * [BUGFIX] Invalid condition test to display response --- src/Mpociot/ApiDoc/Generators/AbstractGenerator.php | 6 ++++-- src/resources/views/partials/route.blade.php | 2 +- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/Mpociot/ApiDoc/Generators/AbstractGenerator.php b/src/Mpociot/ApiDoc/Generators/AbstractGenerator.php index b4efdcbc..313883f4 100644 --- a/src/Mpociot/ApiDoc/Generators/AbstractGenerator.php +++ b/src/Mpociot/ApiDoc/Generators/AbstractGenerator.php @@ -112,9 +112,11 @@ protected function getRouteResponse($route, $bindings, $headers = []) // Split headers into key - value pairs $headers = collect($headers)->map(function ($value) { - $split = explode(':', $value); + $split = explode(':', $value); // explode to get key + values + $key = array_shift($split); // extract the key and keep the values in the array + $value = implode(':', $split); // implode values into string again - return [trim($split[0]) => trim($split[1])]; + return [trim($key) => trim($value)]; })->collapse()->toArray(); //Changes url with parameters like /users/{user} to /users/1 diff --git a/src/resources/views/partials/route.blade.php b/src/resources/views/partials/route.blade.php index f285dc4a..03b4f639 100644 --- a/src/resources/views/partials/route.blade.php +++ b/src/resources/views/partials/route.blade.php @@ -38,7 +38,7 @@ }); ``` -@if(in_array('GET',$parsedRoute['methods']) || isset($parsedRoute['showresponse']) && $parsedRoute['showresponse']) +@if(in_array('GET',$parsedRoute['methods']) || (isset($parsedRoute['showresponse']) && $parsedRoute['showresponse'])) > Example response: ```json From 8917ca4c968867ba370ef9c24298ffa4d05f7ec2 Mon Sep 17 00:00:00 2001 From: Jonathan JEAN Date: Wed, 3 Jan 2018 10:53:18 +0100 Subject: [PATCH 144/780] Laravel 5.5 auto-discovery (#217) * Laravel 5.5 auto-discovery * Removing extra trailing comma --- composer.json | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/composer.json b/composer.json index 086555c6..c4ce211d 100644 --- a/composer.json +++ b/composer.json @@ -37,5 +37,12 @@ "psr-4": { "Mpociot\\ApiDoc\\Tests\\": "tests/" } + }, + "extra": { + "laravel": { + "providers": [ + "Mpociot\\ApiDoc\\ApiDocGeneratorServiceProvider" + ] + } } } From e8021d243f62cb83073c23ae4ae8b9c82484b433 Mon Sep 17 00:00:00 2001 From: antimech Date: Wed, 3 Jan 2018 15:54:02 +0600 Subject: [PATCH 145/780] Update README.md (#226) README.md ready for 5.5! --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index fb9e8f21..203b45f7 100644 --- a/README.md +++ b/README.md @@ -20,7 +20,7 @@ Require this package with composer using the following command: ```sh $ composer require mpociot/laravel-apidoc-generator ``` -Go to your `config/app.php` and add the service provider: +Using Laravel < 5.5? Go to your `config/app.php` and add the service provider: ```php Mpociot\ApiDoc\ApiDocGeneratorServiceProvider::class, From 0925f838047284955f00fab2dd1e8956cba24dfa Mon Sep 17 00:00:00 2001 From: Sergei Belyakov Date: Wed, 3 Jan 2018 11:54:19 +0200 Subject: [PATCH 146/780] Update README.md (#224) --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 203b45f7..1452ea73 100644 --- a/README.md +++ b/README.md @@ -63,7 +63,7 @@ Option | Description `router` | The router to use, when processing the route files (can be Laravel or Dingo - defaults to Laravel) `bindings` | List of route bindings that should be replaced when trying to retrieve route results. Syntax format: `binding_one,id|binding_two,id` `force` | Force the re-generation of existing/modified API routes -`header` | Custom HTTP headers to add to the example requests. Separate the header name and value with ":". For example: `--header 'Authorization: CustomToken'` +`header` | Custom HTTP headers to add to the example requests. Separate the header name and value with ":". For example: `--header="Authorization: CustomToken"` ## Publish rule descriptions for customisation or translation. From 70fd369b61615d590d4f57ca3d305f5ec53aa3b0 Mon Sep 17 00:00:00 2001 From: Rafael Villa-Verde Date: Fri, 5 Jan 2018 13:55:02 -0200 Subject: [PATCH 147/780] remove --actAsUserId parameter conversion to int --- src/Mpociot/ApiDoc/Commands/GenerateDocumentation.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Mpociot/ApiDoc/Commands/GenerateDocumentation.php b/src/Mpociot/ApiDoc/Commands/GenerateDocumentation.php index 857be1ca..9ceb59a0 100644 --- a/src/Mpociot/ApiDoc/Commands/GenerateDocumentation.php +++ b/src/Mpociot/ApiDoc/Commands/GenerateDocumentation.php @@ -222,12 +222,12 @@ private function setUserToBeImpersonated($actAs) if (! empty($actAs)) { if (version_compare($this->laravel->version(), '5.2.0', '<')) { $userModel = config('auth.model'); - $user = $userModel::find((int) $actAs); + $user = $userModel::find($actAs); $this->laravel['auth']->setUser($user); } else { $provider = $this->option('authProvider'); $userModel = config("auth.providers.$provider.model"); - $user = $userModel::find((int) $actAs); + $user = $userModel::find($actAs); $this->laravel['auth']->guard($this->option('authGuard'))->setUser($user); } } From 5f8c5b505cd5ba991f2b995557e5bfcb02840672 Mon Sep 17 00:00:00 2001 From: Tim Date: Thu, 15 Feb 2018 17:46:49 +1100 Subject: [PATCH 148/780] allow formatting and aside tags The group description was escaping everything by default meaning that the aside tag (which is quite useful) can't be used in a group header. This addresses that by no longer escaping the content, as is consistent with the rest of the library. --- src/resources/views/documentarian.blade.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/resources/views/documentarian.blade.php b/src/resources/views/documentarian.blade.php index 9e2a136a..6d7a3c24 100644 --- a/src/resources/views/documentarian.blade.php +++ b/src/resources/views/documentarian.blade.php @@ -7,7 +7,7 @@ @foreach($parsedRoutes as $group => $routes) @if($group) -#{{$group}} +#{!! $group !!} @endif @foreach($routes as $parsedRoute) @if($writeCompareFile === true) From 9d20bd6d7058f2d984e4b633d45c696e8d40326d Mon Sep 17 00:00:00 2001 From: sempixel Date: Mon, 12 Mar 2018 16:16:53 +0100 Subject: [PATCH 149/780] Correct JSON format for @response --- src/Mpociot/ApiDoc/Generators/AbstractGenerator.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Mpociot/ApiDoc/Generators/AbstractGenerator.php b/src/Mpociot/ApiDoc/Generators/AbstractGenerator.php index 313883f4..bf904b54 100644 --- a/src/Mpociot/ApiDoc/Generators/AbstractGenerator.php +++ b/src/Mpociot/ApiDoc/Generators/AbstractGenerator.php @@ -67,7 +67,7 @@ protected function getDocblockResponse($tags) } $responseTag = \array_first($responseTags); - return \response(\json_encode($responseTag->getContent())); + return \response($responseTag->getContent(), 200, ['content-type' => 'application/json']); } /** From c557ca970ad67794c046f0967be415addcb0faa8 Mon Sep 17 00:00:00 2001 From: sempixel Date: Mon, 12 Mar 2018 17:27:35 +0100 Subject: [PATCH 150/780] =?UTF-8?q?We=20have=20to=20go=20deeper=E2=80=A6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Mpociot/ApiDoc/Generators/AbstractGenerator.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Mpociot/ApiDoc/Generators/AbstractGenerator.php b/src/Mpociot/ApiDoc/Generators/AbstractGenerator.php index bf904b54..8e5a2f79 100644 --- a/src/Mpociot/ApiDoc/Generators/AbstractGenerator.php +++ b/src/Mpociot/ApiDoc/Generators/AbstractGenerator.php @@ -67,7 +67,7 @@ protected function getDocblockResponse($tags) } $responseTag = \array_first($responseTags); - return \response($responseTag->getContent(), 200, ['content-type' => 'application/json']); + return \response(json_encode($responseTag->getContent()), 200, ['Content-Type' => 'application/json']); } /** From 460d221a075f7dd0ab5dfb913753b1c049dbb558 Mon Sep 17 00:00:00 2001 From: sempixel Date: Mon, 12 Mar 2018 17:28:17 +0100 Subject: [PATCH 151/780] Fix test --- tests/ApiDocGeneratorTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/ApiDocGeneratorTest.php b/tests/ApiDocGeneratorTest.php index e9009399..9cee1f79 100644 --- a/tests/ApiDocGeneratorTest.php +++ b/tests/ApiDocGeneratorTest.php @@ -345,7 +345,7 @@ public function testCanParseResponseTag() $this->assertTrue(is_array($parsed)); $this->assertArrayHasKey('showresponse', $parsed); $this->assertTrue($parsed['showresponse']); - $this->assertSame($parsed['response'], '"{\n data: [],\n}"'); + $this->assertSame($parsed['response'], "{\n data: [],\n}"); } public function testCanParseTransformerTag() From fbdc6033b8c73037683a1494e60233f21d6598fb Mon Sep 17 00:00:00 2001 From: sempixel Date: Mon, 12 Mar 2018 17:35:09 +0100 Subject: [PATCH 152/780] =?UTF-8?q?Too=20fast=E2=80=A6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Mpociot/ApiDoc/Generators/LaravelGenerator.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Mpociot/ApiDoc/Generators/LaravelGenerator.php b/src/Mpociot/ApiDoc/Generators/LaravelGenerator.php index f86e6f2c..0330d78a 100644 --- a/src/Mpociot/ApiDoc/Generators/LaravelGenerator.php +++ b/src/Mpociot/ApiDoc/Generators/LaravelGenerator.php @@ -79,7 +79,7 @@ public function processRoute($route, $bindings = [], $headers = [], $withRespons $response = $this->getRouteResponse($route, $bindings, $headers); } if ($response->headers->get('Content-Type') === 'application/json') { - $content = json_encode(json_decode($response->getContent()), JSON_PRETTY_PRINT); + $content = json_decode($response->getContent(), JSON_PRETTY_PRINT); } else { $content = $response->getContent(); } From 0dcb277043cca4731285fc7986afeddad7850514 Mon Sep 17 00:00:00 2001 From: vagrant Date: Sat, 14 Apr 2018 12:41:33 +0000 Subject: [PATCH 153/780] Make generator only fetch response from route if method is GET --- src/Mpociot/ApiDoc/Commands/GenerateDocumentation.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Mpociot/ApiDoc/Commands/GenerateDocumentation.php b/src/Mpociot/ApiDoc/Commands/GenerateDocumentation.php index 857be1ca..24726428 100644 --- a/src/Mpociot/ApiDoc/Commands/GenerateDocumentation.php +++ b/src/Mpociot/ApiDoc/Commands/GenerateDocumentation.php @@ -261,7 +261,7 @@ private function processLaravelRoutes(AbstractGenerator $generator, $allowedRout foreach ($routes as $route) { if (in_array($route->getName(), $allowedRoutes) || str_is($routePrefix, $generator->getUri($route)) || in_array($middleware, $route->middleware())) { if ($this->isValidRoute($route) && $this->isRouteVisibleForDocumentation($route->getAction()['uses'])) { - $parsedRoutes[] = $generator->processRoute($route, $bindings, $this->option('header'), $withResponse); + $parsedRoutes[] = $generator->processRoute($route, $bindings, $this->option('header'), $withResponse && in_array('GET', $generator->getMethods($route))); $this->info('Processed route: ['.implode(',', $generator->getMethods($route)).'] '.$generator->getUri($route)); } else { $this->warn('Skipping route: ['.implode(',', $generator->getMethods($route)).'] '.$generator->getUri($route)); From 831fe37dc1e5f460ba1d2f80ef9ac4a9c20ee8fb Mon Sep 17 00:00:00 2001 From: Amir Hossein Babaeian Date: Sun, 5 Aug 2018 16:36:20 +0430 Subject: [PATCH 154/780] updating route-model-binding binding technique and preparing middlewares --- src/Mpociot/ApiDoc/Generators/AbstractGenerator.php | 5 +++-- src/Mpociot/ApiDoc/Generators/LaravelGenerator.php | 9 ++------- 2 files changed, 5 insertions(+), 9 deletions(-) diff --git a/src/Mpociot/ApiDoc/Generators/AbstractGenerator.php b/src/Mpociot/ApiDoc/Generators/AbstractGenerator.php index 313883f4..7b357501 100644 --- a/src/Mpociot/ApiDoc/Generators/AbstractGenerator.php +++ b/src/Mpociot/ApiDoc/Generators/AbstractGenerator.php @@ -44,7 +44,7 @@ abstract public function processRoute($route, $bindings = [], $withResponse = tr * * @return void */ - abstract public function prepareMiddleware($disable = false); + abstract public function prepareMiddleware($enable = false); /** * Get the response from the docblock if available. @@ -120,7 +120,7 @@ protected function getRouteResponse($route, $bindings, $headers = []) })->collapse()->toArray(); //Changes url with parameters like /users/{user} to /users/1 - $uri = preg_replace('/{(.*?)}/', 1, $uri); + $uri = preg_replace('/{(.*?)}/', 1, $uri); // 1 is the default value for route parameters return $this->callRoute(array_shift($methods), $uri, [], [], [], $headers); } @@ -136,6 +136,7 @@ protected function addRouteModelBindings($route, $bindings) $uri = $this->getUri($route); foreach ($bindings as $model => $id) { $uri = str_replace('{'.$model.'}', $id, $uri); + $uri = str_replace('{'.$model.'?}', $id, $uri); } return $uri; diff --git a/src/Mpociot/ApiDoc/Generators/LaravelGenerator.php b/src/Mpociot/ApiDoc/Generators/LaravelGenerator.php index f86e6f2c..b9ec284d 100644 --- a/src/Mpociot/ApiDoc/Generators/LaravelGenerator.php +++ b/src/Mpociot/ApiDoc/Generators/LaravelGenerator.php @@ -105,9 +105,9 @@ public function processRoute($route, $bindings = [], $headers = [], $withRespons * * @return void */ - public function prepareMiddleware($disable = true) + public function prepareMiddleware($enable = true) { - App::instance('middleware.disable', true); + App::instance('middleware.disable', !$enable); } /** @@ -140,11 +140,6 @@ public function callRoute($method, $uri, $parameters = [], $cookies = [], $files $kernel->terminate($request, $response); - if (file_exists($file = App::bootstrapPath().'/app.php')) { - $app = require $file; - $app->make('Illuminate\Contracts\Console\Kernel')->bootstrap(); - } - return $response; } From fdca5eeeb1e8b91af0240c670abd116705155284 Mon Sep 17 00:00:00 2001 From: Tihomir Tonov Date: Tue, 14 Aug 2018 16:33:11 +0300 Subject: [PATCH 155/780] Add method to generate validation rules for nested array properties --- .../ApiDoc/Generators/AbstractGenerator.php | 30 +++++++++++++++++-- 1 file changed, 28 insertions(+), 2 deletions(-) diff --git a/src/Mpociot/ApiDoc/Generators/AbstractGenerator.php b/src/Mpociot/ApiDoc/Generators/AbstractGenerator.php index 313883f4..90bbb5f1 100644 --- a/src/Mpociot/ApiDoc/Generators/AbstractGenerator.php +++ b/src/Mpociot/ApiDoc/Generators/AbstractGenerator.php @@ -79,8 +79,9 @@ protected function getDocblockResponse($tags) */ protected function getParameters($routeData, $routeAction, $bindings) { - $validator = Validator::make([], $this->getRouteRules($routeAction['uses'], $bindings)); - foreach ($validator->getRules() as $attribute => $rules) { + $rules = $this->simplifyRules($this->getRouteRules($routeAction['uses'], $bindings)); + + foreach ($rules as $attribute => $rules) { $attributeData = [ 'required' => false, 'type' => null, @@ -88,6 +89,7 @@ protected function getParameters($routeData, $routeAction, $bindings) 'value' => '', 'description' => [], ]; + foreach ($rules as $ruleName => $rule) { $this->parseRule($rule, $attribute, $attributeData, $routeData['id']); } @@ -97,6 +99,30 @@ protected function getParameters($routeData, $routeAction, $bindings) return $routeData; } + /** + * Format the validation rules as plain array + * + * @param array $rules + * @return array + */ + protected function simplifyRules($rules) + { + $simplifiedRules = Validator::make([], $rules)->getRules(); + + if (count($simplifiedRules) === 0) { + return $simplifiedRules; + } + + $values = collect($simplifiedRules) + ->filter(function ($values) { + return in_array('array', $values); + })->map(function ($val, $key) { + return ['']; + })->all(); + + return Validator::make($values, $rules)->getRules(); + } + /** * @param $route * @param $bindings From 6bb9a8cf479062545fda88d6d15a82b13e529e87 Mon Sep 17 00:00:00 2001 From: Tihomir Tonov Date: Tue, 14 Aug 2018 16:40:25 +0300 Subject: [PATCH 156/780] Remove linting option as depricated in style CI --- .styleci.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.styleci.yml b/.styleci.yml index 2fe66ad1..75bf3532 100644 --- a/.styleci.yml +++ b/.styleci.yml @@ -4,5 +4,3 @@ enabled: - phpdoc_order - phpdoc_separation - unalign_double_arrow - -linting: true From d6e7e79aedeeb616e3ca38cb8d694e63fb97f71c Mon Sep 17 00:00:00 2001 From: Tihomir Tonov Date: Tue, 14 Aug 2018 16:43:56 +0300 Subject: [PATCH 157/780] Fix style violation --- src/Mpociot/ApiDoc/Generators/AbstractGenerator.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Mpociot/ApiDoc/Generators/AbstractGenerator.php b/src/Mpociot/ApiDoc/Generators/AbstractGenerator.php index 90bbb5f1..327a76c0 100644 --- a/src/Mpociot/ApiDoc/Generators/AbstractGenerator.php +++ b/src/Mpociot/ApiDoc/Generators/AbstractGenerator.php @@ -100,9 +100,10 @@ protected function getParameters($routeData, $routeAction, $bindings) } /** - * Format the validation rules as plain array + * Format the validation rules as plain array. * * @param array $rules + * * @return array */ protected function simplifyRules($rules) From 0fce50f42199ee5f8964babd6c5722c4174dd02c Mon Sep 17 00:00:00 2001 From: Marcel Pociot Date: Fri, 7 Sep 2018 09:54:12 +0000 Subject: [PATCH 158/780] Apply fixes from StyleCI --- src/Mpociot/ApiDoc/Generators/LaravelGenerator.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Mpociot/ApiDoc/Generators/LaravelGenerator.php b/src/Mpociot/ApiDoc/Generators/LaravelGenerator.php index 8b1c6444..20659abf 100644 --- a/src/Mpociot/ApiDoc/Generators/LaravelGenerator.php +++ b/src/Mpociot/ApiDoc/Generators/LaravelGenerator.php @@ -107,7 +107,7 @@ public function processRoute($route, $bindings = [], $headers = [], $withRespons */ public function prepareMiddleware($enable = true) { - App::instance('middleware.disable', !$enable); + App::instance('middleware.disable', ! $enable); } /** From afeca2faf71529fd94ce08ef44a2513b1d277ea5 Mon Sep 17 00:00:00 2001 From: Shalvah A Date: Fri, 7 Sep 2018 11:05:27 +0100 Subject: [PATCH 159/780] Update README.md --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index ede1f974..3ffc3539 100644 --- a/README.md +++ b/README.md @@ -34,9 +34,9 @@ To generate your API documentation, use the `api:generate` artisan command. ```sh $ php artisan api:generate --routePrefix="api/v1/*" -``` -After 28ff33be15424ff5a5b819dbe88b34468d1752b2 this commit, it supports passing generation of multiple prefixes by spearating each prefix with comma. +``` +You can pass in multiple prefixes by spearating each prefix with comma. ```sh $ php artisan api:generate --routePrefix="api/v1/*,api/public/*" From 2830ae485a320db85dfe918ba949736196c5f1cd Mon Sep 17 00:00:00 2001 From: Shalvah A Date: Sun, 9 Sep 2018 02:10:58 +0100 Subject: [PATCH 160/780] Update DingoGenerator.php --- src/Mpociot/ApiDoc/Generators/DingoGenerator.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Mpociot/ApiDoc/Generators/DingoGenerator.php b/src/Mpociot/ApiDoc/Generators/DingoGenerator.php index cd741bb5..30f865e1 100644 --- a/src/Mpociot/ApiDoc/Generators/DingoGenerator.php +++ b/src/Mpociot/ApiDoc/Generators/DingoGenerator.php @@ -73,7 +73,7 @@ public function callRoute($method, $uri, $parameters = [], $cookies = [], $files */ public function getDomain($route) { - return $route->uri(); + return $route->domain(); } /** From ac70e500bf501869b47b7c22659c286e963fad69 Mon Sep 17 00:00:00 2001 From: Shalvah A Date: Sun, 9 Sep 2018 02:41:59 +0100 Subject: [PATCH 161/780] Change error message for when neither of required options is supplied --- src/Mpociot/ApiDoc/Commands/GenerateDocumentation.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Mpociot/ApiDoc/Commands/GenerateDocumentation.php b/src/Mpociot/ApiDoc/Commands/GenerateDocumentation.php index 0991347e..eb49f0b8 100644 --- a/src/Mpociot/ApiDoc/Commands/GenerateDocumentation.php +++ b/src/Mpociot/ApiDoc/Commands/GenerateDocumentation.php @@ -76,7 +76,7 @@ public function handle() $this->setUserToBeImpersonated($this->option('actAsUserId')); if ($routePrefix === null && $routeDomain === null && ! count($allowedRoutes) && $middleware === null) { - $this->error('You must provide either a route prefix or a route domain or a middleware to generate the documentation.'); + $this->error('You must provide either a route prefix, a route domain, a route or a middleware to generate the documentation.'); return false; } From 32620365fba71cd729c3e2d5b79360fadb9a9732 Mon Sep 17 00:00:00 2001 From: Marcel Pociot Date: Sun, 9 Sep 2018 01:44:31 +0000 Subject: [PATCH 162/780] Apply fixes from StyleCI --- .../ApiDoc/Commands/GenerateDocumentation.php | 8 ++++---- src/Mpociot/ApiDoc/Generators/LaravelGenerator.php | 13 +++++-------- 2 files changed, 9 insertions(+), 12 deletions(-) diff --git a/src/Mpociot/ApiDoc/Commands/GenerateDocumentation.php b/src/Mpociot/ApiDoc/Commands/GenerateDocumentation.php index eb49f0b8..826fcfde 100644 --- a/src/Mpociot/ApiDoc/Commands/GenerateDocumentation.php +++ b/src/Mpociot/ApiDoc/Commands/GenerateDocumentation.php @@ -272,7 +272,7 @@ private function processLaravelRoutes(AbstractGenerator $generator, $allowedRout $parsedRoutes = []; foreach ($routes as $route) { if (in_array($route->getName(), $allowedRoutes) - || (str_is($routeDomain, $generator->getDomain($route)) + || (str_is($routeDomain, $generator->getDomain($route)) && str_is($routePrefix, $generator->getUri($route))) || in_array($middleware, $route->middleware()) ) { @@ -303,11 +303,11 @@ private function processDingoRoutes(AbstractGenerator $generator, $allowedRoutes $bindings = $this->getBindings(); $parsedRoutes = []; foreach ($routes as $route) { - if (empty($allowedRoutes) + if (empty($allowedRoutes) // TODO extract this into a method || in_array($route->getName(), $allowedRoutes) - || (str_is($routeDomain, $generator->getDomain($route)) - && str_is($routePrefix, $generator->getUri($route))) + || (str_is($routeDomain, $generator->getDomain($route)) + && str_is($routePrefix, $generator->getUri($route))) || in_array($middleware, $route->middleware()) ) { if ($this->isValidRoute($route) && $this->isRouteVisibleForDocumentation($route->getAction()['uses'])) { diff --git a/src/Mpociot/ApiDoc/Generators/LaravelGenerator.php b/src/Mpociot/ApiDoc/Generators/LaravelGenerator.php index 2571ac10..60330f59 100644 --- a/src/Mpociot/ApiDoc/Generators/LaravelGenerator.php +++ b/src/Mpociot/ApiDoc/Generators/LaravelGenerator.php @@ -2,17 +2,15 @@ namespace Mpociot\ApiDoc\Generators; -use Illuminate\Foundation\Http\FormRequest; +use ReflectionClass; +use League\Fractal\Manager; use Illuminate\Routing\Route; -use Illuminate\Routing\RouteUrlGenerator; +use League\Fractal\Resource\Item; use Illuminate\Support\Facades\App; +use Mpociot\Reflection\DocBlock\Tag; use Illuminate\Support\Facades\Request; -use Illuminate\Support\Facades\URL; -use League\Fractal\Manager; use League\Fractal\Resource\Collection; -use League\Fractal\Resource\Item; -use Mpociot\Reflection\DocBlock\Tag; -use ReflectionClass; +use Illuminate\Foundation\Http\FormRequest; class LaravelGenerator extends AbstractGenerator { @@ -26,7 +24,6 @@ public function getDomain($route) return $route->domain(); } - /** * @param Route $route * From f92322729814a39a379233dd7c2d44d0720630ec Mon Sep 17 00:00:00 2001 From: shalvah Date: Sun, 9 Sep 2018 20:29:10 +0100 Subject: [PATCH 163/780] Update tests, ignore IDE files --- .gitignore | 3 +++ .travis.yml | 4 ++-- composer.json | 3 +++ tests/GenerateDocumentationTest.php | 2 +- 4 files changed, 9 insertions(+), 3 deletions(-) diff --git a/.gitignore b/.gitignore index 338893d3..8532760a 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,6 @@ composer.lock .php_cs.cache /vendor/ /public +.idea/ +coverage.xml +results.xml diff --git a/.travis.yml b/.travis.yml index dd082e4c..36f2c9ed 100644 --- a/.travis.yml +++ b/.travis.yml @@ -10,10 +10,10 @@ before_script: - travis_retry composer install --prefer-dist --no-interaction script: - - vendor/bin/phpunit --coverage-clover=coverage.xml + - composer test-ci before_install: - pip install --user codecov after_success: - - codecov \ No newline at end of file + - codecov diff --git a/composer.json b/composer.json index c4ce211d..a7fa4a08 100644 --- a/composer.json +++ b/composer.json @@ -38,6 +38,9 @@ "Mpociot\\ApiDoc\\Tests\\": "tests/" } }, + "scripts": { + "test-ci": "phpunit --coverage-clover=coverage.xml" + }, "extra": { "laravel": { "providers": [ diff --git a/tests/GenerateDocumentationTest.php b/tests/GenerateDocumentationTest.php index 41cf492e..09af58a7 100644 --- a/tests/GenerateDocumentationTest.php +++ b/tests/GenerateDocumentationTest.php @@ -51,7 +51,7 @@ protected function getPackageProviders($app) public function testConsoleCommandNeedsAPrefixOrRoute() { $output = $this->artisan('api:generate'); - $this->assertEquals('You must provide either a route prefix or a route or a middleware to generate the documentation.'.PHP_EOL, $output); + $this->assertEquals('You must provide either a route prefix, a route domain, a route or a middleware to generate the documentation.'.PHP_EOL, $output); } public function testConsoleCommandDoesNotWorkWithClosure() From 2c4ba45935ba418abfd30fadd41c252561529975 Mon Sep 17 00:00:00 2001 From: shalvah Date: Sun, 9 Sep 2018 21:14:42 +0100 Subject: [PATCH 164/780] Add changelog --- CHANGELOG.md | 38 ++++++++++++++++++++++++++++++++++++++ CONTRIBUTING.md | 2 +- 2 files changed, 39 insertions(+), 1 deletion(-) create mode 100644 CHANGELOG.md diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 00000000..dfc8e8bb --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,38 @@ +# Changelog +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), +and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). If you make a pull request to this project, please update this changelog. + +## Unreleased +### Added + +### Changed + +### Fixed + +### Removed + +## [2.1.0] - 2018-09-00 +### Added +- Added support for multiple route domains (https://github.com/mpociot/laravel-apidoc-generator/pull/255) +- Added support for descriptions in custom validation rules (https://github.com/mpociot/laravel-apidoc-generator/pull/208) +- Added support for multiple route prefixes (https://github.com/mpociot/laravel-apidoc-generator/pull/203) +- Added support for formatting and `