diff --git a/composer.json b/composer.json index 107b0d9b448..ec8bdee384f 100644 --- a/composer.json +++ b/composer.json @@ -70,7 +70,7 @@ "twig/twig": "~3.21.1", "voku/portable-ascii": "^2.0", "web-auth/webauthn-lib": "~5.2.4", - "webonyx/graphql-php": "~14.11.10", + "webonyx/graphql-php": "~15.31.5", "yiisoft/arrays": "^3.2", "yiisoft/html": "^3.11", "yiisoft/translator": "^3.2", diff --git a/composer.lock b/composer.lock index 143390624f2..d6f6244583f 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "32d5d21113da19876cd97fe9fb221d64", + "content-hash": "f38dd4468e5a2606b5a05860d854b132", "packages": [ { "name": "bacon/bacon-qr-code", @@ -8942,38 +8942,48 @@ }, { "name": "webonyx/graphql-php", - "version": "v14.11.10", + "version": "v15.31.5", "source": { "type": "git", "url": "https://github.com/webonyx/graphql-php.git", - "reference": "d9c2fdebc6aa01d831bc2969da00e8588cffef19" + "reference": "089c4ef7e112df85788cfe06596278a8f99f4aa9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/webonyx/graphql-php/zipball/d9c2fdebc6aa01d831bc2969da00e8588cffef19", - "reference": "d9c2fdebc6aa01d831bc2969da00e8588cffef19", + "url": "https://api.github.com/repos/webonyx/graphql-php/zipball/089c4ef7e112df85788cfe06596278a8f99f4aa9", + "reference": "089c4ef7e112df85788cfe06596278a8f99f4aa9", "shasum": "" }, "require": { "ext-json": "*", "ext-mbstring": "*", - "php": "^7.1 || ^8" + "php": "^7.4 || ^8" }, "require-dev": { - "amphp/amp": "^2.3", - "doctrine/coding-standard": "^6.0", - "nyholm/psr7": "^1.2", + "amphp/amp": "^2.6", + "amphp/http-server": "^2.1", + "dms/phpunit-arraysubset-asserts": "dev-master", + "ergebnis/composer-normalize": "^2.28", + "friendsofphp/php-cs-fixer": "3.94.2", + "mll-lab/php-cs-fixer-config": "5.13.0", + "nyholm/psr7": "^1.5", "phpbench/phpbench": "^1.2", - "phpstan/extension-installer": "^1.0", - "phpstan/phpstan": "0.12.82", - "phpstan/phpstan-phpunit": "0.12.18", - "phpstan/phpstan-strict-rules": "0.12.9", - "phpunit/phpunit": "^7.2 || ^8.5", - "psr/http-message": "^1.0", - "react/promise": "2.*", - "simpod/php-coveralls-mirror": "^3.0" + "phpstan/extension-installer": "^1.1", + "phpstan/phpstan": "2.1.46", + "phpstan/phpstan-phpunit": "2.0.16", + "phpstan/phpstan-strict-rules": "2.0.10", + "phpunit/phpunit": "^9.5 || ^10.5.21 || ^11", + "psr/http-message": "^1 || ^2", + "react/http": "^1.6", + "react/promise": "^2.0 || ^3.0", + "rector/rector": "^2.0", + "symfony/polyfill-php81": "^1.23", + "symfony/var-exporter": "^5 || ^6 || ^7 || ^8", + "thecodingmachine/safe": "^1.3 || ^2 || ^3", + "ticketswap/phpstan-error-formatter": "1.3.0" }, "suggest": { + "amphp/http-server": "To leverage async resolving with webserver on AMPHP platform", "psr/http-message": "To use standard GraphQL server", "react/promise": "To leverage async resolving on React PHP platform" }, @@ -8995,15 +9005,19 @@ ], "support": { "issues": "https://github.com/webonyx/graphql-php/issues", - "source": "https://github.com/webonyx/graphql-php/tree/v14.11.10" + "source": "https://github.com/webonyx/graphql-php/tree/v15.31.5" }, "funding": [ + { + "url": "https://github.com/spawnia", + "type": "github" + }, { "url": "https://opencollective.com/webonyx-graphql-php", "type": "open_collective" } ], - "time": "2023-07-05T14:23:37+00:00" + "time": "2026-04-11T18:06:15+00:00" }, { "name": "yiisoft/aliases", diff --git a/src/Gql/Directives/FormatDateTime.php b/src/Gql/Directives/FormatDateTime.php index 18715616d4e..62c3844ccbc 100644 --- a/src/Gql/Directives/FormatDateTime.php +++ b/src/Gql/Directives/FormatDateTime.php @@ -12,7 +12,6 @@ use DateTime; use GraphQL\Language\DirectiveLocation; use GraphQL\Type\Definition\Directive as GqlDirective; -use GraphQL\Type\Definition\FieldArgument; use GraphQL\Type\Definition\ResolveInfo; use GraphQL\Type\Definition\Type; @@ -32,23 +31,23 @@ public static function create(): GqlDirective DirectiveLocation::FIELD, ], 'args' => [ - new FieldArgument([ + [ 'name' => 'format', 'type' => Type::string(), 'defaultValue' => self::DEFAULT_FORMAT, 'description' => 'The format to use. Can be `short`, `medium`, `long`, `full`, an [ICU date format](http://userguide.icu-project.org/formatparse/datetime), or a [PHP date format](https://www.php.net/manual/en/function.date.php). Defaults to the [Atom date time format](https://www.php.net/manual/en/class.datetimeinterface.php#datetime.constants.atom]).', - ]), - new FieldArgument([ + ], + [ 'name' => 'timezone', 'type' => Type::string(), 'description' => 'The full name of the timezone (e.g., America/New_York). Defaults to '.self::defaultTimeZone().' if no timezone set on the field.', 'defaultValue' => self::defaultTimeZone(), - ]), - new FieldArgument([ + ], + [ 'name' => 'locale', 'type' => Type::string(), 'description' => 'The locale to use when formatting the date. (E.g., en-US)', - ]), + ], ], 'description' => 'Formats a date in the desired format. Can be applied to all fields, only changes output of DateTime fields.', ])); diff --git a/src/Gql/Directives/Markdown.php b/src/Gql/Directives/Markdown.php index 8dfa43725cf..690832b9c83 100644 --- a/src/Gql/Directives/Markdown.php +++ b/src/Gql/Directives/Markdown.php @@ -8,7 +8,6 @@ use CraftCms\Cms\Support\Facades\Markdown as MarkdownFacade; use GraphQL\Language\DirectiveLocation; use GraphQL\Type\Definition\Directive as GqlDirective; -use GraphQL\Type\Definition\FieldArgument; use GraphQL\Type\Definition\ResolveInfo; use GraphQL\Type\Definition\Type; @@ -28,18 +27,18 @@ public static function create(): GqlDirective DirectiveLocation::FIELD, ], 'args' => [ - new FieldArgument([ + [ 'name' => 'flavor', 'type' => Type::string(), 'defaultValue' => self::DEFAULT_FLAVOR, 'description' => 'The “flavor” of Markdown the input should be interpreted with. Accepts the same flavor names as `CraftCms\\Cms\\Support\\Facades\\Markdown::parse()`.', - ]), - new FieldArgument([ + ], + [ 'name' => 'inlineOnly', 'type' => Type::boolean(), 'defaultValue' => self::DEFAULT_INLINE_ONLY, 'description' => 'Whether to only parse inline elements, omitting any `
` tags.',
- ]),
+ ],
],
'description' => 'Parses the passed field value as Markdown.',
]));
diff --git a/src/Gql/Directives/Money.php b/src/Gql/Directives/Money.php
index b02b8c0be86..ab1069d921c 100644
--- a/src/Gql/Directives/Money.php
+++ b/src/Gql/Directives/Money.php
@@ -9,7 +9,6 @@
use CraftCms\Cms\Support\Money as MoneyHelper;
use GraphQL\Language\DirectiveLocation;
use GraphQL\Type\Definition\Directive as GqlDirective;
-use GraphQL\Type\Definition\FieldArgument;
use GraphQL\Type\Definition\ResolveInfo;
use GraphQL\Type\Definition\Type;
@@ -33,17 +32,17 @@ public static function create(): GqlDirective
DirectiveLocation::FIELD,
],
'args' => [
- new FieldArgument([
+ [
'name' => 'format',
'type' => Type::string(),
'defaultValue' => self::FORMAT_STRING,
'description' => 'This specifies the format to output. This can be `amount`, `decimal`, `number`, or `string`. It defaults to the `string`.',
- ]),
- new FieldArgument([
+ ],
+ [
'name' => 'locale',
'type' => Type::string(),
'description' => 'The locale to use when formatting the money value. (e.g. `en_US`). This argument is only valid with `number` and `string` formats.',
- ]),
+ ],
],
'description' => 'Formats a money object to the desired format. It can be applied to any fields, but only changes a Money field.',
]));
diff --git a/src/Gql/Directives/StripTags.php b/src/Gql/Directives/StripTags.php
index 45a6ca2ecfa..41208888dba 100644
--- a/src/Gql/Directives/StripTags.php
+++ b/src/Gql/Directives/StripTags.php
@@ -7,7 +7,6 @@
use CraftCms\Cms\Gql\GqlEntityRegistry;
use GraphQL\Language\DirectiveLocation;
use GraphQL\Type\Definition\Directive as GqlDirective;
-use GraphQL\Type\Definition\FieldArgument;
use GraphQL\Type\Definition\ResolveInfo;
use GraphQL\Type\Definition\Type;
@@ -24,12 +23,12 @@ public static function create(): GqlDirective
],
'description' => 'Strips HTML tags from the field value.',
'args' => [
- new FieldArgument([
+ [
'name' => 'allowed',
'type' => Type::listOf(Type::string()),
'defaultValue' => [],
'description' => 'List of allowed tag names.',
- ]),
+ ],
],
]));
}
diff --git a/src/Gql/Directives/Transform.php b/src/Gql/Directives/Transform.php
index 02ca9e72b68..f13ead83336 100644
--- a/src/Gql/Directives/Transform.php
+++ b/src/Gql/Directives/Transform.php
@@ -11,23 +11,11 @@
use CraftCms\Cms\Gql\GqlHelper as Gql;
use GraphQL\Language\DirectiveLocation;
use GraphQL\Type\Definition\Directive as GqlDirective;
-use GraphQL\Type\Definition\FieldArgument;
use GraphQL\Type\Definition\ResolveInfo;
use Illuminate\Support\Collection;
class Transform extends Directive
{
- public function __construct(array $config)
- {
- $args = &$config['args'];
-
- foreach ($args as &$argument) {
- $argument = new FieldArgument($argument);
- }
-
- parent::__construct($config);
- }
-
public static function create(): GqlDirective
{
$typeName = static::name();
diff --git a/src/Gql/ElementQueryConditionBuilder.php b/src/Gql/ElementQueryConditionBuilder.php
index b47d696a325..1dc9655262e 100644
--- a/src/Gql/ElementQueryConditionBuilder.php
+++ b/src/Gql/ElementQueryConditionBuilder.php
@@ -34,7 +34,7 @@
use GraphQL\Language\AST\ObjectValueNode;
use GraphQL\Language\AST\VariableNode;
use GraphQL\Type\Definition\ResolveInfo;
-use GraphQL\Type\Definition\WrappingType;
+use GraphQL\Type\Definition\Type;
use InvalidArgumentException;
class ElementQueryConditionBuilder extends Component
@@ -265,11 +265,9 @@ private function _prepareTransformArguments(array $arguments): array
private function _isInsideAssetQuery(): bool
{
- if ($this->_resolveInfo->returnType instanceof WrappingType) {
- return $this->_resolveInfo->returnType->getWrappedType()->name === AssetInterface::getName();
- }
+ $namedType = Type::getNamedType($this->_resolveInfo->returnType);
- return $this->_resolveInfo->returnType->name === AssetInterface::getName();
+ return $namedType !== null && $namedType->name() === AssetInterface::getName();
}
/**
diff --git a/src/Gql/Gql.php b/src/Gql/Gql.php
index bd16753fc47..5f6435bed89 100644
--- a/src/Gql/Gql.php
+++ b/src/Gql/Gql.php
@@ -56,6 +56,7 @@
use CraftCms\Cms\Gql\Types\DateTime;
use CraftCms\Cms\Gql\Types\Mutation;
use CraftCms\Cms\Gql\Types\Number;
+use CraftCms\Cms\Gql\Types\ObjectType;
use CraftCms\Cms\Gql\Types\Query;
use CraftCms\Cms\Gql\Types\QueryArgument;
use CraftCms\Cms\ProjectConfig\Events\ConfigEvent;
@@ -181,7 +182,7 @@ public function getSchemaDef(?GqlSchema $schema = null, bool $prebuildSchema = f
$this->_registerGqlMutations();
$schemaConfig = [
- 'typeLoader' => TypeLoader::class.'::loadType',
+ 'typeLoader' => TypeLoader::loadType(...),
'query' => TypeLoader::loadType('Query'),
'mutation' => TypeLoader::loadType('Mutation'),
'directives' => $this->_loadGqlDirectives($schema),
@@ -191,13 +192,21 @@ public function getSchemaDef(?GqlSchema $schema = null, bool $prebuildSchema = f
// as the query is being resolved thanks to the magic of lazy-loading, so we needn't worry.
if (! $prebuildSchema) {
$this->_schemaDef = new Schema($schemaConfig);
+ // add default description (use schema name), or DumpSchemaCommandTest and SchemaPrinter::printSchemaDefinition() will error
+ // because of SchemaPrinter::hasDefaultRootOperationTypes($schema) and "Subscription"
+ if ($this->_schemaDef->description === null) {
+ $this->_schemaDef->description = $schema?->name;
+ }
// but we always have to add the InputObjectType mutation args
- foreach ($schemaConfig['mutation']->config['fields'] as $item) {
+ /** @var ObjectType $mutation */
+ $mutation = $schemaConfig['mutation'];
+ foreach ($mutation->config['fields'] as $item) {
if (isset($item['args'])) {
foreach ($item['args'] as $arg) {
- if ($arg instanceof InputObjectType) {
- TypeInfo::extractTypes($arg);
+ $argType = Type::getNamedType($arg->getType());
+ if ($argType instanceof InputObjectType) {
+ TypeInfo::extractTypes($argType, $arg);
}
}
}
@@ -220,6 +229,11 @@ public function getSchemaDef(?GqlSchema $schema = null, bool $prebuildSchema = f
try {
$this->_schemaDef = new Schema($schemaConfig);
$this->_schemaDef->getTypeMap();
+ // add default description (use schema name), or DumpSchemaCommandTest and SchemaPrinter::printSchemaDefinition() will error
+ // because of SchemaPrinter::hasDefaultRootOperationTypes($schema) and "Subscription"
+ if ($this->_schemaDef->description === null) {
+ $this->_schemaDef->description = $schema?->name;
+ }
} catch (Throwable $exception) {
throw new GqlException('Failed to validate the GQL Schema - '.$exception->getMessage(),
previous: $exception);
@@ -260,7 +274,7 @@ public function getValidationRules(bool $debug = false, bool $isIntrospectionQue
}
if (! $generalConfig->enableGraphqlIntrospection && Auth::guest()) {
- $validationRules[DisableIntrospection::class] = new DisableIntrospection;
+ $validationRules[DisableIntrospection::class] = new DisableIntrospection(0);
}
event($event = new DefineGqlValidationRules(
@@ -940,8 +954,9 @@ public function getContentArguments(array $contexts, string $elementType): array
}
/**
- * @param Error[] $errors
- * @return Error[]
+ * @param list