X-Git-Url: http://source.bookstackapp.com/bookstack/blobdiff_plain/c429cf78187e80deb63982a282a1c6889f30291a..refs/pull/5280/head:/app/Api/ApiDocsGenerator.php diff --git a/app/Api/ApiDocsGenerator.php b/app/Api/ApiDocsGenerator.php index 0ed7e6712..9cae80617 100644 --- a/app/Api/ApiDocsGenerator.php +++ b/app/Api/ApiDocsGenerator.php @@ -2,20 +2,22 @@ namespace BookStack\Api; -use BookStack\Http\Controllers\Api\ApiController; +use BookStack\Http\ApiController; +use Exception; use Illuminate\Contracts\Container\BindingResolutionException; use Illuminate\Support\Collection; use Illuminate\Support\Facades\Cache; use Illuminate\Support\Facades\Route; use Illuminate\Support\Str; +use Illuminate\Validation\Rules\Password; use ReflectionClass; use ReflectionException; use ReflectionMethod; class ApiDocsGenerator { - protected $reflectionClasses = []; - protected $controllerClasses = []; + protected array $reflectionClasses = []; + protected array $controllerClasses = []; /** * Load the docs form the cache if existing @@ -25,13 +27,16 @@ class ApiDocsGenerator { $appVersion = trim(file_get_contents(base_path('version'))); $cacheKey = 'api-docs::' . $appVersion; - if (Cache::has($cacheKey) && config('app.env') === 'production') { - $docs = Cache::get($cacheKey); - } else { - $docs = (new static())->generate(); - Cache::put($cacheKey, $docs, 60 * 24); + $isProduction = config('app.env') === 'production'; + $cacheVal = $isProduction ? Cache::get($cacheKey) : null; + + if (!is_null($cacheVal)) { + return $cacheVal; } + $docs = (new ApiDocsGenerator())->generate(); + Cache::put($cacheKey, $docs, 60 * 24); + return $docs; } @@ -55,10 +60,16 @@ class ApiDocsGenerator { return $routes->map(function (array $route) { $exampleTypes = ['request', 'response']; + $fileTypes = ['json', 'http']; foreach ($exampleTypes as $exampleType) { - $exampleFile = base_path("dev/api/{$exampleType}s/{$route['name']}.json"); - $exampleContent = file_exists($exampleFile) ? file_get_contents($exampleFile) : null; - $route["example_{$exampleType}"] = $exampleContent; + foreach ($fileTypes as $fileType) { + $exampleFile = base_path("dev/api/{$exampleType}s/{$route['name']}." . $fileType); + if (file_exists($exampleFile)) { + $route["example_{$exampleType}"] = file_get_contents($exampleFile); + continue 2; + } + } + $route["example_{$exampleType}"] = null; } return $route; @@ -94,23 +105,47 @@ class ApiDocsGenerator $this->controllerClasses[$className] = $class; } - $rules = $class->getValdationRules()[$methodName] ?? []; - foreach ($rules as $param => $ruleString) { - $rules[$param] = explode('|', $ruleString); + $rules = collect($class->getValidationRules()[$methodName] ?? [])->map(function ($validations) { + return array_map(function ($validation) { + return $this->getValidationAsString($validation); + }, $validations); + })->toArray(); + + return empty($rules) ? null : $rules; + } + + /** + * Convert the given validation message to a readable string. + */ + protected function getValidationAsString($validation): string + { + if (is_string($validation)) { + return $validation; + } + + if (is_object($validation) && method_exists($validation, '__toString')) { + return strval($validation); } - return count($rules) > 0 ? $rules : null; + if ($validation instanceof Password) { + return 'min:8'; + } + + $class = get_class($validation); + + throw new Exception("Cannot provide string representation of rule for class: {$class}"); } /** * Parse out the description text from a class method comment. */ - protected function parseDescriptionFromMethodComment(string $comment) + protected function parseDescriptionFromMethodComment(string $comment): string { $matches = []; - preg_match_all('/^\s*?\*\s((?![@\s]).*?)$/m', $comment, $matches); + preg_match_all('/^\s*?\*\s?($|((?![\/@\s]).*?))$/m', $comment, $matches); - return implode(' ', $matches[1] ?? []); + $text = implode(' ', $matches[1] ?? []); + return str_replace(' ', "\n", $text); } /**