GenerateDocumentation.php 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119
  1. <?php
  2. namespace Knuckles\Scribe\Commands;
  3. use Illuminate\Console\Command;
  4. use Illuminate\Support\Arr;
  5. use Illuminate\Support\Facades\URL;
  6. use Illuminate\Support\Str;
  7. use Knuckles\Camel\Camel;
  8. use Knuckles\Camel\Extraction\ExtractedEndpointData;
  9. use Knuckles\Camel\Output\OutputEndpointData;
  10. use Knuckles\Scribe\Extracting\ApiDetails;
  11. use Knuckles\Scribe\Extracting\Extractor;
  12. use Knuckles\Scribe\GroupedEndpoints\GroupedEndpointsFactory;
  13. use Knuckles\Scribe\Matching\RouteMatcherInterface;
  14. use Knuckles\Scribe\Tools\ConsoleOutputUtils as c;
  15. use Knuckles\Scribe\Tools\DocumentationConfig;
  16. use Knuckles\Scribe\Tools\ErrorHandlingUtils as e;
  17. use Knuckles\Scribe\Tools\Globals;
  18. use Knuckles\Scribe\Tools\Utils;
  19. use Knuckles\Scribe\Writing\Writer;
  20. use Symfony\Component\Yaml\Yaml;
  21. class GenerateDocumentation extends Command
  22. {
  23. protected $signature = "scribe:generate
  24. {--force : Discard any changes you've made to the YAML or Markdown files}
  25. {--no-extraction : Skip extraction of route and API info and just transform the YAML and Markdown files into HTML}
  26. ";
  27. protected $description = 'Generate API documentation from your Laravel/Dingo routes.';
  28. private DocumentationConfig $docConfig;
  29. public static string $camelDir = ".scribe/endpoints";
  30. public static string $cacheDir = ".scribe/endpoints.cache";
  31. private bool $shouldExtract;
  32. private bool $forcing;
  33. public function handle(RouteMatcherInterface $routeMatcher, GroupedEndpointsFactory $groupedEndpointsFactory): void
  34. {
  35. $this->bootstrap();
  36. $groupedEndpointsInstance = $groupedEndpointsFactory->make($this, $routeMatcher);
  37. $groupedEndpoints = $this->mergeUserDefinedEndpoints(
  38. $groupedEndpointsInstance->get(),
  39. Camel::loadUserDefinedEndpoints(static::$camelDir)
  40. );
  41. $writer = new Writer($this->docConfig);
  42. $writer->writeDocs($groupedEndpoints);
  43. if ($groupedEndpointsInstance->hasEncounteredErrors()) {
  44. c::warn('Generated docs, but encountered some errors while processing routes.');
  45. c::warn('Check the output above for details.');
  46. }
  47. }
  48. public function isForcing(): bool
  49. {
  50. return $this->forcing;
  51. }
  52. public function shouldExtract(): bool
  53. {
  54. return $this->shouldExtract;
  55. }
  56. public function getDocConfig()
  57. {
  58. return $this->docConfig;
  59. }
  60. public function bootstrap(): void
  61. {
  62. // The --verbose option is included with all Artisan commands.
  63. Globals::$shouldBeVerbose = $this->option('verbose');
  64. c::bootstrapOutput($this->output);
  65. $this->docConfig = new DocumentationConfig(config('scribe'));
  66. // Force root URL so it works in Postman collection
  67. $baseUrl = $this->docConfig->get('base_url') ?? config('app.url');
  68. URL::forceRootUrl($baseUrl);
  69. $this->forcing = $this->option('force');
  70. $this->shouldExtract = !$this->option('no-extraction');
  71. if ($this->forcing && !$this->shouldExtract) {
  72. throw new \Exception("Can't use --force and --no-extraction together.");
  73. }
  74. }
  75. protected function mergeUserDefinedEndpoints(array $groupedEndpoints, array $userDefinedEndpoints): array
  76. {
  77. foreach ($userDefinedEndpoints as $endpoint) {
  78. $existingGroupKey = Arr::first(array_keys($groupedEndpoints), function ($key) use ($groupedEndpoints, $endpoint) {
  79. $group = $groupedEndpoints[$key];
  80. return $group['name'] === ($endpoint['metadata']['groupName'] ?? $this->docConfig->get('default_group', ''));
  81. });
  82. if ($existingGroupKey) {
  83. $groupedEndpoints[$existingGroupKey]['endpoints'][] = OutputEndpointData::fromExtractedEndpointArray($endpoint);
  84. } else {
  85. $groupedEndpoints[] = [
  86. 'name' => $endpoint['metadata']['groupName'] ?? $this->docConfig->get('default_group', ''),
  87. 'description' => $endpoint['metadata']['groupDescription'] ?? null,
  88. 'endpoints' => [OutputEndpointData::fromExtractedEndpointArray($endpoint)],
  89. ];
  90. }
  91. }
  92. return $groupedEndpoints;
  93. }
  94. }