diff --git a/.php_cs b/.php_cs
index 19a02d5dc1..06d57b9a90 100644
--- a/.php_cs
+++ b/.php_cs
@@ -18,4 +18,5 @@ return PhpCsFixer\Config::create()
'no_unused_imports' => false,
'ordered_imports' => true,
'phpdoc_order' => true,
+ 'not_operator_with_successor_space' => true
])->setFinder($finder);
diff --git a/assets/node.graphql b/assets/node.graphql
deleted file mode 100644
index d36f0843e1..0000000000
--- a/assets/node.graphql
+++ /dev/null
@@ -1,5 +0,0 @@
-# Node global interface
-interface Node @interface(resolver: "Nuwave\\Lighthouse\\Support\\Http\\GraphQL\\Interfaces\\NodeInterface@resolve") {
- # Global identifier that can be used to resolve any Node implementation.
- _id: ID!
-}
diff --git a/composer.json b/composer.json
index 16840579ca..c55ba3a889 100644
--- a/composer.json
+++ b/composer.json
@@ -49,8 +49,11 @@
}
},
"scripts": {
- "test" : "vendor/bin/phpunit --colors=always",
- "test:ci": "composer test -- --verbose --coverage-text --coverage-clover=coverage.xml"
+ "test" : "phpunit --colors=always",
+ "test:unit" : "phpunit --colors=always --testsuite Unit",
+ "test:integration" : "phpunit --colors=always --testsuite Integration",
+ "test:ci": "phpunit --colors=always --verbose --coverage-text --coverage-clover=coverage.xml",
+ "style": "php-cs-fixer fix"
},
"extra": {
"laravel": {
diff --git a/config/config.php b/config/config.php
index 03822bc01d..8591e28320 100644
--- a/config/config.php
+++ b/config/config.php
@@ -22,15 +22,14 @@
// 'middleware' => ['web','api'], // [ 'loghttp']
],
-
/*
|--------------------------------------------------------------------------
| Directive registry
|--------------------------------------------------------------------------
|
- | This package allows you to create your own server-side directives. Change
- | these values to register the directory that will hold all of your
- | custom directives.
+ | This package allows you to create your own server-side directives.
+ | List directories that will be scanned for custom directives.
+ | Hint: Directives must implement \Nuwave\Lighthouse\Schema\Directives\Directive
|
*/
'directives' => [__DIR__.'/../app/Http/GraphQL/Directives'],
@@ -67,10 +66,14 @@
| Schema Cache
|--------------------------------------------------------------------------
|
- | Specify where the GraphQL schema should be cached.
+ | A large part of the Schema generation is parsing into an AST.
+ | This operation is cached by default when APP_ENV is set to 'production'
|
*/
- 'cache' => null,
+ 'cache' => [
+ 'enable' => true,
+ 'key' => 'lighthouse-schema',
+ ],
/*
|--------------------------------------------------------------------------
diff --git a/phpunit.xml.dist b/phpunit.xml.dist
index 36edc49fcb..3664e85dcd 100644
--- a/phpunit.xml.dist
+++ b/phpunit.xml.dist
@@ -8,12 +8,14 @@
convertWarningsToExceptions="true"
processIsolation="false"
stopOnFailure="false"
- syntaxCheck="false"
verbose="true"
>
-
- ./tests/
+
+ ./tests/Unit/
+
+
+ ./tests/Integration/
@@ -27,7 +29,7 @@
-
+
diff --git a/src/GraphQL.php b/src/GraphQL.php
index de5ccb9057..c3b13128ac 100644
--- a/src/GraphQL.php
+++ b/src/GraphQL.php
@@ -4,25 +4,20 @@
use GraphQL\GraphQL as GraphQLBase;
use GraphQL\Type\Schema;
-use Nuwave\Lighthouse\Schema\CacheManager;
-use Nuwave\Lighthouse\Schema\Factories\DirectiveFactory;
+use Nuwave\Lighthouse\Schema\AST\ASTBuilder;
+use Nuwave\Lighthouse\Schema\AST\DocumentAST;
+use Nuwave\Lighthouse\Schema\AST\SchemaStitcher;
+use Nuwave\Lighthouse\Schema\DirectiveRegistry;
use Nuwave\Lighthouse\Schema\MiddlewareManager;
use Nuwave\Lighthouse\Schema\NodeContainer;
use Nuwave\Lighthouse\Schema\SchemaBuilder;
-use Nuwave\Lighthouse\Schema\Utils\SchemaStitcher;
+use Nuwave\Lighthouse\Schema\TypeRegistry;
use Nuwave\Lighthouse\Support\Traits\CanFormatError;
class GraphQL
{
use CanFormatError;
- /**
- * Cache manager.
- *
- * @var CacheManager
- */
- protected $cache;
-
/**
* Schema builder.
*
@@ -31,12 +26,26 @@ class GraphQL
protected $schema;
/**
- * Directive container.
+ * Directive registry container.
*
- * @var DirectiveFactory
+ * @var DirectiveRegistry
*/
protected $directives;
+ /**
+ * Type registry container.
+ *
+ * @var TypeRegistry
+ */
+ protected $types;
+
+ /**
+ * Instance of node container.
+ *
+ * @var NodeContainer
+ */
+ protected $nodes;
+
/**
* Middleware manager.
*
@@ -58,12 +67,32 @@ class GraphQL
*/
protected $graphqlSchema;
+ /**
+ * Create instance of graphql container.
+ *
+ * @param DirectiveRegistry $directives
+ * @param TypeRegistry $types
+ * @param MiddlewareManager $middleware
+ * @param NodeContainer $nodes
+ */
+ public function __construct(
+ DirectiveRegistry $directives,
+ TypeRegistry $types,
+ MiddlewareManager $middleware,
+ NodeContainer $nodes
+ ) {
+ $this->directives = $directives;
+ $this->types = $types;
+ $this->middleware = $middleware;
+ $this->nodes = $nodes;
+ }
+
/**
* Prepare graphql schema.
*/
public function prepSchema()
{
- $this->graphqlSchema = $this->graphqlSchema ?: $this->buildSchema();
+ return $this->graphqlSchema = $this->graphqlSchema ?: $this->buildSchema();
}
/**
@@ -124,20 +153,43 @@ public function queryAndReturnResult($query, $context = null, $variables = [], $
}
/**
- * Build a new schema instance.
+ * Build a new executable schema.
*
* @return Schema
*/
public function buildSchema()
{
- $schema = $this->cache()->get(function () {
- return $this->stitcher()->stitch(
- config('lighthouse.global_id_field', '_id'),
- config('lighthouse.schema.register')
- );
- });
-
- return $this->schema()->build($schema);
+ $documentAST = $this->shouldCacheAST()
+ ? Cache::rememberForever(config('lighthouse.cache.key'), function () {
+ return $this->buildAST();
+ })
+ : $this->buildAST();
+
+ return (new SchemaBuilder())->build($documentAST);
+ }
+
+ /**
+ * Determine if the AST should be cached.
+ *
+ * @return bool
+ */
+ protected function shouldCacheAST()
+ {
+ return app()->environment('production') && config('cache.enable');
+ }
+
+ /**
+ * Get the stitched schema and build an AST out of it.
+ *
+ * @return DocumentAST
+ */
+ protected function buildAST()
+ {
+ $schemaString = SchemaStitcher::stitch(
+ config('lighthouse.schema.register')
+ );
+
+ return ASTBuilder::generate($schemaString);
}
/**
@@ -163,71 +215,41 @@ public function batch($abstract, $key, array $data = [], $name = null)
/**
* Get an instance of the schema builder.
*
- * @return SchemaBuilder
+ * @return TypeRegistry
*/
public function schema()
{
- if (! $this->schema) {
- $this->schema = app(SchemaBuilder::class);
- }
-
- return $this->schema;
+ return $this->types();
}
/**
* Get an instance of the directive container.
*
- * @return DirectiveFactory
+ * @return DirectiveRegistry
*/
public function directives()
{
- if (! $this->directives) {
- $this->directives = app(DirectiveFactory::class);
- }
-
return $this->directives;
}
/**
- * Get instance of middle manager.
+ * Get instance of type container.
*
- * @return MiddlewareManager
+ * @return TypeRegistry
*/
- public function middleware()
+ public function types()
{
- if (! $this->middleware) {
- $this->middleware = app(MiddlewareManager::class);
- }
-
- return $this->middleware;
+ return $this->types;
}
/**
- * Get instance of cache manager.
- *
- * @return CacheManager
- */
- public function cache()
- {
- if (! $this->cache) {
- $this->cache = app(CacheManager::class);
- }
-
- return $this->cache;
- }
-
- /**
- * Get instance of schema stitcher.
+ * Get instance of middle manager.
*
- * @return SchemaStitcher
+ * @return MiddlewareManager
*/
- public function stitcher()
+ public function middleware()
{
- if (! $this->stitcher) {
- $this->stitcher = app(SchemaStitcher::class);
- }
-
- return $this->stitcher;
+ return $this->middleware;
}
/**
@@ -237,13 +259,6 @@ public function stitcher()
*/
public function nodes()
{
- if (! app()->has(NodeContainer::class)) {
- return app()->instance(
- NodeContainer::class,
- resolve(NodeContainer::class)
- );
- }
-
- return resolve(NodeContainer::class);
+ return $this->nodes;
}
}
diff --git a/src/Providers/LighthouseServiceProvider.php b/src/Providers/LighthouseServiceProvider.php
index 1b2006c6c4..5bb02c9ed3 100644
--- a/src/Providers/LighthouseServiceProvider.php
+++ b/src/Providers/LighthouseServiceProvider.php
@@ -22,16 +22,22 @@ public function boot()
$this->loadRoutesFrom(__DIR__.'/../Support/Http/routes.php');
}
- $this->registerSchema();
$this->registerMacros();
}
+ /**
+ * Load routes from provided path.
+ *
+ * @param string $path
+ */
protected function loadRoutesFrom($path)
{
- if(Str::contains($this->app->version(), "Lumen")) {
- require realpath($path);
- return;
+ if (Str::contains($this->app->version(), 'Lumen')) {
+ require realpath($path);
+
+ return;
}
+
parent::loadRoutesFrom($path);
}
@@ -40,33 +46,14 @@ protected function loadRoutesFrom($path)
*/
public function register()
{
- $this->app->singleton('graphql', function () {
- return new GraphQL();
- });
-
- $this->app->alias('graphql', GraphQL::class);
+ $this->app->singleton(GraphQL::class);
+ $this->app->alias(GraphQL::class, 'graphql');
if ($this->app->runningInConsole()) {
- $this->commands([
- \Nuwave\Lighthouse\Support\Console\Commands\CacheCommand::class,
- ]);
+ $this->commands([]);
}
}
- /**
- * Register GraphQL schema.
- */
- public function registerSchema()
- {
- directives()->load(realpath(__DIR__.'/../Schema/Directives/'), 'Nuwave\\Lighthouse\\');
- directives()->load(config('lighthouse.directives', []));
-
- graphql()->stitcher()->stitch(
- config('lighthouse.global_id_field', '_id'),
- config('lighthouse.schema.register')
- );
- }
-
/**
* Register lighthouse macros.
*/
diff --git a/src/Schema/AST/ASTBuilder.php b/src/Schema/AST/ASTBuilder.php
new file mode 100644
index 0000000000..c6a4f5f3d9
--- /dev/null
+++ b/src/Schema/AST/ASTBuilder.php
@@ -0,0 +1,200 @@
+typeExtensions()
+ // Unwrap extended types so they can be treated same as other types
+ ->map(function (TypeExtensionDefinitionNode $typeExtension) {
+ return $typeExtension->definition;
+ })
+ // This is just temporarily merged together
+ ->concat($document->objectTypes())
+ ->reduce(function (DocumentAST $document, ObjectTypeDefinitionNode $objectType) use (
+ $originalDocument
+ ) {
+ $nodeManipulators = graphql()->directives()->nodeManipulators($objectType);
+
+ return $nodeManipulators->reduce(function (DocumentAST $document, NodeManipulator $nodeManipulator) use (
+ $originalDocument,
+ $objectType
+ ) {
+ return $nodeManipulator->manipulateSchema($objectType, $document, $originalDocument);
+ }, $document);
+ }, $document);
+ }
+
+ protected static function mergeTypeExtensions(DocumentAST $document)
+ {
+ $document->objectTypes()->each(function (ObjectTypeDefinitionNode $objectType) use ($document) {
+ $name = $objectType->name->value;
+
+ $document->typeExtensions($name)->reduce(function (
+ ObjectTypeDefinitionNode $relatedObjectType,
+ TypeExtensionDefinitionNode $typeExtension
+ ) {
+ /** @var NodeList $fields */
+ $fields = $relatedObjectType->fields;
+ $relatedObjectType->fields = $fields->merge($typeExtension->definition->fields);
+
+ return $relatedObjectType;
+ }, $objectType);
+
+ // Modify the original document by overwriting the definition with the merged one
+ $document->setDefinition($objectType);
+ });
+
+ return $document;
+ }
+
+ /**
+ * @param DocumentAST $document
+ *
+ * @return DocumentAST
+ */
+ protected static function applyFieldManipulators(DocumentAST $document)
+ {
+ $originalDocument = $document;
+
+ return $document->objectTypes()->reduce(function (
+ DocumentAST $document,
+ ObjectTypeDefinitionNode $objectType
+ ) use ($originalDocument) {
+ return collect($objectType->fields)->reduce(function (
+ DocumentAST $document,
+ FieldDefinitionNode $fieldDefinition
+ ) use ($objectType, $originalDocument) {
+ $fieldManipulators = graphql()->directives()->fieldManipulators($fieldDefinition);
+
+ return $fieldManipulators->reduce(function (
+ DocumentAST $document,
+ FieldManipulator $fieldManipulator
+ ) use ($fieldDefinition, $objectType, $originalDocument) {
+ return $fieldManipulator->manipulateSchema($fieldDefinition, $objectType, $document,
+ $originalDocument);
+ }, $document);
+ }, $document);
+ }, $document);
+ }
+
+ /**
+ * @param DocumentAST $document
+ *
+ * @return DocumentAST
+ */
+ protected static function applyArgManipulators(DocumentAST $document)
+ {
+ $originalDocument = $document;
+
+ return $document->objectTypes()->reduce(
+ function (DocumentAST $document, ObjectTypeDefinitionNode $parentType) use ($originalDocument) {
+ return collect($parentType->fields)->reduce(
+ function (DocumentAST $document, FieldDefinitionNode $parentField) use (
+ $parentType,
+ $originalDocument
+ ) {
+ return collect($parentField->arguments)->reduce(
+ function (DocumentAST $document, InputValueDefinitionNode $argDefinition) use (
+ $parentType,
+ $parentField,
+ $originalDocument
+ ) {
+ $argManipulators = graphql()->directives()->argManipulators($argDefinition);
+
+ return $argManipulators->reduce(
+ function (DocumentAST $document, ArgManipulator $argManipulator) use (
+ $argDefinition,
+ $parentField,
+ $parentType,
+ $originalDocument
+ ) {
+ return $argManipulator->manipulateSchema($argDefinition, $parentField,
+ $parentType, $document, $originalDocument);
+ }, $document);
+ }, $document);
+ }, $document);
+ }, $document);
+ }
+
+ /**
+ * Inject the node type and a node field into Query.
+ *
+ * @param DocumentAST $document
+ *
+ * @return DocumentAST
+ * @throws \Nuwave\Lighthouse\Support\Exceptions\ParseException
+ */
+ protected static function addNodeSupport(DocumentAST $document)
+ {
+ $hasTypeImplementingNode = $document->objectTypes()->contains(function (ObjectTypeDefinitionNode $objectType) {
+ return collect($objectType->interfaces)->contains(function (NamedTypeNode $interface) {
+ return 'Node' === $interface->name->value;
+ });
+ });
+
+ // Only add the node type and node field if a type actually implements them
+ // Otherwise, a validation error is thrown
+ if (!$hasTypeImplementingNode) {
+ return $document;
+ }
+
+ $globalId = config('lighthouse.global_id_field', '_id');
+
+ // Double slashes to escape the slashes in the namespace.
+ $interface = PartialParser::interfaceTypeDefinition("
+ # Node global interface
+ interface Node @interface(resolver: \"Nuwave\\\\Lighthouse\\\\Support\\\\Http\\\\GraphQL\\\\Interfaces\\\\NodeInterface@resolve\") {
+ # Global identifier that can be used to resolve any Node implementation.
+ $globalId: ID!
+ }
+ ");
+ $document->setDefinition($interface);
+
+ $nodeQuery = PartialParser::fieldDefinition('node(id: ID!): Node @field(resolver: "Nuwave\\\Lighthouse\\\Support\\\Http\\\GraphQL\\\Queries\\\NodeQuery@resolve")');
+ $document->addFieldToQueryType($nodeQuery);
+
+ return $document;
+ }
+}
diff --git a/src/Schema/AST/ASTHelper.php b/src/Schema/AST/ASTHelper.php
new file mode 100644
index 0000000000..74ef0a033b
--- /dev/null
+++ b/src/Schema/AST/ASTHelper.php
@@ -0,0 +1,32 @@
+merge($addition);
+ }
+}
diff --git a/src/Schema/AST/DocumentAST.php b/src/Schema/AST/DocumentAST.php
new file mode 100644
index 0000000000..efeb6f502a
--- /dev/null
+++ b/src/Schema/AST/DocumentAST.php
@@ -0,0 +1,269 @@
+documentNode = $documentNode;
+ }
+
+ /**
+ * Create a new instance from a schema.
+ *
+ * @param $schema
+ *
+ * @return DocumentAST
+ */
+ public static function fromSource($schema)
+ {
+ return new static(Parser::parse($schema));
+ }
+
+ /**
+ * Get a collection of the contained definitions.
+ *
+ * @return Collection
+ */
+ public function definitions()
+ {
+ return collect($this->documentNode->definitions);
+ }
+
+ /**
+ * Get all type definitions from the document.
+ *
+ * @return Collection
+ */
+ public function typeDefinitions()
+ {
+ return $this->definitions()->filter(function (DefinitionNode $node) {
+ return $node instanceof ScalarTypeDefinitionNode
+ || $node instanceof ObjectTypeDefinitionNode
+ || $node instanceof InterfaceTypeDefinitionNode
+ || $node instanceof UnionTypeDefinitionNode
+ || $node instanceof EnumTypeDefinitionNode
+ || $node instanceof InputObjectTypeDefinitionNode;
+ });
+ }
+
+ /**
+ * Get all definitions for directives.
+ *
+ * @return Collection
+ */
+ public function directives()
+ {
+ return $this->definitionsByType(DirectiveDefinitionNode::class);
+ }
+
+ /**
+ * Get all definitions for type extensions.
+ *
+ * Without a name, it simply return all TypeExtensions.
+ * If a name is given, it may return multiple type extensions
+ * that apply to a named type.
+ *
+ * @param string|null $extendedTypeName
+ *
+ * @return Collection
+ */
+ public function typeExtensions($extendedTypeName = null)
+ {
+ return $this->definitionsByType(TypeExtensionDefinitionNode::class)
+ ->filter(function (TypeExtensionDefinitionNode $typeExtension) use ($extendedTypeName) {
+ return is_null($extendedTypeName) || $extendedTypeName === $typeExtension->definition->name->value;
+ });
+ }
+
+ /**
+ * Get all definitions for operations.
+ *
+ * @return Collection
+ */
+ public function operations()
+ {
+ return $this->definitionsByType(OperationDefinitionNode::class);
+ }
+
+ /**
+ * Get all fragment definitions.
+ *
+ * @return Collection
+ */
+ public function fragments()
+ {
+ return $this->definitionsByType(FragmentDefinitionNode::class);
+ }
+
+ /**
+ * Get all definitions for object types.
+ *
+ * @return Collection
+ */
+ public function objectTypes()
+ {
+ return $this->definitionsByType(ObjectTypeDefinitionNode::class);
+ }
+
+ /**
+ * Get all interface definitions.
+ *
+ * @return Collection
+ */
+ public function interfaces()
+ {
+ return $this->definitionsByType(InterfaceTypeDefinitionNode::class);
+ }
+
+ /**
+ * Get the root query type definition.
+ *
+ * @return ObjectTypeDefinitionNode
+ */
+ public function queryType()
+ {
+ return $this->objectTypeOrDefault('Query');
+ }
+
+ /**
+ * Get the root mutation type definition.
+ *
+ * @return ObjectTypeDefinitionNode
+ */
+ public function mutationType()
+ {
+ return $this->objectTypeOrDefault('Mutation');
+ }
+
+ /**
+ * Get the root subscription type definition.
+ *
+ * @return ObjectTypeDefinitionNode
+ */
+ public function subscriptionType()
+ {
+ return $this->objectTypeOrDefault('Subscription');
+ }
+
+ /**
+ * Either get an existing definition or an empty type definition.
+ *
+ * @param string $name
+ *
+ * @return ObjectTypeDefinitionNode
+ */
+ protected function objectTypeOrDefault($name)
+ {
+ return $this->objectType($name)
+ ?: PartialParser::objectTypeDefinition('type '.$name.'{}');
+ }
+
+ /**
+ * @param string $name
+ *
+ * @return ObjectTypeDefinitionNode|null
+ */
+ public function objectType($name)
+ {
+ return $this->objectTypes()->first(function (ObjectTypeDefinitionNode $objectType) use ($name) {
+ return $objectType->name->value === $name;
+ });
+ }
+
+ /**
+ * @param string $type
+ *
+ * @return Collection
+ */
+ protected function definitionsByType($type)
+ {
+ return $this->definitions()->filter(function ($node) use ($type) {
+ return $node instanceof $type;
+ });
+ }
+
+ /**
+ * Add a single field to the query type.
+ *
+ * @param FieldDefinitionNode $field
+ *
+ * @return $this
+ */
+ public function addFieldToQueryType(FieldDefinitionNode $field)
+ {
+ $query = $this->queryType();
+ $query->fields = $query->fields->merge([$field]);
+ $this->setDefinition($query);
+
+ return $this;
+ }
+
+ /**
+ * @param DefinitionNode $definition
+ *
+ * @return DocumentAST
+ */
+ public function setDefinition(DefinitionNode $definition)
+ {
+ $newName = $definition->name->value;
+ $newDefinitions = $this->definitions()
+ ->reject(function (DefinitionNode $node) use ($newName) {
+ $nodeName = data_get($node, 'name.value');
+ // We only consider replacing nodes that have a name
+ // We can safely kick this by name because names must be unique
+ return $nodeName && $nodeName === $newName;
+ })->push($definition)
+ // Reindex, otherwise offset errors might happen in subsequent runs
+ ->values()
+ ->all();
+
+ // This was a NodeList before, so put it back as it was
+ $this->documentNode->definitions = new NodeList($newDefinitions);
+
+ return $this;
+ }
+
+ /**
+ * @param string $definition
+ *
+ * @throws \Exception
+ *
+ * @return static
+ */
+ public function setObjectTypeFromString($definition)
+ {
+ $objectType = self::parseObjectType($definition);
+ $this->setDefinition($objectType);
+
+ return $this;
+ }
+}
diff --git a/src/Schema/AST/PartialParser.php b/src/Schema/AST/PartialParser.php
new file mode 100644
index 0000000000..ebc73072c9
--- /dev/null
+++ b/src/Schema/AST/PartialParser.php
@@ -0,0 +1,287 @@
+definitions,
+ ObjectTypeDefinitionNode::class
+ );
+ }
+
+ /**
+ * @param string $inputValueDefinition
+ *
+ * @throws ParseException
+ *
+ * @return InputValueDefinitionNode
+ */
+ public static function inputValueDefinition($inputValueDefinition)
+ {
+ return self::getFirstAndValidateType(
+ self::fieldDefinition("field($inputValueDefinition): String")->arguments,
+ InputValueDefinitionNode::class
+ );
+ }
+
+ /**
+ * @param string[] $inputValueDefinitions
+ *
+ * @return InputValueDefinitionNode[]
+ */
+ public static function inputValueDefinitions($inputValueDefinitions)
+ {
+ return array_map(function ($inputValueDefinition) {
+ return self::inputValueDefinition($inputValueDefinition);
+ }, $inputValueDefinitions);
+ }
+
+ /**
+ * @param string $argumentDefinition
+ *
+ * @throws ParseException
+ *
+ * @return NodeList
+ */
+ public static function argument($argumentDefinition)
+ {
+ return self::getFirstAndValidateType(
+ self::field("field($argumentDefinition): String")->arguments,
+ ArgumentNode::class
+ );
+ }
+
+ /**
+ * @param string[] $argumentDefinitions
+ *
+ * @return InputValueDefinitionNode[]
+ */
+ public static function arguments($argumentDefinitions)
+ {
+ return array_map(function ($argumentDefinition) {
+ return self::argument($argumentDefinition);
+ }, $argumentDefinitions);
+ }
+
+ /**
+ * @param string $field
+ *
+ * @throws ParseException
+ *
+ * @return FieldNode
+ */
+ public static function field($field)
+ {
+ return self::getFirstAndValidateType(
+ self::operationDefinition("{ $field }")->selectionSet->selections,
+ FieldNode::class
+ );
+ }
+
+ /**
+ * @param string $operation
+ *
+ * @throws ParseException
+ *
+ * @return OperationDefinitionNode
+ */
+ public static function operationDefinition($operation)
+ {
+ return self::getFirstAndValidateType(
+ Parser::parse($operation)->definitions,
+ OperationDefinitionNode::class
+ );
+ }
+
+ /**
+ * @param string $fieldDefinition
+ *
+ * @throws ParseException
+ *
+ * @return FieldDefinitionNode
+ */
+ public static function fieldDefinition($fieldDefinition)
+ {
+ return self::getFirstAndValidateType(
+ self::objectTypeDefinition("type Dummy { $fieldDefinition }")->fields,
+ FieldDefinitionNode::class
+ );
+ }
+
+ /**
+ * @param string $directive
+ *
+ * @throws ParseException
+ *
+ * @return DirectiveNode
+ */
+ public static function directive($directive)
+ {
+ return self::getFirstAndValidateType(
+ self::objectTypeDefinition("type Dummy $directive {}")->directives,
+ DirectiveNode::class
+ );
+ }
+
+ /**
+ * @param string[] $directives
+ *
+ * @return DirectiveNode[]
+ */
+ public static function directives($directives)
+ {
+ return array_map(function ($directive) {
+ return self::inputValueDefinition($directive);
+ }, $directives);
+ }
+
+ /**
+ * @param string $directiveDefinition
+ *
+ * @throws ParseException
+ *
+ * @return DirectiveDefinitionNode
+ */
+ public static function directiveDefinition($directiveDefinition)
+ {
+ return self::getFirstAndValidateType(
+ Parser::parse($directiveDefinition)->definitions,
+ DirectiveDefinitionNode::class
+ );
+ }
+
+ /**
+ * @param string[] $directiveDefinitions
+ *
+ * @return DirectiveDefinitionNode[]
+ */
+ public static function directiveDefinitions($directiveDefinitions)
+ {
+ return array_map(function ($directiveDefinition) {
+ return self::inputValueDefinition($directiveDefinition);
+ }, $directiveDefinitions);
+ }
+
+ /**
+ * @param $interfaceDefinition
+ *
+ * @throws ParseException
+ *
+ * @return InterfaceTypeDefinitionNode
+ */
+ public static function interfaceTypeDefinition($interfaceDefinition)
+ {
+ return self::getFirstAndValidateType(
+ Parser::parse($interfaceDefinition)->definitions,
+ InterfaceTypeDefinitionNode::class
+ );
+ }
+
+ /**
+ * @param string $inputTypeDefinition
+ *
+ * @throws ParseException
+ *
+ * @return InputObjectTypeDefinitionNode
+ */
+ public static function inputObjectTypeDefinition($inputTypeDefinition)
+ {
+ return self::getFirstAndValidateType(
+ Parser::parse($inputTypeDefinition)->definitions,
+ InputObjectTypeDefinitionNode::class
+ );
+ }
+
+ /**
+ * @param string $scalarDefinition
+ *
+ * @throws ParseException
+ *
+ * @return ScalarTypeDefinitionNode
+ */
+ public static function scalarTypeDefinition($scalarDefinition)
+ {
+ return self::getFirstAndValidateType(
+ Parser::parse($scalarDefinition)->definitions,
+ ScalarTypeDefinitionNode::class
+ );
+ }
+
+ /**
+ * @param $enumDefinition
+ *
+ * @throws ParseException
+ *
+ * @return mixed
+ */
+ public static function enumTypeDefinition($enumDefinition)
+ {
+ return self::getFirstAndValidateType(
+ Parser::parse($enumDefinition)->definitions,
+ EnumTypeDefinitionNode::class
+ );
+ }
+
+ /**
+ * Get the first Node from a given NodeList and validate it.
+ *
+ * @param NodeList $list
+ * @param string $expectedType
+ *
+ * @throws ParseException
+ *
+ * @return mixed
+ */
+ protected static function getFirstAndValidateType(NodeList $list, $expectedType)
+ {
+ if (1 !== $list->count()) {
+ throw new ParseException(' More than one definition was found in the passed in schema.');
+ }
+
+ $node = $list[0];
+
+ if (! $node instanceof $expectedType) {
+ throw new ParseException("The given definition was not of type: $expectedType");
+ }
+
+ return $node;
+ }
+}
diff --git a/src/Schema/Utils/SchemaStitcher.php b/src/Schema/AST/SchemaStitcher.php
similarity index 50%
rename from src/Schema/Utils/SchemaStitcher.php
rename to src/Schema/AST/SchemaStitcher.php
index 3e5754a90a..a930037888 100644
--- a/src/Schema/Utils/SchemaStitcher.php
+++ b/src/Schema/AST/SchemaStitcher.php
@@ -1,43 +1,23 @@
lighthouseSchema($globalId);
- $app = $path ? $this->appSchema($path) : '';
+ $lighthouse = file_get_contents(realpath(__DIR__.'/../../../assets/schema.graphql'));
- return $lighthouse.$app;
- }
+ $app = $path ? self::appSchema($path) : '';
- /**
- * Get Lighthouse schema.
- *
- * @param string $globalId
- *
- * @return string
- */
- public function lighthouseSchema($globalId = '_id')
- {
- $schema = file_get_contents(realpath(__DIR__.'/../../../assets/schema.graphql'));
-
- if ($globalId) {
- $node = file_get_contents(realpath(__DIR__.'/../../../assets/node.graphql'));
-
- return str_replace('_id', $globalId, $node).$schema;
- }
-
- return $schema;
+ return $lighthouse.$app;
}
/**
@@ -47,7 +27,7 @@ public function lighthouseSchema($globalId = '_id')
*
* @return string
*/
- protected function appSchema($path)
+ protected static function appSchema($path)
{
try {
$schema = file_get_contents($path);
@@ -61,7 +41,7 @@ protected function appSchema($path)
})->map(function ($import) {
return trim(str_replace('#import', '', $import));
})->map(function ($file) use ($path) {
- return $this->appSchema(realpath(dirname($path).'/'.$file));
+ return self::appSchema(realpath(dirname($path).'/'.$file));
})->implode("\n");
return $imports."\n".$schema;
diff --git a/src/Schema/CacheManager.php b/src/Schema/CacheManager.php
deleted file mode 100644
index b97b806c37..0000000000
--- a/src/Schema/CacheManager.php
+++ /dev/null
@@ -1,52 +0,0 @@
-parseSchema($schema);
-
- file_put_contents(
- config('lighthouse.cache'),
- "set($schema());
- }
-}
diff --git a/src/Schema/DirectiveRegistry.php b/src/Schema/DirectiveRegistry.php
new file mode 100644
index 0000000000..fa0c04d07f
--- /dev/null
+++ b/src/Schema/DirectiveRegistry.php
@@ -0,0 +1,370 @@
+directives = collect();
+
+ // Load built-in directives from the default directory
+ $this->load(realpath(__DIR__ . '/Directives/'), 'Nuwave\\Lighthouse\\');
+
+ // Load custom directives
+ $this->load(config('lighthouse.directives', []));
+ }
+
+ /**
+ * Gather all directives from a given directory and register them.
+ *
+ * Works similar to https://github.com/laravel/framework/blob/5.6/src/Illuminate/Foundation/Console/Kernel.php#L191-L225
+ *
+ * @param array|string $paths
+ * @param null $namespace
+ */
+ protected function load($paths, $namespace = null)
+ {
+ $paths = collect($paths)
+ ->unique()
+ ->filter(function ($path) {
+ return is_dir($path);
+ })->map(function ($path) {
+ return realpath($path);
+ })->all();
+
+ if (empty($paths)) {
+ return;
+ }
+
+ $namespace = $namespace ?: app()->getNamespace();
+ $path = starts_with($namespace, 'Nuwave\\Lighthouse')
+ ? realpath(__DIR__ . '/../../src/')
+ : app_path();
+
+ /** @var SplFileInfo $file */
+ foreach ((new Finder())->in($paths)->files() as $file) {
+ $className = $namespace . str_replace(
+ ['/', '.php'],
+ ['\\', ''],
+ str_after($file->getPathname(), $path . DIRECTORY_SEPARATOR)
+ );
+
+ $this->tryRegisterClassName($className);
+ }
+ }
+
+ /**
+ * Register a directive class.
+ *
+ * @param string $className
+ *
+ * @throws \ReflectionException
+ */
+ protected function tryRegisterClassName($className)
+ {
+ $reflection = new \ReflectionClass($className);
+
+ if ($reflection->isInstantiable() && $reflection->isSubclassOf(Directive::class)) {
+ $directive = $reflection->newInstance();
+ $this->register($directive);
+ }
+ }
+
+ /**
+ * Register a directive.
+ *
+ * @param Directive $directive
+ */
+ public function register(Directive $directive)
+ {
+ $this->directives->put($directive->name(), $directive);
+ }
+
+ /**
+ * Get directive instance by name.
+ *
+ * @param string $name
+ *
+ * @throws DirectiveException
+ *
+ * @return Directive
+ */
+ public function get($name)
+ {
+ $handler = $this->directives->get($name);
+
+ if (!$handler) {
+ throw new DirectiveException("No directive has been registered for [{$name}]");
+ }
+
+ return $handler;
+ }
+
+ /**
+ * Get directive instance by name.
+ *
+ * @param string $name
+ *
+ * @throws DirectiveException
+ *
+ * @return Directive
+ *
+ * @deprecated Will be removed in next major release
+ */
+ public function handler($name)
+ {
+ return $this->get($name);
+ }
+
+ /**
+ * Get all directives associated with a node.
+ *
+ * @param Node $node
+ *
+ * @return \Illuminate\Support\Collection
+ */
+ protected function directives(Node $node)
+ {
+ return collect(data_get($node, 'directives', []))->map(function (DirectiveNode $directive) {
+ return $this->get($directive->name->value);
+ });
+ }
+
+ /**
+ * @param Node $node
+ *
+ * @return \Illuminate\Support\Collection
+ */
+ public function nodeManipulators(Node $node)
+ {
+ return $this->directives($node)->filter(function (Directive $directive) {
+ return $directive instanceof NodeManipulator;
+ });
+ }
+
+ /**
+ * @param FieldDefinitionNode $fieldDefinition
+ *
+ * @return \Illuminate\Support\Collection
+ */
+ public function fieldManipulators(FieldDefinitionNode $fieldDefinition)
+ {
+ return $this->directives($fieldDefinition)->filter(function (Directive $directive) {
+ return $directive instanceof FieldManipulator;
+ })->map(function (FieldManipulator $directive) use ($fieldDefinition) {
+ return $this->hydrate($directive, $fieldDefinition);
+ });
+ }
+
+ /**
+ * @param $inputValueDefinition
+ *
+ * @return \Illuminate\Support\Collection
+ */
+ public function argManipulators(InputValueDefinitionNode $inputValueDefinition)
+ {
+ return $this->directives($inputValueDefinition)->filter(function (Directive $directive) {
+ return $directive instanceof ArgManipulator;
+ });
+ }
+
+ /**
+ * Get the node resolver directive for the given type definition.
+ *
+ * @param Node $node
+ *
+ * @return NodeResolver
+ */
+ public function forNode(Node $node)
+ {
+ return $this->nodeResolver($node);
+ }
+
+ /**
+ * Get the node resolver directive for the given type definition.
+ *
+ * @param Node $node
+ *
+ * @return NodeResolver
+ */
+ public function nodeResolver(Node $node)
+ {
+ $resolvers = $this->directives($node)->filter(function (Directive $directive) {
+ return $directive instanceof NodeResolver;
+ });
+
+ if ($resolvers->count() > 1) {
+ $nodeName = data_get($node, 'name.value');
+ throw new DirectiveException("Node $nodeName can only have one NodeResolver directive. Check your schema definition");
+ }
+
+ return $resolvers->first();
+ }
+
+ /**
+ * Check if the given node has a type resolver directive handler assigned to it.
+ *
+ * @param Node $typeDefinition
+ *
+ * @return bool
+ */
+ public function hasNodeResolver(Node $typeDefinition)
+ {
+ return $this->nodeResolver($typeDefinition) instanceof NodeResolver;
+ }
+
+ /**
+ * @param FieldDefinitionNode $fieldDefinition
+ *
+ * @return bool
+ */
+ public function hasResolver($fieldDefinition)
+ {
+ return $this->hasFieldResolver($fieldDefinition);
+ }
+
+ /**
+ * Check if the given field has a field resolver directive handler assigned to it.
+ *
+ * @param FieldDefinitionNode $fieldDefinition
+ *
+ * @return bool
+ */
+ public function hasFieldResolver($fieldDefinition)
+ {
+ return $this->fieldResolver($fieldDefinition) instanceof FieldResolver;
+ }
+
+ /**
+ * Check if field has a resolver directive.
+ *
+ * @param FieldDefinitionNode $field
+ *
+ * @return bool
+ */
+ public function hasFieldMiddleware($field)
+ {
+ return collect($field->directives)->map(function (DirectiveNode $directive) {
+ return $this->handler($directive->name->value);
+ })->reduce(function ($has, $handler) {
+ return $handler instanceof FieldMiddleware ? true : $has;
+ }, false);
+ }
+
+ /**
+ * Get handler for field.
+ *
+ * @param FieldDefinitionNode $field
+ *
+ * @throws DirectiveException
+ *
+ * @return FieldResolver|null
+ */
+ public function fieldResolver($field)
+ {
+ $resolvers = $this->directives($field)->filter(function ($directive) {
+ return $directive instanceof FieldResolver;
+ });
+
+ if ($resolvers->count() > 1) {
+ throw new DirectiveException(sprintf(
+ 'Fields can only have 1 assigned resolver directive. %s has %s resolver directives [%s]',
+ data_get($field, 'name.value'),
+ $resolvers->count(),
+ collect($field->directives)->map(function (DirectiveNode $directive) {
+ return $directive->name->value;
+ })->implode(', ')
+ ));
+ }
+
+ $resolver = $resolvers->first();
+
+ return $resolver ? $this->hydrate($resolver, $field) : null;
+ }
+
+ /**
+ * Get all middleware directive for a type definitions.
+ *
+ * @param Node $typeDefinition
+ *
+ * @return \Illuminate\Support\Collection
+ */
+ public function nodeMiddleware(Node $typeDefinition)
+ {
+ return $this->directives($typeDefinition)->filter(function (Directive $directive) {
+ return $directive instanceof NodeMiddleware;
+ });
+ }
+
+ /**
+ * Get middleware for field.
+ *
+ * @param FieldDefinitionNode $fieldDefinition
+ *
+ * @return \Illuminate\Support\Collection
+ */
+ public function fieldMiddleware($fieldDefinition)
+ {
+ return $this->directives($fieldDefinition)->filter(function ($handler) {
+ return $handler instanceof FieldMiddleware;
+ })->map(function (FieldMiddleware $fieldDirective) use ($fieldDefinition) {
+ return $this->hydrate($fieldDirective, $fieldDefinition);
+ });
+ }
+
+ /**
+ * Get middleware for field arguments.
+ *
+ * @param InputValueDefinitionNode $arg
+ *
+ * @return \Illuminate\Support\Collection
+ */
+ public function argMiddleware(InputValueDefinitionNode $arg)
+ {
+ return $this->directives($arg)->filter(function (Directive $directive) {
+ return $directive instanceof ArgMiddleware;
+ });
+ }
+
+ /**
+ * @param Directive $directive
+ * @param FieldDefinitionNode $fieldDefinition
+ *
+ * @return Directive
+ */
+ protected function hydrate(Directive $directive, FieldDefinitionNode $fieldDefinition)
+ {
+ return $directive instanceof BaseFieldDirective
+ ? $directive->hydrate($fieldDefinition)
+ : $directive;
+ }
+}
diff --git a/src/Schema/Directives/Args/InFilterDirective.php b/src/Schema/Directives/Args/InFilterDirective.php
index 2e6fd1268a..be9f9a7efe 100644
--- a/src/Schema/Directives/Args/InFilterDirective.php
+++ b/src/Schema/Directives/Args/InFilterDirective.php
@@ -26,7 +26,7 @@ public function name()
*
* @param ArgumentValue $argument
*
- * @return array
+ * @return ArgumentValue
*/
public function handleArgument(ArgumentValue $argument)
{
diff --git a/src/Schema/Directives/Args/NotEqualsFilterDirective.php b/src/Schema/Directives/Args/NotEqualsFilterDirective.php
index e8730e9552..5e071bd8ba 100644
--- a/src/Schema/Directives/Args/NotEqualsFilterDirective.php
+++ b/src/Schema/Directives/Args/NotEqualsFilterDirective.php
@@ -26,7 +26,7 @@ public function name()
*
* @param ArgumentValue $argument
*
- * @return array
+ * @return ArgumentValue
*/
public function handleArgument(ArgumentValue $argument)
{
diff --git a/src/Schema/Directives/Args/NotInFilterDirective.php b/src/Schema/Directives/Args/NotInFilterDirective.php
index 73a85be49f..c69b5f6a3d 100644
--- a/src/Schema/Directives/Args/NotInFilterDirective.php
+++ b/src/Schema/Directives/Args/NotInFilterDirective.php
@@ -26,7 +26,7 @@ public function name()
*
* @param ArgumentValue $argument
*
- * @return array
+ * @return ArgumentValue
*/
public function handleArgument(ArgumentValue $argument)
{
diff --git a/src/Schema/Directives/Args/ScoutDirective.php b/src/Schema/Directives/Args/ScoutDirective.php
index 69e9a4fb7a..c91790ce46 100644
--- a/src/Schema/Directives/Args/ScoutDirective.php
+++ b/src/Schema/Directives/Args/ScoutDirective.php
@@ -1,6 +1,5 @@
within($within);
}
@@ -57,4 +56,4 @@ public function handleArgument(ArgumentValue $argument)
]
);
}
-}
\ No newline at end of file
+}
diff --git a/src/Schema/Directives/Args/ValidateDirective.php b/src/Schema/Directives/Args/ValidateDirective.php
index d1a63923f3..79f64e15b1 100644
--- a/src/Schema/Directives/Args/ValidateDirective.php
+++ b/src/Schema/Directives/Args/ValidateDirective.php
@@ -40,7 +40,8 @@ public function handleField(FieldValue $value)
);
if (! $validator) {
- $message = 'A `validator` argument must be supplied on the @validate field directive';
+ $fieldName = $value->getFieldName();
+ $message = "A `validator` argument must be supplied on the @validate directive on field {$fieldName}";
throw new DirectiveException($message);
}
@@ -65,7 +66,7 @@ public function handleField(FieldValue $value)
*
* @param ArgumentValue $value
*
- * @return array
+ * @return ArgumentValue
*/
public function handleArgument(ArgumentValue $value)
{
diff --git a/src/Schema/Directives/Args/WhereBetweenFilterDirective.php b/src/Schema/Directives/Args/WhereBetweenFilterDirective.php
index 2a03dbd3fa..74734d5d61 100644
--- a/src/Schema/Directives/Args/WhereBetweenFilterDirective.php
+++ b/src/Schema/Directives/Args/WhereBetweenFilterDirective.php
@@ -27,7 +27,7 @@ public function name()
*
* @param ArgumentValue $argument
*
- * @return array
+ * @return ArgumentValue
*/
public function handleArgument(ArgumentValue $argument)
{
diff --git a/src/Schema/Directives/Args/WhereFilterDirective.php b/src/Schema/Directives/Args/WhereFilterDirective.php
index b05607d712..cd9fed13ee 100644
--- a/src/Schema/Directives/Args/WhereFilterDirective.php
+++ b/src/Schema/Directives/Args/WhereFilterDirective.php
@@ -26,7 +26,7 @@ public function name()
*
* @param ArgumentValue $argument
*
- * @return array
+ * @return ArgumentValue
*/
public function handleArgument(ArgumentValue $argument)
{
diff --git a/src/Schema/Directives/Args/WhereNotBetweenFilterDirective.php b/src/Schema/Directives/Args/WhereNotBetweenFilterDirective.php
index 7a39acb38e..04595297f3 100644
--- a/src/Schema/Directives/Args/WhereNotBetweenFilterDirective.php
+++ b/src/Schema/Directives/Args/WhereNotBetweenFilterDirective.php
@@ -27,7 +27,7 @@ public function name()
*
* @param ArgumentValue $argument
*
- * @return array
+ * @return ArgumentValue
*/
public function handleArgument(ArgumentValue $argument)
{
diff --git a/src/Schema/Directives/Fields/AuthDirective.php b/src/Schema/Directives/Fields/AuthDirective.php
index 0754401581..97f59c2286 100644
--- a/src/Schema/Directives/Fields/AuthDirective.php
+++ b/src/Schema/Directives/Fields/AuthDirective.php
@@ -4,12 +4,9 @@
use Nuwave\Lighthouse\Schema\Values\FieldValue;
use Nuwave\Lighthouse\Support\Contracts\FieldResolver;
-use Nuwave\Lighthouse\Support\Traits\HandlesDirectives;
-class AuthDirective implements FieldResolver
+class AuthDirective extends BaseFieldDirective implements FieldResolver
{
- use HandlesDirectives;
-
/**
* Name of the directive.
*
@@ -29,10 +26,7 @@ public function name()
*/
public function resolveField(FieldValue $value)
{
- $guard = $this->directiveArgValue(
- $this->fieldDirective($value->getField(), $this->name()),
- 'guard'
- );
+ $guard = $this->associatedArgValue('name');
return $value->setResolver(function () use ($guard) {
return auth($guard)->user();
diff --git a/src/Schema/Directives/Fields/BaseFieldDirective.php b/src/Schema/Directives/Fields/BaseFieldDirective.php
new file mode 100644
index 0000000000..725aa6d7c5
--- /dev/null
+++ b/src/Schema/Directives/Fields/BaseFieldDirective.php
@@ -0,0 +1,100 @@
+fieldDefinition = $fieldDefinition;
+
+ return $this;
+ }
+
+ /**
+ * Get the directive definition that belongs to the current directive.
+ *
+ * @return DirectiveNode
+ */
+ protected function associatedDirective()
+ {
+ return $this->fieldDirective($this->fieldDefinition, $this->name());
+ }
+
+ /**
+ * Get an argument value from the directive definition that belongs to the current directive.
+ *
+ * @param string $argName
+ * @param mixed|null $default
+ *
+ * @return mixed
+ */
+ protected function associatedArgValue($argName, $default = null)
+ {
+ $directive = $this->associatedDirective($this->fieldDefinition);
+
+ return $this->directiveArgValue($directive, $argName, $default);
+ }
+
+ /**
+ * Add the namespace to a classname and check if it exists.
+ *
+ * @param string $baseClassName
+ *
+ * @throws DirectiveException
+ *
+ * @return string
+ */
+ protected function namespaceClassName($baseClassName)
+ {
+ $className = $this->associatedNamespace() . '\\' . $baseClassName;
+
+ if (!class_exists($className)) {
+ $directiveName = $this->name();
+ throw new DirectiveException("No class '$className' was found for directive '$directiveName'");
+ }
+
+ return $className;
+ }
+
+ /**
+ * Get the namespace for this field, returns an empty string if its not set.
+ *
+ * @return string
+ */
+ protected function associatedNamespace()
+ {
+ $namespaceDirective = $this->fieldDirective(
+ $this->fieldDefinition,
+ (new NamespaceDirective)->name()
+ );
+
+ return $namespaceDirective
+ // Look if a namespace for the current field is set, if not default to an empty string
+ ? $this->directiveArgValue($namespaceDirective, $this->name(), '')
+ // Default to an empty namespace if the namespace directive does not exist
+ : '';
+ }
+}
diff --git a/src/Schema/Directives/Fields/BelongsToDirective.php b/src/Schema/Directives/Fields/BelongsToDirective.php
index 5f10d053e1..0dd7e9f27f 100644
--- a/src/Schema/Directives/Fields/BelongsToDirective.php
+++ b/src/Schema/Directives/Fields/BelongsToDirective.php
@@ -5,12 +5,9 @@
use Nuwave\Lighthouse\Schema\Values\FieldValue;
use Nuwave\Lighthouse\Support\Contracts\FieldResolver;
use Nuwave\Lighthouse\Support\DataLoader\Loaders\BelongsToLoader;
-use Nuwave\Lighthouse\Support\Traits\HandlesDirectives;
-class BelongsToDirective implements FieldResolver
+class BelongsToDirective extends BaseFieldDirective implements FieldResolver
{
- use HandlesDirectives;
-
/**
* Name of the directive.
*
@@ -30,11 +27,7 @@ public function name()
*/
public function resolveField(FieldValue $value)
{
- $relation = $this->directiveArgValue(
- $this->fieldDirective($value->getField(), 'belongsTo'),
- 'relation',
- $value->getField()->name->value
- );
+ $relation = $this->associatedArgValue('relation', $value->getFieldName());
return $value->setResolver(function ($root, array $args, $context = null, $info = null) use ($relation) {
return graphql()->batch(BelongsToLoader::class, $root->getKey(), [
diff --git a/src/Schema/Directives/Fields/CanDirective.php b/src/Schema/Directives/Fields/CanDirective.php
index 40228e6de6..16a660f100 100644
--- a/src/Schema/Directives/Fields/CanDirective.php
+++ b/src/Schema/Directives/Fields/CanDirective.php
@@ -5,12 +5,9 @@
use GraphQL\Error\Error;
use Nuwave\Lighthouse\Schema\Values\FieldValue;
use Nuwave\Lighthouse\Support\Contracts\FieldMiddleware;
-use Nuwave\Lighthouse\Support\Traits\HandlesDirectives;
-class CanDirective implements FieldMiddleware
+class CanDirective extends BaseFieldDirective implements FieldMiddleware
{
- use HandlesDirectives;
-
/**
* Name of the directive.
*
@@ -30,16 +27,8 @@ public function name()
*/
public function handleField(FieldValue $value)
{
- $policies = $this->directiveArgValue(
- $this->fieldDirective($value->getField(), 'can'),
- 'if'
- );
-
- $model = $this->directiveArgValue(
- $this->fieldDirective($value->getField(), 'can'),
- 'model'
- );
-
+ $policies = $this->associatedArgValue('if');
+ $model = $this->associatedArgValue('model');
$resolver = $value->getResolver();
return $value->setResolver(
@@ -48,14 +37,14 @@ function () use ($policies, $resolver, $model) {
$model = $model ?: get_class($args[0]);
$can = collect($policies)->reduce(function ($allowed, $policy) use ($model) {
- if (! app('auth')->user()->can($policy, $model)) {
+ if (!app('auth')->user()->can($policy, $model)) {
return false;
}
return $allowed;
}, true);
- if (! $can) {
+ if (!$can) {
throw new Error('Not authorized to access resource');
}
diff --git a/src/Schema/Directives/Fields/ComplexityDirective.php b/src/Schema/Directives/Fields/ComplexityDirective.php
index 4a31855126..8f598cc855 100644
--- a/src/Schema/Directives/Fields/ComplexityDirective.php
+++ b/src/Schema/Directives/Fields/ComplexityDirective.php
@@ -4,12 +4,10 @@
use Nuwave\Lighthouse\Schema\Values\FieldValue;
use Nuwave\Lighthouse\Support\Contracts\FieldMiddleware;
-use Nuwave\Lighthouse\Support\Traits\CanParseResolvers;
+use Nuwave\Lighthouse\Support\Exceptions\DirectiveException;
-class ComplexityDirective implements FieldMiddleware
+class ComplexityDirective extends BaseFieldDirective implements FieldMiddleware
{
- use CanParseResolvers;
-
/**
* Name of the directive.
*
@@ -29,20 +27,25 @@ public function name()
*/
public function handleField(FieldValue $value)
{
- $directive = $this->fieldDirective($value->getField(), $this->name());
+ $baseClassName = $this->associatedArgValue('class') ?? str_before($this->associatedArgValue('resolver'), '@');
- if ($resolver = $this->getResolver($value, $directive, false)) {
- $method = $this->getResolverMethod($directive);
+ if (empty($baseClassName)) {
+ return $value->setComplexity(function ($childrenComplexity, $args) {
+ $complexity = array_get($args, 'first', array_get($args, 'count', 1));
- return $value->setComplexity(function () use ($resolver, $method) {
- return call_user_func_array([app($resolver), $method], func_get_args());
+ return $childrenComplexity * $complexity;
});
}
- return $value->setComplexity(function ($childrenComplexity, $args) {
- $complexity = array_get($args, 'first', array_get($args, 'count', 1));
+ $resolverClass = $this->namespaceClassName($baseClassName);
+ $resolverMethod = $this->associatedArgValue('method') ?? str_after($this->associatedArgValue('resolver'), '@');
+
+ if (!method_exists($resolverClass, $resolverMethod)) {
+ throw new DirectiveException("Method '{$resolverMethod}' does not exist on class '{$resolverClass}'");
+ }
- return $childrenComplexity * $complexity;
+ return $value->setComplexity(function () use ($resolverClass, $resolverMethod) {
+ return call_user_func_array([app($resolverClass), $resolverMethod], func_get_args());
});
}
}
diff --git a/src/Schema/Directives/Fields/CreateDirective.php b/src/Schema/Directives/Fields/CreateDirective.php
index 3b38db96ca..e7b5a33090 100644
--- a/src/Schema/Directives/Fields/CreateDirective.php
+++ b/src/Schema/Directives/Fields/CreateDirective.php
@@ -5,12 +5,9 @@
use Nuwave\Lighthouse\Schema\Values\FieldValue;
use Nuwave\Lighthouse\Support\Contracts\FieldResolver;
use Nuwave\Lighthouse\Support\Exceptions\DirectiveException;
-use Nuwave\Lighthouse\Support\Traits\HandlesDirectives;
-class CreateDirective implements FieldResolver
+class CreateDirective extends BaseFieldDirective implements FieldResolver
{
- use HandlesDirectives;
-
/**
* Name of the directive.
*
@@ -31,12 +28,9 @@ public function name()
public function resolveField(FieldValue $value)
{
// TODO: create a model registry so we can auto-resolve this.
- $model = $this->directiveArgValue(
- $this->fieldDirective($value->getField(), $this->name()),
- 'model'
- );
+ $model = $this->associatedArgValue('model');
- if (! $model) {
+ if (!$model) {
throw new DirectiveException(sprintf(
'The `create` directive on %s [%s] must have a `model` argument',
$value->getNodeName(),
diff --git a/src/Schema/Directives/Fields/DeleteDirective.php b/src/Schema/Directives/Fields/DeleteDirective.php
index 2062dcb84f..5402ce727a 100644
--- a/src/Schema/Directives/Fields/DeleteDirective.php
+++ b/src/Schema/Directives/Fields/DeleteDirective.php
@@ -7,12 +7,11 @@
use Nuwave\Lighthouse\Schema\Values\FieldValue;
use Nuwave\Lighthouse\Support\Contracts\FieldResolver;
use Nuwave\Lighthouse\Support\Exceptions\DirectiveException;
-use Nuwave\Lighthouse\Support\Traits\HandlesDirectives;
use Nuwave\Lighthouse\Support\Traits\HandlesGlobalId;
-class DeleteDirective implements FieldResolver
+class DeleteDirective extends BaseFieldDirective implements FieldResolver
{
- use HandlesDirectives, HandlesGlobalId;
+ use HandlesGlobalId;
/**
* Name of the directive.
@@ -34,18 +33,10 @@ public function name()
public function resolveField(FieldValue $value)
{
$idArg = $this->getIDField($value);
- $class = $this->directiveArgValue(
- $this->fieldDirective($value->getField(), $this->name()),
- 'model'
- );
+ $class = $this->associatedArgValue('model');
+ $globalId = $this->associatedArgValue('globalId', false);
- $globalId = $this->directiveArgValue(
- $this->fieldDirective($value->getField(), $this->name()),
- 'globalId',
- false
- );
-
- if (! $class) {
+ if (!$class) {
throw new DirectiveException(sprintf(
'The `delete` directive on %s [%s] must have a `model` argument',
$value->getNodeName(),
@@ -53,7 +44,7 @@ public function resolveField(FieldValue $value)
));
}
- if (! $idArg) {
+ if (!$idArg) {
new DirectiveException(sprintf(
'The `delete` requires that you have an `ID` field on %s',
$value->getNodeName()
diff --git a/src/Schema/Directives/Fields/EventDirective.php b/src/Schema/Directives/Fields/EventDirective.php
index a457552d5b..780a77ac11 100644
--- a/src/Schema/Directives/Fields/EventDirective.php
+++ b/src/Schema/Directives/Fields/EventDirective.php
@@ -3,15 +3,11 @@
namespace Nuwave\Lighthouse\Schema\Directives\Fields;
use Closure;
-use GraphQL\Language\AST\FieldDefinitionNode;
use Nuwave\Lighthouse\Schema\Values\FieldValue;
use Nuwave\Lighthouse\Support\Contracts\FieldMiddleware;
-use Nuwave\Lighthouse\Support\Traits\HandlesDirectives;
-class EventDirective implements FieldMiddleware
+class EventDirective extends BaseFieldDirective implements FieldMiddleware
{
- use HandlesDirectives;
-
/**
* Name of the directive.
*
@@ -27,38 +23,21 @@ public function name()
*
* @param FieldValue $value
*
- * @return Closure
+ * @return FieldValue
+ * @throws \Nuwave\Lighthouse\Support\Exceptions\DirectiveException
*/
public function handleField(FieldValue $value)
{
- $event = $this->getEvent($value->getField());
+ $eventBaseName = $this->associatedArgValue('fire') ?? $this->associatedArgValue('class');
+ $eventClassName = $this->namespaceClassName($eventBaseName);
$resolver = $value->getResolver();
- return $value->setResolver(function () use ($resolver, $event) {
+ return $value->setResolver(function () use ($resolver, $eventClassName) {
$args = func_get_args();
$value = call_user_func_array($resolver, $args);
- event(new $event($value));
+ event(new $eventClassName($value));
return $value;
});
}
-
- /**
- * Get the event name.
- *
- * @param FieldDefinitionNode $field
- *
- * @return mixed
- */
- protected function getEvent(FieldDefinitionNode $field)
- {
- return $this->directiveArgValue(
- $this->fieldDirective($field, 'event'),
- 'fire',
- $this->directiveArgValue(
- $this->fieldDirective($field, 'event'),
- 'class'
- )
- );
- }
}
diff --git a/src/Schema/Directives/Fields/FieldDirective.php b/src/Schema/Directives/Fields/FieldDirective.php
index c706f01f8c..54466e097e 100644
--- a/src/Schema/Directives/Fields/FieldDirective.php
+++ b/src/Schema/Directives/Fields/FieldDirective.php
@@ -2,16 +2,12 @@
namespace Nuwave\Lighthouse\Schema\Directives\Fields;
-use GraphQL\Language\AST\DirectiveNode;
use Nuwave\Lighthouse\Schema\Values\FieldValue;
use Nuwave\Lighthouse\Support\Contracts\FieldResolver;
use Nuwave\Lighthouse\Support\Exceptions\DirectiveException;
-use Nuwave\Lighthouse\Support\Traits\CanParseResolvers;
-class FieldDirective implements FieldResolver
+class FieldDirective extends BaseFieldDirective implements FieldResolver
{
- use CanParseResolvers;
-
/**
* Field resolver.
*
@@ -45,19 +41,26 @@ public function name()
*/
public function resolveField(FieldValue $value)
{
- $directive = $this->fieldDirective($value->getField(), $this->name());
- $resolver = $this->getResolver($value, $directive);
- $method = $this->getResolverMethod($directive);
- $data = $this->argValue(collect($directive->arguments)->first(function ($arg) {
- return 'args' === data_get($arg, 'name.value');
- }));
+ $baseClassName = $this->associatedArgValue('class') ?? str_before($this->associatedArgValue('resolver'), '@');
+
+ if (empty($baseClassName)) {
+ $directiveName = $this->name();
+ throw new DirectiveException("Directive '{$directiveName}' must have a `class` argument.");
+ }
+
+ $resolverClass = $this->namespaceClassName($baseClassName);
+ $resolverMethod = $this->associatedArgValue('method') ?? str_after($this->associatedArgValue('resolver'), '@');
+
+ if (! method_exists($resolverClass, $resolverMethod)) {
+ throw new DirectiveException("Method '{$resolverMethod}' does not exist on class '{$resolverClass}'");
+ }
- return $value->setResolver(function ($root, array $args, $context = null, $info = null) use ($resolver, $method, $data) {
- $instance = app($resolver);
+ $additionalData = $this->associatedArgValue('args');
+ return $value->setResolver(function ($root, array $args, $context = null, $info = null) use ($resolverClass, $resolverMethod, $additionalData) {
return call_user_func_array(
- [$instance, $method],
- [$root, array_merge($args, ['directive' => $data]), $context, $info]
+ [app($resolverClass), $resolverMethod],
+ [$root, array_merge($args, ['directive' => $additionalData]), $context, $info]
);
});
}
diff --git a/src/Schema/Directives/Fields/FindDirective.php b/src/Schema/Directives/Fields/FindDirective.php
index 9ab3d793d2..2a6f6a1832 100644
--- a/src/Schema/Directives/Fields/FindDirective.php
+++ b/src/Schema/Directives/Fields/FindDirective.php
@@ -1,21 +1,19 @@
applyFilters($model::query(), $args);
$query = $this->applyScopes($query, $args, $value);
$total = $query->count();
- if($total > 1) {
+ if ($total > 1) {
throw new Error('Query returned more than one result.');
}
return $query->first();
});
}
-}
\ No newline at end of file
+}
diff --git a/src/Schema/Directives/Fields/FirstDirective.php b/src/Schema/Directives/Fields/FirstDirective.php
index 63d68979fc..deb1002f19 100644
--- a/src/Schema/Directives/Fields/FirstDirective.php
+++ b/src/Schema/Directives/Fields/FirstDirective.php
@@ -1,18 +1,16 @@
first();
});
}
-}
\ No newline at end of file
+}
diff --git a/src/Schema/Directives/Fields/GlobalIdDirective.php b/src/Schema/Directives/Fields/GlobalIdDirective.php
index 7f28076e54..41e16aec61 100644
--- a/src/Schema/Directives/Fields/GlobalIdDirective.php
+++ b/src/Schema/Directives/Fields/GlobalIdDirective.php
@@ -4,12 +4,11 @@
use Nuwave\Lighthouse\Schema\Values\FieldValue;
use Nuwave\Lighthouse\Support\Contracts\FieldMiddleware;
-use Nuwave\Lighthouse\Support\Traits\HandlesDirectives;
use Nuwave\Lighthouse\Support\Traits\HandlesGlobalId;
-class GlobalIdDirective implements FieldMiddleware
+class GlobalIdDirective extends BaseFieldDirective implements FieldMiddleware
{
- use HandlesDirectives, HandlesGlobalId;
+ use HandlesGlobalId;
/**
* Name of the directive.
@@ -32,19 +31,15 @@ public function handleField(FieldValue $value)
{
$type = $value->getNodeName();
$resolver = $value->getResolver();
- $process = $this->directiveArgValue(
- $this->fieldDirective($value->getField(), 'globalId'),
- 'process',
- 'encode'
- );
+ $process = $this->associatedArgValue('process', 'encode');
return $value->setResolver(function () use ($resolver, $process, $type) {
$args = func_get_args();
$value = call_user_func_array($resolver, $args);
return 'encode' === $process
- ? $this->encodeGlobalId($type, $value)
- : $this->decodeRelayId($value);
+ ? $this->encodeGlobalId($type, $value)
+ : $this->decodeRelayId($value);
});
}
}
diff --git a/src/Schema/Directives/Fields/HasManyDirective.php b/src/Schema/Directives/Fields/HasManyDirective.php
index 580d43c585..1d8f032c8e 100644
--- a/src/Schema/Directives/Fields/HasManyDirective.php
+++ b/src/Schema/Directives/Fields/HasManyDirective.php
@@ -3,18 +3,17 @@
namespace Nuwave\Lighthouse\Schema\Directives\Fields;
use GraphQL\Language\AST\FieldDefinitionNode;
+use GraphQL\Language\AST\ObjectTypeDefinitionNode;
use GraphQL\Type\Definition\ResolveInfo;
+use Nuwave\Lighthouse\Schema\AST\DocumentAST;
use Nuwave\Lighthouse\Schema\Values\FieldValue;
+use Nuwave\Lighthouse\Support\Contracts\FieldManipulator;
use Nuwave\Lighthouse\Support\Contracts\FieldResolver;
use Nuwave\Lighthouse\Support\DataLoader\Loaders\HasManyLoader;
use Nuwave\Lighthouse\Support\Exceptions\DirectiveException;
-use Nuwave\Lighthouse\Support\Traits\CreatesPaginators;
-use Nuwave\Lighthouse\Support\Traits\HandlesGlobalId;
-class HasManyDirective implements FieldResolver
+class HasManyDirective extends PaginationManipulator implements FieldResolver, FieldManipulator
{
- use CreatesPaginators, HandlesGlobalId;
-
/**
* Name of the directive.
*
@@ -26,148 +25,72 @@ public function name()
}
/**
- * Resolve the field directive.
+ * @param FieldDefinitionNode $fieldDefinition
+ * @param ObjectTypeDefinitionNode $parentType
+ * @param DocumentAST $current
+ * @param DocumentAST $original
*
- * @param FieldValue $value
+ * @throws DirectiveException
*
- * @return FieldValue
+ * @return DocumentAST
*/
- public function resolveField(FieldValue $value)
+ public function manipulateSchema(FieldDefinitionNode $fieldDefinition, ObjectTypeDefinitionNode $parentType, DocumentAST $current, DocumentAST $original)
{
- $relation = $this->getRelationshipName($value->getField());
- $resolver = $this->getResolver($value->getField());
-
- if (! in_array($resolver, ['default', 'paginator', 'relay', 'connection'])) {
- throw new DirectiveException(sprintf(
- '[%s] is not a valid `type` on `hasMany` directive [`paginator`, `relay`, `default`].',
- $resolver
- ));
- }
+ $paginationType = $this->getResolverType();
- switch ($resolver) {
- case 'paginator':
- return $value->setResolver(
- $this->paginatorTypeResolver($relation, $value)
- );
- case 'connection':
- case 'relay':
- return $value->setResolver(
- $this->connectionTypeResolver($relation, $value)
- );
+ switch ($paginationType) {
+ case self::PAGINATION_TYPE_PAGINATOR:
+ return $this->registerPaginator($fieldDefinition, $parentType, $current, $original);
+ case self::PAGINATION_TYPE_CONNECTION:
+ return $this->registerConnection($fieldDefinition, $parentType, $current, $original);
default:
- return $value->setResolver(
- $this->defaultResolver($relation, $value)
- );
+ // Leave the field as-is when no pagination is requested
+ return $current;
}
}
/**
- * Get has many relationship name.
- *
- * @param FieldDefinitionNode $field
- *
- * @return string
- */
- protected function getRelationshipName(FieldDefinitionNode $field)
- {
- return $this->directiveArgValue(
- $this->fieldDirective($field, $this->name()),
- 'relation',
- $field->name->value
- );
- }
-
- /**
- * Get resolver type.
- *
- * @param FieldDefinitionNode $field
- *
- * @return string
- */
- protected function getResolver(FieldDefinitionNode $field)
- {
- return $this->directiveArgValue(
- $this->fieldDirective($field, $this->name()),
- 'type',
- 'default'
- );
- }
-
- /**
- * Get connection type.
+ * Resolve the field directive.
*
- * @param string $relation
* @param FieldValue $value
*
- * @return \Closure
+ * @return FieldValue
*/
- protected function connectionTypeResolver($relation, FieldValue $value)
+ public function resolveField(FieldValue $value)
{
- $this->registerConnection($value);
- $scopes = $this->getScopes($value);
+ $relation = $this->associatedArgValue('relation', $value->getFieldName());
+ $type = $this->getResolverType();
+ $scopes = $this->associatedArgValue('scopes', []);
- return function ($parent, array $args, $context = null, ResolveInfo $info = null) use ($relation, $scopes) {
+ return $value->setResolver(function ($parent, array $args, $context = null, ResolveInfo $info = null) use ($relation, $scopes, $type) {
return graphql()->batch(HasManyLoader::class, $parent->getKey(), array_merge(
compact('relation', 'parent', 'args', 'scopes'),
- ['type' => 'relay']
+ ['type' => $type]
), HasManyLoader::key($parent, $relation, $info));
- };
+ });
}
/**
- * Get paginator type resolver.
+ * @throws DirectiveException
*
- * @param string $relation
- * @param FieldValue $value
- *
- * @return \Closure
+ * @return string
*/
- protected function paginatorTypeResolver($relation, FieldValue $value)
+ protected function getResolverType()
{
- $this->registerPaginator($value);
- $scopes = $this->getScopes($value);
+ $paginationType = $this->associatedArgValue('type', 'default');
- return function ($parent, array $args, $context = null, ResolveInfo $info = null) use ($relation, $scopes) {
- return graphql()->batch(HasManyLoader::class, $parent->getKey(), array_merge(
- compact('relation', 'parent', 'args', 'scopes'),
- ['type' => 'paginator']
- ), HasManyLoader::key($parent, $relation, $info));
- };
- }
+ if ('default' === $paginationType) {
+ return $paginationType;
+ }
- /**
- * Use default resolver for field.
- *
- * @param FieldValue $value
- * @param string $relation
- *
- * @return \Closure
- */
- protected function defaultResolver($relation, FieldValue $value)
- {
- $scopes = $this->getScopes($value);
+ $paginationType = $this->convertAliasToPaginationType($paginationType);
- return function ($parent, array $args, $context = null, ResolveInfo $info = null) use ($relation, $scopes) {
- return graphql()->batch(HasManyLoader::class, $parent->getKey(), array_merge(
- compact('relation', 'parent', 'args', 'scopes'),
- ['type' => 'default']
- ), HasManyLoader::key($parent, $relation, $info));
- };
- }
+ if (!$this->isValidPaginationType($paginationType)) {
+ $fieldName = $this->fieldDefinition->name->value;
+ $directiveName = self::name();
+ throw new DirectiveException("'$paginationType' is not a valid pagination type. Field: '$fieldName', Directive: '$directiveName'");
+ }
- /**
- * Get scope(s) to run on connection.
- *
- * @param FieldValue $value
- *
- * @return array
- */
- protected function getScopes(FieldValue $value)
- {
- return $this->directiveArgValue(
- $this->fieldDirective($value->getField(), $this->name()),
- 'scopes',
- []
- );
+ return $paginationType;
}
}
diff --git a/src/Schema/Directives/Fields/InjectDirective.php b/src/Schema/Directives/Fields/InjectDirective.php
index a95e975839..d3db208927 100644
--- a/src/Schema/Directives/Fields/InjectDirective.php
+++ b/src/Schema/Directives/Fields/InjectDirective.php
@@ -5,12 +5,9 @@
use Nuwave\Lighthouse\Schema\Values\FieldValue;
use Nuwave\Lighthouse\Support\Contracts\FieldMiddleware;
use Nuwave\Lighthouse\Support\Exceptions\DirectiveException;
-use Nuwave\Lighthouse\Support\Traits\HandlesDirectives;
-class InjectDirective implements FieldMiddleware
+class InjectDirective extends BaseFieldDirective implements FieldMiddleware
{
- use HandlesDirectives;
-
/**
* Name of the directive.
*
@@ -31,17 +28,10 @@ public function name()
public function handleField(FieldValue $value)
{
$resolver = $value->getResolver();
- $attr = $this->directiveArgValue(
- $this->fieldDirective($value->getField(), $this->name()),
- 'context'
- );
-
- $name = $this->directiveArgValue(
- $this->fieldDirective($value->getField(), $this->name()),
- 'name'
- );
+ $attr = $this->associatedArgValue('context');
+ $name = $this->associatedArgValue('name');
- if (! $attr) {
+ if (!$attr) {
throw new DirectiveException(sprintf(
'The `inject` directive on %s [%s] must have a `context` argument',
$value->getNodeName(),
@@ -49,7 +39,7 @@ public function handleField(FieldValue $value)
));
}
- if (! $name) {
+ if (!$name) {
throw new DirectiveException(sprintf(
'The `inject` directive on %s [%s] must have a `name` argument',
$value->getNodeName(),
diff --git a/src/Schema/Directives/Fields/MethodDirective.php b/src/Schema/Directives/Fields/MethodDirective.php
index 3e4878eb48..c056eb35ac 100644
--- a/src/Schema/Directives/Fields/MethodDirective.php
+++ b/src/Schema/Directives/Fields/MethodDirective.php
@@ -5,12 +5,9 @@
use GraphQL\Type\Definition\ResolveInfo;
use Nuwave\Lighthouse\Schema\Values\FieldValue;
use Nuwave\Lighthouse\Support\Contracts\FieldResolver;
-use Nuwave\Lighthouse\Support\Traits\HandlesDirectives;
-class MethodDirective implements FieldResolver
+class MethodDirective extends BaseFieldDirective implements FieldResolver
{
- use HandlesDirectives;
-
/**
* Name of the directive.
*
@@ -30,11 +27,7 @@ public function name()
*/
public function resolveField(FieldValue $value)
{
- $method = $this->directiveArgValue(
- $this->fieldDirective($value->getField(), 'method'),
- 'name',
- $value->getField()->name->value
- );
+ $method = $this->associatedArgValue('name', $value->getFieldName());
return $value->setResolver(function ($root, array $args, $context = null, ResolveInfo $info = null) use ($method) {
return call_user_func_array([$root, $method], [$args, $context, $info]);
diff --git a/src/Schema/Directives/Fields/MiddlewareDirective.php b/src/Schema/Directives/Fields/MiddlewareDirective.php
index 6a0876792f..d7cfefe72d 100644
--- a/src/Schema/Directives/Fields/MiddlewareDirective.php
+++ b/src/Schema/Directives/Fields/MiddlewareDirective.php
@@ -4,12 +4,9 @@
use Nuwave\Lighthouse\Schema\Values\FieldValue;
use Nuwave\Lighthouse\Support\Contracts\FieldMiddleware;
-use Nuwave\Lighthouse\Support\Traits\HandlesDirectives;
-class MiddlewareDirective implements FieldMiddleware
+class MiddlewareDirective extends BaseFieldDirective implements FieldMiddleware
{
- use HandlesDirectives;
-
/**
* Name of the directive.
*
@@ -57,16 +54,13 @@ public function handleField(FieldValue $value)
*/
protected function getChecks(FieldValue $value)
{
- if (! in_array($value->getNodeName(), ['Mutation', 'Query'])) {
+ if (!in_array($value->getNodeName(), ['Mutation', 'Query'])) {
return null;
}
- $checks = $this->directiveArgValue(
- $this->fieldDirective($value->getField(), $this->name()),
- 'checks'
- );
+ $checks = $this->associatedArgValue('checks');
- if (! $checks) {
+ if (!$checks) {
return null;
}
diff --git a/src/Schema/Directives/Fields/NamespaceDirective.php b/src/Schema/Directives/Fields/NamespaceDirective.php
new file mode 100644
index 0000000000..91d89efe74
--- /dev/null
+++ b/src/Schema/Directives/Fields/NamespaceDirective.php
@@ -0,0 +1,31 @@
+getPaginationType()) {
+ case self::PAGINATION_TYPE_CONNECTION:
+ return $this->registerConnection($fieldDefinition, $parentType, $current, $original);
+ case self::PAGINATION_TYPE_PAGINATOR:
+ return $this->registerPaginator($fieldDefinition, $parentType, $current, $original);
+ }
+ }
+
/**
* Resolve the field directive.
*
* @param FieldValue $value
*
- * @return FieldValue
* @throws DirectiveException
+ *
+ * @return FieldValue
*/
public function resolveField(FieldValue $value)
{
- $type = $this->directiveArgValue(
- $this->fieldDirective($value->getField(), $this->name()),
- 'type',
- 'paginator'
- );
+ $paginationType = $this->getPaginationType();
$model = $this->getModelClass($value);
- $resolver = in_array($type, ['relay', 'connection'])
- ? $this->connectionTypeResolver($value, $model)
- : $this->paginatorTypeResolver($value, $model);
+ switch ($paginationType) {
+ case self::PAGINATION_TYPE_CONNECTION:
+ return $this->connectionTypeResolver($value, $model);
+ case self::PAGINATION_TYPE_PAGINATOR:
+ return $this->paginatorTypeResolver($value, $model);
+ }
+ }
- return $value->setResolver($resolver);
+ /**
+ * @return string
+ * @throws DirectiveException
+ */
+ protected function getPaginationType()
+ {
+ $paginationType = $this->associatedArgValue('type', self::PAGINATION_TYPE_PAGINATOR);
+
+ $paginationType = $this->convertAliasToPaginationType($paginationType);
+ if (!$this->isValidPaginationType($paginationType)) {
+ $fieldName = $this->fieldDefinition->name->value;
+ $directiveName = self::name();
+ throw new DirectiveException("'$paginationType' is not a valid pagination type. Field: '$fieldName', Directive: '$directiveName'");
+ }
+
+ return $paginationType;
}
/**
* Create a paginator resolver.
*
* @param FieldValue $value
- * @param string $model
+ * @param string $model
*
- * @return \Closure
+ * @return FieldValue
*/
protected function paginatorTypeResolver(FieldValue $value, $model)
{
- $this->registerPaginator($value);
-
- return function ($root, array $args) use ($model, $value) {
+ return $value->setResolver(function ($root, array $args) use ($model, $value) {
$first = data_get($args, 'count', 15);
$page = data_get($args, 'page', 1);
$query = $this->applyFilters($model::query(), $args);
$query = $this->applyScopes($query, $args, $value);
- Paginator::currentPageResolver(function() use ($page) {
+ Paginator::currentPageResolver(function () use ($page) {
return $page;
});
+
return $query->paginate($first);
- };
+ });
}
/**
@@ -83,13 +120,11 @@ protected function paginatorTypeResolver(FieldValue $value, $model)
* @param FieldValue $value
* @param string $model
*
- * @return \Closure
+ * @return FieldValue
*/
protected function connectionTypeResolver(FieldValue $value, $model)
{
- $this->registerConnection($value);
-
- return function ($root, array $args) use ($model, $value) {
+ return $value->setResolver(function ($root, array $args) use ($model, $value) {
$first = data_get($args, 'first', 15);
$after = $this->decodeCursor($args);
$page = $first && $after ? floor(($first + $after) / $first) : 1;
@@ -97,10 +132,11 @@ protected function connectionTypeResolver(FieldValue $value, $model)
$query = $this->applyFilters($model::query(), $args);
$query = $this->applyScopes($query, $args, $value);
- Paginator::currentPageResolver(function() use ($page) {
+ Paginator::currentPageResolver(function () use ($page) {
return $page;
});
+
return $query->paginate($first);
- };
+ });
}
}
diff --git a/src/Schema/Directives/Fields/PaginationManipulator.php b/src/Schema/Directives/Fields/PaginationManipulator.php
new file mode 100644
index 0000000000..8c9ecc419a
--- /dev/null
+++ b/src/Schema/Directives/Fields/PaginationManipulator.php
@@ -0,0 +1,206 @@
+connectionTypeName($fieldDefinition, $parentType);
+ $connectionEdgeName = $this->connectionEdgeName($fieldDefinition, $parentType);
+ $connectionFieldName = addslashes(ConnectionField::class);
+
+ $connectionType = PartialParser::objectTypeDefinition("
+ type $connectionTypeName {
+ pageInfo: PageInfo! @field(class: \"$connectionFieldName\" method: \"pageInfoResolver\")
+ edges: [$connectionEdgeName] @field(class: \"$connectionFieldName\" method: \"edgeResolver\")
+ }
+ ");
+
+ $nodeName = $this->unpackNodeToString($fieldDefinition);
+ $connectionEdge = PartialParser::objectTypeDefinition("
+ type $connectionEdgeName {
+ node: $nodeName
+ cursor: String!
+ }
+ ");
+
+ $connectionArguments = PartialParser::inputValueDefinitions([
+ 'first: Int!',
+ 'after: String',
+ ]);
+
+ $fieldDefinition->arguments = ASTHelper::mergeNodeList($fieldDefinition->arguments, $connectionArguments);
+ $fieldDefinition->type = Parser::parseType($connectionTypeName);
+ $parentType->fields = ASTHelper::mergeNodeList($parentType->fields, [$fieldDefinition]);
+
+ $current->setDefinition($connectionType);
+ $current->setDefinition($connectionEdge);
+ $current->setDefinition($parentType);
+
+ return $current;
+ }
+
+ /**
+ * Register paginator w/ schema.
+ *
+ * @param FieldDefinitionNode $fieldDefinition
+ * @param ObjectTypeDefinitionNode $parentType
+ * @param DocumentAST $current
+ * @param DocumentAST $original
+ *
+ * @throws \Exception
+ *
+ * @return DocumentAST
+ */
+ protected function registerPaginator(FieldDefinitionNode $fieldDefinition, ObjectTypeDefinitionNode $parentType, DocumentAST $current, DocumentAST $original)
+ {
+ $paginatorTypeName = $this->paginatorTypeName($fieldDefinition, $parentType);
+ $paginatorFieldClassName = addslashes(PaginatorField::class);
+ $fieldTypeName = $this->unpackNodeToString($fieldDefinition);
+
+ $paginatorType = PartialParser::objectTypeDefinition("
+ type $paginatorTypeName {
+ paginatorInfo: PaginatorInfo! @field(class: \"$paginatorFieldClassName\" method: \"paginatorInfoResolver\")
+ data: [$fieldTypeName!]! @field(class: \"$paginatorFieldClassName\" method: \"dataResolver\")
+ }
+ ");
+
+ $paginationArguments = PartialParser::inputValueDefinitions([
+ 'count: Int!',
+ 'page: Int',
+ ]);
+
+ $fieldDefinition->arguments = ASTHelper::mergeNodeList($fieldDefinition->arguments, $paginationArguments);
+ $fieldDefinition->type = Parser::parseType($paginatorTypeName);
+ $parentType->fields = ASTHelper::mergeNodeList($parentType->fields, [$fieldDefinition]);
+
+ $current->setDefinition($paginatorType);
+ $current->setDefinition($parentType);
+
+ return $current;
+ }
+
+ /**
+ * Get paginator type name.
+ *
+ * @param FieldDefinitionNode $fieldDefinition
+ * @param ObjectTypeDefinitionNode $parent
+ *
+ * @return string
+ */
+ protected function paginatorTypeName(FieldDefinitionNode $fieldDefinition, ObjectTypeDefinitionNode $parent)
+ {
+ return studly_case(
+ $this->parentTypeName($parent)
+ . $this->singularFieldName($fieldDefinition)
+ . '_Paginator'
+ );
+ }
+
+ /**
+ * Get connection type name.
+ *
+ * @param FieldDefinitionNode $fieldDefinition
+ * @param ObjectTypeDefinitionNode $parent
+ *
+ * @return string
+ */
+ protected function connectionTypeName(FieldDefinitionNode $fieldDefinition, ObjectTypeDefinitionNode $parent)
+ {
+ return studly_case(
+ $this->parentTypeName($parent)
+ . $this->singularFieldName($fieldDefinition)
+ . '_Connection'
+ );
+ }
+
+ /**
+ * Get connection edge name.
+ *
+ * @param FieldDefinitionNode $fieldDefinition
+ * @param ObjectTypeDefinitionNode $parent
+ *
+ * @return string
+ */
+ protected function connectionEdgeName(FieldDefinitionNode $fieldDefinition, ObjectTypeDefinitionNode $parent)
+ {
+ return studly_case(
+ $this->parentTypeName($parent)
+ . $this->singularFieldName($fieldDefinition)
+ . '_Edge'
+ );
+ }
+
+ /**
+ * @param FieldDefinitionNode $fieldDefinition
+ *
+ * @return string
+ */
+ protected function singularFieldName(FieldDefinitionNode $fieldDefinition)
+ {
+ return str_singular($fieldDefinition->name->value);
+ }
+
+ /**
+ * @param ObjectTypeDefinitionNode $objectType
+ *
+ * @return string
+ */
+ protected function parentTypeName(ObjectTypeDefinitionNode $objectType)
+ {
+ $name = $objectType->name->value;
+
+ return 'Query' === $name ? '' : $name . '_';
+ }
+}
diff --git a/src/Schema/Directives/Fields/RenameDirective.php b/src/Schema/Directives/Fields/RenameDirective.php
index ef6d4a8e67..4f86edd09a 100644
--- a/src/Schema/Directives/Fields/RenameDirective.php
+++ b/src/Schema/Directives/Fields/RenameDirective.php
@@ -5,12 +5,9 @@
use Nuwave\Lighthouse\Schema\Values\FieldValue;
use Nuwave\Lighthouse\Support\Contracts\FieldResolver;
use Nuwave\Lighthouse\Support\Exceptions\DirectiveException;
-use Nuwave\Lighthouse\Support\Traits\HandlesDirectives;
-class RenameDirective implements FieldResolver
+class RenameDirective extends BaseFieldDirective implements FieldResolver
{
- use HandlesDirectives;
-
/**
* Name of the directive.
*
@@ -26,16 +23,14 @@ public function name()
*
* @param FieldValue $value
*
- * @return \Closure
+ * @return FieldValue
+ * @throws DirectiveException
*/
public function resolveField(FieldValue $value)
{
- $attribute = $this->directiveArgValue(
- $this->fieldDirective($value->getField(), $this->name()),
- 'attribute'
- );
+ $attribute = $this->associatedArgValue('attribute');
- if (! $attribute) {
+ if (!$attribute) {
throw new DirectiveException(sprintf(
'The [%s] directive requires an `attribute` argument.',
$this->name()
diff --git a/src/Schema/Directives/Fields/UpdateDirective.php b/src/Schema/Directives/Fields/UpdateDirective.php
index 5066272704..1c17b9c4f7 100644
--- a/src/Schema/Directives/Fields/UpdateDirective.php
+++ b/src/Schema/Directives/Fields/UpdateDirective.php
@@ -7,12 +7,11 @@
use Nuwave\Lighthouse\Schema\Values\FieldValue;
use Nuwave\Lighthouse\Support\Contracts\FieldResolver;
use Nuwave\Lighthouse\Support\Exceptions\DirectiveException;
-use Nuwave\Lighthouse\Support\Traits\HandlesDirectives;
use Nuwave\Lighthouse\Support\Traits\HandlesGlobalId;
-class UpdateDirective implements FieldResolver
+class UpdateDirective extends BaseFieldDirective implements FieldResolver
{
- use HandlesDirectives, HandlesGlobalId;
+ use HandlesGlobalId;
/**
* Name of the directive.
@@ -34,18 +33,10 @@ public function name()
public function resolveField(FieldValue $value)
{
$idArg = $this->getIDField($value);
- $class = $this->directiveArgValue(
- $this->fieldDirective($value->getField(), $this->name()),
- 'model'
- );
+ $class = $this->associatedArgValue('model');
+ $globalId = $this->associatedArgValue('globalId', false);
- $globalId = $this->directiveArgValue(
- $this->fieldDirective($value->getField(), $this->name()),
- 'globalId',
- false
- );
-
- if (! $class) {
+ if (!$class) {
throw new DirectiveException(sprintf(
'The `update` directive on %s [%s] must have a `model` argument',
$value->getNodeName(),
@@ -53,7 +44,7 @@ public function resolveField(FieldValue $value)
));
}
- if (! $idArg) {
+ if (!$idArg) {
new DirectiveException(sprintf(
'The `update` requires that you have an `ID` field on %s',
$value->getNodeName()
diff --git a/src/Schema/Directives/Nodes/GroupDirective.php b/src/Schema/Directives/Nodes/GroupDirective.php
index a54bbd57c6..d0b4ab30d1 100644
--- a/src/Schema/Directives/Nodes/GroupDirective.php
+++ b/src/Schema/Directives/Nodes/GroupDirective.php
@@ -2,12 +2,26 @@
namespace Nuwave\Lighthouse\Schema\Directives\Nodes;
-use Nuwave\Lighthouse\Schema\Values\NodeValue;
-use Nuwave\Lighthouse\Support\Contracts\NodeMiddleware;
+use GraphQL\Language\AST\DirectiveNode;
+use GraphQL\Language\AST\FieldDefinitionNode;
+use GraphQL\Language\AST\NodeList;
+use GraphQL\Language\AST\ObjectTypeDefinitionNode;
+use Nuwave\Lighthouse\Schema\AST\DocumentAST;
+use Nuwave\Lighthouse\Schema\AST\PartialParser;
+use Nuwave\Lighthouse\Schema\Directives\Fields\NamespaceDirective;
+use Nuwave\Lighthouse\Support\Contracts\NodeManipulator;
use Nuwave\Lighthouse\Support\Exceptions\DirectiveException;
use Nuwave\Lighthouse\Support\Traits\HandlesDirectives;
-class GroupDirective implements NodeMiddleware
+/**
+ * Class GroupDirective.
+ *
+ * This directive is kept for compatibility reasons but is superseded by
+ * NamespaceDirective and MiddlewareDirective.
+ *
+ * @deprecated Will be removed in next major version
+ */
+class GroupDirective implements NodeManipulator
{
use HandlesDirectives;
@@ -22,68 +36,115 @@ public function name()
}
/**
- * Handle node value.
+ * @param ObjectTypeDefinitionNode $objectType
+ * @param DocumentAST $current
+ * @param DocumentAST $original
*
- * @param NodeValue $value
+ * @throws DirectiveException
*
- * @return NodeValue
+ * @return DocumentAST
*/
- public function handleNode(NodeValue $value)
+ public function manipulateSchema(ObjectTypeDefinitionNode $objectType, DocumentAST $current, DocumentAST $original)
{
- $this->setNamespace($value);
- $this->setMiddleware($value);
+ $nodeName = $objectType->name->value;
- return $value;
+ if (! in_array($nodeName, ['Query', 'Mutation'])) {
+ $message = "The group directive can only be placed on a Query or Mutation [$nodeName]";
+
+ throw new DirectiveException($message);
+ }
+
+ $objectType = $this->setMiddlewareDirectiveOnFields($objectType);
+ $objectType = $this->setNamespaceDirectiveOnFields($objectType);
+
+ $current->setDefinition($objectType);
+
+ return $current;
}
/**
- * Set namespace on node.
+ * @param ObjectTypeDefinitionNode $objectType
*
- * @param NodeValue $value [description]
+ * @throws \Exception
+ *
+ * @return ObjectTypeDefinitionNode
*/
- protected function setNamespace(NodeValue $value)
+ protected function setMiddlewareDirectiveOnFields(ObjectTypeDefinitionNode $objectType)
{
- $namespace = $this->directiveArgValue(
- $this->nodeDirective($value->getNode(), $this->name()),
- 'namespace'
+ $middlewareValues = $this->directiveArgValue(
+ $this->nodeDirective($objectType, self::name()),
+ 'middleware'
);
- if ($namespace) {
- $value->setNamespace($namespace);
+ if (! $middlewareValues) {
+ return $objectType;
}
+
+ $middlewareValues = '["'.implode('", "', $middlewareValues).'"]';
+ $middlewareDirective = PartialParser::directive("@middleware(checks: $middlewareValues)");
+
+ $objectType->fields = new NodeList(collect($objectType->fields)->map(function (FieldDefinitionNode $fieldDefinition) use ($middlewareDirective) {
+ $fieldDefinition->directives = $fieldDefinition->directives->merge([$middlewareDirective]);
+
+ return $fieldDefinition;
+ })->toArray());
+
+ return $objectType;
}
/**
- * Set middleware for field.
+ * @param ObjectTypeDefinitionNode $objectType
+ *
+ * @throws \Exception
*
- * @param NodeValue $value
+ * @return ObjectTypeDefinitionNode
*/
- protected function setMiddleware(NodeValue $value)
+ protected function setNamespaceDirectiveOnFields(ObjectTypeDefinitionNode $objectType)
{
- $node = $value->getNodeName();
+ $namespaceValue = $this->directiveArgValue(
+ $this->nodeDirective($objectType, self::name()),
+ 'namespace'
+ );
- if (! in_array($node, ['Query', 'Mutation'])) {
- $message = 'Middleware can only be placed on a Query or Mutation ['.$node.']';
+ if (! $namespaceValue) {
+ return $objectType;
+ }
- throw new DirectiveException($message);
+ if (! is_string($namespaceValue)) {
+ throw new DirectiveException('The value of the namespace directive on has to be a string');
}
- $middleware = $this->directiveArgValue(
- $this->nodeDirective($value->getNode(), $this->name()),
- 'middleware'
- );
+ $namespaceValue = addslashes($namespaceValue);
- $container = graphql()->middleware();
- $middleware = is_string($middleware) ? [$middleware] : $middleware;
+ $objectType->fields = new NodeList(collect($objectType->fields)->map(function (FieldDefinitionNode $fieldDefinition) use ($namespaceValue) {
+ $previousNamespaces = $this->fieldDirective($fieldDefinition, (new NamespaceDirective)->name());
- if (empty($middleware)) {
- return;
- }
+ $previousNamespaces = $previousNamespaces
+ ? $this->mergeNamespaceOnExistingDirective($namespaceValue, $previousNamespaces)
+ : PartialParser::directive("@namespace(field: \"$namespaceValue\", complexity: \"$namespaceValue\")");
+ $fieldDefinition->directives = $fieldDefinition->directives->merge([$previousNamespaces]);
- foreach ($value->getNodeFields() as $field) {
- 'Query' == $node
- ? $container->registerQuery($field->name->value, $middleware)
- : $container->registerMutation($field->name->value, $middleware);
- }
+ return $fieldDefinition;
+ })->toArray());
+
+ return $objectType;
+ }
+
+ /**
+ * @param string $namespaceValue
+ * @param DirectiveNode $directive
+ *
+ * @return DirectiveNode
+ */
+ protected function mergeNamespaceOnExistingDirective($namespaceValue, DirectiveNode $directive)
+ {
+ $namespaces = PartialParser::arguments([
+ "field: \"$namespaceValue\"",
+ "complexity: \"$namespaceValue\"",
+ ]);
+
+ $directive->arguments = $directive->arguments->merge($namespaces);
+
+ return $directive;
}
}
diff --git a/src/Schema/Factories/ArgumentFactory.php b/src/Schema/Factories/ArgumentFactory.php
index 4aa3450070..5228ae4238 100644
--- a/src/Schema/Factories/ArgumentFactory.php
+++ b/src/Schema/Factories/ArgumentFactory.php
@@ -4,11 +4,12 @@
use Nuwave\Lighthouse\Schema\Resolvers\NodeResolver;
use Nuwave\Lighthouse\Schema\Values\ArgumentValue;
+use Nuwave\Lighthouse\Support\Contracts\ArgMiddleware;
class ArgumentFactory
{
/**
- * Convert field definition to type.
+ * Convert argument definition to type.
*
* @param ArgumentValue $value
*
@@ -31,7 +32,7 @@ public function handle(ArgumentValue $value)
protected function applyMiddleware(ArgumentValue $value)
{
return directives()->argMiddleware($value->getArg())
- ->reduce(function (ArgumentValue $value, $middleware) {
+ ->reduce(function (ArgumentValue $value, ArgMiddleware $middleware) {
return $middleware->handleArgument(
$value->setMiddlewareDirective($middleware->name())
);
diff --git a/src/Schema/Factories/DirectiveFactory.php b/src/Schema/Factories/DirectiveFactory.php
deleted file mode 100644
index db4d1f5713..0000000000
--- a/src/Schema/Factories/DirectiveFactory.php
+++ /dev/null
@@ -1,256 +0,0 @@
-directives = collect();
- }
-
- /**
- * Register all of the commands in the given directory.
- *
- * https://github.com/laravel/framework/blob/5.5/src/Illuminate/Foundation/Console/Kernel.php#L190-L224
- *
- * @param array|string $paths
- * @param string|null $namespace
- */
- public function load($paths, $namespace = null)
- {
- $paths = array_unique(is_array($paths) ? $paths : (array) $paths);
- $paths = array_map(function ($path) {
- return realpath($path);
- }, array_filter($paths, function ($path) {
- return is_dir($path);
- }));
-
- if (empty($paths)) {
- return;
- }
-
- $namespace = $namespace ?: app()->getNamespace();
- $path = starts_with($namespace, 'Nuwave\\Lighthouse')
- ? realpath(__DIR__.'/../../')
- : app_path();
-
- foreach ((new Finder())->in($paths)->files() as $directive) {
- $directive = $namespace.str_replace(
- ['/', '.php'],
- ['\\', ''],
- str_after($directive->getPathname(), $path.DIRECTORY_SEPARATOR)
- );
-
- if (! (new \ReflectionClass($directive))->isAbstract()) {
- $this->register($directive);
- }
- }
- }
-
- /**
- * Register a new directive handler.
- *
- * @param string $handler
- */
- public function register($handler)
- {
- $directive = app($handler);
-
- $this->directives->put($directive->name(), $directive);
- }
-
- /**
- * Get instance of handler for directive.
- *
- * @param string $name
- *
- * @return mixed
- */
- public function handler($name)
- {
- $handler = $this->directives->get($name);
-
- if (! $handler) {
- throw new DirectiveException("No directive has been registered for [{$name}]");
- }
-
- return $handler;
- }
-
- /**
- * Check if field has a resolver directive.
- *
- * @param Node $node
- *
- * @return bool
- */
- public function hasNodeResolver(Node $node)
- {
- return collect(data_get($node, 'directives', []))->map(function (DirectiveNode $directive) {
- return $this->handler($directive->name->value);
- })->reduce(function ($has, $handler) {
- return $handler instanceof NodeResolver ? true : $has;
- }, false);
- }
-
- /**
- * Get handler for node.
- *
- * @param Node $node
- *
- * @return mixed
- */
- public function forNode(Node $node)
- {
- $resolvers = collect(data_get($node, 'directives', []))->map(function (DirectiveNode $directive) {
- return $this->handler($directive->name->value);
- })->filter(function ($handler) {
- return $handler instanceof NodeResolver;
- });
-
- if ($resolvers->count() > 1) {
- throw new DirectiveException(sprintf(
- 'Nodes can only have 1 assigned directive. %s has %s directives [%s]',
- data_get($node, 'name.value'),
- count($directives),
- collect($directives)->map(function ($directive) {
- return $directive->name->value;
- })->implode(', ')
- ));
- }
-
- return $resolvers->first();
- }
-
- /**
- * Get middleware for field.
- *
- * @param Node $node
- *
- * @return \Illuminate\Support\Collection
- */
- public function nodeMiddleware(Node $node)
- {
- return collect(data_get($node, 'directives', []))->map(function (DirectiveNode $directive) {
- return $this->handler($directive->name->value);
- })->filter(function ($handler) {
- return $handler instanceof NodeMiddleware;
- });
- }
-
- /**
- * Check if field has a resolver directive.
- *
- * @param FieldDefinitionNode $field
- *
- * @return bool
- */
- public function hasResolver($field)
- {
- return collect($field->directives)->map(function (DirectiveNode $directive) {
- return $this->handler($directive->name->value);
- })->reduce(function ($has, $handler) {
- return $handler instanceof FieldResolver ? true : $has;
- }, false);
- }
-
- /**
- * Get handler for field.
- *
- * @param FieldDefinitionNode $field
- *
- * @return mixed
- */
- public function fieldResolver($field)
- {
- $resolvers = collect($field->directives)->map(function (DirectiveNode $directive) {
- return $this->handler($directive->name->value);
- })->filter(function ($handler) {
- return $handler instanceof FieldResolver;
- });
-
- if ($resolvers->count() > 1) {
- throw new DirectiveException(sprintf(
- 'Fields can only have 1 assigned resolver directive. %s has %s resolver directives [%s]',
- data_get($field, 'name.value'),
- $resolvers->count(),
- collect($field->directives)->map(function (DirectiveNode $directive) {
- return $directive->name->value;
- })->implode(', ')
- ));
- }
-
- return $resolvers->first();
- }
-
- /**
- * Check if field has a resolver directive.
- *
- * @param FieldDefinitionNode $field
- *
- * @return bool
- */
- public function hasFieldMiddleware($field)
- {
- return collect($field->directives)->map(function (DirectiveNode $directive) {
- return $this->handler($directive->name->value);
- })->reduce(function ($has, $handler) {
- return $handler instanceof FieldMiddleware ? true : $has;
- }, false);
- }
-
- /**
- * Get middleware for field.
- *
- * @param FieldDefinitionNode $field
- *
- * @return \Illuminate\Support\Collection
- */
- public function fieldMiddleware($field)
- {
- return collect($field->directives)->map(function (DirectiveNode $directive) {
- return $this->handler($directive->name->value);
- })->filter(function ($handler) {
- return $handler instanceof FieldMiddleware;
- });
- }
-
- /**
- * Get middleware for field arguments.
- *
- * @param InputValueDefinitionNode $arg
- *
- * @return \Illuminate\Support\Collection
- */
- public function argMiddleware(InputValueDefinitionNode $arg)
- {
- return collect($arg->directives)->map(function (DirectiveNode $directive) {
- return $this->handler($directive->name->value);
- })->filter(function ($handler) {
- return $handler instanceof ArgMiddleware;
- });
- }
-}
diff --git a/src/Schema/Factories/NodeFactory.php b/src/Schema/Factories/NodeFactory.php
index cacdb20ee1..9d6f166fa9 100644
--- a/src/Schema/Factories/NodeFactory.php
+++ b/src/Schema/Factories/NodeFactory.php
@@ -103,7 +103,7 @@ protected function transform(NodeValue $value)
*
* @param NodeValue $value
*
- * @return \GraphQL\Type\Definition\EnumType
+ * @return NodeValue
*/
public function enum(NodeValue $value)
{
@@ -132,7 +132,7 @@ public function enum(NodeValue $value)
*
* @param NodeValue $value
*
- * @return \GraphQL\Type\Definition\ScalarType
+ * @return NodeValue
*/
public function scalar(NodeValue $value)
{
diff --git a/src/Schema/MiddlewareManager.php b/src/Schema/MiddlewareManager.php
index d8feac8885..f06abf632a 100644
--- a/src/Schema/MiddlewareManager.php
+++ b/src/Schema/MiddlewareManager.php
@@ -2,15 +2,12 @@
namespace Nuwave\Lighthouse\Schema;
-use GraphQL\Language\AST\FragmentDefinitionNode;
use GraphQL\Language\AST\OperationDefinitionNode;
use GraphQL\Utils\AST;
-use Nuwave\Lighthouse\Support\Traits\CanParseTypes;
+use Nuwave\Lighthouse\Schema\AST\DocumentAST;
class MiddlewareManager
{
- use CanParseTypes;
-
/**
* Registered query middleware.
*
@@ -34,23 +31,19 @@ class MiddlewareManager
*/
public function forRequest($request)
{
- $definitions = collect($this->parseSchema($request)->definitions);
- $fragments = $definitions->filter(function ($def) {
- return $def instanceof FragmentDefinitionNode;
- });
-
- return collect($this->parseSchema($request)->definitions)
- ->filter(function ($def) {
- return $def instanceof OperationDefinitionNode;
- })->map(function (OperationDefinitionNode $node) use ($fragments) {
+ $document = DocumentAST::fromSource($request);
+ $fragments = $document->fragments();
+
+ return $document->operations()
+ ->map(function (OperationDefinitionNode $node) use ($fragments) {
$definition = AST::toArray($node);
$operation = array_get($definition, 'operation');
$fields = array_map(function ($selection) use ($fragments) {
$field = array_get($selection, 'name.value');
- if ('FragmentSpread' == array_get($selection, 'kind')) {
+ if ('FragmentSpread' === array_get($selection, 'kind')) {
$fragment = $fragments->first(function ($def) use ($field) {
- return data_get($def, 'name.value') == $field;
+ return data_get($def, 'name.value') === $field;
});
return array_pluck(
@@ -73,9 +66,9 @@ public function forRequest($request)
* Register query middleware.
*
* @param string $name
- * @param array $middleware
+ * @param array $middleware
*
- * @return array
+ * @return void
*/
public function registerQuery($name, array $middleware)
{
@@ -86,9 +79,9 @@ public function registerQuery($name, array $middleware)
* Register mutation middleware.
*
* @param string $name
- * @param array $middleware
+ * @param array $middleware
*
- * @return array
+ * @return void
*/
public function registerMutation($name, array $middleware)
{
@@ -148,7 +141,7 @@ public function mutation($name)
* @param OperationDefinitionNode $node
* @param string
*
- * @return array
+ * @return void
*/
protected function byNode(OperationDefinitionNode $node, $operation)
{
diff --git a/src/Schema/NodeContainer.php b/src/Schema/NodeContainer.php
index 6a062ee27d..659163b674 100644
--- a/src/Schema/NodeContainer.php
+++ b/src/Schema/NodeContainer.php
@@ -52,7 +52,7 @@ public function node($type, Closure $resolver, Closure $resolveType)
* @param string $type
* @param string $model
*
- * @return \Illuminate\Database\Eloquent\Model
+ * @return void
*/
public function model($type, $model)
{
diff --git a/src/Schema/Resolvers/AbstractResolver.php b/src/Schema/Resolvers/AbstractResolver.php
index d04a71011a..90c30a2879 100644
--- a/src/Schema/Resolvers/AbstractResolver.php
+++ b/src/Schema/Resolvers/AbstractResolver.php
@@ -59,7 +59,7 @@ protected function hasDirective($node, $name)
{
return collect($node->directives)
->reduce(function ($match, DirectiveNode $directive) use ($name) {
- return $match ?: $directive->name->value == $name ? true : false;
+ return $match ?: $directive->name->value === $name ? true : false;
}, false);
}
@@ -106,7 +106,7 @@ protected function fieldDirective($node, $name, $default = null)
{
return collect($node->directives)
->first(function (DirectiveNode $directive) use ($name) {
- return $directive->name->value == $name;
+ return $directive->name->value === $name;
}, $default);
}
@@ -123,7 +123,7 @@ protected function directiveArgValue(DirectiveNode $node, $key, $default = null)
{
$argument = collect($node->arguments)
->first(function (ArgumentNode $arg) use ($key) {
- return $arg->name->value == $key;
+ return $arg->name->value === $key;
});
return $argument ? $argument->value->value : $default;
diff --git a/src/Schema/Resolvers/EnumResolver.php b/src/Schema/Resolvers/EnumResolver.php
index c6832766b1..7decc1a00c 100644
--- a/src/Schema/Resolvers/EnumResolver.php
+++ b/src/Schema/Resolvers/EnumResolver.php
@@ -23,7 +23,7 @@ class EnumResolver extends AbstractResolver
public function generate()
{
$config = [
- 'name' => $this->getName(),
+ 'name' => $this->getName(),
'values' => $this->getValues(),
];
@@ -74,14 +74,14 @@ protected function getEnumValueKey(EnumValueDefinitionNode $node)
*/
protected function parseEnumNode(EnumValueDefinitionNode $node)
{
- $directive = $this->getDirective($node, "enum");
+ $directive = $this->getDirective($node, 'enum');
if (! $directive) {
return [];
}
return [
- 'value' => $this->directiveArgValue($directive, 'value'),
+ 'value' => $this->directiveArgValue($directive, 'value'),
'description' => $this->safeDescription($node->description),
];
}
diff --git a/src/Schema/Resolvers/FieldTypeResolver.php b/src/Schema/Resolvers/FieldTypeResolver.php
index 7e61f67610..73fcca8bee 100644
--- a/src/Schema/Resolvers/FieldTypeResolver.php
+++ b/src/Schema/Resolvers/FieldTypeResolver.php
@@ -59,12 +59,12 @@ public static function unpack($type)
*/
public function resolveNodeType($node, array $wrappers = [])
{
- if ('NonNullType' == $node->kind) {
+ if ('NonNullType' === $node->kind) {
return $this->resolveNodeType(
$node->type,
array_merge($wrappers, ['NonNullType'])
);
- } elseif ('ListType' == $node->kind) {
+ } elseif ('ListType' === $node->kind) {
return $this->resolveNodeType(
$node->type,
array_merge($wrappers, ['ListType'])
@@ -74,9 +74,9 @@ public function resolveNodeType($node, array $wrappers = [])
return collect($wrappers)
->reverse()
->reduce(function ($type, $kind) {
- if ('NonNullType' == $kind) {
+ if ('NonNullType' === $kind) {
return Type::nonNull($type);
- } elseif ('ListType' == $kind) {
+ } elseif ('ListType' === $kind) {
return Type::listOf($type);
}
@@ -106,8 +106,8 @@ public function unpackNodeType($type, array $wrappers = [])
->reverse()
->reduce(function ($innerType, $wrapper) {
return 'ListOfType' === $wrapper
- ? Type::listOf($innerType)
- : Type::nonNull($innerType);
+ ? Type::listOf($innerType)
+ : Type::nonNull($innerType);
}, $type);
}
diff --git a/src/Schema/Resolvers/NodeResolver.php b/src/Schema/Resolvers/NodeResolver.php
index 1844d5dd98..8bd8cb57ca 100644
--- a/src/Schema/Resolvers/NodeResolver.php
+++ b/src/Schema/Resolvers/NodeResolver.php
@@ -12,7 +12,7 @@ class NodeResolver
*
* @param mixed $node
*
- * @return mixed
+ * @return \GraphQL\Type\Definition\Type
*/
public static function resolve($node)
{
@@ -29,12 +29,12 @@ public static function resolve($node)
*/
public function fromNode($node, array $wrappers = [])
{
- if ('NonNullType' == $node->kind) {
+ if ('NonNullType' === $node->kind) {
return $this->fromNode(
$node->type,
array_merge($wrappers, ['NonNullType'])
);
- } elseif ('ListType' == $node->kind) {
+ } elseif ('ListType' === $node->kind) {
return $this->fromNode(
$node->type,
array_merge($wrappers, ['ListType'])
@@ -44,9 +44,9 @@ public function fromNode($node, array $wrappers = [])
return collect($wrappers)
->reverse()
->reduce(function ($type, $kind) {
- if ('NonNullType' == $kind) {
+ if ('NonNullType' === $kind) {
return Type::nonNull($type);
- } elseif ('ListType' == $kind) {
+ } elseif ('ListType' === $kind) {
return Type::listOf($type);
}
diff --git a/src/Schema/SchemaBuilder.php b/src/Schema/SchemaBuilder.php
index 9621f8a5e4..1946ec65ab 100644
--- a/src/Schema/SchemaBuilder.php
+++ b/src/Schema/SchemaBuilder.php
@@ -3,20 +3,18 @@
namespace Nuwave\Lighthouse\Schema;
use GraphQL\Language\AST\DirectiveDefinitionNode;
-use GraphQL\Language\AST\DocumentNode;
-use GraphQL\Language\AST\Node;
-use GraphQL\Language\AST\TypeExtensionDefinitionNode;
+use GraphQL\Language\AST\TypeDefinitionNode;
use GraphQL\Type\Definition\ObjectType;
+use GraphQL\Type\Definition\Type;
use GraphQL\Type\Schema;
+use GraphQL\Type\SchemaConfig;
+use Illuminate\Support\Collection;
+use Nuwave\Lighthouse\Schema\AST\DocumentAST;
use Nuwave\Lighthouse\Schema\Factories\NodeFactory;
use Nuwave\Lighthouse\Schema\Values\NodeValue;
-use Nuwave\Lighthouse\Support\Traits\CanParseTypes;
-use Nuwave\Lighthouse\Support\Traits\HandlesTypes;
class SchemaBuilder
{
- use CanParseTypes, HandlesTypes;
-
/**
* Definition weights.
*
@@ -29,215 +27,98 @@ class SchemaBuilder
];
/**
- * Collection of schema types.
+ * Build an executable schema from AST.
*
- * @var array
- */
- protected $types = [];
-
- /**
- * Custom (client) directives.
+ * @param DocumentAST $documentAST
*
- * @var array
+ * @return Schema
*/
- protected $directives = [];
-
- /**
- * Generate a GraphQL Schema.
- *
- * @param string $schema
- *
- * @return mixed
- */
- public function build($schema)
+ public function build($documentAST)
{
- $types = $this->register($schema);
- $query = $types->firstWhere('name', 'Query');
- $mutation = $types->firstWhere('name', 'Mutation');
- $subscription = $types->firstWhere('name', 'Subscription');
-
- $types = $types->filter(function ($type) {
- return ! in_array($type->name, ['Query', 'Mutation', 'Subscription']);
- })->toArray();
-
- $directives = $this->directives;
- $typeLoader = function ($name) {
- return $this->instance($name);
- };
-
- return new Schema(compact(
- 'query',
- 'mutation',
- 'subscription',
- 'types',
- 'directives',
- 'typeLoader'
- ));
- }
-
- /**
- * Parse schema definitions.
- *
- * @param string $schema
- *
- * @return \Illuminate\Support\Collection
- */
- public function register($schema)
- {
- $document = $schema instanceof DocumentNode
- ? $schema
- : $this->parseSchema($schema);
-
- $this->setTypes($document);
- $this->extendTypes($document);
- $this->setDirectives($document);
- $this->injectNodeField();
-
- return collect($this->types);
- }
+ $types = $this->convertTypes($documentAST);
+ $this->loadRootOperationFields($types);
+
+ $config = SchemaConfig::create()
+ // Always set Query since it is required
+ ->setQuery($types->firstWhere('name', 'Query'))
+ ->setTypes($types->reject($this->isOperationType())->toArray())
+ ->setDirectives($this->convertDirectives($documentAST)->toArray())
+ ->setTypeLoader(function ($name) {
+ return graphql()->types()->get($name);
+ });
+
+ // Those are optional so only add them if they are present in the schema
+ if ($mutation = $types->firstWhere('name', 'Mutation')) {
+ $config->setMutation($mutation);
+ }
+ if ($subscription = $types->firstWhere('name', 'Subscription')) {
+ $config->setSubscription($subscription);
+ }
- /**
- * Resolve instance by name.
- *
- * @param string $type
- *
- * @return mixed
- */
- public function instance($type)
- {
- return collect($this->types)
- ->first(function ($instance) use ($type) {
- return $instance->name === $type;
- });
+ return new Schema($config);
}
/**
- * Get all registered types.
+ * The fields for the root operations have to be loaded in advance.
*
- * @return array
- */
- public function types()
- {
- return $this->types;
- }
-
- /**
- * Add type to register.
+ * This is because they might have to register middleware.
+ * Other fields can be lazy-loaded to improve performance.
*
- * @param ObjectType|array $type
+ * @param Collection $types
*/
- public function type($type)
+ protected function loadRootOperationFields(Collection $types)
{
- $this->types = is_array($type)
- ? array_merge($this->types, $type)
- : array_merge($this->types, [$type]);
+ $types->filter($this->isOperationType())
+ ->each(function (ObjectType $type) {
+ // This resolves the fields which causes the fields MiddlewareDirective to run
+ // and thus register the (Laravel)-Middleware for the fields.
+ $type->getFields();
+ });
}
/**
- * Serialize AST.
+ * Callback to determine whether a type is one of the three root operation types.
*
- * @return string
+ * @return \Closure
*/
- public function serialize()
+ protected function isOperationType()
{
- $schema = collect($this->types)->map(function ($type) {
- return $this->serializeableType($type);
- })->toArray();
-
- return serialize($schema);
+ return function (Type $type) {
+ return in_array($type->name, ['Query', 'Mutation', 'Subscription']);
+ };
}
/**
- * Unserialize AST.
+ * Convert definitions to types.
*
- * @param string $schema
+ * @param DocumentAST $document
*
- * @return \Illuminate\Support\Collection
+ * @return Collection
*/
- public function unserialize($schema)
+ public function convertTypes(DocumentAST $document)
{
- $this->types = collect(unserialize($schema))->map(function ($type) {
- return $this->unpackType($type);
+ return $document->typeDefinitions()
+ ->sortBy(function (TypeDefinitionNode $typeDefinition) {
+ return array_get($this->weights, get_class($typeDefinition), 9);
+ })->map(function (TypeDefinitionNode $typeDefinition) {
+ return app(NodeFactory::class)->handle(new NodeValue($typeDefinition));
+ })->each(function (Type $type) {
+ // Register in global type registry
+ graphql()->types()->register($type);
});
-
- return collect($this->types);
- }
-
- /**
- * Set schema types.
- *
- * @param DocumentNode $document
- */
- protected function setTypes(DocumentNode $document)
- {
- $types = collect($document->definitions)->reject(function ($node) {
- return $node instanceof TypeExtensionDefinitionNode
- || $node instanceof DirectiveDefinitionNode;
- })->sortBy(function ($node) {
- return array_get($this->weights, get_class($node), 9);
- })->map(function (Node $node) {
- return app(NodeFactory::class)->handle(new NodeValue($node));
- })->toArray();
-
- // NOTE: We don't assign this above because new types may be
- // declared by directives.
- $this->types = array_merge($this->types, $types);
}
/**
* Set custom client directives.
*
- * @param DocumentNode $document
+ * @param DocumentAST $document
*
- * @return array
+ * @return Collection
*/
- protected function setDirectives(DocumentNode $document)
+ protected function convertDirectives(DocumentAST $document)
{
- $this->directives = collect($document->definitions)->filter(function ($node) {
- return $node instanceof DirectiveDefinitionNode;
- })->map(function (Node $node) {
- return app(NodeFactory::class)->handle(new NodeValue($node));
- })->toArray();
- }
-
- /**
- * Extend registered types.
- *
- * @param DocumentNode $document
- */
- protected function extendTypes(DocumentNode $document)
- {
- collect($document->definitions)->filter(function ($def) {
- return $def instanceof TypeExtensionDefinitionNode;
- })->each(function (TypeExtensionDefinitionNode $extension) {
- $name = $extension->definition->name->value;
-
- if ($type = collect($this->types)->firstWhere('name', $name)) {
- $value = new NodeValue($extension);
-
- app(NodeFactory::class)->handle($value->setType($type));
- }
+ return $document->directives()->map(function (DirectiveDefinitionNode $directive) {
+ return app(NodeFactory::class)->handle(new NodeValue($directive));
});
}
-
- /**
- * Inject node field into Query.
- */
- protected function injectNodeField()
- {
- if (is_null(config('lighthouse.global_id_field'))) {
- return;
- }
-
- if (! $query = $this->instance('Query')) {
- return;
- }
-
- $this->extendTypes($this->parseSchema('
- extend type Query {
- node(id: ID!): Node
- @field(resolver: "Nuwave\\\Lighthouse\\\Support\\\Http\\\GraphQL\\\Queries\\\NodeQuery@resolve")
- }
- '));
- }
}
diff --git a/src/Schema/TypeRegistry.php b/src/Schema/TypeRegistry.php
new file mode 100644
index 0000000000..a8758f1da6
--- /dev/null
+++ b/src/Schema/TypeRegistry.php
@@ -0,0 +1,69 @@
+types = collect();
+ }
+
+ /**
+ * Resolve type instance by name.
+ *
+ * @param string $typeName
+ *
+ * @return Type
+ */
+ public function instance($typeName)
+ {
+ return $this->get($typeName);
+ }
+
+ /**
+ * Resolve type instance by name.
+ *
+ * @param string $typeName
+ *
+ * @return Type
+ */
+ public function get($typeName)
+ {
+ return $this->types->get($typeName);
+ }
+
+ /**
+ * Register type with registry.
+ *
+ * @param Type $type
+ * @deprecated in favour of register
+ */
+ public function type(Type $type)
+ {
+ $this->register($type);
+ }
+
+ /**
+ * Register type with registry.
+ *
+ * @param Type $type
+ */
+ public function register(Type $type)
+ {
+ $this->types->put($type->name, $type);
+ }
+}
diff --git a/src/Schema/Types/ConnectionField.php b/src/Schema/Types/ConnectionField.php
index dc9783a845..580ca2d0f7 100644
--- a/src/Schema/Types/ConnectionField.php
+++ b/src/Schema/Types/ConnectionField.php
@@ -1,4 +1,4 @@
-error('The `lighthouse.cache` setting must be set to a file path.');
-
- return;
- }
-
- $schema = graphql()->stitcher()->stitch(
- config('lighthouse.global_id_field', '_id'),
- config('lighthouse.schema.register')
- );
-
- graphql()->cache()->set($schema);
-
- $this->info('GraphQL AST successfully cached.');
- }
-}
diff --git a/src/Support/Contracts/ArgManipulator.php b/src/Support/Contracts/ArgManipulator.php
new file mode 100644
index 0000000000..c795cf883c
--- /dev/null
+++ b/src/Support/Contracts/ArgManipulator.php
@@ -0,0 +1,22 @@
+register('mutation', new \Nuwave\Lighthouse\Schema\Directives\Fields\MutationDirective());
diff --git a/src/Support/DataLoader/BatchLoader.php b/src/Support/DataLoader/BatchLoader.php
index 1ef3e5b930..4907af1628 100644
--- a/src/Support/DataLoader/BatchLoader.php
+++ b/src/Support/DataLoader/BatchLoader.php
@@ -3,13 +3,15 @@
namespace Nuwave\Lighthouse\Support\DataLoader;
use GraphQL\Deferred;
+use GraphQL\Type\Definition\ResolveInfo;
+use Illuminate\Database\Eloquent\Model;
abstract class BatchLoader
{
/**
* Keys to resolve.
*
- * @var \Illuminate\Support\Collection
+ * @var array
*/
protected $keys = [];
@@ -23,13 +25,13 @@ abstract class BatchLoader
/**
* Generate key for field.
*
- * @param \Illuminate\Database\Eloquent\Model $root
- * @param \GraphQL\Type\Definition\ResolveInfo $info
- * @param string $relation
+ * @param Model $root
+ * @param ResolveInfo $info
+ * @param string $relation
*
* @return string
*/
- public static function key($root, $relation, $info = null)
+ public static function key(Model $root, $relation, ResolveInfo $info = null)
{
$path = ! empty(data_get($info, 'path')) ? array_last($info->path) : $relation;
diff --git a/src/Support/DataLoader/Loaders/HasManyLoader.php b/src/Support/DataLoader/Loaders/HasManyLoader.php
index 6a4d50e40c..dcec210f58 100644
--- a/src/Support/DataLoader/Loaders/HasManyLoader.php
+++ b/src/Support/DataLoader/Loaders/HasManyLoader.php
@@ -2,6 +2,7 @@
namespace Nuwave\Lighthouse\Support\DataLoader\Loaders;
+use Nuwave\Lighthouse\Schema\Directives\Fields\PaginationManipulator;
use Nuwave\Lighthouse\Support\Database\QueryFilter;
use Nuwave\Lighthouse\Support\DataLoader\BatchLoader;
use Nuwave\Lighthouse\Support\Traits\HandlesGlobalId;
@@ -36,13 +37,14 @@ public function resolve()
}];
switch ($type) {
- case 'relay':
+ case PaginationManipulator::PAGINATION_TYPE_CONNECTION:
+ case PaginationManipulator::PAGINATION_ALIAS_RELAY:
$first = data_get($args, 'first', 15);
$after = $this->decodeCursor($args);
$currentPage = $first && $after ? floor(($first + $after) / $first) : 1;
$parents->fetchForPage($first, $currentPage, $constraints);
break;
- case 'paginator':
+ case PaginationManipulator::PAGINATION_TYPE_PAGINATOR:
$first = data_get($args, 'count', 15);
$page = data_get($args, 'page', 1);
$parents->fetchForPage($first, $page, $constraints);
diff --git a/src/Support/Exceptions/DirectiveException.php b/src/Support/Exceptions/DirectiveException.php
index 854d8d9948..ef2dc07e00 100644
--- a/src/Support/Exceptions/DirectiveException.php
+++ b/src/Support/Exceptions/DirectiveException.php
@@ -4,4 +4,6 @@
use Exception;
-class DirectiveException extends Exception {}
+class DirectiveException extends Exception
+{
+}
diff --git a/src/Support/Exceptions/ParseException.php b/src/Support/Exceptions/ParseException.php
new file mode 100644
index 0000000000..9edae5203b
--- /dev/null
+++ b/src/Support/Exceptions/ParseException.php
@@ -0,0 +1,9 @@
+validator ? $this->validator->messages() : [];
+ return $this->validator ? $this->validator->messages()->all() : [];
}
}
diff --git a/src/Support/Traits/AttachesNodeInterface.php b/src/Support/Traits/AttachesNodeInterface.php
new file mode 100644
index 0000000000..779194693e
--- /dev/null
+++ b/src/Support/Traits/AttachesNodeInterface.php
@@ -0,0 +1,29 @@
+interfaces = array_merge($objectType->interfaces, [Parser::parseType('Node')]);
+ $globalIdFieldName = config('lighthouse.global_id_field', '_id');
+ $globalIdFieldDefinition = PartialParser::fieldDefinition($globalIdFieldName . ': ID!');
+ $objectType->fields->merge([$globalIdFieldDefinition]);
+
+ return $documentAST->setDefinition($objectType);
+ }
+}
diff --git a/src/Support/Traits/CanParseResolvers.php b/src/Support/Traits/CanParseResolvers.php
index e3c2db8f4b..2d1dc6669c 100644
--- a/src/Support/Traits/CanParseResolvers.php
+++ b/src/Support/Traits/CanParseResolvers.php
@@ -3,10 +3,14 @@
namespace Nuwave\Lighthouse\Support\Traits;
use GraphQL\Language\AST\DirectiveNode;
+use Nuwave\Lighthouse\Schema\Directives\Fields\NamespaceDirective;
use Nuwave\Lighthouse\Schema\Values\FieldValue;
use Nuwave\Lighthouse\Support\Exceptions\DirectiveException;
use Nuwave\Lighthouse\Support\Traits\HandlesDirectives;
+/**
+ * @deprecated This trait will be removed in a future version of Lighthouse.
+ */
trait CanParseResolvers
{
use HandlesDirectives;
@@ -24,8 +28,9 @@ protected function getResolver(FieldValue $value, DirectiveNode $directive, $thr
{
if ($resolver = $this->directiveArgValue($directive, 'resolver')) {
$className = array_get(explode('@', $resolver), '0');
+ $namespace = $this->associatedNamespace($value->getField());
- return $value->getNode()->getNamespace($className);
+ return $namespace ? $namespace . '\\' . $className : $className;
}
return $value->getNode()->getNamespace(
@@ -33,6 +38,27 @@ protected function getResolver(FieldValue $value, DirectiveNode $directive, $thr
);
}
+ /**
+ * Get the namespace for this field, returns an empty string if its not set.
+ *
+ * @param \GraphQL\Language\AST\FieldDefinitionNode $fieldDefinition
+ *
+ * @return string
+ */
+ protected function associatedNamespace($fieldDefinition)
+ {
+ $namespaceDirective = $this->fieldDirective(
+ $fieldDefinition,
+ (new NamespaceDirective)->name()
+ );
+
+ return $namespaceDirective
+ // Look if a namespace for the current field is set, if not default to an empty string
+ ? $this->directiveArgValue($namespaceDirective, $this->name(), '')
+ // Default to an empty namespace if the namespace directive does not exist
+ : '';
+ }
+
/**
* Get class name for resolver.
*
@@ -45,7 +71,7 @@ protected function getResolverClassName(DirectiveNode $directive, $throw = true)
{
$class = $this->directiveArgValue($directive, 'class');
- if (! $class && $throw) {
+ if (!$class && $throw) {
throw new DirectiveException(sprintf(
'Directive [%s] must have a `class` argument.',
$directive->name->value
@@ -72,7 +98,7 @@ protected function getResolverMethod(DirectiveNode $directive)
$method = $this->directiveArgValue($directive, 'method');
- if (! $method) {
+ if (!$method) {
throw new DirectiveException(sprintf(
'Directive [%s] must have a `method` argument.',
$directive->name->value
diff --git a/src/Support/Traits/CanParseTypes.php b/src/Support/Traits/CanParseTypes.php
index 13a8ee14df..d332d52650 100644
--- a/src/Support/Traits/CanParseTypes.php
+++ b/src/Support/Traits/CanParseTypes.php
@@ -3,14 +3,14 @@
namespace Nuwave\Lighthouse\Support\Traits;
use GraphQL\Language\AST\DocumentNode;
-
-
use GraphQL\Language\AST\ObjectTypeDefinitionNode;
-
use GraphQL\Language\Parser;
use Nuwave\Lighthouse\Schema\Factories\NodeFactory;
use Nuwave\Lighthouse\Schema\Values\NodeValue;
+/**
+ * @deprecated this trait will be removed in a future version of Lighthouse
+ */
trait CanParseTypes
{
/**
@@ -60,6 +60,6 @@ protected function objectTypes(DocumentNode $document)
*/
protected function convertNode($node)
{
- return app(NodeFactory::class)->handle(new NodeValue($node));
+ return (new NodeFactory)->handle(new NodeValue($node));
}
}
diff --git a/src/Support/Traits/CreatesPaginators.php b/src/Support/Traits/CreatesPaginators.php
index 9efd1005c6..0c63e9fd20 100644
--- a/src/Support/Traits/CreatesPaginators.php
+++ b/src/Support/Traits/CreatesPaginators.php
@@ -6,7 +6,10 @@
use Nuwave\Lighthouse\Schema\Types\PaginatorField;
use Nuwave\Lighthouse\Schema\Values\FieldValue;
-
+/**
+ * @deprecated Please use the AST to generate/modify the schema.
+ * This will be removed a future release.
+ */
trait CreatesPaginators
{
// TODO: Ugh, get rid of this...
@@ -107,7 +110,7 @@ protected function paginatorTypeName(FieldValue $value)
$parent = $value->getNodeName();
$child = str_singular($value->getField()->name->value);
- return studly_case($parent.'_'.$child.'_Paginator');
+ return studly_case($parent . '_' . $child . '_Paginator');
}
/**
@@ -122,7 +125,7 @@ protected function connectionTypeName(FieldValue $value)
$parent = $value->getNodeName();
$child = str_singular($value->getField()->name->value);
- return studly_case($parent.'_'.$child.'_Connection');
+ return studly_case($parent . '_' . $child . '_Connection');
}
/**
@@ -137,6 +140,6 @@ protected function connectionEdgeName(FieldValue $value)
$parent = $value->getNodeName();
$child = str_singular($value->getField()->name->value);
- return studly_case($parent.'_'.$child.'_Edge');
+ return studly_case($parent . '_' . $child . '_Edge');
}
}
diff --git a/src/Support/Traits/HandleQueries.php b/src/Support/Traits/HandlesQueries.php
similarity index 96%
rename from src/Support/Traits/HandleQueries.php
rename to src/Support/Traits/HandlesQueries.php
index b9ffb196ec..8c327ee8cf 100644
--- a/src/Support/Traits/HandleQueries.php
+++ b/src/Support/Traits/HandlesQueries.php
@@ -1,21 +1,19 @@
config['fields'])) {
+ if (!isset($type->config['fields'])) {
return $type;
}
@@ -118,8 +118,8 @@ protected function serializeableType(Type $type)
if (array_has($config, 'fields')) {
$config['fields'] = collect($config['fields'])->mapWithKeys(function ($field, $key) {
$field['type'] = $field['type'] instanceof Closure
- ? new SerializableClosure($field['type'])
- : $field['type'];
+ ? new SerializableClosure($field['type'])
+ : $field['type'];
if (array_has($field, 'resolve') && $field['resolve'] instanceof Closure) {
$field['resolve'] = new SerializableClosure($field['resolve']);
@@ -154,9 +154,9 @@ protected function unpackFieldType($type, $wrappers = [])
$unpackedType = is_callable($type) ? $type() : $type;
return collect($wrappers)->reduce(function ($innerType, $type) {
- if (ListOfType::class == $type) {
+ if (ListOfType::class === $type) {
return Type::listOf($innerType);
- } elseif (NonNull::class == $type) {
+ } elseif (NonNull::class === $type) {
return Type::nonNull($innerType);
} else {
throw new \Exception("Unknown Type [{$type}]");
diff --git a/src/Support/Traits/IsRelayConnection.php b/src/Support/Traits/IsRelayConnection.php
index 426bf3985e..0464928756 100644
--- a/src/Support/Traits/IsRelayConnection.php
+++ b/src/Support/Traits/IsRelayConnection.php
@@ -2,8 +2,6 @@
namespace Nuwave\Lighthouse\Support\Traits;
-
-
trait IsRelayConnection
{
use HandlesGlobalId;
@@ -12,9 +10,9 @@ trait IsRelayConnection
* Paginate connection w/ query args.
*
* @param \Illuminate\Database\Eloquent\Builder $query
- * @param array $args
+ * @param array $args
*
- * @return \Illuminate\Database\Eloquent\Builder
+ * @return \Illuminate\Contracts\Pagination\LengthAwarePaginator
*/
public function scopeRelayConnection($query, array $args)
{
@@ -30,9 +28,9 @@ public function scopeRelayConnection($query, array $args)
* Paginate connection w/ query args.
*
* @param \Illuminate\Database\Eloquent\Builder $query
- * @param array $args
+ * @param array $args
*
- * @return \Illuminate\Database\Eloquent\Builder
+ * @return \Illuminate\Contracts\Pagination\LengthAwarePaginator
*/
public function scopePaginatorConnection($query, array $args)
{
diff --git a/src/Support/helpers.php b/src/Support/helpers.php
index d6c3be83ee..4cebd7deb1 100644
--- a/src/Support/helpers.php
+++ b/src/Support/helpers.php
@@ -28,11 +28,11 @@ function auth()
/**
* Get instance of schema container.
*
- * @return \Nuwave\Lighthouse\Schema\SchemaBuilder
+ * @return \Nuwave\Lighthouse\Schema\TypeRegistry
*/
function schema()
{
- return graphql()->schema();
+ return graphql()->types();
}
}
@@ -40,7 +40,7 @@ function schema()
/**
* Get instance of directives container.
*
- * @return \Nuwave\Lighthouse\Schema\Factories\DirectiveFactory
+ * @return \Nuwave\Lighthouse\Schema\DirectiveRegistry
*/
function directives()
{
diff --git a/tests/Integration/GraphQLTest.php b/tests/Integration/GraphQLTest.php
index 93c435ecff..69274f50b7 100644
--- a/tests/Integration/GraphQLTest.php
+++ b/tests/Integration/GraphQLTest.php
@@ -104,7 +104,6 @@ public function itCanResolveQuery()
$this->assertEquals($expected, $data);
}
-
/**
* @test
*/
@@ -122,7 +121,7 @@ public function itCanResolveQueryThroughController()
}
';
- $data = $this->postJson("graphql", ['query' => $query])->json();
+ $data = $this->postJson('graphql', ['query' => $query])->json();
$expected = [
'data' => [
@@ -138,13 +137,13 @@ public function itCanResolveQueryThroughController()
$this->assertEquals($expected, $data);
}
- /**
- * @test
- */
- public function itCanResolveQueryThroughControllerViaGetRequest()
- {
- $this->be($this->user);
- $query = '
+ /**
+ * @test
+ */
+ public function itCanResolveQueryThroughControllerViaGetRequest()
+ {
+ $this->be($this->user);
+ $query = '
query UserWithTasks {
user {
email
@@ -155,21 +154,21 @@ public function itCanResolveQueryThroughControllerViaGetRequest()
}
';
- $uri = 'graphql?'.http_build_query(['query' => $query]);
+ $uri = 'graphql?' . http_build_query(['query' => $query]);
- $data = $this->getJson($uri)->json();
+ $data = $this->getJson($uri)->json();
- $expected = [
- 'data' => [
- 'user' => [
- 'email' => $this->user->email,
- 'tasks' => $this->tasks->map(function ($task) {
- return ['name' => $task->name];
- })->toArray(),
- ],
- ],
- ];
+ $expected = [
+ 'data' => [
+ 'user' => [
+ 'email' => $this->user->email,
+ 'tasks' => $this->tasks->map(function ($task) {
+ return ['name' => $task->name];
+ })->toArray(),
+ ],
+ ],
+ ];
- $this->assertEquals($expected, $data);
- }
+ $this->assertEquals($expected, $data);
+ }
}
diff --git a/tests/Integration/Schema/Directives/Args/QueryFilterDirectiveTest.php b/tests/Integration/Schema/Directives/Args/QueryFilterDirectiveTest.php
index 03fe674881..dddb2f313e 100644
--- a/tests/Integration/Schema/Directives/Args/QueryFilterDirectiveTest.php
+++ b/tests/Integration/Schema/Directives/Args/QueryFilterDirectiveTest.php
@@ -3,7 +3,6 @@
namespace Tests\Integration\Schema\Directives\Args;
use Illuminate\Foundation\Testing\RefreshDatabase;
-use Nuwave\Lighthouse\Schema\Directives\Args\EqFilterDirective;
use Tests\DBTestCase;
use Tests\Utils\Models\User;
diff --git a/tests/Integration/Schema/Directives/Fields/HasManyDirectiveTest.php b/tests/Integration/Schema/Directives/Fields/HasManyDirectiveTest.php
index 4e8ad24b6c..fd623baf3f 100644
--- a/tests/Integration/Schema/Directives/Fields/HasManyDirectiveTest.php
+++ b/tests/Integration/Schema/Directives/Fields/HasManyDirectiveTest.php
@@ -3,8 +3,6 @@
namespace Tests\Integration\Schema\Directives\Fields;
use Illuminate\Foundation\Testing\RefreshDatabase;
-use Illuminate\Pagination\LengthAwarePaginator;
-use Nuwave\Lighthouse\Schema\Utils\SchemaStitcher;
use Nuwave\Lighthouse\Support\Exceptions\DirectiveException;
use Tests\DBTestCase;
use Tests\Utils\Models\Task;
@@ -53,6 +51,7 @@ public function itCanQueryHasManyRelationship()
tasks: [Task!]! @hasMany
}
type Task {
+ id: Int
foo: String
}
type Query {
@@ -124,17 +123,15 @@ public function itCanQueryHasManyRelayConnection()
*/
public function itThrowsErrorWithUnknownTypeArg()
{
- $schema = '
+ $this->expectException(DirectiveException::class);
+ $schema = $this->buildSchemaWithDefaultQuery('
type User {
tasks(first: Int! after: Int): [Task!]! @hasMany(type:"foo")
}
type Task {
foo: String
- }
- ';
-
- $this->expectException(DirectiveException::class);
- $type = schema()->register($schema)->first();
+ }');
+ $type = $schema->getType('User');
$type->config['fields']();
}
}
diff --git a/tests/Integration/Schema/Directives/Fields/PaginateDirectiveTest.php b/tests/Integration/Schema/Directives/Fields/PaginateDirectiveTest.php
index 2f713202a7..80a1ab3175 100644
--- a/tests/Integration/Schema/Directives/Fields/PaginateDirectiveTest.php
+++ b/tests/Integration/Schema/Directives/Fields/PaginateDirectiveTest.php
@@ -57,10 +57,10 @@ public function itCanCreateQueryPaginatorsWithDifferentPages()
{
$users = factory(User::class, 10)->create();
$posts = factory(Post::class, 10)->create([
- 'user_id' => $users->first()->id
+ 'user_id' => $users->first()->id,
]);
$comments = factory(Comment::class, 10)->create([
- 'post_id' => $posts->first()->id
+ 'post_id' => $posts->first()->id,
]);
$schema = '
@@ -69,16 +69,16 @@ public function itCanCreateQueryPaginatorsWithDifferentPages()
name: String!
posts: [Post!]! @paginate(type: "paginator" model: "Post")
}
-
+
type Post {
id: ID!
comments: [Comment!]! @paginate(type: "paginator" model: "Comment")
}
-
+
type Comment {
id: ID!
}
-
+
type Query {
users: [User!]! @paginate(type: "paginator" model: "User")
}
diff --git a/tests/Integration/Schema/NodeTest.php b/tests/Integration/Schema/NodeTest.php
index 29fddca4b8..83fcfa53a7 100644
--- a/tests/Integration/Schema/NodeTest.php
+++ b/tests/Integration/Schema/NodeTest.php
@@ -18,7 +18,7 @@ class NodeTest extends DBTestCase
public function itCanResolveNodes()
{
$schema = '
- type User @node(
+ type User implements Node @node(
resolver: "Tests\\\Integration\\\Schema\\\NodeTest@resolveNode"
typeResolver: "Tests\\\Integration\\\Schema\\\NodeTest@resolveNodeType"
) {
@@ -29,7 +29,7 @@ public function itCanResolveNodes()
';
$globalId = $this->encodeGlobalId('User', $this->node['id']);
- $result = $this->execute($schema, '{ node(id: "'.$globalId.'") { name } }', true);
+ $result = $this->execute($schema, '{ node(id: "'.$globalId.'") { ...on User { name } } }', true);
$this->assertEquals($this->node['name'], array_get($result->data, 'node.name'));
}
@@ -43,14 +43,14 @@ public function itCanResolveModelsNodes()
$globalId = $this->encodeGlobalId('User', $user->getKey());
$schema = '
- type User @model {
+ type User implements Node @model {
_id: ID!
name: String!
}
type Query {}
';
- $result = $this->execute($schema, '{ node(id: "'.$globalId.'") { name } }', true);
+ $result = $this->execute($schema, '{ node(id: "'.$globalId.'") { ...on User { name } } }', true);
$this->assertEquals($user->name, array_get($result->data, 'node.name'));
}
diff --git a/tests/TestCase.php b/tests/TestCase.php
index 52f785cf00..3903316b50 100644
--- a/tests/TestCase.php
+++ b/tests/TestCase.php
@@ -2,12 +2,11 @@
namespace Tests;
-use GraphQL\Executor\Executor;
+use GraphQL\GraphQL;
use GraphQL\Language\Parser;
use Laravel\Scout\ScoutServiceProvider;
-use Nuwave\Lighthouse\Schema\Values\ArgumentValue;
-use Nuwave\Lighthouse\Schema\Values\FieldValue;
-use Nuwave\Lighthouse\Schema\Values\NodeValue;
+use Nuwave\Lighthouse\Schema\AST\ASTBuilder;
+use Nuwave\Lighthouse\Schema\SchemaBuilder;
use Orchestra\Testbench\TestCase as BaseTestCase;
class TestCase extends BaseTestCase
@@ -102,30 +101,35 @@ protected function parseSchema($schema = 'schema.graphql')
*
* @return \GraphQL\Language\AST\DocumentNode
*/
- protected function parse(string $schema)
+ protected function parse($schema)
{
return Parser::parse($schema);
}
-
+
/**
* Execute query/mutation.
*
* @param string $schema
* @param string $query
- * @param string $lighthouse
- * @param array $variables
+ * @param bool $lighthouse
+ * @param array $variables
*
* @return \GraphQL\Executor\ExecutionResult
*/
protected function execute($schema, $query, $lighthouse = false, $variables = [])
{
if ($lighthouse) {
- $node = file_get_contents(realpath(__DIR__.'/../assets/node.graphql'));
- $lighthouse = file_get_contents(realpath(__DIR__.'/../assets/schema.graphql'));
- $schema = $node."\n".$lighthouse."\n".$schema;
+ $addDefaultSchema = file_get_contents(realpath(__DIR__.'/../assets/schema.graphql'));
+ $schema = $addDefaultSchema."\n".$schema;
}
- return Executor::execute(schema()->build($schema), $this->parse($query));
+ return GraphQL::executeQuery(
+ $this->buildSchemaFromString($schema),
+ $query,
+ null,
+ null,
+ $variables
+ );
}
/**
@@ -150,45 +154,29 @@ protected function store($fileName, $contents)
}
/**
- * Get a node's field.
- *
- * @param string $schema
- * @param int $index
- * @param string|null $name
+ * @param string $schema
*
- * @return FieldValue
+ * @return \GraphQL\Type\Schema
*/
- protected function getNodeField($schema, $index = 0, $field = null)
+ protected function buildSchemaFromString($schema)
{
- $document = $this->parse($schema);
- $node = new NodeValue($document->definitions[$index]);
-
- if (is_null($field)) {
- return new FieldValue($node, array_get($node->getNodeFields(), '0'));
- }
-
- return collect($node->getNodeFields())->filter(function ($nodeField) use ($field) {
- return $nodeField->name->value === $field;
- })->map(function ($field) use ($node) {
- return new FieldValue($node, $field);
- })->first();
+ return (new SchemaBuilder())->build(ASTBuilder::generate($schema));
}
/**
- * Get field argument value.
+ * Convenience method to add a default Query, sometimes needed
+ * because the Schema is invalid without it.
*
- * @param string $name
- * @param FieldValue $field
+ * @param string $schema
*
- * @return ArgumentValue
+ * @return \GraphQL\Type\Schema
*/
- protected function getFieldArg($name, FieldValue $field)
+ protected function buildSchemaWithDefaultQuery($schema)
{
- return collect(data_get($field->getField(), 'arguments', []))
- ->filter(function ($arg) use ($name) {
- return $arg->name->value === $name;
- })->map(function ($arg) use ($field) {
- return new ArgumentValue($field, $arg);
- })->first();
+ return $this->buildSchemaFromString($schema.'
+ type Query {
+ dummy: String
+ }
+ ');
}
}
diff --git a/tests/Unit/Schema/AST/ASTBuilderTest.php b/tests/Unit/Schema/AST/ASTBuilderTest.php
new file mode 100644
index 0000000000..79ecff65bc
--- /dev/null
+++ b/tests/Unit/Schema/AST/ASTBuilderTest.php
@@ -0,0 +1,28 @@
+assertCount(3, $ast->objectType('Query')->fields);
+ }
+}
diff --git a/tests/Unit/Schema/AST/PartialParserTest.php b/tests/Unit/Schema/AST/PartialParserTest.php
new file mode 100644
index 0000000000..77bee563ce
--- /dev/null
+++ b/tests/Unit/Schema/AST/PartialParserTest.php
@@ -0,0 +1,114 @@
+assertInstanceOf(
+ ObjectTypeDefinitionNode::class,
+ PartialParser::objectTypeDefinition('
+ type Foo {
+ foo: String
+ }
+ ')
+ );
+ }
+
+ public function testThrowsForInvalidDefinition()
+ {
+ $this->expectException(SyntaxError::class);
+ PartialParser::objectTypeDefinition('
+ INVALID
+ ');
+ }
+
+ public function testThrowsIfMultipleDefinitionsAreGiven()
+ {
+ $this->expectException(ParseException::class);
+ PartialParser::objectTypeDefinition('
+ type Foo {
+ foo: String
+ }
+
+ type Bar {
+ bar: Int
+ }
+ ');
+ }
+
+ public function testThrowsIfDefinitionIsUnexpectedType()
+ {
+ $this->expectException(ParseException::class);
+ PartialParser::objectTypeDefinition('
+ interface Foo {
+ foo: String
+ }
+ ');
+ }
+
+ public function testParsesObjectTypesArray()
+ {
+ $objectTypes = PartialParser::objectTypeDefinitions(['
+ type Foo {
+ foo: String
+ }
+ ', '
+ type Bar {
+ bar: Int
+ }
+ ']);
+
+ $this->assertCount(2, $objectTypes);
+ $this->assertInstanceOf(ObjectTypeDefinitionNode::class, $objectTypes[0]);
+ $this->assertInstanceOf(ObjectTypeDefinitionNode::class, $objectTypes[1]);
+ }
+
+ public function testThrowsOnInvalidTypeInObjectTypesArray()
+ {
+ $this->expectException(ParseException::class);
+ PartialParser::objectTypeDefinitions(['
+ type Foo {
+ foo: String
+ }
+ ', '
+ interface Bar {
+ bar: Int
+ }
+ ']);
+ }
+
+ public function testThrowsOnMultipleDefinitionsInArrayItem()
+ {
+ $this->expectException(ParseException::class);
+ PartialParser::objectTypeDefinitions(['
+ type Foo {
+ foo: String
+ }
+
+ type Bar {
+ bar: Int
+ }
+ ']);
+ }
+
+ public function testParseOperationDefinition()
+ {
+ $this->assertInstanceOf(
+ OperationDefinitionNode::class,
+ PartialParser::operationDefinition('
+ {
+ foo: Foo
+ }
+ ')
+ );
+ }
+}
diff --git a/tests/Unit/Schema/AST/SchemaStitcherTest.php b/tests/Unit/Schema/AST/SchemaStitcherTest.php
new file mode 100644
index 0000000000..a3906086bb
--- /dev/null
+++ b/tests/Unit/Schema/AST/SchemaStitcherTest.php
@@ -0,0 +1,73 @@
+assertContains('type Baz', $schema);
+ }
+
+ /**
+ * @test
+ */
+ public function itCanImportSchemas()
+ {
+ $schema = SchemaStitcher::stitch(__DIR__.'/foo.graphql');
+ $this->assertContains('type Foo', $schema);
+ $this->assertContains('type Bar', $schema);
+ $this->assertContains('type Baz', $schema);
+ }
+}
diff --git a/tests/Unit/Schema/DirectiveFactoryTest.php b/tests/Unit/Schema/DirectiveFactoryTest.php
index 6fcd293157..ae56cb5e3b 100644
--- a/tests/Unit/Schema/DirectiveFactoryTest.php
+++ b/tests/Unit/Schema/DirectiveFactoryTest.php
@@ -4,6 +4,7 @@
use GraphQL\Language\Parser;
use GraphQL\Type\Definition\ScalarType;
+use Nuwave\Lighthouse\Schema\AST\PartialParser;
use Nuwave\Lighthouse\Schema\Directives\Nodes\ScalarDirective;
use Nuwave\Lighthouse\Schema\Values\NodeValue;
use Nuwave\Lighthouse\Support\Exceptions\DirectiveException;
@@ -51,14 +52,12 @@ public function itThrowsErrorIfMultipleDirectivesAssignedToNode()
*/
public function itCanCheckIfFieldHasAResolverDirective()
{
- $schema = '
+ $type = PartialParser::objectTypeDefinition('
type Foo {
bar: [Bar!]! @hasMany
- }
- ';
+ }');
- $document = Parser::parse($schema);
- $hasResolver = directives()->hasResolver($document->definitions[0]->fields[0]);
+ $hasResolver = directives()->hasResolver($type->fields[0]);
$this->assertTrue($hasResolver);
}
diff --git a/tests/Unit/Schema/Directives/Client/ClientDirectiveTest.php b/tests/Unit/Schema/Directives/Client/ClientDirectiveTest.php
index 9521ff53bc..dcdf6101ad 100644
--- a/tests/Unit/Schema/Directives/Client/ClientDirectiveTest.php
+++ b/tests/Unit/Schema/Directives/Client/ClientDirectiveTest.php
@@ -3,8 +3,6 @@
namespace Tests\Unit\Schema\Directives\Client;
use GraphQL\Type\Definition\ResolveInfo;
-use GraphQL\Utils\BuildSchema;
-use Nuwave\Lighthouse\Schema\Resolvers\DirectiveResolver;
use Tests\TestCase;
class ClientDirectiveTest extends TestCase
diff --git a/tests/Unit/Schema/Directives/Fields/AuthDirectiveTest.php b/tests/Unit/Schema/Directives/Fields/AuthDirectiveTest.php
index cdadaf62bf..3e0d99af83 100644
--- a/tests/Unit/Schema/Directives/Fields/AuthDirectiveTest.php
+++ b/tests/Unit/Schema/Directives/Fields/AuthDirectiveTest.php
@@ -17,16 +17,16 @@ public function itCanResolveAuthenticatedUser()
};
$this->be($user);
- $schema = '
+
+ $schema = $this->buildSchemaFromString('
type User {
foo: String!
}
type Query {
user: User! @auth
- }
- ';
+ }');
- $query = schema()->register($schema)->firstWhere('name', 'Query');
+ $query = $schema->getType('Query');
$resolver = array_get($query->config['fields'](), 'user.resolve');
$this->assertEquals('bar', data_get($resolver(), 'foo'));
}
diff --git a/tests/Unit/Schema/Directives/Fields/CanDirectiveTest.php b/tests/Unit/Schema/Directives/Fields/CanDirectiveTest.php
index 3926858b43..265c09b05e 100644
--- a/tests/Unit/Schema/Directives/Fields/CanDirectiveTest.php
+++ b/tests/Unit/Schema/Directives/Fields/CanDirectiveTest.php
@@ -11,13 +11,11 @@ class CanDirectiveTest extends TestCase
*/
public function itCanAttachPoliciesToField()
{
- $schema = '
+ $schema = $this->buildSchemaWithDefaultQuery('
type Foo {
bar: String! @can(if: ["viewBar"])
- }
- ';
-
- $type = schema()->register($schema)->first();
+ }');
+ $type = $schema->getType('Foo');
$fields = $type->config['fields'];
$resolver = array_get($fields, 'bar.resolve');
// TODO: Use prophecy to ensure middleware is called
diff --git a/tests/Unit/Schema/Directives/Fields/ComplexityDirectiveTest.php b/tests/Unit/Schema/Directives/Fields/ComplexityDirectiveTest.php
index 5a234a338a..fd6a51f74f 100644
--- a/tests/Unit/Schema/Directives/Fields/ComplexityDirectiveTest.php
+++ b/tests/Unit/Schema/Directives/Fields/ComplexityDirectiveTest.php
@@ -11,16 +11,15 @@ class ComplexityDirectiveTest extends TestCase
*/
public function itCanSetDefaultComplexityOnField()
{
- $schema = '
+ $schema = $this->buildSchemaWithDefaultQuery('
type User {
posts: [Post!]! @complexity @hasMany
}
type Post {
title: String
- }
- ';
+ }');
- $type = schema()->register($schema)->first();
+ $type = $schema->getType('User');
$fields = $type->config['fields']();
$complexity = $fields['posts']['complexity'];
@@ -34,7 +33,8 @@ public function itCanSetDefaultComplexityOnField()
public function itCanSetCustomComplexityResolver()
{
$resolver = addslashes(self::class);
- $schema = '
+
+ $schema = $this->buildSchemaWithDefaultQuery('
type User {
posts: [Post!]!
@complexity(resolver: "'.$resolver.'@complexity")
@@ -42,10 +42,8 @@ public function itCanSetCustomComplexityResolver()
}
type Post {
title: String
- }
- ';
-
- $type = schema()->register($schema)->first();
+ }');
+ $type = $schema->getType('User');
$fields = $type->config['fields']();
$complexity = $fields['posts']['complexity'];
$this->assertEquals(100, $complexity(10, ['foo' => 10]));
diff --git a/tests/Unit/Schema/Directives/Fields/FieldDirectiveTest.php b/tests/Unit/Schema/Directives/Fields/FieldDirectiveTest.php
index 9b1bfb5507..52db40a16e 100644
--- a/tests/Unit/Schema/Directives/Fields/FieldDirectiveTest.php
+++ b/tests/Unit/Schema/Directives/Fields/FieldDirectiveTest.php
@@ -12,13 +12,12 @@ class FieldDirectiveTest extends TestCase
*/
public function itCanResolveFieldWithAssignedClass()
{
- $schema = '
+ $schema = $this->buildSchemaWithDefaultQuery('
type Foo {
bar: String! @field(class:"Tests\\\Utils\\\Resolvers\\\Foo" method: "bar")
- }
- ';
+ }');
- $type = schema()->register($schema)->first();
+ $type = $schema->getType('Foo');
$fields = $type->config['fields']();
$resolve = array_get($fields, 'bar.resolve');
@@ -30,13 +29,13 @@ public function itCanResolveFieldWithAssignedClass()
*/
public function itCanResolveFieldWithMergedArgs()
{
- $schema = '
+ $schema = $this->buildSchemaWithDefaultQuery('
type Foo {
bar: String! @field(class:"Tests\\\Utils\\\Resolvers\\\Foo" method: "baz" args:["foo.baz"])
}
- ';
+ ');
- $type = schema()->register($schema)->first();
+ $type = $schema->getType('Foo');
$fields = $type->config['fields']();
$resolve = array_get($fields, 'bar.resolve');
@@ -48,14 +47,14 @@ public function itCanResolveFieldWithMergedArgs()
*/
public function itThrowsAnErrorIfNoClassIsDefined()
{
- $schema = '
+ $schema = $this->buildSchemaWithDefaultQuery('
type Foo {
bar: String! @field(method: "bar")
}
- ';
+ ');
$this->expectException(DirectiveException::class);
- $type = schema()->register($schema)->first();
+ $type = $schema->getType('Foo');
$type->config['fields']();
}
@@ -64,14 +63,14 @@ public function itThrowsAnErrorIfNoClassIsDefined()
*/
public function itThrowsAnErrorIfNoMethodIsDefined()
{
- $schema = '
+ $schema = $this->buildSchemaWithDefaultQuery('
type Foo {
bar: String! @field(class: "Foo\\\Bar")
}
- ';
+ ');
$this->expectException(DirectiveException::class);
- $type = schema()->register($schema)->first();
+ $type = $schema->getType('Foo');
$type->config['fields']();
}
}
diff --git a/tests/Unit/Schema/Directives/Fields/MethodDirectiveTest.php b/tests/Unit/Schema/Directives/Fields/MethodDirectiveTest.php
index 0d4d1dc04e..52f79d2ec2 100644
--- a/tests/Unit/Schema/Directives/Fields/MethodDirectiveTest.php
+++ b/tests/Unit/Schema/Directives/Fields/MethodDirectiveTest.php
@@ -18,13 +18,13 @@ public function foobar()
}
};
- $schema = '
+ $schema = $this->buildSchemaWithDefaultQuery('
type Foo {
bar: String! @method(name: "foobar")
}
- ';
+ ');
- $type = schema()->register($schema)->first();
+ $type = $schema->getType('Foo');
$fields = $type->config['fields']();
$resolver = array_get($fields, 'bar.resolve');
$this->assertEquals('baz', $resolver($root, []));
@@ -42,13 +42,13 @@ public function bar(array $args)
}
};
- $schema = '
+ $schema = $this->buildSchemaWithDefaultQuery('
type Foo {
bar(baz: String!): String! @method(name: "bar")
}
- ';
+ ');
- $type = schema()->register($schema)->first();
+ $type = $schema->getType('Foo');
$fields = $type->config['fields']();
$resolver = array_get($fields, 'bar.resolve');
$this->assertEquals('foo', $resolver($root, ['baz' => 'foo']));
diff --git a/tests/Unit/Schema/Directives/Fields/MiddlewareDirectiveTest.php b/tests/Unit/Schema/Directives/Fields/MiddlewareDirectiveTest.php
index 7cb00ffbbd..2f3b69c1a0 100644
--- a/tests/Unit/Schema/Directives/Fields/MiddlewareDirectiveTest.php
+++ b/tests/Unit/Schema/Directives/Fields/MiddlewareDirectiveTest.php
@@ -11,7 +11,7 @@ class MiddlewareDirectiveTest extends TestCase
*/
public function itCanRegisterMiddleware()
{
- schema()->register('
+ $schema = $this->buildSchemaFromString('
type Query {
foo: String! @middleware(checks: ["auth:web", "auth:admin"])
bar: String!
@@ -22,10 +22,6 @@ public function itCanRegisterMiddleware()
}
');
- collect(schema()->types())->each(function ($type) {
- $type->config['fields']();
- });
-
$query = 'query FooQuery { foo }';
$middleware = graphql()->middleware()->forRequest($query);
$this->assertCount(2, $middleware);
@@ -43,7 +39,7 @@ public function itCanRegisterMiddleware()
*/
public function itCanRegisterMiddlewareWithFragments()
{
- schema()->register('
+ $schema = $this->buildSchemaFromString('
type Query {
foo: String! @middleware(checks: ["auth:web", "auth:admin"])
bar: String!
@@ -54,10 +50,6 @@ public function itCanRegisterMiddlewareWithFragments()
}
');
- collect(schema()->types())->each(function ($type) {
- $type->config['fields']();
- });
-
$query = 'query FooQuery { ...Foo_Fragment } fragment Foo_Fragment on Query { foo }';
$middleware = graphql()->middleware()->forRequest($query);
$this->assertCount(2, $middleware);
diff --git a/tests/Unit/Schema/Directives/Fields/PaginateDirectiveTest.php b/tests/Unit/Schema/Directives/Fields/PaginateDirectiveTest.php
new file mode 100644
index 0000000000..af1ea8537f
--- /dev/null
+++ b/tests/Unit/Schema/Directives/Fields/PaginateDirectiveTest.php
@@ -0,0 +1,32 @@
+getConnectionQueryField('connection');
+ $relay = $this->getConnectionQueryField('relay');
+
+ $this->assertEquals($connection, $relay);
+ }
+
+ protected function getConnectionQueryField($type)
+ {
+ return $this->buildSchemaFromString("
+ type Users {
+ name: String
+ }
+
+ type Query {
+ users: [User!]! @paginate(type: \"$type\" model: \"User\")
+ }
+ ")->getQueryType()->getField('users');
+ }
+}
diff --git a/tests/Unit/Schema/Directives/Fields/RenameDirectiveTest.php b/tests/Unit/Schema/Directives/Fields/RenameDirectiveTest.php
index 0a67b9bf30..42d2577102 100644
--- a/tests/Unit/Schema/Directives/Fields/RenameDirectiveTest.php
+++ b/tests/Unit/Schema/Directives/Fields/RenameDirectiveTest.php
@@ -12,13 +12,11 @@ class RenameDirectiveTest extends TestCase
*/
public function itCanRenameAField()
{
- $schema = '
+ $schema = $this->buildSchemaWithDefaultQuery('
type Foo {
fooBar: String! @rename(attribute: "foo_bar")
- }
- ';
-
- $type = schema()->register($schema)->first();
+ }');
+ $type = $schema->getType('Foo');
$fields = $type->config['fields']();
$resolver = array_get($fields, 'fooBar.resolve');
$this->assertEquals('bar', $resolver(['foo_bar' => 'bar', 'fooBar' => 'baz'], []));
@@ -29,14 +27,13 @@ public function itCanRenameAField()
*/
public function itThrowsAnExceptionIfNoAttributeDefined()
{
- $schema = '
+ $this->expectException(DirectiveException::class);
+ $schema = $this->buildSchemaWithDefaultQuery('
type Foo {
fooBar: String! @rename
- }
- ';
+ }');
- $this->expectException(DirectiveException::class);
- $type = schema()->register($schema)->first();
+ $type = $schema->getType('Foo');
$type->config['fields']();
}
}
diff --git a/tests/Unit/Schema/Directives/Nodes/GroupDirectiveTest.php b/tests/Unit/Schema/Directives/Nodes/GroupDirectiveTest.php
index 83f7b695c3..324a63170e 100644
--- a/tests/Unit/Schema/Directives/Nodes/GroupDirectiveTest.php
+++ b/tests/Unit/Schema/Directives/Nodes/GroupDirectiveTest.php
@@ -8,6 +8,7 @@ class GroupDirectiveTest extends TestCase
{
/**
* @test
+ * @group fixing
*/
public function itCanSetNamespaces()
{
@@ -15,8 +16,7 @@ public function itCanSetNamespaces()
type Query {}
extend type Query @group(namespace: "Tests\\\Utils\\\Resolvers") {
me: String @field(resolver: "Foo@bar")
- }
- ';
+ }';
$result = $this->execute($schema, '{ me }');
$this->assertEquals('foo.bar', $result->data['me']);
diff --git a/tests/Unit/Schema/Directives/Nodes/SecurityDirectiveTest.php b/tests/Unit/Schema/Directives/Nodes/SecurityDirectiveTest.php
index 9634f1cace..41da928f96 100644
--- a/tests/Unit/Schema/Directives/Nodes/SecurityDirectiveTest.php
+++ b/tests/Unit/Schema/Directives/Nodes/SecurityDirectiveTest.php
@@ -3,7 +3,6 @@
namespace Tests\Unit\Schema\Directives\Nodes;
use GraphQL\Validator\DocumentValidator;
-use GraphQL\Validator\Rules\DisableIntrospection;
use GraphQL\Validator\Rules\QueryComplexity;
use GraphQL\Validator\Rules\QueryDepth;
use Tests\TestCase;
@@ -15,14 +14,13 @@ class SecurityDirectiveTest extends TestCase
*/
public function itCanSetMaxDepth()
{
- $schema = '
- type Query @security(depth: 3) {
+ $schema = $this->buildSchemaFromString('
+ type Query @security(depth: 20) {
me: String
- }';
+ }');
- schema()->register($schema);
$rule = DocumentValidator::getRule(QueryDepth::class);
- $this->assertEquals(3, $rule->getMaxQueryDepth());
+ $this->assertEquals(20, $rule->getMaxQueryDepth());
}
/**
@@ -30,13 +28,12 @@ public function itCanSetMaxDepth()
*/
public function itCanSetMaxComplexity()
{
- $schema = '
- type Query @security(complexity: 3) {
+ $schema = $this->buildSchemaFromString('
+ type Query @security(complexity: 20) {
me: String
- }';
+ }');
- schema()->register($schema);
$rule = DocumentValidator::getRule(QueryComplexity::class);
- $this->assertEquals(3, $rule->getMaxQueryComplexity());
+ $this->assertEquals(20, $rule->getMaxQueryComplexity());
}
}
diff --git a/tests/Unit/Schema/SchemaBuilderTest.php b/tests/Unit/Schema/SchemaBuilderTest.php
index 502e3e03ef..f0deda65f1 100644
--- a/tests/Unit/Schema/SchemaBuilderTest.php
+++ b/tests/Unit/Schema/SchemaBuilderTest.php
@@ -37,7 +37,7 @@ public function getEnvironmentSetUp($app)
*/
public function itCanResolveEnumTypes()
{
- $schema = '
+ $schema = $this->buildSchemaWithDefaultQuery('
enum Role {
# Company administrator.
admin @enum(value:"admin")
@@ -45,11 +45,9 @@ enum Role {
# Company employee.
employee @enum(value:"employee")
}
- ';
+ ');
- $types = schema()->register($schema);
-
- $this->assertInstanceOf(EnumType::class, $types->firstWhere('name', 'Role'));
+ $this->assertInstanceOf(EnumType::class, $schema->getType('Role'));
}
/**
@@ -57,15 +55,14 @@ enum Role {
*/
public function itCanResolveInterfaceTypes()
{
- $schema = '
+ $schema = $this->buildSchemaWithDefaultQuery('
interface Foo {
# bar is baz
bar: String!
}
- ';
+ ');
- $types = schema()->register($schema);
- $this->assertInstanceOf(InterfaceType::class, $types->firstWhere('name', 'Foo'));
+ $this->assertInstanceOf(InterfaceType::class, $schema->getType('Foo'));
}
/**
@@ -73,13 +70,16 @@ interface Foo {
*/
public function itCanResolveScalarTypes()
{
- $schema = '
+ $this->app['config']->set(
+ 'lighthouse.namespaces.scalars',
+ 'Nuwave\Lighthouse\Schema\Types\Scalars'
+ );
+
+ $schema = $this->buildSchemaWithDefaultQuery('
scalar DateTime @scalar(class:"DateTime")
- ';
+ ');
- $this->app['config']->set('lighthouse.namespaces.scalars', 'Nuwave\Lighthouse\Schema\Types\Scalars');
- $types = schema()->register($schema);
- $this->assertInstanceOf(ScalarType::class, $types->firstWhere('name', 'DateTime'));
+ $this->assertInstanceOf(ScalarType::class, $schema->getType('DateTime'));
}
/**
@@ -87,17 +87,17 @@ public function itCanResolveScalarTypes()
*/
public function itCanResolveObjectTypes()
{
- $schema = '
+ $schema = $this->buildSchemaWithDefaultQuery('
type Foo {
# bar attribute of Foo
bar: String!
}
- ';
+ ');
- $types = schema()->register($schema);
- $this->assertInstanceOf(ObjectType::class, $types->firstWhere('name', 'Foo'));
+ $foo = $schema->getType('Foo');
+ $this->assertInstanceOf(ObjectType::class, $foo);
- $config = $types->firstWhere('name', 'Foo')->config;
+ $config = $foo->config;
$this->assertEquals('Foo', data_get($config, 'name'));
$this->assertEquals('bar attribute of Foo', array_get($config['fields'](), 'bar.description'));
}
@@ -107,17 +107,17 @@ public function itCanResolveObjectTypes()
*/
public function itCanResolveInputObjectTypes()
{
- $schema = '
+ $schema = $this->buildSchemaWithDefaultQuery('
input CreateFoo {
foo: String!
bar: Int
}
- ';
+ ');
- $types = schema()->register($schema);
- $this->assertInstanceOf(InputType::class, $types->firstWhere('name', 'CreateFoo'));
+ $createFoo = $schema->getType('CreateFoo');
+ $this->assertInstanceOf(InputType::class, $createFoo);
- $config = $types->firstWhere('name', 'CreateFoo')->config;
+ $config = $createFoo->config;
$fields = $config['fields']();
$this->assertEquals('CreateFoo', data_get($config, 'name'));
$this->assertArrayHasKey('foo', $fields);
@@ -129,13 +129,13 @@ public function itCanResolveInputObjectTypes()
*/
public function itCanResolveMutations()
{
- $schema = '
+ $schema = $this->buildSchemaWithDefaultQuery('
type Mutation {
foo(bar: String! baz: String): String
}
- ';
+ ');
- $type = schema()->register($schema)->firstWhere('name', 'Mutation');
+ $type = $schema->getType('Mutation');
$mutation = $type->config['fields']()['foo'];
$this->assertArrayHasKey('args', $mutation);
@@ -150,13 +150,13 @@ public function itCanResolveMutations()
*/
public function itCanResolveQueries()
{
- $schema = '
+ $schema = $this->buildSchemaFromString('
type Query {
foo(bar: String! baz: String): String
}
- ';
+ ');
- $type = schema()->register($schema)->firstWhere('name', 'Query');
+ $type = $schema->getType('Query');
$query = $type->config['fields']()['foo'];
$this->assertArrayHasKey('args', $query);
@@ -171,16 +171,16 @@ public function itCanResolveQueries()
*/
public function itCanExtendObjectTypes()
{
- $schema = '
+ $schema = $this->buildSchemaWithDefaultQuery('
type Foo {
bar: String!
}
extend type Foo {
baz: String!
}
- ';
+ ');
- $type = schema()->register($schema)->first();
+ $type = $schema->getType('Foo');
$fields = $type->config['fields']();
$this->assertArrayHasKey('baz', $fields);
}
@@ -190,16 +190,16 @@ public function itCanExtendObjectTypes()
*/
public function itCanExtendQuery()
{
- $schema = '
+ $schema = $this->buildSchemaFromString('
type Query {
foo: String!
}
extend type Query {
bar: String!
}
- ';
+ ');
- $type = schema()->register($schema)->firstWhere('name', 'Query');
+ $type = $schema->getType('Query');
$fields = $type->config['fields']();
$this->assertArrayHasKey('bar', $fields);
}
@@ -209,16 +209,16 @@ public function itCanExtendQuery()
*/
public function itCanExtendMutation()
{
- $schema = '
+ $schema = $this->buildSchemaWithDefaultQuery('
type Mutation {
foo: String!
}
extend type Mutation {
bar: String!
}
- ';
+ ');
- $type = schema()->register($schema)->firstWhere('name', 'Mutation');
+ $type = $schema->getType('Mutation');
$fields = $type->config['fields']();
$this->assertArrayHasKey('bar', $fields);
}
@@ -228,15 +228,18 @@ public function itCanExtendMutation()
*/
public function itCanGenerateGraphQLSchema()
{
- $schema = '
- type Query {
- foo: String!
- }
- type Mutation {
- foo: String!
- }
- ';
-
- $this->assertInstanceOf(Schema::class, schema()->build($schema));
+ $schema = $this->buildSchemaFromString('
+ type Query {
+ foo: String!
+ }
+
+ type Mutation {
+ foo: String!
+ }
+ ');
+
+ $this->assertInstanceOf(Schema::class, $schema);
+ // This would throw if the schema were invalid
+ $schema->assertValid();
}
}
diff --git a/tests/Unit/Schema/Utils/SchemaStitcherTest.php b/tests/Unit/Schema/Utils/SchemaStitcherTest.php
deleted file mode 100644
index 22a9cb580e..0000000000
--- a/tests/Unit/Schema/Utils/SchemaStitcherTest.php
+++ /dev/null
@@ -1,100 +0,0 @@
-stitcher = new SchemaStitcher();
-
- if (! is_dir(__DIR__.'/schema')) {
- mkdir(__DIR__.'/schema');
- }
-
- file_put_contents(__DIR__.'/foo.graphql', '
- #import ./schema/bar.graphql
- type Foo {
- foo: String!
- }');
-
- file_put_contents(__DIR__.'/schema/bar.graphql', '
- #import ./baz.graphql
- type Bar {
- bar: String!
- }');
-
- file_put_contents(__DIR__.'/schema/baz.graphql', '
- type Baz {
- baz: String!
- }');
- }
-
- /**
- * Tear down test case.
- */
- protected function tearDown()
- {
- unlink(__DIR__.'/foo.graphql');
- unlink(__DIR__.'/schema/bar.graphql');
- unlink(__DIR__.'/schema/baz.graphql');
-
- if (is_dir(__DIR__.'/schema')) {
- rmdir(__DIR__.'/schema');
- }
- }
-
- /**
- * @test
- */
- public function itStitchesLighthouseSchema()
- {
- $schema = $this->stitcher->stitch('_id');
- $hasNode = false !== strpos($schema, 'interface Node');
-
- $this->assertTrue($hasNode);
- }
-
- /**
- * @test
- */
- public function itConcatsSchemas()
- {
- $schema = $this->stitcher->stitch('_id', __DIR__.'/schema/baz.graphql');
- $hasNode = false !== strpos($schema, 'interface Node');
- $hasBaz = false !== strpos($schema, 'type Baz');
-
- $this->assertTrue($hasNode);
- $this->assertTrue($hasBaz);
- }
-
- /**
- * @test
- */
- public function itCanImportSchemas()
- {
- $schema = $this->stitcher->stitch('_id', __DIR__.'/foo.graphql');
- $hasNode = false !== strpos($schema, 'interface Node');
- $hasFoo = false !== strpos($schema, 'type Foo');
- $hasBar = false !== strpos($schema, 'type Bar');
- $hasBaz = false !== strpos($schema, 'type Baz');
-
- $this->assertTrue($hasNode, 'Schema does not include type Node');
- $this->assertTrue($hasFoo, 'Schema does not include type Foo');
- $this->assertTrue($hasBar, 'Schema does not include type Bar');
- $this->assertTrue($hasBaz, 'Schema does not include type Baz');
- }
-}
diff --git a/tests/Unit/Support/Validator/ValidatorTest.php b/tests/Unit/Support/Validator/ValidatorTest.php
index e18b24d231..8f8c517958 100644
--- a/tests/Unit/Support/Validator/ValidatorTest.php
+++ b/tests/Unit/Support/Validator/ValidatorTest.php
@@ -2,6 +2,7 @@
namespace Tests\Unit\Support\Validator;
+use Nuwave\Lighthouse\Schema\AST\PartialParser;
use Nuwave\Lighthouse\Schema\Directives\Args\ValidateDirective;
use Nuwave\Lighthouse\Schema\Values\FieldValue;
use Nuwave\Lighthouse\Schema\Values\NodeValue;
@@ -28,17 +29,23 @@ protected function rules()
};
});
- $field = $this->getNodeField('
+ $typeDefinition = PartialParser::objectTypeDefinition('
type Mutation {
foo(bar: String baz: Int): String @validate(validator: "foo.validator")
}
- ')->setResolver(function () {
+ ');
+
+ $fieldValue = new FieldValue(new NodeValue($typeDefinition), $typeDefinition->fields[0]);
+ $fieldValue->setResolver(function () {
return 'foo';
});
- (new ValidateDirective())->handleField($field);
+ (new ValidateDirective())->handleField($fieldValue);
$this->expectException(ValidationError::class);
- $field->getResolver()(null, ['bar' => 'foo', 'baz' => 1]);
+ $fieldValue->getResolver()(
+ null,
+ ['bar' => 'foo', 'baz' => 1]
+ );
}
}