Skip to content

Commit

Permalink
Allow config and temp directories to be configured independently. (#763)
Browse files Browse the repository at this point in the history
  • Loading branch information
ArclightHub authored Dec 12, 2023
1 parent 47b77b2 commit 7263152
Show file tree
Hide file tree
Showing 12 changed files with 215 additions and 86 deletions.
9 changes: 5 additions & 4 deletions camel/Camel.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,20 +6,21 @@
use Illuminate\Support\Arr;
use Illuminate\Support\Str;
use Knuckles\Camel\Output\OutputEndpointData;
use Knuckles\Scribe\Configuration\PathConfig;
use Knuckles\Scribe\Tools\Utils;
use Symfony\Component\Yaml\Yaml;


class Camel
{
public static function cacheDir(string $docsName = 'scribe'): string
public static function cacheDir(PathConfig $paths): string
{
return ".$docsName/endpoints.cache";
return $paths->intermediateOutputPath('endpoints.cache');
}

public static function camelDir(string $docsName = 'scribe'): string
public static function camelDir(PathConfig $paths): string
{
return ".$docsName/endpoints";
return $paths->intermediateOutputPath('endpoints');
}

/**
Expand Down
1 change: 1 addition & 0 deletions phpunit.xml
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@
<file>tests/Unit/ExtractorTest.php</file>
<file>tests/Unit/ExtractorPluginSystemTest.php</file>
<file>tests/Unit/ConfigDifferTest.php</file>
<file>tests/Unit/PathConfigurationTest.php</file>
</testsuite>
<testsuite name="Unit Tests 2">
<file>tests/Unit/ExtractedEndpointDataTest.php</file>
Expand Down
39 changes: 27 additions & 12 deletions src/Commands/GenerateDocumentation.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
use Illuminate\Support\Facades\URL;
use Illuminate\Support\Str;
use Knuckles\Camel\Camel;
use Knuckles\Scribe\Configuration\PathConfig;
use Knuckles\Scribe\GroupedEndpoints\GroupedEndpointsFactory;
use Knuckles\Scribe\Matching\RouteMatcherInterface;
use Knuckles\Scribe\Tools\ConsoleOutputUtils as c;
Expand All @@ -23,7 +24,8 @@ 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=scribe : choose which config file to use}
{--config=scribe : Choose which config file to use}
{--scribe-dir= : Specify the directory where Scribe stores its intermediate output and cache. Defaults to `.<config_file>`}
";

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

protected bool $forcing;

protected string $configName;
protected PathConfig $paths;

public function handle(RouteMatcherInterface $routeMatcher, GroupedEndpointsFactory $groupedEndpointsFactory): void
{
Expand All @@ -47,9 +49,9 @@ public function handle(RouteMatcherInterface $routeMatcher, GroupedEndpointsFact
}

// Extraction stage - extract endpoint info either from app or existing Camel files (previously extracted data)
$groupedEndpointsInstance = $groupedEndpointsFactory->make($this, $routeMatcher, $this->configName);
$groupedEndpointsInstance = $groupedEndpointsFactory->make($this, $routeMatcher, $this->paths);
$extractedEndpoints = $groupedEndpointsInstance->get();
$userDefinedEndpoints = Camel::loadUserDefinedEndpoints(Camel::camelDir($this->configName));
$userDefinedEndpoints = Camel::loadUserDefinedEndpoints(Camel::camelDir($this->paths));
$groupedEndpoints = $this->mergeUserDefinedEndpoints($extractedEndpoints, $userDefinedEndpoints);

// Output stage
Expand All @@ -61,7 +63,7 @@ public function handle(RouteMatcherInterface $routeMatcher, GroupedEndpointsFact
$this->writeExampleCustomEndpoint();
}

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

$this->upgradeConfigFileIfNeeded();
Expand Down Expand Up @@ -98,12 +100,19 @@ public function bootstrap(): void

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

$this->configName = $this->option('config');
if (!config($this->configName)) {
throw new \InvalidArgumentException("The specified config (config/{$this->configName}.php) doesn't exist.");
$configName = $this->option('config');
if (!config($configName)) {
throw new \InvalidArgumentException("The specified config (config/{$configName}.php) doesn't exist.");
}

$this->docConfig = new DocumentationConfig(config($this->configName));
$this->paths = new PathConfig(configName: $configName);
if ($this->hasOption('scribe-dir') && !empty($this->option('scribe-dir'))) {
$this->paths = new PathConfig(
configName: $configName, scribeDir: $this->option('scribe-dir')
);
}

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

// Force root URL so it works in Postman collection
$baseUrl = $this->docConfig->get('base_url') ?? config('app.url');
Expand Down Expand Up @@ -146,7 +155,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($this->configName) . '/custom.0.yaml');
copy(__DIR__ . '/../../resources/example_custom_endpoint.yaml', Camel::camelDir($this->paths) . '/custom.0.yaml');
}

protected function upgradeConfigFileIfNeeded(): void
Expand All @@ -155,12 +164,18 @@ protected function upgradeConfigFileIfNeeded(): void

$this->info("Checking for any pending upgrades to your config file...");
try {
if (! $this->laravel['files']->exists($this->laravel->configPath("{$this->configName}.php"))) {
if (!$this->laravel['files']->exists(
$this->laravel->configPath($this->paths->configFileName())
)
) {
$this->info("No config file to upgrade.");
return;
}

$upgrader = Upgrader::ofConfigFile("config/{$this->configName}.php", __DIR__ . '/../../config/scribe.php')
$upgrader = Upgrader::ofConfigFile(
userOldConfigRelativePath: "config/{$this->paths->configFileName()}",
sampleNewConfigAbsolutePath: __DIR__ . '/../../config/scribe.php'
)
->dontTouch(
'routes', 'example_languages', 'database_connections_to_transact', 'strategies', 'laravel.middleware',
'postman.overrides', 'openapi.overrides', 'groups', 'examples.models_source'
Expand Down
6 changes: 4 additions & 2 deletions src/Commands/Upgrade.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

use Illuminate\Console\Command;
use Knuckles\Camel\Camel;
use Knuckles\Scribe\Configuration\PathConfig;
use Knuckles\Scribe\GroupedEndpoints\GroupedEndpointsFactory;
use Knuckles\Scribe\Scribe;
use Shalvah\Upgrader\Upgrader;
Expand Down Expand Up @@ -102,7 +103,8 @@ protected function migrateToConfigFileSort()
$this->info("We'll automatically import your current sorting into the config item `groups.order`.");

$defaultGroup = config($this->configName.".default_group");
$extractedEndpoints = GroupedEndpointsFactory::fromCamelDir($this->configName)->get();
$pathConfig = new PathConfig($this->configName);
$extractedEndpoints = GroupedEndpointsFactory::fromCamelDir($pathConfig)->get();

$order = array_map(function (array $group) {
return array_map(function (array $endpoint) {
Expand All @@ -112,7 +114,7 @@ protected function migrateToConfigFileSort()
$groupsOrder = array_keys($order);
$keyIndices = array_flip($groupsOrder);

$userDefinedEndpoints = Camel::loadUserDefinedEndpoints(Camel::camelDir($this->configName));
$userDefinedEndpoints = Camel::loadUserDefinedEndpoints(Camel::camelDir($pathConfig));

if ($userDefinedEndpoints) {
foreach ($userDefinedEndpoints as $endpoint) {
Expand Down
47 changes: 47 additions & 0 deletions src/Configuration/PathConfig.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
<?php

namespace Knuckles\Scribe\Configuration;

/**
* A home for path configurations. The important paths Scribe depends on.
*/
class PathConfig
{
public function __construct(
public string $configName = 'scribe',
// FOr lack of a better name, we'll call this `scribeDir`.
// It's sort of the cache dir, where Scribe stores its intermediate outputs.
protected ?string $scribeDir = null
)
{
if (is_null($this->scribeDir)) {
$this->scribeDir = ".{$this->configName}";
}
}

public function outputPath(string $resolvePath = null, string $separator = '/'): string
{
if (is_null($resolvePath)) {
return $this->configName;
}

return "{$this->configName}{$separator}{$resolvePath}";
}

public function configFileName(): string
{
return "{$this->configName}.php";
}

/**
* The directory where Scribe writes its intermediate output (default is .<config> ie .scribe)
*/
public function intermediateOutputPath(string $resolvePath = null, string $separator = '/'): string
{
if (is_null($resolvePath)) {
return $this->scribeDir;
}

return "{$this->scribeDir}{$separator}{$resolvePath}";
}
}
12 changes: 8 additions & 4 deletions src/Extracting/ApiDetails.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

namespace Knuckles\Scribe\Extracting;

use Knuckles\Scribe\Configuration\PathConfig;
use Knuckles\Scribe\Tools\ConsoleOutputUtils as c;
use Knuckles\Scribe\Tools\Utils as u;
use Knuckles\Scribe\Tools\DocumentationConfig;
Expand All @@ -23,11 +24,14 @@ class ApiDetails

private array $lastKnownFileContentHashes = [];

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

Expand Down
32 changes: 23 additions & 9 deletions src/GroupedEndpoints/GroupedEndpointsFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,34 +3,48 @@
namespace Knuckles\Scribe\GroupedEndpoints;

use Knuckles\Scribe\Commands\GenerateDocumentation;
use Knuckles\Scribe\Configuration\PathConfig;
use Knuckles\Scribe\Matching\RouteMatcherInterface;

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

if ($command->shouldExtract()) {
return static::fromApp($command, $routeMatcher, true, $docsName);
return static::fromApp(
command: $command,
routeMatcher: $routeMatcher,
preserveUserChanges: true,
paths: $paths
);
}

return static::fromCamelDir($docsName);
return static::fromCamelDir($paths);
}

public static function fromApp(
GenerateDocumentation $command,
RouteMatcherInterface $routeMatcher,
bool $preserveUserChanges,
string $docsName = 'scribe'
PathConfig $paths
): GroupedEndpointsFromApp {
return new GroupedEndpointsFromApp($command, $routeMatcher, $preserveUserChanges, $docsName);
return new GroupedEndpointsFromApp($command, $routeMatcher, $paths, $preserveUserChanges);
}

public static function fromCamelDir(string $docsName = 'scribe'): GroupedEndpointsFromCamelDir
public static function fromCamelDir(PathConfig $paths): GroupedEndpointsFromCamelDir
{
return new GroupedEndpointsFromCamelDir($docsName);
return new GroupedEndpointsFromCamelDir($paths);
}
}
16 changes: 9 additions & 7 deletions src/GroupedEndpoints/GroupedEndpointsFromApp.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
use Knuckles\Camel\Extraction\ExtractedEndpointData;
use Knuckles\Camel\Output\OutputEndpointData;
use Knuckles\Scribe\Commands\GenerateDocumentation;
use Knuckles\Scribe\Configuration\PathConfig;
use Knuckles\Scribe\Exceptions\CouldntGetRouteDetails;
use Knuckles\Scribe\Extracting\ApiDetails;
use Knuckles\Scribe\Extracting\Extractor;
Expand All @@ -34,14 +35,15 @@ class GroupedEndpointsFromApp implements GroupedEndpointsContract
public static string $cacheDir;

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

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

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

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

/**
Expand Down
11 changes: 5 additions & 6 deletions src/GroupedEndpoints/GroupedEndpointsFromCamelDir.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,25 +3,24 @@
namespace Knuckles\Scribe\GroupedEndpoints;

use Knuckles\Camel\Camel;
use Knuckles\Scribe\Configuration\PathConfig;

class GroupedEndpointsFromCamelDir implements GroupedEndpointsContract
{
protected string $docsName;

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

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

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

public function hasEncounteredErrors(): bool
Expand Down
Loading

0 comments on commit 7263152

Please sign in to comment.