diff --git a/src/Commands/GenerateDocumentation.php b/src/Commands/GenerateDocumentation.php
index b7aeb69..3ee9e0d 100644
--- a/src/Commands/GenerateDocumentation.php
+++ b/src/Commands/GenerateDocumentation.php
@@ -6,17 +6,20 @@ use Illuminate\Console\Command;
use Illuminate\Routing\Route;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\URL;
+use Illuminate\Support\Str;
use Knuckles\Scribe\Extracting\Generator;
use Knuckles\Scribe\Matching\Match;
use Knuckles\Scribe\Matching\RouteMatcherInterface;
+use Knuckles\Scribe\Tools\ConsoleOutputUtils as c;
use Knuckles\Scribe\Tools\DocumentationConfig;
+use Knuckles\Scribe\Tools\ErrorHandlingUtils as e;
use Knuckles\Scribe\Tools\Flags;
-use Knuckles\Scribe\Tools\Utils;
+use Knuckles\Scribe\Tools\Utils as u;
use Knuckles\Scribe\Writing\Writer;
use Mpociot\Reflection\DocBlock;
+use Mpociot\Reflection\DocBlock\Tag;
use ReflectionClass;
use ReflectionException;
-use Shalvah\Clara\Clara;
class GenerateDocumentation extends Command
{
@@ -46,11 +49,6 @@ class GenerateDocumentation extends Command
*/
private $baseUrl;
- /**
- * @var Clara
- */
- private $clara;
-
/**
* Execute the console command.
*
@@ -72,53 +70,48 @@ class GenerateDocumentation extends Command
/* @var $group Collection */
return $group->first()['metadata']['groupName'];
}, SORT_NATURAL);
- $writer = new Writer(
- $this->docConfig,
- $this->option('force'),
- $this->clara
- );
+
+ $writer = new Writer($this->docConfig, $this->option('force'));
$writer->writeDocs($groupedRoutes);
}
/**
- * @param Match[] $routes
+ * @param Match[] $matches
*
* @return array
*@throws \ReflectionException
*
*/
- private function processRoutes(array $routes)
+ private function processRoutes(array $matches)
{
$generator = new Generator($this->docConfig);
$parsedRoutes = [];
- foreach ($routes as $routeItem) {
- $route = $routeItem->getRoute();
+ foreach ($matches as $routeItem) {
/** @var Route $route */
- $messageFormat = '%s route: [%s] %s';
- $routeMethods = implode(',', $generator->getMethods($route));
- $routePath = $generator->getUri($route);
+ $route = $routeItem->getRoute();
- $routeControllerAndMethod = Utils::getRouteClassAndMethodNames($route->getAction());
+ $routeControllerAndMethod = u::getRouteClassAndMethodNames($route);
if (! $this->isValidRoute($routeControllerAndMethod)) {
- $this->clara->warn(sprintf($messageFormat, 'Skipping invalid', $routeMethods, $routePath));
+ c::warn('Skipping invalid route: '. c::getRouteRepresentation($route));
continue;
}
if (! $this->doesControllerMethodExist($routeControllerAndMethod)) {
- $this->clara->warn(sprintf($messageFormat, 'Skipping', $routeMethods, $routePath) . ': Controller method does not exist.');
+ c::warn('Skipping route: '. c::getRouteRepresentation($route).' - Controller method does not exist.');
continue;
}
if ($this->isRouteHiddenFromDocumentation($routeControllerAndMethod)) {
- $this->clara->warn(sprintf($messageFormat, 'Skipping', $routeMethods, $routePath) . ': @hideFromAPIDocumentation was specified.');
+ c::warn('Skipping route: '. c::getRouteRepresentation($route). ': @hideFromAPIDocumentation was specified.');
continue;
}
try {
$parsedRoutes[] = $generator->processRoute($route, $routeItem->getRules());
- $this->clara->info(sprintf($messageFormat, 'Processed', $routeMethods, $routePath));
+ c::info('Processed route: '. c::getRouteRepresentation($route));
} catch (\Exception $exception) {
- $this->clara->warn(sprintf($messageFormat, 'Skipping', $routeMethods, $routePath) . '- Exception ' . get_class($exception) . ' encountered : ' . $exception->getMessage());
+ c::warn('Skipping route: '. c::getRouteRepresentation($route) . ' - Exception encountered.');
+ e::dumpExceptionIfVerbose($exception);
}
}
@@ -134,7 +127,7 @@ class GenerateDocumentation extends Command
{
if (is_array($routeControllerAndMethod)) {
[$classOrObject, $method] = $routeControllerAndMethod;
- if (Utils::isInvokableObject($classOrObject)) {
+ if (u::isInvokableObject($classOrObject)) {
return true;
}
$routeControllerAndMethod = $classOrObject . '@' . $method;
@@ -155,11 +148,11 @@ class GenerateDocumentation extends Command
[$class, $method] = $routeControllerAndMethod;
$reflection = new ReflectionClass($class);
- if (! $reflection->hasMethod($method)) {
- return false;
+ if ($reflection->hasMethod($method)) {
+ return true;
}
- return true;
+ return false;
}
/**
@@ -171,30 +164,27 @@ class GenerateDocumentation extends Command
*/
private function isRouteHiddenFromDocumentation(array $routeControllerAndMethod)
{
- $comment = Utils::reflectRouteMethod($routeControllerAndMethod)->getDocComment();
+ $comment = u::reflectRouteMethod($routeControllerAndMethod)->getDocComment();
- if ($comment) {
- $phpdoc = new DocBlock($comment);
-
- return collect($phpdoc->getTags())
- ->filter(function ($tag) {
- return $tag->getName() === 'hideFromAPIDocumentation';
- })
- ->isEmpty();
+ if (!$comment) {
+ return false;
}
- return true;
+ $phpdoc = new DocBlock($comment);
+
+ return collect($phpdoc->getTags())
+ ->filter(function (Tag $tag) {
+ return Str::lower($tag->getName()) === 'hidefromapidocumentation';
+ })->isNotEmpty();
}
public function bootstrap(): void
{
- // Using a global static variable here, so fuck off if you don't like it.
+ // Using a global static variable here, so 🙄 if you don't like it.
// Also, the --verbose option is included with all Artisan commands.
Flags::$shouldBeVerbose = $this->option('verbose');
- $this->clara = clara('knuckleswtf/scribe', Flags::$shouldBeVerbose)
- ->useOutput($this->output)
- ->only();
+ c::bootstrapOutput($this->output);
$this->docConfig = new DocumentationConfig(config('scribe'));
$this->baseUrl = $this->docConfig->get('base_url') ?? config('app.url');
diff --git a/src/Extracting/Generator.php b/src/Extracting/Generator.php
index 9d18e12..25207cd 100644
--- a/src/Extracting/Generator.php
+++ b/src/Extracting/Generator.php
@@ -8,7 +8,7 @@ use Illuminate\Support\Arr;
use Illuminate\Support\Str;
use Knuckles\Scribe\Extracting\Strategies\Strategy;
use Knuckles\Scribe\Tools\DocumentationConfig;
-use Knuckles\Scribe\Tools\Utils;
+use Knuckles\Scribe\Tools\Utils as u;
use ReflectionClass;
use ReflectionFunctionAbstract;
@@ -55,9 +55,9 @@ class Generator
*/
public function processRoute(Route $route, array $routeRules = [])
{
- [$controllerName, $methodName] = Utils::getRouteClassAndMethodNames($route->getAction());
+ [$controllerName, $methodName] = u::getRouteClassAndMethodNames($route);
$controller = new ReflectionClass($controllerName);
- $method = Utils::reflectRouteMethod([$controllerName, $methodName]);
+ $method = u::reflectRouteMethod([$controllerName, $methodName]);
$parsedRoute = [
'id' => md5($this->getUri($route) . ':' . implode($this->getMethods($route))),
@@ -70,7 +70,7 @@ class Generator
$urlParameters = $this->fetchUrlParameters($controller, $method, $route, $routeRules, $parsedRoute);
$parsedRoute['urlParameters'] = $urlParameters;
$parsedRoute['cleanUrlParameters'] = self::cleanParams($urlParameters);
- $parsedRoute['boundUri'] = Utils::getFullUrl($route, $parsedRoute['cleanUrlParameters']);
+ $parsedRoute['boundUri'] = u::getFullUrl($route, $parsedRoute['cleanUrlParameters']);
$parsedRoute = $this->addAuthField($parsedRoute);
diff --git a/src/Extracting/RouteDocBlocker.php b/src/Extracting/RouteDocBlocker.php
index 3d27840..9f4142a 100644
--- a/src/Extracting/RouteDocBlocker.php
+++ b/src/Extracting/RouteDocBlocker.php
@@ -3,7 +3,8 @@
namespace Knuckles\Scribe\Extracting;
use Illuminate\Routing\Route;
-use Knuckles\Scribe\Tools\Utils;
+use Knuckles\Scribe\Tools\ConsoleOutputUtils as c;
+use Knuckles\Scribe\Tools\Utils as u;
use Mpociot\Reflection\DocBlock;
use ReflectionClass;
@@ -26,7 +27,7 @@ class RouteDocBlocker
*/
public static function getDocBlocksFromRoute(Route $route): array
{
- list($className, $methodName) = Utils::getRouteClassAndMethodNames($route);
+ [$className, $methodName] = u::getRouteClassAndMethodNames($route);
$normalizedClassName = static::normalizeClassName($className);
$docBlocks = self::getCachedDocBlock($route, $normalizedClassName, $methodName);
@@ -37,10 +38,10 @@ class RouteDocBlocker
$class = new ReflectionClass($className);
if (! $class->hasMethod($methodName)) {
- throw new \Exception("Error while fetching docblock for route: Class $className does not contain method $methodName");
+ throw new \Exception("Error while fetching docblock for route ". c::getRouteRepresentation($route).": Class $className does not contain method $methodName");
}
- $method = Utils::reflectRouteMethod([$className, $methodName]);
+ $method = u::reflectRouteMethod([$className, $methodName]);
$docBlocks = [
'method' => new DocBlock($method->getDocComment() ?: ''),
diff --git a/src/Extracting/Strategies/BodyParameters/GetFromFormRequest.php b/src/Extracting/Strategies/BodyParameters/GetFromFormRequest.php
index 471df69..623b7e9 100644
--- a/src/Extracting/Strategies/BodyParameters/GetFromFormRequest.php
+++ b/src/Extracting/Strategies/BodyParameters/GetFromFormRequest.php
@@ -9,12 +9,11 @@ use Illuminate\Support\Arr;
use Illuminate\Support\Facades\Validator;
use Illuminate\Support\Str;
use Illuminate\Contracts\Validation\Rule;
-use Knuckles\Scribe\Extracting\BodyParameterDefinition;
use Knuckles\Scribe\Extracting\ParamHelpers;
use Knuckles\Scribe\Extracting\Strategies\Strategy;
-use Knuckles\Scribe\Extracting\ValidationRuleDescriptionParser as Description;
-use Knuckles\Scribe\Tools\Utils;
-use Knuckles\Scribe\Tools\WritingUtils;
+use Knuckles\Scribe\Extracting\ValidationRuleDescriptionParser as d;
+use Knuckles\Scribe\Tools\ConsoleOutputUtils as c;
+use Knuckles\Scribe\Tools\WritingUtils as w;
use ReflectionClass;
use ReflectionException;
use ReflectionFunctionAbstract;
@@ -93,7 +92,8 @@ class GetFromFormRequest extends Strategy
return call_user_func_array([$formRequest, 'bodyParameters'], []);
}
- clara('knuckleswtf/scribe')->warn("No bodyParameters() method found in ".get_class($formRequest)." Scribe will only be able to extract basic information from the rules() method.");
+ c::warn("No bodyParameters() method found in ".get_class($formRequest)." Scribe will only be able to extract basic information from the rules() method.");
+
return [];
}
@@ -105,7 +105,7 @@ class GetFromFormRequest extends Strategy
$parameters = [];
foreach ($rules as $parameter => $ruleset) {
if (count($customParameterData) && !isset($customParameterData[$parameter])) {
- clara('knuckleswtf/scribe')->warn("No data found for parameter '$parameter' from your bodyParameters() method. Add an entry for '$parameter' so you can add description and example.");
+ c::debug("No data found for parameter '$parameter' from your bodyParameters() method. Add an entry for '$parameter' so you can add description and example.");
}
$parameterInfo = $customParameterData[$parameter] ?? [];
@@ -251,11 +251,11 @@ class GetFromFormRequest extends Strategy
*/
case 'timezone':
// Laravel's message merely says "The value must be a valid zone"
- $parameterData['description'] .= "The value must be a valid time zone, such as `Africa/Accra`. ";
+ $parameterData['description'] .= "The value must be a valid time zone, such as Africa/Accra. ";
$parameterData['value'] = $this->getFaker()->timezone;
break;
case 'email':
- $parameterData['description'] .= Description::getDescription($rule).' ';
+ $parameterData['description'] .= d::getDescription($rule).' ';
$parameterData['value'] = $this->getFaker()->safeEmail;
$parameterData['type'] = 'string';
break;
@@ -266,18 +266,18 @@ class GetFromFormRequest extends Strategy
$parameterData['description'] .= "The value must be a valid URL. ";
break;
case 'ip':
- $parameterData['description'] .= Description::getDescription($rule).' ';
+ $parameterData['description'] .= d::getDescription($rule).' ';
$parameterData['value'] = $this->getFaker()->ipv4;
$parameterData['type'] = 'string';
break;
case 'json':
$parameterData['type'] = 'string';
- $parameterData['description'] .= Description::getDescription($rule).' ';
+ $parameterData['description'] .= d::getDescription($rule).' ';
$parameterData['value'] = json_encode([$this->getFaker()->word, $this->getFaker()->word,]);
break;
case 'date':
$parameterData['type'] = 'string';
- $parameterData['description'] .= Description::getDescription($rule).' ';
+ $parameterData['description'] .= d::getDescription($rule).' ';
$parameterData['value'] = date(\DateTime::ISO8601, time());
break;
case 'date_format':
@@ -313,7 +313,7 @@ class GetFromFormRequest extends Strategy
*/
case 'image':
$parameterData['type'] = 'file';
- $parameterData['description'] .= Description::getDescription($rule).' ';
+ $parameterData['description'] .= d::getDescription($rule).' ';
break;
/**
@@ -321,7 +321,7 @@ class GetFromFormRequest extends Strategy
*/
case 'in':
// Not using the rule description here because it only says "The attribute is invalid"
- $description = 'The value must be one of '.WritingUtils::getListOfValuesAsFriendlyHtmlString($arguments);
+ $description = 'The value must be one of '. w::getListOfValuesAsFriendlyHtmlString($arguments);
$parameterData['description'] .= $description.' ';
$parameterData['value'] = Arr::random($arguments);
break;
diff --git a/src/Extracting/Strategies/Responses/ResponseCalls.php b/src/Extracting/Strategies/Responses/ResponseCalls.php
index eca61f2..31ec81c 100644
--- a/src/Extracting/Strategies/Responses/ResponseCalls.php
+++ b/src/Extracting/Strategies/Responses/ResponseCalls.php
@@ -13,8 +13,8 @@ use Illuminate\Support\Str;
use Knuckles\Scribe\Extracting\DatabaseTransactionHelpers;
use Knuckles\Scribe\Extracting\ParamHelpers;
use Knuckles\Scribe\Extracting\Strategies\Strategy;
-use Knuckles\Scribe\Tools\ErrorHandlingUtils;
-use Knuckles\Scribe\Tools\Flags;
+use Knuckles\Scribe\Tools\ConsoleOutputUtils as c;
+use Knuckles\Scribe\Tools\ErrorHandlingUtils as e;
use Knuckles\Scribe\Tools\Utils;
use ReflectionClass;
use ReflectionFunctionAbstract;
@@ -70,12 +70,9 @@ class ResponseCalls extends Strategy
],
];
} catch (Exception $e) {
- clara('knuckleswtf/scribe')->warn('Exception thrown during response call for [' . implode(',', $route->methods) . "] {$route->uri}.");
- if (Flags::$shouldBeVerbose) {
- ErrorHandlingUtils::dumpException($e);
- } else {
- clara('knuckleswtf/scribe')->warn("Run this again with the --verbose flag to see the exception.");
- }
+ c::warn('Exception thrown during response call for [' . implode(',', $route->methods) . "] {$route->uri}.");
+ e::dumpExceptionIfVerbose($e);
+
$response = null;
} finally {
$this->finish();
diff --git a/src/Extracting/Strategies/Responses/UseApiResourceTags.php b/src/Extracting/Strategies/Responses/UseApiResourceTags.php
index ed0c5a2..d6ca5cf 100644
--- a/src/Extracting/Strategies/Responses/UseApiResourceTags.php
+++ b/src/Extracting/Strategies/Responses/UseApiResourceTags.php
@@ -15,11 +15,9 @@ use Illuminate\Support\Arr;
use Knuckles\Scribe\Extracting\DatabaseTransactionHelpers;
use Knuckles\Scribe\Extracting\RouteDocBlocker;
use Knuckles\Scribe\Extracting\Strategies\Strategy;
-use Knuckles\Scribe\Tools\AnnotationParser;
-use Knuckles\Scribe\Tools\ErrorHandlingUtils;
-use Knuckles\Scribe\Tools\Flags;
-use Knuckles\Scribe\Tools\Utils;
-use League\Fractal\Resource\Collection;
+use Knuckles\Scribe\Tools\AnnotationParser as a;
+use Knuckles\Scribe\Tools\ConsoleOutputUtils as c;
+use Knuckles\Scribe\Tools\ErrorHandlingUtils as e;
use Mpociot\Reflection\DocBlock;
use Mpociot\Reflection\DocBlock\Tag;
use ReflectionClass;
@@ -52,13 +50,8 @@ class UseApiResourceTags extends Strategy
try {
return $this->getApiResourceResponse($methodDocBlock->getTags());
} catch (Exception $e) {
- clara('knuckleswtf/scribe')->warn('Exception thrown when fetching Eloquent API resource response for [' . implode(',', $route->methods) . "] {$route->uri}.");
- if (Flags::$shouldBeVerbose) {
- ErrorHandlingUtils::dumpException($e);
- } else {
- clara('knuckleswtf/scribe')->warn("Run this again with the --verbose flag to see the exception.");
- }
-
+ c::warn('Exception thrown when fetching Eloquent API resource response for [' . implode(',', $route->methods) . "] {$route->uri}.");
+ e::dumpExceptionIfVerbose($e);
return null;
}
}
@@ -95,7 +88,7 @@ class UseApiResourceTags extends Strategy
if (count($pagination) == 1) {
$perPage = $pagination[0];
$paginator = new LengthAwarePaginator(
- // For some reason, the LengthAware paginator needs only first page items to work correctly
+ // For some reason, the LengthAware paginator needs only first page items to work correctly
collect($models)->slice(0, $perPage),
count($models),
$perPage
@@ -151,7 +144,7 @@ class UseApiResourceTags extends Strategy
$relations = [];
$pagination = [];
if ($modelTag) {
- ['content' => $type, 'attributes' => $attributes] = AnnotationParser::parseIntoContentAndAttributes($modelTag->getContent(), ['states', 'with', 'paginate']);
+ ['content' => $type, 'attributes' => $attributes] = a::parseIntoContentAndAttributes($modelTag->getContent(), ['states', 'with', 'paginate']);
$states = $attributes['states'] ? explode(',', $attributes['states']) : [];
$relations = $attributes['with'] ? explode(',', $attributes['with']) : [];
$pagination = $attributes['paginate'] ? explode(',', $attributes['paginate']) : [];
@@ -193,9 +186,8 @@ class UseApiResourceTags extends Strategy
return $factory->make();
}
} catch (Exception $e) {
- if (Flags::$shouldBeVerbose) {
- clara('knuckleswtf/scribe')->warn("Eloquent model factory failed to instantiate {$type}; trying to fetch from database.");
- }
+ c::debug("Eloquent model factory failed to instantiate {$type}; trying to fetch from database.");
+ e::dumpExceptionIfVerbose($e);
$instance = new $type();
if ($instance instanceof \Illuminate\Database\Eloquent\Model) {
@@ -207,9 +199,8 @@ class UseApiResourceTags extends Strategy
}
} catch (Exception $e) {
// okay, we'll stick with `new`
- if (Flags::$shouldBeVerbose) {
- clara('knuckleswtf/scribe')->warn("Failed to fetch first {$type} from database; using `new` to instantiate.");
- }
+ c::debug("Failed to fetch first {$type} from database; using `new` to instantiate.");
+ e::dumpExceptionIfVerbose($e);
}
}
} finally {
diff --git a/src/Extracting/Strategies/Responses/UseResponseFileTag.php b/src/Extracting/Strategies/Responses/UseResponseFileTag.php
index 8f17a7e..a09a084 100644
--- a/src/Extracting/Strategies/Responses/UseResponseFileTag.php
+++ b/src/Extracting/Strategies/Responses/UseResponseFileTag.php
@@ -5,7 +5,8 @@ namespace Knuckles\Scribe\Extracting\Strategies\Responses;
use Illuminate\Routing\Route;
use Knuckles\Scribe\Extracting\RouteDocBlocker;
use Knuckles\Scribe\Extracting\Strategies\Strategy;
-use Knuckles\Scribe\Tools\AnnotationParser;
+use Knuckles\Scribe\Tools\AnnotationParser as a;
+use Knuckles\Scribe\Tools\ConsoleOutputUtils as c;
use Mpociot\Reflection\DocBlock;
use Mpociot\Reflection\DocBlock\Tag;
@@ -59,14 +60,14 @@ class UseResponseFileTag extends Strategy
[$_, $status, $mainContent] = $result;
$json = $result[3] ?? null;
- ['attributes' => $attributes, 'content' => $relativeFilePath] = AnnotationParser::parseIntoContentAndAttributes($mainContent, ['status', 'scenario']);
+ ['attributes' => $attributes, 'content' => $relativeFilePath] = a::parseIntoContentAndAttributes($mainContent, ['status', 'scenario']);
$status = $attributes['status'] ?: ($status ?: 200);
$description = $attributes['scenario'] ? "$status, {$attributes['scenario']}" : "$status";
$filePath = storage_path($relativeFilePath);
if (! file_exists($filePath)) {
- throw new \Exception('@responseFile ' . $relativeFilePath . ' does not exist');
+ c::warn("@responseFile {$relativeFilePath} does not exist");
}
$content = file_get_contents($filePath, true);
if ($json) {
diff --git a/src/Extracting/Strategies/Responses/UseResponseTag.php b/src/Extracting/Strategies/Responses/UseResponseTag.php
index 0fa2a01..b95aeff 100644
--- a/src/Extracting/Strategies/Responses/UseResponseTag.php
+++ b/src/Extracting/Strategies/Responses/UseResponseTag.php
@@ -5,7 +5,7 @@ namespace Knuckles\Scribe\Extracting\Strategies\Responses;
use Illuminate\Routing\Route;
use Knuckles\Scribe\Extracting\RouteDocBlocker;
use Knuckles\Scribe\Extracting\Strategies\Strategy;
-use Knuckles\Scribe\Tools\AnnotationParser;
+use Knuckles\Scribe\Tools\AnnotationParser as a;
use Mpociot\Reflection\DocBlock;
use Mpociot\Reflection\DocBlock\Tag;
@@ -60,7 +60,7 @@ class UseResponseTag extends Strategy
$status = $result[1] ?: 200;
$content = $result[2] ?: '{}';
- ['attributes' => $attributes, 'content' => $content] = AnnotationParser::parseIntoContentAndAttributes($content, ['status', 'scenario']);
+ ['attributes' => $attributes, 'content' => $content] = a::parseIntoContentAndAttributes($content, ['status', 'scenario']);
$status = $attributes['status'] ?: $status;
$description = $attributes['scenario'] ? "$status, {$attributes['scenario']}" : $status;
diff --git a/src/Extracting/Strategies/Responses/UseTransformerTags.php b/src/Extracting/Strategies/Responses/UseTransformerTags.php
index afa5d92..504eb04 100644
--- a/src/Extracting/Strategies/Responses/UseTransformerTags.php
+++ b/src/Extracting/Strategies/Responses/UseTransformerTags.php
@@ -10,10 +10,9 @@ use Illuminate\Support\Arr;
use Knuckles\Scribe\Extracting\DatabaseTransactionHelpers;
use Knuckles\Scribe\Extracting\RouteDocBlocker;
use Knuckles\Scribe\Extracting\Strategies\Strategy;
-use Knuckles\Scribe\Tools\AnnotationParser;
-use Knuckles\Scribe\Tools\ErrorHandlingUtils;
-use Knuckles\Scribe\Tools\Flags;
-use Knuckles\Scribe\Tools\Utils;
+use Knuckles\Scribe\Tools\AnnotationParser as a;
+use Knuckles\Scribe\Tools\ConsoleOutputUtils as c;
+use Knuckles\Scribe\Tools\ErrorHandlingUtils as e;
use League\Fractal\Manager;
use League\Fractal\Resource\Collection;
use League\Fractal\Resource\Item;
@@ -36,9 +35,9 @@ class UseTransformerTags extends Strategy
* @param array $rulesToApply
* @param array $context
*
+ * @return array|null
* @throws \Exception
*
- * @return array|null
*/
public function __invoke(Route $route, ReflectionClass $controller, ReflectionFunctionAbstract $method, array $rulesToApply, array $context = [])
{
@@ -49,12 +48,8 @@ class UseTransformerTags extends Strategy
try {
return $this->getTransformerResponse($methodDocBlock->getTags());
} catch (Exception $e) {
- clara('knuckleswtf/scribe')->warn('Exception thrown when fetching transformer response for [' . implode(',', $route->methods) . "] {$route->uri}.");
- if (Flags::$shouldBeVerbose) {
- ErrorHandlingUtils::dumpException($e);
- } else {
- clara('knuckleswtf/scribe')->warn("Run this again with the --verbose flag to see the exception.");
- }
+ c::warn('Exception thrown when fetching transformer response for [' . implode(',', $route->methods) . "] {$route->uri}.");
+ e::dumpExceptionIfVerbose($e);
return null;
}
@@ -79,7 +74,7 @@ class UseTransformerTags extends Strategy
$fractal = new Manager();
- if (! is_null($this->config->get('fractal.serializer'))) {
+ if (!is_null($this->config->get('fractal.serializer'))) {
$fractal->setSerializer(app($this->config->get('fractal.serializer')));
}
@@ -103,11 +98,11 @@ class UseTransformerTags extends Strategy
$response = response($fractal->createData($resource)->toJson());
return [
- [
- 'status' => $statusCode ?: 200,
- 'content' => $response->getContent(),
- ],
- ];
+ [
+ 'status' => $statusCode ?: 200,
+ 'content' => $response->getContent(),
+ ],
+ ];
}
/**
@@ -129,9 +124,9 @@ class UseTransformerTags extends Strategy
* @param array $tags
* @param ReflectionFunctionAbstract $transformerMethod
*
+ * @return array
* @throws Exception
*
- * @return array
*/
private function getClassToBeTransformed(array $tags, ReflectionFunctionAbstract $transformerMethod): array
{
@@ -143,12 +138,12 @@ class UseTransformerTags extends Strategy
$states = [];
$relations = [];
if ($modelTag) {
- ['content' => $type, 'attributes' => $attributes] = AnnotationParser::parseIntoContentAndAttributes($modelTag->getContent(), ['states', 'with']);
+ ['content' => $type, 'attributes' => $attributes] = a::parseIntoContentAndAttributes($modelTag->getContent(), ['states', 'with']);
$states = $attributes['states'] ? explode(',', $attributes['states']) : [];
$relations = $attributes['with'] ? explode(',', $attributes['with']) : [];
} else {
$parameter = Arr::first($transformerMethod->getParameters());
- if ($parameter->hasType() && ! $parameter->getType()->isBuiltin() && class_exists($parameter->getType()->getName())) {
+ if ($parameter->hasType() && !$parameter->getType()->isBuiltin() && class_exists($parameter->getType()->getName())) {
// Ladies and gentlemen, we have a type!
$type = $parameter->getType()->getName();
}
@@ -183,9 +178,8 @@ class UseTransformerTags extends Strategy
return $factory->make();
}
} catch (Exception $e) {
- if (Flags::$shouldBeVerbose) {
- clara('knuckleswtf/scribe')->warn("Eloquent model factory failed to instantiate {$type}; trying to fetch from database.");
- }
+ c::debug("Eloquent model factory failed to instantiate {$type}; trying to fetch from database.");
+ e::dumpExceptionIfVerbose($e);
$instance = new $type();
if ($instance instanceof IlluminateModel) {
@@ -197,9 +191,8 @@ class UseTransformerTags extends Strategy
}
} catch (Exception $e) {
// okay, we'll stick with `new`
- if (Flags::$shouldBeVerbose) {
- clara('knuckleswtf/scribe')->warn("Failed to fetch first {$type} from database; using `new` to instantiate.");
- }
+ c::debug("Failed to fetch first {$type} from database; using `new` to instantiate.");
+ e::dumpExceptionIfVerbose($e);
}
}
} finally {
diff --git a/src/Scribe.php b/src/Scribe.php
deleted file mode 100644
index 7fe4556..0000000
--- a/src/Scribe.php
+++ /dev/null
@@ -1,16 +0,0 @@
-useOutput($outputInterface)
+ ->only();
+ }
+
+ public static function warn($message)
+ {
+ if (!self::$clara) {
+ self::bootstrapOutput(new ConsoleOutput);
+ }
+ self::$clara->warn($message);
+ }
+
+ public static function info($message)
+ {
+ if (!self::$clara) {
+ self::bootstrapOutput(new ConsoleOutput);
+ }
+ self::$clara->info($message);
+ }
+
+ public static function debug($message)
+ {
+ if (!self::$clara) {
+ self::bootstrapOutput(new ConsoleOutput);
+ }
+ self::$clara->debug($message);
+ }
+
+ public static function success($message)
+ {
+ if (!self::$clara) {
+ self::bootstrapOutput(new ConsoleOutput);
+ }
+ self::$clara->success($message);
+ }
+
+ /**
+ * Return a string representation of a route to output to the console eg [GET] /api/users
+ * @param Route $route
+ *
+ * @return string
+ */
+ public static function getRouteRepresentation(Route $route): string
+ {
+ $routeMethods = implode(',', array_diff($route->methods(), ['HEAD']));
+ $routePath = $route->uri();
+ return "[$routeMethods] $routePath";
+ }
+}
diff --git a/src/Tools/ErrorHandlingUtils.php b/src/Tools/ErrorHandlingUtils.php
index 599c0f8..0c43567 100644
--- a/src/Tools/ErrorHandlingUtils.php
+++ b/src/Tools/ErrorHandlingUtils.php
@@ -7,14 +7,19 @@ use Symfony\Component\Console\Output\OutputInterface;
class ErrorHandlingUtils
{
- public static function dumpException(\Throwable $e): void
+ public static function dumpExceptionIfVerbose(\Throwable $e): void
{
- if (!class_exists(\NunoMaduro\Collision\Handler::class)) {
- dump($e);
- ConsoleOutputUtils::info("You can get better exception output by installing the library nunomaduro/collision.");
- return;
+ if (Flags::$shouldBeVerbose) {
+ self::dumpException($e);
+ } else {
+ ConsoleOutputUtils::warn(get_class($e) . ': ' . $e->getMessage());
+ ConsoleOutputUtils::warn('Run again with --verbose for a full stacktrace');
}
+ }
+
+ public static function dumpException(\Throwable $e): void
+ {
$output = new ConsoleOutput(OutputInterface::VERBOSITY_VERBOSE);
try {
$handler = new \NunoMaduro\Collision\Handler(new \NunoMaduro\Collision\Writer(null, $output));
diff --git a/src/Tools/Utils.php b/src/Tools/Utils.php
index c7f9adf..60bc228 100644
--- a/src/Tools/Utils.php
+++ b/src/Tools/Utils.php
@@ -5,15 +5,13 @@ namespace Knuckles\Scribe\Tools;
use Closure;
use Exception;
use Illuminate\Routing\Route;
+use Knuckles\Scribe\Tools\ConsoleOutputUtils as c;
use League\Flysystem\Adapter\Local;
use League\Flysystem\Filesystem;
use ReflectionClass;
use ReflectionException;
use ReflectionFunction;
use ReflectionFunctionAbstract;
-use Symfony\Component\Console\Output\ConsoleOutput;
-use Symfony\Component\Console\Output\OutputInterface;
-use Symfony\Component\VarExporter\VarExporter;
class Utils
{
@@ -24,16 +22,9 @@ class Utils
return self::replaceUrlParameterPlaceholdersWithValues($uri, $urlParameters);
}
- /**
- * @param array|Route $routeOrAction
- *
- * @return array|null
- */
- public static function getRouteClassAndMethodNames($routeOrAction)
+ public static function getRouteClassAndMethodNames(Route $route): array
{
- $action = $routeOrAction instanceof Route
- ? $routeOrAction->getAction()
- : $routeOrAction;
+ $action = $route->getAction();
$uses = $action['uses'];
@@ -53,7 +44,7 @@ class Utils
];
}
- throw new Exception("Couldn't handle route.");
+ throw new Exception("Couldn't get class and method names for route ". c::getRouteRepresentation($route).'.');
}
/**
diff --git a/src/Tools/WritingUtils.php b/src/Tools/WritingUtils.php
index dacc3a5..89fe249 100644
--- a/src/Tools/WritingUtils.php
+++ b/src/Tools/WritingUtils.php
@@ -2,17 +2,6 @@
namespace Knuckles\Scribe\Tools;
-use Closure;
-use Exception;
-use Illuminate\Routing\Route;
-use League\Flysystem\Adapter\Local;
-use League\Flysystem\Filesystem;
-use ReflectionClass;
-use ReflectionException;
-use ReflectionFunction;
-use ReflectionFunctionAbstract;
-use Symfony\Component\Console\Output\ConsoleOutput;
-use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\VarExporter\VarExporter;
class WritingUtils
diff --git a/src/Writing/PostmanCollectionWriter.php b/src/Writing/PostmanCollectionWriter.php
index 522c21c..d688014 100644
--- a/src/Writing/PostmanCollectionWriter.php
+++ b/src/Writing/PostmanCollectionWriter.php
@@ -135,7 +135,7 @@ class PostmanCollectionWriter
return [
'key' => $key,
'value' => urlencode($parameterData['value']),
- 'description' => $parameterData['description'],
+ 'description' => strip_tags($parameterData['description']),
// Default query params to disabled if they aren't required and have empty values
'disabled' => !($parameterData['required'] ?? false) && empty($parameterData['value']),
];
diff --git a/src/Writing/Writer.php b/src/Writing/Writer.php
index b4f8a25..79ae232 100644
--- a/src/Writing/Writer.php
+++ b/src/Writing/Writer.php
@@ -2,24 +2,17 @@
namespace Knuckles\Scribe\Writing;
-use Illuminate\Console\Command;
use Illuminate\Support\Arr;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Storage;
use Illuminate\Support\Str;
use Knuckles\Pastel\Pastel;
+use Knuckles\Scribe\Tools\ConsoleOutputUtils;
use Knuckles\Scribe\Tools\DocumentationConfig;
-use Knuckles\Scribe\Tools\Flags;
use Knuckles\Scribe\Tools\Utils;
-use Shalvah\Clara\Clara;
class Writer
{
- /**
- * @var Clara
- */
- protected $clara;
-
/**
* @var DocumentationConfig
*/
@@ -70,13 +63,12 @@ class Writer
*/
private $lastTimesWeModifiedTheseFiles;
- public function __construct(DocumentationConfig $config = null, bool $forceIt = false, $clara = null)
+ public function __construct(DocumentationConfig $config = null, bool $forceIt = false)
{
// If no config is injected, pull from global
$this->config = $config ?: new DocumentationConfig(config('scribe'));
$this->baseUrl = $this->config->get('base_url') ?? config('app.url');
$this->forceIt = $forceIt;
- $this->clara = $clara ?: clara('knuckleswtf/scribe', Flags::$shouldBeVerbose)->only();
$this->shouldGeneratePostmanCollection = $this->config->get('postman.enabled', false);
$this->pastel = new Pastel();
$this->isStatic = $this->config->get('type') === 'static';
@@ -114,7 +106,7 @@ class Writer
'title' => config('app.name', '') . ' API Documentation',
];
- $this->clara->info('Writing source Markdown files to: ' . $this->sourceOutputPath);
+ ConsoleOutputUtils::info('Writing source Markdown files to: ' . $this->sourceOutputPath);
if (!is_dir($this->sourceOutputPath)) {
mkdir($this->sourceOutputPath, 0777, true);
@@ -124,7 +116,7 @@ class Writer
$this->writeAuthMarkdownFile();
$this->writeRoutesMarkdownFile($parsedRoutes, $settings);
- $this->clara->info('Wrote source Markdown files to: ' . $this->sourceOutputPath);
+ ConsoleOutputUtils::info('Wrote source Markdown files to: ' . $this->sourceOutputPath);
}
public function generateMarkdownOutputForEachRoute(Collection $parsedRoutes, array $settings): Collection
@@ -156,7 +148,7 @@ class Writer
protected function writePostmanCollection(Collection $parsedRoutes): void
{
if ($this->shouldGeneratePostmanCollection) {
- $this->clara->info('Generating Postman collection');
+ ConsoleOutputUtils::info('Generating Postman collection');
$collection = $this->generatePostmanCollection($parsedRoutes);
if ($this->isStatic) {
@@ -167,7 +159,7 @@ class Writer
$collectionPath = 'storage/app/scribe/collection.json';
}
- $this->clara->success("Wrote Postman collection to: {$collectionPath}");
+ ConsoleOutputUtils::success("Wrote Postman collection to: {$collectionPath}");
}
}
@@ -213,7 +205,7 @@ class Writer
public function writeHtmlDocs(): void
{
- $this->clara->info('Generating API HTML code');
+ ConsoleOutputUtils::info('Generating API HTML code');
$this->pastel->generate($this->sourceOutputPath . '/index.md', 'public/docs');
@@ -221,7 +213,7 @@ class Writer
$this->performFinalTasksForLaravelType();
}
- $this->clara->success("Wrote HTML documentation to: {$this->outputPath}");
+ ConsoleOutputUtils::success("Wrote HTML documentation to: {$this->outputPath}");
}
protected function writeIndexMarkdownFile(array $settings): void
@@ -307,9 +299,9 @@ class Writer
if ($this->hasFileBeenModified($routeGroupMarkdownFile)) {
if ($this->forceIt) {
- $this->clara->warn("Discarded manual changes for file $routeGroupMarkdownFile");
+ ConsoleOutputUtils::warn("Discarded manual changes for file $routeGroupMarkdownFile");
} else {
- $this->clara->warn("Skipping modified file $routeGroupMarkdownFile");
+ ConsoleOutputUtils::warn("Skipping modified file $routeGroupMarkdownFile");
return;
}
}
diff --git a/tests/GenerateDocumentationTest.php b/tests/GenerateDocumentationTest.php
index b6158fd..284c5af 100644
--- a/tests/GenerateDocumentationTest.php
+++ b/tests/GenerateDocumentationTest.php
@@ -129,7 +129,7 @@ class GenerateDocumentationTest extends TestCase
}
/** @test */
- public function can_skip_non_existent_response_files()
+ public function can_skip_nonexistent_response_files()
{
RouteFacade::get('/api/non-existent', TestController::class . '@withNonExistentResponseFile');
diff --git a/tests/Strategies/BodyParameters/GetFromFormRequestTest.php b/tests/Strategies/BodyParameters/GetFromFormRequestTest.php
index 3ffd5d2..23e74e0 100644
--- a/tests/Strategies/BodyParameters/GetFromFormRequestTest.php
+++ b/tests/Strategies/BodyParameters/GetFromFormRequestTest.php
@@ -191,7 +191,7 @@ class GetFromFormRequestTest extends TestCase
['timezone' => 'timezone|required'],
['timezone' => ['description' => $description]],
[
- 'description' => 'The value must be a valid time zone, such as `Africa/Accra`.',
+ 'description' => 'The value must be a valid time zone, such as Africa/Accra.',
'type' => 'string',
],
],
@@ -247,7 +247,7 @@ class GetFromFormRequestTest extends TestCase
['in' => 'in:3,5,6|required'],
['in' => ['description' => $description]],
[
- 'description' => 'The value must be one of `3`, `5`, or `6`.',
+ 'description' => 'The value must be one of 3, 5, or 6.',
'type' => 'string',
],
],
diff --git a/tests/Strategies/Responses/ResponseCallsTest.php b/tests/Strategies/Responses/ResponseCallsTest.php
index cb07147..32cf597 100644
--- a/tests/Strategies/Responses/ResponseCallsTest.php
+++ b/tests/Strategies/Responses/ResponseCallsTest.php
@@ -254,6 +254,23 @@ class ResponseCallsTest extends TestCase
$this->assertNull($results);
}
+ /** @test */
+ public function does_not_make_response_call_if_forbidden_by_config()
+ {
+ $route = LaravelRouteFacade::post('/shouldFetchRouteResponse', [TestController::class, 'shouldFetchRouteResponse']);
+
+ $rules = [
+ 'response_calls' => [
+ 'methods' => [],
+ ],
+ ];
+ $context = ['responses' => []];
+ $strategy = new ResponseCalls(new DocumentationConfig([]));
+ $results = $strategy->makeResponseCallIfEnabledAndNoSuccessResponses($route, $rules, $context);
+
+ $this->assertNull($results);
+ }
+
public function registerDingoRoute(string $httpMethod, string $path, string $controllerMethod)
{
$desiredRoute = null;
diff --git a/tests/TestHelpers.php b/tests/TestHelpers.php
index 5b2256b..926a72e 100644
--- a/tests/TestHelpers.php
+++ b/tests/TestHelpers.php
@@ -14,9 +14,11 @@ trait TestHelpers
*/
public function artisan($command, $parameters = [])
{
- $this->app[Kernel::class]->call($command, $parameters);
+ /** @var Kernel $kernel */
+ $kernel = $this->app[Kernel::class];
+ $kernel->call($command, $parameters);
- return $this->app[Kernel::class]->output();
+ return $kernel->output();
}
private function assertFilesHaveSameContent($pathToExpected, $pathToActual)
diff --git a/todo.md b/todo.md
index 7ff65fd..706d6f2 100644
--- a/todo.md
+++ b/todo.md
@@ -4,7 +4,6 @@
- hideFromAPIDocumentation
- overwriting with --force
- binary responses
- - troubleshooting: --verbose
- plugin api: responses - description, $stage property
- --env
- Use database transactions and `create()` when instantiating factory models
@@ -17,9 +16,6 @@
- Command scribe:strategy: It would be nice if we had a make strategy command that can help people generate custom strategies
- Possible feature: https://github.com/mpociot/laravel-apidoc-generator/issues/731
-# Improvements
-- Improve error messaging: there's lots of places where it can crash because of wrong user input. We can try to have more descriptive error messages.
-
# Tests
- Add tests that verify the overwriting behaviour of the command when --force is used