Skip to content

Commit

Permalink
API Update API to reflect changes to CLI interaction
Browse files Browse the repository at this point in the history
  • Loading branch information
GuySartorelli committed Sep 10, 2024
1 parent 5897c5a commit 1853ca5
Show file tree
Hide file tree
Showing 6 changed files with 125 additions and 191 deletions.
12 changes: 5 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 runninghe `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
Expand Down Expand Up @@ -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
```


Expand Down
33 changes: 7 additions & 26 deletions _config/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
10 changes: 5 additions & 5 deletions composer.json
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
{
"name": "silverstripe/graphql-devtools",
"description": "Tools to help developers building new applications on SilverStripes 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": "^9.5",
"silverstripe/framework": "^4.10",
"silverstripe/framework": "^6",
"squizlabs/php_codesniffer": "^3"
},
"autoload": {
Expand Down
49 changes: 0 additions & 49 deletions src/Clear.php

This file was deleted.

154 changes: 50 additions & 104 deletions src/GraphQLSchemaInitTask.php
Original file line number Diff line number Diff line change
Expand Up @@ -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\HybridExecution\HybridOutput;
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;

Expand All @@ -38,79 +39,38 @@ class GraphQLSchemaInitTask extends BuildTask

private string $perms = '';

/**
* @param HTTPRequest $request
*/
public function run($request)
protected function execute(InputInterface $input, HybridOutput $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. <info>--namespace=App</info> or <info>--namespace=MyVendor\MyProject</info>');
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(HybridOutput $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;
}

Expand Down Expand Up @@ -138,7 +98,7 @@ class: SilverStripe\GraphQL\Controller
SilverStripe\Control\Director:
rules:
$rules
SilverStripe\GraphQL\Schema\Schema:
schemas:
$this->schemaName:
Expand All @@ -154,15 +114,15 @@ class: SilverStripe\GraphQL\Controller
/**
* Creates the graphql schema specific config in _graphql/
*/
private function createGraphQLConfig(): void
private function createGraphQLConfig(HybridOutput $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"));
Expand Down Expand Up @@ -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(HybridOutput $output): void
{
$absSrcDir = Path::join(BASE_PATH, $this->projectDir, $this->srcDir);
$absGraphQLCodeDir = Path::join($absSrcDir, $this->graphqlCodeDir);
Expand All @@ -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/'
Expand Down Expand Up @@ -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 <<<TXT
return [
new InputOption('namespace', null, InputOption::VALUE_REQUIRED, 'The root namespace (required)'),
new InputOption('name', null, InputOption::VALUE_REQUIRED, 'The name of the schema', 'default'),
new InputOption('graphqlConfigDir', null, InputOption::VALUE_REQUIRED, 'The folder where the flushless graphql config files will go', '_graphql'),
new InputOption(
'graphqlCodeDir',
null,
InputOption::VALUE_REQUIRED,
'The subfolder of src/ where your GraphQL code (the resolver class) will go. Follows PSR-4 based on the namespace argument',
'GraphQL'
),
new InputOption('endpoint', null, InputOption::VALUE_REQUIRED, 'The endpoint to use for the schema', 'graphql'),
new InputOption('srcDir', null, InputOption::VALUE_REQUIRED, 'The subfolder of the project directory where the src code lives', 'src'),
];
}

****
public static function getHelp(): string
{
return <<<TXT
This task executes a lot of the boilerplate required to build a new GraphQL schema. It will
generate a few files in your project directory. Any files that already exist will not be
overwritten. The task can be run multiple times and is non-destructive.
****
-- Example:
$ vendor/bin/sake dev/tasks/$segment namespace="MyAgency\MyApp"
-- Arguments:
namespace
The root namespace. Required.
name
The name of the schema. Default: "default" (optional)
graphqlConfigDir
The folder where the flushless graphql config files will go. Default: "_graphql" (optional)
graphqlCodeDir
The subfolder of src/ where your GraphQL code (the resolver class) will go. Follows PSR-4 based on the namespace argument. Default: "GraphQL". (optional)
endpoint
The endpoint to use for the schema. Default: "graphql" (optional)
srcDir
The subfolder of the project directory where the src code lives. Default: "src" (optional)
TXT;
}
}
Loading

0 comments on commit 1853ca5

Please sign in to comment.