From 4bf6d3bf953137f9ad3d932485461b8d1bbfa54f Mon Sep 17 00:00:00 2001 From: Guy Sartorelli Date: Wed, 28 Aug 2024 14:11:33 +1200 Subject: [PATCH] API Update API to reflect changes to CLI interaction --- _config/dev.yml | 17 +- _config/logging.yml | 2 +- src/Dev/Build.php | 97 +++++++----- src/Dev/DevelopmentAdmin.php | 146 ------------------ ...uildExtension.php => DbBuildExtension.php} | 13 +- src/Schema/Logger.php | 60 +++++-- 6 files changed, 120 insertions(+), 215 deletions(-) delete mode 100644 src/Dev/DevelopmentAdmin.php rename src/Extensions/{DevBuildExtension.php => DbBuildExtension.php} (79%) diff --git a/_config/dev.yml b/_config/dev.yml index c19ab4e1..b87ddecf 100644 --- a/_config/dev.yml +++ b/_config/dev.yml @@ -1,19 +1,10 @@ --- Name: graphql-dev --- -SilverStripe\ORM\DatabaseAdmin: +SilverStripe\HybridExecution\Command\DbBuild: extensions: - - SilverStripe\GraphQL\Extensions\DevBuildExtension + - SilverStripe\GraphQL\Extensions\DbBuildExtension SilverStripe\Dev\DevelopmentAdmin: - registered_controllers: - graphql: - controller: SilverStripe\GraphQL\Dev\DevelopmentAdmin - links: - graphql: 'List GraphQL development tools' -SilverStripe\GraphQL\Dev\DevelopmentAdmin: - registered_controllers: - build: - controller: SilverStripe\GraphQL\Dev\Build - links: - build: Build the GraphQL schema + commands: + 'graphql/build': 'SilverStripe\GraphQL\Dev\Build' diff --git a/_config/logging.yml b/_config/logging.yml index e19a72a8..0de75516 100644 --- a/_config/logging.yml +++ b/_config/logging.yml @@ -4,7 +4,7 @@ after: '#logging' --- SilverStripe\Core\Injector\Injector: - # Omits the HTTPOutputHandler from the logger so errors won't appear in output + # Omits the ErrorOutputHandler from the logger so errors won't appear in output Psr\Log\LoggerInterface.graphql-quiet: type: singleton class: Monolog\Logger diff --git a/src/Dev/Build.php b/src/Dev/Build.php index 97eb2b07..80b1b645 100644 --- a/src/Dev/Build.php +++ b/src/Dev/Build.php @@ -4,11 +4,10 @@ namespace SilverStripe\GraphQL\Dev; use Psr\Log\LoggerInterface; -use SilverStripe\Control\Controller; -use SilverStripe\Control\Director; -use SilverStripe\Control\HTTPRequest; use SilverStripe\Core\Injector\Injector; -use SilverStripe\Dev\DebugView; +use SilverStripe\Dev\DevelopmentAdmin; +use SilverStripe\HybridExecution\Command\HybridCommand; +use SilverStripe\HybridExecution\HybridOutput; use SilverStripe\GraphQL\Schema\DataObject\FieldAccessor; use SilverStripe\GraphQL\Schema\Exception\EmptySchemaException; use SilverStripe\GraphQL\Schema\Exception\SchemaBuilderException; @@ -18,38 +17,41 @@ use SilverStripe\GraphQL\Schema\SchemaBuilder; use SilverStripe\GraphQL\Schema\Storage\CodeGenerationStore; use SilverStripe\ORM\Connect\NullDatabaseException; +use SilverStripe\Security\PermissionProvider; +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Input\InputOption; -class Build extends Controller +class Build extends HybridCommand implements PermissionProvider { - private static $url_handlers = [ - '' => 'build' - ]; + protected static string $commandName = 'graphql:build'; + + protected static string $description = 'Build the GraphQL schema(s)'; - private static $allowed_actions = [ - 'build' + private static string|array|null $permissions_for_browser_execution = [ + 'ADMIN', + 'ALL_DEV_ADMIN', + 'CAN_DEV_GRAPHQL', ]; - /** - * @throws SchemaBuilderException - * @throws SchemaNotFoundException - */ - public function build(HTTPRequest $request): void + public function getTitle(): string { - $isBrowser = !Director::is_cli(); - if ($isBrowser) { - $renderer = DebugView::create(); - echo $renderer->renderHeader(); - echo $renderer->renderInfo("GraphQL Schema Builder", Director::absoluteBaseURL()); - echo "
"; - } - $clear = true; - - $this->buildSchema($request->getVar('schema'), $clear); + return 'GraphQL Schema Builder'; + } - if ($isBrowser) { - echo "
"; - echo $renderer->renderFooter(); + public function run(InputInterface $input, HybridOutput $output): int + { + $originalLogger = Injector::inst()->get(LoggerInterface::class . '.graphql-build'); + try { + $logger = Logger::singleton(); + $logger->setOutput($output); + Injector::inst()->registerService($logger, LoggerInterface::class . '.graphql-build'); + $this->buildSchema($input->getOption('schema'), true, $output); + } finally { + // Restore default logger back to its starting state + Injector::inst()->registerService($originalLogger, LoggerInterface::class . '.graphql-build'); } + return Command::SUCCESS; } /** @@ -97,15 +99,15 @@ public function buildSchema(string $key = null, bool $clear = true): void break; } } - $logger->warning(" - Your schema configuration requires access to the database. This can happen + $logger->warning( + "Your schema configuration requires access to the database. This can happen when you add fields that require type introspection (i.e. custom getters). It is recommended that you specify an explicit type when adding custom getters - to your schema."); + to your schema." + ); if ($candidate) { $logger->warning(sprintf( - " - This most likely happened when you tried to add the field '%s' to '%s'", + "This most likely happened when you tried to add the field '%s' to '%s'", $candidate['args'][1], get_class($candidate['args'][0]) )); @@ -114,9 +116,32 @@ public function buildSchema(string $key = null, bool $clear = true): void throw $e; } - $logger->info( - Benchmark::end('build-schema-' . $key, 'Built schema in %sms.') - ); + $logger->info(Benchmark::end('build-schema-' . $key, 'Built schema in %sms.')); } } + + public function getOptions(): array + { + return [ + new InputOption( + 'schema', + null, + InputOption::VALUE_REQUIRED, + 'The name of the schema to be built. If not passed, all schemas will be built', + suggestedValues: array_keys(Schema::config()->get('schemas') ?? []) + ) + ]; + } + + public function providePermissions(): array + { + return [ + 'CAN_DEV_GRAPHQL' => [ + 'name' => _t(__CLASS__ . '.CAN_DEV_GRAPHQL_DESCRIPTION', 'Can view and execute /dev/graphql'), + 'help' => _t(__CLASS__ . '.CAN_DEV_GRAPHQL_HELP', 'Can view and execute GraphQL development tools (/dev/graphql).'), + 'category' => DevelopmentAdmin::permissionsCategory(), + 'sort' => 80 + ], + ]; + } } diff --git a/src/Dev/DevelopmentAdmin.php b/src/Dev/DevelopmentAdmin.php deleted file mode 100644 index d1e29cc0..00000000 --- a/src/Dev/DevelopmentAdmin.php +++ /dev/null @@ -1,146 +0,0 @@ - 'index', - '$Action' => 'runRegisteredController', - ]; - - private static $init_permissions = [ - 'ADMIN', - 'ALL_DEV_ADMIN', - 'CAN_DEV_GRAPHQL', - ]; - - protected function init() - { - parent::init(); - - if (RootDevelopmentAdmin::config()->get('deny_non_cli') && !Director::is_cli()) { - return $this->httpError(404); - } - - if (!$this->canInit()) { - Security::permissionFailure($this); - } - - // Define custom logger - $logger = Logger::singleton(); - Injector::inst()->registerService($logger, LoggerInterface::class . '.graphql-build'); - } - - public function index(HTTPRequest $request) - { - // Web mode - if (!Director::is_cli()) { - $renderer = DebugView::create(); - echo $renderer->renderHeader(); - echo $renderer->renderInfo("Silverstripe CMS GraphQL Tools", Director::absoluteBaseURL()); - $base = Director::baseURL(); - - echo '