forked from drupal-graphql/graphql
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(schema): Allow altering of schemas by introducing alter events i…
…n a new base class (drupal-graphql#1301) (drupal-graphql#1302)
- Loading branch information
Showing
8 changed files
with
555 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,58 @@ | ||
<?php | ||
|
||
declare(strict_types = 1); | ||
|
||
namespace Drupal\graphql\Event; | ||
|
||
use Drupal\Component\EventDispatcher\Event; | ||
|
||
/** | ||
* Represents an event that is triggered to alter schema data. | ||
*/ | ||
class AlterSchemaDataEvent extends Event { | ||
|
||
/** | ||
* Event fired to alter schema data. | ||
* | ||
* @var string | ||
*/ | ||
const EVENT_NAME = 'graphql.sdl.alter_schema'; | ||
|
||
/** | ||
* The schema array data. | ||
* | ||
* @var array | ||
*/ | ||
protected $schemaData; | ||
|
||
/** | ||
* AlterSchemaDataEvent constructor. | ||
* | ||
* @param array $schemaData | ||
* The schema data reference. | ||
*/ | ||
public function __construct(array &$schemaData) { | ||
$this->schemaData = $schemaData; | ||
} | ||
|
||
/** | ||
* Returns the schema data. | ||
* | ||
* @return array | ||
* The schema data. | ||
*/ | ||
public function getSchemaData(): array { | ||
return $this->schemaData; | ||
} | ||
|
||
/** | ||
* Sets the schema data. | ||
* | ||
* @param array $schemaData | ||
* The schema data. | ||
*/ | ||
public function setSchemaData(array $schemaData): void { | ||
$this->schemaData = $schemaData; | ||
} | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,58 @@ | ||
<?php | ||
|
||
declare(strict_types = 1); | ||
|
||
namespace Drupal\graphql\Event; | ||
|
||
use Drupal\Component\EventDispatcher\Event; | ||
|
||
/** | ||
* Represents an event that is triggered to alter schema extension data. | ||
*/ | ||
class AlterSchemaExtensionDataEvent extends Event { | ||
|
||
/** | ||
* Event fired to alter schema extension data. | ||
* | ||
* @var string | ||
*/ | ||
const EVENT_NAME = 'graphql.sdl.alter_schema_extension'; | ||
|
||
/** | ||
* The schema array data. | ||
* | ||
* @var array | ||
*/ | ||
protected $schemaExtensionData; | ||
|
||
/** | ||
* AlterSchemaExtensionDataEvent constructor. | ||
* | ||
* @param array $schemaExtensionData | ||
* The schema extension data. | ||
*/ | ||
public function __construct(array $schemaExtensionData) { | ||
$this->schemaExtensionData = $schemaExtensionData; | ||
} | ||
|
||
/** | ||
* Returns the schema extension data. | ||
* | ||
* @return array | ||
* The schema extension data. | ||
*/ | ||
public function getSchemaExtensionData(): array { | ||
return $this->schemaExtensionData; | ||
} | ||
|
||
/** | ||
* Returns the schema extension data. | ||
* | ||
* @param array $schemaExtensionData | ||
* The schema extension data. | ||
*/ | ||
public function setSchemaExtensionData(array $schemaExtensionData): void { | ||
$this->schemaExtensionData = $schemaExtensionData; | ||
} | ||
|
||
} |
183 changes: 183 additions & 0 deletions
183
src/Plugin/GraphQL/Schema/AlterableComposableSchema.php
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,183 @@ | ||
<?php | ||
|
||
namespace Drupal\graphql\Plugin\GraphQL\Schema; | ||
|
||
use Drupal\Core\Cache\CacheBackendInterface; | ||
use Drupal\graphql\Event\AlterSchemaDataEvent; | ||
use Drupal\graphql\Event\AlterSchemaExtensionDataEvent; | ||
use Drupal\graphql\Plugin\SchemaExtensionPluginInterface; | ||
use GraphQL\Language\Parser; | ||
use Drupal\Component\EventDispatcher\ContainerAwareEventDispatcher; | ||
use Drupal\Core\Extension\ModuleHandlerInterface; | ||
use Drupal\graphql\Plugin\SchemaExtensionPluginManager; | ||
use Symfony\Component\DependencyInjection\ContainerInterface; | ||
|
||
/** | ||
* Allows to alter the graphql files data before parsing. | ||
* | ||
* @see \Drupal\graphql\Event\AlterSchemaDataEvent | ||
* @see \Drupal\graphql\Event\AlterSchemaExtensionDataEvent | ||
* | ||
* @Schema( | ||
* id = "alterable_composable", | ||
* name = "Alterable composable schema" | ||
* ) | ||
*/ | ||
class AlterableComposableSchema extends ComposableSchema { | ||
|
||
/** | ||
* The event dispatcher service. | ||
* | ||
* @var \Drupal\Component\EventDispatcher\ContainerAwareEventDispatcher | ||
*/ | ||
protected $dispatcher; | ||
|
||
/** | ||
* {@inheritdoc} | ||
* | ||
* @codeCoverageIgnore | ||
*/ | ||
public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) { | ||
return new static( | ||
$configuration, | ||
$plugin_id, | ||
$plugin_definition, | ||
$container->get('cache.graphql.ast'), | ||
$container->get('module_handler'), | ||
$container->get('plugin.manager.graphql.schema_extension'), | ||
$container->getParameter('graphql.config'), | ||
$container->get('event_dispatcher') | ||
); | ||
} | ||
|
||
/** | ||
* AlterableComposableSchema constructor. | ||
* | ||
* @param array $configuration | ||
* The plugin configuration array. | ||
* @param string $pluginId | ||
* The plugin id. | ||
* @param array $pluginDefinition | ||
* The plugin definition array. | ||
* @param \Drupal\Core\Cache\CacheBackendInterface $astCache | ||
* The cache bin for caching the parsed SDL. | ||
* @param \Drupal\Core\Extension\ModuleHandlerInterface $moduleHandler | ||
* The module handler service. | ||
* @param \Drupal\graphql\Plugin\SchemaExtensionPluginManager $extensionManager | ||
* The schema extension plugin manager. | ||
* @param array $config | ||
* The service configuration. | ||
* @param \Drupal\Component\EventDispatcher\ContainerAwareEventDispatcher $dispatcher | ||
* The event dispatcher. | ||
* | ||
* @codeCoverageIgnore | ||
*/ | ||
public function __construct( | ||
array $configuration, | ||
$pluginId, | ||
array $pluginDefinition, | ||
CacheBackendInterface $astCache, | ||
ModuleHandlerInterface $moduleHandler, | ||
SchemaExtensionPluginManager $extensionManager, | ||
array $config, | ||
ContainerAwareEventDispatcher $dispatcher | ||
) { | ||
parent::__construct( | ||
$configuration, | ||
$pluginId, | ||
$pluginDefinition, | ||
$astCache, | ||
$moduleHandler, | ||
$extensionManager, | ||
$config | ||
); | ||
$this->dispatcher = $dispatcher; | ||
} | ||
|
||
/** | ||
* Retrieves the parsed AST of the schema definition. | ||
* | ||
* Almost copy of the original method except it | ||
* provides alter schema event in order to manipulate data. | ||
* | ||
* @param array $extensions | ||
* The Drupal GraphQl schema plugins data. | ||
* | ||
* @return \GraphQL\Language\AST\DocumentNode | ||
* The parsed schema document. | ||
* | ||
* @throws \GraphQL\Error\SyntaxError | ||
* @throws \Drupal\Component\Plugin\Exception\InvalidPluginDefinitionException | ||
* | ||
* @see \Drupal\graphql\Plugin\GraphQL\Schema\ComposableSchema::getSchemaDocument() | ||
*/ | ||
protected function getSchemaDocument(array $extensions = []) { | ||
// Only use caching of the parsed document if we aren't in development mode. | ||
$cid = "schema:{$this->getPluginId()}"; | ||
if (empty($this->inDevelopment) && $cache = $this->astCache->get($cid)) { | ||
return $cache->data; | ||
} | ||
|
||
$extensions = array_filter(array_map(function (SchemaExtensionPluginInterface $extension) { | ||
return $extension->getBaseDefinition(); | ||
}, $extensions), function ($definition) { | ||
return !empty($definition); | ||
}); | ||
$schema = array_merge([$this->getSchemaDefinition()], $extensions); | ||
// Event in order to alter the schema data. | ||
$event = new AlterSchemaDataEvent($schema); | ||
$this->dispatcher->dispatch( | ||
$event, | ||
AlterSchemaDataEvent::EVENT_NAME | ||
); | ||
$ast = Parser::parse(implode("\n\n", $event->getSchemaData())); | ||
if (empty($this->inDevelopment)) { | ||
$this->astCache->set($cid, $ast, CacheBackendInterface::CACHE_PERMANENT, ['graphql']); | ||
} | ||
return $ast; | ||
} | ||
|
||
/** | ||
* Retrieves the parsed AST of the schema extension definitions. | ||
* | ||
* Almost copy of the original method except it | ||
* provides alter schema extension event in order to manipulate data. | ||
* | ||
* @param array $extensions | ||
* The Drupal GraphQl extensions data. | ||
* | ||
* @return \GraphQL\Language\AST\DocumentNode|null | ||
* The parsed schema document. | ||
* | ||
* @throws \GraphQL\Error\SyntaxError | ||
* | ||
* @see \Drupal\graphql\Plugin\GraphQL\Schema\ComposableSchema::getSchemaDocument() | ||
*/ | ||
protected function getExtensionDocument(array $extensions = []) { | ||
// Only use caching of the parsed document if we aren't in development mode. | ||
$cid = "extension:{$this->getPluginId()}"; | ||
if (empty($this->inDevelopment) && $cache = $this->astCache->get($cid)) { | ||
return $cache->data; | ||
} | ||
|
||
$extensions = array_filter(array_map(function (SchemaExtensionPluginInterface $extension) { | ||
return $extension->getExtensionDefinition(); | ||
}, $extensions), function ($definition) { | ||
return !empty($definition); | ||
}); | ||
|
||
// Event in order to alter the schema extension data. | ||
$event = new AlterSchemaExtensionDataEvent($extensions); | ||
$this->dispatcher->dispatch( | ||
$event, | ||
AlterSchemaExtensionDataEvent::EVENT_NAME | ||
); | ||
$ast = !empty($extensions) ? Parser::parse(implode("\n\n", $event->getSchemaExtensionData())) : NULL; | ||
if (empty($this->inDevelopment)) { | ||
$this->astCache->set($cid, $ast, CacheBackendInterface::CACHE_PERMANENT, ['graphql']); | ||
} | ||
|
||
return $ast; | ||
} | ||
|
||
} |
5 changes: 5 additions & 0 deletions
5
tests/modules/graphql_alterable_schema_test/graphql_alterable_schema_test.info.yml
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
type: module | ||
name: GraphQL Alterable Schema Test | ||
description: Tests if alterting schema is working. | ||
package: Testing | ||
hidden: TRUE |
6 changes: 6 additions & 0 deletions
6
tests/modules/graphql_alterable_schema_test/graphql_alterable_schema_test.services.yml
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
services: | ||
|
||
graphql_alterable_schema_test.alter_data_subscriber: | ||
class: Drupal\graphql_alterable_schema_test\EventSubscriber\GraphQlSubscriber | ||
tags: | ||
- { name: event_subscriber } |
51 changes: 51 additions & 0 deletions
51
tests/modules/graphql_alterable_schema_test/src/EventSubscriber/GraphQlSubscriber.php
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
<?php | ||
|
||
declare(strict_types = 1); | ||
|
||
namespace Drupal\graphql_alterable_schema_test\EventSubscriber; | ||
|
||
use Drupal\graphql\Event\AlterSchemaDataEvent; | ||
use Drupal\graphql\Event\AlterSchemaExtensionDataEvent; | ||
use Symfony\Component\EventDispatcher\EventSubscriberInterface; | ||
|
||
/** | ||
* Subscribes to the graphql schema alter events. | ||
*/ | ||
class GraphQlSubscriber implements EventSubscriberInterface { | ||
|
||
/** | ||
* {@inheritdoc} | ||
* | ||
* @return array | ||
* The event names to listen for, and the methods that should be executed. | ||
*/ | ||
public static function getSubscribedEvents() { | ||
return [ | ||
AlterSchemaExtensionDataEvent::EVENT_NAME => ['alterSchemaExtensionData'], | ||
AlterSchemaDataEvent::EVENT_NAME => ['alterSchemaData'], | ||
]; | ||
} | ||
|
||
/** | ||
* {@inheritdoc} | ||
*/ | ||
public function alterSchemaExtensionData(AlterSchemaExtensionDataEvent $event): void { | ||
$schemaData = $event->getSchemaExtensionData(); | ||
// I do not recommend direct replace, better user parsing or regex. | ||
// But this is an example of what you can do. | ||
$schemaData['graphql_alterable_schema_test'] = str_replace('position: Int', 'position: Int!', $schemaData['graphql_alterable_schema_test']); | ||
$event->setSchemaExtensionData($schemaData); | ||
} | ||
|
||
/** | ||
* {@inheritdoc} | ||
*/ | ||
public function alterSchemaData(AlterSchemaDataEvent $event): void { | ||
$schemaData = $event->getSchemaData(); | ||
// It is not recommended direct replacement, better user parsing or regex. | ||
// But this is an example of what you can do. | ||
$schemaData[0] = str_replace('alterableQuery(id: Int): Result', 'alterableQuery(id: Int!): Result', $schemaData[0]); | ||
$event->setSchemaData($schemaData); | ||
} | ||
|
||
} |
Oops, something went wrong.