From b8eacb23abe2972c621c46fdfc4568e8a1dd17bb Mon Sep 17 00:00:00 2001 From: Aleksei Lebedev <1329824+LastDragon-ru@users.noreply.github.com> Date: Sun, 21 Jan 2024 11:58:44 +0400 Subject: [PATCH 01/26] refactor(graphql)!: `AstManipulationBuilderInfo` => `HandlerContextBuilderInfo` --- packages/graphql/UPGRADE.md | 2 +- .../HandlerContextBuilderInfo.php} | 4 ++-- .../graphql/src/Builder/Directives/HandlerDirective.php | 5 ++--- packages/graphql/src/Builder/Manipulator.php | 4 ++-- packages/graphql/src/Builder/ManipulatorTest.php | 4 ++-- packages/graphql/src/Builder/Types/InputObject.php | 6 +++--- .../graphql/src/SearchBy/Operators/Complex/RelationType.php | 4 ++-- packages/graphql/src/SearchBy/Types/Condition.php | 4 ++-- packages/graphql/src/SearchBy/Types/Enumeration.php | 4 ++-- packages/graphql/src/SearchBy/Types/Scalar.php | 4 ++-- packages/graphql/src/SortBy/Types/Clause.php | 4 ++-- 11 files changed, 22 insertions(+), 23 deletions(-) rename packages/graphql/src/Builder/{Contexts/AstManipulationBuilderInfo.php => Directives/HandlerContextBuilderInfo.php} (68%) diff --git a/packages/graphql/UPGRADE.md b/packages/graphql/UPGRADE.md index 4afab8a39..48ac96d8d 100644 --- a/packages/graphql/UPGRADE.md +++ b/packages/graphql/UPGRADE.md @@ -91,5 +91,5 @@ This section is actual only if you are extending the package. Please review and * [ ] To get `BuilderInfo` instance within Operator the `LastDragon_ru\LaraASP\GraphQL\Builder\Contracts\Context` should be used instead of `LastDragon_ru\LaraASP\GraphQL\Builder\Manipulator`: ```php - $context->get(\LastDragon_ru\LaraASP\GraphQL\Builder\Contexts\AstManipulationBuilderInfo::class)?->builderInfo + $context->get(LastDragon_ru\LaraASP\GraphQL\Builder\Directives\HandlerContextBuilderInfo::class)?->value ``` diff --git a/packages/graphql/src/Builder/Contexts/AstManipulationBuilderInfo.php b/packages/graphql/src/Builder/Directives/HandlerContextBuilderInfo.php similarity index 68% rename from packages/graphql/src/Builder/Contexts/AstManipulationBuilderInfo.php rename to packages/graphql/src/Builder/Directives/HandlerContextBuilderInfo.php index 9c460a2f2..26889f1b5 100644 --- a/packages/graphql/src/Builder/Contexts/AstManipulationBuilderInfo.php +++ b/packages/graphql/src/Builder/Directives/HandlerContextBuilderInfo.php @@ -1,10 +1,10 @@ override([ - AstManipulationBuilderInfo::class => new AstManipulationBuilderInfo($builder), + HandlerContextBuilderInfo::class => new HandlerContextBuilderInfo($builder), ]); $source = $this->getFieldArgumentSource($manipulator, $parentType, $parentField, $argDefinition); $type = $this->getArgDefinitionType($manipulator, $documentAST, $source, $context); diff --git a/packages/graphql/src/Builder/Manipulator.php b/packages/graphql/src/Builder/Manipulator.php index c772bab02..6c8488eac 100644 --- a/packages/graphql/src/Builder/Manipulator.php +++ b/packages/graphql/src/Builder/Manipulator.php @@ -22,12 +22,12 @@ use GraphQL\Type\Definition\Type; use Illuminate\Container\Container; use Illuminate\Support\Str; -use LastDragon_ru\LaraASP\GraphQL\Builder\Contexts\AstManipulationBuilderInfo; use LastDragon_ru\LaraASP\GraphQL\Builder\Contracts\Context; use LastDragon_ru\LaraASP\GraphQL\Builder\Contracts\Operator; use LastDragon_ru\LaraASP\GraphQL\Builder\Contracts\Scope; use LastDragon_ru\LaraASP\GraphQL\Builder\Contracts\TypeProvider; use LastDragon_ru\LaraASP\GraphQL\Builder\Contracts\TypeSource; +use LastDragon_ru\LaraASP\GraphQL\Builder\Directives\HandlerContextBuilderInfo; use LastDragon_ru\LaraASP\GraphQL\Builder\Directives\OperatorsDirective; use LastDragon_ru\LaraASP\GraphQL\Builder\Exceptions\FakeTypeDefinitionIsNotFake; use LastDragon_ru\LaraASP\GraphQL\Builder\Exceptions\FakeTypeDefinitionUnknown; @@ -147,7 +147,7 @@ public function getTypeOperators(string $scope, string $type, Context $context, } // Builder? - $builder = $context->get(AstManipulationBuilderInfo::class)?->value->getBuilder(); + $builder = $context->get(HandlerContextBuilderInfo::class)?->value->getBuilder(); if (!$builder) { return []; diff --git a/packages/graphql/src/Builder/ManipulatorTest.php b/packages/graphql/src/Builder/ManipulatorTest.php index 8d3174429..1b3f476d1 100644 --- a/packages/graphql/src/Builder/ManipulatorTest.php +++ b/packages/graphql/src/Builder/ManipulatorTest.php @@ -5,13 +5,13 @@ use GraphQL\Type\Definition\CustomScalarType; use GraphQL\Type\Definition\ObjectType; use Illuminate\Container\Container; -use LastDragon_ru\LaraASP\GraphQL\Builder\Contexts\AstManipulationBuilderInfo; use LastDragon_ru\LaraASP\GraphQL\Builder\Contracts\Context as ContextContract; use LastDragon_ru\LaraASP\GraphQL\Builder\Contracts\Handler; use LastDragon_ru\LaraASP\GraphQL\Builder\Contracts\Operator; use LastDragon_ru\LaraASP\GraphQL\Builder\Contracts\Scope; use LastDragon_ru\LaraASP\GraphQL\Builder\Contracts\TypeProvider; use LastDragon_ru\LaraASP\GraphQL\Builder\Contracts\TypeSource; +use LastDragon_ru\LaraASP\GraphQL\Builder\Directives\HandlerContextBuilderInfo; use LastDragon_ru\LaraASP\GraphQL\Builder\Directives\OperatorDirective; use LastDragon_ru\LaraASP\GraphQL\Builder\Directives\OperatorsDirective; use LastDragon_ru\LaraASP\GraphQL\Testing\Package\TestCase; @@ -193,7 +193,7 @@ public function getScope(): string { // Manipulator $context = (new Context())->override([ - AstManipulationBuilderInfo::class => new AstManipulationBuilderInfo( + HandlerContextBuilderInfo::class => new HandlerContextBuilderInfo( new BuilderInfo($builder::class, $builder::class), ), ]); diff --git a/packages/graphql/src/Builder/Types/InputObject.php b/packages/graphql/src/Builder/Types/InputObject.php index 8cda5d9e7..06aa67d99 100644 --- a/packages/graphql/src/Builder/Types/InputObject.php +++ b/packages/graphql/src/Builder/Types/InputObject.php @@ -9,12 +9,12 @@ use GraphQL\Language\BlockString; use GraphQL\Language\Parser; use GraphQL\Type\Definition\Type; -use LastDragon_ru\LaraASP\GraphQL\Builder\Contexts\AstManipulationBuilderInfo; use LastDragon_ru\LaraASP\GraphQL\Builder\Contracts\Context; use LastDragon_ru\LaraASP\GraphQL\Builder\Contracts\Operator; use LastDragon_ru\LaraASP\GraphQL\Builder\Contracts\Scope; use LastDragon_ru\LaraASP\GraphQL\Builder\Contracts\TypeDefinition; use LastDragon_ru\LaraASP\GraphQL\Builder\Contracts\TypeSource; +use LastDragon_ru\LaraASP\GraphQL\Builder\Directives\HandlerContextBuilderInfo; use LastDragon_ru\LaraASP\GraphQL\Builder\Exceptions\TypeDefinitionFieldAlreadyDefined; use LastDragon_ru\LaraASP\GraphQL\Builder\Manipulator; use LastDragon_ru\LaraASP\GraphQL\Builder\Sources\InputFieldSource; @@ -166,7 +166,7 @@ protected function getFieldDefinition( Context $context, ): ?InputValueDefinitionNode { // Builder? - $builder = $context->get(AstManipulationBuilderInfo::class)?->value->getBuilder(); + $builder = $context->get(HandlerContextBuilderInfo::class)?->value->getBuilder(); if (!$builder) { return null; @@ -222,7 +222,7 @@ protected function getFieldDirectiveOperator( Context $context, ): ?Operator { // Builder? - $builder = $context->get(AstManipulationBuilderInfo::class)?->value->getBuilder(); + $builder = $context->get(HandlerContextBuilderInfo::class)?->value->getBuilder(); if (!$builder) { return null; diff --git a/packages/graphql/src/SearchBy/Operators/Complex/RelationType.php b/packages/graphql/src/SearchBy/Operators/Complex/RelationType.php index afa41b76a..5497b6fd6 100644 --- a/packages/graphql/src/SearchBy/Operators/Complex/RelationType.php +++ b/packages/graphql/src/SearchBy/Operators/Complex/RelationType.php @@ -6,10 +6,10 @@ use GraphQL\Language\Parser; use GraphQL\Type\Definition\Type; use Illuminate\Support\Str; -use LastDragon_ru\LaraASP\GraphQL\Builder\Contexts\AstManipulationBuilderInfo; use LastDragon_ru\LaraASP\GraphQL\Builder\Contracts\Context; use LastDragon_ru\LaraASP\GraphQL\Builder\Contracts\TypeDefinition; use LastDragon_ru\LaraASP\GraphQL\Builder\Contracts\TypeSource; +use LastDragon_ru\LaraASP\GraphQL\Builder\Directives\HandlerContextBuilderInfo; use LastDragon_ru\LaraASP\GraphQL\Builder\Manipulator; use LastDragon_ru\LaraASP\GraphQL\SearchBy\Directives\Directive; use LastDragon_ru\LaraASP\GraphQL\SearchBy\Types\Condition; @@ -24,7 +24,7 @@ public function __construct() { #[Override] public function getTypeName(TypeSource $source, Context $context): string { $typeName = $source->getTypeName(); - $builderName = $context->get(AstManipulationBuilderInfo::class)?->value->getName() ?? 'Unknown'; + $builderName = $context->get(HandlerContextBuilderInfo::class)?->value->getName() ?? 'Unknown'; $operatorName = Str::studly(Relation::getName()); $directiveName = Directive::Name; diff --git a/packages/graphql/src/SearchBy/Types/Condition.php b/packages/graphql/src/SearchBy/Types/Condition.php index 2993aa1ff..0003d7f3b 100644 --- a/packages/graphql/src/SearchBy/Types/Condition.php +++ b/packages/graphql/src/SearchBy/Types/Condition.php @@ -13,10 +13,10 @@ use GraphQL\Type\Definition\InterfaceType; use GraphQL\Type\Definition\ObjectType; use GraphQL\Type\Definition\ScalarType; -use LastDragon_ru\LaraASP\GraphQL\Builder\Contexts\AstManipulationBuilderInfo; use LastDragon_ru\LaraASP\GraphQL\Builder\Contracts\Context; use LastDragon_ru\LaraASP\GraphQL\Builder\Contracts\Operator as OperatorContract; use LastDragon_ru\LaraASP\GraphQL\Builder\Contracts\TypeSource; +use LastDragon_ru\LaraASP\GraphQL\Builder\Directives\HandlerContextBuilderInfo; use LastDragon_ru\LaraASP\GraphQL\Builder\Manipulator; use LastDragon_ru\LaraASP\GraphQL\Builder\Sources\InputFieldSource; use LastDragon_ru\LaraASP\GraphQL\Builder\Sources\InputSource; @@ -45,7 +45,7 @@ class Condition extends InputObject { #[Override] public function getTypeName(TypeSource $source, Context $context): string { $typeName = $source->getTypeName(); - $builderName = $context->get(AstManipulationBuilderInfo::class)?->value->getName() ?? 'Unknown'; + $builderName = $context->get(HandlerContextBuilderInfo::class)?->value->getName() ?? 'Unknown'; $directiveName = Directive::Name; return "{$directiveName}{$builderName}Condition{$typeName}"; diff --git a/packages/graphql/src/SearchBy/Types/Enumeration.php b/packages/graphql/src/SearchBy/Types/Enumeration.php index de6e5b61c..22ad30bf1 100644 --- a/packages/graphql/src/SearchBy/Types/Enumeration.php +++ b/packages/graphql/src/SearchBy/Types/Enumeration.php @@ -5,10 +5,10 @@ use GraphQL\Language\AST\TypeDefinitionNode; use GraphQL\Language\Parser; use GraphQL\Type\Definition\Type; -use LastDragon_ru\LaraASP\GraphQL\Builder\Contexts\AstManipulationBuilderInfo; use LastDragon_ru\LaraASP\GraphQL\Builder\Contracts\Context; use LastDragon_ru\LaraASP\GraphQL\Builder\Contracts\TypeDefinition; use LastDragon_ru\LaraASP\GraphQL\Builder\Contracts\TypeSource; +use LastDragon_ru\LaraASP\GraphQL\Builder\Directives\HandlerContextBuilderInfo; use LastDragon_ru\LaraASP\GraphQL\Builder\Manipulator; use LastDragon_ru\LaraASP\GraphQL\SearchBy\Directives\Directive; use LastDragon_ru\LaraASP\GraphQL\SearchBy\Operators; @@ -22,7 +22,7 @@ public function __construct() { #[Override] public function getTypeName(TypeSource $source, Context $context): string { $directiveName = Directive::Name; - $builderName = $context->get(AstManipulationBuilderInfo::class)?->value->getName() ?? 'Unknown'; + $builderName = $context->get(HandlerContextBuilderInfo::class)?->value->getName() ?? 'Unknown'; $typeName = $source->getTypeName(); $nullable = $source->isNullable() ? 'OrNull' : ''; diff --git a/packages/graphql/src/SearchBy/Types/Scalar.php b/packages/graphql/src/SearchBy/Types/Scalar.php index 1a8195bb8..13946011e 100644 --- a/packages/graphql/src/SearchBy/Types/Scalar.php +++ b/packages/graphql/src/SearchBy/Types/Scalar.php @@ -5,10 +5,10 @@ use GraphQL\Language\AST\TypeDefinitionNode; use GraphQL\Language\Parser; use GraphQL\Type\Definition\Type; -use LastDragon_ru\LaraASP\GraphQL\Builder\Contexts\AstManipulationBuilderInfo; use LastDragon_ru\LaraASP\GraphQL\Builder\Contracts\Context; use LastDragon_ru\LaraASP\GraphQL\Builder\Contracts\TypeDefinition; use LastDragon_ru\LaraASP\GraphQL\Builder\Contracts\TypeSource; +use LastDragon_ru\LaraASP\GraphQL\Builder\Directives\HandlerContextBuilderInfo; use LastDragon_ru\LaraASP\GraphQL\Builder\Manipulator; use LastDragon_ru\LaraASP\GraphQL\SearchBy\Directives\Directive; use LastDragon_ru\LaraASP\GraphQL\SearchBy\Operators; @@ -22,7 +22,7 @@ public function __construct() { #[Override] public function getTypeName(TypeSource $source, Context $context): string { $directiveName = Directive::Name; - $builderName = $context->get(AstManipulationBuilderInfo::class)?->value->getName() ?? 'Unknown'; + $builderName = $context->get(HandlerContextBuilderInfo::class)?->value->getName() ?? 'Unknown'; $typeName = $source->getTypeName(); $nullable = $source->isNullable() ? 'OrNull' : ''; diff --git a/packages/graphql/src/SortBy/Types/Clause.php b/packages/graphql/src/SortBy/Types/Clause.php index 934950443..66ec0b91c 100644 --- a/packages/graphql/src/SortBy/Types/Clause.php +++ b/packages/graphql/src/SortBy/Types/Clause.php @@ -9,10 +9,10 @@ use GraphQL\Type\Definition\InputObjectType; use GraphQL\Type\Definition\InterfaceType; use GraphQL\Type\Definition\ObjectType; -use LastDragon_ru\LaraASP\GraphQL\Builder\Contexts\AstManipulationBuilderInfo; use LastDragon_ru\LaraASP\GraphQL\Builder\Contracts\Context; use LastDragon_ru\LaraASP\GraphQL\Builder\Contracts\Operator as OperatorContract; use LastDragon_ru\LaraASP\GraphQL\Builder\Contracts\TypeSource; +use LastDragon_ru\LaraASP\GraphQL\Builder\Directives\HandlerContextBuilderInfo; use LastDragon_ru\LaraASP\GraphQL\Builder\Manipulator; use LastDragon_ru\LaraASP\GraphQL\Builder\Sources\InputFieldSource; use LastDragon_ru\LaraASP\GraphQL\Builder\Sources\InputSource; @@ -39,7 +39,7 @@ class Clause extends InputObject { #[Override] public function getTypeName(TypeSource $source, Context $context): string { $directiveName = Directive::Name; - $builderName = $context->get(AstManipulationBuilderInfo::class)?->value->getName() ?? 'Unknown'; + $builderName = $context->get(HandlerContextBuilderInfo::class)?->value->getName() ?? 'Unknown'; $typeName = $source->getTypeName(); return "{$directiveName}{$builderName}Clause{$typeName}"; From 55d486f8c19df21d86c355dc5374826b6b9919fd Mon Sep 17 00:00:00 2001 From: Aleksei Lebedev <1329824+LastDragon-ru@users.noreply.github.com> Date: Mon, 22 Jan 2024 08:38:43 +0400 Subject: [PATCH 02/26] refactor(graphql)!: `Operator:isBuilderSupported()` => `Operator:isAvailable()`. --- packages/graphql/src/Builder/Contracts/Operator.php | 2 +- .../graphql/src/Builder/Directives/PropertyDirective.php | 2 +- packages/graphql/src/Builder/Manipulator.php | 2 +- packages/graphql/src/Builder/ManipulatorTest.php | 6 +++--- packages/graphql/src/Builder/OperatorsTest.php | 2 +- packages/graphql/src/Builder/Types/InputObject.php | 6 +++--- packages/graphql/src/SearchBy/Operators/BaseOperator.php | 2 +- .../graphql/src/SearchBy/Operators/Complex/Relation.php | 2 +- .../graphql/src/SearchBy/Operators/Traits/ScoutSupport.php | 5 +++-- packages/graphql/src/SortBy/Operators/BaseOperator.php | 3 ++- packages/graphql/src/SortBy/Operators/Extra/NullsFirst.php | 2 +- packages/graphql/src/SortBy/Operators/Extra/NullsLast.php | 2 +- packages/graphql/src/SortBy/Operators/Field.php | 2 +- packages/graphql/src/SortBy/Operators/Property.php | 2 +- 14 files changed, 21 insertions(+), 19 deletions(-) diff --git a/packages/graphql/src/Builder/Contracts/Operator.php b/packages/graphql/src/Builder/Contracts/Operator.php index d4a681178..ab3a5d2f6 100644 --- a/packages/graphql/src/Builder/Contracts/Operator.php +++ b/packages/graphql/src/Builder/Contracts/Operator.php @@ -20,7 +20,7 @@ public function getFieldDescription(): string; /** * @param class-string $builder */ - public function isBuilderSupported(string $builder): bool; + public function isAvailable(string $builder, Context $context): bool; /** * @template TBuilder of object diff --git a/packages/graphql/src/Builder/Directives/PropertyDirective.php b/packages/graphql/src/Builder/Directives/PropertyDirective.php index cda2965a6..332ce6379 100644 --- a/packages/graphql/src/Builder/Directives/PropertyDirective.php +++ b/packages/graphql/src/Builder/Directives/PropertyDirective.php @@ -25,7 +25,7 @@ public function getFieldType(TypeProvider $provider, TypeSource $source, Context } #[Override] - public function isBuilderSupported(string $builder): bool { + public function isAvailable(string $builder, Context $context): bool { return true; } diff --git a/packages/graphql/src/Builder/Manipulator.php b/packages/graphql/src/Builder/Manipulator.php index 6c8488eac..e4d84f044 100644 --- a/packages/graphql/src/Builder/Manipulator.php +++ b/packages/graphql/src/Builder/Manipulator.php @@ -198,7 +198,7 @@ public function getTypeOperators(string $scope, string $type, Context $context, continue; } - if (!$operator->isBuilderSupported($builder)) { + if (!$operator->isAvailable($builder, $context)) { continue; } diff --git a/packages/graphql/src/Builder/ManipulatorTest.php b/packages/graphql/src/Builder/ManipulatorTest.php index 1b3f476d1..f25c67f72 100644 --- a/packages/graphql/src/Builder/ManipulatorTest.php +++ b/packages/graphql/src/Builder/ManipulatorTest.php @@ -399,7 +399,7 @@ public function getFieldDescription(): string { } #[Override] - public function isBuilderSupported(string $builder): bool { + public function isAvailable(string $builder, ContextContract $context): bool { return is_a($builder, stdClass::class, true); } @@ -436,7 +436,7 @@ public function getFieldDescription(): string { } #[Override] - public function isBuilderSupported(string $builder): bool { + public function isAvailable(string $builder, ContextContract $context): bool { return false; } @@ -473,7 +473,7 @@ public function getFieldDescription(): string { } #[Override] - public function isBuilderSupported(string $builder): bool { + public function isAvailable(string $builder, ContextContract $context): bool { return is_a($builder, stdClass::class, true); } diff --git a/packages/graphql/src/Builder/OperatorsTest.php b/packages/graphql/src/Builder/OperatorsTest.php index 7d8642562..d8d00832b 100644 --- a/packages/graphql/src/Builder/OperatorsTest.php +++ b/packages/graphql/src/Builder/OperatorsTest.php @@ -179,7 +179,7 @@ public function getFieldDescription(): string { } #[Override] - public function isBuilderSupported(string $builder): bool { + public function isAvailable(string $builder, Context $context): bool { throw new Exception('Should not be called'); } diff --git a/packages/graphql/src/Builder/Types/InputObject.php b/packages/graphql/src/Builder/Types/InputObject.php index 06aa67d99..297ac460a 100644 --- a/packages/graphql/src/Builder/Types/InputObject.php +++ b/packages/graphql/src/Builder/Types/InputObject.php @@ -175,7 +175,7 @@ protected function getFieldDefinition( // Operator? [$operator, $type] = $this->getFieldOperator($manipulator, $field, $context) ?? [null, null]; - if ($operator === null || !$operator->isBuilderSupported($builder)) { + if ($operator === null || !$operator->isAvailable($builder, $context)) { return null; } @@ -236,8 +236,8 @@ protected function getFieldDirectiveOperator( $operator = $manipulator->getDirective( $node, $directive, - static function (Operator $operator) use ($builder): bool { - return $operator->isBuilderSupported($builder); + static function (Operator $operator) use ($builder, $context): bool { + return $operator->isAvailable($builder, $context); }, ); diff --git a/packages/graphql/src/SearchBy/Operators/BaseOperator.php b/packages/graphql/src/SearchBy/Operators/BaseOperator.php index d1d7e3b35..914e4affd 100644 --- a/packages/graphql/src/SearchBy/Operators/BaseOperator.php +++ b/packages/graphql/src/SearchBy/Operators/BaseOperator.php @@ -21,7 +21,7 @@ public function getFieldType(TypeProvider $provider, TypeSource $source, Context } #[Override] - public function isBuilderSupported(string $builder): bool { + public function isAvailable(string $builder, Context $context): bool { return is_a($builder, EloquentBuilder::class, true) || is_a($builder, QueryBuilder::class, true); } diff --git a/packages/graphql/src/SearchBy/Operators/Complex/Relation.php b/packages/graphql/src/SearchBy/Operators/Complex/Relation.php index 6b17564bb..2f74c7b88 100644 --- a/packages/graphql/src/SearchBy/Operators/Complex/Relation.php +++ b/packages/graphql/src/SearchBy/Operators/Complex/Relation.php @@ -64,7 +64,7 @@ public function getFieldDescription(): string { } #[Override] - public function isBuilderSupported(string $builder): bool { + public function isAvailable(string $builder, Context $context): bool { return is_a($builder, EloquentBuilder::class, true); } diff --git a/packages/graphql/src/SearchBy/Operators/Traits/ScoutSupport.php b/packages/graphql/src/SearchBy/Operators/Traits/ScoutSupport.php index 45f73495b..265f01e5c 100644 --- a/packages/graphql/src/SearchBy/Operators/Traits/ScoutSupport.php +++ b/packages/graphql/src/SearchBy/Operators/Traits/ScoutSupport.php @@ -5,6 +5,7 @@ use Composer\InstalledVersions; use Composer\Semver\VersionParser; use Laravel\Scout\Builder as ScoutBuilder; +use LastDragon_ru\LaraASP\GraphQL\Builder\Contracts\Context; use LastDragon_ru\LaraASP\GraphQL\SearchBy\Definitions\SearchByOperatorPropertyDirective; use Override; @@ -15,8 +16,8 @@ */ trait ScoutSupport { #[Override] - public function isBuilderSupported(string $builder): bool { - return parent::isBuilderSupported($builder) + public function isAvailable(string $builder, Context $context): bool { + return parent::isAvailable($builder, $context) || (is_a($builder, ScoutBuilder::class, true) && $this->isScoutSupported()); } diff --git a/packages/graphql/src/SortBy/Operators/BaseOperator.php b/packages/graphql/src/SortBy/Operators/BaseOperator.php index f1c933c46..ea48c0c2e 100644 --- a/packages/graphql/src/SortBy/Operators/BaseOperator.php +++ b/packages/graphql/src/SortBy/Operators/BaseOperator.php @@ -4,6 +4,7 @@ use Illuminate\Database\Eloquent\Builder as EloquentBuilder; use Illuminate\Database\Query\Builder as QueryBuilder; +use LastDragon_ru\LaraASP\GraphQL\Builder\Contracts\Context; use LastDragon_ru\LaraASP\GraphQL\Builder\Directives\OperatorDirective; use LastDragon_ru\LaraASP\GraphQL\SortBy\Contracts\Operator; use Override; @@ -12,7 +13,7 @@ abstract class BaseOperator extends OperatorDirective implements Operator { #[Override] - public function isBuilderSupported(string $builder): bool { + public function isAvailable(string $builder, Context $context): bool { return is_a($builder, EloquentBuilder::class, true) || is_a($builder, QueryBuilder::class, true); } diff --git a/packages/graphql/src/SortBy/Operators/Extra/NullsFirst.php b/packages/graphql/src/SortBy/Operators/Extra/NullsFirst.php index 86f03cdfa..306c56b17 100644 --- a/packages/graphql/src/SortBy/Operators/Extra/NullsFirst.php +++ b/packages/graphql/src/SortBy/Operators/Extra/NullsFirst.php @@ -46,7 +46,7 @@ public function getFieldDescription(): string { } #[Override] - public function isBuilderSupported(string $builder): bool { + public function isAvailable(string $builder, Context $context): bool { return $this->factory->isSupported($builder); } diff --git a/packages/graphql/src/SortBy/Operators/Extra/NullsLast.php b/packages/graphql/src/SortBy/Operators/Extra/NullsLast.php index ba197e1e0..89c5163da 100644 --- a/packages/graphql/src/SortBy/Operators/Extra/NullsLast.php +++ b/packages/graphql/src/SortBy/Operators/Extra/NullsLast.php @@ -46,7 +46,7 @@ public function getFieldDescription(): string { } #[Override] - public function isBuilderSupported(string $builder): bool { + public function isAvailable(string $builder, Context $context): bool { return $this->factory->isSupported($builder); } diff --git a/packages/graphql/src/SortBy/Operators/Field.php b/packages/graphql/src/SortBy/Operators/Field.php index 137655ccf..288cbeb92 100644 --- a/packages/graphql/src/SortBy/Operators/Field.php +++ b/packages/graphql/src/SortBy/Operators/Field.php @@ -48,7 +48,7 @@ public function getFieldDescription(): string { } #[Override] - public function isBuilderSupported(string $builder): bool { + public function isAvailable(string $builder, Context $context): bool { return $this->factory->isSupported($builder); } diff --git a/packages/graphql/src/SortBy/Operators/Property.php b/packages/graphql/src/SortBy/Operators/Property.php index 01caee66d..cd1b26634 100644 --- a/packages/graphql/src/SortBy/Operators/Property.php +++ b/packages/graphql/src/SortBy/Operators/Property.php @@ -26,7 +26,7 @@ public function getFieldDescription(): string { } #[Override] - public function isBuilderSupported(string $builder): bool { + public function isAvailable(string $builder, Context $context): bool { return is_a($builder, EloquentBuilder::class, true) || is_a($builder, ScoutBuilder::class, true); } From b2cae48e1ea2e7237e9a87d301cc005af92d9dda Mon Sep 17 00:00:00 2001 From: Aleksei Lebedev <1329824+LastDragon-ru@users.noreply.github.com> Date: Mon, 22 Jan 2024 09:48:26 +0400 Subject: [PATCH 03/26] Added `HandlerContextImplicit`. --- .../src/Builder/Directives/HandlerContextImplicit.php | 11 +++++++++++ .../src/Builder/Directives/HandlerDirective.php | 6 +++++- 2 files changed, 16 insertions(+), 1 deletion(-) create mode 100644 packages/graphql/src/Builder/Directives/HandlerContextImplicit.php diff --git a/packages/graphql/src/Builder/Directives/HandlerContextImplicit.php b/packages/graphql/src/Builder/Directives/HandlerContextImplicit.php new file mode 100644 index 000000000..594e93ab7 --- /dev/null +++ b/packages/graphql/src/Builder/Directives/HandlerContextImplicit.php @@ -0,0 +1,11 @@ +override([ HandlerContextBuilderInfo::class => new HandlerContextBuilderInfo($builder), + HandlerContextImplicit::class => new HandlerContextImplicit( + $manipulator->isPlaceholder($argDefinition), + ), ]); $source = $this->getFieldArgumentSource($manipulator, $parentType, $parentField, $argDefinition); $type = $this->getArgDefinitionType($manipulator, $documentAST, $source, $context); @@ -258,7 +262,7 @@ protected function getArgumentTypeDefinitionNode( ContextContract $context, ): ListTypeNode|NamedTypeNode|NonNullTypeNode|null { $type = null; - $definition = $manipulator->isPlaceholder($argument->getArgument()) + $definition = $context->get(HandlerContextImplicit::class)?->value ? $manipulator->getPlaceholderTypeDefinitionNode($argument->getField()) : $argument->getTypeDefinition(); From 2f66b3475f89ed74037a4a850051e92e30b2e10a Mon Sep 17 00:00:00 2001 From: Aleksei Lebedev <1329824+LastDragon-ru@users.noreply.github.com> Date: Tue, 23 Jan 2024 11:26:10 +0400 Subject: [PATCH 04/26] `@searchBy` better (isolated) tests. --- packages/graphql/docs/Directives/@searchBy.md | 2 +- .../src/SearchBy/Directives/DirectiveTest.php | 406 ++-- .../AllowedDirectives.expected.graphql | 85 + .../AllowedDirectives.schema.graphql | 12 + .../CustomComplexOperator.expected.graphql} | 0 .../CustomComplexOperator.schema.graphql} | 0 .../Example.expected.graphql} | 0 .../Example.schema.graphql} | 0 .../DirectiveTest/Explicit.expected.graphql | 639 ++++++ .../DirectiveTest/Explicit.schema.graphql | 17 + .../DirectiveTest/Ignored.expected.graphql | 187 ++ .../DirectiveTest/Ignored.schema.graphql | 64 + .../DirectiveTest/Implicit.expected.graphql | 660 ++++++ .../DirectiveTest/Implicit.schema.graphql | 17 + .../InterfaceUpdate.expected.graphql | 102 + .../InterfaceUpdate.schema.graphql | 25 + .../ScalarOperators.expected.graphql | 218 ++ .../ScalarOperators.schema.graphql | 28 + .../Scout.expected.graphql} | 0 .../Scout.expected.v10.3.0.graphql} | 0 .../Scout.schema.graphql} | 0 .../TypeRegistry.expected.graphql} | 0 .../TypeRegistry.schema.graphql} | 0 .../DirectiveTest~full-expected.graphql | 1954 ----------------- .../Directives/DirectiveTest~full.graphql | 209 -- .../Directives/DirectiveTest~unknown.graphql | 7 - .../DirectiveTest~usedonly-expected.graphql | 216 -- .../Directives/DirectiveTest~usedonly.graphql | 7 - 28 files changed, 2281 insertions(+), 2574 deletions(-) create mode 100644 packages/graphql/src/SearchBy/Directives/DirectiveTest/AllowedDirectives.expected.graphql create mode 100644 packages/graphql/src/SearchBy/Directives/DirectiveTest/AllowedDirectives.schema.graphql rename packages/graphql/src/SearchBy/Directives/{DirectiveTest~custom-complex-operators-expected.graphql => DirectiveTest/CustomComplexOperator.expected.graphql} (100%) rename packages/graphql/src/SearchBy/Directives/{DirectiveTest~custom-complex-operators.graphql => DirectiveTest/CustomComplexOperator.schema.graphql} (100%) rename packages/graphql/src/SearchBy/Directives/{DirectiveTest~example-expected.graphql => DirectiveTest/Example.expected.graphql} (100%) rename packages/graphql/src/SearchBy/Directives/{DirectiveTest~example.graphql => DirectiveTest/Example.schema.graphql} (100%) create mode 100644 packages/graphql/src/SearchBy/Directives/DirectiveTest/Explicit.expected.graphql create mode 100644 packages/graphql/src/SearchBy/Directives/DirectiveTest/Explicit.schema.graphql create mode 100644 packages/graphql/src/SearchBy/Directives/DirectiveTest/Ignored.expected.graphql create mode 100644 packages/graphql/src/SearchBy/Directives/DirectiveTest/Ignored.schema.graphql create mode 100644 packages/graphql/src/SearchBy/Directives/DirectiveTest/Implicit.expected.graphql create mode 100644 packages/graphql/src/SearchBy/Directives/DirectiveTest/Implicit.schema.graphql create mode 100644 packages/graphql/src/SearchBy/Directives/DirectiveTest/InterfaceUpdate.expected.graphql create mode 100644 packages/graphql/src/SearchBy/Directives/DirectiveTest/InterfaceUpdate.schema.graphql create mode 100644 packages/graphql/src/SearchBy/Directives/DirectiveTest/ScalarOperators.expected.graphql create mode 100644 packages/graphql/src/SearchBy/Directives/DirectiveTest/ScalarOperators.schema.graphql rename packages/graphql/src/SearchBy/Directives/{DirectiveTest~scout-expected.graphql => DirectiveTest/Scout.expected.graphql} (100%) rename packages/graphql/src/SearchBy/Directives/{DirectiveTest~scout-v10.3.0-expected.graphql => DirectiveTest/Scout.expected.v10.3.0.graphql} (100%) rename packages/graphql/src/SearchBy/Directives/{DirectiveTest~scout.graphql => DirectiveTest/Scout.schema.graphql} (100%) rename packages/graphql/src/SearchBy/Directives/{DirectiveTest~programmatically-expected.graphql => DirectiveTest/TypeRegistry.expected.graphql} (100%) rename packages/graphql/src/SearchBy/Directives/{DirectiveTest~programmatically.graphql => DirectiveTest/TypeRegistry.schema.graphql} (100%) delete mode 100644 packages/graphql/src/SearchBy/Directives/DirectiveTest~full-expected.graphql delete mode 100644 packages/graphql/src/SearchBy/Directives/DirectiveTest~full.graphql delete mode 100644 packages/graphql/src/SearchBy/Directives/DirectiveTest~unknown.graphql delete mode 100644 packages/graphql/src/SearchBy/Directives/DirectiveTest~usedonly-expected.graphql delete mode 100644 packages/graphql/src/SearchBy/Directives/DirectiveTest~usedonly.graphql diff --git a/packages/graphql/docs/Directives/@searchBy.md b/packages/graphql/docs/Directives/@searchBy.md index e0b125fa0..b3aef6568 100644 --- a/packages/graphql/docs/Directives/@searchBy.md +++ b/packages/graphql/docs/Directives/@searchBy.md @@ -63,7 +63,7 @@ input Comment { } ``` -That's all, just search 😃 (or look at [generated GraphQL schema](../../src/SearchBy/Directives/DirectiveTest~example-expected.graphql)) +That's all, just search 😃 (or look at [generated GraphQL schema](../../src/SearchBy/Directives/DirectiveTest/Example.expected.graphql)) ```graphql query { diff --git a/packages/graphql/src/SearchBy/Directives/DirectiveTest.php b/packages/graphql/src/SearchBy/Directives/DirectiveTest.php index f82831fa0..6e3e09f1b 100644 --- a/packages/graphql/src/SearchBy/Directives/DirectiveTest.php +++ b/packages/graphql/src/SearchBy/Directives/DirectiveTest.php @@ -33,6 +33,7 @@ use LastDragon_ru\LaraASP\GraphQL\SearchBy\Contracts\Ignored; use LastDragon_ru\LaraASP\GraphQL\SearchBy\Definitions\SearchByOperatorBetweenDirective; use LastDragon_ru\LaraASP\GraphQL\SearchBy\Definitions\SearchByOperatorEqualDirective; +use LastDragon_ru\LaraASP\GraphQL\SearchBy\Operators; use LastDragon_ru\LaraASP\GraphQL\SearchBy\Operators\BaseOperator; use LastDragon_ru\LaraASP\GraphQL\Testing\Package\Data\Models\WithTestObject; use LastDragon_ru\LaraASP\GraphQL\Testing\Package\DataProviders\BuilderDataProvider; @@ -79,10 +80,9 @@ final class DirectiveTest extends TestCase { /** * @dataProvider dataProviderManipulateArgDefinition * - * @param Closure(static): GraphQLExpected $expected - * @param Closure(static): void|null $prepare + * @param Closure(static): void|null $prepare */ - public function testManipulateArgDefinition(Closure $expected, string $graphql, ?Closure $prepare = null): void { + public function testManipulateArgDefinition(string $expected, string $graphql, ?Closure $prepare = null): void { if ($prepare) { $prepare($this); } @@ -92,7 +92,7 @@ public function testManipulateArgDefinition(Closure $expected, string $graphql, ); self::assertGraphQLSchemaEquals( - $expected($this), + new GraphQLExpected(self::getTestData()->file($expected)), ); } @@ -108,15 +108,15 @@ public function testManipulateArgDefinitionScoutBuilder(): void { ->setResolved('search', SearchDirective::class); $this->useGraphQLSchema( - self::getTestData()->file('~scout.graphql'), + self::getTestData()->file('Scout.schema.graphql'), ); self::assertGraphQLSchemaEquals( new GraphQLExpected( static::getTestData()->file( match (true) { - (new RequiresLaravelScout('>=10.3.0'))->isSatisfied() => '~scout-v10.3.0-expected.graphql', - default => '~scout-expected.graphql', + (new RequiresLaravelScout('>=10.3.0'))->isSatisfied() => 'Scout.expected.v10.3.0.graphql', + default => 'Scout.expected.graphql', }, ), ), @@ -126,78 +126,16 @@ public function testManipulateArgDefinitionScoutBuilder(): void { public function testManipulateArgDefinitionUnknownType(): void { self::expectExceptionObject(new TypeDefinitionUnknown('UnknownType')); - $this->useGraphQLSchema(self::getTestData()->file('~unknown.graphql')); - } - - public function testManipulateArgDefinitionProgrammaticallyAddedType(): void { - $enum = new EnumType([ - 'name' => 'TestEnum', - 'values' => [ - 'property' => [ - 'value' => 123, - 'description' => 'test property', - ], - ], - ]); - $ignored = new class([ - 'name' => 'TestIgnored', - 'fields' => [ - [ - 'name' => 'name', - 'type' => Type::nonNull(Type::string()), - ], - ], - ]) extends InputObjectType implements Ignored { - // empty - }; - $typeA = new InputObjectType([ - 'name' => 'TestTypeA', - 'fields' => [ - [ - 'name' => 'name', - 'type' => Type::string(), - ], - [ - 'name' => 'flag', - 'type' => Type::boolean(), - ], - [ - 'name' => 'value', - 'type' => Type::listOf(Type::nonNull($enum)), - ], - [ - 'name' => 'ignored', - 'type' => Type::listOf(Type::nonNull($ignored)), - ], - ], - ]); - $typeB = new InputObjectType([ - 'name' => 'TestTypeB', - 'fields' => [ - [ - 'name' => 'name', - 'type' => Type::nonNull(Type::string()), - ], - [ - 'name' => 'child', - 'type' => $typeA, - ], - ], - ]); - - $registry = Container::getInstance()->make(TypeRegistry::class); - - $registry->register($enum); - $registry->register($typeA); - $registry->register($typeB); - $registry->register($ignored); - $this->useGraphQLSchema( - self::getTestData()->file('~programmatically.graphql'), - ); + <<<'GRAPHQL' + type Query { + test(where: Properties @searchBy): ID! @all + } - self::assertGraphQLSchemaEquals( - self::getTestData()->file('~programmatically-expected.graphql'), + input Properties { + value: UnknownType + } + GRAPHQL, ); } @@ -382,130 +320,168 @@ static function (MockInterface $mock) use ($resolver): void { // // ========================================================================= /** - * @return array + * @return array */ public static function dataProviderManipulateArgDefinition(): array { return [ - 'full' => [ - static function (self $test): GraphQLExpected { - return (new GraphQLExpected( - $test::getTestData()->file('~full-expected.graphql'), - )); + 'Explicit' => [ + 'Explicit.expected.graphql', + 'Explicit.schema.graphql', + null, + ], + 'Implicit' => [ + 'Implicit.expected.graphql', + 'Implicit.schema.graphql', + null, + ], + 'ScalarOperators' => [ + 'ScalarOperators.expected.graphql', + 'ScalarOperators.schema.graphql', + null, + ], + 'TypeRegistry' => [ + 'TypeRegistry.expected.graphql', + 'TypeRegistry.schema.graphql', + static function (): void { + $enum = new EnumType([ + 'name' => 'TestEnum', + 'values' => [ + 'property' => [ + 'value' => 123, + 'description' => 'test property', + ], + ], + ]); + $ignored = new class([ + 'name' => 'TestIgnored', + 'fields' => [ + [ + 'name' => 'name', + 'type' => Type::nonNull(Type::string()), + ], + ], + ]) extends InputObjectType implements Ignored { + // empty + }; + $typeA = new InputObjectType([ + 'name' => 'TestTypeA', + 'fields' => [ + [ + 'name' => 'name', + 'type' => Type::string(), + ], + [ + 'name' => 'flag', + 'type' => Type::boolean(), + ], + [ + 'name' => 'value', + 'type' => Type::listOf(Type::nonNull($enum)), + ], + [ + 'name' => 'ignored', + 'type' => Type::listOf(Type::nonNull($ignored)), + ], + ], + ]); + $typeB = new InputObjectType([ + 'name' => 'TestTypeB', + 'fields' => [ + [ + 'name' => 'name', + 'type' => Type::nonNull(Type::string()), + ], + [ + 'name' => 'child', + 'type' => $typeA, + ], + ], + ]); + + $registry = Container::getInstance()->make(TypeRegistry::class); + + $registry->register($enum); + $registry->register($typeA); + $registry->register($typeB); + $registry->register($ignored); }, - '~full.graphql', + ], + 'CustomComplexOperator' => [ + 'CustomComplexOperator.expected.graphql', + 'CustomComplexOperator.schema.graphql', static function (): void { - $package = Package::Name; + $locator = Container::getInstance()->make(DirectiveLocator::class); + $resolver = Container::getInstance()->make(BuilderPropertyResolver::class); + $directive = new DirectiveTest__CustomComplexOperator($resolver); + $locator->setResolved('customComplexOperator', $directive::class); + }, + ], + 'AllowedDirectives' => [ + 'AllowedDirectives.expected.graphql', + 'AllowedDirectives.schema.graphql', + static function (): void { config([ - "{$package}.search_by.operators.Date" => [ + Package::Name.'.search_by.operators.String' => [ SearchByOperatorEqualDirective::class, ], + Package::Name.'.search_by.operators.'.Operators::Extra => [ + // empty + ], ]); }, ], - 'example' => [ - static function (self $test): GraphQLExpected { - return (new GraphQLExpected( - $test::getTestData()->file('~example-expected.graphql'), - )); - }, - '~example.graphql', + 'Ignored' => [ + 'Ignored.expected.graphql', + 'Ignored.schema.graphql', static function (): void { - $package = Package::Name; - config([ - "{$package}.search_by.operators.Date" => [ - SearchByOperatorBetweenDirective::class, + Package::Name.'.search_by.operators.String' => [ + SearchByOperatorEqualDirective::class, + ], + Package::Name.'.search_by.operators.'.Operators::Extra => [ + // empty ], ]); + + Container::getInstance()->make(TypeRegistry::class)->register( + new class([ + 'name' => 'IgnoredType', + 'fields' => [ + [ + 'name' => 'name', + 'type' => Type::nonNull(Type::string()), + ], + ], + ]) extends InputObjectType implements Ignored { + // empty + }, + ); }, ], - 'only used type should be added' => [ - static function (self $test): GraphQLExpected { - return (new GraphQLExpected( - $test::getTestData()->file('~usedonly-expected.graphql'), - )); + 'Example' => [ + 'Example.expected.graphql', + 'Example.schema.graphql', + static function (): void { + config([ + Package::Name.'.search_by.operators.Date' => [ + SearchByOperatorBetweenDirective::class, + ], + ]); }, - '~usedonly.graphql', - null, ], - 'custom complex operators' => [ - static function (self $test): GraphQLExpected { - return (new GraphQLExpected( - $test::getTestData()->file('~custom-complex-operators-expected.graphql'), - )); - }, - '~custom-complex-operators.graphql', + 'InterfaceUpdate' => [ + 'InterfaceUpdate.expected.graphql', + 'InterfaceUpdate.schema.graphql', static function (): void { - $locator = Container::getInstance()->make(DirectiveLocator::class); - $resolver = Container::getInstance()->make(BuilderPropertyResolver::class); - $directive = new class($resolver) extends BaseOperator implements TypeDefinition { - #[Override] - public static function getName(): string { - return 'custom'; - } - - #[Override] - public function getFieldType( - TypeProvider $provider, - TypeSource $source, - Context $context, - ): string { - return $provider->getType(static::class, $provider->getTypeSource(Type::int()), $context); - } - - #[Override] - public function getFieldDescription(): string { - return 'Custom condition.'; - } - - #[Override] - public static function definition(): string { - return <<<'GRAPHQL' - directive @customComplexOperator(value: String) on INPUT_FIELD_DEFINITION - GRAPHQL; - } - - #[Override] - public function call( - Handler $handler, - object $builder, - Property $property, - Argument $argument, - Context $context, - ): object { - throw new Exception('Should not be called'); - } - - #[Override] - public function getTypeName(TypeSource $source, Context $context): string { - $directiveName = Directive::Name; - $typeName = Str::studly($source->getTypeName()); - - return "{$directiveName}ComplexCustom{$typeName}"; - } - - #[Override] - public function getTypeDefinition( - Manipulator $manipulator, - TypeSource $source, - Context $context, - string $name, - ): TypeDefinitionNode&Node { - return Parser::inputObjectTypeDefinition( - <<getTypeName()} - } - GRAPHQL, - ); - } - }; - - $locator->setResolved('customComplexOperator', $directive::class); + config([ + Package::Name.'.search_by.operators.'.Operators::ID => [ + SearchByOperatorEqualDirective::class, + ], + Package::Name.'.search_by.operators.'.Operators::Extra => [ + // empty + ], + ]); }, ], ]; @@ -886,3 +862,73 @@ public function __invoke(): mixed { throw new Exception('Should not be called.'); } } + +/** + * @internal + * @noinspection PhpMultipleClassesDeclarationsInOneFile + */ +class DirectiveTest__CustomComplexOperator extends BaseOperator implements TypeDefinition { + #[Override] + public static function getName(): string { + return 'custom'; + } + + #[Override] + public function getFieldType( + TypeProvider $provider, + TypeSource $source, + Context $context, + ): string { + return $provider->getType(static::class, $provider->getTypeSource(Type::int()), $context); + } + + #[Override] + public function getFieldDescription(): string { + return 'Custom condition.'; + } + + #[Override] + public static function definition(): string { + return <<<'GRAPHQL' + directive @customComplexOperator(value: String) on INPUT_FIELD_DEFINITION + GRAPHQL; + } + + #[Override] + public function call( + Handler $handler, + object $builder, + Property $property, + Argument $argument, + Context $context, + ): object { + throw new Exception('Should not be called'); + } + + #[Override] + public function getTypeName(TypeSource $source, Context $context): string { + $directiveName = Directive::Name; + $typeName = Str::studly($source->getTypeName()); + + return "{$directiveName}ComplexCustom{$typeName}"; + } + + #[Override] + public function getTypeDefinition( + Manipulator $manipulator, + TypeSource $source, + Context $context, + string $name, + ): TypeDefinitionNode&Node { + return Parser::inputObjectTypeDefinition( + <<getTypeName()} + } + GRAPHQL, + ); + } +} diff --git a/packages/graphql/src/SearchBy/Directives/DirectiveTest/AllowedDirectives.expected.graphql b/packages/graphql/src/SearchBy/Directives/DirectiveTest/AllowedDirectives.expected.graphql new file mode 100644 index 000000000..b2219dfb1 --- /dev/null +++ b/packages/graphql/src/SearchBy/Directives/DirectiveTest/AllowedDirectives.expected.graphql @@ -0,0 +1,85 @@ +""" +Use Input as Search Conditions for the current Builder. +""" +directive @searchBy +on + | ARGUMENT_DEFINITION + +directive @searchByOperatorEqual +on + | ENUM + | INPUT_FIELD_DEFINITION + | SCALAR + +directive @searchByOperatorProperty +on + | ENUM + | INPUT_FIELD_DEFINITION + | SCALAR + +""" +Available conditions for `type A` (only one property allowed at a time). +""" +input SearchByConditionA { + """ + Property condition. + """ + a: SearchByScalarString + @searchByOperatorProperty + @rename( + attribute: "renamed" + ) +} + +""" +Available conditions for `interface B` (only one property allowed at a time). +""" +input SearchByConditionB { + """ + Property condition. + """ + b: SearchByScalarString + @searchByOperatorProperty + @rename( + attribute: "renamed" + ) +} + +""" +Available operators for `scalar String` (only one operator allowed at a time). +""" +input SearchByScalarString { + """ + Equal (`=`). + """ + equal: String + @searchByOperatorEqual +} + +interface B { + b: String! + @rename( + attribute: "renamed" + ) +} + +type A { + a: String! + @rename( + attribute: "renamed" + ) +} + +type Query { + a( + where: SearchByConditionA + @searchBy + ): A! + @all + + b( + where: SearchByConditionB + @searchBy + ): B! + @all +} diff --git a/packages/graphql/src/SearchBy/Directives/DirectiveTest/AllowedDirectives.schema.graphql b/packages/graphql/src/SearchBy/Directives/DirectiveTest/AllowedDirectives.schema.graphql new file mode 100644 index 000000000..e55fedae2 --- /dev/null +++ b/packages/graphql/src/SearchBy/Directives/DirectiveTest/AllowedDirectives.schema.graphql @@ -0,0 +1,12 @@ +type Query { + a(where: A @searchBy): A! @all + b(where: _ @searchBy): B! @all +} + +type A { + a: String! @rename(attribute: "renamed") +} + +interface B { + b: String! @rename(attribute: "renamed") +} diff --git a/packages/graphql/src/SearchBy/Directives/DirectiveTest~custom-complex-operators-expected.graphql b/packages/graphql/src/SearchBy/Directives/DirectiveTest/CustomComplexOperator.expected.graphql similarity index 100% rename from packages/graphql/src/SearchBy/Directives/DirectiveTest~custom-complex-operators-expected.graphql rename to packages/graphql/src/SearchBy/Directives/DirectiveTest/CustomComplexOperator.expected.graphql diff --git a/packages/graphql/src/SearchBy/Directives/DirectiveTest~custom-complex-operators.graphql b/packages/graphql/src/SearchBy/Directives/DirectiveTest/CustomComplexOperator.schema.graphql similarity index 100% rename from packages/graphql/src/SearchBy/Directives/DirectiveTest~custom-complex-operators.graphql rename to packages/graphql/src/SearchBy/Directives/DirectiveTest/CustomComplexOperator.schema.graphql diff --git a/packages/graphql/src/SearchBy/Directives/DirectiveTest~example-expected.graphql b/packages/graphql/src/SearchBy/Directives/DirectiveTest/Example.expected.graphql similarity index 100% rename from packages/graphql/src/SearchBy/Directives/DirectiveTest~example-expected.graphql rename to packages/graphql/src/SearchBy/Directives/DirectiveTest/Example.expected.graphql diff --git a/packages/graphql/src/SearchBy/Directives/DirectiveTest~example.graphql b/packages/graphql/src/SearchBy/Directives/DirectiveTest/Example.schema.graphql similarity index 100% rename from packages/graphql/src/SearchBy/Directives/DirectiveTest~example.graphql rename to packages/graphql/src/SearchBy/Directives/DirectiveTest/Example.schema.graphql diff --git a/packages/graphql/src/SearchBy/Directives/DirectiveTest/Explicit.expected.graphql b/packages/graphql/src/SearchBy/Directives/DirectiveTest/Explicit.expected.graphql new file mode 100644 index 000000000..d90eb25d3 --- /dev/null +++ b/packages/graphql/src/SearchBy/Directives/DirectiveTest/Explicit.expected.graphql @@ -0,0 +1,639 @@ +""" +Use Input as Search Conditions for the current Builder. +""" +directive @searchBy +on + | ARGUMENT_DEFINITION + +directive @searchByOperatorAllOf +on + | ENUM + | INPUT_FIELD_DEFINITION + | SCALAR + +directive @searchByOperatorAnyOf +on + | ENUM + | INPUT_FIELD_DEFINITION + | SCALAR + +directive @searchByOperatorBetween +on + | ENUM + | INPUT_FIELD_DEFINITION + | SCALAR + +directive @searchByOperatorBitwiseAnd +on + | ENUM + | INPUT_FIELD_DEFINITION + | SCALAR + +directive @searchByOperatorBitwiseLeftShift +on + | ENUM + | INPUT_FIELD_DEFINITION + | SCALAR + +directive @searchByOperatorBitwiseOr +on + | ENUM + | INPUT_FIELD_DEFINITION + | SCALAR + +directive @searchByOperatorBitwiseRightShift +on + | ENUM + | INPUT_FIELD_DEFINITION + | SCALAR + +directive @searchByOperatorBitwiseXor +on + | ENUM + | INPUT_FIELD_DEFINITION + | SCALAR + +directive @searchByOperatorContains +on + | ENUM + | INPUT_FIELD_DEFINITION + | SCALAR + +directive @searchByOperatorEndsWith +on + | ENUM + | INPUT_FIELD_DEFINITION + | SCALAR + +directive @searchByOperatorEqual +on + | ENUM + | INPUT_FIELD_DEFINITION + | SCALAR + +directive @searchByOperatorGreaterThan +on + | ENUM + | INPUT_FIELD_DEFINITION + | SCALAR + +directive @searchByOperatorGreaterThanOrEqual +on + | ENUM + | INPUT_FIELD_DEFINITION + | SCALAR + +directive @searchByOperatorIn +on + | ENUM + | INPUT_FIELD_DEFINITION + | SCALAR + +directive @searchByOperatorIsNotNull +on + | ENUM + | INPUT_FIELD_DEFINITION + | SCALAR + +directive @searchByOperatorIsNull +on + | ENUM + | INPUT_FIELD_DEFINITION + | SCALAR + +directive @searchByOperatorLessThan +on + | ENUM + | INPUT_FIELD_DEFINITION + | SCALAR + +directive @searchByOperatorLessThanOrEqual +on + | ENUM + | INPUT_FIELD_DEFINITION + | SCALAR + +directive @searchByOperatorLike +on + | ENUM + | INPUT_FIELD_DEFINITION + | SCALAR + +directive @searchByOperatorNot +on + | ENUM + | INPUT_FIELD_DEFINITION + | SCALAR + +directive @searchByOperatorNotBetween +on + | ENUM + | INPUT_FIELD_DEFINITION + | SCALAR + +directive @searchByOperatorNotContains +on + | ENUM + | INPUT_FIELD_DEFINITION + | SCALAR + +directive @searchByOperatorNotEndsWith +on + | ENUM + | INPUT_FIELD_DEFINITION + | SCALAR + +directive @searchByOperatorNotEqual +on + | ENUM + | INPUT_FIELD_DEFINITION + | SCALAR + +directive @searchByOperatorNotIn +on + | ENUM + | INPUT_FIELD_DEFINITION + | SCALAR + +directive @searchByOperatorNotLike +on + | ENUM + | INPUT_FIELD_DEFINITION + | SCALAR + +directive @searchByOperatorNotStartsWith +on + | ENUM + | INPUT_FIELD_DEFINITION + | SCALAR + +directive @searchByOperatorProperty +on + | ENUM + | INPUT_FIELD_DEFINITION + | SCALAR + +directive @searchByOperatorRelation +on + | ENUM + | FIELD_DEFINITION + | INPUT_FIELD_DEFINITION + | SCALAR + +directive @searchByOperatorStartsWith +on + | ENUM + | INPUT_FIELD_DEFINITION + | SCALAR + +enum SearchByTypeFlag { + Yes + + yes + @deprecated( + reason: "Please use `Yes` instead." + ) +} + +""" +Conditions for the related objects (`has()`/`doesntHave()`) for `input A`. + +See also: +* https://laravel.com/docs/eloquent-relationships#querying-relationship-existence +* https://laravel.com/docs/eloquent-relationships#querying-relationship-absence +""" +input SearchByComplexRelationA { + """ + Count conditions. + """ + count: SearchByScalarInt + + """ + Alias for `count: {greaterThanOrEqual: 1}`. Will be ignored if `count` used. + """ + exists: Boolean + + """ + Alias for `count: {lessThan: 1}`. Will be ignored if `count` used. + """ + notExists: Boolean! = false + + """ + Additional conditions. + """ + where: SearchByConditionA +} + +""" +Conditions for the related objects (`has()`/`doesntHave()`) for `input B`. + +See also: +* https://laravel.com/docs/eloquent-relationships#querying-relationship-existence +* https://laravel.com/docs/eloquent-relationships#querying-relationship-absence +""" +input SearchByComplexRelationB { + """ + Count conditions. + """ + count: SearchByScalarInt + + """ + Alias for `count: {greaterThanOrEqual: 1}`. Will be ignored if `count` used. + """ + exists: Boolean + + """ + Alias for `count: {lessThan: 1}`. Will be ignored if `count` used. + """ + notExists: Boolean! = false + + """ + Additional conditions. + """ + where: SearchByConditionB +} + +""" +Available conditions for `input A` (only one property allowed at a time). +""" +input SearchByConditionA { + """ + All of the conditions must be true. + """ + allOf: [SearchByConditionA!] + @searchByOperatorAllOf + + """ + Any of the conditions must be true. + """ + anyOf: [SearchByConditionA!] + @searchByOperatorAnyOf + + """ + Relationship condition. + """ + children: SearchByComplexRelationB + @searchByOperatorRelation + + """ + Description should be copied. + """ + id: SearchByScalarID + @searchByOperatorProperty + + """ + Property condition. + """ + name: SearchByScalarString + @searchByOperatorProperty + + """ + Not. + """ + not: SearchByConditionA + @searchByOperatorNot +} + +""" +Available conditions for `input B` (only one property allowed at a time). +""" +input SearchByConditionB { + """ + All of the conditions must be true. + """ + allOf: [SearchByConditionB!] + @searchByOperatorAllOf + + """ + Any of the conditions must be true. + """ + anyOf: [SearchByConditionB!] + @searchByOperatorAnyOf + + """ + Description should be copied. + """ + id: SearchByScalarID + @searchByOperatorProperty + + """ + Property condition. + """ + name: SearchByScalarStringOrNull + @searchByOperatorProperty + + """ + Not. + """ + not: SearchByConditionB + @searchByOperatorNot + + """ + Relationship condition. + """ + parent: SearchByComplexRelationA + @searchByOperatorRelation +} + +""" +Available operators for `scalar ID` (only one operator allowed at a time). +""" +input SearchByScalarID { + """ + Equal (`=`). + """ + equal: ID + @searchByOperatorEqual + + """ + Within a set of values. + """ + in: [ID!] + @searchByOperatorIn + + """ + Not Equal (`!=`). + """ + notEqual: ID + @searchByOperatorNotEqual + + """ + Outside a set of values. + """ + notIn: [ID!] + @searchByOperatorNotIn +} + +""" +Available operators for `scalar Int` (only one operator allowed at a time). +""" +input SearchByScalarInt { + """ + Within a range. + """ + between: SearchByTypeRangeInt + @searchByOperatorBetween + + """ + Bitwise AND (`&`). + """ + bitwiseAnd: Int + @searchByOperatorBitwiseAnd + + """ + Bitwise Left shift (`<<`). + """ + bitwiseLeftShift: Int + @searchByOperatorBitwiseLeftShift + + """ + Bitwise OR (`|`). + """ + bitwiseOr: Int + @searchByOperatorBitwiseOr + + """ + Bitwise Right shift (`>>`). + """ + bitwiseRightShift: Int + @searchByOperatorBitwiseRightShift + + """ + Bitwise XOR (`^`). + """ + bitwiseXor: Int + @searchByOperatorBitwiseXor + + """ + Equal (`=`). + """ + equal: Int + @searchByOperatorEqual + + """ + Greater than (`>`). + """ + greaterThan: Int + @searchByOperatorGreaterThan + + """ + Greater than or equal to (`>=`). + """ + greaterThanOrEqual: Int + @searchByOperatorGreaterThanOrEqual + + """ + Within a set of values. + """ + in: [Int!] + @searchByOperatorIn + + """ + Less than (`<`). + """ + lessThan: Int + @searchByOperatorLessThan + + """ + Less than or equal to (`<=`). + """ + lessThanOrEqual: Int + @searchByOperatorLessThanOrEqual + + """ + Outside a range. + """ + notBetween: SearchByTypeRangeInt + @searchByOperatorNotBetween + + """ + Not Equal (`!=`). + """ + notEqual: Int + @searchByOperatorNotEqual + + """ + Outside a set of values. + """ + notIn: [Int!] + @searchByOperatorNotIn +} + +""" +Available operators for `scalar String` (only one operator allowed at a time). +""" +input SearchByScalarString { + """ + Contains. + """ + contains: String + @searchByOperatorContains + + """ + Ends with a string. + """ + endsWith: String + @searchByOperatorEndsWith + + """ + Equal (`=`). + """ + equal: String + @searchByOperatorEqual + + """ + Within a set of values. + """ + in: [String!] + @searchByOperatorIn + + """ + Like. + """ + like: String + @searchByOperatorLike + + """ + Not contains. + """ + notContains: String + @searchByOperatorNotContains + + """ + Not ends with a string. + """ + notEndsWith: String + @searchByOperatorNotEndsWith + + """ + Not Equal (`!=`). + """ + notEqual: String + @searchByOperatorNotEqual + + """ + Outside a set of values. + """ + notIn: [String!] + @searchByOperatorNotIn + + """ + Not like. + """ + notLike: String + @searchByOperatorNotLike + + """ + Not starts with a string. + """ + notStartsWith: String + @searchByOperatorNotStartsWith + + """ + Starts with a string. + """ + startsWith: String + @searchByOperatorStartsWith +} + +""" +Available operators for `scalar String` (only one operator allowed at a time). +""" +input SearchByScalarStringOrNull { + """ + Contains. + """ + contains: String + @searchByOperatorContains + + """ + Ends with a string. + """ + endsWith: String + @searchByOperatorEndsWith + + """ + Equal (`=`). + """ + equal: String + @searchByOperatorEqual + + """ + Within a set of values. + """ + in: [String!] + @searchByOperatorIn + + """ + Is NOT NULL? + """ + isNotNull: SearchByTypeFlag + @searchByOperatorIsNotNull + + """ + Is NULL? + """ + isNull: SearchByTypeFlag + @searchByOperatorIsNull + + """ + Like. + """ + like: String + @searchByOperatorLike + + """ + Not contains. + """ + notContains: String + @searchByOperatorNotContains + + """ + Not ends with a string. + """ + notEndsWith: String + @searchByOperatorNotEndsWith + + """ + Not Equal (`!=`). + """ + notEqual: String + @searchByOperatorNotEqual + + """ + Outside a set of values. + """ + notIn: [String!] + @searchByOperatorNotIn + + """ + Not like. + """ + notLike: String + @searchByOperatorNotLike + + """ + Not starts with a string. + """ + notStartsWith: String + @searchByOperatorNotStartsWith + + """ + Starts with a string. + """ + startsWith: String + @searchByOperatorStartsWith +} + +input SearchByTypeRangeInt { + max: Int! + min: Int! +} + +type Query { + a( + where: SearchByConditionA + @searchBy + ): ID! + @all +} diff --git a/packages/graphql/src/SearchBy/Directives/DirectiveTest/Explicit.schema.graphql b/packages/graphql/src/SearchBy/Directives/DirectiveTest/Explicit.schema.graphql new file mode 100644 index 000000000..874661ffe --- /dev/null +++ b/packages/graphql/src/SearchBy/Directives/DirectiveTest/Explicit.schema.graphql @@ -0,0 +1,17 @@ +type Query { + a(where: A @searchBy): ID! @all +} + +input A { + "Description should be copied." + id: ID! + name: String! + children: [B!]! +} + +input B { + "Description should be copied." + id: ID! + name: String + parent: A! +} diff --git a/packages/graphql/src/SearchBy/Directives/DirectiveTest/Ignored.expected.graphql b/packages/graphql/src/SearchBy/Directives/DirectiveTest/Ignored.expected.graphql new file mode 100644 index 000000000..e68bf962c --- /dev/null +++ b/packages/graphql/src/SearchBy/Directives/DirectiveTest/Ignored.expected.graphql @@ -0,0 +1,187 @@ +""" +Use Input as Search Conditions for the current Builder. +""" +directive @searchBy +on + | ARGUMENT_DEFINITION + +""" +Marks that field/definition should be excluded from search. +""" +directive @searchByIgnored +on + | ENUM + | FIELD_DEFINITION + | INPUT_FIELD_DEFINITION + | INPUT_OBJECT + | OBJECT + | SCALAR + +directive @searchByOperatorEqual +on + | ENUM + | INPUT_FIELD_DEFINITION + | SCALAR + +directive @searchByOperatorProperty +on + | ENUM + | INPUT_FIELD_DEFINITION + | SCALAR + +enum IgnoredEnum +@searchByIgnored +{ + One +} + +input A { + """ + Not ignored + """ + a: String! + + b: [String!]! + + """ + Marked by @searchByIgnored + """ + c: IgnoredInput! + + d: IgnoredDate! + e: [IgnoredDate!]! + f: IgnoredEnum! + + g: String + @searchByIgnored +} + +input IgnoredInput +@searchByIgnored +{ + id: ID +} + +input IgnoredType { + name: String! +} + +""" +Available conditions for `input A` (only one property allowed at a time). +""" +input SearchByConditionA { + """ + Not ignored + """ + a: SearchByScalarString + @searchByOperatorProperty + + """ + Property condition. + """ + b: SearchByScalarString + @searchByOperatorProperty +} + +""" +Available conditions for `interface B` (only one property allowed at a time). +""" +input SearchByConditionB { + """ + Property condition. + """ + a: SearchByScalarString + @searchByOperatorProperty + + """ + Property condition. + """ + b: SearchByScalarString + @searchByOperatorProperty +} + +""" +Available operators for `scalar String` (only one operator allowed at a time). +""" +input SearchByScalarString { + """ + Equal (`=`). + """ + equal: String + @searchByOperatorEqual +} + +interface B { + """ + Not ignored + """ + a: String! + + b: [String!]! + + """ + Marked by @searchByIgnored + """ + c: IgnoredType! + + d: IgnoredDate! + e: [IgnoredDate!]! + f: IgnoredEnum! + + g: String + @searchByIgnored + + """ + Resolver + """ + h: Float + @field( + resolver: "\\LastDragon_ru\\LaraASP\\GraphQL\\SearchBy\\Directives\\DirectiveTest__Resolver" + ) + + """ + Arguments + """ + i( + arg: String + ): Int! + + """ + Union + """ + j: ObjectUnion + + k: [ObjectUnion!] +} + +scalar IgnoredDate +@scalar( + class: "Nuwave\\Lighthouse\\Schema\\Types\\Scalars\\Date" +) +@searchByIgnored + +type ObjectA { + id: ID! +} + +type ObjectB { + id: ID! +} + +type Query { + a( + where: SearchByConditionA + @searchBy + ): A! + @all + + b( + where: SearchByConditionB + @searchBy + ): B! + @all +} + +union ObjectUnion = + | ObjectA + | ObjectB diff --git a/packages/graphql/src/SearchBy/Directives/DirectiveTest/Ignored.schema.graphql b/packages/graphql/src/SearchBy/Directives/DirectiveTest/Ignored.schema.graphql new file mode 100644 index 000000000..400c52a4e --- /dev/null +++ b/packages/graphql/src/SearchBy/Directives/DirectiveTest/Ignored.schema.graphql @@ -0,0 +1,64 @@ +type Query { + a(where: A @searchBy): A! @all + b(where: _ @searchBy): B! @all +} + +input A { + "Not ignored" + a: String! + b: [String!]! + + "Marked by @searchByIgnored" + c: IgnoredInput! + d: IgnoredDate! + e: [IgnoredDate!]! + f: IgnoredEnum! + g: String @searchByIgnored +} + +interface B { + "Not ignored" + a: String! + b: [String!]! + + "Marked by @searchByIgnored" + c: IgnoredType! + d: IgnoredDate! + e: [IgnoredDate!]! + f: IgnoredEnum! + g: String @searchByIgnored + + "Resolver" + h: Float @field( + resolver: "\\LastDragon_ru\\LaraASP\\GraphQL\\SearchBy\\Directives\\DirectiveTest__Resolver" + ) + + "Arguments" + i(arg: String): Int! + + "Union" + j: ObjectUnion + k: [ObjectUnion!] +} + +input IgnoredInput @searchByIgnored { + id: ID +} + +scalar IgnoredDate +@scalar(class: "Nuwave\\Lighthouse\\Schema\\Types\\Scalars\\Date") +@searchByIgnored + +enum IgnoredEnum @searchByIgnored { + One +} + +union ObjectUnion = ObjectA | ObjectB + +type ObjectA { + id: ID! +} + +type ObjectB { + id: ID! +} diff --git a/packages/graphql/src/SearchBy/Directives/DirectiveTest/Implicit.expected.graphql b/packages/graphql/src/SearchBy/Directives/DirectiveTest/Implicit.expected.graphql new file mode 100644 index 000000000..764b17bf9 --- /dev/null +++ b/packages/graphql/src/SearchBy/Directives/DirectiveTest/Implicit.expected.graphql @@ -0,0 +1,660 @@ +""" +Use Input as Search Conditions for the current Builder. +""" +directive @searchBy +on + | ARGUMENT_DEFINITION + +directive @searchByOperatorAllOf +on + | ENUM + | INPUT_FIELD_DEFINITION + | SCALAR + +directive @searchByOperatorAnyOf +on + | ENUM + | INPUT_FIELD_DEFINITION + | SCALAR + +directive @searchByOperatorBetween +on + | ENUM + | INPUT_FIELD_DEFINITION + | SCALAR + +directive @searchByOperatorBitwiseAnd +on + | ENUM + | INPUT_FIELD_DEFINITION + | SCALAR + +directive @searchByOperatorBitwiseLeftShift +on + | ENUM + | INPUT_FIELD_DEFINITION + | SCALAR + +directive @searchByOperatorBitwiseOr +on + | ENUM + | INPUT_FIELD_DEFINITION + | SCALAR + +directive @searchByOperatorBitwiseRightShift +on + | ENUM + | INPUT_FIELD_DEFINITION + | SCALAR + +directive @searchByOperatorBitwiseXor +on + | ENUM + | INPUT_FIELD_DEFINITION + | SCALAR + +directive @searchByOperatorContains +on + | ENUM + | INPUT_FIELD_DEFINITION + | SCALAR + +directive @searchByOperatorEndsWith +on + | ENUM + | INPUT_FIELD_DEFINITION + | SCALAR + +directive @searchByOperatorEqual +on + | ENUM + | INPUT_FIELD_DEFINITION + | SCALAR + +directive @searchByOperatorGreaterThan +on + | ENUM + | INPUT_FIELD_DEFINITION + | SCALAR + +directive @searchByOperatorGreaterThanOrEqual +on + | ENUM + | INPUT_FIELD_DEFINITION + | SCALAR + +directive @searchByOperatorIn +on + | ENUM + | INPUT_FIELD_DEFINITION + | SCALAR + +directive @searchByOperatorIsNotNull +on + | ENUM + | INPUT_FIELD_DEFINITION + | SCALAR + +directive @searchByOperatorIsNull +on + | ENUM + | INPUT_FIELD_DEFINITION + | SCALAR + +directive @searchByOperatorLessThan +on + | ENUM + | INPUT_FIELD_DEFINITION + | SCALAR + +directive @searchByOperatorLessThanOrEqual +on + | ENUM + | INPUT_FIELD_DEFINITION + | SCALAR + +directive @searchByOperatorLike +on + | ENUM + | INPUT_FIELD_DEFINITION + | SCALAR + +directive @searchByOperatorNot +on + | ENUM + | INPUT_FIELD_DEFINITION + | SCALAR + +directive @searchByOperatorNotBetween +on + | ENUM + | INPUT_FIELD_DEFINITION + | SCALAR + +directive @searchByOperatorNotContains +on + | ENUM + | INPUT_FIELD_DEFINITION + | SCALAR + +directive @searchByOperatorNotEndsWith +on + | ENUM + | INPUT_FIELD_DEFINITION + | SCALAR + +directive @searchByOperatorNotEqual +on + | ENUM + | INPUT_FIELD_DEFINITION + | SCALAR + +directive @searchByOperatorNotIn +on + | ENUM + | INPUT_FIELD_DEFINITION + | SCALAR + +directive @searchByOperatorNotLike +on + | ENUM + | INPUT_FIELD_DEFINITION + | SCALAR + +directive @searchByOperatorNotStartsWith +on + | ENUM + | INPUT_FIELD_DEFINITION + | SCALAR + +directive @searchByOperatorProperty +on + | ENUM + | INPUT_FIELD_DEFINITION + | SCALAR + +directive @searchByOperatorRelation +on + | ENUM + | FIELD_DEFINITION + | INPUT_FIELD_DEFINITION + | SCALAR + +directive @searchByOperatorStartsWith +on + | ENUM + | INPUT_FIELD_DEFINITION + | SCALAR + +enum SearchByTypeFlag { + Yes + + yes + @deprecated( + reason: "Please use `Yes` instead." + ) +} + +""" +Conditions for the related objects (`has()`/`doesntHave()`) for `type A`. + +See also: +* https://laravel.com/docs/eloquent-relationships#querying-relationship-existence +* https://laravel.com/docs/eloquent-relationships#querying-relationship-absence +""" +input SearchByComplexRelationA { + """ + Count conditions. + """ + count: SearchByScalarInt + + """ + Alias for `count: {greaterThanOrEqual: 1}`. Will be ignored if `count` used. + """ + exists: Boolean + + """ + Alias for `count: {lessThan: 1}`. Will be ignored if `count` used. + """ + notExists: Boolean! = false + + """ + Additional conditions. + """ + where: SearchByConditionA +} + +""" +Conditions for the related objects (`has()`/`doesntHave()`) for `type B`. + +See also: +* https://laravel.com/docs/eloquent-relationships#querying-relationship-existence +* https://laravel.com/docs/eloquent-relationships#querying-relationship-absence +""" +input SearchByComplexRelationB { + """ + Count conditions. + """ + count: SearchByScalarInt + + """ + Alias for `count: {greaterThanOrEqual: 1}`. Will be ignored if `count` used. + """ + exists: Boolean + + """ + Alias for `count: {lessThan: 1}`. Will be ignored if `count` used. + """ + notExists: Boolean! = false + + """ + Additional conditions. + """ + where: SearchByConditionB +} + +""" +Available conditions for `type A` (only one property allowed at a time). +""" +input SearchByConditionA { + """ + All of the conditions must be true. + """ + allOf: [SearchByConditionA!] + @searchByOperatorAllOf + + """ + Any of the conditions must be true. + """ + anyOf: [SearchByConditionA!] + @searchByOperatorAnyOf + + """ + Relationship condition. + """ + children: SearchByComplexRelationB + @searchByOperatorRelation + + """ + Property condition. + """ + id: SearchByScalarID + @searchByOperatorProperty + + """ + Property condition. + """ + name: SearchByScalarString + @searchByOperatorProperty + + """ + Not. + """ + not: SearchByConditionA + @searchByOperatorNot +} + +""" +Available conditions for `type B` (only one property allowed at a time). +""" +input SearchByConditionB { + """ + All of the conditions must be true. + """ + allOf: [SearchByConditionB!] + @searchByOperatorAllOf + + """ + Any of the conditions must be true. + """ + anyOf: [SearchByConditionB!] + @searchByOperatorAnyOf + + """ + Property condition. + """ + id: SearchByScalarID + @searchByOperatorProperty + + """ + Property condition. + """ + name: SearchByScalarStringOrNull + @searchByOperatorProperty + + """ + Not. + """ + not: SearchByConditionB + @searchByOperatorNot + + """ + Relationship condition. + """ + parent: SearchByComplexRelationA + @searchByOperatorRelation +} + +""" +Available operators for `scalar ID` (only one operator allowed at a time). +""" +input SearchByScalarID { + """ + Equal (`=`). + """ + equal: ID + @searchByOperatorEqual + + """ + Within a set of values. + """ + in: [ID!] + @searchByOperatorIn + + """ + Not Equal (`!=`). + """ + notEqual: ID + @searchByOperatorNotEqual + + """ + Outside a set of values. + """ + notIn: [ID!] + @searchByOperatorNotIn +} + +""" +Available operators for `scalar Int` (only one operator allowed at a time). +""" +input SearchByScalarInt { + """ + Within a range. + """ + between: SearchByTypeRangeInt + @searchByOperatorBetween + + """ + Bitwise AND (`&`). + """ + bitwiseAnd: Int + @searchByOperatorBitwiseAnd + + """ + Bitwise Left shift (`<<`). + """ + bitwiseLeftShift: Int + @searchByOperatorBitwiseLeftShift + + """ + Bitwise OR (`|`). + """ + bitwiseOr: Int + @searchByOperatorBitwiseOr + + """ + Bitwise Right shift (`>>`). + """ + bitwiseRightShift: Int + @searchByOperatorBitwiseRightShift + + """ + Bitwise XOR (`^`). + """ + bitwiseXor: Int + @searchByOperatorBitwiseXor + + """ + Equal (`=`). + """ + equal: Int + @searchByOperatorEqual + + """ + Greater than (`>`). + """ + greaterThan: Int + @searchByOperatorGreaterThan + + """ + Greater than or equal to (`>=`). + """ + greaterThanOrEqual: Int + @searchByOperatorGreaterThanOrEqual + + """ + Within a set of values. + """ + in: [Int!] + @searchByOperatorIn + + """ + Less than (`<`). + """ + lessThan: Int + @searchByOperatorLessThan + + """ + Less than or equal to (`<=`). + """ + lessThanOrEqual: Int + @searchByOperatorLessThanOrEqual + + """ + Outside a range. + """ + notBetween: SearchByTypeRangeInt + @searchByOperatorNotBetween + + """ + Not Equal (`!=`). + """ + notEqual: Int + @searchByOperatorNotEqual + + """ + Outside a set of values. + """ + notIn: [Int!] + @searchByOperatorNotIn +} + +""" +Available operators for `scalar String` (only one operator allowed at a time). +""" +input SearchByScalarString { + """ + Contains. + """ + contains: String + @searchByOperatorContains + + """ + Ends with a string. + """ + endsWith: String + @searchByOperatorEndsWith + + """ + Equal (`=`). + """ + equal: String + @searchByOperatorEqual + + """ + Within a set of values. + """ + in: [String!] + @searchByOperatorIn + + """ + Like. + """ + like: String + @searchByOperatorLike + + """ + Not contains. + """ + notContains: String + @searchByOperatorNotContains + + """ + Not ends with a string. + """ + notEndsWith: String + @searchByOperatorNotEndsWith + + """ + Not Equal (`!=`). + """ + notEqual: String + @searchByOperatorNotEqual + + """ + Outside a set of values. + """ + notIn: [String!] + @searchByOperatorNotIn + + """ + Not like. + """ + notLike: String + @searchByOperatorNotLike + + """ + Not starts with a string. + """ + notStartsWith: String + @searchByOperatorNotStartsWith + + """ + Starts with a string. + """ + startsWith: String + @searchByOperatorStartsWith +} + +""" +Available operators for `scalar String` (only one operator allowed at a time). +""" +input SearchByScalarStringOrNull { + """ + Contains. + """ + contains: String + @searchByOperatorContains + + """ + Ends with a string. + """ + endsWith: String + @searchByOperatorEndsWith + + """ + Equal (`=`). + """ + equal: String + @searchByOperatorEqual + + """ + Within a set of values. + """ + in: [String!] + @searchByOperatorIn + + """ + Is NOT NULL? + """ + isNotNull: SearchByTypeFlag + @searchByOperatorIsNotNull + + """ + Is NULL? + """ + isNull: SearchByTypeFlag + @searchByOperatorIsNull + + """ + Like. + """ + like: String + @searchByOperatorLike + + """ + Not contains. + """ + notContains: String + @searchByOperatorNotContains + + """ + Not ends with a string. + """ + notEndsWith: String + @searchByOperatorNotEndsWith + + """ + Not Equal (`!=`). + """ + notEqual: String + @searchByOperatorNotEqual + + """ + Outside a set of values. + """ + notIn: [String!] + @searchByOperatorNotIn + + """ + Not like. + """ + notLike: String + @searchByOperatorNotLike + + """ + Not starts with a string. + """ + notStartsWith: String + @searchByOperatorNotStartsWith + + """ + Starts with a string. + """ + startsWith: String + @searchByOperatorStartsWith +} + +input SearchByTypeRangeInt { + max: Int! + min: Int! +} + +type A { + children: [B!]! + + """ + Description should be ignored. + """ + id: ID! + + name: String! +} + +type B { + """ + Description should be ignored. + """ + id: ID! + + name: String + parent: A! +} + +type Query { + a( + where: SearchByConditionA + @searchBy + ): [A!]! + @all +} diff --git a/packages/graphql/src/SearchBy/Directives/DirectiveTest/Implicit.schema.graphql b/packages/graphql/src/SearchBy/Directives/DirectiveTest/Implicit.schema.graphql new file mode 100644 index 000000000..60c706bc3 --- /dev/null +++ b/packages/graphql/src/SearchBy/Directives/DirectiveTest/Implicit.schema.graphql @@ -0,0 +1,17 @@ +type Query { + a(where: _ @searchBy): [A!]! @all +} + +type A { + "Description should be ignored." + id: ID! + name: String! + children: [B!]! +} + +type B { + "Description should be ignored." + id: ID! + name: String + parent: A! +} diff --git a/packages/graphql/src/SearchBy/Directives/DirectiveTest/InterfaceUpdate.expected.graphql b/packages/graphql/src/SearchBy/Directives/DirectiveTest/InterfaceUpdate.expected.graphql new file mode 100644 index 000000000..ede9c74df --- /dev/null +++ b/packages/graphql/src/SearchBy/Directives/DirectiveTest/InterfaceUpdate.expected.graphql @@ -0,0 +1,102 @@ +""" +Use Input as Search Conditions for the current Builder. +""" +directive @searchBy +on + | ARGUMENT_DEFINITION + +directive @searchByOperatorEqual +on + | ENUM + | INPUT_FIELD_DEFINITION + | SCALAR + +directive @searchByOperatorProperty +on + | ENUM + | INPUT_FIELD_DEFINITION + | SCALAR + +input A { + id: ID! +} + +""" +Available conditions for `input A` (only one property allowed at a time). +""" +input SearchByConditionA { + """ + Property condition. + """ + id: SearchByScalarID + @searchByOperatorProperty +} + +""" +Available conditions for `type B` (only one property allowed at a time). +""" +input SearchByConditionB { + """ + Property condition. + """ + id: SearchByScalarID + @searchByOperatorProperty +} + +""" +Available operators for `scalar ID` (only one operator allowed at a time). +""" +input SearchByScalarID { + """ + Equal (`=`). + """ + equal: ID + @searchByOperatorEqual +} + +interface QueryInterface +implements + & QueryInterfaceA + & QueryInterfaceB +{ + a( + where: SearchByConditionA + ): A! + + b( + where: SearchByConditionB + ): B! +} + +interface QueryInterfaceA { + a( + where: SearchByConditionA + ): A! +} + +interface QueryInterfaceB { + b( + where: SearchByConditionB + ): B! +} + +type B { + id: ID! +} + +type Query +implements + & QueryInterface +{ + a( + where: SearchByConditionA + @searchBy + ): A! + @all + + b( + where: SearchByConditionB + @searchBy + ): B! + @all +} diff --git a/packages/graphql/src/SearchBy/Directives/DirectiveTest/InterfaceUpdate.schema.graphql b/packages/graphql/src/SearchBy/Directives/DirectiveTest/InterfaceUpdate.schema.graphql new file mode 100644 index 000000000..359d7ef5b --- /dev/null +++ b/packages/graphql/src/SearchBy/Directives/DirectiveTest/InterfaceUpdate.schema.graphql @@ -0,0 +1,25 @@ +type Query implements QueryInterface { + a(where: A @searchBy): A! @all + b(where: _ @searchBy): B! @all +} + +input A { + id: ID! +} + +type B { + id: ID! +} + +interface QueryInterface implements QueryInterfaceA & QueryInterfaceB { + a(where: A): A! + b(where: _): B! +} + +interface QueryInterfaceA { + a(where: A): A! +} + +interface QueryInterfaceB { + b(where: _): B! +} diff --git a/packages/graphql/src/SearchBy/Directives/DirectiveTest/ScalarOperators.expected.graphql b/packages/graphql/src/SearchBy/Directives/DirectiveTest/ScalarOperators.expected.graphql new file mode 100644 index 000000000..8e2ce73b3 --- /dev/null +++ b/packages/graphql/src/SearchBy/Directives/DirectiveTest/ScalarOperators.expected.graphql @@ -0,0 +1,218 @@ +""" +Use Input as Search Conditions for the current Builder. +""" +directive @searchBy +on + | ARGUMENT_DEFINITION + +directive @searchByOperatorAllOf +on + | ENUM + | INPUT_FIELD_DEFINITION + | SCALAR + +directive @searchByOperatorEqual +on + | ENUM + | INPUT_FIELD_DEFINITION + | SCALAR + +directive @searchByOperatorIn +on + | ENUM + | INPUT_FIELD_DEFINITION + | SCALAR + +directive @searchByOperatorIsNotNull +on + | ENUM + | INPUT_FIELD_DEFINITION + | SCALAR + +directive @searchByOperatorIsNull +on + | ENUM + | INPUT_FIELD_DEFINITION + | SCALAR + +directive @searchByOperatorLessThan +on + | ENUM + | INPUT_FIELD_DEFINITION + | SCALAR + +directive @searchByOperatorLessThanOrEqual +on + | ENUM + | INPUT_FIELD_DEFINITION + | SCALAR + +directive @searchByOperatorNotEqual +on + | ENUM + | INPUT_FIELD_DEFINITION + | SCALAR + +directive @searchByOperatorProperty +on + | ENUM + | INPUT_FIELD_DEFINITION + | SCALAR + +""" +Extends the list of operators by the operators from the specified `type`. +""" +directive @searchByOperators( + type: String! +) +on + | ENUM + | SCALAR + +enum EnumA +@searchByOperatorIn +{ + A +} + +enum SearchByTypeFlag { + Yes + + yes + @deprecated( + reason: "Please use `Yes` instead." + ) +} + +""" +Available conditions for `input A` (only one property allowed at a time). +""" +input SearchByConditionA { + """ + Property condition. + """ + a: SearchByScalarMixed + @searchByOperatorProperty + + """ + All of the conditions must be true. + """ + allOf: [SearchByConditionA!] + @searchByOperatorAllOf + + """ + Property condition. + """ + b: SearchByScalarDateOrNull + @searchByOperatorProperty + + """ + Property condition. + """ + c: SearchByEnumEnumAOrNull + @searchByOperatorProperty +} + +""" +Available operators for `enum EnumA` (only one operator allowed at a time). +""" +input SearchByEnumEnumAOrNull { + """ + Within a set of values. + """ + in: [EnumA!] + @searchByOperatorIn + + """ + Is NOT NULL? + """ + isNotNull: SearchByTypeFlag + @searchByOperatorIsNotNull + + """ + Is NULL? + """ + isNull: SearchByTypeFlag + @searchByOperatorIsNull +} + +""" +Available operators for `scalar Date` (only one operator allowed at a time). +""" +input SearchByScalarDateOrNull { + """ + Equal (`=`). + """ + equal: Date + @searchByOperatorEqual + + """ + Is NOT NULL? + """ + isNotNull: SearchByTypeFlag + @searchByOperatorIsNotNull + + """ + Is NULL? + """ + isNull: SearchByTypeFlag + @searchByOperatorIsNull + + """ + Less than (`<`). + """ + lessThan: Date + @searchByOperatorLessThan + + """ + Less than or equal to (`<=`). + """ + lessThanOrEqual: Date + @searchByOperatorLessThanOrEqual + + """ + Not Equal (`!=`). + """ + notEqual: Date + @searchByOperatorNotEqual +} + +""" +Available operators for `scalar Mixed` (only one operator allowed at a time). +""" +input SearchByScalarMixed { + """ + Equal (`=`). + """ + equal: Mixed + @searchByOperatorEqual +} + +scalar Date +@scalar( + class: "Nuwave\\Lighthouse\\Schema\\Types\\Scalars\\Date" +) +@searchByOperators( + type: "Boolean" +) +@searchByOperatorLessThan +@searchByOperatorLessThanOrEqual + +""" +The `String` scalar type represents textual data, represented as UTF-8 +character sequences. The String type is most often used by GraphQL to +represent free-form human-readable text. +""" +scalar Mixed +@scalar( + class: "GraphQL\\Type\\Definition\\StringType" +) +@searchByOperatorEqual + +type Query { + test( + where: SearchByConditionA + @searchBy + ): ID! + @all +} diff --git a/packages/graphql/src/SearchBy/Directives/DirectiveTest/ScalarOperators.schema.graphql b/packages/graphql/src/SearchBy/Directives/DirectiveTest/ScalarOperators.schema.graphql new file mode 100644 index 000000000..a5edc3ddf --- /dev/null +++ b/packages/graphql/src/SearchBy/Directives/DirectiveTest/ScalarOperators.schema.graphql @@ -0,0 +1,28 @@ +type Query { + test(where: A @searchBy): ID! @all +} + +input A { + a: Mixed! + b: Date + c: EnumA +} + +scalar Date +@scalar(class: "Nuwave\\Lighthouse\\Schema\\Types\\Scalars\\Date") +@searchByOperators(type: "Boolean") +@searchByOperatorLessThan +@searchByOperatorLessThanOrEqual + +enum EnumA +@searchByOperatorIn +{ + A +} + +scalar SearchByExtra +@searchByOperatorAllOf + +scalar Mixed +@scalar(class: "GraphQL\\Type\\Definition\\StringType") +@searchByOperatorEqual diff --git a/packages/graphql/src/SearchBy/Directives/DirectiveTest~scout-expected.graphql b/packages/graphql/src/SearchBy/Directives/DirectiveTest/Scout.expected.graphql similarity index 100% rename from packages/graphql/src/SearchBy/Directives/DirectiveTest~scout-expected.graphql rename to packages/graphql/src/SearchBy/Directives/DirectiveTest/Scout.expected.graphql diff --git a/packages/graphql/src/SearchBy/Directives/DirectiveTest~scout-v10.3.0-expected.graphql b/packages/graphql/src/SearchBy/Directives/DirectiveTest/Scout.expected.v10.3.0.graphql similarity index 100% rename from packages/graphql/src/SearchBy/Directives/DirectiveTest~scout-v10.3.0-expected.graphql rename to packages/graphql/src/SearchBy/Directives/DirectiveTest/Scout.expected.v10.3.0.graphql diff --git a/packages/graphql/src/SearchBy/Directives/DirectiveTest~scout.graphql b/packages/graphql/src/SearchBy/Directives/DirectiveTest/Scout.schema.graphql similarity index 100% rename from packages/graphql/src/SearchBy/Directives/DirectiveTest~scout.graphql rename to packages/graphql/src/SearchBy/Directives/DirectiveTest/Scout.schema.graphql diff --git a/packages/graphql/src/SearchBy/Directives/DirectiveTest~programmatically-expected.graphql b/packages/graphql/src/SearchBy/Directives/DirectiveTest/TypeRegistry.expected.graphql similarity index 100% rename from packages/graphql/src/SearchBy/Directives/DirectiveTest~programmatically-expected.graphql rename to packages/graphql/src/SearchBy/Directives/DirectiveTest/TypeRegistry.expected.graphql diff --git a/packages/graphql/src/SearchBy/Directives/DirectiveTest~programmatically.graphql b/packages/graphql/src/SearchBy/Directives/DirectiveTest/TypeRegistry.schema.graphql similarity index 100% rename from packages/graphql/src/SearchBy/Directives/DirectiveTest~programmatically.graphql rename to packages/graphql/src/SearchBy/Directives/DirectiveTest/TypeRegistry.schema.graphql diff --git a/packages/graphql/src/SearchBy/Directives/DirectiveTest~full-expected.graphql b/packages/graphql/src/SearchBy/Directives/DirectiveTest~full-expected.graphql deleted file mode 100644 index f6297f3c3..000000000 --- a/packages/graphql/src/SearchBy/Directives/DirectiveTest~full-expected.graphql +++ /dev/null @@ -1,1954 +0,0 @@ -""" -Use Input as Search Conditions for the current Builder. -""" -directive @searchBy -on - | ARGUMENT_DEFINITION - -""" -Marks that field/definition should be excluded from search. -""" -directive @searchByIgnored -on - | ENUM - | FIELD_DEFINITION - | INPUT_FIELD_DEFINITION - | INPUT_OBJECT - | OBJECT - | SCALAR - -directive @searchByOperatorAllOf -on - | ENUM - | INPUT_FIELD_DEFINITION - | SCALAR - -directive @searchByOperatorAnyOf -on - | ENUM - | INPUT_FIELD_DEFINITION - | SCALAR - -directive @searchByOperatorBetween -on - | ENUM - | INPUT_FIELD_DEFINITION - | SCALAR - -directive @searchByOperatorBitwiseAnd -on - | ENUM - | INPUT_FIELD_DEFINITION - | SCALAR - -directive @searchByOperatorBitwiseLeftShift -on - | ENUM - | INPUT_FIELD_DEFINITION - | SCALAR - -directive @searchByOperatorBitwiseOr -on - | ENUM - | INPUT_FIELD_DEFINITION - | SCALAR - -directive @searchByOperatorBitwiseRightShift -on - | ENUM - | INPUT_FIELD_DEFINITION - | SCALAR - -directive @searchByOperatorBitwiseXor -on - | ENUM - | INPUT_FIELD_DEFINITION - | SCALAR - -directive @searchByOperatorContains -on - | ENUM - | INPUT_FIELD_DEFINITION - | SCALAR - -directive @searchByOperatorEndsWith -on - | ENUM - | INPUT_FIELD_DEFINITION - | SCALAR - -directive @searchByOperatorEqual -on - | ENUM - | INPUT_FIELD_DEFINITION - | SCALAR - -directive @searchByOperatorGreaterThan -on - | ENUM - | INPUT_FIELD_DEFINITION - | SCALAR - -directive @searchByOperatorGreaterThanOrEqual -on - | ENUM - | INPUT_FIELD_DEFINITION - | SCALAR - -directive @searchByOperatorIn -on - | ENUM - | INPUT_FIELD_DEFINITION - | SCALAR - -directive @searchByOperatorIsNotNull -on - | ENUM - | INPUT_FIELD_DEFINITION - | SCALAR - -directive @searchByOperatorIsNull -on - | ENUM - | INPUT_FIELD_DEFINITION - | SCALAR - -directive @searchByOperatorLessThan -on - | ENUM - | INPUT_FIELD_DEFINITION - | SCALAR - -directive @searchByOperatorLessThanOrEqual -on - | ENUM - | INPUT_FIELD_DEFINITION - | SCALAR - -directive @searchByOperatorLike -on - | ENUM - | INPUT_FIELD_DEFINITION - | SCALAR - -directive @searchByOperatorNot -on - | ENUM - | INPUT_FIELD_DEFINITION - | SCALAR - -directive @searchByOperatorNotBetween -on - | ENUM - | INPUT_FIELD_DEFINITION - | SCALAR - -directive @searchByOperatorNotContains -on - | ENUM - | INPUT_FIELD_DEFINITION - | SCALAR - -directive @searchByOperatorNotEndsWith -on - | ENUM - | INPUT_FIELD_DEFINITION - | SCALAR - -directive @searchByOperatorNotEqual -on - | ENUM - | INPUT_FIELD_DEFINITION - | SCALAR - -directive @searchByOperatorNotIn -on - | ENUM - | INPUT_FIELD_DEFINITION - | SCALAR - -directive @searchByOperatorNotLike -on - | ENUM - | INPUT_FIELD_DEFINITION - | SCALAR - -directive @searchByOperatorNotStartsWith -on - | ENUM - | INPUT_FIELD_DEFINITION - | SCALAR - -directive @searchByOperatorProperty -on - | ENUM - | INPUT_FIELD_DEFINITION - | SCALAR - -directive @searchByOperatorRelation -on - | ENUM - | FIELD_DEFINITION - | INPUT_FIELD_DEFINITION - | SCALAR - -directive @searchByOperatorStartsWith -on - | ENUM - | INPUT_FIELD_DEFINITION - | SCALAR - -""" -Extends the list of operators by the operators from the specified `type`. -""" -directive @searchByOperators( - type: String! -) -on - | ENUM - | SCALAR - -enum EnumA { - One - Two -} - -enum EnumIgnored -@searchByIgnored -{ - One -} - -enum SearchByTypeFlag { - Yes - - yes - @deprecated( - reason: "Please use `Yes` instead." - ) -} - -""" -Conditions for the related objects (`has()`/`doesntHave()`) for `input NestedA`. - -See also: -* https://laravel.com/docs/eloquent-relationships#querying-relationship-existence -* https://laravel.com/docs/eloquent-relationships#querying-relationship-absence -""" -input SearchByComplexRelationNestedA { - """ - Count conditions. - """ - count: SearchByScalarInt - - """ - Alias for `count: {greaterThanOrEqual: 1}`. Will be ignored if `count` used. - """ - exists: Boolean - - """ - Alias for `count: {lessThan: 1}`. Will be ignored if `count` used. - """ - notExists: Boolean! = false - - """ - Additional conditions. - """ - where: SearchByConditionNestedA -} - -""" -Conditions for the related objects (`has()`/`doesntHave()`) for `input NestedB`. - -See also: -* https://laravel.com/docs/eloquent-relationships#querying-relationship-existence -* https://laravel.com/docs/eloquent-relationships#querying-relationship-absence -""" -input SearchByComplexRelationNestedB { - """ - Count conditions. - """ - count: SearchByScalarInt - - """ - Alias for `count: {greaterThanOrEqual: 1}`. Will be ignored if `count` used. - """ - exists: Boolean - - """ - Alias for `count: {lessThan: 1}`. Will be ignored if `count` used. - """ - notExists: Boolean! = false - - """ - Additional conditions. - """ - where: SearchByConditionNestedB -} - -""" -Conditions for the related objects (`has()`/`doesntHave()`) for `input NestedC`. - -See also: -* https://laravel.com/docs/eloquent-relationships#querying-relationship-existence -* https://laravel.com/docs/eloquent-relationships#querying-relationship-absence -""" -input SearchByComplexRelationNestedC { - """ - Count conditions. - """ - count: SearchByScalarInt - - """ - Alias for `count: {greaterThanOrEqual: 1}`. Will be ignored if `count` used. - """ - exists: Boolean - - """ - Alias for `count: {lessThan: 1}`. Will be ignored if `count` used. - """ - notExists: Boolean! = false - - """ - Additional conditions. - """ - where: SearchByConditionNestedC -} - -""" -Conditions for the related objects (`has()`/`doesntHave()`) for `type ObjectNested`. - -See also: -* https://laravel.com/docs/eloquent-relationships#querying-relationship-existence -* https://laravel.com/docs/eloquent-relationships#querying-relationship-absence -""" -input SearchByComplexRelationObjectNested { - """ - Count conditions. - """ - count: SearchByScalarInt - - """ - Alias for `count: {greaterThanOrEqual: 1}`. Will be ignored if `count` used. - """ - exists: Boolean - - """ - Alias for `count: {lessThan: 1}`. Will be ignored if `count` used. - """ - notExists: Boolean! = false - - """ - Additional conditions. - """ - where: SearchByConditionObjectNested -} - -""" -Available conditions for `input InputA` (only one property allowed at a time). -""" -input SearchByConditionInputA { - """ - All of the conditions must be true. - """ - allOf: [SearchByConditionInputA!] - @searchByOperatorAllOf - - """ - Any of the conditions must be true. - """ - anyOf: [SearchByConditionInputA!] - @searchByOperatorAnyOf - - """ - Property condition. - """ - booleanScalar: SearchByScalarBooleanOrNull - @searchByOperatorProperty - - """ - Property condition. - """ - booleanScalarNotNull: SearchByScalarBoolean - @searchByOperatorProperty - - """ - Property condition. - """ - customScalar: SearchByScalarDateOrNull - @searchByOperatorProperty - - """ - Property condition. - """ - customScalarList: SearchByScalarDate - @searchByOperatorProperty - - """ - Property condition. - """ - customScalarNonNull: SearchByScalarDate - @searchByOperatorProperty - - """ - Property condition. - """ - customScalarOperators: SearchByScalarScalarCustomOrNull - @searchByOperatorProperty - - """ - Description should be used. - """ - description: SearchByScalarIDOrNull - @searchByOperatorProperty - - """ - Property condition. - """ - enum: SearchByEnumEnumAOrNull - @searchByOperatorProperty - - """ - Property condition. - """ - enumNotNull: SearchByEnumEnumA - @searchByOperatorProperty - - """ - Property condition. - """ - fieldRenamed: SearchByScalarIDOrNull - @searchByOperatorProperty - @rename( - attribute: "renamed" - ) - - """ - Property condition. - """ - floatScalar: SearchByScalarFloatOrNull - @searchByOperatorProperty - - """ - Property condition. - """ - floatScalarNotNull: SearchByScalarFloat - @searchByOperatorProperty - - """ - Property condition. - """ - idScalar: SearchByScalarIDOrNull - @searchByOperatorProperty - - """ - Property condition. - """ - idScalarNotNull: SearchByScalarID - @searchByOperatorProperty - - """ - Property condition. - """ - intScalar: SearchByScalarIntOrNull - @searchByOperatorProperty - - """ - Property condition. - """ - intScalarNotNull: SearchByScalarInt - @searchByOperatorProperty - - """ - Relationship condition. - """ - nested: SearchByComplexRelationNestedA - @searchByOperatorRelation - - """ - Relationship condition. - """ - nestedNotNull: SearchByComplexRelationNestedA - @searchByOperatorRelation - - """ - Not. - """ - not: SearchByConditionInputA - @searchByOperatorNot - - """ - Property condition. - """ - stringScalar: SearchByScalarStringOrNull - @searchByOperatorProperty - - """ - Property condition. - """ - stringScalarNotNull: SearchByScalarString - @searchByOperatorProperty -} - -""" -Available conditions for `input InputB` (only one property allowed at a time). -""" -input SearchByConditionInputB { - """ - All of the conditions must be true. - """ - allOf: [SearchByConditionInputB!] - @searchByOperatorAllOf - - """ - Any of the conditions must be true. - """ - anyOf: [SearchByConditionInputB!] - @searchByOperatorAnyOf - - """ - Property condition. - """ - id: SearchByScalarIDOrNull - @searchByOperatorProperty - - """ - Not. - """ - not: SearchByConditionInputB - @searchByOperatorNot -} - -""" -Available conditions for `input NestedA` (only one property allowed at a time). -""" -input SearchByConditionNestedA { - """ - All of the conditions must be true. - """ - allOf: [SearchByConditionNestedA!] - @searchByOperatorAllOf - - """ - Any of the conditions must be true. - """ - anyOf: [SearchByConditionNestedA!] - @searchByOperatorAnyOf - - """ - Relationship condition. - """ - nested: SearchByComplexRelationNestedA - @searchByOperatorRelation - - """ - Not. - """ - not: SearchByConditionNestedA - @searchByOperatorNot - - """ - Property condition. - """ - value: SearchByScalarStringOrNull - @searchByOperatorProperty -} - -""" -Available conditions for `input NestedB` (only one property allowed at a time). -""" -input SearchByConditionNestedB { - """ - All of the conditions must be true. - """ - allOf: [SearchByConditionNestedB!] - @searchByOperatorAllOf - - """ - Any of the conditions must be true. - """ - anyOf: [SearchByConditionNestedB!] - @searchByOperatorAnyOf - - """ - Relationship condition. - """ - nested: SearchByComplexRelationNestedC - @searchByOperatorRelation - - """ - Not. - """ - not: SearchByConditionNestedB - @searchByOperatorNot -} - -""" -Available conditions for `input NestedC` (only one property allowed at a time). -""" -input SearchByConditionNestedC { - """ - All of the conditions must be true. - """ - allOf: [SearchByConditionNestedC!] - @searchByOperatorAllOf - - """ - Any of the conditions must be true. - """ - anyOf: [SearchByConditionNestedC!] - @searchByOperatorAnyOf - - """ - Relationship condition. - """ - nested: SearchByComplexRelationNestedB - @searchByOperatorRelation - - """ - Not. - """ - not: SearchByConditionNestedC - @searchByOperatorNot -} - -""" -Available conditions for `type Object` (only one property allowed at a time). -""" -input SearchByConditionObject { - """ - All of the conditions must be true. - """ - allOf: [SearchByConditionObject!] - @searchByOperatorAllOf - - """ - Any of the conditions must be true. - """ - anyOf: [SearchByConditionObject!] - @searchByOperatorAnyOf - - """ - Property condition. - """ - booleanScalar: SearchByScalarBooleanOrNull - @searchByOperatorProperty - - """ - Property condition. - """ - booleanScalarNotNull: SearchByScalarBoolean - @searchByOperatorProperty - - """ - Property condition. - """ - customScalar: SearchByScalarDateOrNull - @searchByOperatorProperty - - """ - Property condition. - """ - customScalarList: SearchByScalarDate - @searchByOperatorProperty - - """ - Property condition. - """ - customScalarNonNull: SearchByScalarDate - @searchByOperatorProperty - - """ - Property condition. - """ - customScalarOperators: SearchByScalarScalarCustomOrNull - @searchByOperatorProperty - - """ - Property condition. - """ - description: SearchByScalarIDOrNull - @searchByOperatorProperty - - """ - Property condition. - """ - enum: SearchByEnumEnumAOrNull - @searchByOperatorProperty - - """ - Property condition. - """ - enumNotNull: SearchByEnumEnumA - @searchByOperatorProperty - - """ - Property condition. - """ - fieldRenamed: SearchByScalarIDOrNull - @searchByOperatorProperty - @rename( - attribute: "renamed" - ) - - """ - Property condition. - """ - floatScalar: SearchByScalarFloatOrNull - @searchByOperatorProperty - - """ - Property condition. - """ - floatScalarNotNull: SearchByScalarFloat - @searchByOperatorProperty - - """ - Property condition. - """ - idScalar: SearchByScalarIDOrNull - @searchByOperatorProperty - - """ - Property condition. - """ - idScalarNotNull: SearchByScalarID - @searchByOperatorProperty - - """ - Property condition. - """ - intScalar: SearchByScalarIntOrNull - @searchByOperatorProperty - - """ - Property condition. - """ - intScalarNotNull: SearchByScalarInt - @searchByOperatorProperty - - """ - Relationship condition. - """ - nested: SearchByComplexRelationObjectNested - @searchByOperatorRelation - - """ - Relationship condition. - """ - nestedNotNull: SearchByComplexRelationObjectNested - @searchByOperatorRelation - - """ - Not. - """ - not: SearchByConditionObject - @searchByOperatorNot - - """ - Property condition. - """ - stringScalar: SearchByScalarStringOrNull - @searchByOperatorProperty - - """ - Property condition. - """ - stringScalarNotNull: SearchByScalarString - @searchByOperatorProperty -} - -""" -Available conditions for `interface ObjectInterface` (only one property allowed at a time). -""" -input SearchByConditionObjectInterface { - """ - All of the conditions must be true. - """ - allOf: [SearchByConditionObjectInterface!] - @searchByOperatorAllOf - - """ - Any of the conditions must be true. - """ - anyOf: [SearchByConditionObjectInterface!] - @searchByOperatorAnyOf - - """ - Property condition. - """ - booleanScalar: SearchByScalarBooleanOrNull - @searchByOperatorProperty - - """ - Property condition. - """ - booleanScalarNotNull: SearchByScalarBoolean - @searchByOperatorProperty - - """ - Property condition. - """ - customScalar: SearchByScalarDateOrNull - @searchByOperatorProperty - - """ - Property condition. - """ - customScalarList: SearchByScalarDate - @searchByOperatorProperty - - """ - Property condition. - """ - customScalarNonNull: SearchByScalarDate - @searchByOperatorProperty - - """ - Property condition. - """ - customScalarOperators: SearchByScalarScalarCustomOrNull - @searchByOperatorProperty - - """ - Property condition. - """ - description: SearchByScalarIDOrNull - @searchByOperatorProperty - - """ - Property condition. - """ - enum: SearchByEnumEnumAOrNull - @searchByOperatorProperty - - """ - Property condition. - """ - enumNotNull: SearchByEnumEnumA - @searchByOperatorProperty - - """ - Property condition. - """ - fieldRenamed: SearchByScalarIDOrNull - @searchByOperatorProperty - @rename( - attribute: "renamed" - ) - - """ - Property condition. - """ - floatScalar: SearchByScalarFloatOrNull - @searchByOperatorProperty - - """ - Property condition. - """ - floatScalarNotNull: SearchByScalarFloat - @searchByOperatorProperty - - """ - Property condition. - """ - idScalar: SearchByScalarIDOrNull - @searchByOperatorProperty - - """ - Property condition. - """ - idScalarNotNull: SearchByScalarID - @searchByOperatorProperty - - """ - Property condition. - """ - intScalar: SearchByScalarIntOrNull - @searchByOperatorProperty - - """ - Property condition. - """ - intScalarNotNull: SearchByScalarInt - @searchByOperatorProperty - - """ - Relationship condition. - """ - nested: SearchByComplexRelationObjectNested - @searchByOperatorRelation - - """ - Relationship condition. - """ - nestedNotNull: SearchByComplexRelationObjectNested - @searchByOperatorRelation - - """ - Not. - """ - not: SearchByConditionObjectInterface - @searchByOperatorNot - - """ - Property condition. - """ - stringScalar: SearchByScalarStringOrNull - @searchByOperatorProperty - - """ - Property condition. - """ - stringScalarNotNull: SearchByScalarString - @searchByOperatorProperty -} - -""" -Available conditions for `type ObjectNested` (only one property allowed at a time). -""" -input SearchByConditionObjectNested { - """ - All of the conditions must be true. - """ - allOf: [SearchByConditionObjectNested!] - @searchByOperatorAllOf - - """ - Any of the conditions must be true. - """ - anyOf: [SearchByConditionObjectNested!] - @searchByOperatorAnyOf - - """ - Relationship condition. - """ - nested: SearchByComplexRelationObjectNested - @searchByOperatorRelation - - """ - Not. - """ - not: SearchByConditionObjectNested - @searchByOperatorNot - - """ - Property condition. - """ - value: SearchByScalarStringOrNull - @searchByOperatorProperty -} - -""" -Available operators for `enum EnumA` (only one operator allowed at a time). -""" -input SearchByEnumEnumA { - """ - Equal (`=`). - """ - equal: EnumA - @searchByOperatorEqual - - """ - Within a set of values. - """ - in: [EnumA!] - @searchByOperatorIn - - """ - Not Equal (`!=`). - """ - notEqual: EnumA - @searchByOperatorNotEqual - - """ - Outside a set of values. - """ - notIn: [EnumA!] - @searchByOperatorNotIn -} - -""" -Available operators for `enum EnumA` (only one operator allowed at a time). -""" -input SearchByEnumEnumAOrNull { - """ - Equal (`=`). - """ - equal: EnumA - @searchByOperatorEqual - - """ - Within a set of values. - """ - in: [EnumA!] - @searchByOperatorIn - - """ - Is NOT NULL? - """ - isNotNull: SearchByTypeFlag - @searchByOperatorIsNotNull - - """ - Is NULL? - """ - isNull: SearchByTypeFlag - @searchByOperatorIsNull - - """ - Not Equal (`!=`). - """ - notEqual: EnumA - @searchByOperatorNotEqual - - """ - Outside a set of values. - """ - notIn: [EnumA!] - @searchByOperatorNotIn -} - -""" -Available operators for `scalar Boolean` (only one operator allowed at a time). -""" -input SearchByScalarBoolean { - """ - Equal (`=`). - """ - equal: Boolean - @searchByOperatorEqual - - """ - Not Equal (`!=`). - """ - notEqual: Boolean - @searchByOperatorNotEqual -} - -""" -Available operators for `scalar Boolean` (only one operator allowed at a time). -""" -input SearchByScalarBooleanOrNull { - """ - Equal (`=`). - """ - equal: Boolean - @searchByOperatorEqual - - """ - Is NOT NULL? - """ - isNotNull: SearchByTypeFlag - @searchByOperatorIsNotNull - - """ - Is NULL? - """ - isNull: SearchByTypeFlag - @searchByOperatorIsNull - - """ - Not Equal (`!=`). - """ - notEqual: Boolean - @searchByOperatorNotEqual -} - -""" -Available operators for `scalar Date` (only one operator allowed at a time). -""" -input SearchByScalarDate { - """ - Equal (`=`). - """ - equal: Date - @searchByOperatorEqual -} - -""" -Available operators for `scalar Date` (only one operator allowed at a time). -""" -input SearchByScalarDateOrNull { - """ - Equal (`=`). - """ - equal: Date - @searchByOperatorEqual - - """ - Is NOT NULL? - """ - isNotNull: SearchByTypeFlag - @searchByOperatorIsNotNull - - """ - Is NULL? - """ - isNull: SearchByTypeFlag - @searchByOperatorIsNull -} - -""" -Available operators for `scalar Float` (only one operator allowed at a time). -""" -input SearchByScalarFloat { - """ - Within a range. - """ - between: SearchByTypeRangeFloat - @searchByOperatorBetween - - """ - Equal (`=`). - """ - equal: Float - @searchByOperatorEqual - - """ - Greater than (`>`). - """ - greaterThan: Float - @searchByOperatorGreaterThan - - """ - Greater than or equal to (`>=`). - """ - greaterThanOrEqual: Float - @searchByOperatorGreaterThanOrEqual - - """ - Within a set of values. - """ - in: [Float!] - @searchByOperatorIn - - """ - Less than (`<`). - """ - lessThan: Float - @searchByOperatorLessThan - - """ - Less than or equal to (`<=`). - """ - lessThanOrEqual: Float - @searchByOperatorLessThanOrEqual - - """ - Outside a range. - """ - notBetween: SearchByTypeRangeFloat - @searchByOperatorNotBetween - - """ - Not Equal (`!=`). - """ - notEqual: Float - @searchByOperatorNotEqual - - """ - Outside a set of values. - """ - notIn: [Float!] - @searchByOperatorNotIn -} - -""" -Available operators for `scalar Float` (only one operator allowed at a time). -""" -input SearchByScalarFloatOrNull { - """ - Within a range. - """ - between: SearchByTypeRangeFloat - @searchByOperatorBetween - - """ - Equal (`=`). - """ - equal: Float - @searchByOperatorEqual - - """ - Greater than (`>`). - """ - greaterThan: Float - @searchByOperatorGreaterThan - - """ - Greater than or equal to (`>=`). - """ - greaterThanOrEqual: Float - @searchByOperatorGreaterThanOrEqual - - """ - Within a set of values. - """ - in: [Float!] - @searchByOperatorIn - - """ - Is NOT NULL? - """ - isNotNull: SearchByTypeFlag - @searchByOperatorIsNotNull - - """ - Is NULL? - """ - isNull: SearchByTypeFlag - @searchByOperatorIsNull - - """ - Less than (`<`). - """ - lessThan: Float - @searchByOperatorLessThan - - """ - Less than or equal to (`<=`). - """ - lessThanOrEqual: Float - @searchByOperatorLessThanOrEqual - - """ - Outside a range. - """ - notBetween: SearchByTypeRangeFloat - @searchByOperatorNotBetween - - """ - Not Equal (`!=`). - """ - notEqual: Float - @searchByOperatorNotEqual - - """ - Outside a set of values. - """ - notIn: [Float!] - @searchByOperatorNotIn -} - -""" -Available operators for `scalar ID` (only one operator allowed at a time). -""" -input SearchByScalarID { - """ - Equal (`=`). - """ - equal: ID - @searchByOperatorEqual - - """ - Within a set of values. - """ - in: [ID!] - @searchByOperatorIn - - """ - Not Equal (`!=`). - """ - notEqual: ID - @searchByOperatorNotEqual - - """ - Outside a set of values. - """ - notIn: [ID!] - @searchByOperatorNotIn -} - -""" -Available operators for `scalar ID` (only one operator allowed at a time). -""" -input SearchByScalarIDOrNull { - """ - Equal (`=`). - """ - equal: ID - @searchByOperatorEqual - - """ - Within a set of values. - """ - in: [ID!] - @searchByOperatorIn - - """ - Is NOT NULL? - """ - isNotNull: SearchByTypeFlag - @searchByOperatorIsNotNull - - """ - Is NULL? - """ - isNull: SearchByTypeFlag - @searchByOperatorIsNull - - """ - Not Equal (`!=`). - """ - notEqual: ID - @searchByOperatorNotEqual - - """ - Outside a set of values. - """ - notIn: [ID!] - @searchByOperatorNotIn -} - -""" -Available operators for `scalar Int` (only one operator allowed at a time). -""" -input SearchByScalarInt { - """ - Within a range. - """ - between: SearchByTypeRangeInt - @searchByOperatorBetween - - """ - Bitwise AND (`&`). - """ - bitwiseAnd: Int - @searchByOperatorBitwiseAnd - - """ - Bitwise Left shift (`<<`). - """ - bitwiseLeftShift: Int - @searchByOperatorBitwiseLeftShift - - """ - Bitwise OR (`|`). - """ - bitwiseOr: Int - @searchByOperatorBitwiseOr - - """ - Bitwise Right shift (`>>`). - """ - bitwiseRightShift: Int - @searchByOperatorBitwiseRightShift - - """ - Bitwise XOR (`^`). - """ - bitwiseXor: Int - @searchByOperatorBitwiseXor - - """ - Equal (`=`). - """ - equal: Int - @searchByOperatorEqual - - """ - Greater than (`>`). - """ - greaterThan: Int - @searchByOperatorGreaterThan - - """ - Greater than or equal to (`>=`). - """ - greaterThanOrEqual: Int - @searchByOperatorGreaterThanOrEqual - - """ - Within a set of values. - """ - in: [Int!] - @searchByOperatorIn - - """ - Less than (`<`). - """ - lessThan: Int - @searchByOperatorLessThan - - """ - Less than or equal to (`<=`). - """ - lessThanOrEqual: Int - @searchByOperatorLessThanOrEqual - - """ - Outside a range. - """ - notBetween: SearchByTypeRangeInt - @searchByOperatorNotBetween - - """ - Not Equal (`!=`). - """ - notEqual: Int - @searchByOperatorNotEqual - - """ - Outside a set of values. - """ - notIn: [Int!] - @searchByOperatorNotIn -} - -""" -Available operators for `scalar Int` (only one operator allowed at a time). -""" -input SearchByScalarIntOrNull { - """ - Within a range. - """ - between: SearchByTypeRangeInt - @searchByOperatorBetween - - """ - Bitwise AND (`&`). - """ - bitwiseAnd: Int - @searchByOperatorBitwiseAnd - - """ - Bitwise Left shift (`<<`). - """ - bitwiseLeftShift: Int - @searchByOperatorBitwiseLeftShift - - """ - Bitwise OR (`|`). - """ - bitwiseOr: Int - @searchByOperatorBitwiseOr - - """ - Bitwise Right shift (`>>`). - """ - bitwiseRightShift: Int - @searchByOperatorBitwiseRightShift - - """ - Bitwise XOR (`^`). - """ - bitwiseXor: Int - @searchByOperatorBitwiseXor - - """ - Equal (`=`). - """ - equal: Int - @searchByOperatorEqual - - """ - Greater than (`>`). - """ - greaterThan: Int - @searchByOperatorGreaterThan - - """ - Greater than or equal to (`>=`). - """ - greaterThanOrEqual: Int - @searchByOperatorGreaterThanOrEqual - - """ - Within a set of values. - """ - in: [Int!] - @searchByOperatorIn - - """ - Is NOT NULL? - """ - isNotNull: SearchByTypeFlag - @searchByOperatorIsNotNull - - """ - Is NULL? - """ - isNull: SearchByTypeFlag - @searchByOperatorIsNull - - """ - Less than (`<`). - """ - lessThan: Int - @searchByOperatorLessThan - - """ - Less than or equal to (`<=`). - """ - lessThanOrEqual: Int - @searchByOperatorLessThanOrEqual - - """ - Outside a range. - """ - notBetween: SearchByTypeRangeInt - @searchByOperatorNotBetween - - """ - Not Equal (`!=`). - """ - notEqual: Int - @searchByOperatorNotEqual - - """ - Outside a set of values. - """ - notIn: [Int!] - @searchByOperatorNotIn -} - -""" -Available operators for `scalar ScalarCustom` (only one operator allowed at a time). -""" -input SearchByScalarScalarCustomOrNull { - """ - Equal (`=`). - """ - equal: ScalarCustom - @searchByOperatorEqual - - """ - Is NOT NULL? - """ - isNotNull: SearchByTypeFlag - @searchByOperatorIsNotNull - - """ - Is NULL? - """ - isNull: SearchByTypeFlag - @searchByOperatorIsNull - - """ - Less than (`<`). - """ - lessThan: ScalarCustom - @searchByOperatorLessThan - - """ - Less than or equal to (`<=`). - """ - lessThanOrEqual: ScalarCustom - @searchByOperatorLessThanOrEqual - - """ - Not Equal (`!=`). - """ - notEqual: ScalarCustom - @searchByOperatorNotEqual -} - -""" -Available operators for `scalar String` (only one operator allowed at a time). -""" -input SearchByScalarString { - """ - Contains. - """ - contains: String - @searchByOperatorContains - - """ - Ends with a string. - """ - endsWith: String - @searchByOperatorEndsWith - - """ - Equal (`=`). - """ - equal: String - @searchByOperatorEqual - - """ - Within a set of values. - """ - in: [String!] - @searchByOperatorIn - - """ - Like. - """ - like: String - @searchByOperatorLike - - """ - Not contains. - """ - notContains: String - @searchByOperatorNotContains - - """ - Not ends with a string. - """ - notEndsWith: String - @searchByOperatorNotEndsWith - - """ - Not Equal (`!=`). - """ - notEqual: String - @searchByOperatorNotEqual - - """ - Outside a set of values. - """ - notIn: [String!] - @searchByOperatorNotIn - - """ - Not like. - """ - notLike: String - @searchByOperatorNotLike - - """ - Not starts with a string. - """ - notStartsWith: String - @searchByOperatorNotStartsWith - - """ - Starts with a string. - """ - startsWith: String - @searchByOperatorStartsWith -} - -""" -Available operators for `scalar String` (only one operator allowed at a time). -""" -input SearchByScalarStringOrNull { - """ - Contains. - """ - contains: String - @searchByOperatorContains - - """ - Ends with a string. - """ - endsWith: String - @searchByOperatorEndsWith - - """ - Equal (`=`). - """ - equal: String - @searchByOperatorEqual - - """ - Within a set of values. - """ - in: [String!] - @searchByOperatorIn - - """ - Is NOT NULL? - """ - isNotNull: SearchByTypeFlag - @searchByOperatorIsNotNull - - """ - Is NULL? - """ - isNull: SearchByTypeFlag - @searchByOperatorIsNull - - """ - Like. - """ - like: String - @searchByOperatorLike - - """ - Not contains. - """ - notContains: String - @searchByOperatorNotContains - - """ - Not ends with a string. - """ - notEndsWith: String - @searchByOperatorNotEndsWith - - """ - Not Equal (`!=`). - """ - notEqual: String - @searchByOperatorNotEqual - - """ - Outside a set of values. - """ - notIn: [String!] - @searchByOperatorNotIn - - """ - Not like. - """ - notLike: String - @searchByOperatorNotLike - - """ - Not starts with a string. - """ - notStartsWith: String - @searchByOperatorNotStartsWith - - """ - Starts with a string. - """ - startsWith: String - @searchByOperatorStartsWith -} - -input SearchByTypeRangeFloat { - max: Float! - min: Float! -} - -input SearchByTypeRangeInt { - max: Int! - min: Int! -} - -interface A -implements - & C - & F -{ - a( - where: SearchByConditionInputA - ): ID! -} - -interface B { - b( - where: SearchByConditionNestedA - ): ID! -} - -interface C { - c( - where: SearchByConditionInputB - ): ID! -} - -interface F { - f( - where: SearchByConditionObjectInterface - @searchBy - ): Object! - @all -} - -interface ObjectInterface { - booleanScalar: Boolean - booleanScalarNotNull: Boolean! - customScalar: Date - customScalarIgnored: DateIgnored - customScalarIgnoredList: [DateIgnored!]! - customScalarIgnoredNonNull: DateIgnored! - customScalarList: [Date!]! - customScalarNonNull: Date! - customScalarOperators: ScalarCustom - - """ - Description should NOT be used. - """ - description: ID - - enum: EnumA - enumEnumIgnoredNotNull: EnumIgnored! - enumIgnored: EnumIgnored - enumNotNull: EnumA! - - fieldRenamed: ID - @rename( - attribute: "renamed" - ) - - """ - Should be ignored - """ - fieldWithArguments( - arg: Int - ): Boolean - - floatScalar: Float - floatScalarNotNull: Float! - idScalar: ID - idScalarNotNull: ID! - - ignored: String - @searchByIgnored - - ignoredType: ObjectIgnored - ignoredTypeList: [ObjectIgnored!]! - ignoredTypeNonNull: ObjectIgnored! - intScalar: Int - intScalarNotNull: Int! - nested: ObjectNested - nestedNotNull: ObjectNested! - - resolver: Float - @field( - resolver: "\\LastDragon_ru\\LaraASP\\GraphQL\\SearchBy\\Directives\\DirectiveTest__Resolver" - ) - - stringScalar: String - stringScalarNotNull: String! - union: ObjectUnion - unionList: [ObjectUnion!]! - unionNonNull: ObjectUnion! -} - -scalar Date -@scalar( - class: "Nuwave\\Lighthouse\\Schema\\Types\\Scalars\\Date" -) - -scalar DateIgnored -@scalar( - class: "Nuwave\\Lighthouse\\Schema\\Types\\Scalars\\Date" -) -@searchByIgnored - -scalar ScalarCustom -@scalar( - class: "Nuwave\\Lighthouse\\Schema\\Types\\Scalars\\Date" -) -@searchByOperators( - type: "Boolean" -) -@searchByOperatorLessThan -@searchByOperatorLessThanOrEqual - -type Object -implements - & ObjectInterface -{ - booleanScalar: Boolean - booleanScalarNotNull: Boolean! - customScalar: Date - customScalarIgnored: DateIgnored - customScalarIgnoredList: [DateIgnored!]! - customScalarIgnoredNonNull: DateIgnored! - customScalarList: [Date!]! - customScalarNonNull: Date! - customScalarOperators: ScalarCustom - - """ - Description should NOT be used. - """ - description: ID - - enum: EnumA - enumEnumIgnoredNotNull: EnumIgnored! - enumIgnored: EnumIgnored - enumNotNull: EnumA! - - fieldRenamed: ID - @rename( - attribute: "renamed" - ) - - """ - Should be ignored - """ - fieldWithArguments( - arg: Int - ): Boolean - - floatScalar: Float - floatScalarNotNull: Float! - idScalar: ID - idScalarNotNull: ID! - - ignored: String - @searchByIgnored - - ignoredType: ObjectIgnored - ignoredTypeList: [ObjectIgnored!]! - ignoredTypeNonNull: ObjectIgnored! - intScalar: Int - intScalarNotNull: Int! - nested: ObjectNested - nestedNotNull: ObjectNested! - - resolver: Float - @field( - resolver: "\\LastDragon_ru\\LaraASP\\GraphQL\\SearchBy\\Directives\\DirectiveTest__Resolver" - ) - - stringScalar: String - stringScalarNotNull: String! - union: ObjectUnion - unionList: [ObjectUnion!]! - unionNonNull: ObjectUnion! -} - -type ObjectIgnored -@searchByIgnored -{ - value: String -} - -type ObjectNested { - nested: ObjectNested - value: String -} - -type Query -implements - & A - & B -{ - a( - where: SearchByConditionInputA - @searchBy - ): ID! - @all - - b( - where: SearchByConditionNestedA - @searchBy - ): ID! - @all - - c( - where: SearchByConditionInputB - @searchBy - ): ID! - @all - - d( - where: SearchByConditionNestedB - @searchBy - ): ID! - @all - - e( - where: SearchByConditionObject - @searchBy - ): Object! - @all - - f( - where: SearchByConditionObjectInterface - @searchBy - ): Object! - @all -} - -union ObjectUnion = - | Object - | ObjectNested diff --git a/packages/graphql/src/SearchBy/Directives/DirectiveTest~full.graphql b/packages/graphql/src/SearchBy/Directives/DirectiveTest~full.graphql deleted file mode 100644 index 0d61e55f5..000000000 --- a/packages/graphql/src/SearchBy/Directives/DirectiveTest~full.graphql +++ /dev/null @@ -1,209 +0,0 @@ -type Query implements A & B { - a(where: InputA @searchBy): ID! @all - - b(where: NestedA @searchBy): ID! @all - - c(where: InputB @searchBy): ID! @all - - d(where: NestedB @searchBy): ID! @all - - e(where: _ @searchBy): Object! @all - - f(where: ObjectInterface @searchBy): Object! @all -} - -interface A implements C & F { - a(where: InputA): ID! -} - -interface B { - b(where: TypeDoesntMatter): ID! -} - -interface C { - c(where: TypeDoesntMatter): ID! -} - -interface F { - f(where: _ @searchBy): Object! @all -} - -input InputA { - idScalar: ID - idScalarNotNull: ID! - intScalar: Int - intScalarNotNull: Int! - floatScalar: Float - floatScalarNotNull: Float! - stringScalar: String - stringScalarNotNull: String! - booleanScalar: Boolean - booleanScalarNotNull: Boolean! - nested: NestedA - nestedNotNull: NestedA! - enum: EnumA - enumNotNull: EnumA! - enumIgnored: EnumIgnored - enumEnumIgnoredNotNull: EnumIgnored! - ignored: String @searchByIgnored - resolver: Float @field( - resolver: "\\LastDragon_ru\\LaraASP\\GraphQL\\SearchBy\\Directives\\DirectiveTest__Resolver" - ) - ignoredType: InputIgnored - ignoredTypeList: [InputIgnored!]! - ignoredTypeNonNull: InputIgnored! - customScalar: Date - customScalarList: [Date!]! - customScalarNonNull: Date! - customScalarIgnored: DateIgnored - customScalarIgnoredList: [DateIgnored!]! - customScalarIgnoredNonNull: DateIgnored - customScalarOperators: ScalarCustom - - "Description should be used." - description: ID - - fieldRenamed: ID @rename(attribute: "renamed") -} - -input NestedA { - value: String - nested: NestedA -} - -input NestedB { - nested: NestedC -} - -input NestedC { - nested: NestedB -} - -input InputB { - id: ID -} - -input InputIgnored @searchByIgnored { - id: ID -} - -scalar Date -@scalar(class: "Nuwave\\Lighthouse\\Schema\\Types\\Scalars\\Date") - -scalar DateIgnored -@scalar(class: "Nuwave\\Lighthouse\\Schema\\Types\\Scalars\\Date") -@searchByIgnored - -scalar ScalarCustom -@scalar(class: "Nuwave\\Lighthouse\\Schema\\Types\\Scalars\\Date") -@searchByOperators(type: "Boolean") -@searchByOperatorLessThan -@searchByOperatorLessThanOrEqual - -enum EnumA { - One - Two -} - -enum EnumIgnored @searchByIgnored { - One -} - -interface ObjectInterface { - idScalar: ID - idScalarNotNull: ID! - intScalar: Int - intScalarNotNull: Int! - floatScalar: Float - floatScalarNotNull: Float! - stringScalar: String - stringScalarNotNull: String! - booleanScalar: Boolean - booleanScalarNotNull: Boolean! - nested: ObjectNested - nestedNotNull: ObjectNested! - enum: EnumA - enumNotNull: EnumA! - enumIgnored: EnumIgnored - enumEnumIgnoredNotNull: EnumIgnored! - ignored: String @searchByIgnored - resolver: Float @field( - resolver: "\\LastDragon_ru\\LaraASP\\GraphQL\\SearchBy\\Directives\\DirectiveTest__Resolver" - ) - union: ObjectUnion - unionList: [ObjectUnion!]! - unionNonNull: ObjectUnion! - ignoredType: ObjectIgnored - ignoredTypeList: [ObjectIgnored!]! - ignoredTypeNonNull: ObjectIgnored! - customScalar: Date - customScalarList: [Date!]! - customScalarNonNull: Date! - customScalarIgnored: DateIgnored - customScalarIgnoredList: [DateIgnored!]! - customScalarIgnoredNonNull: DateIgnored! - customScalarOperators: ScalarCustom - - "Description should NOT be used." - description: ID - - "Should be ignored" - fieldWithArguments(arg: Int): Boolean - - fieldRenamed: ID @rename(attribute: "renamed") -} - -type Object implements ObjectInterface{ - idScalar: ID - idScalarNotNull: ID! - intScalar: Int - intScalarNotNull: Int! - floatScalar: Float - floatScalarNotNull: Float! - stringScalar: String - stringScalarNotNull: String! - booleanScalar: Boolean - booleanScalarNotNull: Boolean! - nested: ObjectNested - nestedNotNull: ObjectNested! - enum: EnumA - enumNotNull: EnumA! - enumIgnored: EnumIgnored - enumEnumIgnoredNotNull: EnumIgnored! - ignored: String @searchByIgnored - resolver: Float @field( - resolver: "\\LastDragon_ru\\LaraASP\\GraphQL\\SearchBy\\Directives\\DirectiveTest__Resolver" - ) - union: ObjectUnion - unionList: [ObjectUnion!]! - unionNonNull: ObjectUnion! - ignoredType: ObjectIgnored - ignoredTypeList: [ObjectIgnored!]! - ignoredTypeNonNull: ObjectIgnored! - customScalar: Date - customScalarList: [Date!]! - customScalarNonNull: Date! - customScalarIgnored: DateIgnored - customScalarIgnoredList: [DateIgnored!]! - customScalarIgnoredNonNull: DateIgnored! - customScalarOperators: ScalarCustom - - "Description should NOT be used." - description: ID - - "Should be ignored" - fieldWithArguments(arg: Int): Boolean - - fieldRenamed: ID @rename(attribute: "renamed") -} - -type ObjectNested { - value: String - nested: ObjectNested -} - -union ObjectUnion = Object | ObjectNested - -type ObjectIgnored @searchByIgnored { - value: String -} diff --git a/packages/graphql/src/SearchBy/Directives/DirectiveTest~unknown.graphql b/packages/graphql/src/SearchBy/Directives/DirectiveTest~unknown.graphql deleted file mode 100644 index d27128c3d..000000000 --- a/packages/graphql/src/SearchBy/Directives/DirectiveTest~unknown.graphql +++ /dev/null @@ -1,7 +0,0 @@ -type Query { - test(where: Properties @searchBy): ID! @all -} - -input Properties { - value: UnknownType -} diff --git a/packages/graphql/src/SearchBy/Directives/DirectiveTest~usedonly-expected.graphql b/packages/graphql/src/SearchBy/Directives/DirectiveTest~usedonly-expected.graphql deleted file mode 100644 index 8ba14389a..000000000 --- a/packages/graphql/src/SearchBy/Directives/DirectiveTest~usedonly-expected.graphql +++ /dev/null @@ -1,216 +0,0 @@ -""" -Use Input as Search Conditions for the current Builder. -""" -directive @searchBy -on - | ARGUMENT_DEFINITION - -directive @searchByOperatorAllOf -on - | ENUM - | INPUT_FIELD_DEFINITION - | SCALAR - -directive @searchByOperatorAnyOf -on - | ENUM - | INPUT_FIELD_DEFINITION - | SCALAR - -directive @searchByOperatorContains -on - | ENUM - | INPUT_FIELD_DEFINITION - | SCALAR - -directive @searchByOperatorEndsWith -on - | ENUM - | INPUT_FIELD_DEFINITION - | SCALAR - -directive @searchByOperatorEqual -on - | ENUM - | INPUT_FIELD_DEFINITION - | SCALAR - -directive @searchByOperatorIn -on - | ENUM - | INPUT_FIELD_DEFINITION - | SCALAR - -directive @searchByOperatorLike -on - | ENUM - | INPUT_FIELD_DEFINITION - | SCALAR - -directive @searchByOperatorNot -on - | ENUM - | INPUT_FIELD_DEFINITION - | SCALAR - -directive @searchByOperatorNotContains -on - | ENUM - | INPUT_FIELD_DEFINITION - | SCALAR - -directive @searchByOperatorNotEndsWith -on - | ENUM - | INPUT_FIELD_DEFINITION - | SCALAR - -directive @searchByOperatorNotEqual -on - | ENUM - | INPUT_FIELD_DEFINITION - | SCALAR - -directive @searchByOperatorNotIn -on - | ENUM - | INPUT_FIELD_DEFINITION - | SCALAR - -directive @searchByOperatorNotLike -on - | ENUM - | INPUT_FIELD_DEFINITION - | SCALAR - -directive @searchByOperatorNotStartsWith -on - | ENUM - | INPUT_FIELD_DEFINITION - | SCALAR - -directive @searchByOperatorProperty -on - | ENUM - | INPUT_FIELD_DEFINITION - | SCALAR - -directive @searchByOperatorStartsWith -on - | ENUM - | INPUT_FIELD_DEFINITION - | SCALAR - -""" -Available conditions for `input Properties` (only one property allowed at a time). -""" -input SearchByConditionProperties { - """ - All of the conditions must be true. - """ - allOf: [SearchByConditionProperties!] - @searchByOperatorAllOf - - """ - Any of the conditions must be true. - """ - anyOf: [SearchByConditionProperties!] - @searchByOperatorAnyOf - - """ - Not. - """ - not: SearchByConditionProperties - @searchByOperatorNot - - """ - Property condition. - """ - value: SearchByScalarString - @searchByOperatorProperty -} - -""" -Available operators for `scalar String` (only one operator allowed at a time). -""" -input SearchByScalarString { - """ - Contains. - """ - contains: String - @searchByOperatorContains - - """ - Ends with a string. - """ - endsWith: String - @searchByOperatorEndsWith - - """ - Equal (`=`). - """ - equal: String - @searchByOperatorEqual - - """ - Within a set of values. - """ - in: [String!] - @searchByOperatorIn - - """ - Like. - """ - like: String - @searchByOperatorLike - - """ - Not contains. - """ - notContains: String - @searchByOperatorNotContains - - """ - Not ends with a string. - """ - notEndsWith: String - @searchByOperatorNotEndsWith - - """ - Not Equal (`!=`). - """ - notEqual: String - @searchByOperatorNotEqual - - """ - Outside a set of values. - """ - notIn: [String!] - @searchByOperatorNotIn - - """ - Not like. - """ - notLike: String - @searchByOperatorNotLike - - """ - Not starts with a string. - """ - notStartsWith: String - @searchByOperatorNotStartsWith - - """ - Starts with a string. - """ - startsWith: String - @searchByOperatorStartsWith -} - -type Query { - test( - where: SearchByConditionProperties - @searchBy - ): ID! - @all -} diff --git a/packages/graphql/src/SearchBy/Directives/DirectiveTest~usedonly.graphql b/packages/graphql/src/SearchBy/Directives/DirectiveTest~usedonly.graphql deleted file mode 100644 index 1ed84b9ad..000000000 --- a/packages/graphql/src/SearchBy/Directives/DirectiveTest~usedonly.graphql +++ /dev/null @@ -1,7 +0,0 @@ -type Query { - test(where: Properties @searchBy): ID! @all -} - -input Properties { - value: String! -} From f280112e1fd4c3a474656e09f2b758d26d3d3d69 Mon Sep 17 00:00:00 2001 From: Aleksei Lebedev <1329824+LastDragon-ru@users.noreply.github.com> Date: Tue, 23 Jan 2024 13:21:41 +0400 Subject: [PATCH 05/26] `@sortBy` better (isolated) tests. --- packages/graphql/docs/Directives/@sortBy.md | 2 +- .../src/SortBy/Directives/DirectiveTest.php | 262 ++++--- .../AllowedDirectives.expected.graphql | 86 +++ .../AllowedDirectives.schema.graphql | 12 + .../Example.expected.graphql} | 0 .../Example.schema.graphql} | 0 .../DirectiveTest/Explicit.expected.graphql | 126 ++++ .../DirectiveTest/Explicit.schema.graphql | 17 + .../DirectiveTest/Ignored.expected.graphql | 182 +++++ .../DirectiveTest/Ignored.schema.graphql | 68 ++ .../DirectiveTest/Implicit.expected.graphql | 147 ++++ .../DirectiveTest/Implicit.schema.graphql | 17 + .../InterfaceUpdate.expected.graphql | 103 +++ .../InterfaceUpdate.schema.graphql | 25 + .../DirectiveTest/Query.expected.graphql | 130 ++++ .../DirectiveTest/Query.schema.graphql | 32 + .../Scout.expected.graphql} | 0 .../Scout.schema.graphql} | 0 .../TypeRegistry.expected.graphql} | 0 .../TypeRegistry.schema.graphql} | 0 .../DirectiveTest~full-expected.graphql | 670 ------------------ .../Directives/DirectiveTest~full.graphql | 154 ---- 22 files changed, 1098 insertions(+), 935 deletions(-) create mode 100644 packages/graphql/src/SortBy/Directives/DirectiveTest/AllowedDirectives.expected.graphql create mode 100644 packages/graphql/src/SortBy/Directives/DirectiveTest/AllowedDirectives.schema.graphql rename packages/graphql/src/SortBy/Directives/{DirectiveTest~example-expected.graphql => DirectiveTest/Example.expected.graphql} (100%) rename packages/graphql/src/SortBy/Directives/{DirectiveTest~example.graphql => DirectiveTest/Example.schema.graphql} (100%) create mode 100644 packages/graphql/src/SortBy/Directives/DirectiveTest/Explicit.expected.graphql create mode 100644 packages/graphql/src/SortBy/Directives/DirectiveTest/Explicit.schema.graphql create mode 100644 packages/graphql/src/SortBy/Directives/DirectiveTest/Ignored.expected.graphql create mode 100644 packages/graphql/src/SortBy/Directives/DirectiveTest/Ignored.schema.graphql create mode 100644 packages/graphql/src/SortBy/Directives/DirectiveTest/Implicit.expected.graphql create mode 100644 packages/graphql/src/SortBy/Directives/DirectiveTest/Implicit.schema.graphql create mode 100644 packages/graphql/src/SortBy/Directives/DirectiveTest/InterfaceUpdate.expected.graphql create mode 100644 packages/graphql/src/SortBy/Directives/DirectiveTest/InterfaceUpdate.schema.graphql create mode 100644 packages/graphql/src/SortBy/Directives/DirectiveTest/Query.expected.graphql create mode 100644 packages/graphql/src/SortBy/Directives/DirectiveTest/Query.schema.graphql rename packages/graphql/src/SortBy/Directives/{DirectiveTest~scout-expected.graphql => DirectiveTest/Scout.expected.graphql} (100%) rename packages/graphql/src/SortBy/Directives/{DirectiveTest~scout.graphql => DirectiveTest/Scout.schema.graphql} (100%) rename packages/graphql/src/SortBy/Directives/{DirectiveTest~registry-expected.graphql => DirectiveTest/TypeRegistry.expected.graphql} (100%) rename packages/graphql/src/SortBy/Directives/{DirectiveTest~registry.graphql => DirectiveTest/TypeRegistry.schema.graphql} (100%) delete mode 100644 packages/graphql/src/SortBy/Directives/DirectiveTest~full-expected.graphql delete mode 100644 packages/graphql/src/SortBy/Directives/DirectiveTest~full.graphql diff --git a/packages/graphql/docs/Directives/@sortBy.md b/packages/graphql/docs/Directives/@sortBy.md index a1d4f2764..8686626f6 100644 --- a/packages/graphql/docs/Directives/@sortBy.md +++ b/packages/graphql/docs/Directives/@sortBy.md @@ -19,7 +19,7 @@ on ## Basic usage -How to use (and [generated GraphQL schema](../../src/SortBy/Directives/DirectiveTest~example-expected.graphql)): +How to use (and [generated GraphQL schema](../../src/SortBy/Directives/DirectiveTest/Example.expected.graphql)): ```graphql type Query { diff --git a/packages/graphql/src/SortBy/Directives/DirectiveTest.php b/packages/graphql/src/SortBy/Directives/DirectiveTest.php index 436fd0989..858975511 100644 --- a/packages/graphql/src/SortBy/Directives/DirectiveTest.php +++ b/packages/graphql/src/SortBy/Directives/DirectiveTest.php @@ -66,10 +66,9 @@ final class DirectiveTest extends TestCase { /** * @dataProvider dataProviderManipulateArgDefinition * - * @param Closure(static): GraphQLExpected $expected - * @param Closure(static): void|null $prepare + * @param Closure(static): void|null $prepare */ - public function testManipulateArgDefinition(Closure $expected, string $graphql, ?Closure $prepare = null): void { + public function testManipulateArgDefinition(string $expected, string $graphql, ?Closure $prepare = null): void { if ($prepare) { $prepare($this); } @@ -79,7 +78,7 @@ public function testManipulateArgDefinition(Closure $expected, string $graphql, ); self::assertGraphQLSchemaEquals( - $expected($this), + new GraphQLExpected(self::getTestData()->file($expected)), ); } @@ -98,96 +97,11 @@ public function testManipulateArgDefinitionScoutBuilder(): void { ->setResolved('search', SearchDirective::class); $this->useGraphQLSchema( - self::getTestData()->file('~scout.graphql'), + self::getTestData()->file('Scout.schema.graphql'), ); self::assertGraphQLSchemaEquals( - self::getTestData()->file('~scout-expected.graphql'), - ); - } - - public function testManipulateArgDefinitionTypeRegistry(): void { - $i = new class([ - 'name' => 'I', - 'fields' => [ - [ - 'name' => 'name', - 'type' => Type::string(), - ], - ], - ]) extends InputObjectType implements Ignored { - // empty - }; - $a = new InputObjectType([ - 'name' => 'A', - 'fields' => [ - [ - 'name' => 'name', - 'type' => Type::string(), - ], - [ - 'name' => 'flag', - 'type' => Type::nonNull(Type::boolean()), - ], - [ - 'name' => 'ignored', - 'type' => Type::nonNull($i), - ], - ], - ]); - $b = new InputObjectType([ - 'name' => 'B', - 'fields' => [ - [ - 'name' => 'name', - 'type' => Type::string(), - ], - [ - 'name' => 'child', - 'type' => $a, - ], - ], - ]); - $c = new ObjectType([ - 'name' => 'C', - 'fields' => [ - [ - 'name' => 'name', - 'type' => Type::string(), - ], - [ - 'name' => 'flag', - 'type' => Type::nonNull(Type::boolean()), - ], - [ - 'name' => 'list', - 'type' => Type::nonNull(Type::listOf(Type::nonNull(Type::boolean()))), - ], - ], - ]); - $d = new ObjectType([ - 'name' => 'D', - 'fields' => [ - [ - 'name' => 'child', - 'type' => Type::nonNull($c), - ], - ], - ]); - - $registry = Container::getInstance()->make(TypeRegistry::class); - $registry->register($a); - $registry->register($b); - $registry->register($c); - $registry->register($d); - $registry->register($i); - - $this->useGraphQLSchema( - self::getTestData()->file('~registry.graphql'), - ); - - self::assertGraphQLSchemaEquals( - self::getTestData()->file('~registry-expected.graphql'), + self::getTestData()->file('Scout.expected.graphql'), ); } @@ -419,37 +333,155 @@ static function (MockInterface $mock) use ($resolver): void { // // ========================================================================= /** - * @return array + * @return array */ public static function dataProviderManipulateArgDefinition(): array { return [ - 'full' => [ - static function (self $test): GraphQLExpected { - return (new GraphQLExpected( - $test::getTestData()->file('~full-expected.graphql'), - )); + 'Explicit' => [ + 'Explicit.expected.graphql', + 'Explicit.schema.graphql', + null, + ], + 'Implicit' => [ + 'Implicit.expected.graphql', + 'Implicit.schema.graphql', + null, + ], + 'Query' => [ + 'Query.expected.graphql', + 'Query.schema.graphql', + null, + ], + 'AllowedDirectives' => [ + 'AllowedDirectives.expected.graphql', + 'AllowedDirectives.schema.graphql', + static function (): void { + config([ + Package::Name.'.sort_by.operators.'.Operators::Extra => [ + // empty + ], + ]); }, - '~full.graphql', + ], + 'Ignored' => [ + 'Ignored.expected.graphql', + 'Ignored.schema.graphql', static function (): void { - $package = Package::Name; - config([ - "{$package}.sort_by.operators" => [ - Operators::Extra => [ - Operators::Extra, - SortByOperatorRandomDirective::class, + Package::Name.'.sort_by.operators.'.Operators::Extra => [ + // empty + ], + ]); + + Container::getInstance()->make(TypeRegistry::class)->register( + new class([ + 'name' => 'IgnoredType', + 'fields' => [ + [ + 'name' => 'name', + 'type' => Type::nonNull(Type::string()), + ], ], + ]) extends InputObjectType implements Ignored { + // empty + }, + ); + }, + ], + 'InterfaceUpdate' => [ + 'InterfaceUpdate.expected.graphql', + 'InterfaceUpdate.schema.graphql', + static function (): void { + config([ + Package::Name.'.sort_by.operators.'.Operators::Extra => [ + // empty ], ]); }, ], - 'example' => [ - static function (self $test): GraphQLExpected { - return (new GraphQLExpected( - $test::getTestData()->file('~example-expected.graphql'), - )); + 'TypeRegistry' => [ + 'Example.expected.graphql', + 'Example.schema.graphql', + static function (): void { + $i = new class([ + 'name' => 'I', + 'fields' => [ + [ + 'name' => 'name', + 'type' => Type::string(), + ], + ], + ]) extends InputObjectType implements Ignored { + // empty + }; + $a = new InputObjectType([ + 'name' => 'A', + 'fields' => [ + [ + 'name' => 'name', + 'type' => Type::string(), + ], + [ + 'name' => 'flag', + 'type' => Type::nonNull(Type::boolean()), + ], + [ + 'name' => 'ignored', + 'type' => Type::nonNull($i), + ], + ], + ]); + $b = new InputObjectType([ + 'name' => 'B', + 'fields' => [ + [ + 'name' => 'name', + 'type' => Type::string(), + ], + [ + 'name' => 'child', + 'type' => $a, + ], + ], + ]); + $c = new ObjectType([ + 'name' => 'C', + 'fields' => [ + [ + 'name' => 'name', + 'type' => Type::string(), + ], + [ + 'name' => 'flag', + 'type' => Type::nonNull(Type::boolean()), + ], + [ + 'name' => 'list', + 'type' => Type::nonNull(Type::listOf(Type::nonNull(Type::boolean()))), + ], + ], + ]); + $d = new ObjectType([ + 'name' => 'D', + 'fields' => [ + [ + 'name' => 'child', + 'type' => Type::nonNull($c), + ], + ], + ]); + + $registry = Container::getInstance()->make(TypeRegistry::class); + $registry->register($a); + $registry->register($b); + $registry->register($c); + $registry->register($d); + $registry->register($i); }, - '~example.graphql', + ], + 'example' => [ + 'Example.expected.graphql', + 'Example.schema.graphql', null, ], ]; @@ -818,6 +850,16 @@ static function (object $builder, Property $property): string { // @phpcs:disable PSR1.Classes.ClassDeclaration.MultipleClasses // @phpcs:disable Squiz.Classes.ValidClassName.NotCamelCaps +/** + * @internal + * @noinspection PhpMultipleClassesDeclarationsInOneFile + */ +class DirectiveTest__Resolver { + public function __invoke(): mixed { + throw new Exception('Should not be called.'); + } +} + /** * @internal * @noinspection PhpMultipleClassesDeclarationsInOneFile diff --git a/packages/graphql/src/SortBy/Directives/DirectiveTest/AllowedDirectives.expected.graphql b/packages/graphql/src/SortBy/Directives/DirectiveTest/AllowedDirectives.expected.graphql new file mode 100644 index 000000000..0466f17f1 --- /dev/null +++ b/packages/graphql/src/SortBy/Directives/DirectiveTest/AllowedDirectives.expected.graphql @@ -0,0 +1,86 @@ +""" +Use Input as Sort Conditions for the current Builder. +""" +directive @sortBy +on + | ARGUMENT_DEFINITION + +directive @sortByOperatorField +on + | ENUM + | INPUT_FIELD_DEFINITION + | SCALAR + +""" +Sort direction. +""" +enum SortByTypeDirection { + Asc + Desc + + asc + @deprecated( + reason: "Please use `Asc` instead." + ) + + desc + @deprecated( + reason: "Please use `Desc` instead." + ) +} + +""" +Sort clause for `type A` (only one property allowed at a time). +""" +input SortByClauseA { + """ + Property clause. + """ + a: SortByTypeDirection + @sortByOperatorField + @rename( + attribute: "renamed" + ) +} + +""" +Sort clause for `interface B` (only one property allowed at a time). +""" +input SortByClauseB { + """ + Property clause. + """ + b: SortByTypeDirection + @sortByOperatorField + @rename( + attribute: "renamed" + ) +} + +interface B { + b: String! + @rename( + attribute: "renamed" + ) +} + +type A { + a: String! + @rename( + attribute: "renamed" + ) +} + +type Query { + a( + order: [SortByClauseA!] + @sortBy + ): A! + @all + + b( + order: [SortByClauseB!] + @sortBy + ): B! + @all +} diff --git a/packages/graphql/src/SortBy/Directives/DirectiveTest/AllowedDirectives.schema.graphql b/packages/graphql/src/SortBy/Directives/DirectiveTest/AllowedDirectives.schema.graphql new file mode 100644 index 000000000..33b672e41 --- /dev/null +++ b/packages/graphql/src/SortBy/Directives/DirectiveTest/AllowedDirectives.schema.graphql @@ -0,0 +1,12 @@ +type Query { + a(order: A @sortBy): A! @all + b(order: _ @sortBy): B! @all +} + +type A { + a: String! @rename(attribute: "renamed") +} + +interface B { + b: String! @rename(attribute: "renamed") +} diff --git a/packages/graphql/src/SortBy/Directives/DirectiveTest~example-expected.graphql b/packages/graphql/src/SortBy/Directives/DirectiveTest/Example.expected.graphql similarity index 100% rename from packages/graphql/src/SortBy/Directives/DirectiveTest~example-expected.graphql rename to packages/graphql/src/SortBy/Directives/DirectiveTest/Example.expected.graphql diff --git a/packages/graphql/src/SortBy/Directives/DirectiveTest~example.graphql b/packages/graphql/src/SortBy/Directives/DirectiveTest/Example.schema.graphql similarity index 100% rename from packages/graphql/src/SortBy/Directives/DirectiveTest~example.graphql rename to packages/graphql/src/SortBy/Directives/DirectiveTest/Example.schema.graphql diff --git a/packages/graphql/src/SortBy/Directives/DirectiveTest/Explicit.expected.graphql b/packages/graphql/src/SortBy/Directives/DirectiveTest/Explicit.expected.graphql new file mode 100644 index 000000000..9b687b42a --- /dev/null +++ b/packages/graphql/src/SortBy/Directives/DirectiveTest/Explicit.expected.graphql @@ -0,0 +1,126 @@ +""" +Use Input as Sort Conditions for the current Builder. +""" +directive @sortBy +on + | ARGUMENT_DEFINITION + +directive @sortByOperatorField +on + | ENUM + | INPUT_FIELD_DEFINITION + | SCALAR + +directive @sortByOperatorNullsFirst +on + | ENUM + | INPUT_FIELD_DEFINITION + | SCALAR + +directive @sortByOperatorNullsLast +on + | ENUM + | INPUT_FIELD_DEFINITION + | SCALAR + +directive @sortByOperatorProperty +on + | ENUM + | INPUT_FIELD_DEFINITION + | SCALAR + +""" +Sort direction. +""" +enum SortByTypeDirection { + Asc + Desc + + asc + @deprecated( + reason: "Please use `Asc` instead." + ) + + desc + @deprecated( + reason: "Please use `Desc` instead." + ) +} + +""" +Sort clause for `input A` (only one property allowed at a time). +""" +input SortByClauseA { + """ + Property clause. + """ + child: SortByClauseB + @sortByOperatorProperty + + """ + Description should be ignored. + """ + id: SortByTypeDirection + @sortByOperatorField + + """ + Property clause. + """ + name: SortByTypeDirection + @sortByOperatorField + + """ + NULLs first + """ + nullsFirst: SortByClauseA + @sortByOperatorNullsFirst + + """ + NULLs last + """ + nullsLast: SortByClauseA + @sortByOperatorNullsLast +} + +""" +Sort clause for `input B` (only one property allowed at a time). +""" +input SortByClauseB { + """ + Description should be ignored. + """ + id: SortByTypeDirection + @sortByOperatorField + + """ + Property clause. + """ + name: SortByTypeDirection + @sortByOperatorField + + """ + NULLs first + """ + nullsFirst: SortByClauseB + @sortByOperatorNullsFirst + + """ + NULLs last + """ + nullsLast: SortByClauseB + @sortByOperatorNullsLast + + """ + Property clause. + """ + parent: SortByClauseA + @sortByOperatorProperty +} + +type Query { + a( + order: [SortByClauseA!] + @sortBy + ): ID! + @all +} diff --git a/packages/graphql/src/SortBy/Directives/DirectiveTest/Explicit.schema.graphql b/packages/graphql/src/SortBy/Directives/DirectiveTest/Explicit.schema.graphql new file mode 100644 index 000000000..09752b82e --- /dev/null +++ b/packages/graphql/src/SortBy/Directives/DirectiveTest/Explicit.schema.graphql @@ -0,0 +1,17 @@ +type Query { + a(order: A @sortBy): ID! @all +} + +input A { + "Description should be ignored." + id: ID! + name: String! + child: B! +} + +input B { + "Description should be ignored." + id: ID! + name: String + parent: A! +} diff --git a/packages/graphql/src/SortBy/Directives/DirectiveTest/Ignored.expected.graphql b/packages/graphql/src/SortBy/Directives/DirectiveTest/Ignored.expected.graphql new file mode 100644 index 000000000..1444533e9 --- /dev/null +++ b/packages/graphql/src/SortBy/Directives/DirectiveTest/Ignored.expected.graphql @@ -0,0 +1,182 @@ +""" +Use Input as Sort Conditions for the current Builder. +""" +directive @sortBy +on + | ARGUMENT_DEFINITION + +""" +Marks that field/definition should be excluded from sort. +""" +directive @sortByIgnored +on + | ENUM + | FIELD_DEFINITION + | INPUT_FIELD_DEFINITION + | INPUT_OBJECT + | OBJECT + | SCALAR + +directive @sortByOperatorField +on + | ENUM + | INPUT_FIELD_DEFINITION + | SCALAR + +enum IgnoredEnum +@sortByIgnored +{ + One +} + +""" +Sort direction. +""" +enum SortByTypeDirection { + Asc + Desc + + asc + @deprecated( + reason: "Please use `Asc` instead." + ) + + desc + @deprecated( + reason: "Please use `Desc` instead." + ) +} + +input A { + """ + Not ignored + """ + a: String! + + """ + List + """ + b: [String!] + + """ + Marked by @sortByIgnored + """ + c: IgnoredInput! + + d: IgnoredDate! + e: [IgnoredDate!]! + f: IgnoredEnum! + + g: String + @sortByIgnored +} + +input IgnoredInput +@sortByIgnored +{ + id: ID +} + +input IgnoredType { + name: String! +} + +""" +Sort clause for `input A` (only one property allowed at a time). +""" +input SortByClauseA { + """ + Not ignored + """ + a: SortByTypeDirection + @sortByOperatorField +} + +""" +Sort clause for `interface B` (only one property allowed at a time). +""" +input SortByClauseB { + """ + Property clause. + """ + a: SortByTypeDirection + @sortByOperatorField +} + +interface B { + """ + Not ignored + """ + a: String! + + """ + List + """ + b: [String!] + + """ + Marked by @sortByIgnored + """ + c: IgnoredType! + + d: IgnoredDate! + e: [IgnoredDate!]! + f: IgnoredEnum! + + g: String + @sortByIgnored + + """ + Resolver + """ + h: Float + @field( + resolver: "\\LastDragon_ru\\LaraASP\\GraphQL\\SortBy\\Directives\\DirectiveTest__Resolver" + ) + + """ + Arguments + """ + i( + arg: String + ): Int! + + """ + Union + """ + j: ObjectUnion + + k: [ObjectUnion!] +} + +scalar IgnoredDate +@scalar( + class: "Nuwave\\Lighthouse\\Schema\\Types\\Scalars\\Date" +) +@sortByIgnored + +type ObjectA { + id: ID! +} + +type ObjectB { + id: ID! +} + +type Query { + a( + order: [SortByClauseA!] + @sortBy + ): A! + @all + + b( + order: [SortByClauseB!] + @sortBy + ): B! + @all +} + +union ObjectUnion = + | ObjectA + | ObjectB diff --git a/packages/graphql/src/SortBy/Directives/DirectiveTest/Ignored.schema.graphql b/packages/graphql/src/SortBy/Directives/DirectiveTest/Ignored.schema.graphql new file mode 100644 index 000000000..cb25030fa --- /dev/null +++ b/packages/graphql/src/SortBy/Directives/DirectiveTest/Ignored.schema.graphql @@ -0,0 +1,68 @@ +type Query { + a(order: A @sortBy): A! @all + b(order: _ @sortBy): B! @all +} + +input A { + "Not ignored" + a: String! + + "List" + b: [String!] + + "Marked by @sortByIgnored" + c: IgnoredInput! + d: IgnoredDate! + e: [IgnoredDate!]! + f: IgnoredEnum! + g: String @sortByIgnored +} + +interface B { + "Not ignored" + a: String! + + "List" + b: [String!] + + "Marked by @sortByIgnored" + c: IgnoredType! + d: IgnoredDate! + e: [IgnoredDate!]! + f: IgnoredEnum! + g: String @sortByIgnored + + "Resolver" + h: Float @field( + resolver: "\\LastDragon_ru\\LaraASP\\GraphQL\\SortBy\\Directives\\DirectiveTest__Resolver" + ) + + "Arguments" + i(arg: String): Int! + + "Union" + j: ObjectUnion + k: [ObjectUnion!] +} + +input IgnoredInput @sortByIgnored { + id: ID +} + +scalar IgnoredDate +@scalar(class: "Nuwave\\Lighthouse\\Schema\\Types\\Scalars\\Date") +@sortByIgnored + +enum IgnoredEnum @sortByIgnored { + One +} + +union ObjectUnion = ObjectA | ObjectB + +type ObjectA { + id: ID! +} + +type ObjectB { + id: ID! +} diff --git a/packages/graphql/src/SortBy/Directives/DirectiveTest/Implicit.expected.graphql b/packages/graphql/src/SortBy/Directives/DirectiveTest/Implicit.expected.graphql new file mode 100644 index 000000000..075c3020f --- /dev/null +++ b/packages/graphql/src/SortBy/Directives/DirectiveTest/Implicit.expected.graphql @@ -0,0 +1,147 @@ +""" +Use Input as Sort Conditions for the current Builder. +""" +directive @sortBy +on + | ARGUMENT_DEFINITION + +directive @sortByOperatorField +on + | ENUM + | INPUT_FIELD_DEFINITION + | SCALAR + +directive @sortByOperatorNullsFirst +on + | ENUM + | INPUT_FIELD_DEFINITION + | SCALAR + +directive @sortByOperatorNullsLast +on + | ENUM + | INPUT_FIELD_DEFINITION + | SCALAR + +directive @sortByOperatorProperty +on + | ENUM + | INPUT_FIELD_DEFINITION + | SCALAR + +""" +Sort direction. +""" +enum SortByTypeDirection { + Asc + Desc + + asc + @deprecated( + reason: "Please use `Asc` instead." + ) + + desc + @deprecated( + reason: "Please use `Desc` instead." + ) +} + +""" +Sort clause for `type A` (only one property allowed at a time). +""" +input SortByClauseA { + """ + Property clause. + """ + child: SortByClauseB + @sortByOperatorProperty + + """ + Property clause. + """ + id: SortByTypeDirection + @sortByOperatorField + + """ + Property clause. + """ + name: SortByTypeDirection + @sortByOperatorField + + """ + NULLs first + """ + nullsFirst: SortByClauseA + @sortByOperatorNullsFirst + + """ + NULLs last + """ + nullsLast: SortByClauseA + @sortByOperatorNullsLast +} + +""" +Sort clause for `type B` (only one property allowed at a time). +""" +input SortByClauseB { + """ + Property clause. + """ + id: SortByTypeDirection + @sortByOperatorField + + """ + Property clause. + """ + name: SortByTypeDirection + @sortByOperatorField + + """ + NULLs first + """ + nullsFirst: SortByClauseB + @sortByOperatorNullsFirst + + """ + NULLs last + """ + nullsLast: SortByClauseB + @sortByOperatorNullsLast + + """ + Property clause. + """ + parent: SortByClauseA + @sortByOperatorProperty +} + +type A { + child: B! + + """ + Description should be copied. + """ + id: ID! + + name: String! +} + +type B { + """ + Description should be copied. + """ + id: ID! + + name: String + parent: A! +} + +type Query { + a( + order: [SortByClauseA!] + @sortBy + ): [A!]! + @all +} diff --git a/packages/graphql/src/SortBy/Directives/DirectiveTest/Implicit.schema.graphql b/packages/graphql/src/SortBy/Directives/DirectiveTest/Implicit.schema.graphql new file mode 100644 index 000000000..5512d4fe2 --- /dev/null +++ b/packages/graphql/src/SortBy/Directives/DirectiveTest/Implicit.schema.graphql @@ -0,0 +1,17 @@ +type Query { + a(order: _ @sortBy): [A!]! @all +} + +type A { + "Description should be copied." + id: ID! + name: String! + child: B! +} + +type B { + "Description should be copied." + id: ID! + name: String + parent: A! +} diff --git a/packages/graphql/src/SortBy/Directives/DirectiveTest/InterfaceUpdate.expected.graphql b/packages/graphql/src/SortBy/Directives/DirectiveTest/InterfaceUpdate.expected.graphql new file mode 100644 index 000000000..221790bab --- /dev/null +++ b/packages/graphql/src/SortBy/Directives/DirectiveTest/InterfaceUpdate.expected.graphql @@ -0,0 +1,103 @@ +""" +Use Input as Sort Conditions for the current Builder. +""" +directive @sortBy +on + | ARGUMENT_DEFINITION + +directive @sortByOperatorField +on + | ENUM + | INPUT_FIELD_DEFINITION + | SCALAR + +""" +Sort direction. +""" +enum SortByTypeDirection { + Asc + Desc + + asc + @deprecated( + reason: "Please use `Asc` instead." + ) + + desc + @deprecated( + reason: "Please use `Desc` instead." + ) +} + +input A { + id: ID! +} + +""" +Sort clause for `input A` (only one property allowed at a time). +""" +input SortByClauseA { + """ + Property clause. + """ + id: SortByTypeDirection + @sortByOperatorField +} + +""" +Sort clause for `type B` (only one property allowed at a time). +""" +input SortByClauseB { + """ + Property clause. + """ + id: SortByTypeDirection + @sortByOperatorField +} + +interface QueryInterface +implements + & QueryInterfaceA + & QueryInterfaceB +{ + a( + order: [SortByClauseA!] + ): A! + + b( + order: [SortByClauseB!] + ): B! +} + +interface QueryInterfaceA { + a( + order: [SortByClauseA!] + ): A! +} + +interface QueryInterfaceB { + b( + order: [SortByClauseB!] + ): B! +} + +type B { + id: ID! +} + +type Query +implements + & QueryInterface +{ + a( + order: [SortByClauseA!] + @sortBy + ): A! + @all + + b( + order: [SortByClauseB!] + @sortBy + ): B! + @all +} diff --git a/packages/graphql/src/SortBy/Directives/DirectiveTest/InterfaceUpdate.schema.graphql b/packages/graphql/src/SortBy/Directives/DirectiveTest/InterfaceUpdate.schema.graphql new file mode 100644 index 000000000..5cfa826cd --- /dev/null +++ b/packages/graphql/src/SortBy/Directives/DirectiveTest/InterfaceUpdate.schema.graphql @@ -0,0 +1,25 @@ +type Query implements QueryInterface { + a(order: A @sortBy): A! @all + b(order: _ @sortBy): B! @all +} + +input A { + id: ID! +} + +type B { + id: ID! +} + +interface QueryInterface implements QueryInterfaceA & QueryInterfaceB { + a(order: A): A! + b(order: _): B! +} + +interface QueryInterfaceA { + a(order: A): A! +} + +interface QueryInterfaceB { + b(order: _): B! +} diff --git a/packages/graphql/src/SortBy/Directives/DirectiveTest/Query.expected.graphql b/packages/graphql/src/SortBy/Directives/DirectiveTest/Query.expected.graphql new file mode 100644 index 000000000..7ad229588 --- /dev/null +++ b/packages/graphql/src/SortBy/Directives/DirectiveTest/Query.expected.graphql @@ -0,0 +1,130 @@ +""" +Use Input as Sort Conditions for the current Builder. +""" +directive @sortBy +on + | ARGUMENT_DEFINITION + +directive @sortByOperatorField +on + | ENUM + | INPUT_FIELD_DEFINITION + | SCALAR + +directive @sortByOperatorNullsFirst +on + | ENUM + | INPUT_FIELD_DEFINITION + | SCALAR + +directive @sortByOperatorNullsLast +on + | ENUM + | INPUT_FIELD_DEFINITION + | SCALAR + +""" +Sort direction. +""" +enum SortByTypeDirection { + Asc + Desc + + asc + @deprecated( + reason: "Please use `Asc` instead." + ) + + desc + @deprecated( + reason: "Please use `Desc` instead." + ) +} + +""" +Sort clause for `input InputA` (only one property allowed at a time). +""" +input SortByQueryClauseInputA { + """ + Property clause. + """ + id: SortByTypeDirection + @sortByOperatorField + + """ + Property clause. + """ + name: SortByTypeDirection + @sortByOperatorField + + """ + NULLs first + """ + nullsFirst: SortByQueryClauseInputA + @sortByOperatorNullsFirst + + """ + NULLs last + """ + nullsLast: SortByQueryClauseInputA + @sortByOperatorNullsLast +} + +""" +Sort clause for `input TypeB` (only one property allowed at a time). +""" +input SortByQueryClauseTypeB { + """ + Property clause. + """ + id: SortByTypeDirection + @sortByOperatorField + + """ + Property clause. + """ + name: SortByTypeDirection + @sortByOperatorField + + """ + NULLs first + """ + nullsFirst: SortByQueryClauseTypeB + @sortByOperatorNullsFirst + + """ + NULLs last + """ + nullsLast: SortByQueryClauseTypeB + @sortByOperatorNullsLast +} + +input TypeA { + child: TypeB! + id: ID! + name: String! +} + +input TypeB { + id: ID! + name: String + parent: TypeA! +} + +type Query { + a( + order: [SortByQueryClauseInputA!] + @sortBy + ): ID! + @all( + builder: "\\LastDragon_ru\\LaraASP\\GraphQL\\SortBy\\Directives\\DirectiveTest__QueryBuilderResolver" + ) + + b( + order: [SortByQueryClauseTypeB!] + @sortBy + ): [TypeB!]! + @all( + builder: "\\LastDragon_ru\\LaraASP\\GraphQL\\SortBy\\Directives\\DirectiveTest__QueryBuilderResolver" + ) +} diff --git a/packages/graphql/src/SortBy/Directives/DirectiveTest/Query.schema.graphql b/packages/graphql/src/SortBy/Directives/DirectiveTest/Query.schema.graphql new file mode 100644 index 000000000..7af84528d --- /dev/null +++ b/packages/graphql/src/SortBy/Directives/DirectiveTest/Query.schema.graphql @@ -0,0 +1,32 @@ +type Query { + a(order: InputA @sortBy): ID! @all( + builder: "\\LastDragon_ru\\LaraASP\\GraphQL\\SortBy\\Directives\\DirectiveTest__QueryBuilderResolver" + ) + b(order: _ @sortBy): [TypeB!]! @all( + builder: "\\LastDragon_ru\\LaraASP\\GraphQL\\SortBy\\Directives\\DirectiveTest__QueryBuilderResolver" + ) +} + +input InputA { + id: ID! + name: String! + child: InputB! +} + +input InputB { + id: ID! + name: String + parent: InputA! +} + +input TypeA { + id: ID! + name: String! + child: TypeB! +} + +input TypeB { + id: ID! + name: String + parent: TypeA! +} diff --git a/packages/graphql/src/SortBy/Directives/DirectiveTest~scout-expected.graphql b/packages/graphql/src/SortBy/Directives/DirectiveTest/Scout.expected.graphql similarity index 100% rename from packages/graphql/src/SortBy/Directives/DirectiveTest~scout-expected.graphql rename to packages/graphql/src/SortBy/Directives/DirectiveTest/Scout.expected.graphql diff --git a/packages/graphql/src/SortBy/Directives/DirectiveTest~scout.graphql b/packages/graphql/src/SortBy/Directives/DirectiveTest/Scout.schema.graphql similarity index 100% rename from packages/graphql/src/SortBy/Directives/DirectiveTest~scout.graphql rename to packages/graphql/src/SortBy/Directives/DirectiveTest/Scout.schema.graphql diff --git a/packages/graphql/src/SortBy/Directives/DirectiveTest~registry-expected.graphql b/packages/graphql/src/SortBy/Directives/DirectiveTest/TypeRegistry.expected.graphql similarity index 100% rename from packages/graphql/src/SortBy/Directives/DirectiveTest~registry-expected.graphql rename to packages/graphql/src/SortBy/Directives/DirectiveTest/TypeRegistry.expected.graphql diff --git a/packages/graphql/src/SortBy/Directives/DirectiveTest~registry.graphql b/packages/graphql/src/SortBy/Directives/DirectiveTest/TypeRegistry.schema.graphql similarity index 100% rename from packages/graphql/src/SortBy/Directives/DirectiveTest~registry.graphql rename to packages/graphql/src/SortBy/Directives/DirectiveTest/TypeRegistry.schema.graphql diff --git a/packages/graphql/src/SortBy/Directives/DirectiveTest~full-expected.graphql b/packages/graphql/src/SortBy/Directives/DirectiveTest~full-expected.graphql deleted file mode 100644 index 18815ca22..000000000 --- a/packages/graphql/src/SortBy/Directives/DirectiveTest~full-expected.graphql +++ /dev/null @@ -1,670 +0,0 @@ -""" -Use Input as Sort Conditions for the current Builder. -""" -directive @sortBy -on - | ARGUMENT_DEFINITION - -""" -Marks that field/definition should be excluded from sort. -""" -directive @sortByIgnored -on - | ENUM - | FIELD_DEFINITION - | INPUT_FIELD_DEFINITION - | INPUT_OBJECT - | OBJECT - | SCALAR - -directive @sortByOperatorField -on - | ENUM - | INPUT_FIELD_DEFINITION - | SCALAR - -directive @sortByOperatorNullsFirst -on - | ENUM - | INPUT_FIELD_DEFINITION - | SCALAR - -directive @sortByOperatorNullsLast -on - | ENUM - | INPUT_FIELD_DEFINITION - | SCALAR - -directive @sortByOperatorProperty -on - | ENUM - | INPUT_FIELD_DEFINITION - | SCALAR - -directive @sortByOperatorRandom -on - | ENUM - | FIELD_DEFINITION - | INPUT_FIELD_DEFINITION - | SCALAR - -enum EnumIgnored -@sortByIgnored -{ - One -} - -""" -Sort direction. -""" -enum SortByTypeDirection { - Asc - Desc - - asc - @deprecated( - reason: "Please use `Asc` instead." - ) - - desc - @deprecated( - reason: "Please use `Desc` instead." - ) -} - -enum SortByTypeFlag { - Yes - - yes - @deprecated( - reason: "Please use `Yes` instead." - ) -} - -enum Value { - One - Two -} - -""" -Sort clause for `input Nested` (only one property allowed at a time). -""" -input SortByClauseNested { - """ - Property clause. - """ - nested: SortByClauseNested - @sortByOperatorProperty - - """ - NULLs first - """ - nullsFirst: SortByClauseNested - @sortByOperatorNullsFirst - - """ - NULLs last - """ - nullsLast: SortByClauseNested - @sortByOperatorNullsLast - - """ - By random - """ - random: SortByTypeFlag - @sortByOperatorRandom - - """ - Property clause. - """ - value: SortByTypeDirection - @sortByOperatorField -} - -""" -Sort clause for `type Object` (only one property allowed at a time). -""" -input SortByClauseObject { - """ - Property clause. - """ - customScalar: SortByTypeDirection - @sortByOperatorField - - """ - Property clause. - """ - customScalarNonNull: SortByTypeDirection - @sortByOperatorField - - """ - Property clause. - """ - description: SortByTypeDirection - @sortByOperatorField - - """ - Property clause. - """ - enum: SortByTypeDirection - @sortByOperatorField - - """ - Property clause. - """ - enumNotNull: SortByTypeDirection - @sortByOperatorField - - """ - Property clause. - """ - fieldRenamed: SortByTypeDirection - @sortByOperatorField - @rename( - attribute: "renamed" - ) - - """ - Property clause. - """ - idScalar: SortByTypeDirection - @sortByOperatorField - - """ - Property clause. - """ - idScalarNotNull: SortByTypeDirection - @sortByOperatorField - - """ - Property clause. - """ - nested: SortByClauseObjectNested - @sortByOperatorProperty - - """ - Property clause. - """ - nestedNotNull: SortByClauseObjectNested - @sortByOperatorProperty - - """ - NULLs first - """ - nullsFirst: SortByClauseObject - @sortByOperatorNullsFirst - - """ - NULLs last - """ - nullsLast: SortByClauseObject - @sortByOperatorNullsLast - - """ - By random - """ - random: SortByTypeFlag - @sortByOperatorRandom -} - -""" -Sort clause for `interface ObjectInterface` (only one property allowed at a time). -""" -input SortByClauseObjectInterface { - """ - Property clause. - """ - customScalar: SortByTypeDirection - @sortByOperatorField - - """ - Property clause. - """ - customScalarNonNull: SortByTypeDirection - @sortByOperatorField - - """ - Property clause. - """ - description: SortByTypeDirection - @sortByOperatorField - - """ - Property clause. - """ - enum: SortByTypeDirection - @sortByOperatorField - - """ - Property clause. - """ - enumNotNull: SortByTypeDirection - @sortByOperatorField - - """ - Property clause. - """ - fieldRenamed: SortByTypeDirection - @sortByOperatorField - @rename( - attribute: "renamed" - ) - - """ - Property clause. - """ - idScalar: SortByTypeDirection - @sortByOperatorField - - """ - Property clause. - """ - idScalarNotNull: SortByTypeDirection - @sortByOperatorField - - """ - Property clause. - """ - nested: SortByClauseObjectNested - @sortByOperatorProperty - - """ - Property clause. - """ - nestedNotNull: SortByClauseObjectNested - @sortByOperatorProperty - - """ - NULLs first - """ - nullsFirst: SortByClauseObjectInterface - @sortByOperatorNullsFirst - - """ - NULLs last - """ - nullsLast: SortByClauseObjectInterface - @sortByOperatorNullsLast - - """ - By random - """ - random: SortByTypeFlag - @sortByOperatorRandom -} - -""" -Sort clause for `type ObjectNested` (only one property allowed at a time). -""" -input SortByClauseObjectNested { - """ - Property clause. - """ - nested: SortByClauseObjectNested - @sortByOperatorProperty - - """ - NULLs first - """ - nullsFirst: SortByClauseObjectNested - @sortByOperatorNullsFirst - - """ - NULLs last - """ - nullsLast: SortByClauseObjectNested - @sortByOperatorNullsLast - - """ - By random - """ - random: SortByTypeFlag - @sortByOperatorRandom - - """ - Property clause. - """ - value: SortByTypeDirection - @sortByOperatorField -} - -""" -Sort clause for `input Properties` (only one property allowed at a time). -""" -input SortByClauseProperties { - """ - Property clause. - """ - customScalar: SortByTypeDirection - @sortByOperatorField - - """ - Property clause. - """ - customScalarNonNull: SortByTypeDirection - @sortByOperatorField - - """ - Description should be used. - """ - description: SortByTypeDirection - @sortByOperatorField - - """ - Property clause. - """ - enum: SortByTypeDirection - @sortByOperatorField - - """ - Property clause. - """ - enumNotNull: SortByTypeDirection - @sortByOperatorField - - """ - Property clause. - """ - fieldRenamed: SortByTypeDirection - @sortByOperatorField - @rename( - attribute: "renamed" - ) - - """ - Property clause. - """ - idScalar: SortByTypeDirection - @sortByOperatorField - - """ - Property clause. - """ - idScalarNotNull: SortByTypeDirection - @sortByOperatorField - - """ - Property clause. - """ - nested: SortByClauseNested - @sortByOperatorProperty - - """ - Property clause. - """ - nestedNotNull: SortByClauseNested - @sortByOperatorProperty - - """ - NULLs first - """ - nullsFirst: SortByClauseProperties - @sortByOperatorNullsFirst - - """ - NULLs last - """ - nullsLast: SortByClauseProperties - @sortByOperatorNullsLast - - """ - By random - """ - random: SortByTypeFlag - @sortByOperatorRandom -} - -""" -Sort clause for `input Properties` (only one property allowed at a time). -""" -input SortByQueryClauseProperties { - """ - Property clause. - """ - customScalar: SortByTypeDirection - @sortByOperatorField - - """ - Property clause. - """ - customScalarNonNull: SortByTypeDirection - @sortByOperatorField - - """ - Description should be used. - """ - description: SortByTypeDirection - @sortByOperatorField - - """ - Property clause. - """ - enum: SortByTypeDirection - @sortByOperatorField - - """ - Property clause. - """ - enumNotNull: SortByTypeDirection - @sortByOperatorField - - """ - Property clause. - """ - fieldRenamed: SortByTypeDirection - @sortByOperatorField - @rename( - attribute: "renamed" - ) - - """ - Property clause. - """ - idScalar: SortByTypeDirection - @sortByOperatorField - - """ - Property clause. - """ - idScalarNotNull: SortByTypeDirection - @sortByOperatorField - - """ - NULLs first - """ - nullsFirst: SortByQueryClauseProperties - @sortByOperatorNullsFirst - - """ - NULLs last - """ - nullsLast: SortByQueryClauseProperties - @sortByOperatorNullsLast - - """ - By random - """ - random: SortByTypeFlag - @sortByOperatorRandom -} - -interface Eloquent -implements - & Placeholder -{ - eloquent( - order: [SortByClauseProperties!] - ): ID! -} - -interface ObjectInterface { - customScalar: Date - customScalarIgnored: DateIgnored - customScalarIgnoredList: [DateIgnored!]! - customScalarIgnoredNonNull: DateIgnored! - customScalarList: [Date!]! - customScalarNonNull: Date! - - """ - Description should NOT be used. - """ - description: ID - - enum: Value - enumEnumIgnoredNotNull: EnumIgnored! - enumIgnored: EnumIgnored - enumNotNull: Value! - - fieldRenamed: ID - @rename( - attribute: "renamed" - ) - - """ - Should be ignored - """ - fieldWithArguments( - arg: Int - ): Boolean - - idScalar: ID - idScalarNotNull: ID! - - ignored: ID! - @sortByIgnored - - ignoredType: ObjectIgnored - ignoredTypeList: [ObjectIgnored!]! - ignoredTypeNonNull: ObjectIgnored! - nested: ObjectNested - nestedNotNull: ObjectNested! - - resolver: ID! - @field( - resolver: "\\LastDragon_ru\\LaraASP\\GraphQL\\SortBy\\Directives\\DirectiveTest__QueryBuilderResolver" - ) - - union: ObjectUnion - unionList: [ObjectUnion!]! - unionNonNull: ObjectUnion! -} - -interface Placeholder { - placeholder( - order: [SortByClauseObject!] - @sortBy - ): Object! - @all -} - -scalar Date -@scalar( - class: "Nuwave\\Lighthouse\\Schema\\Types\\Scalars\\Date" -) - -scalar DateIgnored -@scalar( - class: "Nuwave\\Lighthouse\\Schema\\Types\\Scalars\\Date" -) -@sortByIgnored - -type Object -implements - & ObjectInterface -{ - customScalar: Date - customScalarIgnored: DateIgnored - customScalarIgnoredList: [DateIgnored!]! - customScalarIgnoredNonNull: DateIgnored! - customScalarList: [Date!]! - customScalarNonNull: Date! - - """ - Description should NOT be used. - """ - description: ID - - enum: Value - enumEnumIgnoredNotNull: EnumIgnored! - enumIgnored: EnumIgnored - enumNotNull: Value! - - fieldRenamed: ID - @rename( - attribute: "renamed" - ) - - """ - Should be ignored - """ - fieldWithArguments( - arg: Int - ): Boolean - - idScalar: ID - idScalarNotNull: ID! - - ignored: ID! - @sortByIgnored - - ignoredType: ObjectIgnored - ignoredTypeList: [ObjectIgnored!]! - ignoredTypeNonNull: ObjectIgnored! - nested: ObjectNested - nestedNotNull: ObjectNested! - - resolver: ID! - @field( - resolver: "\\LastDragon_ru\\LaraASP\\GraphQL\\SortBy\\Directives\\DirectiveTest__QueryBuilderResolver" - ) - - union: ObjectUnion - unionList: [ObjectUnion!]! - unionNonNull: ObjectUnion! -} - -type ObjectIgnored -@sortByIgnored -{ - value: String -} - -type ObjectNested { - nested: ObjectNested - value: String -} - -type Query -implements - & Eloquent -{ - eloquent( - order: [SortByClauseProperties!] - @sortBy - ): ID! - @all - - interface( - order: [SortByClauseObjectInterface!] - @sortBy - ): Object! - @all - - placeholder( - order: [SortByClauseObject!] - @sortBy - ): Object! - @all - - query( - order: [SortByQueryClauseProperties!] - @sortBy - ): ID! - @all( - builder: "\\LastDragon_ru\\LaraASP\\GraphQL\\SortBy\\Directives\\DirectiveTest__QueryBuilderResolver" - ) -} - -union ObjectUnion = - | Object - | ObjectNested diff --git a/packages/graphql/src/SortBy/Directives/DirectiveTest~full.graphql b/packages/graphql/src/SortBy/Directives/DirectiveTest~full.graphql deleted file mode 100644 index 98a9ba142..000000000 --- a/packages/graphql/src/SortBy/Directives/DirectiveTest~full.graphql +++ /dev/null @@ -1,154 +0,0 @@ -type Query implements Eloquent { - eloquent(order: Properties @sortBy): ID! @all - - query(order: Properties @sortBy): ID! @all( - builder: "\\LastDragon_ru\\LaraASP\\GraphQL\\SortBy\\Directives\\DirectiveTest__QueryBuilderResolver" - ) - - placeholder(order: _ @sortBy): Object! @all - - interface(order: ObjectInterface @sortBy): Object! @all -} - -interface Eloquent implements Placeholder { - eloquent(order: Properties): ID! -} - -interface Placeholder { - placeholder(order: _ @sortBy): Object! @all -} - -input Properties { - idScalar: ID - idScalarNotNull: ID! - nested: Nested - nestedNotNull: Nested! - enum: Value - enumNotNull: Value! - enumIgnored: EnumIgnored - enumEnumIgnoredNotNull: EnumIgnored! - ignored: ID! @sortByIgnored - resolver: ID! @field( - resolver: "\\LastDragon_ru\\LaraASP\\GraphQL\\SortBy\\Directives\\DirectiveTest__QueryBuilderResolver" - ) - ignoredType: InputIgnored - ignoredTypeList: [InputIgnored!]! - ignoredTypeNonNull: InputIgnored! - customScalar: Date - customScalarList: [Date!]! - customScalarNonNull: Date! - customScalarIgnored: DateIgnored - customScalarIgnoredList: [DateIgnored!]! - customScalarIgnoredNonNull: DateIgnored! - - "Description should be used." - description: ID - - fieldRenamed: ID @rename(attribute: "renamed") -} - -input Nested { - value: String - nested: Nested -} - -input InputIgnored @sortByIgnored { - id: ID -} - -scalar Date -@scalar(class: "Nuwave\\Lighthouse\\Schema\\Types\\Scalars\\Date") - -scalar DateIgnored -@scalar(class: "Nuwave\\Lighthouse\\Schema\\Types\\Scalars\\Date") -@sortByIgnored - -enum Value { - One - Two -} - -enum EnumIgnored @sortByIgnored { - One -} - -interface ObjectInterface { - idScalar: ID - idScalarNotNull: ID! - nested: ObjectNested - nestedNotNull: ObjectNested! - enum: Value - enumNotNull: Value! - enumIgnored: EnumIgnored - enumEnumIgnoredNotNull: EnumIgnored! - ignored: ID! @sortByIgnored - resolver: ID! @field( - resolver: "\\LastDragon_ru\\LaraASP\\GraphQL\\SortBy\\Directives\\DirectiveTest__QueryBuilderResolver" - ) - union: ObjectUnion - unionList: [ObjectUnion!]! - unionNonNull: ObjectUnion! - ignoredType: ObjectIgnored - ignoredTypeList: [ObjectIgnored!]! - ignoredTypeNonNull: ObjectIgnored! - customScalar: Date - customScalarList: [Date!]! - customScalarNonNull: Date! - customScalarIgnored: DateIgnored - customScalarIgnoredList: [DateIgnored!]! - customScalarIgnoredNonNull: DateIgnored! - - "Description should NOT be used." - description: ID - - "Should be ignored" - fieldWithArguments(arg: Int): Boolean - - fieldRenamed: ID @rename(attribute: "renamed") -} - -type Object implements ObjectInterface { - idScalar: ID - idScalarNotNull: ID! - nested: ObjectNested - nestedNotNull: ObjectNested! - enum: Value - enumNotNull: Value! - enumIgnored: EnumIgnored - enumEnumIgnoredNotNull: EnumIgnored! - ignored: ID! @sortByIgnored - resolver: ID! @field( - resolver: "\\LastDragon_ru\\LaraASP\\GraphQL\\SortBy\\Directives\\DirectiveTest__QueryBuilderResolver" - ) - union: ObjectUnion - unionList: [ObjectUnion!]! - unionNonNull: ObjectUnion! - ignoredType: ObjectIgnored - ignoredTypeList: [ObjectIgnored!]! - ignoredTypeNonNull: ObjectIgnored! - customScalar: Date - customScalarList: [Date!]! - customScalarNonNull: Date! - customScalarIgnored: DateIgnored - customScalarIgnoredList: [DateIgnored!]! - customScalarIgnoredNonNull: DateIgnored! - - "Description should NOT be used." - description: ID - - "Should be ignored" - fieldWithArguments(arg: Int): Boolean - - fieldRenamed: ID @rename(attribute: "renamed") -} - -type ObjectNested { - value: String - nested: ObjectNested -} - -union ObjectUnion = Object | ObjectNested - -type ObjectIgnored @sortByIgnored { - value: String -} From 099c681c70ac901d40e4f9f9348868f64861aa0f Mon Sep 17 00:00:00 2001 From: Aleksei Lebedev <1329824+LastDragon-ru@users.noreply.github.com> Date: Tue, 23 Jan 2024 13:48:11 +0400 Subject: [PATCH 06/26] More tests. --- .../DirectiveTest/Explicit.expected.graphql | 6 +++++ .../DirectiveTest/Explicit.schema.graphql | 1 + .../DirectiveTest/Implicit.expected.graphql | 23 +++++++++++++++++++ .../DirectiveTest/Implicit.schema.graphql | 5 ++++ .../DirectiveTest/Explicit.schema.graphql | 1 + .../DirectiveTest/Implicit.expected.graphql | 17 ++++++++++++++ .../DirectiveTest/Implicit.schema.graphql | 5 ++++ 7 files changed, 58 insertions(+) diff --git a/packages/graphql/src/SearchBy/Directives/DirectiveTest/Explicit.expected.graphql b/packages/graphql/src/SearchBy/Directives/DirectiveTest/Explicit.expected.graphql index d90eb25d3..9dbd354dd 100644 --- a/packages/graphql/src/SearchBy/Directives/DirectiveTest/Explicit.expected.graphql +++ b/packages/graphql/src/SearchBy/Directives/DirectiveTest/Explicit.expected.graphql @@ -269,6 +269,12 @@ input SearchByConditionA { anyOf: [SearchByConditionA!] @searchByOperatorAnyOf + """ + Relationship condition. + """ + child: SearchByComplexRelationB + @searchByOperatorRelation + """ Relationship condition. """ diff --git a/packages/graphql/src/SearchBy/Directives/DirectiveTest/Explicit.schema.graphql b/packages/graphql/src/SearchBy/Directives/DirectiveTest/Explicit.schema.graphql index 874661ffe..b4040b618 100644 --- a/packages/graphql/src/SearchBy/Directives/DirectiveTest/Explicit.schema.graphql +++ b/packages/graphql/src/SearchBy/Directives/DirectiveTest/Explicit.schema.graphql @@ -6,6 +6,7 @@ input A { "Description should be copied." id: ID! name: String! + child: B! children: [B!]! } diff --git a/packages/graphql/src/SearchBy/Directives/DirectiveTest/Implicit.expected.graphql b/packages/graphql/src/SearchBy/Directives/DirectiveTest/Implicit.expected.graphql index 764b17bf9..8f6b19d27 100644 --- a/packages/graphql/src/SearchBy/Directives/DirectiveTest/Implicit.expected.graphql +++ b/packages/graphql/src/SearchBy/Directives/DirectiveTest/Implicit.expected.graphql @@ -269,6 +269,12 @@ input SearchByConditionA { anyOf: [SearchByConditionA!] @searchByOperatorAnyOf + """ + Relationship condition. + """ + child: SearchByComplexRelationB + @searchByOperatorRelation + """ Relationship condition. """ @@ -631,6 +637,15 @@ input SearchByTypeRangeInt { } type A { + argument( + arg: String + ): A! + + arguments( + arg: String + ): [A!] + + child: B! children: [B!]! """ @@ -642,6 +657,14 @@ type A { } type B { + argument( + arg: String + ): B! + + arguments( + arg: String + ): [B!] + """ Description should be ignored. """ diff --git a/packages/graphql/src/SearchBy/Directives/DirectiveTest/Implicit.schema.graphql b/packages/graphql/src/SearchBy/Directives/DirectiveTest/Implicit.schema.graphql index 60c706bc3..a85e679f5 100644 --- a/packages/graphql/src/SearchBy/Directives/DirectiveTest/Implicit.schema.graphql +++ b/packages/graphql/src/SearchBy/Directives/DirectiveTest/Implicit.schema.graphql @@ -6,7 +6,10 @@ type A { "Description should be ignored." id: ID! name: String! + child: B! children: [B!]! + argument(arg: String): A! + arguments(arg: String): [A!] } type B { @@ -14,4 +17,6 @@ type B { id: ID! name: String parent: A! + argument(arg: String): B! + arguments(arg: String): [B!] } diff --git a/packages/graphql/src/SortBy/Directives/DirectiveTest/Explicit.schema.graphql b/packages/graphql/src/SortBy/Directives/DirectiveTest/Explicit.schema.graphql index 09752b82e..d204e22ba 100644 --- a/packages/graphql/src/SortBy/Directives/DirectiveTest/Explicit.schema.graphql +++ b/packages/graphql/src/SortBy/Directives/DirectiveTest/Explicit.schema.graphql @@ -7,6 +7,7 @@ input A { id: ID! name: String! child: B! + children: [B!]! } input B { diff --git a/packages/graphql/src/SortBy/Directives/DirectiveTest/Implicit.expected.graphql b/packages/graphql/src/SortBy/Directives/DirectiveTest/Implicit.expected.graphql index 075c3020f..7b4dcb46f 100644 --- a/packages/graphql/src/SortBy/Directives/DirectiveTest/Implicit.expected.graphql +++ b/packages/graphql/src/SortBy/Directives/DirectiveTest/Implicit.expected.graphql @@ -118,7 +118,16 @@ input SortByClauseB { } type A { + argument( + arg: String + ): A! + + arguments( + arg: String + ): [A!] + child: B! + children: [B!]! """ Description should be copied. @@ -129,6 +138,14 @@ type A { } type B { + argument( + arg: String + ): B! + + arguments( + arg: String + ): [B!] + """ Description should be copied. """ diff --git a/packages/graphql/src/SortBy/Directives/DirectiveTest/Implicit.schema.graphql b/packages/graphql/src/SortBy/Directives/DirectiveTest/Implicit.schema.graphql index 5512d4fe2..ec59e5088 100644 --- a/packages/graphql/src/SortBy/Directives/DirectiveTest/Implicit.schema.graphql +++ b/packages/graphql/src/SortBy/Directives/DirectiveTest/Implicit.schema.graphql @@ -7,6 +7,9 @@ type A { id: ID! name: String! child: B! + children: [B!]! + argument(arg: String): A! + arguments(arg: String): [A!] } type B { @@ -14,4 +17,6 @@ type B { id: ID! name: String parent: A! + argument(arg: String): B! + arguments(arg: String): [B!] } From 9f5964a01edae6d352739b6d4d730c7c16bed9a2 Mon Sep 17 00:00:00 2001 From: Aleksei Lebedev <1329824+LastDragon-ru@users.noreply.github.com> Date: Wed, 24 Jan 2024 11:25:18 +0400 Subject: [PATCH 07/26] Added `AstManipulator::isObject()`. --- packages/graphql/src/Utils/AstManipulator.php | 67 ++++++++++++++----- 1 file changed, 50 insertions(+), 17 deletions(-) diff --git a/packages/graphql/src/Utils/AstManipulator.php b/packages/graphql/src/Utils/AstManipulator.php index c91c8e8b5..05b368815 100644 --- a/packages/graphql/src/Utils/AstManipulator.php +++ b/packages/graphql/src/Utils/AstManipulator.php @@ -162,24 +162,28 @@ public function isList( public function isUnion( Node|Type|InputObjectField|FieldDefinition|TypeDefinitionNode $node, ): bool { - $type = null; - - if ($node instanceof WrappingType) { - $type = $node->getInnermostType(); - } elseif ($node instanceof Node) { - try { - $type = $this->getTypeDefinition($node); - } catch (TypeDefinitionUnknown) { - // empty - } - } else { - // empty - } + $type = $this->getType($node); return $type instanceof UnionTypeDefinitionNode || $type instanceof UnionType; } + /** + * @param Node|Type|InputObjectField|FieldDefinition|(TypeDefinitionNode&Node) $node + */ + public function isObject( + Node|Type|InputObjectField|FieldDefinition|TypeDefinitionNode $node, + ): bool { + $type = $this->getType($node); + + return $type instanceof ObjectTypeDefinitionNode + || $type instanceof ObjectType + || $type instanceof InterfaceTypeDefinitionNode + || $type instanceof InterfaceType + || $type instanceof InputObjectTypeDefinitionNode + || $type instanceof InputObjectType; + } + public function isDeprecated( Node|Argument|EnumValueDefinition|FieldDefinition|InputObjectField $node, ): bool { @@ -208,6 +212,10 @@ public function isTypeDefinitionExists(string $name): bool { public function getTypeDefinition( Node|Type|InputObjectField|FieldDefinition|Argument|string $node, ): TypeDefinitionNode|Type { + if ($node instanceof TypeDefinitionNode && $node instanceof Node) { + return $node; + } + $name = $this->getTypeName($node); $types = $this->getTypes(); $definition = $this->getDocument()->types[$name] ?? null; @@ -660,7 +668,7 @@ public function addArgument( } else { $argument = new Argument([ 'name' => $name, - 'type' => $this->getType($type, InputType::class), + 'type' => $this->toType($type, InputType::class), 'description' => $description, 'defaultValue' => $default, ]); @@ -817,15 +825,15 @@ public function setArgumentType( * * @return Type&T */ - private function getType(TypeNode|string $name, string $expected): Type { + private function toType(TypeNode|string $name, string $expected): Type { // todo(graphql): Is there a better way to get Type? $type = null; $node = is_string($name) ? Parser::typeReference($name) : $name; if ($node instanceof ListTypeNode) { - $type = Type::listOf($this->getType($node->type, Type::class)); + $type = Type::listOf($this->toType($node->type, Type::class)); } elseif ($node instanceof NonNullTypeNode) { - $type = Type::nonNull($this->getType($node->type, NullableType::class)); + $type = Type::nonNull($this->toType($node->type, NullableType::class)); } else { $type = $this->getTypeDefinition($node); @@ -857,5 +865,30 @@ private function setType( throw new NotImplemented($node::class); } } + + /** + * @param Node|Type|InputObjectField|FieldDefinition|(TypeDefinitionNode&Node) $node + * + * @return (Node&TypeDefinitionNode)|Type|null + */ + private function getType( + Node|Type|InputObjectField|FieldDefinition|TypeDefinitionNode $node, + ): TypeDefinitionNode|Type|null { + $type = null; + + if ($node instanceof WrappingType) { + $type = $node->getInnermostType(); + } elseif ($node instanceof Node) { + try { + $type = $this->getTypeDefinition($node); + } catch (TypeDefinitionUnknown) { + // empty + } + } else { + // empty + } + + return $type; + } // } From 0c53721dba1408e530273493d7e180292977e178 Mon Sep 17 00:00:00 2001 From: Aleksei Lebedev <1329824+LastDragon-ru@users.noreply.github.com> Date: Thu, 25 Jan 2024 09:37:17 +0400 Subject: [PATCH 08/26] refactor(graphql)!: Added `TypeSource::isObject()`. --- packages/graphql/src/Builder/Contracts/TypeSource.php | 2 ++ packages/graphql/src/Builder/Sources/Source.php | 5 +++++ 2 files changed, 7 insertions(+) diff --git a/packages/graphql/src/Builder/Contracts/TypeSource.php b/packages/graphql/src/Builder/Contracts/TypeSource.php index d95aef4a5..30d4c3744 100644 --- a/packages/graphql/src/Builder/Contracts/TypeSource.php +++ b/packages/graphql/src/Builder/Contracts/TypeSource.php @@ -28,4 +28,6 @@ public function isNullable(): bool; public function isList(): bool; public function isUnion(): bool; + + public function isObject(): bool; } diff --git a/packages/graphql/src/Builder/Sources/Source.php b/packages/graphql/src/Builder/Sources/Source.php index 7687d6e45..26634c9fc 100644 --- a/packages/graphql/src/Builder/Sources/Source.php +++ b/packages/graphql/src/Builder/Sources/Source.php @@ -76,6 +76,11 @@ public function isUnion(): bool { return $this->getManipulator()->isUnion($this->getType()); } + #[Override] + public function isObject(): bool { + return $this->getManipulator()->isObject($this->getType()); + } + #[Override] public function __toString(): string { return $this->getManipulator()->getTypeFullName($this->getType()); From 078b0011a31b5223ae6fcf922f5a7bbc0b0cc023 Mon Sep 17 00:00:00 2001 From: Aleksei Lebedev <1329824+LastDragon-ru@users.noreply.github.com> Date: Thu, 25 Jan 2024 10:41:54 +0400 Subject: [PATCH 09/26] refactor(graphql): Changed arguments of `InputObject::isFieldDirectiveAllowed()` (will accept `$field`). --- packages/graphql/UPGRADE.md | 2 ++ packages/graphql/src/Builder/Types/InputObject.php | 7 ++++--- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/packages/graphql/UPGRADE.md b/packages/graphql/UPGRADE.md index 48ac96d8d..16e2e77f2 100644 --- a/packages/graphql/UPGRADE.md +++ b/packages/graphql/UPGRADE.md @@ -86,6 +86,8 @@ This section is actual only if you are extending the package. Please review and * [ ] `LastDragon_ru\LaraASP\GraphQL\Builder\Directives\PropertyDirective` +* [ ] `LastDragon_ru\LaraASP\GraphQL\Builder\Types\InputObject` + * [ ] `LastDragon_ru\LaraASP\GraphQL\SortBy\Builders\*` => `LastDragon_ru\LaraASP\GraphQL\SortBy\Sorters\*` * [ ] To get `BuilderInfo` instance within Operator the `LastDragon_ru\LaraASP\GraphQL\Builder\Contracts\Context` should be used instead of `LastDragon_ru\LaraASP\GraphQL\Builder\Manipulator`: diff --git a/packages/graphql/src/Builder/Types/InputObject.php b/packages/graphql/src/Builder/Types/InputObject.php index 297ac460a..bc4a87541 100644 --- a/packages/graphql/src/Builder/Types/InputObject.php +++ b/packages/graphql/src/Builder/Types/InputObject.php @@ -152,7 +152,7 @@ protected function isFieldConvertable( // Resolver? $resolver = $manipulator->getDirective($field->getField(), FieldResolver::class); - if ($resolver !== null && !$this->isFieldDirectiveAllowed($manipulator, $resolver, $context)) { + if ($resolver !== null && !$this->isFieldDirectiveAllowed($manipulator, $field, $context, $resolver)) { return false; } @@ -283,7 +283,7 @@ protected function getFieldDirectives( $directives = []; foreach ($manipulator->getDirectives($field->getField()) as $directive) { - if ($this->isFieldDirectiveAllowed($manipulator, $directive, $context)) { + if ($this->isFieldDirectiveAllowed($manipulator, $field, $context, $directive)) { $node = $manipulator->getDirectiveNode($directive); if ($node) { @@ -297,8 +297,9 @@ protected function getFieldDirectives( protected function isFieldDirectiveAllowed( Manipulator $manipulator, - Directive $directive, + InputFieldSource|ObjectFieldSource|InterfaceFieldSource $field, Context $context, + Directive $directive, ): bool { return $directive instanceof Operator || $directive instanceof RenameDirective; From c10125f30e51e053307f48b06d7f3f7c8a90e097 Mon Sep 17 00:00:00 2001 From: Aleksei Lebedev <1329824+LastDragon-ru@users.noreply.github.com> Date: Fri, 26 Jan 2024 09:20:37 +0400 Subject: [PATCH 10/26] Different processing for Explicit and Implicit types. --- .../graphql/src/Builder/Types/InputObject.php | 44 ++- .../DirectiveTest/Example.expected.graphql | 2 + .../DirectiveTest/Example.schema.graphql | 2 +- .../DirectiveTest/Explicit.expected.graphql | 114 +++++- .../DirectiveTest/Explicit.schema.graphql | 8 +- .../DirectiveTest/Implicit.expected.graphql | 362 ++++++++++++++++-- .../DirectiveTest/Implicit.schema.graphql | 44 ++- .../DirectiveTest/Scout.expected.graphql | 22 +- .../Scout.expected.v10.3.0.graphql | 22 +- .../DirectiveTest/Scout.schema.graphql | 12 +- .../DirectiveTest/Example.expected.graphql | 2 + .../DirectiveTest/Example.schema.graphql | 2 +- .../DirectiveTest/Explicit.expected.graphql | 116 +++++- .../DirectiveTest/Explicit.schema.graphql | 12 +- .../DirectiveTest/Implicit.expected.graphql | 334 +++++++++++++++- .../DirectiveTest/Implicit.schema.graphql | 50 ++- .../DirectiveTest/Scout.expected.graphql | 34 +- .../DirectiveTest/Scout.schema.graphql | 10 +- .../graphql/src/Testing/Package/TestCase.php | 2 + .../src/Utils/RelationDirectiveHelper.php | 15 + 20 files changed, 1099 insertions(+), 110 deletions(-) create mode 100644 packages/graphql/src/Utils/RelationDirectiveHelper.php diff --git a/packages/graphql/src/Builder/Types/InputObject.php b/packages/graphql/src/Builder/Types/InputObject.php index bc4a87541..167c859c5 100644 --- a/packages/graphql/src/Builder/Types/InputObject.php +++ b/packages/graphql/src/Builder/Types/InputObject.php @@ -15,6 +15,7 @@ use LastDragon_ru\LaraASP\GraphQL\Builder\Contracts\TypeDefinition; use LastDragon_ru\LaraASP\GraphQL\Builder\Contracts\TypeSource; use LastDragon_ru\LaraASP\GraphQL\Builder\Directives\HandlerContextBuilderInfo; +use LastDragon_ru\LaraASP\GraphQL\Builder\Directives\HandlerContextImplicit; use LastDragon_ru\LaraASP\GraphQL\Builder\Exceptions\TypeDefinitionFieldAlreadyDefined; use LastDragon_ru\LaraASP\GraphQL\Builder\Manipulator; use LastDragon_ru\LaraASP\GraphQL\Builder\Sources\InputFieldSource; @@ -23,6 +24,8 @@ use LastDragon_ru\LaraASP\GraphQL\Builder\Sources\InterfaceSource; use LastDragon_ru\LaraASP\GraphQL\Builder\Sources\ObjectFieldSource; use LastDragon_ru\LaraASP\GraphQL\Builder\Sources\ObjectSource; +use LastDragon_ru\LaraASP\GraphQL\Utils\RelationDirectiveHelper; +use Nuwave\Lighthouse\Schema\Directives\RelationDirective; use Nuwave\Lighthouse\Schema\Directives\RenameDirective; use Nuwave\Lighthouse\Support\Contracts\Directive; use Nuwave\Lighthouse\Support\Contracts\FieldResolver; @@ -139,20 +142,39 @@ protected function isFieldConvertable( InputFieldSource|ObjectFieldSource|InterfaceFieldSource $field, Context $context, ): bool { - // Args? (in general case we don't know how they should be converted) - if ($field->hasArguments()) { + /** + * Union? + */ + if ($field->isUnion()) { + // todo(graphql): Would be nice to support Unions. Maybe just use + // fields with same name and type for all members? return false; } - // Union? - if ($field->isUnion()) { - return false; + /** + * Explicit? We are expecting that the type created for the directive + * and all fields are valid and available. Also, keep in mind that the + * type is a `input` so it cannot have arguments, `FieldResolver` + * (if GraphQL Schema valid). + */ + if (!$context->get(HandlerContextImplicit::class)?->value) { + return true; } + /** + * Nope. Implicit type (=placeholder) is an `object`/`interface` and may + * contain fields with arguments and/or `FieldResolver` directive - these + * fields (most likely) cannot be used to modify the Builder. + */ // Resolver? $resolver = $manipulator->getDirective($field->getField(), FieldResolver::class); - if ($resolver !== null && !$this->isFieldDirectiveAllowed($manipulator, $field, $context, $resolver)) { + if ($resolver && !$this->isFieldDirectiveConvertable($manipulator, $field, $context, $resolver)) { + return false; + } + + // Object/Arguments allowed only if Resolver defined + if (($field->hasArguments() || $field->isObject()) && !$resolver) { return false; } @@ -160,6 +182,16 @@ protected function isFieldConvertable( return true; } + protected function isFieldDirectiveConvertable( + Manipulator $manipulator, + InputFieldSource|ObjectFieldSource|InterfaceFieldSource $field, + Context $context, + Directive $directive, + ): bool { + return ($directive instanceof RelationDirective && !RelationDirectiveHelper::getPaginationType($directive)) + || $this->isFieldDirectiveAllowed($manipulator, $field, $context, $directive); + } + protected function getFieldDefinition( Manipulator $manipulator, InputFieldSource|ObjectFieldSource|InterfaceFieldSource $field, diff --git a/packages/graphql/src/SearchBy/Directives/DirectiveTest/Example.expected.graphql b/packages/graphql/src/SearchBy/Directives/DirectiveTest/Example.expected.graphql index 0a03051cc..85aa0ac1a 100644 --- a/packages/graphql/src/SearchBy/Directives/DirectiveTest/Example.expected.graphql +++ b/packages/graphql/src/SearchBy/Directives/DirectiveTest/Example.expected.graphql @@ -198,7 +198,9 @@ enum SearchByTypeFlag { input Comment { date: Date text: String! + user: User + @belongsTo } """ diff --git a/packages/graphql/src/SearchBy/Directives/DirectiveTest/Example.schema.graphql b/packages/graphql/src/SearchBy/Directives/DirectiveTest/Example.schema.graphql index 61db99b43..962dff203 100644 --- a/packages/graphql/src/SearchBy/Directives/DirectiveTest/Example.schema.graphql +++ b/packages/graphql/src/SearchBy/Directives/DirectiveTest/Example.schema.graphql @@ -23,6 +23,6 @@ type User { input Comment { text: String! - user: User + user: User @belongsTo date: Date } diff --git a/packages/graphql/src/SearchBy/Directives/DirectiveTest/Explicit.expected.graphql b/packages/graphql/src/SearchBy/Directives/DirectiveTest/Explicit.expected.graphql index 9dbd354dd..b541726bb 100644 --- a/packages/graphql/src/SearchBy/Directives/DirectiveTest/Explicit.expected.graphql +++ b/packages/graphql/src/SearchBy/Directives/DirectiveTest/Explicit.expected.graphql @@ -195,6 +195,28 @@ enum SearchByTypeFlag { ) } +input A { + field: B! + fields: [B!]! + + """ + Description should be copied. + """ + id: ID! + + name: String! +} + +input B { + """ + Description should be copied. + """ + id: ID! + + name: String + parent: A! +} + """ Conditions for the related objects (`has()`/`doesntHave()`) for `input A`. @@ -272,13 +294,13 @@ input SearchByConditionA { """ Relationship condition. """ - child: SearchByComplexRelationB + field: SearchByComplexRelationB @searchByOperatorRelation """ Relationship condition. """ - children: SearchByComplexRelationB + fields: SearchByComplexRelationB @searchByOperatorRelation """ @@ -636,10 +658,98 @@ input SearchByTypeRangeInt { min: Int! } +""" +A paginated list of A items. +""" +type APaginator +@model( + class: "LastDragon_ru\\LaraASP\\GraphQL\\Testing\\Package\\Data\\Models\\TestObject" +) +{ + """ + A list of A items. + """ + data: [A!]! + @field( + resolver: "Nuwave\\Lighthouse\\Pagination\\PaginatorField@dataResolver" + ) + + """ + Pagination information about the list of items. + """ + paginatorInfo: PaginatorInfo! + @field( + resolver: "Nuwave\\Lighthouse\\Pagination\\PaginatorField@paginatorInfoResolver" + ) +} + +""" +Information about pagination using a fully featured paginator. +""" +type PaginatorInfo { + """ + Number of items in the current page. + """ + count: Int! + + """ + Index of the current page. + """ + currentPage: Int! + + """ + Index of the first item in the current page. + """ + firstItem: Int + + """ + Are there more pages after this one? + """ + hasMorePages: Boolean! + + """ + Index of the last item in the current page. + """ + lastItem: Int + + """ + Index of the last available page. + """ + lastPage: Int! + + """ + Number of items per page. + """ + perPage: Int! + + """ + Number of total available items. + """ + total: Int! +} + type Query { a( where: SearchByConditionA @searchBy ): ID! @all + + b( + where: SearchByConditionA + @searchBy + + """ + Limits number of fetched items. + """ + first: Int! + + """ + The offset from which items are returned. + """ + page: Int + ): APaginator! + @paginate( + model: "LastDragon_ru\\LaraASP\\GraphQL\\Testing\\Package\\Data\\Models\\TestObject" + ) } diff --git a/packages/graphql/src/SearchBy/Directives/DirectiveTest/Explicit.schema.graphql b/packages/graphql/src/SearchBy/Directives/DirectiveTest/Explicit.schema.graphql index b4040b618..75254a616 100644 --- a/packages/graphql/src/SearchBy/Directives/DirectiveTest/Explicit.schema.graphql +++ b/packages/graphql/src/SearchBy/Directives/DirectiveTest/Explicit.schema.graphql @@ -1,13 +1,17 @@ type Query { a(where: A @searchBy): ID! @all + b(where: A @searchBy): [A!]! @paginate( + model: "LastDragon_ru\\LaraASP\\GraphQL\\Testing\\Package\\Data\\Models\\TestObject" + ) } input A { + # Should be processed "Description should be copied." id: ID! name: String! - child: B! - children: [B!]! + field: B! + fields: [B!]! } input B { diff --git a/packages/graphql/src/SearchBy/Directives/DirectiveTest/Implicit.expected.graphql b/packages/graphql/src/SearchBy/Directives/DirectiveTest/Implicit.expected.graphql index 8f6b19d27..534591411 100644 --- a/packages/graphql/src/SearchBy/Directives/DirectiveTest/Implicit.expected.graphql +++ b/packages/graphql/src/SearchBy/Directives/DirectiveTest/Implicit.expected.graphql @@ -186,6 +186,26 @@ on | INPUT_FIELD_DEFINITION | SCALAR +""" +Options for the `type` argument of `@hasMany`. +""" +enum HasManyType { + """ + Cursor-based pagination, compatible with the Relay specification. + """ + CONNECTION + + """ + Offset-based pagination, similar to the Laravel default. + """ + PAGINATOR + + """ + Offset-based pagination like the Laravel "Simple Pagination", which does not count the total number of records. + """ + SIMPLE +} + enum SearchByTypeFlag { Yes @@ -269,18 +289,65 @@ input SearchByConditionA { anyOf: [SearchByConditionA!] @searchByOperatorAnyOf + """ + Property condition. + """ + id: SearchByScalarID + @searchByOperatorProperty + + """ + Property condition. + """ + name: SearchByScalarString + @searchByOperatorProperty + + """ + Not. + """ + not: SearchByConditionA + @searchByOperatorNot + """ Relationship condition. """ - child: SearchByComplexRelationB + relation: SearchByComplexRelationB @searchByOperatorRelation """ Relationship condition. """ - children: SearchByComplexRelationB + relationWithArgs: SearchByComplexRelationA @searchByOperatorRelation + """ + Relationship condition. + """ + relations: SearchByComplexRelationB + @searchByOperatorRelation + + """ + Relationship condition. + """ + relationsWithArgs: SearchByComplexRelationA + @searchByOperatorRelation +} + +""" +Available conditions for `type B` (only one property allowed at a time). +""" +input SearchByConditionB { + """ + All of the conditions must be true. + """ + allOf: [SearchByConditionB!] + @searchByOperatorAllOf + + """ + Any of the conditions must be true. + """ + anyOf: [SearchByConditionB!] + @searchByOperatorAnyOf + """ Property condition. """ @@ -290,30 +357,36 @@ input SearchByConditionA { """ Property condition. """ - name: SearchByScalarString + name: SearchByScalarStringOrNull @searchByOperatorProperty """ Not. """ - not: SearchByConditionA + not: SearchByConditionB @searchByOperatorNot + + """ + Relationship condition. + """ + parent: SearchByComplexRelationA + @searchByOperatorRelation } """ -Available conditions for `type B` (only one property allowed at a time). +Available conditions for `interface C` (only one property allowed at a time). """ -input SearchByConditionB { +input SearchByConditionC { """ All of the conditions must be true. """ - allOf: [SearchByConditionB!] + allOf: [SearchByConditionC!] @searchByOperatorAllOf """ Any of the conditions must be true. """ - anyOf: [SearchByConditionB!] + anyOf: [SearchByConditionC!] @searchByOperatorAnyOf """ @@ -325,19 +398,37 @@ input SearchByConditionB { """ Property condition. """ - name: SearchByScalarStringOrNull + name: SearchByScalarString @searchByOperatorProperty """ Not. """ - not: SearchByConditionB + not: SearchByConditionC @searchByOperatorNot """ Relationship condition. """ - parent: SearchByComplexRelationA + relation: SearchByComplexRelationB + @searchByOperatorRelation + + """ + Relationship condition. + """ + relationWithArgs: SearchByComplexRelationA + @searchByOperatorRelation + + """ + Relationship condition. + """ + relations: SearchByComplexRelationB + @searchByOperatorRelation + + """ + Relationship condition. + """ + relationsWithArgs: SearchByComplexRelationA @searchByOperatorRelation } @@ -636,17 +727,78 @@ input SearchByTypeRangeInt { min: Int! } -type A { - argument( +interface C { + field: B! + fields: [B!]! + + """ + Description should be ignored. + """ + id: ID! + + name: String! + + property( + arg: String + ): Int! + + relation: B! + @hasOne + + relationWithArgs( arg: String ): A! + @hasOne + + relations: [B!]! + @hasMany + + relationsPaginated( + """ + Limits number of fetched items. + """ + first: Int! + + """ + The offset from which items are returned. + """ + page: Int + ): BPaginator! + @hasMany( + type: PAGINATOR + ) - arguments( + relationsPaginatedWithArgs( + arg: String + + """ + Limits number of fetched items. + """ + first: Int! + + """ + The offset from which items are returned. + """ + page: Int + ): BPaginator! + @hasMany( + type: PAGINATOR + ) + + relationsWithArgs( arg: String ): [A!] + @hasMany + + resolver: Int! + @field( + resolver: "\\LastDragon_ru\\LaraASP\\GraphQL\\SearchBy\\Directives\\DirectiveTest__Resolver" + ) +} - child: B! - children: [B!]! +type A { + field: B! + fields: [B!]! """ Description should be ignored. @@ -654,24 +806,166 @@ type A { id: ID! name: String! -} -type B { - argument( + property( + arg: String + ): Int! + + relation: B! + @hasOne + + relationWithArgs( arg: String - ): B! + ): A! + @hasOne + + relations: [B!]! + @hasMany + + relationsPaginated( + """ + Limits number of fetched items. + """ + first: Int! + + """ + The offset from which items are returned. + """ + page: Int + ): BPaginator! + @hasMany( + type: PAGINATOR + ) + + relationsPaginatedWithArgs( + arg: String + + """ + Limits number of fetched items. + """ + first: Int! + + """ + The offset from which items are returned. + """ + page: Int + ): BPaginator! + @hasMany( + type: PAGINATOR + ) - arguments( + relationsWithArgs( arg: String - ): [B!] + ): [A!] + @hasMany + + resolver: Int! + @field( + resolver: "\\LastDragon_ru\\LaraASP\\GraphQL\\SearchBy\\Directives\\DirectiveTest__Resolver" + ) +} +""" +A paginated list of A items. +""" +type APaginator +@model( + class: "LastDragon_ru\\LaraASP\\GraphQL\\Testing\\Package\\Data\\Models\\TestObject" +) +{ + """ + A list of A items. + """ + data: [A!]! + @field( + resolver: "Nuwave\\Lighthouse\\Pagination\\PaginatorField@dataResolver" + ) + + """ + Pagination information about the list of items. + """ + paginatorInfo: PaginatorInfo! + @field( + resolver: "Nuwave\\Lighthouse\\Pagination\\PaginatorField@paginatorInfoResolver" + ) +} + +type B { """ Description should be ignored. """ id: ID! name: String + parent: A! + @hasOne +} + +""" +A paginated list of B items. +""" +type BPaginator { + """ + A list of B items. + """ + data: [B!]! + @field( + resolver: "Nuwave\\Lighthouse\\Pagination\\PaginatorField@dataResolver" + ) + + """ + Pagination information about the list of items. + """ + paginatorInfo: PaginatorInfo! + @field( + resolver: "Nuwave\\Lighthouse\\Pagination\\PaginatorField@paginatorInfoResolver" + ) +} + +""" +Information about pagination using a fully featured paginator. +""" +type PaginatorInfo { + """ + Number of items in the current page. + """ + count: Int! + + """ + Index of the current page. + """ + currentPage: Int! + + """ + Index of the first item in the current page. + """ + firstItem: Int + + """ + Are there more pages after this one? + """ + hasMorePages: Boolean! + + """ + Index of the last item in the current page. + """ + lastItem: Int + + """ + Index of the last available page. + """ + lastPage: Int! + + """ + Number of items per page. + """ + perPage: Int! + + """ + Number of total available items. + """ + total: Int! } type Query { @@ -680,4 +974,28 @@ type Query { @searchBy ): [A!]! @all + + b( + where: SearchByConditionA + @searchBy + + """ + Limits number of fetched items. + """ + first: Int! + + """ + The offset from which items are returned. + """ + page: Int + ): APaginator! + @paginate( + model: "LastDragon_ru\\LaraASP\\GraphQL\\Testing\\Package\\Data\\Models\\TestObject" + ) + + c( + where: SearchByConditionC + @searchBy + ): [C!]! + @all } diff --git a/packages/graphql/src/SearchBy/Directives/DirectiveTest/Implicit.schema.graphql b/packages/graphql/src/SearchBy/Directives/DirectiveTest/Implicit.schema.graphql index a85e679f5..19e1a3ee3 100644 --- a/packages/graphql/src/SearchBy/Directives/DirectiveTest/Implicit.schema.graphql +++ b/packages/graphql/src/SearchBy/Directives/DirectiveTest/Implicit.schema.graphql @@ -1,22 +1,52 @@ type Query { a(where: _ @searchBy): [A!]! @all + b(where: _ @searchBy): [A!]! @paginate( + model: "LastDragon_ru\\LaraASP\\GraphQL\\Testing\\Package\\Data\\Models\\TestObject" + ) + c(where: _ @searchBy): [C!]! @all } type A { + # Should be processed "Description should be ignored." id: ID! name: String! - child: B! - children: [B!]! - argument(arg: String): A! - arguments(arg: String): [A!] + relation: B! @hasOne + relations: [B!]! @hasMany + relationWithArgs(arg: String): A! @hasOne + relationsWithArgs(arg: String): [A!] @hasMany + + # Should be ignored + field: B! + fields: [B!]! + property(arg: String): Int! + resolver: Int! @field(resolver: "\\LastDragon_ru\\LaraASP\\GraphQL\\SearchBy\\Directives\\DirectiveTest__Resolver") + relationsPaginated: B! @hasMany(type: PAGINATOR) + relationsPaginatedWithArgs(arg: String): [B!]! @hasMany(type: PAGINATOR) } type B { "Description should be ignored." id: ID! name: String - parent: A! - argument(arg: String): B! - arguments(arg: String): [B!] + parent: A! @hasOne +} + +interface C { + # Should be processed + "Description should be ignored." + id: ID! + name: String! + relation: B! @hasOne + relations: [B!]! @hasMany + relationWithArgs(arg: String): A! @hasOne + relationsWithArgs(arg: String): [A!] @hasMany + + # Should be ignored + field: B! + fields: [B!]! + property(arg: String): Int! + resolver: Int! @field(resolver: "\\LastDragon_ru\\LaraASP\\GraphQL\\SearchBy\\Directives\\DirectiveTest__Resolver") + relationsPaginated: B! @hasMany(type: PAGINATOR) + relationsPaginatedWithArgs(arg: String): [B!]! @hasMany(type: PAGINATOR) } diff --git a/packages/graphql/src/SearchBy/Directives/DirectiveTest/Scout.expected.graphql b/packages/graphql/src/SearchBy/Directives/DirectiveTest/Scout.expected.graphql index 0c80c97b3..177596a9e 100644 --- a/packages/graphql/src/SearchBy/Directives/DirectiveTest/Scout.expected.graphql +++ b/packages/graphql/src/SearchBy/Directives/DirectiveTest/Scout.expected.graphql @@ -201,6 +201,12 @@ input SearchByScoutConditionInputA { nestedNotNull: SearchByScoutConditionNestedA @searchByOperatorCondition + """ + Property condition. + """ + resolver: SearchByScoutScalarFloatOrNull + @searchByOperatorProperty + """ Property condition. """ @@ -566,12 +572,6 @@ input SearchByScoutConditionObjectNested { allOf: [SearchByScoutConditionObjectNested!] @searchByOperatorAllOf - """ - Property condition. - """ - nested: SearchByScoutConditionObjectNested - @searchByOperatorCondition - """ Property condition. """ @@ -883,8 +883,12 @@ interface ObjectInterface { ignoredTypeNonNull: ObjectIgnored! intScalar: Int intScalarNotNull: Int! + nested: ObjectNested + @hasOne + nestedNotNull: ObjectNested! + @hasOne resolver: Float @field( @@ -968,8 +972,12 @@ implements ignoredTypeNonNull: ObjectIgnored! intScalar: Int intScalarNotNull: Int! + nested: ObjectNested + @hasOne + nestedNotNull: ObjectNested! + @hasOne resolver: Float @field( @@ -1059,7 +1067,7 @@ implements where: SearchByScoutConditionObjectInterface @searchBy - ): Object! + ): ObjectInterface! @all } diff --git a/packages/graphql/src/SearchBy/Directives/DirectiveTest/Scout.expected.v10.3.0.graphql b/packages/graphql/src/SearchBy/Directives/DirectiveTest/Scout.expected.v10.3.0.graphql index 04c05a36d..18b591525 100644 --- a/packages/graphql/src/SearchBy/Directives/DirectiveTest/Scout.expected.v10.3.0.graphql +++ b/packages/graphql/src/SearchBy/Directives/DirectiveTest/Scout.expected.v10.3.0.graphql @@ -207,6 +207,12 @@ input SearchByScoutConditionInputA { nestedNotNull: SearchByScoutConditionNestedA @searchByOperatorCondition + """ + Property condition. + """ + resolver: SearchByScoutScalarFloatOrNull + @searchByOperatorProperty + """ Property condition. """ @@ -572,12 +578,6 @@ input SearchByScoutConditionObjectNested { allOf: [SearchByScoutConditionObjectNested!] @searchByOperatorAllOf - """ - Property condition. - """ - nested: SearchByScoutConditionObjectNested - @searchByOperatorCondition - """ Property condition. """ @@ -949,8 +949,12 @@ interface ObjectInterface { ignoredTypeNonNull: ObjectIgnored! intScalar: Int intScalarNotNull: Int! + nested: ObjectNested + @hasOne + nestedNotNull: ObjectNested! + @hasOne resolver: Float @field( @@ -1034,8 +1038,12 @@ implements ignoredTypeNonNull: ObjectIgnored! intScalar: Int intScalarNotNull: Int! + nested: ObjectNested + @hasOne + nestedNotNull: ObjectNested! + @hasOne resolver: Float @field( @@ -1125,7 +1133,7 @@ implements where: SearchByScoutConditionObjectInterface @searchBy - ): Object! + ): ObjectInterface! @all } diff --git a/packages/graphql/src/SearchBy/Directives/DirectiveTest/Scout.schema.graphql b/packages/graphql/src/SearchBy/Directives/DirectiveTest/Scout.schema.graphql index 94d6c0ba1..bd88c2cf7 100644 --- a/packages/graphql/src/SearchBy/Directives/DirectiveTest/Scout.schema.graphql +++ b/packages/graphql/src/SearchBy/Directives/DirectiveTest/Scout.schema.graphql @@ -11,7 +11,7 @@ type Query implements A & B { f(search: String @search, where: _ @searchBy): Object! @all - g(search: String @search, where: ObjectInterface @searchBy): Object! @all + g(search: String @search, where: _ @searchBy): ObjectInterface! @all } interface A implements C & F { @@ -122,8 +122,8 @@ interface ObjectInterface { stringScalarNotNull: String! booleanScalar: Boolean booleanScalarNotNull: Boolean! - nested: ObjectNested - nestedNotNull: ObjectNested! + nested: ObjectNested @hasOne + nestedNotNull: ObjectNested! @hasOne enum: EnumA enumNotNull: EnumA! enumIgnored: EnumIgnored @@ -155,7 +155,7 @@ interface ObjectInterface { fieldRenamed: ID @rename(attribute: "renamed") } -type Object implements ObjectInterface{ +type Object implements ObjectInterface { idScalar: ID idScalarNotNull: ID! intScalar: Int @@ -166,8 +166,8 @@ type Object implements ObjectInterface{ stringScalarNotNull: String! booleanScalar: Boolean booleanScalarNotNull: Boolean! - nested: ObjectNested - nestedNotNull: ObjectNested! + nested: ObjectNested @hasOne + nestedNotNull: ObjectNested! @hasOne enum: EnumA enumNotNull: EnumA! enumIgnored: EnumIgnored diff --git a/packages/graphql/src/SortBy/Directives/DirectiveTest/Example.expected.graphql b/packages/graphql/src/SortBy/Directives/DirectiveTest/Example.expected.graphql index 1000cc8cc..09d102fed 100644 --- a/packages/graphql/src/SortBy/Directives/DirectiveTest/Example.expected.graphql +++ b/packages/graphql/src/SortBy/Directives/DirectiveTest/Example.expected.graphql @@ -136,7 +136,9 @@ input SortByClauseUsersSort { type Comment { text: String + user: User + @belongsTo } type Query { diff --git a/packages/graphql/src/SortBy/Directives/DirectiveTest/Example.schema.graphql b/packages/graphql/src/SortBy/Directives/DirectiveTest/Example.schema.graphql index aa9d8fffb..e9c4123b3 100644 --- a/packages/graphql/src/SortBy/Directives/DirectiveTest/Example.schema.graphql +++ b/packages/graphql/src/SortBy/Directives/DirectiveTest/Example.schema.graphql @@ -13,7 +13,7 @@ input UsersSort { type Comment { text: String - user: User + user: User @belongsTo } type User { diff --git a/packages/graphql/src/SortBy/Directives/DirectiveTest/Explicit.expected.graphql b/packages/graphql/src/SortBy/Directives/DirectiveTest/Explicit.expected.graphql index 9b687b42a..15edf1284 100644 --- a/packages/graphql/src/SortBy/Directives/DirectiveTest/Explicit.expected.graphql +++ b/packages/graphql/src/SortBy/Directives/DirectiveTest/Explicit.expected.graphql @@ -47,6 +47,28 @@ enum SortByTypeDirection { ) } +input A { + field: B! + fields: [B!]! + + """ + Description should be copied. + """ + id: ID! + + name: String! +} + +input B { + """ + Description should be copied. + """ + id: ID! + + name: String + parent: A! +} + """ Sort clause for `input A` (only one property allowed at a time). """ @@ -54,11 +76,11 @@ input SortByClauseA { """ Property clause. """ - child: SortByClauseB + field: SortByClauseB @sortByOperatorProperty """ - Description should be ignored. + Description should be copied. """ id: SortByTypeDirection @sortByOperatorField @@ -87,7 +109,7 @@ Sort clause for `input B` (only one property allowed at a time). """ input SortByClauseB { """ - Description should be ignored. + Description should be copied. """ id: SortByTypeDirection @sortByOperatorField @@ -117,10 +139,98 @@ input SortByClauseB { @sortByOperatorProperty } +""" +A paginated list of A items. +""" +type APaginator +@model( + class: "LastDragon_ru\\LaraASP\\GraphQL\\Testing\\Package\\Data\\Models\\TestObject" +) +{ + """ + A list of A items. + """ + data: [A!]! + @field( + resolver: "Nuwave\\Lighthouse\\Pagination\\PaginatorField@dataResolver" + ) + + """ + Pagination information about the list of items. + """ + paginatorInfo: PaginatorInfo! + @field( + resolver: "Nuwave\\Lighthouse\\Pagination\\PaginatorField@paginatorInfoResolver" + ) +} + +""" +Information about pagination using a fully featured paginator. +""" +type PaginatorInfo { + """ + Number of items in the current page. + """ + count: Int! + + """ + Index of the current page. + """ + currentPage: Int! + + """ + Index of the first item in the current page. + """ + firstItem: Int + + """ + Are there more pages after this one? + """ + hasMorePages: Boolean! + + """ + Index of the last item in the current page. + """ + lastItem: Int + + """ + Index of the last available page. + """ + lastPage: Int! + + """ + Number of items per page. + """ + perPage: Int! + + """ + Number of total available items. + """ + total: Int! +} + type Query { a( order: [SortByClauseA!] @sortBy ): ID! @all + + b( + where: [SortByClauseA!] + @sortBy + + """ + Limits number of fetched items. + """ + first: Int! + + """ + The offset from which items are returned. + """ + page: Int + ): APaginator! + @paginate( + model: "LastDragon_ru\\LaraASP\\GraphQL\\Testing\\Package\\Data\\Models\\TestObject" + ) } diff --git a/packages/graphql/src/SortBy/Directives/DirectiveTest/Explicit.schema.graphql b/packages/graphql/src/SortBy/Directives/DirectiveTest/Explicit.schema.graphql index d204e22ba..540afc328 100644 --- a/packages/graphql/src/SortBy/Directives/DirectiveTest/Explicit.schema.graphql +++ b/packages/graphql/src/SortBy/Directives/DirectiveTest/Explicit.schema.graphql @@ -1,17 +1,21 @@ type Query { a(order: A @sortBy): ID! @all + b(where: A @sortBy): [A!]! @paginate( + model: "LastDragon_ru\\LaraASP\\GraphQL\\Testing\\Package\\Data\\Models\\TestObject" + ) } input A { - "Description should be ignored." + # Should be processed + "Description should be copied." id: ID! name: String! - child: B! - children: [B!]! + field: B! + fields: [B!]! } input B { - "Description should be ignored." + "Description should be copied." id: ID! name: String parent: A! diff --git a/packages/graphql/src/SortBy/Directives/DirectiveTest/Implicit.expected.graphql b/packages/graphql/src/SortBy/Directives/DirectiveTest/Implicit.expected.graphql index 7b4dcb46f..e9119052e 100644 --- a/packages/graphql/src/SortBy/Directives/DirectiveTest/Implicit.expected.graphql +++ b/packages/graphql/src/SortBy/Directives/DirectiveTest/Implicit.expected.graphql @@ -29,6 +29,26 @@ on | INPUT_FIELD_DEFINITION | SCALAR +""" +Options for the `type` argument of `@hasMany`. +""" +enum HasManyType { + """ + Cursor-based pagination, compatible with the Relay specification. + """ + CONNECTION + + """ + Offset-based pagination, similar to the Laravel default. + """ + PAGINATOR + + """ + Offset-based pagination like the Laravel "Simple Pagination", which does not count the total number of records. + """ + SIMPLE +} + """ Sort direction. """ @@ -51,12 +71,6 @@ enum SortByTypeDirection { Sort clause for `type A` (only one property allowed at a time). """ input SortByClauseA { - """ - Property clause. - """ - child: SortByClauseB - @sortByOperatorProperty - """ Property clause. """ @@ -80,6 +94,18 @@ input SortByClauseA { """ nullsLast: SortByClauseA @sortByOperatorNullsLast + + """ + Property clause. + """ + relation: SortByClauseB + @sortByOperatorProperty + + """ + Property clause. + """ + relationWithArgs: SortByClauseA + @sortByOperatorProperty } """ @@ -117,48 +143,316 @@ input SortByClauseB { @sortByOperatorProperty } -type A { - argument( +""" +Sort clause for `interface C` (only one property allowed at a time). +""" +input SortByClauseC { + """ + Property clause. + """ + id: SortByTypeDirection + @sortByOperatorField + + """ + Property clause. + """ + name: SortByTypeDirection + @sortByOperatorField + + """ + NULLs first + """ + nullsFirst: SortByClauseC + @sortByOperatorNullsFirst + + """ + NULLs last + """ + nullsLast: SortByClauseC + @sortByOperatorNullsLast + + """ + Property clause. + """ + relation: SortByClauseB + @sortByOperatorProperty + + """ + Property clause. + """ + relationWithArgs: SortByClauseA + @sortByOperatorProperty +} + +interface C { + field: B! + fields: [B!]! + + """ + Description should be ignored. + """ + id: ID! + + name: String! + + property( + arg: String + ): Int! + + relation: B! + @hasOne + + relationWithArgs( arg: String ): A! + @hasOne + + relations: [B!]! + @hasMany + + relationsPaginated( + """ + Limits number of fetched items. + """ + first: Int! + + """ + The offset from which items are returned. + """ + page: Int + ): BPaginator! + @hasMany( + type: PAGINATOR + ) + + relationsPaginatedWithArgs( + arg: String + + """ + Limits number of fetched items. + """ + first: Int! + + """ + The offset from which items are returned. + """ + page: Int + ): BPaginator! + @hasMany( + type: PAGINATOR + ) - arguments( + relationsWithArgs( arg: String ): [A!] + @hasMany - child: B! - children: [B!]! + resolver: Int! + @field( + resolver: "\\LastDragon_ru\\LaraASP\\GraphQL\\SortBy\\Directives\\DirectiveTest__Resolver" + ) +} + +type A { + field: B! + fields: [B!]! """ - Description should be copied. + Description should be ignored. """ id: ID! name: String! -} -type B { - argument( + property( arg: String - ): B! + ): Int! - arguments( + relation: B! + @hasOne + + relationWithArgs( arg: String - ): [B!] + ): A! + @hasOne + + relations: [B!]! + @hasMany + relationsPaginated( + """ + Limits number of fetched items. + """ + first: Int! + + """ + The offset from which items are returned. + """ + page: Int + ): BPaginator! + @hasMany( + type: PAGINATOR + ) + + relationsPaginatedWithArgs( + arg: String + + """ + Limits number of fetched items. + """ + first: Int! + + """ + The offset from which items are returned. + """ + page: Int + ): BPaginator! + @hasMany( + type: PAGINATOR + ) + + relationsWithArgs( + arg: String + ): [A!] + @hasMany + + resolver: Int! + @field( + resolver: "\\LastDragon_ru\\LaraASP\\GraphQL\\SortBy\\Directives\\DirectiveTest__Resolver" + ) +} + +""" +A paginated list of A items. +""" +type APaginator +@model( + class: "LastDragon_ru\\LaraASP\\GraphQL\\Testing\\Package\\Data\\Models\\TestObject" +) +{ + """ + A list of A items. + """ + data: [A!]! + @field( + resolver: "Nuwave\\Lighthouse\\Pagination\\PaginatorField@dataResolver" + ) + + """ + Pagination information about the list of items. + """ + paginatorInfo: PaginatorInfo! + @field( + resolver: "Nuwave\\Lighthouse\\Pagination\\PaginatorField@paginatorInfoResolver" + ) +} + +type B { """ - Description should be copied. + Description should be ignored. """ id: ID! name: String + parent: A! + @hasOne +} + +""" +A paginated list of B items. +""" +type BPaginator { + """ + A list of B items. + """ + data: [B!]! + @field( + resolver: "Nuwave\\Lighthouse\\Pagination\\PaginatorField@dataResolver" + ) + + """ + Pagination information about the list of items. + """ + paginatorInfo: PaginatorInfo! + @field( + resolver: "Nuwave\\Lighthouse\\Pagination\\PaginatorField@paginatorInfoResolver" + ) +} + +""" +Information about pagination using a fully featured paginator. +""" +type PaginatorInfo { + """ + Number of items in the current page. + """ + count: Int! + + """ + Index of the current page. + """ + currentPage: Int! + + """ + Index of the first item in the current page. + """ + firstItem: Int + + """ + Are there more pages after this one? + """ + hasMorePages: Boolean! + + """ + Index of the last item in the current page. + """ + lastItem: Int + + """ + Index of the last available page. + """ + lastPage: Int! + + """ + Number of items per page. + """ + perPage: Int! + + """ + Number of total available items. + """ + total: Int! } type Query { a( - order: [SortByClauseA!] + where: [SortByClauseA!] @sortBy ): [A!]! @all + + b( + where: [SortByClauseA!] + @sortBy + + """ + Limits number of fetched items. + """ + first: Int! + + """ + The offset from which items are returned. + """ + page: Int + ): APaginator! + @paginate( + model: "LastDragon_ru\\LaraASP\\GraphQL\\Testing\\Package\\Data\\Models\\TestObject" + ) + + c( + where: [SortByClauseC!] + @sortBy + ): [C!]! + @all } diff --git a/packages/graphql/src/SortBy/Directives/DirectiveTest/Implicit.schema.graphql b/packages/graphql/src/SortBy/Directives/DirectiveTest/Implicit.schema.graphql index ec59e5088..7e5e4b101 100644 --- a/packages/graphql/src/SortBy/Directives/DirectiveTest/Implicit.schema.graphql +++ b/packages/graphql/src/SortBy/Directives/DirectiveTest/Implicit.schema.graphql @@ -1,22 +1,52 @@ type Query { - a(order: _ @sortBy): [A!]! @all + a(where: _ @sortBy): [A!]! @all + b(where: _ @sortBy): [A!]! @paginate( + model: "LastDragon_ru\\LaraASP\\GraphQL\\Testing\\Package\\Data\\Models\\TestObject" + ) + c(where: _ @sortBy): [C!]! @all } type A { - "Description should be copied." + # Should be processed + "Description should be ignored." id: ID! name: String! - child: B! - children: [B!]! - argument(arg: String): A! - arguments(arg: String): [A!] + relation: B! @hasOne + relations: [B!]! @hasMany + relationWithArgs(arg: String): A! @hasOne + relationsWithArgs(arg: String): [A!] @hasMany + + # Should be ignored + field: B! + fields: [B!]! + property(arg: String): Int! + resolver: Int! @field(resolver: "\\LastDragon_ru\\LaraASP\\GraphQL\\SortBy\\Directives\\DirectiveTest__Resolver") + relationsPaginated: B! @hasMany(type: PAGINATOR) + relationsPaginatedWithArgs(arg: String): [B!]! @hasMany(type: PAGINATOR) } type B { - "Description should be copied." + "Description should be ignored." id: ID! name: String - parent: A! - argument(arg: String): B! - arguments(arg: String): [B!] + parent: A! @hasOne +} + +interface C { + # Should be processed + "Description should be ignored." + id: ID! + name: String! + relation: B! @hasOne + relations: [B!]! @hasMany + relationWithArgs(arg: String): A! @hasOne + relationsWithArgs(arg: String): [A!] @hasMany + + # Should be ignored + field: B! + fields: [B!]! + property(arg: String): Int! + resolver: Int! @field(resolver: "\\LastDragon_ru\\LaraASP\\GraphQL\\SortBy\\Directives\\DirectiveTest__Resolver") + relationsPaginated: B! @hasMany(type: PAGINATOR) + relationsPaginatedWithArgs(arg: String): [B!]! @hasMany(type: PAGINATOR) } diff --git a/packages/graphql/src/SortBy/Directives/DirectiveTest/Scout.expected.graphql b/packages/graphql/src/SortBy/Directives/DirectiveTest/Scout.expected.graphql index 213c46714..4f2444921 100644 --- a/packages/graphql/src/SortBy/Directives/DirectiveTest/Scout.expected.graphql +++ b/packages/graphql/src/SortBy/Directives/DirectiveTest/Scout.expected.graphql @@ -297,12 +297,6 @@ input SortByClauseObjectInterface { Sort clause for `type ObjectNested` (only one property allowed at a time). """ input SortByClauseObjectNested { - """ - Property clause. - """ - nested: SortByClauseObjectNested - @sortByOperatorProperty - """ NULLs first """ @@ -412,6 +406,12 @@ input SortByClauseProperties { """ random: SortByTypeFlag @sortByOperatorRandom + + """ + Property clause. + """ + resolver: SortByTypeDirection + @sortByOperatorField } """ @@ -486,6 +486,12 @@ input SortByQueryClauseProperties { """ random: SortByTypeFlag @sortByOperatorRandom + + """ + Property clause. + """ + resolver: SortByTypeDirection + @sortByOperatorField } """ @@ -595,6 +601,12 @@ input SortByScoutClauseProperties { """ nullsLast: SortByScoutClauseProperties @sortByOperatorNullsLast + + """ + Property clause. + """ + resolver: SortByTypeDirection + @sortByOperatorField } interface Eloquent @@ -645,8 +657,12 @@ interface ObjectInterface { ignoredType: ObjectIgnored ignoredTypeList: [ObjectIgnored!]! ignoredTypeNonNull: ObjectIgnored! + nested: ObjectNested + @hasOne + nestedNotNull: ObjectNested! + @hasOne resolver: ID! @field( @@ -726,8 +742,12 @@ implements ignoredType: ObjectIgnored ignoredTypeList: [ObjectIgnored!]! ignoredTypeNonNull: ObjectIgnored! + nested: ObjectNested + @hasOne + nestedNotNull: ObjectNested! + @hasOne resolver: ID! @field( @@ -764,7 +784,7 @@ implements interface( order: [SortByClauseObjectInterface!] @sortBy - ): Object! + ): ObjectInterface! @all placeholder( diff --git a/packages/graphql/src/SortBy/Directives/DirectiveTest/Scout.schema.graphql b/packages/graphql/src/SortBy/Directives/DirectiveTest/Scout.schema.graphql index 458154e45..c9e4ad880 100644 --- a/packages/graphql/src/SortBy/Directives/DirectiveTest/Scout.schema.graphql +++ b/packages/graphql/src/SortBy/Directives/DirectiveTest/Scout.schema.graphql @@ -9,7 +9,7 @@ type Query implements Eloquent & Scout { placeholder(order: _ @sortBy): Object! @all - interface(order: ObjectInterface @sortBy): Object! @all + interface(order: _ @sortBy): ObjectInterface! @all } interface Eloquent implements Placeholder { @@ -81,8 +81,8 @@ enum EnumIgnored @sortByIgnored { interface ObjectInterface { idScalar: ID idScalarNotNull: ID! - nested: ObjectNested - nestedNotNull: ObjectNested! + nested: ObjectNested @hasOne + nestedNotNull: ObjectNested! @hasOne enum: Value enumNotNull: Value! enumIgnored: EnumIgnored @@ -116,8 +116,8 @@ interface ObjectInterface { type Object implements ObjectInterface { idScalar: ID idScalarNotNull: ID! - nested: ObjectNested - nestedNotNull: ObjectNested! + nested: ObjectNested @hasOne + nestedNotNull: ObjectNested! @hasOne enum: Value enumNotNull: Value! enumIgnored: EnumIgnored diff --git a/packages/graphql/src/Testing/Package/TestCase.php b/packages/graphql/src/Testing/Package/TestCase.php index 0de43d47d..1373b2264 100644 --- a/packages/graphql/src/Testing/Package/TestCase.php +++ b/packages/graphql/src/Testing/Package/TestCase.php @@ -22,6 +22,7 @@ use LastDragon_ru\LaraASP\Testing\Package\TestCase as PackageTestCase; use Nuwave\Lighthouse\Execution\Arguments\Argument; use Nuwave\Lighthouse\LighthouseServiceProvider; +use Nuwave\Lighthouse\Pagination\PaginationServiceProvider as LighthousePaginationServiceProvider; use Nuwave\Lighthouse\Schema\DirectiveLocator; use Nuwave\Lighthouse\Testing\TestingServiceProvider as LighthouseTestingServiceProvider; use Nuwave\Lighthouse\Validation\ValidationServiceProvider as LighthouseValidationServiceProvider; @@ -52,6 +53,7 @@ protected function getPackageProviders(mixed $app): array { LighthouseServiceProvider::class, LighthouseTestingServiceProvider::class, LighthouseValidationServiceProvider::class, + LighthousePaginationServiceProvider::class, ]; } diff --git a/packages/graphql/src/Utils/RelationDirectiveHelper.php b/packages/graphql/src/Utils/RelationDirectiveHelper.php new file mode 100644 index 000000000..de3827683 --- /dev/null +++ b/packages/graphql/src/Utils/RelationDirectiveHelper.php @@ -0,0 +1,15 @@ +paginationType(); + } +} From 7bd4f2b1998ca6ec24b7f8b59286ee13d6056627 Mon Sep 17 00:00:00 2001 From: Aleksei Lebedev <1329824+LastDragon-ru@users.noreply.github.com> Date: Fri, 26 Jan 2024 09:25:19 +0400 Subject: [PATCH 11/26] Examples in docs will use test data. --- packages/graphql/docs/Directives/@searchBy.md | 8 +++++++- packages/graphql/docs/Directives/@sortBy.md | 8 +++++++- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/packages/graphql/docs/Directives/@searchBy.md b/packages/graphql/docs/Directives/@searchBy.md index b3aef6568..8b49b226d 100644 --- a/packages/graphql/docs/Directives/@searchBy.md +++ b/packages/graphql/docs/Directives/@searchBy.md @@ -32,6 +32,10 @@ Out the box directives provides following features: Let's start: +[include:example]: ../../src/SearchBy/Directives/DirectiveTest/Example.schema.graphql +[//]: # (start: e552ccbddb2cf6a9dd4e14f9295ad974ca19c375ba683681d959d5190028ded4) +[//]: # (warning: Generated automatically. Do not edit.) + ```graphql scalar Date @scalar(class: "Nuwave\\Lighthouse\\Schema\\Types\\Scalars\\Date") @@ -58,11 +62,13 @@ type User { input Comment { text: String! - user: User + user: User @belongsTo date: Date } ``` +[//]: # (end: e552ccbddb2cf6a9dd4e14f9295ad974ca19c375ba683681d959d5190028ded4) + That's all, just search 😃 (or look at [generated GraphQL schema](../../src/SearchBy/Directives/DirectiveTest/Example.expected.graphql)) ```graphql diff --git a/packages/graphql/docs/Directives/@sortBy.md b/packages/graphql/docs/Directives/@sortBy.md index 8686626f6..e86fc0198 100644 --- a/packages/graphql/docs/Directives/@sortBy.md +++ b/packages/graphql/docs/Directives/@sortBy.md @@ -21,6 +21,10 @@ on How to use (and [generated GraphQL schema](../../src/SortBy/Directives/DirectiveTest/Example.expected.graphql)): +[include:example]: ../../src/SortBy/Directives/DirectiveTest/Example.schema.graphql +[//]: # (start: f4e6a88c853cd9b15aa5c3388c1b61148589eddbc18b410b0e846695c5765e4f) +[//]: # (warning: Generated automatically. Do not edit.) + ```graphql type Query { "You can use normal input type" @@ -37,7 +41,7 @@ input UsersSort { type Comment { text: String - user: User + user: User @belongsTo } type User { @@ -46,6 +50,8 @@ type User { } ``` +[//]: # (end: f4e6a88c853cd9b15aa5c3388c1b61148589eddbc18b410b0e846695c5765e4f) + And: ```graphql From 2215613a678ae7b8c4a8d145faf56f5ef8f04077 Mon Sep 17 00:00:00 2001 From: Aleksei Lebedev <1329824+LastDragon-ru@users.noreply.github.com> Date: Fri, 26 Jan 2024 09:40:29 +0400 Subject: [PATCH 12/26] Test fixes. --- .../src/SearchBy/Directives/DirectiveTest.php | 15 +++++++++++++++ .../src/SortBy/Directives/DirectiveTest.php | 15 +++++++++++++++ packages/graphql/src/Testing/Package/TestCase.php | 2 -- 3 files changed, 30 insertions(+), 2 deletions(-) diff --git a/packages/graphql/src/SearchBy/Directives/DirectiveTest.php b/packages/graphql/src/SearchBy/Directives/DirectiveTest.php index 6e3e09f1b..e25976482 100644 --- a/packages/graphql/src/SearchBy/Directives/DirectiveTest.php +++ b/packages/graphql/src/SearchBy/Directives/DirectiveTest.php @@ -53,6 +53,7 @@ use LastDragon_ru\LaraASP\Testing\Providers\MergeDataProvider; use Mockery\MockInterface; use Nuwave\Lighthouse\Execution\Arguments\Argument; +use Nuwave\Lighthouse\Pagination\PaginationServiceProvider as LighthousePaginationServiceProvider; use Nuwave\Lighthouse\Schema\DirectiveLocator; use Nuwave\Lighthouse\Schema\TypeRegistry; use Nuwave\Lighthouse\Scout\SearchDirective; @@ -60,6 +61,7 @@ use Override; use PHPUnit\Framework\Attributes\CoversClass; +use function array_merge; use function config; use function implode; use function is_array; @@ -75,6 +77,19 @@ final class DirectiveTest extends TestCase { use WithTestObject; use MakesGraphQLRequests; + // + // ========================================================================= + /** + * @inheritDoc + */ + #[Override] + protected function getPackageProviders(mixed $app): array { + return array_merge(parent::getPackageProviders($app), [ + LighthousePaginationServiceProvider::class, + ]); + } + // + // // ========================================================================= /** diff --git a/packages/graphql/src/SortBy/Directives/DirectiveTest.php b/packages/graphql/src/SortBy/Directives/DirectiveTest.php index 858975511..74e90f7e2 100644 --- a/packages/graphql/src/SortBy/Directives/DirectiveTest.php +++ b/packages/graphql/src/SortBy/Directives/DirectiveTest.php @@ -39,6 +39,7 @@ use LastDragon_ru\LaraASP\Testing\Providers\ArrayDataProvider; use LastDragon_ru\LaraASP\Testing\Providers\CompositeDataProvider; use Mockery\MockInterface; +use Nuwave\Lighthouse\Pagination\PaginationServiceProvider as LighthousePaginationServiceProvider; use Nuwave\Lighthouse\Schema\DirectiveLocator; use Nuwave\Lighthouse\Schema\TypeRegistry; use Nuwave\Lighthouse\Scout\SearchDirective; @@ -46,6 +47,7 @@ use Override; use PHPUnit\Framework\Attributes\CoversClass; +use function array_merge; use function config; use function implode; use function is_array; @@ -61,6 +63,19 @@ final class DirectiveTest extends TestCase { use WithTestObject; use MakesGraphQLRequests; + // + // ========================================================================= + /** + * @inheritDoc + */ + #[Override] + protected function getPackageProviders(mixed $app): array { + return array_merge(parent::getPackageProviders($app), [ + LighthousePaginationServiceProvider::class, + ]); + } + // + // // ========================================================================= /** diff --git a/packages/graphql/src/Testing/Package/TestCase.php b/packages/graphql/src/Testing/Package/TestCase.php index 1373b2264..0de43d47d 100644 --- a/packages/graphql/src/Testing/Package/TestCase.php +++ b/packages/graphql/src/Testing/Package/TestCase.php @@ -22,7 +22,6 @@ use LastDragon_ru\LaraASP\Testing\Package\TestCase as PackageTestCase; use Nuwave\Lighthouse\Execution\Arguments\Argument; use Nuwave\Lighthouse\LighthouseServiceProvider; -use Nuwave\Lighthouse\Pagination\PaginationServiceProvider as LighthousePaginationServiceProvider; use Nuwave\Lighthouse\Schema\DirectiveLocator; use Nuwave\Lighthouse\Testing\TestingServiceProvider as LighthouseTestingServiceProvider; use Nuwave\Lighthouse\Validation\ValidationServiceProvider as LighthouseValidationServiceProvider; @@ -53,7 +52,6 @@ protected function getPackageProviders(mixed $app): array { LighthouseServiceProvider::class, LighthouseTestingServiceProvider::class, LighthouseValidationServiceProvider::class, - LighthousePaginationServiceProvider::class, ]; } From e65075d26621b11d82f66a63a8df15a425296f98 Mon Sep 17 00:00:00 2001 From: Aleksei Lebedev <1329824+LastDragon-ru@users.noreply.github.com> Date: Fri, 26 Jan 2024 10:16:06 +0400 Subject: [PATCH 13/26] refactor(graphql)!: Context moved to `LastDragon_ru\LaraASP\GraphQL\Builder\Context` namespace. --- packages/graphql/UPGRADE.md | 2 +- .../{Directives => Context}/HandlerContextBuilderInfo.php | 2 +- .../{Directives => Context}/HandlerContextImplicit.php | 2 +- packages/graphql/src/Builder/Directives/HandlerDirective.php | 2 ++ packages/graphql/src/Builder/Manipulator.php | 2 +- packages/graphql/src/Builder/ManipulatorTest.php | 2 +- packages/graphql/src/Builder/Types/InputObject.php | 4 ++-- .../graphql/src/SearchBy/Operators/Complex/RelationType.php | 2 +- packages/graphql/src/SearchBy/Types/Condition.php | 2 +- packages/graphql/src/SearchBy/Types/Enumeration.php | 2 +- packages/graphql/src/SearchBy/Types/Scalar.php | 2 +- packages/graphql/src/SortBy/Types/Clause.php | 2 +- 12 files changed, 14 insertions(+), 12 deletions(-) rename packages/graphql/src/Builder/{Directives => Context}/HandlerContextBuilderInfo.php (79%) rename packages/graphql/src/Builder/{Directives => Context}/HandlerContextImplicit.php (73%) diff --git a/packages/graphql/UPGRADE.md b/packages/graphql/UPGRADE.md index 16e2e77f2..9b954174b 100644 --- a/packages/graphql/UPGRADE.md +++ b/packages/graphql/UPGRADE.md @@ -93,5 +93,5 @@ This section is actual only if you are extending the package. Please review and * [ ] To get `BuilderInfo` instance within Operator the `LastDragon_ru\LaraASP\GraphQL\Builder\Contracts\Context` should be used instead of `LastDragon_ru\LaraASP\GraphQL\Builder\Manipulator`: ```php - $context->get(LastDragon_ru\LaraASP\GraphQL\Builder\Directives\HandlerContextBuilderInfo::class)?->value + $context->get(LastDragon_ru\LaraASP\GraphQL\Builder\Context\HandlerContextBuilderInfo::class)?->value ``` diff --git a/packages/graphql/src/Builder/Directives/HandlerContextBuilderInfo.php b/packages/graphql/src/Builder/Context/HandlerContextBuilderInfo.php similarity index 79% rename from packages/graphql/src/Builder/Directives/HandlerContextBuilderInfo.php rename to packages/graphql/src/Builder/Context/HandlerContextBuilderInfo.php index 26889f1b5..d028846d7 100644 --- a/packages/graphql/src/Builder/Directives/HandlerContextBuilderInfo.php +++ b/packages/graphql/src/Builder/Context/HandlerContextBuilderInfo.php @@ -1,6 +1,6 @@ Date: Fri, 26 Jan 2024 10:37:41 +0400 Subject: [PATCH 14/26] `Manipulator::getPlaceholderTypeDefinitionNode()` will support Lighthouse `RelationDirective`. --- packages/graphql/src/Builder/Manipulator.php | 47 ++++++++++--------- .../graphql/src/Builder/ManipulatorTest.php | 31 ++++++++++++ .../src/Utils/PaginateDirectiveHelper.php | 15 ++++++ 3 files changed, 71 insertions(+), 22 deletions(-) create mode 100644 packages/graphql/src/Utils/PaginateDirectiveHelper.php diff --git a/packages/graphql/src/Builder/Manipulator.php b/packages/graphql/src/Builder/Manipulator.php index 97877e08f..2b5ff71e6 100644 --- a/packages/graphql/src/Builder/Manipulator.php +++ b/packages/graphql/src/Builder/Manipulator.php @@ -39,9 +39,12 @@ use LastDragon_ru\LaraASP\GraphQL\Builder\Sources\Source; use LastDragon_ru\LaraASP\GraphQL\Stream\Directives\Directive as StreamDirective; use LastDragon_ru\LaraASP\GraphQL\Utils\AstManipulator; +use LastDragon_ru\LaraASP\GraphQL\Utils\PaginateDirectiveHelper; +use LastDragon_ru\LaraASP\GraphQL\Utils\RelationDirectiveHelper; use Nuwave\Lighthouse\Pagination\PaginateDirective; -use Nuwave\Lighthouse\Pagination\PaginationType; use Nuwave\Lighthouse\Schema\DirectiveLocator; +use Nuwave\Lighthouse\Schema\Directives\RelationDirective; +use Nuwave\Lighthouse\Support\Contracts\Directive; use Override; use function array_map; @@ -314,35 +317,35 @@ public function getPlaceholderTypeDefinitionNode( ): TypeDefinitionNode|Type|null { $node = $this->getTypeDefinition($field); $name = $this->getTypeName($node); - $directives = [ - StreamDirective::class, - PaginateDirective::class, - ]; + $directives = $this->getDirectives($field, null, static function (Directive $directive): bool { + return $directive instanceof StreamDirective + || $directive instanceof PaginateDirective + || $directive instanceof RelationDirective; + }); foreach ($directives as $directive) { - $directive = $this->getDirective($field, $directive); - $type = null; + $type = null; if ($directive instanceof StreamDirective) { $type = Str::singular(mb_substr($name, 0, -mb_strlen(StreamDirective::Name))); - } elseif ($directive instanceof PaginateDirective) { - $pagination = (new class() extends PaginateDirective { - public function getPaginationType(PaginateDirective $directive): PaginationType { - return $directive->paginationType(); + } elseif ($directive instanceof PaginateDirective || $directive instanceof RelationDirective) { + $pagination = $directive instanceof PaginateDirective + ? PaginateDirectiveHelper::getPaginationType($directive) + : RelationDirectiveHelper::getPaginationType($directive); + + if ($pagination) { + if ($pagination->isPaginator()) { + $type = mb_substr($name, 0, -mb_strlen('Paginator')); + } elseif ($pagination->isSimple()) { + $type = mb_substr($name, 0, -mb_strlen('SimplePaginator')); + } elseif ($pagination->isConnection()) { + $type = mb_substr($name, 0, -mb_strlen('Connection')); + } else { + // empty } - })->getPaginationType($directive); - - if ($pagination->isPaginator()) { - $type = mb_substr($name, 0, -mb_strlen('Paginator')); - } elseif ($pagination->isSimple()) { - $type = mb_substr($name, 0, -mb_strlen('SimplePaginator')); - } elseif ($pagination->isConnection()) { - $type = mb_substr($name, 0, -mb_strlen('Connection')); - } else { - // empty } } else { - // empty + // empty } if ($type) { diff --git a/packages/graphql/src/Builder/ManipulatorTest.php b/packages/graphql/src/Builder/ManipulatorTest.php index 1c6c55045..554aca9d0 100644 --- a/packages/graphql/src/Builder/ManipulatorTest.php +++ b/packages/graphql/src/Builder/ManipulatorTest.php @@ -344,6 +344,37 @@ public static function dataProviderGetPlaceholderTypeDefinitionNode(): array { } GRAPHQL, ], + '@hasOne' => [ + 'Test', + <<<'GRAPHQL' + type Query { + field: [Test!] + @hasOne( + model: "\\LastDragon_ru\\LaraASP\\GraphQL\\Testing\\Package\\Data\\Models\\TestObject" + ) + } + + type Test { + field: Int + } + GRAPHQL, + ], + '@hasMany(type: PAGINATOR)' => [ + 'Test', + <<<'GRAPHQL' + type Query { + field: [Test!] + @hasMany( + model: "\\LastDragon_ru\\LaraASP\\GraphQL\\Testing\\Package\\Data\\Models\\TestObject" + type: PAGINATOR + ) + } + + type Test { + field: Int + } + GRAPHQL, + ], '@stream' => [ 'Test', <<<'GRAPHQL' diff --git a/packages/graphql/src/Utils/PaginateDirectiveHelper.php b/packages/graphql/src/Utils/PaginateDirectiveHelper.php new file mode 100644 index 000000000..e50dbcff5 --- /dev/null +++ b/packages/graphql/src/Utils/PaginateDirectiveHelper.php @@ -0,0 +1,15 @@ +paginationType(); + } +} From 30f3e95fb8b691198ebab4b73d007635258b38f2 Mon Sep 17 00:00:00 2001 From: Aleksei Lebedev <1329824+LastDragon-ru@users.noreply.github.com> Date: Fri, 26 Jan 2024 14:03:38 +0400 Subject: [PATCH 15/26] refactor(graphql)!: Added parent to the `Source`. --- packages/graphql/UPGRADE.md | 2 + .../src/Builder/BuilderInfoDetectorTest.php | 271 ++++++++---------- .../src/Builder/Sources/InputFieldSource.php | 12 +- .../src/Builder/Sources/InputSource.php | 4 +- .../Sources/InterfaceFieldArgumentSource.php | 22 +- .../Builder/Sources/InterfaceFieldSource.php | 23 +- .../src/Builder/Sources/InterfaceSource.php | 4 +- .../Sources/ObjectFieldArgumentSource.php | 22 +- .../src/Builder/Sources/ObjectFieldSource.php | 18 +- .../src/Builder/Sources/ObjectSource.php | 4 +- .../graphql/src/Builder/Sources/Source.php | 12 +- .../graphql/src/Builder/Traits/WithSource.php | 28 +- .../src/Stream/Directives/Directive.php | 3 +- .../src/Stream/Directives/DirectiveTest.php | 83 +++--- 14 files changed, 242 insertions(+), 266 deletions(-) diff --git a/packages/graphql/UPGRADE.md b/packages/graphql/UPGRADE.md index 9b954174b..58d4a4dd1 100644 --- a/packages/graphql/UPGRADE.md +++ b/packages/graphql/UPGRADE.md @@ -86,6 +86,8 @@ This section is actual only if you are extending the package. Please review and * [ ] `LastDragon_ru\LaraASP\GraphQL\Builder\Directives\PropertyDirective` +* [ ] `LastDragon_ru\LaraASP\GraphQL\Builder\Sources\*` + * [ ] `LastDragon_ru\LaraASP\GraphQL\Builder\Types\InputObject` * [ ] `LastDragon_ru\LaraASP\GraphQL\SortBy\Builders\*` => `LastDragon_ru\LaraASP\GraphQL\SortBy\Sorters\*` diff --git a/packages/graphql/src/Builder/BuilderInfoDetectorTest.php b/packages/graphql/src/Builder/BuilderInfoDetectorTest.php index 8345f3196..e5877fd45 100644 --- a/packages/graphql/src/Builder/BuilderInfoDetectorTest.php +++ b/packages/graphql/src/Builder/BuilderInfoDetectorTest.php @@ -16,6 +16,7 @@ use LastDragon_ru\LaraASP\GraphQL\Builder\Sources\InterfaceFieldSource; use LastDragon_ru\LaraASP\GraphQL\Builder\Sources\ObjectFieldArgumentSource; use LastDragon_ru\LaraASP\GraphQL\Builder\Sources\ObjectFieldSource; +use LastDragon_ru\LaraASP\GraphQL\Builder\Sources\ObjectSource; use LastDragon_ru\LaraASP\GraphQL\Builder\Traits\WithManipulator; use LastDragon_ru\LaraASP\GraphQL\Testing\Package\Requirements\RequiresLaravelScout; use LastDragon_ru\LaraASP\GraphQL\Testing\Package\TestCase; @@ -112,11 +113,10 @@ public static function dataProviderGetNodeBuilderInfo(): array { 'builder' => null, ], static function (DirectiveLocator $locator, AstManipulator $manipulator): ObjectFieldSource { - return new ObjectFieldSource( - $manipulator, - new ObjectType(['name' => 'Test', 'fields' => []]), - Parser::fieldDefinition('field: String'), - ); + return (new ObjectSource($manipulator, new ObjectType(['name' => 'Test', 'fields' => []]))) + ->getField( + Parser::fieldDefinition('field: String'), + ); }, ], '@all' => [ @@ -127,11 +127,10 @@ static function (DirectiveLocator $locator, AstManipulator $manipulator): Object static function (DirectiveLocator $locator, AstManipulator $manipulator): ObjectFieldSource { $locator->setResolved('all', AllDirective::class); - return new ObjectFieldSource( - $manipulator, - new ObjectType(['name' => 'Test', 'fields' => []]), - Parser::fieldDefinition('field: String @all'), - ); + return (new ObjectSource($manipulator, new ObjectType(['name' => 'Test', 'fields' => []]))) + ->getField( + Parser::fieldDefinition('field: String @all'), + ); }, ], '@all(query)' => [ @@ -145,11 +144,10 @@ static function (DirectiveLocator $locator, AstManipulator $manipulator): Object $class = json_encode(BuilderInfoDetectorTest__QueryBuilderResolver::class, JSON_THROW_ON_ERROR); $field = Parser::fieldDefinition("field: String @all(builder: {$class})"); - return new ObjectFieldSource( - $manipulator, - new ObjectType(['name' => 'Test', 'fields' => []]), - $field, - ); + return (new ObjectSource($manipulator, new ObjectType(['name' => 'Test', 'fields' => []]))) + ->getField( + $field, + ); }, ], '@all(custom query)' => [ @@ -163,11 +161,10 @@ static function (DirectiveLocator $locator, AstManipulator $manipulator): Object $class = json_encode(BuilderInfoDetectorTest__CustomBuilderResolver::class, JSON_THROW_ON_ERROR); $field = Parser::fieldDefinition("field: String @all(builder: {$class})"); - return new ObjectFieldSource( - $manipulator, - new ObjectType(['name' => 'Test', 'fields' => []]), - $field, - ); + return (new ObjectSource($manipulator, new ObjectType(['name' => 'Test', 'fields' => []]))) + ->getField( + $field, + ); }, ], '@paginate' => [ @@ -178,11 +175,10 @@ static function (DirectiveLocator $locator, AstManipulator $manipulator): Object static function (DirectiveLocator $locator, AstManipulator $manipulator): ObjectFieldSource { $locator->setResolved('paginate', PaginateDirective::class); - return new ObjectFieldSource( - $manipulator, - new ObjectType(['name' => 'Test', 'fields' => []]), - Parser::fieldDefinition('field: String @paginate'), - ); + return (new ObjectSource($manipulator, new ObjectType(['name' => 'Test', 'fields' => []]))) + ->getField( + Parser::fieldDefinition('field: String @paginate'), + ); }, ], '@paginate(resolver)' => [ @@ -196,11 +192,10 @@ static function (DirectiveLocator $locator, AstManipulator $manipulator): Object $class = json_encode(BuilderInfoDetectorTest__PaginatorResolver::class, JSON_THROW_ON_ERROR); $field = Parser::fieldDefinition("field: String @paginate(resolver: {$class})"); - return new ObjectFieldSource( - $manipulator, - new ObjectType(['name' => 'Test', 'fields' => []]), - $field, - ); + return (new ObjectSource($manipulator, new ObjectType(['name' => 'Test', 'fields' => []]))) + ->getField( + $field, + ); }, ], '@paginate(query)' => [ @@ -214,11 +209,10 @@ static function (DirectiveLocator $locator, AstManipulator $manipulator): Object $class = json_encode(BuilderInfoDetectorTest__QueryBuilderResolver::class, JSON_THROW_ON_ERROR); $field = Parser::fieldDefinition("field: String @paginate(builder: {$class})"); - return new ObjectFieldSource( - $manipulator, - new ObjectType(['name' => 'Test', 'fields' => []]), - $field, - ); + return (new ObjectSource($manipulator, new ObjectType(['name' => 'Test', 'fields' => []]))) + ->getField( + $field, + ); }, ], '@paginate(custom query)' => [ @@ -232,11 +226,10 @@ static function (DirectiveLocator $locator, AstManipulator $manipulator): Object $class = json_encode(BuilderInfoDetectorTest__CustomBuilderResolver::class, JSON_THROW_ON_ERROR); $field = Parser::fieldDefinition("field: String @paginate(builder: {$class})"); - return new ObjectFieldSource( - $manipulator, - new ObjectType(['name' => 'Test', 'fields' => []]), - $field, - ); + return (new ObjectSource($manipulator, new ObjectType(['name' => 'Test', 'fields' => []]))) + ->getField( + $field, + ); }, ], '@relation' => [ @@ -247,11 +240,10 @@ static function (DirectiveLocator $locator, AstManipulator $manipulator): Object static function (DirectiveLocator $locator, AstManipulator $manipulator): ObjectFieldSource { $locator->setResolved('relation', BuilderInfoDetectorTest__RelationDirective::class); - return new ObjectFieldSource( - $manipulator, - new ObjectType(['name' => 'Test', 'fields' => []]), - Parser::fieldDefinition('field: String @relation'), - ); + return (new ObjectSource($manipulator, new ObjectType(['name' => 'Test', 'fields' => []]))) + ->getField( + Parser::fieldDefinition('field: String @relation'), + ); }, ], '@find' => [ @@ -262,11 +254,10 @@ static function (DirectiveLocator $locator, AstManipulator $manipulator): Object static function (DirectiveLocator $locator, AstManipulator $manipulator): ObjectFieldSource { $locator->setResolved('find', FindDirective::class); - return new ObjectFieldSource( - $manipulator, - new ObjectType(['name' => 'Test', 'fields' => []]), - Parser::fieldDefinition('field: String @find'), - ); + return (new ObjectSource($manipulator, new ObjectType(['name' => 'Test', 'fields' => []]))) + ->getField( + Parser::fieldDefinition('field: String @find'), + ); }, ], '@first' => [ @@ -277,11 +268,10 @@ static function (DirectiveLocator $locator, AstManipulator $manipulator): Object static function (DirectiveLocator $locator, AstManipulator $manipulator): ObjectFieldSource { $locator->setResolved('first', FirstDirective::class); - return new ObjectFieldSource( - $manipulator, - new ObjectType(['name' => 'Test', 'fields' => []]), - Parser::fieldDefinition('field: String @first'), - ); + return (new ObjectSource($manipulator, new ObjectType(['name' => 'Test', 'fields' => []]))) + ->getField( + Parser::fieldDefinition('field: String @first'), + ); }, ], '@count' => [ @@ -292,11 +282,10 @@ static function (DirectiveLocator $locator, AstManipulator $manipulator): Object static function (DirectiveLocator $locator, AstManipulator $manipulator): ObjectFieldSource { $locator->setResolved('count', CountDirective::class); - return new ObjectFieldSource( - $manipulator, - new ObjectType(['name' => 'Test', 'fields' => []]), - Parser::fieldDefinition('field: String @count'), - ); + return (new ObjectSource($manipulator, new ObjectType(['name' => 'Test', 'fields' => []]))) + ->getField( + Parser::fieldDefinition('field: String @count'), + ); }, ], '@aggregate' => [ @@ -307,11 +296,10 @@ static function (DirectiveLocator $locator, AstManipulator $manipulator): Object static function (DirectiveLocator $locator, AstManipulator $manipulator): ObjectFieldSource { $locator->setResolved('aggregate', AggregateDirective::class); - return new ObjectFieldSource( - $manipulator, - new ObjectType(['name' => 'Test', 'fields' => []]), - Parser::fieldDefinition('field: String @aggregate'), - ); + return (new ObjectSource($manipulator, new ObjectType(['name' => 'Test', 'fields' => []]))) + ->getField( + Parser::fieldDefinition('field: String @aggregate'), + ); }, ], '@aggregate(query)' => [ @@ -325,11 +313,10 @@ static function (DirectiveLocator $locator, AstManipulator $manipulator): Object $class = json_encode(BuilderInfoDetectorTest__QueryBuilderResolver::class, JSON_THROW_ON_ERROR); $field = Parser::fieldDefinition("field: String @aggregate(builder: {$class})"); - return new ObjectFieldSource( - $manipulator, - new ObjectType(['name' => 'Test', 'fields' => []]), - $field, - ); + return (new ObjectSource($manipulator, new ObjectType(['name' => 'Test', 'fields' => []]))) + ->getField( + $field, + ); }, ], BuilderInfoProvider::class => [ @@ -340,11 +327,10 @@ static function (DirectiveLocator $locator, AstManipulator $manipulator): Object static function (DirectiveLocator $locator, AstManipulator $manipulator): ObjectFieldSource { $locator->setResolved('custom', BuilderInfoDetectorTest__BuilderInfoProviderDirective::class); - return new ObjectFieldSource( - $manipulator, - new ObjectType(['name' => 'Test', 'fields' => []]), - Parser::fieldDefinition('field: String @custom'), - ); + return (new ObjectSource($manipulator, new ObjectType(['name' => 'Test', 'fields' => []]))) + ->getField( + Parser::fieldDefinition('field: String @custom'), + ); }, ], ]; @@ -366,11 +352,10 @@ public static function dataProviderGetNodeBuilderInfoScoutBuilder(): array { static function (DirectiveLocator $locator, AstManipulator $manipulator): ObjectFieldSource { $locator->setResolved('search', SearchDirective::class); - return new ObjectFieldSource( - $manipulator, - new ObjectType(['name' => 'Test', 'fields' => []]), - Parser::fieldDefinition('field(search: String @search): String'), - ); + return (new ObjectSource($manipulator, new ObjectType(['name' => 'Test', 'fields' => []]))) + ->getField( + Parser::fieldDefinition('field(search: String @search): String'), + ); }, ], '@all' => [ @@ -382,11 +367,10 @@ static function (DirectiveLocator $locator, AstManipulator $manipulator): Object $locator->setResolved('search', SearchDirective::class); $locator->setResolved('all', AllDirective::class); - return new ObjectFieldSource( - $manipulator, - new ObjectType(['name' => 'Test', 'fields' => []]), - Parser::fieldDefinition('field(search: String @search): String @all'), - ); + return (new ObjectSource($manipulator, new ObjectType(['name' => 'Test', 'fields' => []]))) + ->getField( + Parser::fieldDefinition('field(search: String @search): String @all'), + ); }, ], '@all(query)' => [ @@ -401,11 +385,10 @@ static function (DirectiveLocator $locator, AstManipulator $manipulator): Object $class = json_encode(BuilderInfoDetectorTest__QueryBuilderResolver::class, JSON_THROW_ON_ERROR); $field = Parser::fieldDefinition("field(search: String @search): String @all(builder: {$class})"); - return new ObjectFieldSource( - $manipulator, - new ObjectType(['name' => 'Test', 'fields' => []]), - $field, - ); + return (new ObjectSource($manipulator, new ObjectType(['name' => 'Test', 'fields' => []]))) + ->getField( + $field, + ); }, ], '@all(custom query)' => [ @@ -420,11 +403,10 @@ static function (DirectiveLocator $locator, AstManipulator $manipulator): Object $class = json_encode(BuilderInfoDetectorTest__CustomBuilderResolver::class, JSON_THROW_ON_ERROR); $field = Parser::fieldDefinition("field(search: String @search): String @all(builder: {$class})"); - return new ObjectFieldSource( - $manipulator, - new ObjectType(['name' => 'Test', 'fields' => []]), - $field, - ); + return (new ObjectSource($manipulator, new ObjectType(['name' => 'Test', 'fields' => []]))) + ->getField( + $field, + ); }, ], '@paginate' => [ @@ -436,11 +418,10 @@ static function (DirectiveLocator $locator, AstManipulator $manipulator): Object $locator->setResolved('search', SearchDirective::class); $locator->setResolved('paginate', PaginateDirective::class); - return new ObjectFieldSource( - $manipulator, - new ObjectType(['name' => 'Test', 'fields' => []]), - Parser::fieldDefinition('field(search: String @search): String @paginate'), - ); + return (new ObjectSource($manipulator, new ObjectType(['name' => 'Test', 'fields' => []]))) + ->getField( + Parser::fieldDefinition('field(search: String @search): String @paginate'), + ); }, ], '@paginate(resolver)' => [ @@ -457,11 +438,10 @@ static function (DirectiveLocator $locator, AstManipulator $manipulator): Object "field(search: String @search): String @paginate(resolver: {$class})", ); - return new ObjectFieldSource( - $manipulator, - new ObjectType(['name' => 'Test', 'fields' => []]), - $field, - ); + return (new ObjectSource($manipulator, new ObjectType(['name' => 'Test', 'fields' => []]))) + ->getField( + $field, + ); }, ], '@paginate(query)' => [ @@ -478,11 +458,10 @@ static function (DirectiveLocator $locator, AstManipulator $manipulator): Object "field(search: String @search): String @paginate(builder: {$class})", ); - return new ObjectFieldSource( - $manipulator, - new ObjectType(['name' => 'Test', 'fields' => []]), - $field, - ); + return (new ObjectSource($manipulator, new ObjectType(['name' => 'Test', 'fields' => []]))) + ->getField( + $field, + ); }, ], '@paginate(custom query)' => [ @@ -499,11 +478,10 @@ static function (DirectiveLocator $locator, AstManipulator $manipulator): Object "field(search: String @search): String @paginate(builder: {$class})", ); - return new ObjectFieldSource( - $manipulator, - new ObjectType(['name' => 'Test', 'fields' => []]), - $field, - ); + return (new ObjectSource($manipulator, new ObjectType(['name' => 'Test', 'fields' => []]))) + ->getField( + $field, + ); }, ], '@relation' => [ @@ -515,11 +493,10 @@ static function (DirectiveLocator $locator, AstManipulator $manipulator): Object $locator->setResolved('search', SearchDirective::class); $locator->setResolved('relation', BuilderInfoDetectorTest__RelationDirective::class); - return new ObjectFieldSource( - $manipulator, - new ObjectType(['name' => 'Test', 'fields' => []]), - Parser::fieldDefinition('field(search: String @search): String @relation'), - ); + return (new ObjectSource($manipulator, new ObjectType(['name' => 'Test', 'fields' => []]))) + ->getField( + Parser::fieldDefinition('field(search: String @search): String @relation'), + ); }, ], '@find' => [ @@ -531,11 +508,10 @@ static function (DirectiveLocator $locator, AstManipulator $manipulator): Object $locator->setResolved('search', SearchDirective::class); $locator->setResolved('find', FindDirective::class); - return new ObjectFieldSource( - $manipulator, - new ObjectType(['name' => 'Test', 'fields' => []]), - Parser::fieldDefinition('field(search: String @search): String @find'), - ); + return (new ObjectSource($manipulator, new ObjectType(['name' => 'Test', 'fields' => []]))) + ->getField( + Parser::fieldDefinition('field(search: String @search): String @find'), + ); }, ], '@first' => [ @@ -547,11 +523,10 @@ static function (DirectiveLocator $locator, AstManipulator $manipulator): Object $locator->setResolved('search', SearchDirective::class); $locator->setResolved('first', FirstDirective::class); - return new ObjectFieldSource( - $manipulator, - new ObjectType(['name' => 'Test', 'fields' => []]), - Parser::fieldDefinition('field(search: String @search): String @first'), - ); + return (new ObjectSource($manipulator, new ObjectType(['name' => 'Test', 'fields' => []]))) + ->getField( + Parser::fieldDefinition('field(search: String @search): String @first'), + ); }, ], '@count' => [ @@ -563,11 +538,10 @@ static function (DirectiveLocator $locator, AstManipulator $manipulator): Object $locator->setResolved('search', SearchDirective::class); $locator->setResolved('count', CountDirective::class); - return new ObjectFieldSource( - $manipulator, - new ObjectType(['name' => 'Test', 'fields' => []]), - Parser::fieldDefinition('field(search: String @search): String @count'), - ); + return (new ObjectSource($manipulator, new ObjectType(['name' => 'Test', 'fields' => []]))) + ->getField( + Parser::fieldDefinition('field(search: String @search): String @count'), + ); }, ], '@aggregate' => [ @@ -579,11 +553,10 @@ static function (DirectiveLocator $locator, AstManipulator $manipulator): Object $locator->setResolved('search', SearchDirective::class); $locator->setResolved('aggregate', AggregateDirective::class); - return new ObjectFieldSource( - $manipulator, - new ObjectType(['name' => 'Test', 'fields' => []]), - Parser::fieldDefinition('field(search: String @search): String @aggregate'), - ); + return (new ObjectSource($manipulator, new ObjectType(['name' => 'Test', 'fields' => []]))) + ->getField( + Parser::fieldDefinition('field(search: String @search): String @aggregate'), + ); }, ], '@aggregate(query)' => [ @@ -600,11 +573,10 @@ static function (DirectiveLocator $locator, AstManipulator $manipulator): Object "field(search: String @search): String @aggregate(builder: {$class})", ); - return new ObjectFieldSource( - $manipulator, - new ObjectType(['name' => 'Test', 'fields' => []]), - $field, - ); + return (new ObjectSource($manipulator, new ObjectType(['name' => 'Test', 'fields' => []]))) + ->getField( + $field, + ); }, ], BuilderInfoProvider::class => [ @@ -616,11 +588,10 @@ static function (DirectiveLocator $locator, AstManipulator $manipulator): Object $locator->setResolved('search', SearchDirective::class); $locator->setResolved('custom', BuilderInfoDetectorTest__BuilderInfoProviderDirective::class); - return new ObjectFieldSource( - $manipulator, - new ObjectType(['name' => 'Test', 'fields' => []]), - Parser::fieldDefinition('field(search: String @search): String @custom'), - ); + return (new ObjectSource($manipulator, new ObjectType(['name' => 'Test', 'fields' => []]))) + ->getField( + Parser::fieldDefinition('field(search: String @search): String @custom'), + ); }, ], ]; diff --git a/packages/graphql/src/Builder/Sources/InputFieldSource.php b/packages/graphql/src/Builder/Sources/InputFieldSource.php index b1c7774a8..da67a2721 100644 --- a/packages/graphql/src/Builder/Sources/InputFieldSource.php +++ b/packages/graphql/src/Builder/Sources/InputFieldSource.php @@ -17,23 +17,27 @@ use LastDragon_ru\LaraASP\GraphQL\Utils\AstManipulator; /** - * @extends Source + * @extends Source */ class InputFieldSource extends Source { use Field; public function __construct( AstManipulator $manipulator, - private InputObjectTypeDefinitionNode|InputObjectType $object, + InputSource $parent, private InputValueDefinitionNode|InputObjectField $field, ) { - parent::__construct($manipulator, $field instanceof InputObjectField ? $field->getType() : $field->type); + parent::__construct( + $manipulator, + $field instanceof InputObjectField ? $field->getType() : $field->type, + $parent, + ); } // // ========================================================================= public function getObject(): InputObjectTypeDefinitionNode|InputObjectType { - return $this->object; + return $this->getParent()->getType(); } public function getField(): InputValueDefinitionNode|InputObjectField { diff --git a/packages/graphql/src/Builder/Sources/InputSource.php b/packages/graphql/src/Builder/Sources/InputSource.php index e04b6f8c8..233267c34 100644 --- a/packages/graphql/src/Builder/Sources/InputSource.php +++ b/packages/graphql/src/Builder/Sources/InputSource.php @@ -8,10 +8,10 @@ use GraphQL\Type\Definition\InputObjectType; /** - * @extends Source + * @extends Source */ class InputSource extends Source { public function getField(InputValueDefinitionNode|InputObjectField $field): InputFieldSource { - return new InputFieldSource($this->getManipulator(), $this->getType(), $field); + return new InputFieldSource($this->getManipulator(), $this, $field); } } diff --git a/packages/graphql/src/Builder/Sources/InterfaceFieldArgumentSource.php b/packages/graphql/src/Builder/Sources/InterfaceFieldArgumentSource.php index 1fe949168..5eda800cd 100644 --- a/packages/graphql/src/Builder/Sources/InterfaceFieldArgumentSource.php +++ b/packages/graphql/src/Builder/Sources/InterfaceFieldArgumentSource.php @@ -16,39 +16,35 @@ use LastDragon_ru\LaraASP\GraphQL\Utils\AstManipulator; /** - * @extends Source + * @extends Source */ class InterfaceFieldArgumentSource extends Source { use FieldArgument; public function __construct( AstManipulator $manipulator, - private InterfaceTypeDefinitionNode|InterfaceType $object, - private FieldDefinitionNode|FieldDefinition $field, + InterfaceFieldSource $parent, private InputValueDefinitionNode|Argument $argument, ) { - parent::__construct($manipulator, $argument instanceof Argument ? $argument->getType() : $argument->type); + parent::__construct( + $manipulator, + $argument instanceof Argument ? $argument->getType() : $argument->type, + $parent, + ); } // // ========================================================================= public function getObject(): InterfaceTypeDefinitionNode|InterfaceType { - return $this->object; + return $this->getParent()->getObject(); } public function getField(): FieldDefinition|FieldDefinitionNode { - return $this->field; + return $this->getParent()->getField(); } public function getArgument(): InputValueDefinitionNode|Argument { return $this->argument; } // - - // - // ================================================================================================================= - public function getParent(): InterfaceFieldSource { - return new InterfaceFieldSource($this->getManipulator(), $this->getObject(), $this->getField()); - } - // } diff --git a/packages/graphql/src/Builder/Sources/InterfaceFieldSource.php b/packages/graphql/src/Builder/Sources/InterfaceFieldSource.php index 5a7235c11..1e10a7dd9 100644 --- a/packages/graphql/src/Builder/Sources/InterfaceFieldSource.php +++ b/packages/graphql/src/Builder/Sources/InterfaceFieldSource.php @@ -16,23 +16,27 @@ use LastDragon_ru\LaraASP\GraphQL\Utils\AstManipulator; /** - * @extends Source + * @extends Source */ class InterfaceFieldSource extends Source { use Field; public function __construct( AstManipulator $manipulator, - private InterfaceTypeDefinitionNode|InterfaceType $object, + InterfaceSource $parent, private FieldDefinitionNode|FieldDefinition $field, ) { - parent::__construct($manipulator, $field instanceof FieldDefinition ? $field->getType() : $field->type); + parent::__construct( + $manipulator, + $field instanceof FieldDefinition ? $field->getType() : $field->type, + $parent, + ); } // // ========================================================================= public function getObject(): InterfaceTypeDefinitionNode|InterfaceType { - return $this->object; + return $this->getParent()->getType(); } public function getField(): FieldDefinition|FieldDefinitionNode { @@ -42,17 +46,8 @@ public function getField(): FieldDefinition|FieldDefinitionNode { // // ================================================================================================================= - public function getParent(): InterfaceSource { - return new InterfaceSource($this->getManipulator(), $this->getObject()); - } - public function getArgument(InputValueDefinitionNode|Argument $argument): InterfaceFieldArgumentSource { - return new InterfaceFieldArgumentSource( - $this->getManipulator(), - $this->getObject(), - $this->getField(), - $argument, - ); + return new InterfaceFieldArgumentSource($this->getManipulator(), $this, $argument); } // } diff --git a/packages/graphql/src/Builder/Sources/InterfaceSource.php b/packages/graphql/src/Builder/Sources/InterfaceSource.php index 600d528bc..dae936201 100644 --- a/packages/graphql/src/Builder/Sources/InterfaceSource.php +++ b/packages/graphql/src/Builder/Sources/InterfaceSource.php @@ -8,10 +8,10 @@ use GraphQL\Type\Definition\InterfaceType; /** - * @extends Source + * @extends Source */ class InterfaceSource extends Source { public function getField(FieldDefinitionNode|FieldDefinition $field): InterfaceFieldSource { - return new InterfaceFieldSource($this->getManipulator(), $this->getType(), $field); + return new InterfaceFieldSource($this->getManipulator(), $this, $field); } } diff --git a/packages/graphql/src/Builder/Sources/ObjectFieldArgumentSource.php b/packages/graphql/src/Builder/Sources/ObjectFieldArgumentSource.php index ac7f18f5f..f6f4f8f49 100644 --- a/packages/graphql/src/Builder/Sources/ObjectFieldArgumentSource.php +++ b/packages/graphql/src/Builder/Sources/ObjectFieldArgumentSource.php @@ -16,39 +16,35 @@ use LastDragon_ru\LaraASP\GraphQL\Utils\AstManipulator; /** - * @extends Source + * @extends Source */ class ObjectFieldArgumentSource extends Source { use FieldArgument; public function __construct( AstManipulator $manipulator, - private ObjectTypeDefinitionNode|ObjectType $object, - private FieldDefinitionNode|FieldDefinition $field, + ObjectFieldSource $parent, private InputValueDefinitionNode|Argument $argument, ) { - parent::__construct($manipulator, $argument instanceof Argument ? $argument->getType() : $argument->type); + parent::__construct( + $manipulator, + $argument instanceof Argument ? $argument->getType() : $argument->type, + $parent, + ); } // // ========================================================================= public function getObject(): ObjectTypeDefinitionNode|ObjectType { - return $this->object; + return $this->getParent()->getObject(); } public function getField(): FieldDefinition|FieldDefinitionNode { - return $this->field; + return $this->getParent()->getField(); } public function getArgument(): InputValueDefinitionNode|Argument { return $this->argument; } // - - // - // ================================================================================================================= - public function getParent(): ObjectFieldSource { - return new ObjectFieldSource($this->getManipulator(), $this->getObject(), $this->getField()); - } - // } diff --git a/packages/graphql/src/Builder/Sources/ObjectFieldSource.php b/packages/graphql/src/Builder/Sources/ObjectFieldSource.php index 3a85ec2a2..e5c22b09f 100644 --- a/packages/graphql/src/Builder/Sources/ObjectFieldSource.php +++ b/packages/graphql/src/Builder/Sources/ObjectFieldSource.php @@ -16,23 +16,27 @@ use LastDragon_ru\LaraASP\GraphQL\Utils\AstManipulator; /** - * @extends Source + * @extends Source */ class ObjectFieldSource extends Source { use Field; public function __construct( AstManipulator $manipulator, - private ObjectTypeDefinitionNode|ObjectType $object, + ObjectSource $parent, private FieldDefinitionNode|FieldDefinition $field, ) { - parent::__construct($manipulator, $field instanceof FieldDefinition ? $field->getType() : $field->type); + parent::__construct( + $manipulator, + $field instanceof FieldDefinition ? $field->getType() : $field->type, + $parent, + ); } // // ========================================================================= public function getObject(): ObjectTypeDefinitionNode|ObjectType { - return $this->object; + return $this->getParent()->getType(); } public function getField(): FieldDefinition|FieldDefinitionNode { @@ -42,12 +46,8 @@ public function getField(): FieldDefinition|FieldDefinitionNode { // // ================================================================================================================= - public function getParent(): ObjectSource { - return new ObjectSource($this->getManipulator(), $this->getObject()); - } - public function getArgument(InputValueDefinitionNode|Argument $argument): ObjectFieldArgumentSource { - return new ObjectFieldArgumentSource($this->getManipulator(), $this->getObject(), $this->getField(), $argument); + return new ObjectFieldArgumentSource($this->getManipulator(), $this, $argument); } // } diff --git a/packages/graphql/src/Builder/Sources/ObjectSource.php b/packages/graphql/src/Builder/Sources/ObjectSource.php index 69e078d53..dfcbaebf6 100644 --- a/packages/graphql/src/Builder/Sources/ObjectSource.php +++ b/packages/graphql/src/Builder/Sources/ObjectSource.php @@ -8,10 +8,10 @@ use GraphQL\Type\Definition\ObjectType; /** - * @extends Source + * @extends Source */ class ObjectSource extends Source { public function getField(FieldDefinitionNode|FieldDefinition $field): ObjectFieldSource { - return new ObjectFieldSource($this->getManipulator(), $this->getType(), $field); + return new ObjectFieldSource($this->getManipulator(), $this, $field); } } diff --git a/packages/graphql/src/Builder/Sources/Source.php b/packages/graphql/src/Builder/Sources/Source.php index 26634c9fc..be989267a 100644 --- a/packages/graphql/src/Builder/Sources/Source.php +++ b/packages/graphql/src/Builder/Sources/Source.php @@ -14,14 +14,17 @@ /** * @template TType of (TypeDefinitionNode&Node)|NamedTypeNode|ListTypeNode|NonNullTypeNode|Type + * @template TParent of TypeSource|null */ class Source implements TypeSource { /** - * @param TType $type + * @param TType $type + * @param TParent $parent */ public function __construct( private AstManipulator $manipulator, private TypeDefinitionNode|Node|Type $type, + private TypeSource|null $parent = null, ) { // empty } @@ -31,6 +34,13 @@ public function __construct( protected function getManipulator(): AstManipulator { return $this->manipulator; } + + /** + * @return TParent + */ + public function getParent(): ?TypeSource { + return $this->parent; + } // // diff --git a/packages/graphql/src/Builder/Traits/WithSource.php b/packages/graphql/src/Builder/Traits/WithSource.php index b2c285d8b..ee632a68e 100644 --- a/packages/graphql/src/Builder/Traits/WithSource.php +++ b/packages/graphql/src/Builder/Traits/WithSource.php @@ -8,29 +8,45 @@ use GraphQL\Language\AST\ObjectTypeDefinitionNode; use LastDragon_ru\LaraASP\GraphQL\Builder\Sources\InterfaceFieldArgumentSource; use LastDragon_ru\LaraASP\GraphQL\Builder\Sources\InterfaceFieldSource; +use LastDragon_ru\LaraASP\GraphQL\Builder\Sources\InterfaceSource; use LastDragon_ru\LaraASP\GraphQL\Builder\Sources\ObjectFieldArgumentSource; use LastDragon_ru\LaraASP\GraphQL\Builder\Sources\ObjectFieldSource; +use LastDragon_ru\LaraASP\GraphQL\Builder\Sources\ObjectSource; use LastDragon_ru\LaraASP\GraphQL\Utils\AstManipulator; trait WithSource { + /** + * @return ($type is InterfaceTypeDefinitionNode ? InterfaceSource : ObjectSource) + */ + protected function getTypeSource( + AstManipulator $manipulator, + ObjectTypeDefinitionNode|InterfaceTypeDefinitionNode $type, + ): InterfaceSource|ObjectSource { + return $type instanceof InterfaceTypeDefinitionNode + ? new InterfaceSource($manipulator, $type) + : new ObjectSource($manipulator, $type); + } + + /** + * @return ($type is InterfaceTypeDefinitionNode ? InterfaceFieldSource : ObjectFieldSource) + */ protected function getFieldSource( AstManipulator $manipulator, ObjectTypeDefinitionNode|InterfaceTypeDefinitionNode $type, FieldDefinitionNode $field, ): ObjectFieldSource|InterfaceFieldSource { - return $type instanceof InterfaceTypeDefinitionNode - ? new InterfaceFieldSource($manipulator, $type, $field) - : new ObjectFieldSource($manipulator, $type, $field); + return $this->getTypeSource($manipulator, $type)->getField($field); } + /** + * @return ($type is InterfaceTypeDefinitionNode ? InterfaceFieldArgumentSource : ObjectFieldArgumentSource) + */ protected function getFieldArgumentSource( AstManipulator $manipulator, ObjectTypeDefinitionNode|InterfaceTypeDefinitionNode $type, FieldDefinitionNode $field, InputValueDefinitionNode $argument, ): ObjectFieldArgumentSource|InterfaceFieldArgumentSource { - return $type instanceof InterfaceTypeDefinitionNode - ? new InterfaceFieldArgumentSource($manipulator, $type, $field, $argument) - : new ObjectFieldArgumentSource($manipulator, $type, $field, $argument); + return $this->getFieldSource($manipulator, $type, $field)->getArgument($argument); } } diff --git a/packages/graphql/src/Stream/Directives/Directive.php b/packages/graphql/src/Stream/Directives/Directive.php index ed1e7747c..198701649 100644 --- a/packages/graphql/src/Stream/Directives/Directive.php +++ b/packages/graphql/src/Stream/Directives/Directive.php @@ -24,6 +24,7 @@ use LastDragon_ru\LaraASP\GraphQL\Builder\Sources\InterfaceFieldSource; use LastDragon_ru\LaraASP\GraphQL\Builder\Sources\ObjectFieldArgumentSource; use LastDragon_ru\LaraASP\GraphQL\Builder\Sources\ObjectFieldSource; +use LastDragon_ru\LaraASP\GraphQL\Builder\Sources\ObjectSource; use LastDragon_ru\LaraASP\GraphQL\Builder\Traits\WithManipulator; use LastDragon_ru\LaraASP\GraphQL\Builder\Traits\WithSource; use LastDragon_ru\LaraASP\GraphQL\Package; @@ -436,7 +437,7 @@ public function resolveField(FieldValue $fieldValue): callable { return function (mixed $root, array $args, GraphQLContext $context, ResolveInfo $info): StreamValue { // Offset $manipulator = $this->getAstManipulator(new DocumentAST()); - $source = new ObjectFieldSource($manipulator, $info->parentType, $info->fieldDefinition); + $source = (new ObjectSource($manipulator, $info->parentType))->getField($info->fieldDefinition); $offset = $this->getFieldValue(StreamOffsetDirective::class, $manipulator, $source, $info, $args); // Builder diff --git a/packages/graphql/src/Stream/Directives/DirectiveTest.php b/packages/graphql/src/Stream/Directives/DirectiveTest.php index 269bdbebc..c3acbce79 100644 --- a/packages/graphql/src/Stream/Directives/DirectiveTest.php +++ b/packages/graphql/src/Stream/Directives/DirectiveTest.php @@ -577,11 +577,11 @@ public function testGetResolverExplicit(array|null $expected, string $arguments) ]); $field = Parser::fieldDefinition('test: String'); - $source = new ObjectFieldSource( + $object = new ObjectSource( Mockery::mock(AstManipulator::class)->makePartial(), new ObjectType(['name' => 'Car', 'fields' => []]), - $field, ); + $source = $object->getField($field); $directive = Mockery::mock(Directive::class); $directive->shouldAllowMockingProtectedMethods(); $directive->makePartial(); @@ -1131,9 +1131,7 @@ public function getFieldArgumentValue(ResolveInfo $info, mixed $value): mixed { $value = $directive->getFieldValue( DirectiveTest_Directive::class, $manipulator, - new ObjectFieldSource( - $manipulator, - $object, + (new ObjectSource($manipulator, $object))->getField( Parser::fieldDefinition( 'test(d: Int @markerA, b: String @markerA @deprecated): String', ), @@ -1148,9 +1146,7 @@ public function getFieldArgumentValue(ResolveInfo $info, mixed $value): mixed { $value = $directive->getFieldValue( DirectiveTest_Directive::class, $manipulator, - new ObjectFieldSource( - $manipulator, - $object, + (new ObjectSource($manipulator, $object))->getField( Parser::fieldDefinition( 'test(a: String @markerA @deprecated, b: Int @markerB): String', ), @@ -1205,9 +1201,7 @@ public function getFieldValue( $directive->getFieldValue( DirectiveTest_Directive::class, $manipulator, - new ObjectFieldSource( - $manipulator, - $object, + (new ObjectSource($manipulator, $object))->getField( Parser::fieldDefinition( 'test(a: Int, b: String): String', ), @@ -1261,9 +1255,7 @@ public function getFieldValue( $directive->getFieldValue( $marker::class, $manipulator, - new ObjectFieldSource( - $manipulator, - $object, + (new ObjectSource($manipulator, $object))->getField( Parser::fieldDefinition( 'test(a: Int @marker, b: String @marker @deprecated): String', ), @@ -1523,11 +1515,10 @@ static function (): void { public static function dataProviderGetBuilderInfo(): array { $class = new DirectiveTest_Model(); $factory = static function (AstManipulator $manipulator): ObjectFieldSource { - return new ObjectFieldSource( - $manipulator, - new ObjectType(['name' => 'ObjectA', 'fields' => []]), - Parser::fieldDefinition('test: String'), - ); + return (new ObjectSource($manipulator, new ObjectType(['name' => 'ObjectA', 'fields' => []]))) + ->getField( + Parser::fieldDefinition('test: String'), + ); }; return [ @@ -1589,11 +1580,10 @@ static function () use ($class): stdClass|self { */ public static function dataProviderGetBuilderInfoScoutBuilder(): array { $factory = static function (AstManipulator $manipulator): ObjectFieldSource { - return new ObjectFieldSource( - $manipulator, - new ObjectType(['name' => 'ObjectA', 'fields' => []]), - Parser::fieldDefinition('test(search: String! @search): String'), - ); + return (new ObjectSource($manipulator, new ObjectType(['name' => 'ObjectA', 'fields' => []]))) + ->getField( + Parser::fieldDefinition('test(search: String! @search): String'), + ); }; return [ @@ -1682,11 +1672,10 @@ public static function dataProviderGetArgKey(): array { } GRAPHQL; $factory = static function (AstManipulator $manipulator): ObjectFieldSource { - return new ObjectFieldSource( - $manipulator, - new ObjectType(['name' => 'ObjectA', 'fields' => []]), - Parser::fieldDefinition('test: String'), - ); + return (new ObjectSource($manipulator, new ObjectType(['name' => 'ObjectA', 'fields' => []]))) + ->getField( + Parser::fieldDefinition('test: String'), + ); }; return [ @@ -1707,11 +1696,10 @@ public static function dataProviderGetArgKey(): array { $schema, Parser::directive('@stream'), static function (AstManipulator $manipulator): ObjectFieldSource { - return new ObjectFieldSource( - $manipulator, - new ObjectType(['name' => 'Object', 'fields' => []]), - Parser::fieldDefinition('test: ObjectA'), - ); + return (new ObjectSource($manipulator, new ObjectType(['name' => 'Object', 'fields' => []]))) + ->getField( + Parser::fieldDefinition('test: ObjectA'), + ); }, ], 'Invalid type' => [ @@ -1719,11 +1707,10 @@ static function (AstManipulator $manipulator): ObjectFieldSource { $schema, Parser::directive('@stream'), static function (AstManipulator $manipulator): ObjectFieldSource { - return new ObjectFieldSource( - $manipulator, - new ObjectType(['name' => 'ObjectA', 'fields' => []]), - Parser::fieldDefinition('test: ObjectB'), - ); + return (new ObjectSource($manipulator, new ObjectType(['name' => 'ObjectA', 'fields' => []]))) + ->getField( + Parser::fieldDefinition('test: ObjectB'), + ); }, ], 'Converted' => [ @@ -1731,11 +1718,10 @@ static function (AstManipulator $manipulator): ObjectFieldSource { $schema, Parser::directive('@stream'), static function (AstManipulator $manipulator): ObjectFieldSource { - return new ObjectFieldSource( - $manipulator, - new ObjectType(['name' => 'ObjectB', 'fields' => []]), - Parser::fieldDefinition('test: ObjectAsStream'), - ); + return (new ObjectSource($manipulator, new ObjectType(['name' => 'ObjectB', 'fields' => []]))) + ->getField( + Parser::fieldDefinition('test: ObjectAsStream'), + ); }, ], '@rename' => [ @@ -1743,11 +1729,10 @@ static function (AstManipulator $manipulator): ObjectFieldSource { $schema, Parser::directive('@stream'), static function (AstManipulator $manipulator): ObjectFieldSource { - return new ObjectFieldSource( - $manipulator, - new ObjectType(['name' => 'ObjectB', 'fields' => []]), - Parser::fieldDefinition('test: ObjectC'), - ); + return (new ObjectSource($manipulator, new ObjectType(['name' => 'ObjectB', 'fields' => []]))) + ->getField( + Parser::fieldDefinition('test: ObjectC'), + ); }, ], ]; From 760a71d25ccae905682515dfd80a893bdcde8d85 Mon Sep 17 00:00:00 2001 From: Aleksei Lebedev <1329824+LastDragon-ru@users.noreply.github.com> Date: Fri, 26 Jan 2024 14:21:25 +0400 Subject: [PATCH 16/26] refactor(graphql)!: Ability to override type of `*FieldSource`/`*ArgumentSource`. --- .../src/Builder/Contracts/TypeSource.php | 8 +++---- .../src/Builder/Sources/InputFieldSource.php | 13 +++++++---- .../src/Builder/Sources/InputSource.php | 13 +++++++++-- .../Sources/InterfaceFieldArgumentSource.php | 13 +++++++---- .../Builder/Sources/InterfaceFieldSource.php | 23 +++++++++++++------ .../src/Builder/Sources/InterfaceSource.php | 13 +++++++++-- .../Sources/ObjectFieldArgumentSource.php | 13 +++++++---- .../src/Builder/Sources/ObjectFieldSource.php | 23 +++++++++++++------ .../src/Builder/Sources/ObjectSource.php | 13 +++++++++-- .../graphql/src/Builder/Sources/Source.php | 10 ++++---- 10 files changed, 96 insertions(+), 46 deletions(-) diff --git a/packages/graphql/src/Builder/Contracts/TypeSource.php b/packages/graphql/src/Builder/Contracts/TypeSource.php index 30d4c3744..e1ca1a83b 100644 --- a/packages/graphql/src/Builder/Contracts/TypeSource.php +++ b/packages/graphql/src/Builder/Contracts/TypeSource.php @@ -2,19 +2,17 @@ namespace LastDragon_ru\LaraASP\GraphQL\Builder\Contracts; -use GraphQL\Language\AST\ListTypeNode; -use GraphQL\Language\AST\NamedTypeNode; use GraphQL\Language\AST\Node; -use GraphQL\Language\AST\NonNullTypeNode; use GraphQL\Language\AST\TypeDefinitionNode; +use GraphQL\Language\AST\TypeNode; use GraphQL\Type\Definition\Type; use Stringable; interface TypeSource extends Stringable { /** - * @return (TypeDefinitionNode&Node)|NamedTypeNode|ListTypeNode|NonNullTypeNode|Type + * @return (TypeDefinitionNode&Node)|(TypeNode&Node)|Type */ - public function getType(): TypeDefinitionNode|NamedTypeNode|ListTypeNode|NonNullTypeNode|Type; + public function getType(): TypeDefinitionNode|TypeNode|Type; public function getTypeName(): string; diff --git a/packages/graphql/src/Builder/Sources/InputFieldSource.php b/packages/graphql/src/Builder/Sources/InputFieldSource.php index da67a2721..b1a460420 100644 --- a/packages/graphql/src/Builder/Sources/InputFieldSource.php +++ b/packages/graphql/src/Builder/Sources/InputFieldSource.php @@ -6,9 +6,8 @@ use GraphQL\Language\AST\ArgumentNode; use GraphQL\Language\AST\InputObjectTypeDefinitionNode; use GraphQL\Language\AST\InputValueDefinitionNode; -use GraphQL\Language\AST\ListTypeNode; -use GraphQL\Language\AST\NamedTypeNode; -use GraphQL\Language\AST\NonNullTypeNode; +use GraphQL\Language\AST\Node; +use GraphQL\Language\AST\TypeNode; use GraphQL\Type\Definition\Argument; use GraphQL\Type\Definition\InputObjectField; use GraphQL\Type\Definition\InputObjectType; @@ -17,19 +16,23 @@ use LastDragon_ru\LaraASP\GraphQL\Utils\AstManipulator; /** - * @extends Source + * @extends Source<(TypeNode&Node)|Type, InputSource> */ class InputFieldSource extends Source { use Field; + /** + * @param (TypeNode&Node)|Type|null $type + */ public function __construct( AstManipulator $manipulator, InputSource $parent, private InputValueDefinitionNode|InputObjectField $field, + TypeNode|Type|null $type = null, ) { parent::__construct( $manipulator, - $field instanceof InputObjectField ? $field->getType() : $field->type, + $type ?? ($field instanceof InputObjectField ? $field->getType() : $field->type), $parent, ); } diff --git a/packages/graphql/src/Builder/Sources/InputSource.php b/packages/graphql/src/Builder/Sources/InputSource.php index 233267c34..d682b1a08 100644 --- a/packages/graphql/src/Builder/Sources/InputSource.php +++ b/packages/graphql/src/Builder/Sources/InputSource.php @@ -4,14 +4,23 @@ use GraphQL\Language\AST\InputObjectTypeDefinitionNode; use GraphQL\Language\AST\InputValueDefinitionNode; +use GraphQL\Language\AST\Node; +use GraphQL\Language\AST\TypeNode; use GraphQL\Type\Definition\InputObjectField; use GraphQL\Type\Definition\InputObjectType; +use GraphQL\Type\Definition\Type; /** * @extends Source */ class InputSource extends Source { - public function getField(InputValueDefinitionNode|InputObjectField $field): InputFieldSource { - return new InputFieldSource($this->getManipulator(), $this, $field); + /** + * @param (TypeNode&Node)|Type|null $type + */ + public function getField( + InputValueDefinitionNode|InputObjectField $field, + TypeNode|Type $type = null, + ): InputFieldSource { + return new InputFieldSource($this->getManipulator(), $this, $field, $type); } } diff --git a/packages/graphql/src/Builder/Sources/InterfaceFieldArgumentSource.php b/packages/graphql/src/Builder/Sources/InterfaceFieldArgumentSource.php index 5eda800cd..1b9a8c12b 100644 --- a/packages/graphql/src/Builder/Sources/InterfaceFieldArgumentSource.php +++ b/packages/graphql/src/Builder/Sources/InterfaceFieldArgumentSource.php @@ -5,9 +5,8 @@ use GraphQL\Language\AST\FieldDefinitionNode; use GraphQL\Language\AST\InputValueDefinitionNode; use GraphQL\Language\AST\InterfaceTypeDefinitionNode; -use GraphQL\Language\AST\ListTypeNode; -use GraphQL\Language\AST\NamedTypeNode; -use GraphQL\Language\AST\NonNullTypeNode; +use GraphQL\Language\AST\Node; +use GraphQL\Language\AST\TypeNode; use GraphQL\Type\Definition\Argument; use GraphQL\Type\Definition\FieldDefinition; use GraphQL\Type\Definition\InterfaceType; @@ -16,19 +15,23 @@ use LastDragon_ru\LaraASP\GraphQL\Utils\AstManipulator; /** - * @extends Source + * @extends Source<(TypeNode&Node)|Type, InterfaceFieldSource> */ class InterfaceFieldArgumentSource extends Source { use FieldArgument; + /** + * @param (TypeNode&Node)|Type|null $type + */ public function __construct( AstManipulator $manipulator, InterfaceFieldSource $parent, private InputValueDefinitionNode|Argument $argument, + TypeNode|Type|null $type = null, ) { parent::__construct( $manipulator, - $argument instanceof Argument ? $argument->getType() : $argument->type, + $type ?? ($argument instanceof Argument ? $argument->getType() : $argument->type), $parent, ); } diff --git a/packages/graphql/src/Builder/Sources/InterfaceFieldSource.php b/packages/graphql/src/Builder/Sources/InterfaceFieldSource.php index 1e10a7dd9..9672aaca4 100644 --- a/packages/graphql/src/Builder/Sources/InterfaceFieldSource.php +++ b/packages/graphql/src/Builder/Sources/InterfaceFieldSource.php @@ -5,9 +5,8 @@ use GraphQL\Language\AST\FieldDefinitionNode; use GraphQL\Language\AST\InputValueDefinitionNode; use GraphQL\Language\AST\InterfaceTypeDefinitionNode; -use GraphQL\Language\AST\ListTypeNode; -use GraphQL\Language\AST\NamedTypeNode; -use GraphQL\Language\AST\NonNullTypeNode; +use GraphQL\Language\AST\Node; +use GraphQL\Language\AST\TypeNode; use GraphQL\Type\Definition\Argument; use GraphQL\Type\Definition\FieldDefinition; use GraphQL\Type\Definition\InterfaceType; @@ -16,19 +15,23 @@ use LastDragon_ru\LaraASP\GraphQL\Utils\AstManipulator; /** - * @extends Source + * @extends Source<(TypeNode&Node)|Type, InterfaceSource> */ class InterfaceFieldSource extends Source { use Field; + /** + * @param (TypeNode&Node)|Type|null $type + */ public function __construct( AstManipulator $manipulator, InterfaceSource $parent, private FieldDefinitionNode|FieldDefinition $field, + TypeNode|Type|null $type, ) { parent::__construct( $manipulator, - $field instanceof FieldDefinition ? $field->getType() : $field->type, + $type ?? ($field instanceof FieldDefinition ? $field->getType() : $field->type), $parent, ); } @@ -46,8 +49,14 @@ public function getField(): FieldDefinition|FieldDefinitionNode { // // ================================================================================================================= - public function getArgument(InputValueDefinitionNode|Argument $argument): InterfaceFieldArgumentSource { - return new InterfaceFieldArgumentSource($this->getManipulator(), $this, $argument); + /** + * @param (TypeNode&Node)|Type|null $type + */ + public function getArgument( + InputValueDefinitionNode|Argument $argument, + TypeNode|Type $type = null, + ): InterfaceFieldArgumentSource { + return new InterfaceFieldArgumentSource($this->getManipulator(), $this, $argument, $type); } // } diff --git a/packages/graphql/src/Builder/Sources/InterfaceSource.php b/packages/graphql/src/Builder/Sources/InterfaceSource.php index dae936201..d38d5ed21 100644 --- a/packages/graphql/src/Builder/Sources/InterfaceSource.php +++ b/packages/graphql/src/Builder/Sources/InterfaceSource.php @@ -4,14 +4,23 @@ use GraphQL\Language\AST\FieldDefinitionNode; use GraphQL\Language\AST\InterfaceTypeDefinitionNode; +use GraphQL\Language\AST\Node; +use GraphQL\Language\AST\TypeNode; use GraphQL\Type\Definition\FieldDefinition; use GraphQL\Type\Definition\InterfaceType; +use GraphQL\Type\Definition\Type; /** * @extends Source */ class InterfaceSource extends Source { - public function getField(FieldDefinitionNode|FieldDefinition $field): InterfaceFieldSource { - return new InterfaceFieldSource($this->getManipulator(), $this, $field); + /** + * @param (TypeNode&Node)|Type|null $type + */ + public function getField( + FieldDefinitionNode|FieldDefinition $field, + TypeNode|Type $type = null, + ): InterfaceFieldSource { + return new InterfaceFieldSource($this->getManipulator(), $this, $field, $type); } } diff --git a/packages/graphql/src/Builder/Sources/ObjectFieldArgumentSource.php b/packages/graphql/src/Builder/Sources/ObjectFieldArgumentSource.php index f6f4f8f49..e7dcce0a6 100644 --- a/packages/graphql/src/Builder/Sources/ObjectFieldArgumentSource.php +++ b/packages/graphql/src/Builder/Sources/ObjectFieldArgumentSource.php @@ -4,10 +4,9 @@ use GraphQL\Language\AST\FieldDefinitionNode; use GraphQL\Language\AST\InputValueDefinitionNode; -use GraphQL\Language\AST\ListTypeNode; -use GraphQL\Language\AST\NamedTypeNode; -use GraphQL\Language\AST\NonNullTypeNode; +use GraphQL\Language\AST\Node; use GraphQL\Language\AST\ObjectTypeDefinitionNode; +use GraphQL\Language\AST\TypeNode; use GraphQL\Type\Definition\Argument; use GraphQL\Type\Definition\FieldDefinition; use GraphQL\Type\Definition\ObjectType; @@ -16,19 +15,23 @@ use LastDragon_ru\LaraASP\GraphQL\Utils\AstManipulator; /** - * @extends Source + * @extends Source<(TypeNode&Node)|Type, ObjectFieldSource> */ class ObjectFieldArgumentSource extends Source { use FieldArgument; + /** + * @param (TypeNode&Node)|Type|null $type + */ public function __construct( AstManipulator $manipulator, ObjectFieldSource $parent, private InputValueDefinitionNode|Argument $argument, + TypeNode|Type|null $type = null, ) { parent::__construct( $manipulator, - $argument instanceof Argument ? $argument->getType() : $argument->type, + $type ?? ($argument instanceof Argument ? $argument->getType() : $argument->type), $parent, ); } diff --git a/packages/graphql/src/Builder/Sources/ObjectFieldSource.php b/packages/graphql/src/Builder/Sources/ObjectFieldSource.php index e5c22b09f..34d139cf3 100644 --- a/packages/graphql/src/Builder/Sources/ObjectFieldSource.php +++ b/packages/graphql/src/Builder/Sources/ObjectFieldSource.php @@ -4,10 +4,9 @@ use GraphQL\Language\AST\FieldDefinitionNode; use GraphQL\Language\AST\InputValueDefinitionNode; -use GraphQL\Language\AST\ListTypeNode; -use GraphQL\Language\AST\NamedTypeNode; -use GraphQL\Language\AST\NonNullTypeNode; +use GraphQL\Language\AST\Node; use GraphQL\Language\AST\ObjectTypeDefinitionNode; +use GraphQL\Language\AST\TypeNode; use GraphQL\Type\Definition\Argument; use GraphQL\Type\Definition\FieldDefinition; use GraphQL\Type\Definition\ObjectType; @@ -16,19 +15,23 @@ use LastDragon_ru\LaraASP\GraphQL\Utils\AstManipulator; /** - * @extends Source + * @extends Source<(TypeNode&Node)|Type, ObjectSource> */ class ObjectFieldSource extends Source { use Field; + /** + * @param (TypeNode&Node)|Type|null $type + */ public function __construct( AstManipulator $manipulator, ObjectSource $parent, private FieldDefinitionNode|FieldDefinition $field, + TypeNode|Type|null $type = null, ) { parent::__construct( $manipulator, - $field instanceof FieldDefinition ? $field->getType() : $field->type, + $type ?? ($field instanceof FieldDefinition ? $field->getType() : $field->type), $parent, ); } @@ -46,8 +49,14 @@ public function getField(): FieldDefinition|FieldDefinitionNode { // // ================================================================================================================= - public function getArgument(InputValueDefinitionNode|Argument $argument): ObjectFieldArgumentSource { - return new ObjectFieldArgumentSource($this->getManipulator(), $this, $argument); + /** + * @param (TypeNode&Node)|Type|null $type + */ + public function getArgument( + InputValueDefinitionNode|Argument $argument, + TypeNode|Type $type = null, + ): ObjectFieldArgumentSource { + return new ObjectFieldArgumentSource($this->getManipulator(), $this, $argument, $type); } // } diff --git a/packages/graphql/src/Builder/Sources/ObjectSource.php b/packages/graphql/src/Builder/Sources/ObjectSource.php index dfcbaebf6..8bc080d45 100644 --- a/packages/graphql/src/Builder/Sources/ObjectSource.php +++ b/packages/graphql/src/Builder/Sources/ObjectSource.php @@ -3,15 +3,24 @@ namespace LastDragon_ru\LaraASP\GraphQL\Builder\Sources; use GraphQL\Language\AST\FieldDefinitionNode; +use GraphQL\Language\AST\Node; use GraphQL\Language\AST\ObjectTypeDefinitionNode; +use GraphQL\Language\AST\TypeNode; use GraphQL\Type\Definition\FieldDefinition; use GraphQL\Type\Definition\ObjectType; +use GraphQL\Type\Definition\Type; /** * @extends Source */ class ObjectSource extends Source { - public function getField(FieldDefinitionNode|FieldDefinition $field): ObjectFieldSource { - return new ObjectFieldSource($this->getManipulator(), $this, $field); + /** + * @param (TypeNode&Node)|Type|null $type + */ + public function getField( + FieldDefinitionNode|FieldDefinition $field, + TypeNode|Type $type = null, + ): ObjectFieldSource { + return new ObjectFieldSource($this->getManipulator(), $this, $field, $type); } } diff --git a/packages/graphql/src/Builder/Sources/Source.php b/packages/graphql/src/Builder/Sources/Source.php index be989267a..44342a89a 100644 --- a/packages/graphql/src/Builder/Sources/Source.php +++ b/packages/graphql/src/Builder/Sources/Source.php @@ -2,18 +2,16 @@ namespace LastDragon_ru\LaraASP\GraphQL\Builder\Sources; -use GraphQL\Language\AST\ListTypeNode; -use GraphQL\Language\AST\NamedTypeNode; use GraphQL\Language\AST\Node; -use GraphQL\Language\AST\NonNullTypeNode; use GraphQL\Language\AST\TypeDefinitionNode; +use GraphQL\Language\AST\TypeNode; use GraphQL\Type\Definition\Type; use LastDragon_ru\LaraASP\GraphQL\Builder\Contracts\TypeSource; use LastDragon_ru\LaraASP\GraphQL\Utils\AstManipulator; use Override; /** - * @template TType of (TypeDefinitionNode&Node)|NamedTypeNode|ListTypeNode|NonNullTypeNode|Type + * @template TType of (TypeDefinitionNode&Node)|(TypeNode&Node)|Type * @template TParent of TypeSource|null */ class Source implements TypeSource { @@ -23,7 +21,7 @@ class Source implements TypeSource { */ public function __construct( private AstManipulator $manipulator, - private TypeDefinitionNode|Node|Type $type, + private TypeDefinitionNode|TypeNode|Type $type, private TypeSource|null $parent = null, ) { // empty @@ -49,7 +47,7 @@ public function getParent(): ?TypeSource { * @return TType */ #[Override] - public function getType(): TypeDefinitionNode|NamedTypeNode|ListTypeNode|NonNullTypeNode|Type { + public function getType(): TypeDefinitionNode|TypeNode|Type { return $this->type; } From efc4c385fd1450af52cf496aed5ce6ad969a39cc Mon Sep 17 00:00:00 2001 From: Aleksei Lebedev <1329824+LastDragon-ru@users.noreply.github.com> Date: Sat, 27 Jan 2024 09:24:44 +0400 Subject: [PATCH 17/26] More tests (field with `@stream`). --- .../DirectiveTest/Implicit.expected.graphql | 159 ++++++++++++++++++ .../DirectiveTest/Implicit.schema.graphql | 12 ++ .../DirectiveTest/Implicit.expected.graphql | 159 ++++++++++++++++++ .../DirectiveTest/Implicit.schema.graphql | 12 ++ 4 files changed, 342 insertions(+) diff --git a/packages/graphql/src/SearchBy/Directives/DirectiveTest/Implicit.expected.graphql b/packages/graphql/src/SearchBy/Directives/DirectiveTest/Implicit.expected.graphql index 534591411..9368619c8 100644 --- a/packages/graphql/src/SearchBy/Directives/DirectiveTest/Implicit.expected.graphql +++ b/packages/graphql/src/SearchBy/Directives/DirectiveTest/Implicit.expected.graphql @@ -186,6 +186,54 @@ on | INPUT_FIELD_DEFINITION | SCALAR +""" +Splits list of items into the chunks and returns one chunk specified +by an offset or a cursor. +""" +directive @stream( + """ + Overrides default searchable status. + """ + searchable: Boolean + + """ + Overrides default sortable status. + """ + sortable: Boolean + + """ + Overrides default builder. Useful if the standard detection + algorithm doesn't fit/work. By default, the directive will use + the field and its type to determine the Builder to query. + """ + builder: StreamBuilder + + """ + Overrides default limit. + """ + limit: Int + + """ + Overrides default unique key. Useful if the standard detection + algorithm doesn't fit/work. By default, the directive will use + the name of field with `ID!` type. + """ + key: String +) +on + | FIELD_DEFINITION + +directive @streamLimit( + default: Int + max: Int +) +on + | ARGUMENT_DEFINITION + +directive @streamOffset +on + | ARGUMENT_DEFINITION + """ Options for the `type` argument of `@hasMany`. """ @@ -727,6 +775,26 @@ input SearchByTypeRangeInt { min: Int! } +""" +Explicit builder. Only one of fields allowed. +""" +input StreamBuilder { + """ + The reference to a function that provides a Builder instance. + """ + builder: String + + """ + The class name of the model to query. + """ + model: String + + """ + The relation name to query. + """ + relation: String +} + interface C { field: B! fields: [B!]! @@ -794,8 +862,48 @@ interface C { @field( resolver: "\\LastDragon_ru\\LaraASP\\GraphQL\\SearchBy\\Directives\\DirectiveTest__Resolver" ) + + stream( + where: SearchByConditionA + @searchBy + + """ + Maximum count of items to return. The value must be between `1` and `100`. + """ + limit: Int! = 25 + @streamLimit + @rules( + apply: ["integer", "min:1", "max:100"] + ) + + """ + The cursor or offset within the stream to start. + """ + offset: StreamOffset + @streamOffset + ): ASStream! + @stream( + sortable: false + builder: { + model: "LastDragon_ru\\LaraASP\\GraphQL\\Testing\\Package\\Data\\Models\\TestObject" + } + ) + @validate } +""" +Represents a offset for the `@stream` directive. The value can be a +positive `Int` or a `String`. The `Int` value represents the offset +(zero-based) to navigate to any position within the stream (= offset +pagination). And the `String` value represents the cursor and allows +navigation only to the previous/current/next pages (= cursor +pagination). +""" +scalar StreamOffset +@scalar( + class: "LastDragon_ru\\LaraASP\\GraphQL\\Stream\\Types\\Offset" +) + type A { field: B! fields: [B!]! @@ -863,6 +971,33 @@ type A { @field( resolver: "\\LastDragon_ru\\LaraASP\\GraphQL\\SearchBy\\Directives\\DirectiveTest__Resolver" ) + + stream( + where: SearchByConditionA + @searchBy + + """ + Maximum count of items to return. The value must be between `1` and `100`. + """ + limit: Int! = 25 + @streamLimit + @rules( + apply: ["integer", "min:1", "max:100"] + ) + + """ + The cursor or offset within the stream to start. + """ + offset: StreamOffset + @streamOffset + ): ASStream! + @stream( + sortable: false + builder: { + model: "LastDragon_ru\\LaraASP\\GraphQL\\Testing\\Package\\Data\\Models\\TestObject" + } + ) + @validate } """ @@ -890,6 +1025,24 @@ type APaginator ) } +type ASStream { + """ + Requested items. + """ + items: [A!]! + + """ + Total number of items. Not recommended querying it in each query + due to performance. + """ + length: Int + + """ + Offsets/Cursors to navigate within the stream. + """ + navigation: StreamNavigation! +} + type B { """ Description should be ignored. @@ -999,3 +1152,9 @@ type Query { ): [C!]! @all } + +type StreamNavigation { + current: StreamOffset! + next: StreamOffset + previous: StreamOffset +} diff --git a/packages/graphql/src/SearchBy/Directives/DirectiveTest/Implicit.schema.graphql b/packages/graphql/src/SearchBy/Directives/DirectiveTest/Implicit.schema.graphql index 19e1a3ee3..1c79e4acf 100644 --- a/packages/graphql/src/SearchBy/Directives/DirectiveTest/Implicit.schema.graphql +++ b/packages/graphql/src/SearchBy/Directives/DirectiveTest/Implicit.schema.graphql @@ -23,6 +23,12 @@ type A { resolver: Int! @field(resolver: "\\LastDragon_ru\\LaraASP\\GraphQL\\SearchBy\\Directives\\DirectiveTest__Resolver") relationsPaginated: B! @hasMany(type: PAGINATOR) relationsPaginatedWithArgs(arg: String): [B!]! @hasMany(type: PAGINATOR) + stream: [A!]! @stream( + sortable: false + builder: { + model: "LastDragon_ru\\LaraASP\\GraphQL\\Testing\\Package\\Data\\Models\\TestObject" + } + ) } type B { @@ -49,4 +55,10 @@ interface C { resolver: Int! @field(resolver: "\\LastDragon_ru\\LaraASP\\GraphQL\\SearchBy\\Directives\\DirectiveTest__Resolver") relationsPaginated: B! @hasMany(type: PAGINATOR) relationsPaginatedWithArgs(arg: String): [B!]! @hasMany(type: PAGINATOR) + stream: [A!]! @stream( + sortable: false + builder: { + model: "LastDragon_ru\\LaraASP\\GraphQL\\Testing\\Package\\Data\\Models\\TestObject" + } + ) } diff --git a/packages/graphql/src/SortBy/Directives/DirectiveTest/Implicit.expected.graphql b/packages/graphql/src/SortBy/Directives/DirectiveTest/Implicit.expected.graphql index e9119052e..5e4e96b1d 100644 --- a/packages/graphql/src/SortBy/Directives/DirectiveTest/Implicit.expected.graphql +++ b/packages/graphql/src/SortBy/Directives/DirectiveTest/Implicit.expected.graphql @@ -29,6 +29,54 @@ on | INPUT_FIELD_DEFINITION | SCALAR +""" +Splits list of items into the chunks and returns one chunk specified +by an offset or a cursor. +""" +directive @stream( + """ + Overrides default searchable status. + """ + searchable: Boolean + + """ + Overrides default sortable status. + """ + sortable: Boolean + + """ + Overrides default builder. Useful if the standard detection + algorithm doesn't fit/work. By default, the directive will use + the field and its type to determine the Builder to query. + """ + builder: StreamBuilder + + """ + Overrides default limit. + """ + limit: Int + + """ + Overrides default unique key. Useful if the standard detection + algorithm doesn't fit/work. By default, the directive will use + the name of field with `ID!` type. + """ + key: String +) +on + | FIELD_DEFINITION + +directive @streamLimit( + default: Int + max: Int +) +on + | ARGUMENT_DEFINITION + +directive @streamOffset +on + | ARGUMENT_DEFINITION + """ Options for the `type` argument of `@hasMany`. """ @@ -184,6 +232,26 @@ input SortByClauseC { @sortByOperatorProperty } +""" +Explicit builder. Only one of fields allowed. +""" +input StreamBuilder { + """ + The reference to a function that provides a Builder instance. + """ + builder: String + + """ + The class name of the model to query. + """ + model: String + + """ + The relation name to query. + """ + relation: String +} + interface C { field: B! fields: [B!]! @@ -251,8 +319,48 @@ interface C { @field( resolver: "\\LastDragon_ru\\LaraASP\\GraphQL\\SortBy\\Directives\\DirectiveTest__Resolver" ) + + stream( + order: [SortByClauseA!] + @sortBy + + """ + Maximum count of items to return. The value must be between `1` and `100`. + """ + limit: Int! = 25 + @streamLimit + @rules( + apply: ["integer", "min:1", "max:100"] + ) + + """ + The cursor or offset within the stream to start. + """ + offset: StreamOffset + @streamOffset + ): ASStream! + @stream( + searchable: false + builder: { + model: "LastDragon_ru\\LaraASP\\GraphQL\\Testing\\Package\\Data\\Models\\TestObject" + } + ) + @validate } +""" +Represents a offset for the `@stream` directive. The value can be a +positive `Int` or a `String`. The `Int` value represents the offset +(zero-based) to navigate to any position within the stream (= offset +pagination). And the `String` value represents the cursor and allows +navigation only to the previous/current/next pages (= cursor +pagination). +""" +scalar StreamOffset +@scalar( + class: "LastDragon_ru\\LaraASP\\GraphQL\\Stream\\Types\\Offset" +) + type A { field: B! fields: [B!]! @@ -320,6 +428,33 @@ type A { @field( resolver: "\\LastDragon_ru\\LaraASP\\GraphQL\\SortBy\\Directives\\DirectiveTest__Resolver" ) + + stream( + order: [SortByClauseA!] + @sortBy + + """ + Maximum count of items to return. The value must be between `1` and `100`. + """ + limit: Int! = 25 + @streamLimit + @rules( + apply: ["integer", "min:1", "max:100"] + ) + + """ + The cursor or offset within the stream to start. + """ + offset: StreamOffset + @streamOffset + ): ASStream! + @stream( + searchable: false + builder: { + model: "LastDragon_ru\\LaraASP\\GraphQL\\Testing\\Package\\Data\\Models\\TestObject" + } + ) + @validate } """ @@ -347,6 +482,24 @@ type APaginator ) } +type ASStream { + """ + Requested items. + """ + items: [A!]! + + """ + Total number of items. Not recommended querying it in each query + due to performance. + """ + length: Int + + """ + Offsets/Cursors to navigate within the stream. + """ + navigation: StreamNavigation! +} + type B { """ Description should be ignored. @@ -456,3 +609,9 @@ type Query { ): [C!]! @all } + +type StreamNavigation { + current: StreamOffset! + next: StreamOffset + previous: StreamOffset +} diff --git a/packages/graphql/src/SortBy/Directives/DirectiveTest/Implicit.schema.graphql b/packages/graphql/src/SortBy/Directives/DirectiveTest/Implicit.schema.graphql index 7e5e4b101..ddee7dfdf 100644 --- a/packages/graphql/src/SortBy/Directives/DirectiveTest/Implicit.schema.graphql +++ b/packages/graphql/src/SortBy/Directives/DirectiveTest/Implicit.schema.graphql @@ -23,6 +23,12 @@ type A { resolver: Int! @field(resolver: "\\LastDragon_ru\\LaraASP\\GraphQL\\SortBy\\Directives\\DirectiveTest__Resolver") relationsPaginated: B! @hasMany(type: PAGINATOR) relationsPaginatedWithArgs(arg: String): [B!]! @hasMany(type: PAGINATOR) + stream: [A!]! @stream( + searchable: false + builder: { + model: "LastDragon_ru\\LaraASP\\GraphQL\\Testing\\Package\\Data\\Models\\TestObject" + } + ) } type B { @@ -49,4 +55,10 @@ interface C { resolver: Int! @field(resolver: "\\LastDragon_ru\\LaraASP\\GraphQL\\SortBy\\Directives\\DirectiveTest__Resolver") relationsPaginated: B! @hasMany(type: PAGINATOR) relationsPaginatedWithArgs(arg: String): [B!]! @hasMany(type: PAGINATOR) + stream: [A!]! @stream( + searchable: false + builder: { + model: "LastDragon_ru\\LaraASP\\GraphQL\\Testing\\Package\\Data\\Models\\TestObject" + } + ) } From 342a1708a64f4dada9f6a5ec59012c765e9ab4bc Mon Sep 17 00:00:00 2001 From: Aleksei Lebedev <1329824+LastDragon-ru@users.noreply.github.com> Date: Sat, 27 Jan 2024 10:03:07 +0400 Subject: [PATCH 18/26] refactor(graphql)!: `Manipulator::getPlaceholderTypeDefinitionNode()` => `AstManipulator::getOriginTypeDefinition()`. --- packages/graphql/UPGRADE.md | 16 +- .../Builder/Directives/HandlerDirective.php | 14 +- packages/graphql/src/Builder/Manipulator.php | 61 ----- .../graphql/src/Builder/ManipulatorTest.php | 213 ------------------ packages/graphql/src/Utils/AstManipulator.php | 55 +++++ .../graphql/src/Utils/AstManipulatorTest.php | 178 +++++++++++++++ 6 files changed, 247 insertions(+), 290 deletions(-) diff --git a/packages/graphql/UPGRADE.md b/packages/graphql/UPGRADE.md index 58d4a4dd1..50e04f7ec 100644 --- a/packages/graphql/UPGRADE.md +++ b/packages/graphql/UPGRADE.md @@ -80,7 +80,15 @@ This section is actual only if you are extending the package. Please review and * [ ] `LastDragon_ru\LaraASP\GraphQL\Builder\Contracts\TypeProvider` -* [ ] `LastDragon_ru\LaraASP\GraphQL\Builder\Manipulator` (removed `BuilderInfo`) +* [ ] `LastDragon_ru\LaraASP\GraphQL\Builder\Manipulator` + + * [ ] Removed `BuilderInfo`. To get `BuilderInfo` instance within Operator the `LastDragon_ru\LaraASP\GraphQL\Builder\Contracts\Context` should be used instead + + ```php + $context->get(LastDragon_ru\LaraASP\GraphQL\Builder\Context\HandlerContextBuilderInfo::class)?->value + ``` + + * [ ] Removed `getPlaceholderTypeDefinitionNode()` => `LastDragon_ru\LaraASP\GraphQL\Utils\AstManipulator::getOriginTypeDefinition()` * [ ] `LastDragon_ru\LaraASP\GraphQL\Builder\Directives\HandlerDirective` @@ -91,9 +99,3 @@ This section is actual only if you are extending the package. Please review and * [ ] `LastDragon_ru\LaraASP\GraphQL\Builder\Types\InputObject` * [ ] `LastDragon_ru\LaraASP\GraphQL\SortBy\Builders\*` => `LastDragon_ru\LaraASP\GraphQL\SortBy\Sorters\*` - -* [ ] To get `BuilderInfo` instance within Operator the `LastDragon_ru\LaraASP\GraphQL\Builder\Contracts\Context` should be used instead of `LastDragon_ru\LaraASP\GraphQL\Builder\Manipulator`: - - ```php - $context->get(LastDragon_ru\LaraASP\GraphQL\Builder\Context\HandlerContextBuilderInfo::class)?->value - ``` diff --git a/packages/graphql/src/Builder/Directives/HandlerDirective.php b/packages/graphql/src/Builder/Directives/HandlerDirective.php index 0c46c3e44..47f4de7ff 100644 --- a/packages/graphql/src/Builder/Directives/HandlerDirective.php +++ b/packages/graphql/src/Builder/Directives/HandlerDirective.php @@ -263,17 +263,13 @@ protected function getArgumentTypeDefinitionNode( string $operator, ContextContract $context, ): ListTypeNode|NamedTypeNode|NonNullTypeNode|null { - $type = null; $definition = $context->get(HandlerContextImplicit::class)?->value - ? $manipulator->getPlaceholderTypeDefinitionNode($argument->getField()) + ? $manipulator->getOriginTypeDefinition($argument->getField()) : $argument->getTypeDefinition(); - - if ($definition) { - $operator = $manipulator->getOperator(static::getScope(), $operator); - $node = $manipulator->getTypeSource($definition); - $type = $operator->getFieldType($manipulator, $node, $context); - $type = Parser::typeReference($type); - } + $operator = $manipulator->getOperator(static::getScope(), $operator); + $node = $manipulator->getTypeSource($definition); + $type = $operator->getFieldType($manipulator, $node, $context); + $type = Parser::typeReference($type); return $type; } diff --git a/packages/graphql/src/Builder/Manipulator.php b/packages/graphql/src/Builder/Manipulator.php index 2b5ff71e6..11a3cda9f 100644 --- a/packages/graphql/src/Builder/Manipulator.php +++ b/packages/graphql/src/Builder/Manipulator.php @@ -3,25 +3,21 @@ namespace LastDragon_ru\LaraASP\GraphQL\Builder; use GraphQL\Language\AST\DirectiveNode; -use GraphQL\Language\AST\FieldDefinitionNode; use GraphQL\Language\AST\InputObjectTypeDefinitionNode; use GraphQL\Language\AST\InterfaceTypeDefinitionNode; use GraphQL\Language\AST\ListTypeNode; use GraphQL\Language\AST\NamedTypeNode; -use GraphQL\Language\AST\Node; use GraphQL\Language\AST\NonNullTypeNode; use GraphQL\Language\AST\ObjectTypeDefinitionNode; use GraphQL\Language\AST\TypeDefinitionNode; use GraphQL\Language\BlockString; use GraphQL\Language\Parser; use GraphQL\Language\Printer; -use GraphQL\Type\Definition\FieldDefinition; use GraphQL\Type\Definition\InputObjectType; use GraphQL\Type\Definition\InterfaceType; use GraphQL\Type\Definition\ObjectType; use GraphQL\Type\Definition\Type; use Illuminate\Container\Container; -use Illuminate\Support\Str; use LastDragon_ru\LaraASP\GraphQL\Builder\Context\HandlerContextBuilderInfo; use LastDragon_ru\LaraASP\GraphQL\Builder\Contracts\Context; use LastDragon_ru\LaraASP\GraphQL\Builder\Contracts\Operator; @@ -37,14 +33,8 @@ use LastDragon_ru\LaraASP\GraphQL\Builder\Sources\InterfaceSource; use LastDragon_ru\LaraASP\GraphQL\Builder\Sources\ObjectSource; use LastDragon_ru\LaraASP\GraphQL\Builder\Sources\Source; -use LastDragon_ru\LaraASP\GraphQL\Stream\Directives\Directive as StreamDirective; use LastDragon_ru\LaraASP\GraphQL\Utils\AstManipulator; -use LastDragon_ru\LaraASP\GraphQL\Utils\PaginateDirectiveHelper; -use LastDragon_ru\LaraASP\GraphQL\Utils\RelationDirectiveHelper; -use Nuwave\Lighthouse\Pagination\PaginateDirective; use Nuwave\Lighthouse\Schema\DirectiveLocator; -use Nuwave\Lighthouse\Schema\Directives\RelationDirective; -use Nuwave\Lighthouse\Support\Contracts\Directive; use Override; use function array_map; @@ -53,8 +43,6 @@ use function array_values; use function count; use function implode; -use function mb_strlen; -use function mb_substr; class Manipulator extends AstManipulator implements TypeProvider { /** @@ -308,54 +296,5 @@ protected function removeFakeTypeDefinition(string $name): void { // Remove $this->removeTypeDefinition($name); } - - /** - * @return (TypeDefinitionNode&Node)|Type|null - */ - public function getPlaceholderTypeDefinitionNode( - FieldDefinitionNode|FieldDefinition $field, - ): TypeDefinitionNode|Type|null { - $node = $this->getTypeDefinition($field); - $name = $this->getTypeName($node); - $directives = $this->getDirectives($field, null, static function (Directive $directive): bool { - return $directive instanceof StreamDirective - || $directive instanceof PaginateDirective - || $directive instanceof RelationDirective; - }); - - foreach ($directives as $directive) { - $type = null; - - if ($directive instanceof StreamDirective) { - $type = Str::singular(mb_substr($name, 0, -mb_strlen(StreamDirective::Name))); - } elseif ($directive instanceof PaginateDirective || $directive instanceof RelationDirective) { - $pagination = $directive instanceof PaginateDirective - ? PaginateDirectiveHelper::getPaginationType($directive) - : RelationDirectiveHelper::getPaginationType($directive); - - if ($pagination) { - if ($pagination->isPaginator()) { - $type = mb_substr($name, 0, -mb_strlen('Paginator')); - } elseif ($pagination->isSimple()) { - $type = mb_substr($name, 0, -mb_strlen('SimplePaginator')); - } elseif ($pagination->isConnection()) { - $type = mb_substr($name, 0, -mb_strlen('Connection')); - } else { - // empty - } - } - } else { - // empty - } - - if ($type) { - $node = $this->getTypeDefinition($type); - - break; - } - } - - return $node; - } // } diff --git a/packages/graphql/src/Builder/ManipulatorTest.php b/packages/graphql/src/Builder/ManipulatorTest.php index 554aca9d0..6efb9437d 100644 --- a/packages/graphql/src/Builder/ManipulatorTest.php +++ b/packages/graphql/src/Builder/ManipulatorTest.php @@ -3,7 +3,6 @@ namespace LastDragon_ru\LaraASP\GraphQL\Builder; use GraphQL\Type\Definition\CustomScalarType; -use GraphQL\Type\Definition\ObjectType; use Illuminate\Container\Container; use LastDragon_ru\LaraASP\GraphQL\Builder\Context\HandlerContextBuilderInfo; use LastDragon_ru\LaraASP\GraphQL\Builder\Contracts\Context as ContextContract; @@ -15,11 +14,8 @@ use LastDragon_ru\LaraASP\GraphQL\Builder\Directives\OperatorDirective; use LastDragon_ru\LaraASP\GraphQL\Builder\Directives\OperatorsDirective; use LastDragon_ru\LaraASP\GraphQL\Testing\Package\TestCase; -use Mockery; use Nuwave\Lighthouse\Execution\Arguments\Argument; -use Nuwave\Lighthouse\Pagination\PaginationServiceProvider; use Nuwave\Lighthouse\Schema\AST\ASTBuilder; -use Nuwave\Lighthouse\Schema\AST\DocumentAST; use Nuwave\Lighthouse\Schema\DirectiveLocator; use Nuwave\Lighthouse\Schema\TypeRegistry; use Override; @@ -27,7 +23,6 @@ use stdClass; use function array_map; -use function array_merge; use function is_a; /** @@ -35,72 +30,8 @@ */ #[CoversClass(Manipulator::class)] final class ManipulatorTest extends TestCase { - // - // ========================================================================= - /** - * @inheritDoc - */ - #[Override] - protected function getPackageProviders(mixed $app): array { - return array_merge(parent::getPackageProviders($app), [ - PaginationServiceProvider::class, - ]); - } - // - // // ========================================================================= - /** - * @dataProvider dataProviderGetPlaceholderTypeDefinitionNode - */ - public function testGetPlaceholderTypeDefinitionNode(?string $expected, string $graphql): void { - $ast = Mockery::mock(DocumentAST::class); - $types = Container::getInstance()->make(TypeRegistry::class); - $directives = Container::getInstance()->make(DirectiveLocator::class); - $manipulator = new class($directives, $types, $ast) extends Manipulator { - /** @noinspection PhpMissingParentConstructorInspection */ - public function __construct( - protected DirectiveLocator $directiveLocator, - protected TypeRegistry $types, - protected DocumentAST $document, - ) { - // empty - } - - #[Override] - protected function getDirectiveLocator(): DirectiveLocator { - return $this->directiveLocator; - } - - #[Override] - public function getDocument(): DocumentAST { - return $this->document; - } - - #[Override] - protected function getTypes(): TypeRegistry { - return $this->types; - } - }; - - $schema = $this->useGraphQLSchema($graphql)->getGraphQLSchema(); - $query = $schema->getType('Query'); - $field = $query instanceof ObjectType - ? $query->getField('field')->astNode - : null; - - self::assertNotNull($field); - - $type = $manipulator->getPlaceholderTypeDefinitionNode($field); - - if ($expected !== null) { - self::assertNotNull($type); - self::assertEquals($expected, $manipulator->getName($type)); - } else { - self::assertNull($type); - } - } - public function testGetTypeOperators(): void { // Operators $scope = new class() implements Scope { @@ -252,150 +183,6 @@ public function getScope(): string { ); } // - - // - // ========================================================================= - /** - * @return array - */ - public static function dataProviderGetPlaceholderTypeDefinitionNode(): array { - return [ - 'field nullable' => [ - 'Test', - <<<'GRAPHQL' - type Query { - field: Test @mock - } - - type Test { - field: Int - } - GRAPHQL, - ], - 'field not null' => [ - 'Test', - <<<'GRAPHQL' - type Query { - field: Test! @mock - } - - type Test { - field: Int - } - GRAPHQL, - ], - 'list' => [ - 'Test', - <<<'GRAPHQL' - type Query { - field: [Test] @mock - } - - type Test { - field: Int - } - GRAPHQL, - ], - '@paginate(type: PAGINATOR)' => [ - 'Test', - <<<'GRAPHQL' - type Query { - field: [Test!] - @paginate( - model: "\\LastDragon_ru\\LaraASP\\GraphQL\\Testing\\Package\\Data\\Models\\TestObject" - type: PAGINATOR - ) - } - - type Test { - field: Int - } - GRAPHQL, - ], - '@paginate(type: SIMPLE)' => [ - 'Test', - <<<'GRAPHQL' - type Query { - field: [Test!] - @paginate( - model: "\\LastDragon_ru\\LaraASP\\GraphQL\\Testing\\Package\\Data\\Models\\TestObject" - type: SIMPLE - ) - } - - type Test { - field: Int - } - GRAPHQL, - ], - '@paginate(type: CONNECTION)' => [ - 'Test', - <<<'GRAPHQL' - type Query { - field: [Test!] - @paginate( - model: "\\LastDragon_ru\\LaraASP\\GraphQL\\Testing\\Package\\Data\\Models\\TestObject" - type: CONNECTION - ) - } - - type Test { - field: Int - } - GRAPHQL, - ], - '@hasOne' => [ - 'Test', - <<<'GRAPHQL' - type Query { - field: [Test!] - @hasOne( - model: "\\LastDragon_ru\\LaraASP\\GraphQL\\Testing\\Package\\Data\\Models\\TestObject" - ) - } - - type Test { - field: Int - } - GRAPHQL, - ], - '@hasMany(type: PAGINATOR)' => [ - 'Test', - <<<'GRAPHQL' - type Query { - field: [Test!] - @hasMany( - model: "\\LastDragon_ru\\LaraASP\\GraphQL\\Testing\\Package\\Data\\Models\\TestObject" - type: PAGINATOR - ) - } - - type Test { - field: Int - } - GRAPHQL, - ], - '@stream' => [ - 'Test', - <<<'GRAPHQL' - type Query { - field: [Test!] - @stream( - key: "id" - builder: { - model: "\\LastDragon_ru\\LaraASP\\GraphQL\\Testing\\Package\\Data\\Models\\TestObject" - } - ) - } - - type Test { - field: Int - } - GRAPHQL, - ], - ]; - } - // } // @phpcs:disable PSR1.Classes.ClassDeclaration.MultipleClasses diff --git a/packages/graphql/src/Utils/AstManipulator.php b/packages/graphql/src/Utils/AstManipulator.php index 05b368815..fd97a74ce 100644 --- a/packages/graphql/src/Utils/AstManipulator.php +++ b/packages/graphql/src/Utils/AstManipulator.php @@ -41,17 +41,21 @@ use GraphQL\Type\Definition\Type; use GraphQL\Type\Definition\UnionType; use GraphQL\Type\Definition\WrappingType; +use Illuminate\Support\Str; use LastDragon_ru\LaraASP\GraphQL\Exceptions\ArgumentAlreadyDefined; use LastDragon_ru\LaraASP\GraphQL\Exceptions\NotImplemented; use LastDragon_ru\LaraASP\GraphQL\Exceptions\TypeDefinitionAlreadyDefined; use LastDragon_ru\LaraASP\GraphQL\Exceptions\TypeDefinitionUnknown; use LastDragon_ru\LaraASP\GraphQL\Exceptions\TypeUnexpected; +use LastDragon_ru\LaraASP\GraphQL\Stream\Directives\Directive as StreamDirective; use LastDragon_ru\LaraASP\GraphQL\Utils\Definitions\LaraAspAsEnumDirective; +use Nuwave\Lighthouse\Pagination\PaginateDirective; use Nuwave\Lighthouse\Schema\AST\ASTHelper; use Nuwave\Lighthouse\Schema\AST\DocumentAST; use Nuwave\Lighthouse\Schema\DirectiveLocator; use Nuwave\Lighthouse\Schema\Directives\BaseDirective; use Nuwave\Lighthouse\Schema\Directives\DeprecatedDirective; +use Nuwave\Lighthouse\Schema\Directives\RelationDirective; use Nuwave\Lighthouse\Schema\TypeRegistry; use Nuwave\Lighthouse\Support\Contracts\Directive; @@ -59,6 +63,8 @@ use function assert; use function is_string; use function json_encode; +use function mb_strlen; +use function mb_substr; use function sprintf; use function trim; @@ -289,6 +295,55 @@ public function removeTypeDefinition(string $name): void { unset($this->getDocument()->types[$name]); } + /** + * @return (TypeDefinitionNode&Node)|Type + */ + public function getOriginTypeDefinition( + FieldDefinitionNode|FieldDefinition|InputValueDefinitionNode|InputObjectField $field, + ): TypeDefinitionNode|Type { + $node = $this->getTypeDefinition($field); + $name = $this->getTypeName($node); + $directives = $this->getDirectives($field, null, static function (Directive $directive): bool { + return $directive instanceof StreamDirective + || $directive instanceof PaginateDirective + || $directive instanceof RelationDirective; + }); + + foreach ($directives as $directive) { + $type = null; + + if ($directive instanceof StreamDirective) { + $type = Str::singular(mb_substr($name, 0, -mb_strlen(StreamDirective::Name))); + } elseif ($directive instanceof PaginateDirective || $directive instanceof RelationDirective) { + $pagination = $directive instanceof PaginateDirective + ? PaginateDirectiveHelper::getPaginationType($directive) + : RelationDirectiveHelper::getPaginationType($directive); + + if ($pagination) { + if ($pagination->isPaginator()) { + $type = mb_substr($name, 0, -mb_strlen('Paginator')); + } elseif ($pagination->isSimple()) { + $type = mb_substr($name, 0, -mb_strlen('SimplePaginator')); + } elseif ($pagination->isConnection()) { + $type = mb_substr($name, 0, -mb_strlen('Connection')); + } else { + // empty + } + } + } else { + // empty + } + + if ($type) { + $node = $this->getTypeDefinition($type); + + break; + } + } + + return $node; + } + /** * @template T * diff --git a/packages/graphql/src/Utils/AstManipulatorTest.php b/packages/graphql/src/Utils/AstManipulatorTest.php index 89c051f7d..dd5e33705 100644 --- a/packages/graphql/src/Utils/AstManipulatorTest.php +++ b/packages/graphql/src/Utils/AstManipulatorTest.php @@ -28,6 +28,8 @@ use LastDragon_ru\LaraASP\GraphQL\Exceptions\ArgumentAlreadyDefined; use LastDragon_ru\LaraASP\GraphQL\Exceptions\NotImplemented; use LastDragon_ru\LaraASP\GraphQL\Testing\Package\TestCase; +use Mockery; +use Nuwave\Lighthouse\Pagination\PaginationServiceProvider; use Nuwave\Lighthouse\Schema\AST\ASTBuilder; use Nuwave\Lighthouse\Schema\AST\DocumentAST; use Nuwave\Lighthouse\Schema\DirectiveLocator; @@ -41,6 +43,7 @@ use function array_keys; use function array_map; +use function array_merge; use function assert; use function is_string; @@ -51,6 +54,19 @@ */ #[CoversClass(AstManipulator::class)] final class AstManipulatorTest extends TestCase { + // + // ========================================================================= + /** + * @inheritDoc + */ + #[Override] + protected function getPackageProviders(mixed $app): array { + return array_merge(parent::getPackageProviders($app), [ + PaginationServiceProvider::class, + ]); + } + // + // // ========================================================================= public function testGetInterfaces(): void { @@ -557,6 +573,27 @@ static function (mixed $field) use ($manipulator): bool { self::assertEquals('c', $typeField->name); } + /** + * @dataProvider dataProviderGetOriginTypeDefinition + */ + public function testGetOriginTypeDefinition(string $expected, string $graphql): void { + $ast = Mockery::mock(DocumentAST::class); + $types = Container::getInstance()->make(TypeRegistry::class); + $directives = Container::getInstance()->make(DirectiveLocator::class); + $manipulator = new AstManipulator($directives, $ast, $types); + + $schema = $this->useGraphQLSchema($graphql)->getGraphQLSchema(); + $query = $schema->getType('Query'); + $field = $query instanceof ObjectType + ? $query->getField('field')->astNode + : null; + + self::assertNotNull($field); + + $type = $manipulator->getOriginTypeDefinition($field); + + self::assertEquals($expected, $manipulator->getName($type)); + } // // @@ -1169,6 +1206,147 @@ interface InterfaceC { ], ]; } + + /** + * @return array + */ + public static function dataProviderGetOriginTypeDefinition(): array { + return [ + 'field nullable' => [ + 'Test', + <<<'GRAPHQL' + type Query { + field: Test @mock + } + + type Test { + field: Int + } + GRAPHQL, + ], + 'field not null' => [ + 'Test', + <<<'GRAPHQL' + type Query { + field: Test! @mock + } + + type Test { + field: Int + } + GRAPHQL, + ], + 'list' => [ + 'Test', + <<<'GRAPHQL' + type Query { + field: [Test] @mock + } + + type Test { + field: Int + } + GRAPHQL, + ], + '@paginate(type: PAGINATOR)' => [ + 'Test', + <<<'GRAPHQL' + type Query { + field: [Test!] + @paginate( + model: "\\LastDragon_ru\\LaraASP\\GraphQL\\Testing\\Package\\Data\\Models\\TestObject" + type: PAGINATOR + ) + } + + type Test { + field: Int + } + GRAPHQL, + ], + '@paginate(type: SIMPLE)' => [ + 'Test', + <<<'GRAPHQL' + type Query { + field: [Test!] + @paginate( + model: "\\LastDragon_ru\\LaraASP\\GraphQL\\Testing\\Package\\Data\\Models\\TestObject" + type: SIMPLE + ) + } + + type Test { + field: Int + } + GRAPHQL, + ], + '@paginate(type: CONNECTION)' => [ + 'Test', + <<<'GRAPHQL' + type Query { + field: [Test!] + @paginate( + model: "\\LastDragon_ru\\LaraASP\\GraphQL\\Testing\\Package\\Data\\Models\\TestObject" + type: CONNECTION + ) + } + + type Test { + field: Int + } + GRAPHQL, + ], + '@hasOne' => [ + 'Test', + <<<'GRAPHQL' + type Query { + field: [Test!] + @hasOne( + model: "\\LastDragon_ru\\LaraASP\\GraphQL\\Testing\\Package\\Data\\Models\\TestObject" + ) + } + + type Test { + field: Int + } + GRAPHQL, + ], + '@hasMany(type: PAGINATOR)' => [ + 'Test', + <<<'GRAPHQL' + type Query { + field: [Test!] + @hasMany( + model: "\\LastDragon_ru\\LaraASP\\GraphQL\\Testing\\Package\\Data\\Models\\TestObject" + type: PAGINATOR + ) + } + + type Test { + field: Int + } + GRAPHQL, + ], + '@stream' => [ + 'Test', + <<<'GRAPHQL' + type Query { + field: [Test!] + @stream( + key: "id" + builder: { + model: "\\LastDragon_ru\\LaraASP\\GraphQL\\Testing\\Package\\Data\\Models\\TestObject" + } + ) + } + + type Test { + field: Int + } + GRAPHQL, + ], + ]; + } // } From 2f29617179a30f53fe8852de7a43a0da4323a2da Mon Sep 17 00:00:00 2001 From: Aleksei Lebedev <1329824+LastDragon-ru@users.noreply.github.com> Date: Sat, 27 Jan 2024 11:09:13 +0400 Subject: [PATCH 19/26] refactor(graphql)!: `AstManipulator::getOriginTypeDefinition()` => (more useful) `AstManipulator::getOriginType()`. --- packages/graphql/UPGRADE.md | 2 +- .../Builder/Directives/HandlerDirective.php | 2 +- packages/graphql/src/Utils/AstManipulator.php | 67 +++++++++---------- .../graphql/src/Utils/AstManipulatorTest.php | 28 ++++---- 4 files changed, 49 insertions(+), 50 deletions(-) diff --git a/packages/graphql/UPGRADE.md b/packages/graphql/UPGRADE.md index 50e04f7ec..ea6ac34ae 100644 --- a/packages/graphql/UPGRADE.md +++ b/packages/graphql/UPGRADE.md @@ -88,7 +88,7 @@ This section is actual only if you are extending the package. Please review and $context->get(LastDragon_ru\LaraASP\GraphQL\Builder\Context\HandlerContextBuilderInfo::class)?->value ``` - * [ ] Removed `getPlaceholderTypeDefinitionNode()` => `LastDragon_ru\LaraASP\GraphQL\Utils\AstManipulator::getOriginTypeDefinition()` + * [ ] Removed `getPlaceholderTypeDefinitionNode()` => `LastDragon_ru\LaraASP\GraphQL\Utils\AstManipulator::getOriginType()` * [ ] `LastDragon_ru\LaraASP\GraphQL\Builder\Directives\HandlerDirective` diff --git a/packages/graphql/src/Builder/Directives/HandlerDirective.php b/packages/graphql/src/Builder/Directives/HandlerDirective.php index 47f4de7ff..f1f0d9f70 100644 --- a/packages/graphql/src/Builder/Directives/HandlerDirective.php +++ b/packages/graphql/src/Builder/Directives/HandlerDirective.php @@ -264,7 +264,7 @@ protected function getArgumentTypeDefinitionNode( ContextContract $context, ): ListTypeNode|NamedTypeNode|NonNullTypeNode|null { $definition = $context->get(HandlerContextImplicit::class)?->value - ? $manipulator->getOriginTypeDefinition($argument->getField()) + ? $manipulator->getTypeDefinition($manipulator->getOriginType($argument->getField())) : $argument->getTypeDefinition(); $operator = $manipulator->getOperator(static::getScope(), $operator); $node = $manipulator->getTypeSource($definition); diff --git a/packages/graphql/src/Utils/AstManipulator.php b/packages/graphql/src/Utils/AstManipulator.php index fd97a74ce..b68667403 100644 --- a/packages/graphql/src/Utils/AstManipulator.php +++ b/packages/graphql/src/Utils/AstManipulator.php @@ -296,52 +296,49 @@ public function removeTypeDefinition(string $name): void { } /** - * @return (TypeDefinitionNode&Node)|Type + * @return (TypeNode&Node)|Type */ - public function getOriginTypeDefinition( + public function getOriginType( FieldDefinitionNode|FieldDefinition|InputValueDefinitionNode|InputObjectField $field, - ): TypeDefinitionNode|Type { - $node = $this->getTypeDefinition($field); - $name = $this->getTypeName($node); - $directives = $this->getDirectives($field, null, static function (Directive $directive): bool { + ): TypeNode|Type { + $directive = $this->getDirective($field, Directive::class, static function (Directive $directive): bool { return $directive instanceof StreamDirective || $directive instanceof PaginateDirective || $directive instanceof RelationDirective; }); - - foreach ($directives as $directive) { - $type = null; - - if ($directive instanceof StreamDirective) { - $type = Str::singular(mb_substr($name, 0, -mb_strlen(StreamDirective::Name))); - } elseif ($directive instanceof PaginateDirective || $directive instanceof RelationDirective) { - $pagination = $directive instanceof PaginateDirective - ? PaginateDirectiveHelper::getPaginationType($directive) - : RelationDirectiveHelper::getPaginationType($directive); - - if ($pagination) { - if ($pagination->isPaginator()) { - $type = mb_substr($name, 0, -mb_strlen('Paginator')); - } elseif ($pagination->isSimple()) { - $type = mb_substr($name, 0, -mb_strlen('SimplePaginator')); - } elseif ($pagination->isConnection()) { - $type = mb_substr($name, 0, -mb_strlen('Connection')); - } else { - // empty - } + $origin = $field instanceof FieldDefinition || $field instanceof InputObjectField + ? $field->getType() + : $field->type; + $name = $this->getTypeName($origin); + $type = null; + + if ($directive instanceof StreamDirective) { + $type = Str::singular(mb_substr($name, 0, -mb_strlen(StreamDirective::Name))); + } elseif ($directive instanceof PaginateDirective || $directive instanceof RelationDirective) { + $pagination = $directive instanceof PaginateDirective + ? PaginateDirectiveHelper::getPaginationType($directive) + : RelationDirectiveHelper::getPaginationType($directive); + + if ($pagination) { + if ($pagination->isPaginator()) { + $type = mb_substr($name, 0, -mb_strlen('Paginator')); + } elseif ($pagination->isSimple()) { + $type = mb_substr($name, 0, -mb_strlen('SimplePaginator')); + } elseif ($pagination->isConnection()) { + $type = mb_substr($name, 0, -mb_strlen('Connection')); + } else { + // empty } - } else { - // empty } + } else { + // empty + } - if ($type) { - $node = $this->getTypeDefinition($type); - - break; - } + if ($type) { + $origin = Parser::typeReference("[{$type}!]!"); } - return $node; + return $origin; } /** diff --git a/packages/graphql/src/Utils/AstManipulatorTest.php b/packages/graphql/src/Utils/AstManipulatorTest.php index dd5e33705..fde617e5b 100644 --- a/packages/graphql/src/Utils/AstManipulatorTest.php +++ b/packages/graphql/src/Utils/AstManipulatorTest.php @@ -574,9 +574,9 @@ static function (mixed $field) use ($manipulator): bool { } /** - * @dataProvider dataProviderGetOriginTypeDefinition + * @dataProvider dataProviderGetOriginType */ - public function testGetOriginTypeDefinition(string $expected, string $graphql): void { + public function testGetOriginType(string $expected, string $graphql): void { $ast = Mockery::mock(DocumentAST::class); $types = Container::getInstance()->make(TypeRegistry::class); $directives = Container::getInstance()->make(DirectiveLocator::class); @@ -590,9 +590,9 @@ public function testGetOriginTypeDefinition(string $expected, string $graphql): self::assertNotNull($field); - $type = $manipulator->getOriginTypeDefinition($field); + $type = $manipulator->getOriginType($field); - self::assertEquals($expected, $manipulator->getName($type)); + self::assertGraphQLPrintableEquals($expected, $type); } // @@ -1210,7 +1210,7 @@ interface InterfaceC { /** * @return array */ - public static function dataProviderGetOriginTypeDefinition(): array { + public static function dataProviderGetOriginType(): array { return [ 'field nullable' => [ 'Test', @@ -1225,7 +1225,7 @@ public static function dataProviderGetOriginTypeDefinition(): array { GRAPHQL, ], 'field not null' => [ - 'Test', + 'Test!', <<<'GRAPHQL' type Query { field: Test! @mock @@ -1237,7 +1237,7 @@ public static function dataProviderGetOriginTypeDefinition(): array { GRAPHQL, ], 'list' => [ - 'Test', + '[Test]', <<<'GRAPHQL' type Query { field: [Test] @mock @@ -1249,7 +1249,7 @@ public static function dataProviderGetOriginTypeDefinition(): array { GRAPHQL, ], '@paginate(type: PAGINATOR)' => [ - 'Test', + '[Test!]!', <<<'GRAPHQL' type Query { field: [Test!] @@ -1265,7 +1265,7 @@ public static function dataProviderGetOriginTypeDefinition(): array { GRAPHQL, ], '@paginate(type: SIMPLE)' => [ - 'Test', + '[Test!]!', <<<'GRAPHQL' type Query { field: [Test!] @@ -1281,7 +1281,7 @@ public static function dataProviderGetOriginTypeDefinition(): array { GRAPHQL, ], '@paginate(type: CONNECTION)' => [ - 'Test', + '[Test!]!', <<<'GRAPHQL' type Query { field: [Test!] @@ -1297,7 +1297,7 @@ public static function dataProviderGetOriginTypeDefinition(): array { GRAPHQL, ], '@hasOne' => [ - 'Test', + '[Test!]', <<<'GRAPHQL' type Query { field: [Test!] @@ -1312,7 +1312,7 @@ public static function dataProviderGetOriginTypeDefinition(): array { GRAPHQL, ], '@hasMany(type: PAGINATOR)' => [ - 'Test', + '[Test!]!', <<<'GRAPHQL' type Query { field: [Test!] @@ -1328,12 +1328,14 @@ public static function dataProviderGetOriginTypeDefinition(): array { GRAPHQL, ], '@stream' => [ - 'Test', + '[Test!]!', <<<'GRAPHQL' type Query { field: [Test!] @stream( key: "id" + searchable: false + sortable: false builder: { model: "\\LastDragon_ru\\LaraASP\\GraphQL\\Testing\\Package\\Data\\Models\\TestObject" } From 2682e5b14481523d9ef4c2dd1b22548a3ada2ed1 Mon Sep 17 00:00:00 2001 From: Aleksei Lebedev <1329824+LastDragon-ru@users.noreply.github.com> Date: Sat, 27 Jan 2024 11:47:00 +0400 Subject: [PATCH 20/26] refactor(graphql/`@sortBy`)!: `EloquentSorter` code simplification (removed unnecessary check that relation supported). --- .../SortBy/Exceptions/RelationUnsupported.php | 36 +++------ .../src/SortBy/Sorters/EloquentSorter.php | 67 ++++------------- .../src/SortBy/Sorters/EloquentSorterTest.php | 26 ------- .../Package/Models/Relations/Unsupported.php | 73 ------------------- .../src/Testing/Package/Models/User.php | 8 -- phpstan-baseline.neon | 10 --- 6 files changed, 24 insertions(+), 196 deletions(-) delete mode 100644 packages/graphql/src/Testing/Package/Models/Relations/Unsupported.php diff --git a/packages/graphql/src/SortBy/Exceptions/RelationUnsupported.php b/packages/graphql/src/SortBy/Exceptions/RelationUnsupported.php index 752ffe119..d5d29d21a 100644 --- a/packages/graphql/src/SortBy/Exceptions/RelationUnsupported.php +++ b/packages/graphql/src/SortBy/Exceptions/RelationUnsupported.php @@ -6,43 +6,29 @@ use Illuminate\Database\Eloquent\Relations\Relation; use Throwable; -use function implode; use function sprintf; class RelationUnsupported extends SortByException { /** - * @param class-string> $relationClass - * @param list>> $supported + * @param class-string> $class */ public function __construct( - protected string $relationName, - protected string $relationClass, - protected array $supported, + protected string $class, Throwable $previous = null, ) { - parent::__construct(sprintf( - 'Relation `%s` of type `%s` cannot be used for sort, only `%s` supported.', - $this->relationName, - $this->relationClass, - implode('`, `', $this->supported), - ), $previous); - } - - public function getRelationName(): string { - return $this->relationName; + parent::__construct( + sprintf( + 'Relation `%s` cannot be used for sort.', + $this->class, + ), + $previous, + ); } /** * @return class-string> */ - public function getRelationClass(): string { - return $this->relationClass; - } - - /** - * @return list>> - */ - public function getSupported(): array { - return $this->supported; + public function getClass(): string { + return $this->class; } } diff --git a/packages/graphql/src/SortBy/Sorters/EloquentSorter.php b/packages/graphql/src/SortBy/Sorters/EloquentSorter.php index b7072af61..89a5b8c24 100644 --- a/packages/graphql/src/SortBy/Sorters/EloquentSorter.php +++ b/packages/graphql/src/SortBy/Sorters/EloquentSorter.php @@ -6,14 +6,9 @@ use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\BelongsTo; use Illuminate\Database\Eloquent\Relations\BelongsToMany; -use Illuminate\Database\Eloquent\Relations\HasMany; use Illuminate\Database\Eloquent\Relations\HasManyThrough; -use Illuminate\Database\Eloquent\Relations\HasOne; -use Illuminate\Database\Eloquent\Relations\HasOneThrough; -use Illuminate\Database\Eloquent\Relations\MorphMany; -use Illuminate\Database\Eloquent\Relations\MorphOne; +use Illuminate\Database\Eloquent\Relations\HasOneOrMany; use Illuminate\Database\Eloquent\Relations\MorphOneOrMany; -use Illuminate\Database\Eloquent\Relations\MorphToMany; use Illuminate\Database\Eloquent\Relations\Relation; use Illuminate\Database\Query\JoinClause; use LastDragon_ru\LaraASP\Eloquent\ModelHelper; @@ -21,32 +16,14 @@ use LastDragon_ru\LaraASP\GraphQL\SortBy\Enums\Direction; use LastDragon_ru\LaraASP\GraphQL\SortBy\Enums\Nulls; use LastDragon_ru\LaraASP\GraphQL\SortBy\Exceptions\RelationUnsupported; -use LogicException; use Override; use function array_shift; -use function implode; -use function is_a; /** * @extends DatabaseSorter> */ class EloquentSorter extends DatabaseSorter { - /** - * @var list>> - */ - protected array $relations = [ - BelongsTo::class, - BelongsToMany::class, - HasOne::class, - HasMany::class, - MorphOne::class, - MorphMany::class, - MorphToMany::class, - HasOneThrough::class, - HasManyThrough::class, - ]; - // // ========================================================================= #[Override] @@ -124,25 +101,7 @@ protected function getRelationColumn( * @return Relation */ protected function getRelation(EloquentBuilder $builder, string $name, array $stack = []): Relation { - $relation = (new ModelHelper($builder))->getRelation($name); - $supported = false; - - foreach ($this->relations as $class) { - if (is_a($relation, $class)) { - $supported = true; - break; - } - } - - if (!$supported) { - throw new RelationUnsupported( - implode('.', [...$stack, $name]), - $relation::class, - $this->relations, - ); - } - - return $relation; + return (new ModelHelper($builder))->getRelation($name); } /** @@ -169,16 +128,6 @@ protected function joinRelation( ? "{$parentAlias}.{$relation->getForeignKeyName()}" : $relation->getQualifiedForeignKeyName(), ); - } elseif ($relation instanceof HasOne || $relation instanceof HasMany) { - $builder->joinSub( - $relation->getQuery(), - $currentAlias, - "{$currentAlias}.{$relation->getForeignKeyName()}", - '=', - $parentAlias - ? "{$parentAlias}.{$relation->getLocalKeyName()}" - : $relation->getQualifiedParentKeyName(), - ); } elseif ($relation instanceof MorphOneOrMany) { $builder->joinSub( $relation->getQuery(), @@ -198,6 +147,16 @@ static function (JoinClause $join) use ($relation, $currentAlias, $parentAlias): ); }, ); + } elseif ($relation instanceof HasOneOrMany) { + $builder->joinSub( + $relation->getQuery(), + $currentAlias, + "{$currentAlias}.{$relation->getForeignKeyName()}", + '=', + $parentAlias + ? "{$parentAlias}.{$relation->getLocalKeyName()}" + : $relation->getQualifiedParentKeyName(), + ); } elseif ($relation instanceof HasManyThrough) { $builder->joinSub( $relation->getQuery()->select([ @@ -222,7 +181,7 @@ static function (JoinClause $join) use ($relation, $currentAlias, $parentAlias): : $relation->getQualifiedRelatedKeyName(), ); } else { - throw new LogicException('O_o => Please contact to developer.'); + throw new RelationUnsupported($relation::class); } return $builder; diff --git a/packages/graphql/src/SortBy/Sorters/EloquentSorterTest.php b/packages/graphql/src/SortBy/Sorters/EloquentSorterTest.php index 44099ed9d..04238d7dc 100644 --- a/packages/graphql/src/SortBy/Sorters/EloquentSorterTest.php +++ b/packages/graphql/src/SortBy/Sorters/EloquentSorterTest.php @@ -21,11 +21,9 @@ use LastDragon_ru\LaraASP\GraphQL\Builder\Property; use LastDragon_ru\LaraASP\GraphQL\SortBy\Enums\Direction; use LastDragon_ru\LaraASP\GraphQL\SortBy\Enums\Nulls; -use LastDragon_ru\LaraASP\GraphQL\SortBy\Exceptions\RelationUnsupported; use LastDragon_ru\LaraASP\GraphQL\Testing\Package\DataProviders\EloquentBuilderDataProvider; use LastDragon_ru\LaraASP\GraphQL\Testing\Package\Models\Car; use LastDragon_ru\LaraASP\GraphQL\Testing\Package\Models\CarEngine; -use LastDragon_ru\LaraASP\GraphQL\Testing\Package\Models\Relations\Unsupported; use LastDragon_ru\LaraASP\GraphQL\Testing\Package\Models\Role; use LastDragon_ru\LaraASP\GraphQL\Testing\Package\Models\User; use LastDragon_ru\LaraASP\GraphQL\Testing\Package\TestCase; @@ -121,30 +119,6 @@ static function (): EloquentBuilder { null, null, ], - 'unsupported' => [ - new RelationUnsupported( - 'unsupported', - Unsupported::class, - [ - BelongsTo::class, - BelongsToMany::class, - HasOne::class, - HasMany::class, - MorphOne::class, - MorphMany::class, - MorphToMany::class, - HasOneThrough::class, - HasManyThrough::class, - ], - ), - static function (): EloquentBuilder { - return User::query(); - }, - new Property('unsupported', 'id'), - Direction::Asc, - null, - null, - ], BelongsTo::class => [ [ 'query' => <<<'SQL' diff --git a/packages/graphql/src/Testing/Package/Models/Relations/Unsupported.php b/packages/graphql/src/Testing/Package/Models/Relations/Unsupported.php deleted file mode 100644 index b5ea3f1e1..000000000 --- a/packages/graphql/src/Testing/Package/Models/Relations/Unsupported.php +++ /dev/null @@ -1,73 +0,0 @@ - - * - * @internal - */ -class Unsupported extends Relation { - public function __construct() { - $model = new class('models') extends Model { - // empty - }; - - parent::__construct( - $model->newQuery(), - $model, - ); - } - - #[Override] - public function addConstraints(): void { - // empty - } - - /** - * @inheritDoc - * - * @param array $models - */ - #[Override] - public function addEagerConstraints(array $models): void { - // empty - } - - /** - * @inheritDoc - * - * @param array $models - * - * @return array - */ - #[Override] - public function initRelation(array $models, $relation): array { - return []; - } - - /** - * @inheritDoc - * - * @param array $models - * @param Collection $results - * - * @return array - */ - #[Override] - public function match(array $models, Collection $results, $relation): array { - return []; - } - - #[Override] - public function getResults(): mixed { - return null; - } -} diff --git a/packages/graphql/src/Testing/Package/Models/User.php b/packages/graphql/src/Testing/Package/Models/User.php index 918539751..905901c2b 100644 --- a/packages/graphql/src/Testing/Package/Models/User.php +++ b/packages/graphql/src/Testing/Package/Models/User.php @@ -12,7 +12,6 @@ use Illuminate\Database\Eloquent\Relations\MorphOne; use Illuminate\Database\Eloquent\Relations\MorphToMany; use LastDragon_ru\LaraASP\GraphQL\Testing\Package\Models\Concerns\Model; -use LastDragon_ru\LaraASP\GraphQL\Testing\Package\Models\Relations\Unsupported; /** * @internal @@ -130,11 +129,4 @@ public function tags(): MorphToMany { 'relatedKey', ); } - - /** - * @return Unsupported - */ - public function unsupported(): Unsupported { - return new Unsupported(); - } } diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index e04f28460..09832cf26 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -25,21 +25,11 @@ parameters: count: 1 path: packages/formatter/src/Formatter.php - - - message: "#^Parameter \\#2 \\$relationClass of class LastDragon_ru\\\\LaraASP\\\\GraphQL\\\\SortBy\\\\Exceptions\\\\RelationUnsupported constructor expects class\\-string\\\\>, 'LastDragon_ru\\\\\\\\LaraASP\\\\\\\\GraphQL\\\\\\\\Testing\\\\\\\\Package\\\\\\\\Models\\\\\\\\Relations\\\\\\\\Unsupported' given\\.$#" - count: 1 - path: packages/graphql/src/SortBy/Sorters/EloquentSorterTest.php - - message: "#^Method LastDragon_ru\\\\LaraASP\\\\GraphQL\\\\Testing\\\\Package\\\\Models\\\\Image\\:\\:imageable\\(\\) should return Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\MorphTo\\ but returns Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\MorphTo\\\\.$#" count: 1 path: packages/graphql/src/Testing/Package/Models/Image.php - - - message: "#^Parameter \\#2 \\$results \\(Illuminate\\\\Database\\\\Eloquent\\\\Collection\\\\) of method LastDragon_ru\\\\LaraASP\\\\GraphQL\\\\Testing\\\\Package\\\\Models\\\\Relations\\\\Unsupported\\:\\:match\\(\\) should be contravariant with parameter \\$results \\(Illuminate\\\\Database\\\\Eloquent\\\\Collection\\) of method Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\Relation\\\\:\\:match\\(\\)$#" - count: 1 - path: packages/graphql/src/Testing/Package/Models/Relations/Unsupported.php - - message: "#^Method LastDragon_ru\\\\LaraASP\\\\GraphQL\\\\Utils\\\\AstManipulator\\:\\:addTypeDefinition\\(\\) should return TDefinition of \\(GraphQL\\\\Language\\\\AST\\\\Node&GraphQL\\\\Language\\\\AST\\\\TypeDefinitionNode\\)\\|\\(GraphQL\\\\Type\\\\Definition\\\\NamedType&GraphQL\\\\Type\\\\Definition\\\\Type\\) but returns GraphQL\\\\Type\\\\Definition\\\\PhpEnumType\\|GraphQL\\\\Type\\\\Definition\\\\ScalarType\\|TDefinition of GraphQL\\\\Language\\\\AST\\\\Node&GraphQL\\\\Language\\\\AST\\\\TypeDefinitionNode\\.$#" count: 1 From 0d279d98a0b20261006ad452eddcc096d7e283c2 Mon Sep 17 00:00:00 2001 From: Aleksei Lebedev <1329824+LastDragon-ru@users.noreply.github.com> Date: Sun, 28 Jan 2024 11:28:56 +0400 Subject: [PATCH 21/26] Any lists allowed for explicit, list of object allowed for implicit (`@sortBy`). --- .../DirectiveTest/Explicit.expected.graphql | 6 +++++ .../DirectiveTest/Ignored.expected.graphql | 6 +++++ .../DirectiveTest/Ignored.schema.graphql | 4 +++ .../DirectiveTest/Implicit.expected.graphql | 26 +++++++++++++++++++ .../DirectiveTest/Implicit.schema.graphql | 2 ++ .../DirectiveTest/Scout.expected.graphql | 18 +++++++++++++ packages/graphql/src/SortBy/Types/Clause.php | 5 ++-- 7 files changed, 65 insertions(+), 2 deletions(-) diff --git a/packages/graphql/src/SortBy/Directives/DirectiveTest/Explicit.expected.graphql b/packages/graphql/src/SortBy/Directives/DirectiveTest/Explicit.expected.graphql index 15edf1284..2a2b1cb3e 100644 --- a/packages/graphql/src/SortBy/Directives/DirectiveTest/Explicit.expected.graphql +++ b/packages/graphql/src/SortBy/Directives/DirectiveTest/Explicit.expected.graphql @@ -79,6 +79,12 @@ input SortByClauseA { field: SortByClauseB @sortByOperatorProperty + """ + Property clause. + """ + fields: SortByClauseB + @sortByOperatorProperty + """ Description should be copied. """ diff --git a/packages/graphql/src/SortBy/Directives/DirectiveTest/Ignored.expected.graphql b/packages/graphql/src/SortBy/Directives/DirectiveTest/Ignored.expected.graphql index 1444533e9..6b9bfcf92 100644 --- a/packages/graphql/src/SortBy/Directives/DirectiveTest/Ignored.expected.graphql +++ b/packages/graphql/src/SortBy/Directives/DirectiveTest/Ignored.expected.graphql @@ -90,6 +90,12 @@ input SortByClauseA { """ a: SortByTypeDirection @sortByOperatorField + + """ + List + """ + b: SortByTypeDirection + @sortByOperatorField } """ diff --git a/packages/graphql/src/SortBy/Directives/DirectiveTest/Ignored.schema.graphql b/packages/graphql/src/SortBy/Directives/DirectiveTest/Ignored.schema.graphql index cb25030fa..ef0f7d9fa 100644 --- a/packages/graphql/src/SortBy/Directives/DirectiveTest/Ignored.schema.graphql +++ b/packages/graphql/src/SortBy/Directives/DirectiveTest/Ignored.schema.graphql @@ -4,12 +4,14 @@ type Query { } input A { + # Should be processed "Not ignored" a: String! "List" b: [String!] + # Should be ignored "Marked by @sortByIgnored" c: IgnoredInput! d: IgnoredDate! @@ -19,9 +21,11 @@ input A { } interface B { + # Should be processed "Not ignored" a: String! + # Should be ignored "List" b: [String!] diff --git a/packages/graphql/src/SortBy/Directives/DirectiveTest/Implicit.expected.graphql b/packages/graphql/src/SortBy/Directives/DirectiveTest/Implicit.expected.graphql index 5e4e96b1d..3a3189609 100644 --- a/packages/graphql/src/SortBy/Directives/DirectiveTest/Implicit.expected.graphql +++ b/packages/graphql/src/SortBy/Directives/DirectiveTest/Implicit.expected.graphql @@ -154,6 +154,18 @@ input SortByClauseA { """ relationWithArgs: SortByClauseA @sortByOperatorProperty + + """ + Property clause. + """ + relations: SortByClauseB + @sortByOperatorProperty + + """ + Property clause. + """ + relationsWithArgs: SortByClauseA + @sortByOperatorProperty } """ @@ -230,6 +242,18 @@ input SortByClauseC { """ relationWithArgs: SortByClauseA @sortByOperatorProperty + + """ + Property clause. + """ + relations: SortByClauseB + @sortByOperatorProperty + + """ + Property clause. + """ + relationsWithArgs: SortByClauseA + @sortByOperatorProperty } """ @@ -261,6 +285,7 @@ interface C { """ id: ID! + list: [String!] name: String! property( @@ -370,6 +395,7 @@ type A { """ id: ID! + list: [String!] name: String! property( diff --git a/packages/graphql/src/SortBy/Directives/DirectiveTest/Implicit.schema.graphql b/packages/graphql/src/SortBy/Directives/DirectiveTest/Implicit.schema.graphql index ddee7dfdf..3a6f31299 100644 --- a/packages/graphql/src/SortBy/Directives/DirectiveTest/Implicit.schema.graphql +++ b/packages/graphql/src/SortBy/Directives/DirectiveTest/Implicit.schema.graphql @@ -17,6 +17,7 @@ type A { relationsWithArgs(arg: String): [A!] @hasMany # Should be ignored + list: [String!] field: B! fields: [B!]! property(arg: String): Int! @@ -49,6 +50,7 @@ interface C { relationsWithArgs(arg: String): [A!] @hasMany # Should be ignored + list: [String!] field: B! fields: [B!]! property(arg: String): Int! diff --git a/packages/graphql/src/SortBy/Directives/DirectiveTest/Scout.expected.graphql b/packages/graphql/src/SortBy/Directives/DirectiveTest/Scout.expected.graphql index 4f2444921..ce0e941b6 100644 --- a/packages/graphql/src/SortBy/Directives/DirectiveTest/Scout.expected.graphql +++ b/packages/graphql/src/SortBy/Directives/DirectiveTest/Scout.expected.graphql @@ -332,6 +332,12 @@ input SortByClauseProperties { customScalar: SortByTypeDirection @sortByOperatorField + """ + Property clause. + """ + customScalarList: SortByTypeDirection + @sortByOperatorField + """ Property clause. """ @@ -424,6 +430,12 @@ input SortByQueryClauseProperties { customScalar: SortByTypeDirection @sortByOperatorField + """ + Property clause. + """ + customScalarList: SortByTypeDirection + @sortByOperatorField + """ Property clause. """ @@ -533,6 +545,12 @@ input SortByScoutClauseProperties { customScalar: SortByTypeDirection @sortByOperatorField + """ + Property clause. + """ + customScalarList: SortByTypeDirection + @sortByOperatorField + """ Property clause. """ diff --git a/packages/graphql/src/SortBy/Types/Clause.php b/packages/graphql/src/SortBy/Types/Clause.php index 69b07f1f1..2c5f16095 100644 --- a/packages/graphql/src/SortBy/Types/Clause.php +++ b/packages/graphql/src/SortBy/Types/Clause.php @@ -10,6 +10,7 @@ use GraphQL\Type\Definition\InterfaceType; use GraphQL\Type\Definition\ObjectType; use LastDragon_ru\LaraASP\GraphQL\Builder\Context\HandlerContextBuilderInfo; +use LastDragon_ru\LaraASP\GraphQL\Builder\Context\HandlerContextImplicit; use LastDragon_ru\LaraASP\GraphQL\Builder\Contracts\Context; use LastDragon_ru\LaraASP\GraphQL\Builder\Contracts\Operator as OperatorContract; use LastDragon_ru\LaraASP\GraphQL\Builder\Contracts\TypeSource; @@ -90,8 +91,8 @@ protected function isFieldConvertable( return false; } - // List? - if ($field->isList()) { + // List of scalars/enums? + if ($context->get(HandlerContextImplicit::class)?->value && $field->isList() && !$field->isObject()) { return false; } From 5dcbba1f8136ca937356fb0dda9c9cb3713dcf77 Mon Sep 17 00:00:00 2001 From: Aleksei Lebedev <1329824+LastDragon-ru@users.noreply.github.com> Date: Sun, 28 Jan 2024 11:39:43 +0400 Subject: [PATCH 22/26] Code cleanup and reorganization. --- .../graphql/src/Builder/Sources/Source.php | 7 +- .../graphql/src/Builder/Types/InputObject.php | 106 ++++++++++++++---- .../graphql/src/SearchBy/Types/Condition.php | 26 +---- packages/graphql/src/SortBy/Types/Clause.php | 26 ++--- 4 files changed, 98 insertions(+), 67 deletions(-) diff --git a/packages/graphql/src/Builder/Sources/Source.php b/packages/graphql/src/Builder/Sources/Source.php index 44342a89a..fabf0f7d5 100644 --- a/packages/graphql/src/Builder/Sources/Source.php +++ b/packages/graphql/src/Builder/Sources/Source.php @@ -61,12 +61,7 @@ public function getTypeName(): string { */ #[Override] public function getTypeDefinition(): TypeDefinitionNode|Type { - $type = $this->getType(); - $definition = !($type instanceof TypeDefinitionNode) - ? $this->getManipulator()->getTypeDefinition($type) - : $type; - - return $definition; + return $this->getManipulator()->getTypeDefinition($this->getType()); } #[Override] diff --git a/packages/graphql/src/Builder/Types/InputObject.php b/packages/graphql/src/Builder/Types/InputObject.php index 654f6d494..174d55480 100644 --- a/packages/graphql/src/Builder/Types/InputObject.php +++ b/packages/graphql/src/Builder/Types/InputObject.php @@ -137,43 +137,72 @@ protected function getOperators( return []; } + /** + * Determines if the field can be converted or not. + * + * Explicit and Implicit (=placeholder) types processing a bit differently, + * see {@see self::isFieldConvertableExplicit()} and {@see self::isFieldConvertableImplicit()} + * accordingly. Field also can be marked by a special marker (class/interface) + * as ignored, see {@see self::isFieldConvertableIgnored()} and + * {@see self::getFieldMarkerIgnored()}. + */ protected function isFieldConvertable( Manipulator $manipulator, InputFieldSource|ObjectFieldSource|InterfaceFieldSource $field, Context $context, ): bool { - /** - * Union? - */ + // Union? if ($field->isUnion()) { // todo(graphql): Would be nice to support Unions. Maybe just use // fields with same name and type for all members? return false; } - /** - * Explicit? We are expecting that the type created for the directive - * and all fields are valid and available. Also, keep in mind that the - * type is a `input` so it cannot have arguments, `FieldResolver` - * (if GraphQL Schema valid). - */ - if (!$context->get(HandlerContextImplicit::class)?->value) { - return true; + // Convertable? + $convertable = $context->get(HandlerContextImplicit::class)?->value + ? $this->isFieldConvertableImplicit($manipulator, $field, $context) + : $this->isFieldConvertableExplicit($manipulator, $field, $context); + + if (!$convertable) { + return false; } - /** - * Nope. Implicit type (=placeholder) is an `object`/`interface` and may - * contain fields with arguments and/or `FieldResolver` directive - these - * fields (most likely) cannot be used to modify the Builder. - */ + // Ignored? + if ($this->isFieldConvertableIgnored($manipulator, $field, $context)) { + return false; + } + + // Ok + return true; + } + + protected function isFieldConvertableExplicit( + Manipulator $manipulator, + InputFieldSource|ObjectFieldSource|InterfaceFieldSource $field, + Context $context, + ): bool { + // Explicit type is an `input` and we are expecting this type was created + // for the directive, so all fields are valid and available. + return true; + } + + protected function isFieldConvertableImplicit( + Manipulator $manipulator, + InputFieldSource|ObjectFieldSource|InterfaceFieldSource $field, + Context $context, + ): bool { + // Implicit type (=placeholder) is an `object`/`interface` and may contain + // fields with arguments and/or `FieldResolver` directive - these fields + // (most likely) cannot be used to modify the Builder. + // Resolver? $resolver = $manipulator->getDirective($field->getField(), FieldResolver::class); - if ($resolver && !$this->isFieldDirectiveConvertable($manipulator, $field, $context, $resolver)) { + if ($resolver && !$this->isFieldConvertableResolver($manipulator, $field, $context, $resolver)) { return false; } - // Object/Arguments allowed only if Resolver defined + // Object/Arguments allowed only if Resolver defined and convertable if (($field->hasArguments() || $field->isObject()) && !$resolver) { return false; } @@ -182,16 +211,53 @@ protected function isFieldConvertable( return true; } - protected function isFieldDirectiveConvertable( + protected function isFieldConvertableResolver( Manipulator $manipulator, InputFieldSource|ObjectFieldSource|InterfaceFieldSource $field, Context $context, - Directive $directive, + FieldResolver $directive, ): bool { return ($directive instanceof RelationDirective && !RelationDirectiveHelper::getPaginationType($directive)) || $this->isFieldDirectiveAllowed($manipulator, $field, $context, $directive); } + protected function isFieldConvertableIgnored( + Manipulator $manipulator, + InputFieldSource|ObjectFieldSource|InterfaceFieldSource $field, + Context $context, + ): bool { + // Marker? + $marker = $this->getFieldMarkerIgnored(); + + if (!$marker) { + return false; + } + + // Ignored? + if ($field instanceof $marker || $manipulator->getDirective($field->getField(), $marker) !== null) { + return true; + } + + // Ignored type? + $fieldType = $field->getTypeDefinition(); + + if ($fieldType instanceof $marker || $manipulator->getDirective($fieldType, $marker) !== null) { + return true; + } + + // Nope + return false; + } + + /** + * @see self::isFieldConvertableIgnored() + * + * @return class-string|null + */ + protected function getFieldMarkerIgnored(): ?string { + return null; + } + protected function getFieldDefinition( Manipulator $manipulator, InputFieldSource|ObjectFieldSource|InterfaceFieldSource $field, diff --git a/packages/graphql/src/SearchBy/Types/Condition.php b/packages/graphql/src/SearchBy/Types/Condition.php index b3f751929..92cc934e3 100644 --- a/packages/graphql/src/SearchBy/Types/Condition.php +++ b/packages/graphql/src/SearchBy/Types/Condition.php @@ -84,30 +84,8 @@ protected function getOperators( } #[Override] - protected function isFieldConvertable( - Manipulator $manipulator, - InputFieldSource|ObjectFieldSource|InterfaceFieldSource $field, - Context $context, - ): bool { - // Parent? - if (!parent::isFieldConvertable($manipulator, $field, $context)) { - return false; - } - - // Ignored field? - if ($manipulator->getDirective($field->getField(), Ignored::class) !== null) { - return false; - } - - // Ignored type? - $fieldType = $field->getTypeDefinition(); - - if ($fieldType instanceof Ignored || $manipulator->getDirective($fieldType, Ignored::class) !== null) { - return false; - } - - // Ok - return true; + protected function getFieldMarkerIgnored(): ?string { + return Ignored::class; } /** diff --git a/packages/graphql/src/SortBy/Types/Clause.php b/packages/graphql/src/SortBy/Types/Clause.php index 2c5f16095..a84c4b4b9 100644 --- a/packages/graphql/src/SortBy/Types/Clause.php +++ b/packages/graphql/src/SortBy/Types/Clause.php @@ -10,7 +10,6 @@ use GraphQL\Type\Definition\InterfaceType; use GraphQL\Type\Definition\ObjectType; use LastDragon_ru\LaraASP\GraphQL\Builder\Context\HandlerContextBuilderInfo; -use LastDragon_ru\LaraASP\GraphQL\Builder\Context\HandlerContextImplicit; use LastDragon_ru\LaraASP\GraphQL\Builder\Contracts\Context; use LastDragon_ru\LaraASP\GraphQL\Builder\Contracts\Operator as OperatorContract; use LastDragon_ru\LaraASP\GraphQL\Builder\Contracts\TypeSource; @@ -81,30 +80,18 @@ protected function getOperators( } #[Override] - protected function isFieldConvertable( + protected function isFieldConvertableImplicit( Manipulator $manipulator, - InputFieldSource|ObjectFieldSource|InterfaceFieldSource $field, + ObjectFieldSource|InputFieldSource|InterfaceFieldSource $field, Context $context, ): bool { // Parent? - if (!parent::isFieldConvertable($manipulator, $field, $context)) { + if (!parent::isFieldConvertableImplicit($manipulator, $field, $context,)) { return false; } // List of scalars/enums? - if ($context->get(HandlerContextImplicit::class)?->value && $field->isList() && !$field->isObject()) { - return false; - } - - // Ignored field? - if ($manipulator->getDirective($field->getField(), Ignored::class) !== null) { - return false; - } - - // Ignored type? - $fieldType = $field->getTypeDefinition(); - - if ($fieldType instanceof Ignored || $manipulator->getDirective($fieldType, Ignored::class) !== null) { + if ($field->isList() && !$field->isObject()) { return false; } @@ -112,6 +99,11 @@ protected function isFieldConvertable( return true; } + #[Override] + protected function getFieldMarkerIgnored(): ?string { + return Ignored::class; + } + /** * @inheritDoc */ From 9f13447a3e3fa72928fa8f397aa0716da09289ae Mon Sep 17 00:00:00 2001 From: Aleksei Lebedev <1329824+LastDragon-ru@users.noreply.github.com> Date: Sun, 28 Jan 2024 11:47:09 +0400 Subject: [PATCH 23/26] Paginated relations available for `@searchBy`/`@sortBy`. --- .../graphql/src/Builder/Types/InputObject.php | 5 ++-- .../DirectiveTest/Implicit.expected.graphql | 24 +++++++++++++++++++ .../DirectiveTest/Implicit.schema.graphql | 4 ++-- .../DirectiveTest/Implicit.expected.graphql | 24 +++++++++++++++++++ .../DirectiveTest/Implicit.schema.graphql | 4 ++-- 5 files changed, 54 insertions(+), 7 deletions(-) diff --git a/packages/graphql/src/Builder/Types/InputObject.php b/packages/graphql/src/Builder/Types/InputObject.php index 174d55480..6a3265fbb 100644 --- a/packages/graphql/src/Builder/Types/InputObject.php +++ b/packages/graphql/src/Builder/Types/InputObject.php @@ -24,7 +24,6 @@ use LastDragon_ru\LaraASP\GraphQL\Builder\Sources\InterfaceSource; use LastDragon_ru\LaraASP\GraphQL\Builder\Sources\ObjectFieldSource; use LastDragon_ru\LaraASP\GraphQL\Builder\Sources\ObjectSource; -use LastDragon_ru\LaraASP\GraphQL\Utils\RelationDirectiveHelper; use Nuwave\Lighthouse\Schema\Directives\RelationDirective; use Nuwave\Lighthouse\Schema\Directives\RenameDirective; use Nuwave\Lighthouse\Support\Contracts\Directive; @@ -100,7 +99,7 @@ public function getTypeDefinition( } // Field & Type - $fieldSource = $source->getField($field); + $fieldSource = $source->getField($field, $manipulator->getOriginType($field)); if (!$this->isFieldConvertable($manipulator, $fieldSource, $context)) { continue; @@ -217,7 +216,7 @@ protected function isFieldConvertableResolver( Context $context, FieldResolver $directive, ): bool { - return ($directive instanceof RelationDirective && !RelationDirectiveHelper::getPaginationType($directive)) + return $directive instanceof RelationDirective || $this->isFieldDirectiveAllowed($manipulator, $field, $context, $directive); } diff --git a/packages/graphql/src/SearchBy/Directives/DirectiveTest/Implicit.expected.graphql b/packages/graphql/src/SearchBy/Directives/DirectiveTest/Implicit.expected.graphql index 9368619c8..44a2e5673 100644 --- a/packages/graphql/src/SearchBy/Directives/DirectiveTest/Implicit.expected.graphql +++ b/packages/graphql/src/SearchBy/Directives/DirectiveTest/Implicit.expected.graphql @@ -373,6 +373,18 @@ input SearchByConditionA { relations: SearchByComplexRelationB @searchByOperatorRelation + """ + Relationship condition. + """ + relationsPaginated: SearchByComplexRelationB + @searchByOperatorRelation + + """ + Relationship condition. + """ + relationsPaginatedWithArgs: SearchByComplexRelationB + @searchByOperatorRelation + """ Relationship condition. """ @@ -473,6 +485,18 @@ input SearchByConditionC { relations: SearchByComplexRelationB @searchByOperatorRelation + """ + Relationship condition. + """ + relationsPaginated: SearchByComplexRelationB + @searchByOperatorRelation + + """ + Relationship condition. + """ + relationsPaginatedWithArgs: SearchByComplexRelationB + @searchByOperatorRelation + """ Relationship condition. """ diff --git a/packages/graphql/src/SearchBy/Directives/DirectiveTest/Implicit.schema.graphql b/packages/graphql/src/SearchBy/Directives/DirectiveTest/Implicit.schema.graphql index 1c79e4acf..aba265b1d 100644 --- a/packages/graphql/src/SearchBy/Directives/DirectiveTest/Implicit.schema.graphql +++ b/packages/graphql/src/SearchBy/Directives/DirectiveTest/Implicit.schema.graphql @@ -47,14 +47,14 @@ interface C { relations: [B!]! @hasMany relationWithArgs(arg: String): A! @hasOne relationsWithArgs(arg: String): [A!] @hasMany + relationsPaginated: B! @hasMany(type: PAGINATOR) + relationsPaginatedWithArgs(arg: String): [B!]! @hasMany(type: PAGINATOR) # Should be ignored field: B! fields: [B!]! property(arg: String): Int! resolver: Int! @field(resolver: "\\LastDragon_ru\\LaraASP\\GraphQL\\SearchBy\\Directives\\DirectiveTest__Resolver") - relationsPaginated: B! @hasMany(type: PAGINATOR) - relationsPaginatedWithArgs(arg: String): [B!]! @hasMany(type: PAGINATOR) stream: [A!]! @stream( sortable: false builder: { diff --git a/packages/graphql/src/SortBy/Directives/DirectiveTest/Implicit.expected.graphql b/packages/graphql/src/SortBy/Directives/DirectiveTest/Implicit.expected.graphql index 3a3189609..8bd2e9d51 100644 --- a/packages/graphql/src/SortBy/Directives/DirectiveTest/Implicit.expected.graphql +++ b/packages/graphql/src/SortBy/Directives/DirectiveTest/Implicit.expected.graphql @@ -161,6 +161,18 @@ input SortByClauseA { relations: SortByClauseB @sortByOperatorProperty + """ + Property clause. + """ + relationsPaginated: SortByClauseB + @sortByOperatorProperty + + """ + Property clause. + """ + relationsPaginatedWithArgs: SortByClauseB + @sortByOperatorProperty + """ Property clause. """ @@ -249,6 +261,18 @@ input SortByClauseC { relations: SortByClauseB @sortByOperatorProperty + """ + Property clause. + """ + relationsPaginated: SortByClauseB + @sortByOperatorProperty + + """ + Property clause. + """ + relationsPaginatedWithArgs: SortByClauseB + @sortByOperatorProperty + """ Property clause. """ diff --git a/packages/graphql/src/SortBy/Directives/DirectiveTest/Implicit.schema.graphql b/packages/graphql/src/SortBy/Directives/DirectiveTest/Implicit.schema.graphql index 3a6f31299..aab9429fe 100644 --- a/packages/graphql/src/SortBy/Directives/DirectiveTest/Implicit.schema.graphql +++ b/packages/graphql/src/SortBy/Directives/DirectiveTest/Implicit.schema.graphql @@ -48,6 +48,8 @@ interface C { relations: [B!]! @hasMany relationWithArgs(arg: String): A! @hasOne relationsWithArgs(arg: String): [A!] @hasMany + relationsPaginated: B! @hasMany(type: PAGINATOR) + relationsPaginatedWithArgs(arg: String): [B!]! @hasMany(type: PAGINATOR) # Should be ignored list: [String!] @@ -55,8 +57,6 @@ interface C { fields: [B!]! property(arg: String): Int! resolver: Int! @field(resolver: "\\LastDragon_ru\\LaraASP\\GraphQL\\SortBy\\Directives\\DirectiveTest__Resolver") - relationsPaginated: B! @hasMany(type: PAGINATOR) - relationsPaginatedWithArgs(arg: String): [B!]! @hasMany(type: PAGINATOR) stream: [A!]! @stream( searchable: false builder: { From 31d962b050f95d3f93e1d7df63cf22a9ab4ce488 Mon Sep 17 00:00:00 2001 From: Aleksei Lebedev <1329824+LastDragon-ru@users.noreply.github.com> Date: Mon, 29 Jan 2024 10:01:38 +0400 Subject: [PATCH 24/26] More precise of fields check for implicit type. --- .../graphql/src/Builder/Types/InputObject.php | 22 ++- .../DirectiveTest/Implicit.expected.graphql | 126 ++++++++++++++++++ .../DirectiveTest/Implicit.schema.graphql | 18 ++- .../graphql/src/SearchBy/Types/Condition.php | 5 + .../DirectiveTest/Implicit.expected.graphql | 108 +++++++++++++++ .../DirectiveTest/Implicit.schema.graphql | 18 ++- packages/graphql/src/SortBy/Types/Clause.php | 5 + 7 files changed, 295 insertions(+), 7 deletions(-) diff --git a/packages/graphql/src/Builder/Types/InputObject.php b/packages/graphql/src/Builder/Types/InputObject.php index 6a3265fbb..4979e1b9c 100644 --- a/packages/graphql/src/Builder/Types/InputObject.php +++ b/packages/graphql/src/Builder/Types/InputObject.php @@ -31,6 +31,7 @@ use Override; use function count; +use function is_a; use function trim; abstract class InputObject implements TypeDefinition { @@ -194,6 +195,16 @@ protected function isFieldConvertableImplicit( // fields with arguments and/or `FieldResolver` directive - these fields // (most likely) cannot be used to modify the Builder. + // Operator? + $marker = $this->getFieldMarkerOperator(); + $operator = $marker + ? $manipulator->getDirective($field->getField(), $marker) + : null; + + if ($operator) { + return true; + } + // Resolver? $resolver = $manipulator->getDirective($field->getField(), FieldResolver::class); @@ -216,8 +227,8 @@ protected function isFieldConvertableResolver( Context $context, FieldResolver $directive, ): bool { - return $directive instanceof RelationDirective - || $this->isFieldDirectiveAllowed($manipulator, $field, $context, $directive); + return ($directive instanceof RelationDirective && $field->isObject()) + || ($directive instanceof RenameDirective && !$field->hasArguments() && !$field->isObject()); } protected function isFieldConvertableIgnored( @@ -248,6 +259,11 @@ protected function isFieldConvertableIgnored( return false; } + /** + * @return class-string + */ + abstract protected function getFieldMarkerOperator(): string; + /** * @see self::isFieldConvertableIgnored() * @@ -398,7 +414,7 @@ protected function isFieldDirectiveAllowed( Context $context, Directive $directive, ): bool { - return $directive instanceof Operator + return is_a($directive, $this->getFieldMarkerOperator()) || $directive instanceof RenameDirective; } } diff --git a/packages/graphql/src/SearchBy/Directives/DirectiveTest/Implicit.expected.graphql b/packages/graphql/src/SearchBy/Directives/DirectiveTest/Implicit.expected.graphql index 44a2e5673..cd1b0b986 100644 --- a/packages/graphql/src/SearchBy/Directives/DirectiveTest/Implicit.expected.graphql +++ b/packages/graphql/src/SearchBy/Directives/DirectiveTest/Implicit.expected.graphql @@ -355,6 +355,18 @@ input SearchByConditionA { not: SearchByConditionA @searchByOperatorNot + """ + Relationship condition. + """ + operator: SearchByComplexRelationB + @searchByOperatorRelation + + """ + Relationship condition. + """ + operators: SearchByComplexRelationB + @searchByOperatorRelation + """ Relationship condition. """ @@ -390,6 +402,24 @@ input SearchByConditionA { """ relationsWithArgs: SearchByComplexRelationA @searchByOperatorRelation + + """ + Property condition. + """ + renamed: SearchByScalarString + @searchByOperatorProperty + @rename( + attribute: "internal" + ) + + """ + Property condition. + """ + renamedList: SearchByScalarStringOrNull + @searchByOperatorProperty + @rename( + attribute: "internal" + ) } """ @@ -467,6 +497,18 @@ input SearchByConditionC { not: SearchByConditionC @searchByOperatorNot + """ + Relationship condition. + """ + operator: SearchByComplexRelationB + @searchByOperatorRelation + + """ + Relationship condition. + """ + operators: SearchByComplexRelationB + @searchByOperatorRelation + """ Relationship condition. """ @@ -502,6 +544,24 @@ input SearchByConditionC { """ relationsWithArgs: SearchByComplexRelationA @searchByOperatorRelation + + """ + Property condition. + """ + renamed: SearchByScalarString + @searchByOperatorProperty + @rename( + attribute: "internal" + ) + + """ + Property condition. + """ + renamedList: SearchByScalarStringOrNull + @searchByOperatorProperty + @rename( + attribute: "internal" + ) } """ @@ -830,6 +890,12 @@ interface C { name: String! + operator: B! + @searchByOperatorRelation + + operators: [B!] + @searchByOperatorRelation + property( arg: String ): Int! @@ -882,6 +948,33 @@ interface C { ): [A!] @hasMany + renamed: String! + @rename( + attribute: "internal" + ) + + renamedList: [String!] + @rename( + attribute: "internal" + ) + + renamedObject: B + @rename( + attribute: "internal" + ) + + renamedObjectList: [B] + @rename( + attribute: "internal" + ) + + renamedWithArgs( + arg: String + ): String! + @rename( + attribute: "internal" + ) + resolver: Int! @field( resolver: "\\LastDragon_ru\\LaraASP\\GraphQL\\SearchBy\\Directives\\DirectiveTest__Resolver" @@ -939,6 +1032,12 @@ type A { name: String! + operator: B! + @searchByOperatorRelation + + operators: [B!] + @searchByOperatorRelation + property( arg: String ): Int! @@ -991,6 +1090,33 @@ type A { ): [A!] @hasMany + renamed: String! + @rename( + attribute: "internal" + ) + + renamedList: [String!] + @rename( + attribute: "internal" + ) + + renamedObject: B + @rename( + attribute: "internal" + ) + + renamedObjectList: [B] + @rename( + attribute: "internal" + ) + + renamedWithArgs( + arg: String + ): String! + @rename( + attribute: "internal" + ) + resolver: Int! @field( resolver: "\\LastDragon_ru\\LaraASP\\GraphQL\\SearchBy\\Directives\\DirectiveTest__Resolver" diff --git a/packages/graphql/src/SearchBy/Directives/DirectiveTest/Implicit.schema.graphql b/packages/graphql/src/SearchBy/Directives/DirectiveTest/Implicit.schema.graphql index aba265b1d..e1bc18eb2 100644 --- a/packages/graphql/src/SearchBy/Directives/DirectiveTest/Implicit.schema.graphql +++ b/packages/graphql/src/SearchBy/Directives/DirectiveTest/Implicit.schema.graphql @@ -11,24 +11,31 @@ type A { "Description should be ignored." id: ID! name: String! + operator: B! @searchByOperatorRelation + operators: [B!] @searchByOperatorRelation relation: B! @hasOne relations: [B!]! @hasMany relationWithArgs(arg: String): A! @hasOne relationsWithArgs(arg: String): [A!] @hasMany + relationsPaginated: B! @hasMany(type: PAGINATOR) + relationsPaginatedWithArgs(arg: String): [B!]! @hasMany(type: PAGINATOR) + renamed: String! @rename(attribute: "internal") + renamedList: [String!] @rename(attribute: "internal") # Should be ignored field: B! fields: [B!]! property(arg: String): Int! resolver: Int! @field(resolver: "\\LastDragon_ru\\LaraASP\\GraphQL\\SearchBy\\Directives\\DirectiveTest__Resolver") - relationsPaginated: B! @hasMany(type: PAGINATOR) - relationsPaginatedWithArgs(arg: String): [B!]! @hasMany(type: PAGINATOR) stream: [A!]! @stream( sortable: false builder: { model: "LastDragon_ru\\LaraASP\\GraphQL\\Testing\\Package\\Data\\Models\\TestObject" } ) + renamedObject: B @rename(attribute: "internal") + renamedObjectList: [B] @rename(attribute: "internal") + renamedWithArgs(arg: String): String! @rename(attribute: "internal") } type B { @@ -43,12 +50,16 @@ interface C { "Description should be ignored." id: ID! name: String! + operator: B! @searchByOperatorRelation + operators: [B!] @searchByOperatorRelation relation: B! @hasOne relations: [B!]! @hasMany relationWithArgs(arg: String): A! @hasOne relationsWithArgs(arg: String): [A!] @hasMany relationsPaginated: B! @hasMany(type: PAGINATOR) relationsPaginatedWithArgs(arg: String): [B!]! @hasMany(type: PAGINATOR) + renamed: String! @rename(attribute: "internal") + renamedList: [String!] @rename(attribute: "internal") # Should be ignored field: B! @@ -61,4 +72,7 @@ interface C { model: "LastDragon_ru\\LaraASP\\GraphQL\\Testing\\Package\\Data\\Models\\TestObject" } ) + renamedObject: B @rename(attribute: "internal") + renamedObjectList: [B] @rename(attribute: "internal") + renamedWithArgs(arg: String): String! @rename(attribute: "internal") } diff --git a/packages/graphql/src/SearchBy/Types/Condition.php b/packages/graphql/src/SearchBy/Types/Condition.php index 92cc934e3..236b52533 100644 --- a/packages/graphql/src/SearchBy/Types/Condition.php +++ b/packages/graphql/src/SearchBy/Types/Condition.php @@ -83,6 +83,11 @@ protected function getOperators( )); } + #[Override] + protected function getFieldMarkerOperator(): string { + return Operator::class; + } + #[Override] protected function getFieldMarkerIgnored(): ?string { return Ignored::class; diff --git a/packages/graphql/src/SortBy/Directives/DirectiveTest/Implicit.expected.graphql b/packages/graphql/src/SortBy/Directives/DirectiveTest/Implicit.expected.graphql index 8bd2e9d51..cbb45f64b 100644 --- a/packages/graphql/src/SortBy/Directives/DirectiveTest/Implicit.expected.graphql +++ b/packages/graphql/src/SortBy/Directives/DirectiveTest/Implicit.expected.graphql @@ -143,6 +143,18 @@ input SortByClauseA { nullsLast: SortByClauseA @sortByOperatorNullsLast + """ + Property clause. + """ + operator: SortByClauseB + @sortByOperatorProperty + + """ + Property clause. + """ + operators: SortByClauseB + @sortByOperatorProperty + """ Property clause. """ @@ -178,6 +190,15 @@ input SortByClauseA { """ relationsWithArgs: SortByClauseA @sortByOperatorProperty + + """ + Property clause. + """ + renamed: SortByTypeDirection + @sortByOperatorField + @rename( + attribute: "internal" + ) } """ @@ -243,6 +264,18 @@ input SortByClauseC { nullsLast: SortByClauseC @sortByOperatorNullsLast + """ + Property clause. + """ + operator: SortByClauseB + @sortByOperatorProperty + + """ + Property clause. + """ + operators: SortByClauseB + @sortByOperatorProperty + """ Property clause. """ @@ -278,6 +311,15 @@ input SortByClauseC { """ relationsWithArgs: SortByClauseA @sortByOperatorProperty + + """ + Property clause. + """ + renamed: SortByTypeDirection + @sortByOperatorField + @rename( + attribute: "internal" + ) } """ @@ -312,6 +354,12 @@ interface C { list: [String!] name: String! + operator: B! + @sortByOperatorProperty + + operators: [B!] + @sortByOperatorProperty + property( arg: String ): Int! @@ -364,6 +412,33 @@ interface C { ): [A!] @hasMany + renamed: String! + @rename( + attribute: "internal" + ) + + renamedList: [String!] + @rename( + attribute: "internal" + ) + + renamedObject: B + @rename( + attribute: "internal" + ) + + renamedObjectList: [B] + @rename( + attribute: "internal" + ) + + renamedWithArgs( + arg: String + ): String! + @rename( + attribute: "internal" + ) + resolver: Int! @field( resolver: "\\LastDragon_ru\\LaraASP\\GraphQL\\SortBy\\Directives\\DirectiveTest__Resolver" @@ -422,6 +497,12 @@ type A { list: [String!] name: String! + operator: B! + @sortByOperatorProperty + + operators: [B!] + @sortByOperatorProperty + property( arg: String ): Int! @@ -474,6 +555,33 @@ type A { ): [A!] @hasMany + renamed: String! + @rename( + attribute: "internal" + ) + + renamedList: [String!] + @rename( + attribute: "internal" + ) + + renamedObject: B + @rename( + attribute: "internal" + ) + + renamedObjectList: [B] + @rename( + attribute: "internal" + ) + + renamedWithArgs( + arg: String + ): String! + @rename( + attribute: "internal" + ) + resolver: Int! @field( resolver: "\\LastDragon_ru\\LaraASP\\GraphQL\\SortBy\\Directives\\DirectiveTest__Resolver" diff --git a/packages/graphql/src/SortBy/Directives/DirectiveTest/Implicit.schema.graphql b/packages/graphql/src/SortBy/Directives/DirectiveTest/Implicit.schema.graphql index aab9429fe..eed207cf6 100644 --- a/packages/graphql/src/SortBy/Directives/DirectiveTest/Implicit.schema.graphql +++ b/packages/graphql/src/SortBy/Directives/DirectiveTest/Implicit.schema.graphql @@ -11,10 +11,15 @@ type A { "Description should be ignored." id: ID! name: String! + operator: B! @sortByOperatorProperty + operators: [B!] @sortByOperatorProperty relation: B! @hasOne relations: [B!]! @hasMany relationWithArgs(arg: String): A! @hasOne relationsWithArgs(arg: String): [A!] @hasMany + relationsPaginated: B! @hasMany(type: PAGINATOR) + relationsPaginatedWithArgs(arg: String): [B!]! @hasMany(type: PAGINATOR) + renamed: String! @rename(attribute: "internal") # Should be ignored list: [String!] @@ -22,14 +27,16 @@ type A { fields: [B!]! property(arg: String): Int! resolver: Int! @field(resolver: "\\LastDragon_ru\\LaraASP\\GraphQL\\SortBy\\Directives\\DirectiveTest__Resolver") - relationsPaginated: B! @hasMany(type: PAGINATOR) - relationsPaginatedWithArgs(arg: String): [B!]! @hasMany(type: PAGINATOR) stream: [A!]! @stream( searchable: false builder: { model: "LastDragon_ru\\LaraASP\\GraphQL\\Testing\\Package\\Data\\Models\\TestObject" } ) + renamedList: [String!] @rename(attribute: "internal") + renamedObject: B @rename(attribute: "internal") + renamedObjectList: [B] @rename(attribute: "internal") + renamedWithArgs(arg: String): String! @rename(attribute: "internal") } type B { @@ -44,12 +51,15 @@ interface C { "Description should be ignored." id: ID! name: String! + operator: B! @sortByOperatorProperty + operators: [B!] @sortByOperatorProperty relation: B! @hasOne relations: [B!]! @hasMany relationWithArgs(arg: String): A! @hasOne relationsWithArgs(arg: String): [A!] @hasMany relationsPaginated: B! @hasMany(type: PAGINATOR) relationsPaginatedWithArgs(arg: String): [B!]! @hasMany(type: PAGINATOR) + renamed: String! @rename(attribute: "internal") # Should be ignored list: [String!] @@ -63,4 +73,8 @@ interface C { model: "LastDragon_ru\\LaraASP\\GraphQL\\Testing\\Package\\Data\\Models\\TestObject" } ) + renamedList: [String!] @rename(attribute: "internal") + renamedObject: B @rename(attribute: "internal") + renamedObjectList: [B] @rename(attribute: "internal") + renamedWithArgs(arg: String): String! @rename(attribute: "internal") } diff --git a/packages/graphql/src/SortBy/Types/Clause.php b/packages/graphql/src/SortBy/Types/Clause.php index a84c4b4b9..c263fe460 100644 --- a/packages/graphql/src/SortBy/Types/Clause.php +++ b/packages/graphql/src/SortBy/Types/Clause.php @@ -99,6 +99,11 @@ protected function isFieldConvertableImplicit( return true; } + #[Override] + protected function getFieldMarkerOperator(): string { + return Operator::class; + } + #[Override] protected function getFieldMarkerIgnored(): ?string { return Ignored::class; From 657a269ba0a08c5d88218e3a4b80a41a56358a09 Mon Sep 17 00:00:00 2001 From: Aleksei Lebedev <1329824+LastDragon-ru@users.noreply.github.com> Date: Mon, 29 Jan 2024 11:13:51 +0400 Subject: [PATCH 25/26] docs(graphql): Input type auto-generation changes. --- packages/graphql/README.md | 20 +++++++++++ packages/graphql/UPGRADE.md | 8 +++++ packages/graphql/docs/Directives/@searchBy.md | 28 +++++++++++---- packages/graphql/docs/Directives/@sortBy.md | 35 ++++++++++++++----- 4 files changed, 77 insertions(+), 14 deletions(-) diff --git a/packages/graphql/README.md b/packages/graphql/README.md index 171948262..a94bf0655 100644 --- a/packages/graphql/README.md +++ b/packages/graphql/README.md @@ -104,6 +104,26 @@ Represents [JSON](https://json.org) string. [Scout](https://laravel.com/docs/scout) is also supported 🤩. You just need to add [`@search`](https://lighthouse-php.com/master/api-reference/directives.html#search) directive to an argument. +# Input type auto-generation + +The type used with the Builder directives like `@searchBy`/`@sortBy` may be Explicit (when you specify the `input` name `field(where: InputTypeName @searchBy): [Object!]!`) or Implicit (when the `_` used, `field(where: _ @searchBy): [Object!]!`). They are processing a bit differently. + +For Explicit type, all fields except unions and marked as ignored (if supported by the directive) will be included. + +For Implicit type, the following rules are applied (in this order; concrete directive may have differences, please check its docs): + +* Union? - exclude +* Has `Operator` of the concrete directive? - include +* Has `Nuwave\Lighthouse\Support\Contracts\FieldResolver`? + * Yes + * Is `Nuwave\Lighthouse\Schema\Directives\RelationDirective`? - Include if is the `type` or list of `type` + * Is `Nuwave\Lighthouse\Schema\Directives\RenameDirective`? - Include if is `scalar`/`enum` (not `type`) and no arguments + * Otherwise - exclude + * No + * Is `type` or has arguments - exclude + * Otherwise - include +* Ignored (if supported)? - exclude + # Builder property name By default `@searchBy`/`@sortBy` will convert nested/related properties into dot string: eg `{user: {name: asc}}` will be converted into `user.name`. You can redefine this behavior by [`BuilderPropertyResolver`](./src/Builder/Contracts/BuilderPropertyResolver.php): diff --git a/packages/graphql/UPGRADE.md b/packages/graphql/UPGRADE.md index ea6ac34ae..ed18ba61d 100644 --- a/packages/graphql/UPGRADE.md +++ b/packages/graphql/UPGRADE.md @@ -27,6 +27,12 @@ Please also see [changelog](https://github.com/LastDragon-ru/lara-asp/releases) [//]: # (end: c70a9a43c0a80bd2e7fa6010a9b2c0fbcab4cb4d536d7a498216d9df7431f7e2) +## Tips + +> [!TIP] +> +> Maybe a good idea to add test (at least) with `LastDragon_ru\LaraASP\GraphQL\Testing\GraphQLAssertions::assertGraphQLSchemaEquals()` assertion before the upgrade 🤗 + # Upgrade from v5 ## General @@ -39,6 +45,8 @@ Please also see [changelog](https://github.com/LastDragon-ru/lara-asp/releases) [//]: # (end: fd146cf51ef5a8d9d13e0317c09860f472c63cb3d60d02f4d95deb3e12cae73d) +* [ ] [Input type auto-generation](README.md#input-type-auto-generation) reworked and may include more/fewer fields. Please check the documentation and update the schema if needed. + ## `@searchBy` * [ ] `enum SearchByTypeFlag { yes }` => `enum SearchByTypeFlag { Yes }`. 🤝 diff --git a/packages/graphql/docs/Directives/@searchBy.md b/packages/graphql/docs/Directives/@searchBy.md index 8b49b226d..26c17af77 100644 --- a/packages/graphql/docs/Directives/@searchBy.md +++ b/packages/graphql/docs/Directives/@searchBy.md @@ -145,13 +145,29 @@ query { ## Input type auto-generation -As you can see in the example above you can use the special placeholder `_` instead of real `input`. In this case, `@searchBy` will generate `input` automatically by the actual `type` of the query. While converting `type` into `input` following fields will be excluded: +As you can see in the example above you can use the special placeholder `_` instead of real `input`. In this case, `@searchBy` will generate `input` automatically by the actual `type` of the query. Please check the main section of [Input type auto-generation](../../README.md#input-type-auto-generation) to learn more about general conversion rules. -* unions -* with `@field` directive -* with `@searchByIgnored` directive -* with any directive that implements [`Ignored`](../../src/SearchBy/Contracts/Ignored.php) -* any `Type` that implements [`Ignored`](../../src/SearchBy/Contracts/Ignored.php) +The `@searchByIgnored` can be used as Ignored marker. + +[include:exec]: <../../../../dev/artisan dev:directive @searchByIgnored> +[//]: # (start: 20d300e04ef04c52684a5d3db6a419825ada6f67a950a418e26dee5c9b5d218c) +[//]: # (warning: Generated automatically. Do not edit.) + +```graphql +""" +Marks that field/definition should be excluded from search. +""" +directive @searchByIgnored +on + | ENUM + | FIELD_DEFINITION + | INPUT_FIELD_DEFINITION + | INPUT_OBJECT + | OBJECT + | SCALAR +``` + +[//]: # (end: 20d300e04ef04c52684a5d3db6a419825ada6f67a950a418e26dee5c9b5d218c) ## Operators diff --git a/packages/graphql/docs/Directives/@sortBy.md b/packages/graphql/docs/Directives/@sortBy.md index e86fc0198..660b91d8a 100644 --- a/packages/graphql/docs/Directives/@sortBy.md +++ b/packages/graphql/docs/Directives/@sortBy.md @@ -66,14 +66,33 @@ query { ## Input type auto-generation -As you can see in the example above you can use the special placeholder `_` instead of real `input`. In this case, `@sortBy` will generate `input` automatically by the actual `type` of the query. While converting `type` into `input` following fields will be excluded: - -* unions -* with list/array type -* with `@field` directive -* with `@sortByIgnored` directive -* with any directive that implements [`Ignored`](../../src/SortBy/Contracts/Ignored.php) -* any `Type` that implements [`Ignored`](../../src/SortBy/Contracts/Ignored.php) +As you can see in the example above you can use the special placeholder `_` instead of real `input`. In this case, `@sortBy` will generate `input` automatically by the actual `type` of the query. Please check the main section of [Input type auto-generation](../../README.md#input-type-auto-generation) to learn more about general conversion rules. + +Addition rules for Implicit type: + +* The field is a list of `scalar`/`enum`? - exclude + +The `@sortByIgnored` can be used as Ignored marker. + +[include:exec]: <../../../../dev/artisan dev:directive @sortByIgnored> +[//]: # (start: 08dddca7c96cf62e6e6e632190eb16fa49d5c1652e35e29b74417dc9d52c29ff) +[//]: # (warning: Generated automatically. Do not edit.) + +```graphql +""" +Marks that field/definition should be excluded from sort. +""" +directive @sortByIgnored +on + | ENUM + | FIELD_DEFINITION + | INPUT_FIELD_DEFINITION + | INPUT_OBJECT + | OBJECT + | SCALAR +``` + +[//]: # (end: 08dddca7c96cf62e6e6e632190eb16fa49d5c1652e35e29b74417dc9d52c29ff) ## Operators From 88bd6da747c14305c84373fee46f01dab4bdb3e3 Mon Sep 17 00:00:00 2001 From: Aleksei Lebedev <1329824+LastDragon-ru@users.noreply.github.com> Date: Mon, 29 Jan 2024 11:20:01 +0400 Subject: [PATCH 26/26] docs: Cleanup. --- packages/graphql/README.md | 2 +- packages/graphql/docs/Directives/@sortBy.md | 16 +--------------- 2 files changed, 2 insertions(+), 16 deletions(-) diff --git a/packages/graphql/README.md b/packages/graphql/README.md index a94bf0655..74d4f3dbf 100644 --- a/packages/graphql/README.md +++ b/packages/graphql/README.md @@ -102,7 +102,7 @@ Represents [JSON](https://json.org) string. # Scout -[Scout](https://laravel.com/docs/scout) is also supported 🤩. You just need to add [`@search`](https://lighthouse-php.com/master/api-reference/directives.html#search) directive to an argument. +[Scout](https://laravel.com/docs/scout) is also supported 🤩. You just need to add [`@search`](https://lighthouse-php.com/master/api-reference/directives.html#search) directive to an argument. Please note that available operators depend on [Scout itself](https://laravel.com/docs/scout#where-clauses). # Input type auto-generation diff --git a/packages/graphql/docs/Directives/@sortBy.md b/packages/graphql/docs/Directives/@sortBy.md index 660b91d8a..250e40e27 100644 --- a/packages/graphql/docs/Directives/@sortBy.md +++ b/packages/graphql/docs/Directives/@sortBy.md @@ -102,20 +102,6 @@ The package defines only one's own type. To extend/replace the list of its opera ## Eloquent/Database -### Supported Relations - -The main feature - the ability to sort results by relation properties, at the moment supported the following relation types: - -* `HasOne` () -* `HasMany` () -* `HasManyThrough` () -* `BelongsTo` () -* `BelongsToMany` () -* `MorphOne` () -* `MorphMany` () -* `MorphToMany` () -* `HasOneThrough` () - ### Order by random It is also possible to sort records in random order, but it is not enabled by default. To enable it you just need to add [`Random`](../../src/SortBy/Operators/Extra/Random.php)/`@sortByOperatorRandom` operator/directive to `Extra` type: @@ -171,7 +157,7 @@ query { ### NULLs ordering -`NULL`s order different in different databases. Sometimes you may to change it. There is no default/built-it support in Laravel nor Lighthouse, but you can do it! :) Please note, not all databases have native `NULLS FIRST`/`NULLS LAST` support (eg MySQL and SQL Server doesn't). The additional `ORDER BY` clause with `CASE WHEN` will be used for these databases. It may be slow for big datasets. +`NULL`s order different in different databases. Sometimes you may want to change it. There is no default/built-it support in Laravel nor Lighthouse, but you can do it! :) Please note, not all databases have native `NULLS FIRST`/`NULLS LAST` support (eg MySQL and SQL Server doesn't). The additional `ORDER BY` clause with `CASE WHEN` will be used for these databases. It may be slow for big datasets. Default ordering can be changed via config. You may set it for all directions if single value used, in this case NULL always be first/last: