From ad9d3c5a8fc757a13ac1d6c057cf81ffe10c0aa0 Mon Sep 17 00:00:00 2001
From: Guy Sartorelli <36352093+GuySartorelli@users.noreply.github.com>
Date: Fri, 27 Sep 2024 09:59:30 +1200
Subject: [PATCH] API Update API to reflect changes to CLI interaction (#58)
---
README.md | 12 ++-
_config/config.yml | 33 ++------
composer.json | 10 +--
src/Clear.php | 70 ----------------
src/GraphQLSchemaInitTask.php | 154 +++++++++++-----------------------
src/SchemaClear.php | 58 +++++++++++++
6 files changed, 125 insertions(+), 212 deletions(-)
delete mode 100644 src/Clear.php
create mode 100644 src/SchemaClear.php
diff --git a/README.md b/README.md
index 5890942..076e738 100644
--- a/README.md
+++ b/README.md
@@ -16,17 +16,15 @@ This module adds an implementation of [graphiql](https://github.com/graphql/grap
### Accessing the IDE
-**In GraphQL 3.x**, it can be accessed at `/dev/graphiql/`.
-
-**In GraphQL 4.x+**, it can be accessed at `/dev/graphql/ide`.
+It can be accessed at `/dev/graphql/ide`.
This is because GraphQL 4+ has its own `DevelopmentAdmin` controller.
-The GraphQL v4 version of the module allows you to clear your schema by calling the `/dev/graphql/clear` task.
+The GraphQL v4 version of the module allows you to clear your schema by running `sake graphql:clear`.
## Security
-By default, the tool has the same restrictions as other development tools like `dev/build`:
+By default, the tool has the same restrictions as other development tools like `/dev/build`:
* In "dev" mode, it's available without authentication
* In "test" and "live" mode, it requires ADMIN permissions
@@ -100,13 +98,13 @@ You must be in CLI mode to use this task
To view help for the task to see what options are available:
```bash
-vendor/bin/sake dev/tasks/GraphQLSchemaInitTask help=1
+vendor/bin/sake tasks:GraphQLSchemaInitTask --help
```
To run the task with with minimal options:
```bash
-vendor/bin/sake dev/tasks/GraphQLSchemaInitTask namespace=App
+vendor/bin/sake tasks:GraphQLSchemaInitTask --namespace=App
```
diff --git a/_config/config.yml b/_config/config.yml
index 0a99422..39478ad 100644
--- a/_config/config.yml
+++ b/_config/config.yml
@@ -9,33 +9,14 @@ SilverStripe\GraphQLDevTools\Controller:
default_schema: default
---
-Name: graphql4-devtool-routes
+Name: devtool-routes
After:
- 'graphql-dev'
-Only:
- classexists: 'SilverStripe\GraphQL\Schema\Schema'
---
SilverStripe\GraphQL\Dev\DevelopmentAdmin:
- registered_controllers:
- ide:
- controller: 'SilverStripe\GraphQLDevTools\Controller'
- links:
- ide: Run the GraphQL IDE
- clear:
- controller: SilverStripe\GraphQLDevTools\Clear
- links:
- clear: Clear the GraphQL schema
-
----
-Name: graphql3-devtool-routes
-After:
- - 'graphql-dev'
-Except:
- classexists: 'SilverStripe\GraphQL\Schema\Schema'
----
-SilverStripe\Dev\DevelopmentAdmin:
- registered_controllers:
- graphiql:
- controller: 'SilverStripe\GraphQLDevTools\Controller'
-SilverStripe\GraphQLDevTools\Controller:
- default_route: 'graphql'
+ commands:
+ 'graphql/clear': 'SilverStripe\GraphQLDevTools\SchemaClear'
+ controllers:
+ 'graphql/ide':
+ class: 'SilverStripe\GraphQLDevTools\Controller'
+ description: Run the GraphQL IDE
diff --git a/composer.json b/composer.json
index 9acaa51..90ce595 100644
--- a/composer.json
+++ b/composer.json
@@ -1,16 +1,16 @@
{
"name": "silverstripe/graphql-devtools",
- "description": "Tools to help developers building new applications on SilverStripe’s GraphQL API",
+ "description": "Tools to help developers building new applications on SilverStripe's GraphQL API",
"type": "silverstripe-vendormodule",
"license": "BSD-3-Clause",
"require": {
- "silverstripe/graphql": "^3 || ^4 || ^5 || ^6",
- "symfony/finder": "^4 || ^5 || ^6 || ^7",
- "symfony/filesystem": "^4 || ^5 || ^6 || ^7"
+ "silverstripe/graphql": "^6",
+ "symfony/finder": "^7",
+ "symfony/filesystem": "^7"
},
"require-dev": {
"phpunit/phpunit": "^11.3",
- "silverstripe/framework": "^4.10",
+ "silverstripe/framework": "^6",
"squizlabs/php_codesniffer": "^3"
},
"autoload": {
diff --git a/src/Clear.php b/src/Clear.php
deleted file mode 100644
index 2004431..0000000
--- a/src/Clear.php
+++ /dev/null
@@ -1,70 +0,0 @@
- 'clear',
- ];
-
- private static $allowed_actions = [
- 'clear',
- ];
-
- public function __construct()
- {
- parent::__construct();
- if (!method_exists(Deprecation::class, 'withSuppressedNotice')
- || !method_exists(Deprecation::class, 'notice')
- ) {
- return;
- }
- Deprecation::withSuppressedNotice(function () {
- Deprecation::notice(
- '1.1.0',
- 'Will be replaced with SilverStripe\GraphQLDevTools\SchemaClear',
- Deprecation::SCOPE_CLASS
- );
- });
- }
-
- public function clear(HTTPRequest $request): void
- {
- $logger = Injector::inst()->get(LoggerInterface::class . '.graphql-build');
- $dirName = CodeGenerationStore::config()->get('dirName');
- $expectedPath = BASE_PATH . DIRECTORY_SEPARATOR . $dirName;
- $fs = new Filesystem();
-
- $finder = new Finder();
- // Make finder not recursive
- $finder->depth('== 0');
-
- $logger->info('Clearing GraphQL code generation directory');
- if ($fs->exists($expectedPath)) {
- $logger->info('Directory has been found');
- if ($finder->in($expectedPath)->hasResults()) {
- foreach ($finder as $file) {
- $logger->info('Removing ' . $file->getFilename());
- $fs->remove($file->getRealPath());
- }
- $logger->info('Directory now empty');
- } else {
- $logger->info('Directory is already empty.');
- }
- } else {
- $logger->info('Directory was not found. There is nothing to clear');
- }
- }
-}
diff --git a/src/GraphQLSchemaInitTask.php b/src/GraphQLSchemaInitTask.php
index c074586..57dee78 100644
--- a/src/GraphQLSchemaInitTask.php
+++ b/src/GraphQLSchemaInitTask.php
@@ -2,25 +2,26 @@
namespace SilverStripe\GraphQLDevTools;
-use SilverStripe\Control\Director;
-use SilverStripe\Control\HTTPRequest;
+use Composer\Console\Input\InputOption;
use SilverStripe\Core\Manifest\ModuleManifest;
use SilverStripe\Core\Path;
use SilverStripe\Dev\BuildTask;
-use SilverStripe\GraphQL\Config\Configuration;
+use SilverStripe\PolyExecution\PolyOutput;
+use Symfony\Component\Console\Command\Command;
+use Symfony\Component\Console\Input\InputInterface;
/**
* A task that initialises a GraphQL 4+ schema with boilerplate config and files.
*/
class GraphQLSchemaInitTask extends BuildTask
{
- private static $segment = 'GraphQLSchemaInitTask';
+ private static bool $can_run_in_cli = false;
- protected $enabled = true;
+ protected static string $commandName = 'GraphQLSchemaInitTask';
- protected $title = 'Initialise a new GraphQL schema';
+ protected static string $description = 'Boilerplate setup for a new GraphQL schema';
- protected $description = 'Boilerplate setup for a new GraphQL schema';
+ protected string $title = 'Initialise a new GraphQL schema';
private string $appNamespace;
@@ -38,79 +39,38 @@ class GraphQLSchemaInitTask extends BuildTask
private string $perms = '';
- /**
- * @param HTTPRequest $request
- */
- public function run($request)
+ protected function execute(InputInterface $input, PolyOutput $output): int
{
- if (!Director::is_cli()) {
- echo "This task can only be run from CLI\n";
- return;
- }
-
- if (!class_exists(Configuration::class)) {
- echo "This task requires GraphQL v4+ to be installed\n";
- return;
- }
-
- if ($request->getVar('help')) {
- $this->showHelp();
- return;
- }
-
- $appNamespace = $request->getVar('namespace');
-
- if (!$appNamespace) {
- $segment = static::$segment;
- echo "Please provide a base namespace for your app, e.g. \"namespace=App\" or \"namespace=MyVendor\MyProject\".\nFor help, run \"dev/tasks/$segment help=1\"\n";
- return;
- }
-
- $this->appNamespace = $appNamespace;
-
$this->projectDir = ModuleManifest::config()->get('project');
+ $this->appNamespace = $input->getOption('namespace');
+ $this->schemaName = $input->getOption('name');
+ $this->graphqlConfigDir = $input->getOption('graphqlConfigDir');
+ $this->graphqlCodeDir = $input->getOption('graphqlCodeDir');
+ $this->endpoint = $input->getOption('endpoint');
+ $this->srcDir = $input->getOption('srcDir');
- $schemaName = $request->getVar('name');
- if ($schemaName) {
- $this->schemaName = $schemaName;
- }
-
- $graphqlConfigDir = $request->getVar('graphqlConfigDir');
- if ($graphqlConfigDir) {
- $this->graphqlConfigDir = $graphqlConfigDir;
- }
-
- $graphqlCodeDir = $request->getVar('graphqlCodeDir');
- if ($graphqlCodeDir) {
- $this->graphqlCodeDir = $graphqlCodeDir;
- }
-
- $endpoint = $request->getVar('endpoint');
- if ($endpoint) {
- $this->endpoint = $endpoint;
- }
-
- $srcDir = $request->getVar('srcDir');
- if ($srcDir) {
- $this->srcDir = $srcDir;
+ if (!$this->appNamespace) {
+ $output->writeln('Please provide a base namespace for your app, e.g. --namespace=App or --namespace=MyVendor\MyProject');
+ return Command::INVALID;
}
$absProjectDir = Path::join(BASE_PATH, $this->projectDir);
$this->perms = fileperms($absProjectDir);
- $this->createGraphQLConfig();
- $this->createProjectConfig();
- $this->createResolvers();
+ $this->createGraphQLConfig($output);
+ $this->createProjectConfig($output);
+ $this->createResolvers($output);
+ return Command::SUCCESS;
}
/**
* Creates the SS config in _config/graphql.yml
*/
- private function createProjectConfig(): void
+ private function createProjectConfig(PolyOutput $output): void
{
$absConfigFile = Path::join(BASE_PATH, $this->projectDir, '_config', 'graphql.yml');
if (file_exists($absConfigFile)) {
- echo "Config file $absConfigFile already exists. Skipping." . PHP_EOL;
+ $output->writeln("Config file $absConfigFile already exists. Skipping.");
return;
}
@@ -138,7 +98,7 @@ class: SilverStripe\GraphQL\Controller
SilverStripe\Control\Director:
rules:
$rules
-
+
SilverStripe\GraphQL\Schema\Schema:
schemas:
$this->schemaName:
@@ -154,15 +114,15 @@ class: SilverStripe\GraphQL\Controller
/**
* Creates the graphql schema specific config in _graphql/
*/
- private function createGraphQLConfig(): void
+ private function createGraphQLConfig(PolyOutput $output): void
{
$absGraphQLDir = Path::join(BASE_PATH, $this->projectDir, $this->graphqlConfigDir);
if (is_dir($absGraphQLDir)) {
- echo "GraphQL config directory already exists. Skipping." . PHP_EOL;
+ $output->writeln("GraphQL config directory already exists. Skipping.");
return;
}
- echo "Creating graphql config directory: $this->graphqlConfigDir" . PHP_EOL;
+ $output->writeln("Creating graphql config directory: $this->graphqlConfigDir");
mkdir($absGraphQLDir, $this->perms);
foreach (['models', 'config', 'types', 'queries', 'mutations'] as $file) {
touch(Path::join($absGraphQLDir, "$file.yml"));
@@ -190,7 +150,7 @@ private function createGraphQLConfig(): void
/**
* Creates an example resolvers class for autodiscovery in app/src/GraphQL/Resolvers.php
*/
- private function createResolvers(): void
+ private function createResolvers(PolyOutput $output): void
{
$absSrcDir = Path::join(BASE_PATH, $this->projectDir, $this->srcDir);
$absGraphQLCodeDir = Path::join($absSrcDir, $this->graphqlCodeDir);
@@ -199,11 +159,11 @@ private function createResolvers(): void
str_replace('/', '\\', $this->graphqlCodeDir)
]);
if (is_dir($absGraphQLCodeDir)) {
- echo "GraphQL code dir $this->graphqlCodeDir already exists. Skipping" . PHP_EOL;
+ $output->writeln("GraphQL code dir $this->graphqlCodeDir already exists. Skipping");
return;
}
- echo "Creating resolvers class in $graphqlNamespace" . PHP_EOL;
+ $output->writeln("Creating resolvers class in $graphqlNamespace");
mkdir($absGraphQLCodeDir, $this->perms, true);
$resolverFile = Path::join($absGraphQLCodeDir, 'Resolvers.php');
$moreInfo = 'https://docs.silverstripe.org/en/developer_guides/graphql/'
@@ -235,44 +195,30 @@ public static function resolveMyQuery(\$obj, array \$args, array \$context): arr
file_put_contents($resolverFile, $resolverCode);
}
- /**
- * Outputs help text to the console
- */
- private function showHelp(): void
+ public function getOptions(): array
{
- $segment = static::$segment;
- echo <<get('dirName');
+ $expectedPath = BASE_PATH . DIRECTORY_SEPARATOR . $dirName;
+ $fs = new Filesystem();
+
+ $finder = new Finder();
+ // Make finder not recursive
+ $finder->depth('== 0');
+
+ if ($fs->exists($expectedPath)) {
+ $output->writeln('Directory has been found');
+ if ($finder->in($expectedPath)->hasResults()) {
+ foreach ($finder as $file) {
+ $output->writeln('Removing ' . $file->getFilename());
+ $fs->remove($file->getRealPath());
+ }
+ $output->writeln('Directory now empty');
+ } else {
+ $output->writeln('Directory is already empty.');
+ }
+ } else {
+ $output->writeln('Directory was not found. There is nothing to clear');
+ }
+ return Command::SUCCESS;
+ }
+
+ protected function getHeading(): string
+ {
+ return 'Clearing GraphQL code generation directory';
+ }
+}