View source
<?php
namespace Drupal\graphql\Plugin\GraphQL\Schemas;
use Drupal\Component\Plugin\PluginBase;
use Drupal\Core\Cache\CacheableDependencyInterface;
use Drupal\Core\Language\LanguageManagerInterface;
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
use Drupal\Core\Session\AccountProxyInterface;
use Drupal\graphql\GraphQL\Execution\ResolveContext;
use Drupal\graphql\GraphQL\QueryProvider\QueryProviderInterface;
use Drupal\graphql\Plugin\FieldPluginManager;
use Drupal\graphql\Plugin\MutationPluginManager;
use Drupal\graphql\Plugin\SubscriptionPluginManager;
use Drupal\graphql\Plugin\SchemaBuilderInterface;
use Drupal\graphql\Plugin\SchemaPluginInterface;
use Drupal\graphql\Plugin\TypePluginManagerAggregator;
use GraphQL\Language\AST\DocumentNode;
use GraphQL\Server\OperationParams;
use GraphQL\Server\ServerConfig;
use GraphQL\Type\Definition\ObjectType;
use GraphQL\Type\Definition\ResolveInfo;
use GraphQL\Type\Schema;
use GraphQL\Type\SchemaConfig;
use GraphQL\Validator\DocumentValidator;
use Psr\Log\LoggerInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
abstract class SchemaPluginBase extends PluginBase implements SchemaPluginInterface, SchemaBuilderInterface, ContainerFactoryPluginInterface, CacheableDependencyInterface {
protected $fieldManager;
protected $mutationManager;
protected $subscriptionManager;
protected $typeManagers;
protected $fields = [];
protected $mutations = [];
protected $subscriptions = [];
protected $types = [];
protected $parameters;
protected $queryProvider;
protected $currentUser;
protected $logger;
protected $languageManager;
public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
return new static($configuration, $plugin_id, $plugin_definition, $container
->get('plugin.manager.graphql.field'), $container
->get('plugin.manager.graphql.mutation'), $container
->get('plugin.manager.graphql.subscription'), $container
->get('graphql.type_manager_aggregator'), $container
->get('graphql.query_provider'), $container
->get('current_user'), $container
->get('logger.channel.graphql'), $container
->get('language_manager'), $container
->getParameter('graphql.config'));
}
public function __construct($configuration, $pluginId, $pluginDefinition, FieldPluginManager $fieldManager, MutationPluginManager $mutationManager, SubscriptionPluginManager $subscriptionManager, TypePluginManagerAggregator $typeManagers, QueryProviderInterface $queryProvider, AccountProxyInterface $currentUser, LoggerInterface $logger, LanguageManagerInterface $languageManager, array $parameters) {
parent::__construct($configuration, $pluginId, $pluginDefinition);
$this->fieldManager = $fieldManager;
$this->mutationManager = $mutationManager;
$this->subscriptionManager = $subscriptionManager;
$this->typeManagers = $typeManagers;
$this->queryProvider = $queryProvider;
$this->currentUser = $currentUser;
$this->parameters = $parameters;
$this->logger = $logger;
$this->languageManager = $languageManager;
}
public function getSchema() {
$config = new SchemaConfig();
if ($this
->hasMutations()) {
$config
->setMutation(new ObjectType([
'name' => 'Mutation',
'fields' => function () {
return $this
->getMutations();
},
]));
}
if ($this
->hasSubscriptions()) {
$config
->setSubscription(new ObjectType([
'name' => 'Subscription',
'fields' => function () {
return $this
->getSubscriptions();
},
]));
}
$config
->setQuery(new ObjectType([
'name' => 'Query',
'fields' => function () {
return $this
->getFields('Root');
},
]));
$config
->setTypes(function () {
return $this
->getTypes();
});
$config
->setTypeLoader(function ($name) {
return $this
->getType($name);
});
return new Schema($config);
}
public function validateSchema() {
return NULL;
}
public function getServer() {
$globals['bypass field security'] = $this->currentUser
->hasPermission('bypass graphql field security');
$config = ServerConfig::create();
$config
->setContext(function ($params, $document, $operation) use ($globals) {
$context = new ResolveContext($globals, [
'language' => $this->languageManager
->getCurrentLanguage()
->getId(),
]);
$context
->addCacheTags([
'graphql',
]);
$context
->addCacheContexts([
'languages:language_url',
'languages:language_interface',
'languages:language_content',
'user.permissions',
]);
return $context;
});
$config
->setValidationRules(function (OperationParams $params, DocumentNode $document, $operation) {
if (isset($params->queryId) && empty($params
->getOriginalInput('query'))) {
return [];
}
return array_values(DocumentValidator::defaultRules());
});
$config
->setPersistentQueryLoader([
$this->queryProvider,
'getQuery',
]);
$config
->setQueryBatching(TRUE);
$config
->setDebug(!!$this->parameters['development']);
$config
->setSchema($this
->getSchema());
$config
->setErrorsHandler(function (array $errors, callable $formatter) {
foreach ($errors as $error) {
$this->logger
->error($error
->getMessage());
}
return array_map($formatter, $errors);
});
return $config;
}
public function hasFields($type) {
return isset($this->pluginDefinition['field_association_map'][$type]);
}
public function hasMutations() {
return !empty($this->pluginDefinition['mutation_map']);
}
public function hasSubscriptions() {
return !empty($this->pluginDefinition['subscription_map']);
}
public function hasType($name) {
return isset($this->pluginDefinition['type_map'][$name]);
}
public function getFields($type) {
$association = $this->pluginDefinition['field_association_map'];
$fields = $this->pluginDefinition['field_map'];
if (isset($association[$type])) {
return $this
->processFields(array_map(function ($id) use ($fields) {
return $fields[$id];
}, $association[$type]));
}
return [];
}
public function getMutations() {
return $this
->processMutations($this->pluginDefinition['mutation_map']);
}
public function getSubscriptions() {
return $this
->processSubscriptions($this->pluginDefinition['subscription_map']);
}
public function getTypes() {
return array_map(function ($name) {
return $this
->getType($name);
}, array_keys($this->pluginDefinition['type_map']));
}
public function getSubTypes($name) {
$association = $this->pluginDefinition['type_association_map'];
return isset($association[$name]) ? $association[$name] : [];
}
public function resolveType($name, $value, ResolveContext $context, ResolveInfo $info) {
$association = $this->pluginDefinition['type_association_map'];
$types = $this->pluginDefinition['type_map'];
if (!isset($association[$name])) {
return NULL;
}
foreach ($association[$name] as $type) {
if (isset($types[$type]) && ($instance = $this
->buildType($types[$type]))) {
if ($instance
->isTypeOf($value, $context, $info)) {
return $instance;
}
}
}
return NULL;
}
public function getType($name) {
$types = $this->pluginDefinition['type_map'];
$references = $this->pluginDefinition['type_reference_map'];
if (isset($types[$name])) {
return $this
->buildType($this->pluginDefinition['type_map'][$name]);
}
do {
if (isset($references[$name])) {
return $this
->buildType($types[$references[$name]]);
}
} while (($pos = strpos($name, ':')) !== FALSE && ($name = substr($name, 0, $pos)));
throw new \LogicException(sprintf('Missing type %s.', $name));
}
public function processMutations(array $mutations) {
return array_map([
$this,
'buildMutation',
], $mutations);
}
public function processSubscriptions(array $subscriptions) {
return array_map([
$this,
'buildSubscription',
], $subscriptions);
}
public function processFields(array $fields) {
return array_map([
$this,
'buildField',
], $fields);
}
public function processArguments(array $args) {
return array_filter(array_map(function ($arg) {
try {
$type = $this
->processType($arg['type']);
} catch (\Exception $e) {
if (empty($arg['optional'])) {
throw $e;
}
return NULL;
}
return [
'type' => $type,
] + $arg;
}, $args));
}
public function processType(array $type) {
list($type, $decorators) = $type;
return array_reduce($decorators, function ($type, $decorator) {
return $decorator($type);
}, $this
->getType($type));
}
protected function buildType($type) {
if (!isset($this->types[$type['id']])) {
$creator = [
$type['class'],
'createInstance',
];
$manager = $this->typeManagers
->getTypeManager($type['type']);
$this->types[$type['id']] = $creator($this, $manager, $type['definition'], $type['id']);
}
return $this->types[$type['id']];
}
protected function buildField($field) {
if (!isset($this->fields[$field['id']])) {
$creator = [
$field['class'],
'createInstance',
];
$this->fields[$field['id']] = $creator($this, $this->fieldManager, $field['definition'], $field['id']);
}
return $this->fields[$field['id']];
}
protected function buildMutation($mutation) {
if (!isset($this->mutations[$mutation['id']])) {
$creator = [
$mutation['class'],
'createInstance',
];
$this->mutations[$mutation['id']] = $creator($this, $this->mutationManager, $mutation['definition'], $mutation['id']);
}
return $this->mutations[$mutation['id']];
}
protected function buildSubscription($subscription) {
if (!isset($this->subscriptions[$subscription['id']])) {
$creator = [
$subscription['class'],
'createInstance',
];
$this->subscriptions[$subscription['id']] = $creator($this, $this->subscriptionManager, $subscription['definition'], $subscription['id']);
}
return $this->subscriptions[$subscription['id']];
}
public function getCacheContexts() {
return [];
}
public function getCacheTags() {
return $this->pluginDefinition['schema_cache_tags'];
}
public function getCacheMaxAge() {
return $this->pluginDefinition['schema_cache_max_age'];
}
}