瀏覽代碼

Merge pull request #5 from mpociot/master

Update from base
Shalvah A 6 年之前
父節點
當前提交
90d7a9f66d

+ 7 - 0
README.md

@@ -240,6 +240,13 @@ $ php artisan api:update
 
 As an optional parameter, you can use `--location` to tell the update command where your documentation can be found.
 
+## Automatically add markdown to the beginning or end of the documentation
+ If you wish to automatically add the same content to the docs every time you generate, you can add a `prepend.md` and/or `append.md` file to the source folder, and they will be included above and below the generated documentation.
+ 
+ **File locations:**
+- `public/docs/source/prepend.md` - Will be added after the front matter and info text
+- `public/docs/source/append.md` - Will be added at the end of the document
+
 ## 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.

+ 14 - 11
src/Mpociot/ApiDoc/Commands/GenerateDocumentation.php

@@ -89,17 +89,9 @@ class GenerateDocumentation extends Command
 
         $parsedRoutes = [];
 
-        if ($this->option('router') === 'laravel') {
-            foreach ($routeDomains as $routeDomain) {
-                foreach ($routePrefixes as $routePrefix) {
-                    $parsedRoutes += $this->processRoutes($generator, $allowedRoutes, $routeDomain, $routePrefix, $middleware);
-                }
-            }
-        } else {
-            foreach ($routeDomains as $routeDomain) {
-                foreach ($routePrefixes as $routePrefix) {
-                    $parsedRoutes += $this->processDingoRoutes($generator, $allowedRoutes, $routeDomain, $routePrefix, $middleware);
-                }
+        foreach ($routeDomains as $routeDomain) {
+            foreach ($routePrefixes as $routePrefix) {
+                $parsedRoutes += $this->processRoutes($generator, $allowedRoutes, $routeDomain, $routePrefix, $middleware);
             }
         }
         $parsedRoutes = collect($parsedRoutes)->groupBy('resource')->sort(function ($a, $b) {
@@ -119,6 +111,8 @@ class GenerateDocumentation extends Command
         $outputPath = $this->option('output');
         $targetFile = $outputPath.DIRECTORY_SEPARATOR.'source'.DIRECTORY_SEPARATOR.'index.md';
         $compareFile = $outputPath.DIRECTORY_SEPARATOR.'source'.DIRECTORY_SEPARATOR.'.compare.md';
+        $prependFile = $outputPath.DIRECTORY_SEPARATOR.'source'.DIRECTORY_SEPARATOR.'prepend.md';
+        $appendFile = $outputPath.DIRECTORY_SEPARATOR.'source'.DIRECTORY_SEPARATOR.'append.md';
 
         $infoText = view('apidoc::partials.info')
             ->with('outputPath', ltrim($outputPath, 'public/'))
@@ -164,12 +158,19 @@ class GenerateDocumentation extends Command
             });
         }
 
+        $prependFileContents = file_exists($prependFile)
+            ? file_get_contents($prependFile)."\n" : '';
+        $appendFileContents = file_exists($appendFile)
+            ? "\n".file_get_contents($appendFile) : '';
+
         $documentarian = new Documentarian();
 
         $markdown = view('apidoc::documentarian')
             ->with('writeCompareFile', false)
             ->with('frontmatter', $frontmatter)
             ->with('infoText', $infoText)
+            ->with('prependMd', $prependFileContents)
+            ->with('appendMd', $appendFileContents)
             ->with('outputPath', $this->option('output'))
             ->with('showPostmanCollectionButton', ! $this->option('noPostmanCollection'))
             ->with('parsedRoutes', $parsedRouteOutput);
@@ -186,6 +187,8 @@ class GenerateDocumentation extends Command
             ->with('writeCompareFile', true)
             ->with('frontmatter', $frontmatter)
             ->with('infoText', $infoText)
+            ->with('prependMd', $prependFileContents)
+            ->with('appendMd', $appendFileContents)
             ->with('outputPath', $this->option('output'))
             ->with('showPostmanCollectionButton', ! $this->option('noPostmanCollection'))
             ->with('parsedRoutes', $parsedRouteOutput);

+ 19 - 13
src/Mpociot/ApiDoc/Generators/AbstractGenerator.php

@@ -148,7 +148,8 @@ abstract class AbstractGenerator
      */
     protected function getParameters($routeData, $routeAction, $bindings)
     {
-        $rules = $this->simplifyRules($this->getRouteRules($routeData['methods'], $routeAction['uses'], $bindings));
+        $validationRules = $this->getRouteValidationRules($routeData['methods'], $routeAction['uses'], $bindings);
+        $rules = $this->simplifyRules($validationRules);
 
         foreach ($rules as $attribute => $ruleset) {
             $attributeData = [
@@ -168,7 +169,7 @@ abstract class AbstractGenerator
     }
 
     /**
-     * Format the validation rules as plain array.
+     * Format the validation rules as a plain array.
      *
      * @param array $rules
      *
@@ -176,19 +177,24 @@ abstract class AbstractGenerator
      */
     protected function simplifyRules($rules)
     {
-        $simplifiedRules = Validator::make([], $rules)->getRules();
+        // this will split all string rules into arrays of strings
+        $rules = Validator::make([], $rules)->getRules();
 
-        if (count($simplifiedRules) === 0) {
-            return $simplifiedRules;
+        if (count($rules) === 0) {
+            return $rules;
         }
 
-        $values = collect($simplifiedRules)
+        // Laravel will ignore the nested array rules unless the key referenced exists and is an array
+        // So we'll to create an empty array for each array attribute
+        $values = collect($rules)
             ->filter(function ($values) {
                 return in_array('array', $values);
             })->map(function ($val, $key) {
                 return [''];
             })->all();
 
+        // Now this will return the complete ruleset.
+        // Nested array parameters will be present, with '*' replaced by '0'
         return Validator::make($values, $rules)->getRules();
     }
 
@@ -287,10 +293,10 @@ abstract class AbstractGenerator
      *
      * @return array
      */
-    protected function getRouteRules(array $routeMethods, $routeAction, $bindings)
+    protected function getRouteValidationRules(array $routeMethods, $routeAction, $bindings)
     {
-        list($class, $method) = explode('@', $routeAction);
-        $reflection = new ReflectionClass($class);
+        list($controller, $method) = explode('@', $routeAction);
+        $reflection = new ReflectionClass($controller);
         $reflectionMethod = $reflection->getMethod($method);
 
         foreach ($reflectionMethod->getParameters() as $parameter) {
@@ -357,13 +363,13 @@ abstract class AbstractGenerator
 
     /**
      * @param  string  $rule
-     * @param  string  $ruleName
+     * @param  string  $attribute
      * @param  array  $attributeData
      * @param  int  $seed
      *
      * @return void
      */
-    protected function parseRule($rule, $ruleName, &$attributeData, $seed, $routeData)
+    protected function parseRule($rule, $attribute, &$attributeData, $seed, $routeData)
     {
         $faker = Factory::create();
         $faker->seed(crc32($seed));
@@ -510,7 +516,7 @@ abstract class AbstractGenerator
                 $attributeData['value'] = $faker->timezone;
                 break;
             case 'exists':
-                $fieldName = isset($parameters[1]) ? $parameters[1] : $ruleName;
+                $fieldName = isset($parameters[1]) ? $parameters[1] : $attribute;
                 $attributeData['description'][] = Description::parse($rule)->with([Str::singular($parameters[0]), $fieldName])->getDescription();
                 break;
             case 'active_url':
@@ -627,7 +633,7 @@ abstract class AbstractGenerator
 
         // The format for specifying validation rules and parameters follows an
         // easy {rule}:{parameters} formatting convention. For instance the
-        // rule "Max:3" states that the value may only be three letters.
+        // rule "max:3" states that the value may only be three letters.
         if (strpos($rules, ':') !== false) {
             list($rules, $parameter) = explode(':', $rules, 2);
 

+ 4 - 4
src/resources/views/documentarian.blade.php

@@ -4,16 +4,16 @@
 <!-- START_INFO -->
 {!! $infoText !!}
 <!-- END_INFO -->
-
+{!! $prependMd !!}
 @foreach($parsedRoutes as $group => $routes)
 @if($group)
 #{!! $group !!}
 @endif
 @foreach($routes as $parsedRoute)
 @if($writeCompareFile === true)
-{!! $parsedRoute['output']!!}
+{!! $parsedRoute['output'] !!}
 @else
-{!! isset($parsedRoute['modified_output']) ? $parsedRoute['modified_output'] : $parsedRoute['output']!!}
+{!! isset($parsedRoute['modified_output']) ? $parsedRoute['modified_output'] : $parsedRoute['output'] !!}
 @endif
 @endforeach
-@endforeach
+@endforeach{!! $appendMd !!}

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

@@ -10,10 +10,11 @@
 > Example request:
 
 ```bash
-curl -X {{$parsedRoute['methods'][0]}} "{{ trim(config('app.docs_url') ?: config('app.url'), '/')}}/{{ ltrim($parsedRoute['uri'], '/') }}" \
--H "Accept: application/json"@if(count($parsedRoute['parameters'])) \
+curl -X {{$parsedRoute['methods'][0]}} {{$parsedRoute['methods'][0] == 'GET' ? '-G ' : ''}}"{{ trim(config('app.docs_url') ?: config('app.url'), '/')}}/{{ ltrim($parsedRoute['uri'], '/') }}" \
+    -H "Accept: application/json"@if(count($parsedRoute['parameters'])) \
 @foreach($parsedRoute['parameters'] as $attribute => $parameter)
-    -d "{{$attribute}}"="{{$parameter['value']}}" \
+    -d "{{$attribute}}"="{{$parameter['value']}}" @if(! ($loop->last))\
+    @endif
 @endforeach
 @endif
 
@@ -26,7 +27,7 @@ var settings = {
     "url": "{{ rtrim(config('app.docs_url') ?: config('app.url'), '/') }}/{{ ltrim($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)) !!},
+"data": {!! str_replace("\n}","\n    }", 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"

+ 3 - 0
tests/Fixtures/append.md

@@ -0,0 +1,3 @@
+# Appended Markdown
+
+This markdown should be added to the end of generated docs

+ 5 - 4
tests/Fixtures/index.md

@@ -30,8 +30,8 @@ It can also be multiple lines long.
 > Example request:
 
 ```bash
-curl -X GET "http://localhost/api/test" \
--H "Accept: application/json"
+curl -X GET -G "http://localhost/api/test" \
+    -H "Accept: application/json"
 ```
 
 ```javascript
@@ -68,8 +68,8 @@ null
 > Example request:
 
 ```bash
-curl -X GET "http://localhost/api/fetch" \
--H "Accept: application/json"
+curl -X GET -G "http://localhost/api/fetch" \
+    -H "Accept: application/json"
 ```
 
 ```javascript
@@ -106,3 +106,4 @@ $.ajax(settings).done(function (response) {
 
 <!-- END_960a1b2b0f0f4dde8ce993307397f9c4 -->
 
+

+ 3 - 0
tests/Fixtures/prepend.md

@@ -0,0 +1,3 @@
+# Prepended Markdown
+
+This markdown should be added to the start of generated docs

+ 12 - 11
tests/Fixtures/resource_index.md

@@ -27,8 +27,8 @@ Welcome to the generated API reference.
 > Example request:
 
 ```bash
-curl -X GET "http://localhost/api/user" \
--H "Accept: application/json"
+curl -X GET -G "http://localhost/api/user" \
+    -H "Accept: application/json"
 ```
 
 ```javascript
@@ -67,8 +67,8 @@ $.ajax(settings).done(function (response) {
 > Example request:
 
 ```bash
-curl -X GET "http://localhost/api/user/create" \
--H "Accept: application/json"
+curl -X GET -G "http://localhost/api/user/create" \
+    -H "Accept: application/json"
 ```
 
 ```javascript
@@ -108,7 +108,7 @@ $.ajax(settings).done(function (response) {
 
 ```bash
 curl -X POST "http://localhost/api/user" \
--H "Accept: application/json"
+    -H "Accept: application/json"
 ```
 
 ```javascript
@@ -140,8 +140,8 @@ $.ajax(settings).done(function (response) {
 > Example request:
 
 ```bash
-curl -X GET "http://localhost/api/user/{user}" \
--H "Accept: application/json"
+curl -X GET -G "http://localhost/api/user/{user}" \
+    -H "Accept: application/json"
 ```
 
 ```javascript
@@ -180,8 +180,8 @@ $.ajax(settings).done(function (response) {
 > Example request:
 
 ```bash
-curl -X GET "http://localhost/api/user/{user}/edit" \
--H "Accept: application/json"
+curl -X GET -G "http://localhost/api/user/{user}/edit" \
+    -H "Accept: application/json"
 ```
 
 ```javascript
@@ -221,7 +221,7 @@ $.ajax(settings).done(function (response) {
 
 ```bash
 curl -X PUT "http://localhost/api/user/{user}" \
--H "Accept: application/json"
+    -H "Accept: application/json"
 ```
 
 ```javascript
@@ -256,7 +256,7 @@ $.ajax(settings).done(function (response) {
 
 ```bash
 curl -X DELETE "http://localhost/api/user/{user}" \
--H "Accept: application/json"
+    -H "Accept: application/json"
 ```
 
 ```javascript
@@ -282,3 +282,4 @@ $.ajax(settings).done(function (response) {
 
 <!-- END_4bb7fb4a7501d3cb1ed21acfc3b205a9 -->
 
+

+ 25 - 2
tests/GenerateDocumentationTest.php

@@ -123,8 +123,8 @@ class GenerateDocumentationTest extends TestCase
             '--routePrefix' => 'api/*',
         ]);
         $fixtureMarkdown = __DIR__.'/Fixtures/resource_index.md';
-        $gneratedMarkdown = __DIR__.'/../public/docs/source/index.md';
-        $this->assertFilesHaveSameContent($fixtureMarkdown, $gneratedMarkdown);
+        $generatedMarkdown = __DIR__.'/../public/docs/source/index.md';
+        $this->assertFilesHaveSameContent($fixtureMarkdown, $generatedMarkdown);
     }
 
     public function testGeneratedMarkdownFileIsCorrect()
@@ -143,6 +143,29 @@ class GenerateDocumentationTest extends TestCase
         $this->assertFilesHaveSameContent($fixtureMarkdown, $compareMarkdown);
     }
 
+    public function testCanPrependAndAppendDataToGeneratedMarkdown()
+    {
+        RouteFacade::get('/api/test', TestController::class.'@parseMethodDescription');
+        RouteFacade::get('/api/fetch', TestController::class.'@fetchRouteResponse');
+
+        $this->artisan('api:generate', [
+            '--routePrefix' => 'api/*',
+        ]);
+
+        $prependMarkdown = __DIR__.'/Fixtures/prepend.md';
+        $appendMarkdown = __DIR__.'/Fixtures/append.md';
+        copy($prependMarkdown, __DIR__.'/../public/docs/source/prepend.md');
+        copy($appendMarkdown, __DIR__.'/../public/docs/source/append.md');
+
+        $this->artisan('api:generate', [
+            '--routePrefix' => 'api/*',
+        ]);
+
+        $generatedMarkdown = __DIR__.'/../public/docs/source/index.md';
+        $this->assertContainsRaw($this->getFileContents($prependMarkdown), $this->getFileContents($generatedMarkdown));
+        $this->assertContainsRaw($this->getFileContents($appendMarkdown), $this->getFileContents($generatedMarkdown));
+    }
+
     public function testAddsBindingsToGetRouteRules()
     {
         RouteFacade::get('/api/test/{foo}', TestController::class.'@addRouteBindingsToRequestClass');

+ 2 - 2
tests/RuleDescriptionParserTest.php

@@ -112,7 +112,7 @@ class RuleDescriptionParserTest extends TestCase
      */
     protected function getEnvironmentSetUp($app)
     {
-        $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.
+        $app['config']->set('app.locale', 'es'); // Just to be different from default language.
+        $app['config']->set('app.fallback_locale', 'ch'); // Just to be different from default language.
     }
 }