Skip to content

Commit

Permalink
Support multiple docs
Browse files Browse the repository at this point in the history
  • Loading branch information
shalvah committed May 22, 2022
1 parent 77cba39 commit cc6c95e
Show file tree
Hide file tree
Showing 7 changed files with 93 additions and 39 deletions.
17 changes: 17 additions & 0 deletions camel/Camel.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,26 @@ class Camel
* @var array<string, string>
*/
public static array $groupFileNames = [];

/**
* @deprecated Use the cacheDir() method instead
*/
public static string $cacheDir = ".scribe/endpoints.cache";
/**
* @deprecated Use the camelDir() method instead
*/
public static string $camelDir = ".scribe/endpoints";

public static function cacheDir(string $docsName = 'scribe')
{
return ".$docsName/endpoints.cache";
}

public static function camelDir(string $docsName = 'scribe')
{
return ".$docsName/endpoints";
}

/**
* Load endpoints from the Camel files into groups (arrays).
*
Expand Down
50 changes: 36 additions & 14 deletions src/Commands/GenerateDocumentation.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
use Illuminate\Console\Command;
use Illuminate\Support\Arr;
use Illuminate\Support\Facades\URL;
use Illuminate\Support\Str;
use Knuckles\Camel\Camel;
use Knuckles\Camel\Output\OutputEndpointData;
use Knuckles\Scribe\Exceptions\GroupNotFound;
Expand All @@ -23,7 +24,7 @@ class GenerateDocumentation extends Command
{--force : Discard any changes you've made to the YAML or Markdown files}
{--no-extraction : Skip extraction of route and API info and just transform the YAML and Markdown files into HTML}
{--no-upgrade-check : Skip checking for config file upgrades. Won't make things faster, but can be helpful if the command is buggy}
{--config= : choose which config file to use}
{--config=scribe : choose which config file to use}
";

protected $description = 'Generate API documentation from your Laravel/Dingo routes.';
Expand All @@ -34,6 +35,8 @@ class GenerateDocumentation extends Command

private bool $forcing;

protected string $configName;

public function newLine($count = 1)
{
// TODO Remove when Laravel 6 is no longer supported
Expand All @@ -45,9 +48,9 @@ public function handle(RouteMatcherInterface $routeMatcher, GroupedEndpointsFact
{
$this->bootstrap();

$groupedEndpointsInstance = $groupedEndpointsFactory->make($this, $routeMatcher);
$groupedEndpointsInstance = $groupedEndpointsFactory->make($this, $routeMatcher, $this->configName);

$userDefinedEndpoints = Camel::loadUserDefinedEndpoints(Camel::$camelDir);
$userDefinedEndpoints = Camel::loadUserDefinedEndpoints(Camel::camelDir($this->configName));
$groupedEndpoints = $this->mergeUserDefinedEndpoints(
$groupedEndpointsInstance->get(),
$userDefinedEndpoints
Expand All @@ -58,7 +61,7 @@ public function handle(RouteMatcherInterface $routeMatcher, GroupedEndpointsFact
$this->writeExampleCustomEndpoint();
}

$writer = new Writer($this->docConfig);
$writer = new Writer($this->docConfig, $this->configName);
$writer->writeDocs($groupedEndpoints);

if ($groupedEndpointsInstance->hasEncounteredErrors()) {
Expand All @@ -67,6 +70,8 @@ public function handle(RouteMatcherInterface $routeMatcher, GroupedEndpointsFact
}

$this->upgradeConfigFileIfNeeded();

$this->sayGoodbye();
}

public function isForcing(): bool
Expand All @@ -91,16 +96,17 @@ public function bootstrap(): void

c::bootstrapOutput($this->output);

$this->docConfig = new DocumentationConfig(config('scribe'));

if($this->option('config')){
$config = config_path($this->option('config')).".php";
if(!file_exists($config)){
die("There is no suitable config found at {$config}\n");
$this->configName = $this->option('config');
if ($this->configName !== 'scribe') {
$configPath = config_path($this->configName) . ".php";
if (!file_exists($configPath)) {
c::error("The specified config file doesn't exist: {$configPath}.\n");
exit(1);
}
$this->docConfig = new DocumentationConfig(config($this->option('config')));
}

$this->docConfig = new DocumentationConfig(config($this->configName));

// Force root URL so it works in Postman collection
$baseUrl = $this->docConfig->get('base_url') ?? config('app.url');
URL::forceRootUrl($baseUrl);
Expand All @@ -109,7 +115,8 @@ public function bootstrap(): void
$this->shouldExtract = !$this->option('no-extraction');

if ($this->forcing && !$this->shouldExtract) {
throw new \Exception("Can't use --force and --no-extraction together.");
c::error("Can't use --force and --no-extraction together.\n");
exit(1);
}

// Reset this map (useful for tests)
Expand Down Expand Up @@ -180,7 +187,7 @@ protected function mergeUserDefinedEndpoints(array $groupedEndpoints, array $use
protected function writeExampleCustomEndpoint(): void
{
// We add an example to guide users in case they need to add a custom endpoint.
copy(__DIR__ . '/../../resources/example_custom_endpoint.yaml', Camel::$camelDir . '/custom.0.yaml');
copy(__DIR__ . '/../../resources/example_custom_endpoint.yaml', Camel::camelDir($this->configName) . '/custom.0.yaml');
}

protected function upgradeConfigFileIfNeeded(): void
Expand All @@ -189,7 +196,7 @@ protected function upgradeConfigFileIfNeeded(): void

$this->info("Checking for any pending upgrades to your config file...");
try {
$upgrader = Upgrader::ofConfigFile('config/scribe.php', __DIR__ . '/../../config/scribe.php')
$upgrader = Upgrader::ofConfigFile("config/{$this->configName}.php", __DIR__ . '/../../config/scribe.php')
->dontTouch(
'routes', 'example_languages', 'database_connections_to_transact', 'strategies', 'laravel.middleware',
'postman.overrides', 'openapi.overrides'
Expand Down Expand Up @@ -222,4 +229,19 @@ protected function upgradeConfigFileIfNeeded(): void
}

}

protected function sayGoodbye(): void
{
$message = 'All done. ';
if ($this->docConfig->get('type') == 'laravel') {
if ($this->docConfig->get('laravel.add_routes')) {
$message .= 'Visit your docs at ' . url($this->docConfig->get('laravel.docs_url'));
}
} else if (Str::endsWith(public_path(), 'public') && Str::startsWith($this->docConfig->get('static.output_path'), 'public/')) {
$message = 'Visit your docs at ' . url(str_replace('public/', '', $this->docConfig->get('static.output_path')));
}

$this->newLine();
c::success($message);
}
}
7 changes: 4 additions & 3 deletions src/Extracting/ApiDetails.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,16 +17,17 @@ class ApiDetails

private bool $preserveUserChanges;

private string $markdownOutputPath = '.scribe';
private string $markdownOutputPath;

private string $fileHashesTrackingFile;

private array $lastKnownFileContentHashes = [];

public function __construct(DocumentationConfig $config = null, bool $preserveUserChanges = true)
public function __construct(DocumentationConfig $config = null, bool $preserveUserChanges = true, string $docsName = 'scribe')
{
$this->markdownOutputPath = ".{$docsName}"; //.scribe by default
// If no config is injected, pull from global. Makes testing easier.
$this->config = $config ?: new DocumentationConfig(config('scribe'));
$this->config = $config ?: new DocumentationConfig(config($docsName));
$this->baseUrl = $this->config->get('base_url') ?? config('app.url');
$this->preserveUserChanges = $preserveUserChanges;

Expand Down
17 changes: 9 additions & 8 deletions src/GroupedEndpoints/GroupedEndpointsFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,29 +8,30 @@

class GroupedEndpointsFactory
{
public function make(GenerateDocumentation $command, RouteMatcherInterface $routeMatcher): GroupedEndpointsContract
public function make(GenerateDocumentation $command, RouteMatcherInterface $routeMatcher, string $docsName = 'scribe'): GroupedEndpointsContract
{
if ($command->isForcing()) {
return $this->makeGroupedEndpointsFromApp($command, $routeMatcher, false);
return $this->makeGroupedEndpointsFromApp($command, $routeMatcher, false, $docsName);
}

if ($command->shouldExtract()) {
return $this->makeGroupedEndpointsFromApp($command, $routeMatcher, true);
return $this->makeGroupedEndpointsFromApp($command, $routeMatcher, true, $docsName);
}

return $this->makeGroupedEndpointsFromCamelDir();
return $this->makeGroupedEndpointsFromCamelDir($docsName);
}

protected function makeGroupedEndpointsFromApp(
GenerateDocumentation $command,
RouteMatcherInterface $routeMatcher,
bool $preserveUserChanges
bool $preserveUserChanges,
string $docsName = 'scribe'
): GroupedEndpointsFromApp {
return new GroupedEndpointsFromApp($command, $routeMatcher, $preserveUserChanges);
return new GroupedEndpointsFromApp($command, $routeMatcher, $preserveUserChanges, $docsName);
}

protected function makeGroupedEndpointsFromCamelDir(): GroupedEndpointsFromCamelDir
protected function makeGroupedEndpointsFromCamelDir(string $docsName = 'scribe'): GroupedEndpointsFromCamelDir
{
return new GroupedEndpointsFromCamelDir();
return new GroupedEndpointsFromCamelDir($docsName);
}
}
13 changes: 9 additions & 4 deletions src/GroupedEndpoints/GroupedEndpointsFromApp.php
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@

class GroupedEndpointsFromApp implements GroupedEndpointsContract
{
protected string $docsName;
private GenerateDocumentation $command;
private RouteMatcherInterface $routeMatcher;
private DocumentationConfig $docConfig;
Expand All @@ -35,15 +36,19 @@ class GroupedEndpointsFromApp implements GroupedEndpointsContract

private array $endpointGroupIndexes = [];

public function __construct(GenerateDocumentation $command, RouteMatcherInterface $routeMatcher, $preserveUserChanges)
public function __construct(
GenerateDocumentation $command, RouteMatcherInterface $routeMatcher,
bool $preserveUserChanges, string $docsName = 'scribe'
)
{
$this->command = $command;
$this->routeMatcher = $routeMatcher;
$this->docConfig = $command->getDocConfig();
$this->preserveUserChanges = $preserveUserChanges;
$this->docsName = $docsName;

static::$camelDir = Camel::$camelDir;
static::$cacheDir = Camel::$cacheDir;
static::$camelDir = Camel::camelDir($this->docsName);
static::$cacheDir = Camel::cacheDir($this->docsName);
}

public function get(): array
Expand Down Expand Up @@ -282,7 +287,7 @@ protected function extractAndWriteApiDetailsToDisk(): void

protected function makeApiDetails(): ApiDetails
{
return new ApiDetails($this->docConfig, !$this->command->option('force'));
return new ApiDetails($this->docConfig, !$this->command->option('force'), $this->docsName);
}

/**
Expand Down
13 changes: 10 additions & 3 deletions src/GroupedEndpoints/GroupedEndpointsFromCamelDir.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,22 @@

class GroupedEndpointsFromCamelDir implements GroupedEndpointsContract
{
protected string $docsName;

public function __construct(string $docsName = 'scribe')
{
$this->docsName = $docsName;
}

public function get(): array
{
if (!is_dir(Camel::$camelDir)) {
if (!is_dir(Camel::camelDir($this->docsName))) {
throw new \InvalidArgumentException(
"Can't use --no-extraction because there are no endpoints in the " . Camel::$camelDir . " directory."
"Can't use --no-extraction because there are no endpoints in the " . Camel::camelDir($this->docsName) . " directory."
);
}

return Camel::loadEndpointsIntoGroups(Camel::$camelDir);
return Camel::loadEndpointsIntoGroups(Camel::camelDir($this->docsName));
}

public function hasEncounteredErrors(): bool
Expand Down
15 changes: 8 additions & 7 deletions src/Writing/Writer.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ class Writer

private bool $isStatic;

private string $markdownOutputPath = '.scribe';
private string $markdownOutputPath;

private string $staticTypeOutputPath;

Expand All @@ -29,15 +29,16 @@ class Writer
'js' => null,
'css' => null,
'images' => null,
]
],
];

private string $laravelAssetsPath;

public function __construct(DocumentationConfig $config = null)
public function __construct(DocumentationConfig $config = null, $docsName = 'scribe')
{
$this->markdownOutputPath = ".{$docsName}"; //.scribe by default
// If no config is injected, pull from global. Makes testing easier.
$this->config = $config ?: new DocumentationConfig(config('scribe'));
$this->config = $config ?: new DocumentationConfig(config($docsName));

$this->isStatic = $this->config->get('type') === 'static';
$this->staticTypeOutputPath = rtrim($this->config->get('static.output_path', 'public/docs'), '/');
Expand Down Expand Up @@ -178,7 +179,7 @@ protected function performFinalTasksForLaravelType(): void

public function writeHtmlDocs(array $groupedEndpoints): void
{
c::info('Writing HTML docs...');
c::info('Writing ' . ($this->isStatic ? 'HTML' : 'Blade') . ' docs...');

// Then we convert them to HTML, and throw in the endpoints as well.
/** @var HtmlWriter $writer */
Expand All @@ -191,14 +192,14 @@ public function writeHtmlDocs(array $groupedEndpoints): void

if ($this->isStatic) {
$outputPath = rtrim($this->staticTypeOutputPath, '/') . '/';
c::success("Wrote HTML docs to: $outputPath");
c::success("Wrote HTML docs and assets to: $outputPath");
$this->generatedFiles['html'] = realpath("{$outputPath}index.html");
$assetsOutputPath = $outputPath;
} else {
$outputPath = rtrim($this->laravelTypeOutputPath, '/') . '/';
c::success("Wrote Blade docs to: $outputPath");
$this->generatedFiles['blade'] = realpath("{$outputPath}index.blade.php");
$assetsOutputPath = app()->get('path.public').$this->laravelAssetsPath;
$assetsOutputPath = app()->get('path.public') . $this->laravelAssetsPath;
c::success("Wrote Laravel assets to: " . realpath($assetsOutputPath));
}
$this->generatedFiles['assets']['js'] = realpath("{$assetsOutputPath}js");
Expand Down

0 comments on commit cc6c95e

Please sign in to comment.