diff --git a/.travis.yml b/.travis.yml
index 36f2c9ed..d5889592 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,9 +1,8 @@
language: php
php:
- - 5.6
- - 7.0
- - 7.1
+ - 7.1.3
+ - 7.2
before_script:
- travis_retry composer self-update
diff --git a/composer.json b/composer.json
index a7fa4a08..bb0fa47d 100644
--- a/composer.json
+++ b/composer.json
@@ -15,22 +15,24 @@
}
],
"require": {
- "php": ">=5.5.0",
- "fzaninotto/faker": "~1.0",
- "laravel/framework": "~5.4",
+ "php": ">=7.0.0",
+ "fzaninotto/faker": "~1.8",
+ "illuminate/routing": "5.5.* || 5.6.* || 5.7.*",
+ "illuminate/support": "5.5.* 5.6.* || 5.7.*",
+ "illuminate/console": "5.5.* 5.6.* || 5.7.*",
"mpociot/documentarian": "^0.2.0",
"mpociot/reflection-docblock": "^1.0",
- "ramsey/uuid": "^3.0"
+ "ramsey/uuid": "^3.8"
},
"require-dev": {
- "orchestra/testbench": "~3.0",
- "phpunit/phpunit": "~4.0 || ~5.0",
- "dingo/api": "1.0.*@dev",
- "mockery/mockery": "^0.9.5"
+ "orchestra/testbench": "3.5.* || 3.6.* || 3.7.*",
+ "phpunit/phpunit": "^6.0.0 || ^7.4.0",
+ "dingo/api": "2.0.0-alpha1",
+ "mockery/mockery": "^1.2.0"
},
"autoload": {
- "psr-0": {
- "Mpociot\\ApiDoc": "src/"
+ "psr-4": {
+ "Mpociot\\ApiDoc\\": "src/"
}
},
"autoload-dev": {
diff --git a/config/apidoc.php b/config/apidoc.php
new file mode 100644
index 00000000..f2ff1585
--- /dev/null
+++ b/config/apidoc.php
@@ -0,0 +1,95 @@
+ 'public/docs',
+
+
+ /*
+ * The router to be used (Laravel or Dingo).
+ */
+ 'router' => 'laravel',
+
+ /*
+ * Generate a Postman collection in addition to HTML docs.
+ */
+ 'postman' => true,
+
+
+ /*
+ * The routes for which documentation should be generated.
+ * Each group contains rules defining which routes should be included ('match', 'include' and 'exclude' sections)
+ * and rules which should be applied to them ('apply' section).
+ */
+ 'routes' => [
+ [
+ /*
+ * Specify conditions to determine what routes will be parsed in this group.
+ * A route must fulfill ALL conditions to pass.
+ */
+ 'match' => [
+
+ /*
+ * Match only routes whose domains match this pattern (use * as a wildcard to match any characters).
+ */
+ 'domains' => [
+ '*',
+ // 'domain1.*',
+ ],
+
+ /*
+ * Match only routes whose paths match this pattern (use * as a wildcard to match any characters).
+ */
+ 'prefixes' => [
+ '*',
+ // 'users/*',
+ ],
+
+ /*
+ * Match only routes registered under this version. This option is ignored for Laravel router.
+ * Note that wildcards are not supported.
+ */
+ 'versions' => [
+ 'v1',
+ ],
+ ],
+
+ /*
+ * Include these routes when generating documentation,
+ * even if they did not match the rules above.
+ * Note that the route must be referenced by name here.
+ */
+ 'include' => [
+ // 'users.index',
+ ],
+
+ /*
+ * Exclude these routes when generating documentation,
+ * even if they matched the rules above.
+ * Note that the route must be referenced by name here.
+ */
+ 'exclude' => [
+ // 'users.create',
+ ],
+
+ /*
+ * Specify rules to be applied to all the routes in this group when generating documentation
+ */
+ 'apply' => [
+ 'requests' => [
+
+ /*
+ * Specify headers to be added to the example requests
+ */
+ 'headers' => [
+ // 'Authorization' => 'Bearer: {token}',
+ // 'Api-Version' => 'v2',
+ ],
+ ],
+ ],
+ ],
+ ],
+];
diff --git a/phpunit.xml b/phpunit.xml
index 1458c056..8875a97f 100644
--- a/phpunit.xml
+++ b/phpunit.xml
@@ -7,8 +7,7 @@
convertNoticesToExceptions="true"
convertWarningsToExceptions="true"
processIsolation="false"
- stopOnFailure="true"
- syntaxCheck="false">
+ stopOnFailure="true">
tests/
@@ -16,10 +15,10 @@
- src/Mpociot/
+ src/
- src/Mpociot/ApiDoc/ApiDocGeneratorServiceProvider.php
- src/resources/views/documentarian.blade.php
+ src/ApiDocGeneratorServiceProvider.php
+ resources/views/documentarian.blade.php
diff --git a/src/resources/views/documentarian.blade.php b/resources/views/documentarian.blade.php
similarity index 100%
rename from src/resources/views/documentarian.blade.php
rename to resources/views/documentarian.blade.php
diff --git a/src/resources/views/partials/frontmatter.blade.php b/resources/views/partials/frontmatter.blade.php
similarity index 100%
rename from src/resources/views/partials/frontmatter.blade.php
rename to resources/views/partials/frontmatter.blade.php
diff --git a/src/resources/views/partials/info.blade.php b/resources/views/partials/info.blade.php
similarity index 100%
rename from src/resources/views/partials/info.blade.php
rename to resources/views/partials/info.blade.php
diff --git a/src/resources/views/partials/route.blade.php b/resources/views/partials/route.blade.php
similarity index 100%
rename from src/resources/views/partials/route.blade.php
rename to resources/views/partials/route.blade.php
diff --git a/src/ApiDocGeneratorServiceProvider.php b/src/ApiDocGeneratorServiceProvider.php
new file mode 100644
index 00000000..ab62dcac
--- /dev/null
+++ b/src/ApiDocGeneratorServiceProvider.php
@@ -0,0 +1,45 @@
+loadViewsFrom(__DIR__ . '/../resources/views/', 'apidoc');
+
+ $this->publishes([
+ __DIR__ . '/../resources/views' => app()->basePath().'/resources/views/vendor/apidoc',
+ ], 'views');
+
+ $this->publishes([
+ __DIR__.'/../config/apidoc.php' => config_path('apidoc.php'),
+ ], 'config');
+
+ if ($this->app->runningInConsole()) {
+ $this->commands([
+ GenerateDocumentation::class,
+ UpdateDocumentation::class,
+ ]);
+ }
+ }
+
+ /**
+ * Register the API doc commands.
+ *
+ * @return void
+ */
+ public function register()
+ {
+ //
+ }
+}
diff --git a/src/Mpociot/ApiDoc/Commands/GenerateDocumentation.php b/src/Commands/GenerateDocumentation.php
similarity index 63%
rename from src/Mpociot/ApiDoc/Commands/GenerateDocumentation.php
rename to src/Commands/GenerateDocumentation.php
index 391b32c1..7b32afb7 100644
--- a/src/Mpociot/ApiDoc/Commands/GenerateDocumentation.php
+++ b/src/Commands/GenerateDocumentation.php
@@ -2,6 +2,7 @@
namespace Mpociot\ApiDoc\Commands;
+use Mpociot\ApiDoc\Tools\RouteMatcher;
use ReflectionClass;
use Illuminate\Routing\Route;
use Illuminate\Console\Command;
@@ -12,7 +13,6 @@
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
{
@@ -22,21 +22,7 @@ class GenerateDocumentation extends Command
* @var string
*/
protected $signature = 'apidoc:generate
- {--output=public/docs : The output path for the generated documentation}
- {--routeDomain= : The route domain (or domains) to use for generation}
- {--routePrefix= : The route prefix (or prefixes) 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}
- {--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}
- {--bindings= : Route Model Bindings}
- {--header=* : Custom HTTP headers to add to the example requests. Separate the header name and value with ":"}
';
/**
@@ -46,14 +32,13 @@ class GenerateDocumentation extends Command
*/
protected $description = 'Generate your API documentation from existing Laravel routes.';
- /**
- * Create a new command instance.
- *
- * @return void
- */
- public function __construct()
+
+ private $routeMatcher;
+
+ public function __construct(RouteMatcher $routeMatcher)
{
parent::__construct();
+ $this->routeMatcher = $routeMatcher;
}
/**
@@ -63,39 +48,21 @@ public function __construct()
*/
public function handle()
{
+ $routes = config('apidoc.router') == 'dingo'
+ ? $this->routeMatcher->getDingoRoutesToBeDocumented(config('apidoc.routes'))
+ : $this->routeMatcher->getLaravelRoutesToBeDocumented(config('apidoc.routes'));
+
if ($this->option('router') === 'laravel') {
$generator = new LaravelGenerator();
} else {
$generator = new DingoGenerator();
}
- $allowedRoutes = $this->option('routes');
- $routeDomain = $this->option('routeDomain');
- $routePrefix = $this->option('routePrefix');
- $middleware = $this->option('middleware');
-
- $this->setUserToBeImpersonated($this->option('actAsUserId'));
-
- if ($routePrefix === null && $routeDomain === null && ! count($allowedRoutes) && $middleware === null) {
- $this->error('You must provide either a route prefix, a route domain, a route or a middleware to generate the documentation.');
-
- return false;
- }
-
- $generator->prepareMiddleware($this->option('useMiddlewares'));
-
- $routePrefixes = explode(',', $routePrefix ?: '*');
- $routeDomains = explode(',', $routeDomain ?: '*');
-
- $parsedRoutes = [];
- 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) {
- return strcmp($a->first()['resource'], $b->first()['resource']);
+ $parsedRoutes = $this->processRoutes($generator, $routes);
+ $parsedRoutes = collect($parsedRoutes)->groupBy('resource')
+ ->sort(function ($a, $b) {
+ return strcmp($a->first()['resource'], $b->first()['resource']);
});
$this->writeMarkdown($parsedRoutes);
@@ -210,85 +177,24 @@ private function writeMarkdown($parsedRoutes)
}
}
- /**
- * @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 {
- $provider = $this->option('authProvider');
- $userModel = config("auth.providers.$provider.model");
- $user = $userModel::find($actAs);
- $this->laravel['auth']->guard($this->option('authGuard'))->setUser($user);
- }
- }
- }
-
- /**
- * @return mixed
- */
- private function getRoutes()
- {
- if ($this->option('router') === 'laravel') {
- return RouteFacade::getRoutes();
- } else {
- return app('Dingo\Api\Routing\Router')->getRoutes();
- }
- }
/**
- * @param AbstractGenerator $generator
- * @param $allowedRoutes
- * @param $routeDomain
- * @param $routePrefix
- *
+ * @param AbstractGenerator $generator
+ * @param array $routes
* @return array
+ *
*/
- private function processRoutes(AbstractGenerator $generator, array $allowedRoutes, $routeDomain, $routePrefix, $middleware)
+ private function processRoutes(AbstractGenerator $generator, array $routes)
{
- $withResponse = $this->option('noResponseCalls') == false;
- $routes = $this->getRoutes();
- $bindings = $this->getBindings();
$parsedRoutes = [];
- foreach ($routes as $route) {
+ foreach ($routes as ['route' => $route, 'apply' => $apply]) {
/** @var Route $route */
- 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 && in_array('GET', $generator->getMethods($route)));
+ $parsedRoutes[] = $generator->processRoute($route, $apply);
$this->info('Processed route: ['.implode(',', $generator->getMethods($route)).'] '.$generator->getUri($route));
} else {
$this->warn('Skipping route: ['.implode(',', $generator->getMethods($route)).'] '.$generator->getUri($route));
}
- }
}
return $parsedRoutes;
diff --git a/src/Mpociot/ApiDoc/Commands/UpdateDocumentation.php b/src/Commands/UpdateDocumentation.php
similarity index 100%
rename from src/Mpociot/ApiDoc/Commands/UpdateDocumentation.php
rename to src/Commands/UpdateDocumentation.php
diff --git a/src/Generators/AbstractGenerator.php b/src/Generators/AbstractGenerator.php
new file mode 100644
index 00000000..7b89b5c5
--- /dev/null
+++ b/src/Generators/AbstractGenerator.php
@@ -0,0 +1,371 @@
+domain() == null ? '*' : $route->domain();
+ }
+
+ /**
+ * @param Route $route
+ *
+ * @return mixed
+ */
+ public function getUri(Route $route)
+ {
+ return $route->uri();
+ }
+
+ /**
+ * @param Route $route
+ *
+ * @return mixed
+ */
+ public function getMethods(Route $route)
+ {
+ return array_diff($route->methods(), ['HEAD']);
+ }
+
+ /**
+ * @param \Illuminate\Routing\Route $route
+ * @param array $apply Rules to apply when generating documentation for this route
+ *
+ * @return array
+ */
+ public function processRoute($route, $apply = [])
+ {
+ $routeAction = $route->getAction();
+ $routeGroup = $this->getRouteGroup($routeAction['uses']);
+ $routeDescription = $this->getRouteDescription($routeAction['uses']);
+ $showresponse = null;
+
+ $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;
+ }
+ }
+
+ $content = $this->getResponseContent($response);
+
+ return [
+ 'id' => md5($this->getUri($route).':'.implode($this->getMethods($route))),
+ 'resource' => $routeGroup,
+ 'title' => $routeDescription['short'],
+ 'description' => $routeDescription['long'],
+ 'methods' => $this->getMethods($route),
+ 'uri' => $this->getUri($route),
+ 'parameters' => $this->getParametersFromDocBlock($routeAction['uses']),
+ 'response' => $content,
+ 'showresponse' => $showresponse,
+ ];
+ }
+
+ /**
+ * Prepares / Disables route middlewares.
+ *
+ * @param bool $disable
+ *
+ * @return void
+ */
+ abstract public function prepareMiddleware($enable = 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()), 200, ['Content-Type' => 'application/json']);
+ }
+
+ /**
+ * @param array $routeAction
+ * @return array
+ */
+ protected function getParametersFromDocBlock($routeAction)
+ {
+ return [];
+ }
+
+ /**
+ * @param $route
+ * @param $bindings
+ * @param $headers
+ *
+ * @return \Illuminate\Http\Response
+ */
+ protected function getRouteResponse($route, $bindings, $headers = [])
+ {
+ $uri = $this->addRouteModelBindings($route, $bindings);
+
+ $methods = $this->getMethods($route);
+
+ // Split headers into key - value pairs
+ $headers = collect($headers)->map(function ($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($key) => trim($value)];
+ })->collapse()->toArray();
+
+ //Changes url with parameters like /users/{user} to /users/1
+ $uri = preg_replace('/{(.*?)}/', 1, $uri); // 1 is the default value for route parameters
+
+ return $this->callRoute(array_shift($methods), $uri, [], [], [], $headers);
+ }
+
+ /**
+ * @param $route
+ * @param array $bindings
+ *
+ * @return mixed
+ */
+ 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;
+ }
+
+ /**
+ * @param \Illuminate\Routing\Route $route
+ *
+ * @return array
+ */
+ protected function getRouteDescription($route)
+ {
+ list($class, $method) = explode('@', $route);
+ $reflection = new ReflectionClass($class);
+ $reflectionMethod = $reflection->getMethod($method);
+
+ $comment = $reflectionMethod->getDocComment();
+ $phpdoc = new DocBlock($comment);
+
+ return [
+ 'short' => $phpdoc->getShortDescription(),
+ 'long' => $phpdoc->getLongDescription()->getContents(),
+ 'tags' => $phpdoc->getTags(),
+ ];
+ }
+
+ /**
+ * @param string $route
+ *
+ * @return string
+ */
+ protected function getRouteGroup($route)
+ {
+ list($class, $method) = explode('@', $route);
+ $reflection = new ReflectionClass($class);
+ $comment = $reflection->getDocComment();
+ if ($comment) {
+ $phpdoc = new DocBlock($comment);
+ foreach ($phpdoc->getTags() as $tag) {
+ if ($tag->getName() === 'resource') {
+ return $tag->getContent();
+ }
+ }
+ }
+
+ return 'general';
+ }
+
+ /**
+ * 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
+ */
+ abstract public function callRoute($method, $uri, $parameters = [], $cookies = [], $files = [], $server = [], $content = null);
+
+ /**
+ * Transform headers array to array of $_SERVER vars with HTTP_* format.
+ *
+ * @param array $headers
+ *
+ * @return array
+ */
+ protected function transformHeadersToServerVars(array $headers)
+ {
+ $server = [];
+ $prefix = 'HTTP_';
+
+ foreach ($headers as $name => $value) {
+ $name = strtr(strtoupper($name), '-', '_');
+
+ if (! Str::startsWith($name, $prefix) && $name !== 'CONTENT_TYPE') {
+ $name = $prefix.$name;
+ }
+
+ $server[$name] = $value;
+ }
+
+ return $server;
+ }
+
+ /**
+ * @param $response
+ *
+ * @return mixed
+ */
+ private function getResponseContent($response)
+ {
+ if (empty($response)) {
+ return '';
+ }
+ if ($response->headers->get('Content-Type') === 'application/json') {
+ $content = json_decode($response->getContent(), JSON_PRETTY_PRINT);
+ } else {
+ $content = $response->getContent();
+ }
+
+ return $content;
+ }
+
+ /**
+ * 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;
+ }
+ }
+}
diff --git a/src/Mpociot/ApiDoc/Generators/DingoGenerator.php b/src/Generators/DingoGenerator.php
similarity index 100%
rename from src/Mpociot/ApiDoc/Generators/DingoGenerator.php
rename to src/Generators/DingoGenerator.php
diff --git a/src/Mpociot/ApiDoc/Generators/LaravelGenerator.php b/src/Generators/LaravelGenerator.php
similarity index 100%
rename from src/Mpociot/ApiDoc/Generators/LaravelGenerator.php
rename to src/Generators/LaravelGenerator.php
diff --git a/src/Mpociot/ApiDoc/ApiDocGeneratorServiceProvider.php b/src/Mpociot/ApiDoc/ApiDocGeneratorServiceProvider.php
deleted file mode 100644
index 125f5f14..00000000
--- a/src/Mpociot/ApiDoc/ApiDocGeneratorServiceProvider.php
+++ /dev/null
@@ -1,58 +0,0 @@
-loadViewsFrom(__DIR__.'/../../resources/views/', 'apidoc');
- $this->loadTranslationsFrom(__DIR__.'/../../resources/lang', 'apidoc');
-
- $this->publishes([
- __DIR__.'/../../resources/lang' => $this->resource_path('lang/vendor/apidoc'),
- __DIR__.'/../../resources/views' => $this->resource_path('views/vendor/apidoc'),
- ]);
- }
-
- /**
- * Register the API doc commands.
- *
- * @return void
- */
- public function register()
- {
- $this->app->singleton('apidoc.generate', function () {
- return new GenerateDocumentation();
- });
- $this->app->singleton('apidoc.update', function () {
- return new UpdateDocumentation();
- });
-
- $this->commands([
- 'apidoc.generate',
- '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);
- }
-}
diff --git a/src/Mpociot/ApiDoc/Generators/AbstractGenerator.php b/src/Mpociot/ApiDoc/Generators/AbstractGenerator.php
deleted file mode 100644
index 74594489..00000000
--- a/src/Mpociot/ApiDoc/Generators/AbstractGenerator.php
+++ /dev/null
@@ -1,790 +0,0 @@
-domain();
- }
-
- /**
- * @param Route $route
- *
- * @return mixed
- */
- public function getUri(Route $route)
- {
- return $route->uri();
- }
-
- /**
- * @param Route $route
- *
- * @return mixed
- */
- public function getMethods(Route $route)
- {
- return array_diff($route->methods(), ['HEAD']);
- }
-
- /**
- * @param \Illuminate\Routing\Route $route
- * @param array $bindings
- * @param bool $withResponse
- *
- * @return array
- */
- public function processRoute($route, $bindings = [], $headers = [], $withResponse = true)
- {
- $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}";
-
- $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 && $withResponse) {
- try {
- $response = $this->getRouteResponse($route, $bindings, $headers);
- } catch (\Exception $e) {
- echo "Couldn't get response for route: ".implode(',', $this->getMethods($route)).$route->uri().']: '.$e->getMessage()."\n";
- }
- }
-
- $content = $this->getResponseContent($response);
-
- return $this->getParameters([
- 'id' => md5($this->getUri($route).':'.implode($this->getMethods($route))),
- 'resource' => $routeGroup,
- 'title' => $routeDescription['short'],
- 'description' => $routeDescription['long'],
- 'methods' => $this->getMethods($route),
- 'uri' => $this->getUri($route),
- 'parameters' => [],
- 'response' => $content,
- 'showresponse' => $showresponse,
- ], $routeAction, $bindings);
- }
-
- /**
- * Prepares / Disables route middlewares.
- *
- * @param bool $disable
- *
- * @return void
- */
- abstract public function prepareMiddleware($enable = 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()), 200, ['Content-Type' => 'application/json']);
- }
-
- /**
- * @param array $routeData
- * @param array $routeAction
- * @param array $bindings
- *
- * @return mixed
- */
- protected function getParameters($routeData, $routeAction, $bindings)
- {
- $validationRules = $this->getRouteValidationRules($routeData['methods'], $routeAction['uses'], $bindings);
- $rules = $this->simplifyRules($validationRules);
-
- foreach ($rules as $attribute => $ruleset) {
- $attributeData = [
- 'required' => false,
- 'type' => null,
- 'default' => '',
- 'value' => '',
- 'description' => [],
- ];
- foreach ($ruleset as $rule) {
- $this->parseRule($rule, $attribute, $attributeData, $routeData['id'], $routeData);
- }
- $routeData['parameters'][$attribute] = $attributeData;
- }
-
- return $routeData;
- }
-
- /**
- * Format the validation rules as a plain array.
- *
- * @param array $rules
- *
- * @return array
- */
- protected function simplifyRules($rules)
- {
- // this will split all string rules into arrays of strings
- $newRules = Validator::make([], $rules)->getRules();
-
- // Laravel will ignore the nested array rules unless the key referenced exists and is an array
- // So we'll create an empty array for each array attribute
- $values = collect($newRules)
- ->filter(function ($values) {
- return in_array('array', $values);
- })->map(function ($val, $key) {
- return [str_random()];
- })->all();
-
- // Now this will return the complete ruleset.
- // Nested array parameters will be present, with '*' replaced by '0'
- return Validator::make($values, $rules)->getRules();
- }
-
- /**
- * @param $route
- * @param $bindings
- * @param $headers
- *
- * @return \Illuminate\Http\Response
- */
- protected function getRouteResponse($route, $bindings, $headers = [])
- {
- $uri = $this->addRouteModelBindings($route, $bindings);
-
- $methods = $this->getMethods($route);
-
- // Split headers into key - value pairs
- $headers = collect($headers)->map(function ($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($key) => trim($value)];
- })->collapse()->toArray();
-
- //Changes url with parameters like /users/{user} to /users/1
- $uri = preg_replace('/{(.*?)}/', 1, $uri); // 1 is the default value for route parameters
-
- return $this->callRoute(array_shift($methods), $uri, [], [], [], $headers);
- }
-
- /**
- * @param $route
- * @param array $bindings
- *
- * @return mixed
- */
- 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;
- }
-
- /**
- * @param \Illuminate\Routing\Route $route
- *
- * @return array
- */
- protected function getRouteDescription($route)
- {
- list($class, $method) = explode('@', $route);
- $reflection = new ReflectionClass($class);
- $reflectionMethod = $reflection->getMethod($method);
-
- $comment = $reflectionMethod->getDocComment();
- $phpdoc = new DocBlock($comment);
-
- return [
- 'short' => $phpdoc->getShortDescription(),
- 'long' => $phpdoc->getLongDescription()->getContents(),
- 'tags' => $phpdoc->getTags(),
- ];
- }
-
- /**
- * @param string $route
- *
- * @return string
- */
- protected function getRouteGroup($route)
- {
- list($class, $method) = explode('@', $route);
- $reflection = new ReflectionClass($class);
- $comment = $reflection->getDocComment();
- if ($comment) {
- $phpdoc = new DocBlock($comment);
- foreach ($phpdoc->getTags() as $tag) {
- if ($tag->getName() === 'resource') {
- return $tag->getContent();
- }
- }
- }
-
- return 'general';
- }
-
- /**
- * @param array $routeMethods
- * @param string $routeAction
- * @param array $bindings
- *
- * @return array
- */
- protected function getRouteValidationRules(array $routeMethods, $routeAction, $bindings)
- {
- list($controller, $method) = explode('@', $routeAction);
- $reflection = new ReflectionClass($controller);
- $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)) {
- /** @var FormRequest $formRequest */
- $formRequest = new $className;
- // Add route parameter bindings
- $formRequest->setContainer(app());
- $formRequest->request->add($bindings);
- $formRequest->query->add($bindings);
- $formRequest->setMethod($routeMethods[0]);
-
- if (method_exists($formRequest, 'validator')) {
- $factory = app(ValidationFactory::class);
-
- return call_user_func_array([$formRequest, 'validator'], [$factory])
- ->getRules();
- } else {
- return call_user_func_array([$formRequest, 'rules'], []);
- }
- }
- }
- }
-
- return [];
- }
-
- /**
- * @param array $arr
- * @param string $first
- * @param string $last
- *
- * @return string
- */
- protected function fancyImplode($arr, $first, $last)
- {
- $arr = array_map(function ($value) {
- return '`'.$value.'`';
- }, $arr);
- array_push($arr, implode($last, array_splice($arr, -2)));
-
- 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 $attribute
- * @param array $attributeData
- * @param int $seed
- *
- * @return void
- */
- protected function parseRule($rule, $attribute, &$attributeData, $seed, $routeData)
- {
- $faker = Factory::create();
- $faker->seed(crc32($seed));
-
- $parsedRule = $this->parseStringRule($rule);
- $parsedRule[0] = $this->normalizeRule($parsedRule[0]);
- list($rule, $parameters) = $parsedRule;
-
- switch ($rule) {
- case 'required':
- $attributeData['required'] = true;
- break;
- case 'accepted':
- $attributeData['required'] = true;
- $attributeData['type'] = 'boolean';
- $attributeData['value'] = true;
- break;
- case 'after':
- $attributeData['type'] = 'date';
- $format = isset($attributeData['format']) ? $attributeData['format'] : DATE_RFC850;
-
- if (strtotime($parameters[0]) === false) {
- // the `after` date refers to another parameter in the request
- $paramName = $parameters[0];
- $attributeData['description'][] = Description::parse($rule)->with($paramName)->getDescription();
- $attributeData['value'] = date($format, strtotime('+1 day', strtotime($routeData['parameters'][$paramName]['value'])));
- } else {
- $attributeData['description'][] = Description::parse($rule)->with(date($format, strtotime($parameters[0])))->getDescription();
- $attributeData['value'] = date($format, strtotime('+1 day', strtotime($parameters[0])));
- }
- break;
- case 'alpha':
- $attributeData['description'][] = Description::parse($rule)->getDescription();
- $attributeData['value'] = $faker->word;
- break;
- case 'alpha_dash':
- $attributeData['description'][] = Description::parse($rule)->getDescription();
- break;
- case 'alpha_num':
- $attributeData['description'][] = Description::parse($rule)->getDescription();
- break;
- case 'in':
- $attributeData['description'][] = Description::parse($rule)->with($this->fancyImplode($parameters, ', ', ' or '))->getDescription();
- $attributeData['value'] = $faker->randomElement($parameters);
- break;
- case 'not_in':
- $attributeData['description'][] = Description::parse($rule)->with($this->fancyImplode($parameters, ', ', ' or '))->getDescription();
- $attributeData['value'] = $faker->word;
- 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'])) {
- $attributeData['type'] = 'numeric';
- }
- $attributeData['description'][] = Description::parse($rule)->with($parameters)->getDescription();
- $attributeData['value'] = $faker->numberBetween($parameters[0], $parameters[1]);
- break;
- case 'before':
- $attributeData['type'] = 'date';
- $format = isset($attributeData['format']) ? $attributeData['format'] : DATE_RFC850;
-
- if (strtotime($parameters[0]) === false) {
- // the `before` date refers to another parameter in the request
- $paramName = $parameters[0];
- $attributeData['description'][] = Description::parse($rule)->with($paramName)->getDescription();
- $attributeData['value'] = date($format, strtotime('-1 day', strtotime($routeData['parameters'][$paramName]['value'])));
- } else {
- $attributeData['description'][] = Description::parse($rule)->with(date($format, strtotime($parameters[0])))->getDescription();
- $attributeData['value'] = date($format, strtotime('-1 day', strtotime($parameters[0])));
- }
- break;
- case 'date_format':
- $attributeData['type'] = 'date';
- $attributeData['description'][] = Description::parse($rule)->with($parameters)->getDescription();
- $attributeData['format'] = $parameters[0];
- $attributeData['value'] = date($attributeData['format']);
- break;
- case 'different':
- $attributeData['description'][] = Description::parse($rule)->with($parameters)->getDescription();
- break;
- case 'digits':
- $attributeData['type'] = 'numeric';
- $attributeData['description'][] = Description::parse($rule)->with($parameters)->getDescription();
- $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';
- $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();
- break;
- case 'json':
- $attributeData['type'] = 'string';
- $attributeData['description'][] = Description::parse($rule)->getDescription();
- $attributeData['value'] = json_encode(['foo', 'bar', 'baz']);
- break;
- case 'mimetypes':
- case 'mimes':
- $attributeData['description'][] = Description::parse($rule)->with($this->fancyImplode($parameters, ', ', ' or '))->getDescription();
- break;
- case 'required_if':
- $attributeData['description'][] = Description::parse($rule)->with($this->splitValuePairs($parameters))->getDescription();
- break;
- case 'required_unless':
- $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();
- break;
- case 'required_with_all':
- $attributeData['description'][] = Description::parse($rule)->with($this->fancyImplode($parameters, ', ', ' and '))->getDescription();
- break;
- case 'required_without':
- $attributeData['description'][] = Description::parse($rule)->with($this->fancyImplode($parameters, ', ', ' or '))->getDescription();
- break;
- case 'required_without_all':
- $attributeData['description'][] = Description::parse($rule)->with($this->fancyImplode($parameters, ', ', ' and '))->getDescription();
- break;
- case 'same':
- $attributeData['description'][] = Description::parse($rule)->with($parameters)->getDescription();
- break;
- case 'size':
- $attributeData['description'][] = Description::parse($rule)->with($parameters)->getDescription();
- break;
- case 'timezone':
- $attributeData['description'][] = Description::parse($rule)->getDescription();
- $attributeData['value'] = $faker->timezone;
- break;
- case 'exists':
- $fieldName = isset($parameters[1]) ? $parameters[1] : $attribute;
- $attributeData['description'][] = Description::parse($rule)->with([Str::singular($parameters[0]), $fieldName])->getDescription();
- break;
- case 'active_url':
- $attributeData['type'] = 'url';
- $attributeData['value'] = $faker->url;
- break;
- case 'regex':
- $attributeData['type'] = 'string';
- $attributeData['description'][] = Description::parse($rule)->with($parameters)->getDescription();
- break;
- case 'boolean':
- $attributeData['value'] = true;
- $attributeData['type'] = $rule;
- break;
- case 'array':
- $attributeData['value'] = [$faker->word];
- $attributeData['type'] = $rule;
- $attributeData['description'][] = Description::parse($rule)->getDescription();
- break;
- case 'date':
- $attributeData['value'] = $faker->date();
- $attributeData['type'] = $rule;
- break;
- case 'email':
- $attributeData['value'] = $faker->safeEmail;
- $attributeData['type'] = $rule;
- break;
- case 'string':
- $attributeData['value'] = $faker->word;
- $attributeData['type'] = $rule;
- break;
- case 'integer':
- $attributeData['value'] = $faker->randomNumber();
- $attributeData['type'] = $rule;
- break;
- case 'numeric':
- $attributeData['value'] = $faker->randomNumber();
- $attributeData['type'] = $rule;
- break;
- case 'url':
- $attributeData['value'] = $faker->url;
- $attributeData['type'] = $rule;
- break;
- case 'ip':
- $attributeData['value'] = $faker->ipv4;
- $attributeData['type'] = $rule;
- break;
- default:
- $unknownRuleDescription = Description::parse($rule)->getDescription();
- if ($unknownRuleDescription) {
- $attributeData['description'][] = $unknownRuleDescription;
- }
- break;
- }
-
- if ($attributeData['value'] === '') {
- $attributeData['value'] = $faker->word;
- }
-
- if (is_null($attributeData['type'])) {
- $attributeData['type'] = 'string';
- }
- }
-
- /**
- * 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
- */
- abstract public function callRoute($method, $uri, $parameters = [], $cookies = [], $files = [], $server = [], $content = null);
-
- /**
- * Transform headers array to array of $_SERVER vars with HTTP_* format.
- *
- * @param array $headers
- *
- * @return array
- */
- protected function transformHeadersToServerVars(array $headers)
- {
- $server = [];
- $prefix = 'HTTP_';
-
- foreach ($headers as $name => $value) {
- $name = strtr(strtoupper($name), '-', '_');
-
- if (! Str::startsWith($name, $prefix) && $name !== 'CONTENT_TYPE') {
- $name = $prefix.$name;
- }
-
- $server[$name] = $value;
- }
-
- return $server;
- }
-
- /**
- * Parse a string based rule.
- *
- * @param string $rules
- *
- * @return array
- */
- protected function parseStringRule($rules)
- {
- $parameters = [];
-
- // 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.
- if (strpos($rules, ':') !== false) {
- list($rules, $parameter) = explode(':', $rules, 2);
-
- $parameters = $this->parseParameters($rules, $parameter);
- }
-
- return [strtolower(trim($rules)), $parameters];
- }
-
- /**
- * Parse a parameter list.
- *
- * @param string $rule
- * @param string $parameter
- *
- * @return array
- */
- protected function parseParameters($rule, $parameter)
- {
- if (strtolower($rule) === 'regex') {
- return [$parameter];
- }
-
- return str_getcsv($parameter);
- }
-
- /**
- * Normalizes a rule so that we can accept short types.
- *
- * @param string $rule
- *
- * @return string
- */
- protected function normalizeRule($rule)
- {
- switch ($rule) {
- case 'int':
- return 'integer';
- case 'bool':
- return 'boolean';
- default:
- return $rule;
- }
- }
-
- /**
- * @param $response
- *
- * @return mixed
- */
- private function getResponseContent($response)
- {
- if (empty($response)) {
- return '';
- }
- if ($response->headers->get('Content-Type') === 'application/json') {
- $content = json_decode($response->getContent(), JSON_PRETTY_PRINT);
- } else {
- $content = $response->getContent();
- }
-
- return $content;
- }
-
- /**
- * 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;
- }
- }
-}
diff --git a/src/Mpociot/ApiDoc/Parsers/RuleDescriptionParser.php b/src/Mpociot/ApiDoc/Parsers/RuleDescriptionParser.php
deleted file mode 100644
index 06aa335c..00000000
--- a/src/Mpociot/ApiDoc/Parsers/RuleDescriptionParser.php
+++ /dev/null
@@ -1,86 +0,0 @@
-rule = "apidoc::rules.{$rule}";
- }
-
- /**
- * @return array|string
- */
- public function getDescription()
- {
- return $this->ruleDescriptionExist() ? $this->makeDescription() : [];
- }
-
- /**
- * @param string|array $parameters
- *
- * @return $this
- */
- public function with($parameters)
- {
- is_array($parameters) ?
- $this->parameters += $parameters :
- $this->parameters[] = $parameters;
-
- return $this;
- }
-
- /**
- * @return bool
- */
- protected function ruleDescriptionExist()
- {
- return trans()->hasForLocale($this->rule) || trans()->hasForLocale($this->rule, self::DEFAULT_LOCALE);
- }
-
- /**
- * @return string
- */
- protected function makeDescription()
- {
- $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) {
- $description = preg_replace('/:attribute/', $parameter, $description, 1);
- }
-
- return $description;
- }
-
- /**
- * @param null $rule
- *
- * @return static
- */
- public static function parse($rule = null)
- {
- return new static($rule);
- }
-}
diff --git a/src/Mpociot/ApiDoc/Postman/CollectionWriter.php b/src/Postman/CollectionWriter.php
similarity index 100%
rename from src/Mpociot/ApiDoc/Postman/CollectionWriter.php
rename to src/Postman/CollectionWriter.php
diff --git a/src/Tools/RouteMatcher.php b/src/Tools/RouteMatcher.php
new file mode 100644
index 00000000..fd7f00b6
--- /dev/null
+++ b/src/Tools/RouteMatcher.php
@@ -0,0 +1,74 @@
+getRoutesToBeDocumented($routeRules,true);
+ }
+
+ public function getLaravelRoutesToBeDocumented(array $routeRules)
+ {
+ return $this->getRoutesToBeDocumented($routeRules);
+ }
+
+ public function getRoutesToBeDocumented(array $routeRules, bool $usingDingoRouter = false)
+ {
+ $matchedRoutes = [];
+
+ foreach ($routeRules as $routeRule) {
+ $excludes = $routeRule['exclude'] ?? [];
+ $includes = $routeRule['include'] ?? [];
+ $allRoutes = $this->getAllRoutes($usingDingoRouter, $routeRule['match']['versions'] ?? []);
+
+ foreach ($allRoutes as $route) {
+ /** @var Route $route */
+ if (in_array($route->getName(), $excludes)) {
+ continue;
+ }
+
+ if ($this->shouldIncludeRoute($route, $routeRule, $includes, $usingDingoRouter)) {
+ $matchedRoutes[] = [
+ 'route' => $route,
+ 'apply' => $routeRule['apply'] ?? [],
+ ];
+ continue;
+ }
+ }
+ }
+
+ return $matchedRoutes;
+ }
+
+ private function getAllRoutes(bool $usingDingoRouter, array $versions = [])
+ {
+ if (!$usingDingoRouter) {
+ return RouteFacade::getRoutes();
+ }
+
+ $allRouteCollections = app(\Dingo\Api\Routing\Router::class)->getRoutes();
+ return collect($allRouteCollections)
+ ->flatMap(function (RouteCollection $collection) {
+ return $collection->getRoutes();
+ })->toArray();
+ }
+
+ private function shouldIncludeRoute(Route $route, array $routeRule, array $mustIncludes, bool $usingDingoRouter)
+ {
+ $matchesVersion = $usingDingoRouter
+ ? !empty(array_intersect($route->versions(), $routeRule['match']['versions'] ?? []))
+ : true;
+
+ return in_array($route->getName(), $mustIncludes)
+ || (str_is($routeRule['match']['domains'] ?? [], $route->getDomain())
+ && str_is($routeRule['match']['prefixes'] ?? [], $route->uri())
+ && $matchesVersion);
+ }
+
+}
diff --git a/src/resources/lang/en/rules.php b/src/resources/lang/en/rules.php
deleted file mode 100644
index 5bab0bf7..00000000
--- a/src/resources/lang/en/rules.php
+++ /dev/null
@@ -1,35 +0,0 @@
- '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',
- 'array' => 'Must be an array',
- '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`',
- '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',
- 'mimes' => 'Allowed mime types: :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.',
- '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 b3407514..95db8d4c 100644
--- a/tests/ApiDocGeneratorTest.php
+++ b/tests/ApiDocGeneratorTest.php
@@ -4,7 +4,6 @@
use Illuminate\Routing\Route;
use Orchestra\Testbench\TestCase;
-use Mpociot\ApiDoc\Tests\Fixtures\TestRequest;
use Mpociot\ApiDoc\Generators\LaravelGenerator;
use Mpociot\ApiDoc\Tests\Fixtures\TestController;
use Mpociot\ApiDoc\ApiDocGeneratorServiceProvider;
@@ -76,277 +75,6 @@ public function testCanParseDependencyInjectionInControllerMethods()
$this->assertTrue(is_array($parsed));
}
- public function testCanParseFormRequestRules()
- {
- RouteFacade::post('/post', TestController::class.'@parseFormRequestRules');
- $route = new Route(['POST'], '/post', ['uses' => TestController::class.'@parseFormRequestRules']);
- $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(1, $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 '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']);
- $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 '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 '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']);
- $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 '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']);
- $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;
-
- }
- }
- }
-
- 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/DingoGeneratorTest.php b/tests/DingoGeneratorTest.php
index 2311498d..8f09ab57 100644
--- a/tests/DingoGeneratorTest.php
+++ b/tests/DingoGeneratorTest.php
@@ -37,10 +37,6 @@ 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');
@@ -55,10 +51,6 @@ 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');
@@ -83,272 +75,4 @@ public function testCanParseRouteMethods()
$this->assertSame(['DELETE'], $parsed['methods']);
}
- 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');
- });
-
- $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 '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']);
- $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 '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 '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']);
- $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 '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']);
- $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/GenerateDocumentationTest.php b/tests/GenerateDocumentationTest.php
index 957986dd..b2ab6a81 100644
--- a/tests/GenerateDocumentationTest.php
+++ b/tests/GenerateDocumentationTest.php
@@ -34,7 +34,7 @@ public function setUp()
public function tearDown()
{
// delete the generated docs - compatible cross-platform
- $dir = __DIR__.'/../public/docs';
+ $dir = __DIR__.'/../public/docs';/*
if (is_dir($dir)) {
$files = new RecursiveIteratorIterator(
new RecursiveDirectoryIterator($dir, RecursiveDirectoryIterator::SKIP_DOTS),
@@ -46,7 +46,7 @@ public function tearDown()
$todo($fileinfo->getRealPath());
}
rmdir($dir);
- }
+ }*/
}
/**
@@ -84,20 +84,16 @@ 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 () {
+ $api->get('v1/closure', function () {
return 'foo';
});
- $api->get('/test', DingoTestController::class.'@parseMethodDescription');
+ $api->get('v1/test', DingoTestController::class.'@parseMethodDescription');
$output = $this->artisan('apidoc:generate', [
'--router' => 'dingo',
- '--routePrefix' => 'v1',
+ '--routePrefix' => 'v1/*',
]);
$this->assertContains('Skipping route: [GET] closure', $output);
$this->assertContains('Processed route: [GET] test', $output);
diff --git a/tests/RouteMatcherTest.php b/tests/RouteMatcherTest.php
new file mode 100644
index 00000000..ecdf573e
--- /dev/null
+++ b/tests/RouteMatcherTest.php
@@ -0,0 +1,364 @@
+matcher = new RouteMatcher();
+ }
+
+ protected function getPackageProviders($app)
+ {
+ return [
+ \Dingo\Api\Provider\LaravelServiceProvider::class,
+ ];
+ }
+
+ public function testRespectsDomainsRuleForLaravelRouter()
+ {
+ $this->registerLaravelRoutes();
+ $routeRules[0]['match']['prefixes'] = ['*'];
+
+ $routeRules[0]['match']['domains'] = ['*'];
+ $routes = $this->matcher->getRoutesToBeDocumented($routeRules);
+ $this->assertCount(12, $routes);
+
+ $routeRules[0]['match']['domains'] = ['domain1.*', 'domain2.*'];
+ $routes = $this->matcher->getRoutesToBeDocumented($routeRules);
+ $this->assertCount(12, $routes);
+
+ $routeRules[0]['match']['domains'] = ['domain1.*'];
+ $routes = $this->matcher->getRoutesToBeDocumented($routeRules);
+ $this->assertCount(6, $routes);
+ foreach ($routes as $route){
+ $this->assertContains('domain1', $route['route']->getDomain());
+ }
+
+ $routeRules[0]['match']['domains'] = ['domain2.*'];
+ $routes = $this->matcher->getRoutesToBeDocumented($routeRules);
+ $this->assertCount(6, $routes);
+ foreach ($routes as $route){
+ $this->assertContains('domain2', $route['route']->getDomain());
+ }
+ }
+
+ public function testRespectsDomainsRuleForDingoRouter()
+ {
+ $this->registerDingoRoutes();
+ $routeRules[0]['match']['versions'] = ['v1'];
+ $routeRules[0]['match']['prefixes'] = ['*'];
+
+ $routeRules[0]['match']['domains'] = ['*'];
+ $routes = $this->matcher->getDingoRoutesToBeDocumented($routeRules);
+ $this->assertCount(12, $routes);
+
+ $routeRules[0]['match']['domains'] = ['domain1.*', 'domain2.*'];
+ $routes = $this->matcher->getDingoRoutesToBeDocumented($routeRules);
+ $this->assertCount(12, $routes);
+
+ $routeRules[0]['match']['domains'] = ['domain1.*'];
+ $routes = $this->matcher->getDingoRoutesToBeDocumented($routeRules);
+ $this->assertCount(6, $routes);
+ foreach ($routes as $route){
+ $this->assertContains('domain1', $route['route']->getDomain());
+ }
+
+ $routeRules[0]['match']['domains'] = ['domain2.*'];
+ $routes = $this->matcher->getDingoRoutesToBeDocumented($routeRules);
+ $this->assertCount(6, $routes);
+ foreach ($routes as $route){
+ $this->assertContains('domain2', $route['route']->getDomain());
+ }
+ }
+
+ public function testRespectsPrefixesRuleForLaravelRouter()
+ {
+ $this->registerLaravelRoutes();
+ $routeRules[0]['match']['domains'] = ['*'];
+
+ $routeRules[0]['match']['prefixes'] = ['*'];
+ $routes = $this->matcher->getRoutesToBeDocumented($routeRules);
+ $this->assertCount(12, $routes);
+
+ $routeRules[0]['match']['prefixes'] = ['prefix1/*', 'prefix2/*'];
+ $routes = $this->matcher->getRoutesToBeDocumented($routeRules);
+ $this->assertCount(8, $routes);
+
+ $routeRules[0]['match']['prefixes'] = ['prefix1/*'];
+ $routes = $this->matcher->getRoutesToBeDocumented($routeRules);
+ $this->assertCount(4, $routes);
+ foreach ($routes as $route){
+ $this->assertTrue(str_is('prefix1/*', $route['route']->uri()));
+ }
+
+ $routeRules[0]['match']['prefixes'] = ['prefix2/*'];
+ $routes = $this->matcher->getRoutesToBeDocumented($routeRules);
+ $this->assertCount(4, $routes);
+ foreach ($routes as $route){
+ $this->assertTrue(str_is('prefix2/*', $route['route']->uri()));
+ }
+ }
+
+ public function testRespectsPrefixesRuleForDingoRouter()
+ {
+ $this->registerDingoRoutes();
+ $routeRules[0]['match']['versions'] = ['v1'];
+ $routeRules[0]['match']['domains'] = ['*'];
+
+ $routeRules[0]['match']['prefixes'] = ['*'];
+ $routes = $this->matcher->getDingoRoutesToBeDocumented($routeRules);
+ $this->assertCount(12, $routes);
+
+ $routeRules[0]['match']['prefixes'] = ['prefix1/*', 'prefix2/*'];
+ $routes = $this->matcher->getDingoRoutesToBeDocumented($routeRules);
+ $this->assertCount(8, $routes);
+
+ $routeRules[0]['match']['prefixes'] = ['prefix1/*'];
+ $routes = $this->matcher->getDingoRoutesToBeDocumented($routeRules);
+ $this->assertCount(4, $routes);
+ foreach ($routes as $route){
+ $this->assertTrue(str_is('prefix1/*', $route['route']->uri()));
+ }
+
+ $routeRules[0]['match']['prefixes'] = ['prefix2/*'];
+ $routes = $this->matcher->getDingoRoutesToBeDocumented($routeRules);
+ $this->assertCount(4, $routes);
+ foreach ($routes as $route){
+ $this->assertTrue(str_is('prefix2/*', $route['route']->uri()));
+ }
+ }
+
+ public function testRespectsVersionsRuleForDingoRouter()
+ {
+ $this->registerDingoRoutes();
+
+ $routeRules[0]['match']['versions'] = ['v2'];
+ $routeRules[0]['match']['domains'] = ['*'];
+ $routeRules[0]['match']['prefixes'] = ['*'];
+ $routes = $this->matcher->getDingoRoutesToBeDocumented($routeRules);
+ $this->assertCount(6, $routes);
+ foreach ($routes as $route){
+ $this->assertNotEmpty(array_intersect($route['route']->versions(), ['v2']));
+ }
+
+ $routeRules[0]['match']['versions'] = ['v1', 'v2'];
+ $routeRules[0]['match']['domains'] = ['*'];
+ $routeRules[0]['match']['prefixes'] = ['*'];
+ $routes = $this->matcher->getDingoRoutesToBeDocumented($routeRules);
+ $this->assertCount(18, $routes);
+ }
+
+ public function testWillIncludeRouteIfListedExplicitlyForLaravelRouter()
+ {
+ $this->registerLaravelRoutes();
+ $mustInclude = 'domain1-1';
+ $routeRules[0]['include'] = [$mustInclude];
+
+ $routeRules[0]['match']['domains'] = ['domain1.*'];
+ $routeRules[0]['match']['prefixes'] = ['prefix1/*'];
+ $routes = $this->matcher->getRoutesToBeDocumented($routeRules);
+ $oddRuleOut = collect($routes)->filter(function ($route) use ($mustInclude) {
+ return $route['route']->getName() === $mustInclude;
+ });
+ $this->assertCount(1, $oddRuleOut);
+ }
+
+ public function testWillIncludeRouteIfListedExplicitlyForDingoRouter()
+ {
+ $this->registerDingoRoutes();
+
+ $mustInclude = 'v2.domain2';
+ $routeRules = [
+ [
+ 'match' => [
+ 'domains' => ['domain1.*'],
+ 'prefixes' => ['prefix1/*'],
+ 'versions' => ['v1']
+ ],
+ 'include' => [$mustInclude],
+ ],
+ ];
+ $routes = $this->matcher->getDingoRoutesToBeDocumented($routeRules);
+ $oddRuleOut = collect($routes)->filter(function ($route) use ($mustInclude) {
+ return $route['route']->getName() === $mustInclude;
+ });
+ $this->assertCount(1, $oddRuleOut);
+ }
+
+ public function testWillExcludeRouteIfListedExplicitlyForLaravelRouter()
+ {
+ $this->registerLaravelRoutes();
+ $mustNotInclude = 'prefix1.domain1-1';
+ $routeRules[0]['exclude'] = [$mustNotInclude];
+
+ $routeRules[0]['match']['domains'] = ['domain1.*'];
+ $routeRules[0]['match']['prefixes'] = ['prefix1/*'];
+ $routes = $this->matcher->getRoutesToBeDocumented($routeRules);
+ $oddRuleOut = collect($routes)->filter(function ($route) use ($mustNotInclude) {
+ return $route['route']->getName() === $mustNotInclude;
+ });
+ $this->assertCount(0, $oddRuleOut);
+ }
+
+ public function testWillExcludeRouteIfListedExplicitlyForDingoRouter()
+ {
+ $this->registerDingoRoutes();
+
+ $mustNotInclude = 'v2.domain2';
+ $routeRules = [
+ [
+ 'match' => [
+ 'domains' => ['domain2.*'],
+ 'prefixes' => ['*'],
+ 'versions' => ['v2']
+ ],
+ 'exclude' => [$mustNotInclude],
+ ],
+ ];
+ $routes = $this->matcher->getDingoRoutesToBeDocumented($routeRules);
+ $oddRuleOut = collect($routes)->filter(function ($route) use ($mustNotInclude) {
+ return $route['route']->getName() === $mustNotInclude;
+ });
+ $this->assertCount(0, $oddRuleOut);
+ }
+
+ public function testMergesRoutesFromDifferentRuleGroupsForLaravelRouter()
+ {
+ $this->registerLaravelRoutes();
+
+ $routeRules = [
+ [
+ 'match' => [
+ 'domains' => ['domain1.*'],
+ 'prefixes' => ['prefix1/*'],
+ ],
+ ],
+ [
+ 'match' => [
+ 'domains' => ['domain2.*'],
+ 'prefixes' => ['prefix2*'],
+ ],
+ ],
+ ];
+
+ $routes = $this->matcher->getRoutesToBeDocumented($routeRules);
+ $this->assertCount(4, $routes);
+
+ $routes = collect($routes);
+ $firstRuleGroup = $routes->filter(function ($route) {
+ return str_is('prefix1/*', $route['route']->uri())
+ && str_is('domain1.*', $route['route']->getDomain());
+ });
+ $this->assertCount(2, $firstRuleGroup);
+
+ $secondRuleGroup = $routes->filter(function ($route) {
+ return str_is('prefix2/*', $route['route']->uri())
+ && str_is('domain2.*', $route['route']->getDomain());
+ });
+ $this->assertCount(2, $secondRuleGroup);
+ }
+
+ public function testMergesRoutesFromDifferentRuleGroupsForDingoRouter()
+ {
+ $this->registerDingoRoutes();
+ $routeRules = [
+ [
+ 'match' => [
+ 'domains' => ['*'],
+ 'prefixes' => ['*'],
+ 'versions' => ['v1'],
+ ],
+ ],
+ [
+ 'match' => [
+ 'domains' => ['*'],
+ 'prefixes' => ['*'],
+ 'versions' => ['v2'],
+ ],
+ ],
+ ];
+
+ $routes = $this->matcher->getDingoRoutesToBeDocumented($routeRules);
+ $this->assertCount(18, $routes);
+
+ $routes = collect($routes);
+ $firstRuleGroup = $routes->filter(function ($route) {
+ return !empty(array_intersect($route['route']->versions(), ['v1']));
+ });
+ $this->assertCount(12, $firstRuleGroup);
+
+ $secondRuleGroup = $routes->filter(function ($route) {
+ return !empty(array_intersect($route['route']->versions(), ['v2']));
+ });
+ $this->assertCount(6, $secondRuleGroup);
+ }
+
+ private function registerLaravelRoutes()
+ {
+ RouteFacade::group(['domain' => 'domain1.app.test'], function () {
+ RouteFacade::post('/domain1-1', function () { return 'hi'; })->name('domain1-1');
+ RouteFacade::get('domain1-2', function () { return 'hi'; })->name('domain1-2');
+ RouteFacade::get('/prefix1/domain1-1', function () { return 'hi'; })->name('prefix1.domain1-1');
+ RouteFacade::get('prefix1/domain1-2', function () { return 'hi'; })->name('prefix1.domain1-2');
+ RouteFacade::get('/prefix2/domain1-1', function () { return 'hi'; })->name('prefix2.domain1-1');
+ RouteFacade::get('prefix2/domain1-2', function () { return 'hi'; })->name('prefix2.domain1-2');
+ });
+ RouteFacade::group(['domain' => 'domain2.app.test'], function () {
+ RouteFacade::post('/domain2-1', function () { return 'hi'; })->name('domain2-1');
+ RouteFacade::get('domain2-2', function () { return 'hi'; })->name('domain2-2');
+ RouteFacade::get('/prefix1/domain2-1', function () { return 'hi'; })->name('prefix1.domain2-1');
+ RouteFacade::get('prefix1/domain2-2', function () { return 'hi'; })->name('prefix1.domain2-2');
+ RouteFacade::get('/prefix2/domain2-1', function () { return 'hi'; })->name('prefix2.domain2-1');
+ RouteFacade::get('prefix2/domain2-2', function () { return 'hi'; })->name('prefix2.domain2-2');
+ });
+ }
+
+ private function registerDingoRoutes()
+ {
+
+ $api = app('api.router');
+ $api->version('v1', function (Router $api) {
+ $api->group(['domain' => 'domain1.app.test'], function (Router $api) {
+ $api->post('/domain1-1', function () { return 'hi'; })->name('v1.domain1-1');
+ $api->get('domain1-2', function () { return 'hi'; })->name('v1.domain1-2');
+ $api->get('/prefix1/domain1-1', function () { return 'hi'; })->name('v1.prefix1.domain1-1');
+ $api->get('prefix1/domain1-2', function () { return 'hi'; })->name('v1.prefix1.domain1-2');
+ $api->get('/prefix2/domain1-1', function () { return 'hi'; })->name('v1.prefix2.domain1-1');
+ $api->get('prefix2/domain1-2', function () { return 'hi'; })->name('v1.prefix2.domain1-2');
+ });
+ $api->group(['domain' => 'domain2.app.test'], function (Router $api) {
+ $api->post('/domain2-1', function () { return 'hi'; })->name('v1.domain2-1');
+ $api->get('domain2-2', function () { return 'hi'; })->name('v1.domain2-2');
+ $api->get('/prefix1/domain2-1', function () { return 'hi'; })->name('v1.prefix1.domain2-1');
+ $api->get('prefix1/domain2-2', function () { return 'hi'; })->name('v1.prefix1.domain2-2');
+ $api->get('/prefix2/domain2-1', function () { return 'hi'; })->name('v1.prefix2.domain2-1');
+ $api->get('prefix2/domain2-2', function () { return 'hi'; })->name('v1.prefix2.domain2-2');
+ });
+ });
+ $api->version('v2', function (Router $api) {
+ $api->group(['domain' => 'domain1.app.test'], function (Router $api) {
+ $api->post('/domain1', function () { return 'hi'; })->name('v2.domain1');
+ $api->get('/prefix1/domain1', function () { return 'hi'; })->name('v2.prefix1.domain1');
+ $api->get('/prefix2/domain1', function () { return 'hi'; })->name('v2.prefix2.domain1');
+ });
+ $api->group(['domain' => 'domain2.app.test'], function (Router $api) {
+ $api->post('/domain2', function () { return 'hi'; })->name('v2.domain2');
+ $api->get('/prefix1/domain2', function () { return 'hi'; })->name('v2.prefix1.domain2');
+ $api->get('/prefix2/domain2', function () { return 'hi'; })->name('v2.prefix2.domain2');
+ });
+ });
+ }
+}
diff --git a/tests/RuleDescriptionParserTest.php b/tests/RuleDescriptionParserTest.php
deleted file mode 100644
index f1f6f724..00000000
--- a/tests/RuleDescriptionParserTest.php
+++ /dev/null
@@ -1,118 +0,0 @@
-translatorMock = m::mock(Translator::class, [$fileLoaderMock, 'es']);
- $this->app->instance('translator', $this->translatorMock);
- }
-
- public function tearDown()
- {
- m::close();
- }
-
- public function testReturnsAnEmptyDescriptionIfARuleIsNotParsed()
- {
- $this->translatorMock->shouldReceive('hasForLocale')->twice()->andReturn(false);
-
- $description = new RuleDescriptionParser();
-
- $this->assertEmpty($description->getDescription());
- }
-
- public function testProvidesANamedContructor()
- {
- $this->assertInstanceOf(RuleDescriptionParser::class, RuleDescriptionParser::parse());
- }
-
- public function testReturnsADescriptionInMainLanguageIfAvailable()
- {
- $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');
-
- $description = RuleDescriptionParser::parse('alpha')->getDescription();
-
- $this->assertEquals('Solo caracteres alfabeticos permitidos', $description);
- }
-
- public function testReturnsDescriptionInDefaultLanguageIfNotAvailableInMainLanguage()
- {
- $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 = RuleDescriptionParser::parse('alpha')->getDescription();
-
- $this->assertEquals('Only alphabetic characters allowed', $description);
- }
-
- public function testReturnsAnEmptyDescriptionIfNotAvailable()
- {
- $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);
-
- $description = RuleDescriptionParser::parse('dummy_rule')->getDescription();
-
- $this->assertEmpty($description);
- }
-
- public function testAllowsToPassParametersToTheDescription()
- {
- $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`');
-
- $description = RuleDescriptionParser::parse('digits')->with(2)->getDescription();
-
- $this->assertEquals('Must have an exact length of `2`', $description);
- }
-
- public function testAllowsToPassMultipleParametersToTheDescription()
- {
- $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`');
-
- $description = RuleDescriptionParser::parse('required_if')->with(['2 + 2', 4])->getDescription();
-
- $this->assertEquals('Required if `2 + 2` is `4`', $description);
- }
-
- /**
- * @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', 'es'); // Just to be different from default language.
- $app['config']->set('app.fallback_locale', 'ch'); // Just to be different from default language.
- }
-}