Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 10 additions & 41 deletions src/Mpociot/ApiDoc/Commands/GenerateDocumentation.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,16 @@
namespace Mpociot\ApiDoc\Commands;

use ReflectionClass;
use Illuminate\Routing\Route;
use Illuminate\Console\Command;
use Mpociot\Reflection\DocBlock;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Route;
use Mpociot\Documentarian\Documentarian;
use Mpociot\ApiDoc\Postman\CollectionWriter;
use Mpociot\ApiDoc\Generators\DingoGenerator;
use Mpociot\ApiDoc\Generators\LaravelGenerator;
use Mpociot\ApiDoc\Generators\AbstractGenerator;
use Illuminate\Support\Facades\Route as RouteFacade;

class GenerateDocumentation extends Command
{
Expand Down Expand Up @@ -91,7 +92,7 @@ public function handle()
if ($this->option('router') === 'laravel') {
foreach ($routeDomains as $routeDomain) {
foreach ($routePrefixes as $routePrefix) {
$parsedRoutes += $this->processLaravelRoutes($generator, $allowedRoutes, $routeDomain, $routePrefix, $middleware);
$parsedRoutes += $this->processRoutes($generator, $allowedRoutes, $routeDomain, $routePrefix, $middleware);
}
}
} else {
Expand Down Expand Up @@ -215,6 +216,7 @@ private function getBindings()
if (empty($bindings)) {
return [];
}

$bindings = explode('|', $bindings);
$resultBindings = [];
foreach ($bindings as $binding) {
Expand Down Expand Up @@ -250,9 +252,9 @@ private function setUserToBeImpersonated($actAs)
private function getRoutes()
{
if ($this->option('router') === 'laravel') {
return Route::getRoutes();
return RouteFacade::getRoutes();
} else {
return app('Dingo\Api\Routing\Router')->getRoutes()[$this->option('routePrefix')];
return app('Dingo\Api\Routing\Router')->getRoutes();
}
}

Expand All @@ -264,13 +266,14 @@ private function getRoutes()
*
* @return array
*/
private function processLaravelRoutes(AbstractGenerator $generator, $allowedRoutes, $routeDomain, $routePrefix, $middleware)
private function processRoutes(AbstractGenerator $generator, array $allowedRoutes, $routeDomain, $routePrefix, $middleware)
{
$withResponse = $this->option('noResponseCalls') === false;
$withResponse = $this->option('noResponseCalls') == false;
$routes = $this->getRoutes();
$bindings = $this->getBindings();
$parsedRoutes = [];
foreach ($routes as $route) {
/** @var Route $route */
if (in_array($route->getName(), $allowedRoutes)
|| (str_is($routeDomain, $generator->getDomain($route))
&& str_is($routePrefix, $generator->getUri($route)))
Expand All @@ -288,46 +291,12 @@ private function processLaravelRoutes(AbstractGenerator $generator, $allowedRout
return $parsedRoutes;
}

/**
* @param AbstractGenerator $generator
* @param $allowedRoutes
* @param $routeDomain
* @param $routePrefix
*
* @return array
*/
private function processDingoRoutes(AbstractGenerator $generator, $allowedRoutes, $routeDomain, $routePrefix, $middleware)
{
$withResponse = $this->option('noResponseCalls') === false;
$routes = $this->getRoutes();
$bindings = $this->getBindings();
$parsedRoutes = [];
foreach ($routes as $route) {
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)))
|| in_array($middleware, $route->middleware())
) {
if ($this->isValidRoute($route) && $this->isRouteVisibleForDocumentation($route->getAction()['uses'])) {
$parsedRoutes[] = $generator->processRoute($route, $bindings, $this->option('header'), $withResponse && in_array('GET', $generator->getMethods($route)));
$this->info('Processed route: ['.implode(',', $generator->getMethods($route)).'] '.$route->uri());
} else {
$this->warn('Skipping route: ['.implode(',', $generator->getMethods($route)).'] '.$route->uri());
}
}
}

return $parsedRoutes;
}

/**
* @param $route
*
* @return bool
*/
private function isValidRoute($route)
private function isValidRoute(Route $route)
{
return ! is_callable($route->getAction()['uses']) && ! is_null($route->getAction()['uses']);
}
Expand Down
24 changes: 17 additions & 7 deletions src/Mpociot/ApiDoc/Generators/AbstractGenerator.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
use Illuminate\Support\Arr;
use Illuminate\Support\Str;
use League\Fractal\Manager;
use Illuminate\Routing\Route;
use Mpociot\Reflection\DocBlock;
use League\Fractal\Resource\Item;
use Mpociot\Reflection\DocBlock\Tag;
Expand All @@ -19,25 +20,34 @@
abstract class AbstractGenerator
{
/**
* @param $route
* @param Route $route
*
* @return mixed
*/
abstract public function getDomain($route);
public function getDomain(Route $route)
{
return $route->domain();
}

/**
* @param $route
* @param Route $route
*
* @return mixed
*/
abstract public function getUri($route);
public function getUri(Route $route)
{
return $route->uri();
}

/**
* @param $route
* @param Route $route
*
* @return mixed
*/
abstract public function getMethods($route);
public function getMethods(Route $route)
{
return array_diff($route->methods(), ['HEAD']);
}

/**
* @param \Illuminate\Routing\Route $route
Expand Down Expand Up @@ -77,7 +87,7 @@ public function processRoute($route, $bindings = [], $headers = [], $withRespons
try {
$response = $this->getRouteResponse($route, $bindings, $headers);
} catch (\Exception $e) {
dump("Couldn't get response for route: ".implode(',', $this->getMethods($route)).'] '.$route->uri().'', $e->getMessage());
echo "Couldn't get response for route: ".implode(',', $this->getMethods($route)).$route->uri().']: '.$e->getMessage()."\n";
}
}

Expand Down
24 changes: 0 additions & 24 deletions src/Mpociot/ApiDoc/Generators/DingoGenerator.php
Original file line number Diff line number Diff line change
Expand Up @@ -30,28 +30,4 @@ public function callRoute($method, $uri, $parameters = [], $cookies = [], $files

return call_user_func_array([$dispatcher, strtolower($method)], [$uri]);
}

/**
* {@inheritdoc}
*/
public function getDomain($route)
{
return $route->domain();
}

/**
* {@inheritdoc}
*/
public function getUri($route)
{
return $route->uri();
}

/**
* {@inheritdoc}
*/
public function getMethods($route)
{
return array_diff($route->getMethods(), ['HEAD']);
}
}
14 changes: 2 additions & 12 deletions src/Mpociot/ApiDoc/Generators/LaravelGenerator.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,17 +13,7 @@ class LaravelGenerator extends AbstractGenerator
*
* @return mixed
*/
public function getDomain($route)
{
return $route->domain();
}

/**
* @param Route $route
*
* @return mixed
*/
public function getUri($route)
public function getUri(Route $route)
{
if (version_compare(app()->version(), '5.4', '<')) {
return $route->getUri();
Expand All @@ -37,7 +27,7 @@ public function getUri($route)
*
* @return mixed
*/
public function getMethods($route)
public function getMethods(Route $route)
{
if (version_compare(app()->version(), '5.4', '<')) {
$methods = $route->getMethods();
Expand Down
72 changes: 59 additions & 13 deletions tests/GenerateDocumentationTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@

namespace Mpociot\ApiDoc\Tests;

use Illuminate\Routing\Route;
use RecursiveIteratorIterator;
use RecursiveDirectoryIterator;
use Orchestra\Testbench\TestCase;
use Illuminate\Contracts\Console\Kernel;
use Dingo\Api\Provider\LaravelServiceProvider;
Expand Down Expand Up @@ -32,7 +33,20 @@ public function setUp()

public function tearDown()
{
exec('rm -rf '.__DIR__.'/../public/docs');
// delete the generated docs - compatible cross-platform
$dir = __DIR__.'/../public/docs';
if (is_dir($dir)) {
$files = new RecursiveIteratorIterator(
new RecursiveDirectoryIterator($dir, RecursiveDirectoryIterator::SKIP_DOTS),
RecursiveIteratorIterator::CHILD_FIRST
);

foreach ($files as $fileinfo) {
$todo = ($fileinfo->isDir() ? 'rmdir' : 'unlink');
$todo($fileinfo->getRealPath());
}
rmdir($dir);
}
}

/**
Expand All @@ -48,7 +62,7 @@ protected function getPackageProviders($app)
];
}

public function testConsoleCommandNeedsAPrefixOrRoute()
public function testConsoleCommandNeedsPrefixesOrDomainsOrRoutes()
{
$output = $this->artisan('api:generate');
$this->assertEquals('You must provide either a route prefix, a route domain, a route or a middleware to generate the documentation.'.PHP_EOL, $output);
Expand Down Expand Up @@ -108,9 +122,9 @@ public function testCanParseResourceRoutes()
$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);
$fixtureMarkdown = __DIR__.'/Fixtures/resource_index.md';
$gneratedMarkdown = __DIR__.'/../public/docs/source/index.md';
$this->assertFilesHaveSameContent($fixtureMarkdown, $gneratedMarkdown);
}

public function testGeneratedMarkdownFileIsCorrect()
Expand All @@ -122,11 +136,11 @@ public function testGeneratedMarkdownFileIsCorrect()
'--routePrefix' => 'api/*',
]);

$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);
$generatedMarkdown = __DIR__.'/../public/docs/source/index.md';
$compareMarkdown = __DIR__.'/../public/docs/source/.compare.md';
$fixtureMarkdown = __DIR__.'/Fixtures/index.md';
$this->assertFilesHaveSameContent($fixtureMarkdown, $generatedMarkdown);
$this->assertFilesHaveSameContent($fixtureMarkdown, $compareMarkdown);
}

public function testAddsBindingsToGetRouteRules()
Expand Down Expand Up @@ -171,8 +185,8 @@ public function testCanAppendCustomHttpHeaders()
],
]);

$generatedMarkdown = file_get_contents(__DIR__.'/../public/docs/source/index.md');
$this->assertContains('"authorization": [
$generatedMarkdown = $this->getFileContents(__DIR__.'/../public/docs/source/index.md');
$this->assertContainsRaw('"authorization": [
"customAuthToken"
],
"x-custom-header": [
Expand Down Expand Up @@ -204,4 +218,36 @@ public function artisan($command, $parameters = [])

return $this->app[Kernel::class]->output();
}

private function assertFilesHaveSameContent($pathToExpected, $pathToActual)
{
$actual = $this->getFileContents($pathToActual);
$expected = $this->getFileContents($pathToExpected);
$this->assertSame($expected, $actual);
}

/**
* Get the contents of a file in a cross-platform-compatible way.
*
* @param $path
*
* @return string
*/
private function getFileContents($path)
{
return str_replace("\r\n", "\n", file_get_contents($path));
}

/**
* Assert that a string contains another string, ignoring all whitespace.
*
* @param $needle
* @param $haystack
*/
private function assertContainsRaw($needle, $haystack)
{
$haystack = preg_replace('/\s/', '', $haystack);
$needle = preg_replace('/\s/', '', $needle);
$this->assertContains($needle, $haystack);
}
}