This commit is contained in:
Vladimir Razuvaev 2019-03-04 19:03:55 +07:00
commit e6e9d9ea22
34 changed files with 426 additions and 213 deletions

View File

@ -20,7 +20,7 @@ build:
tools:
external_code_coverage:
timeout: 600
timeout: 900
build_failure_conditions:
- 'elements.rating(<= C).new.exists' # No new classes/methods with a rating of C or worse allowed

View File

@ -4,12 +4,16 @@ language: php
php:
- 7.1
- 7.2
- 7.3
- 7.4snapshot
- nightly
env:
matrix:
- EXECUTOR=coroutine
- EXECUTOR= DEPENDENCIES=--prefer-lowest
- EXECUTOR=coroutine DEPENDENCIES=--prefer-lowest
- EXECUTOR=
- EXECUTOR=coroutine
cache:
@ -26,13 +30,13 @@ script: ./vendor/bin/phpunit --group default,ReactPromise
jobs:
allow_failures:
- php: 7.4snapshot
- php: nightly
include:
- stage: Test
env: DEPENDENCIES=low
install:
- travis_retry composer update --prefer-dist --prefer-lowest
- travis_retry composer update --prefer-dist {$DEPENDENCIES}
- stage: Test
env: COVERAGE
@ -40,10 +44,12 @@ jobs:
- mv ~/.phpenv/versions/$(phpenv version-name)/etc/conf.d/xdebug.ini{.disabled,}
- if [[ ! $(php -m | grep -si xdebug) ]]; then echo "xdebug required for coverage"; exit 1; fi
script:
- ./vendor/bin/phpunit --coverage-clover clover.xml
- ./vendor/bin/phpunit --coverage-php /tmp/coverage/clover_executor.cov
- EXECUTOR=coroutine ./vendor/bin/phpunit --coverage-php /tmp/coverage/clover_executor-coroutine.cov
after_script:
- wget https://scrutinizer-ci.com/ocular.phar
- php ocular.phar code-coverage:upload --format=php-clover clover.xml
- ./vendor/bin/phpcov merge /tmp/coverage --clover /tmp/clover.xml
- wget https://github.com/scrutinizer-ci/ocular/releases/download/1.5.2/ocular.phar
- php ocular.phar code-coverage:upload --format=php-clover /tmp/clover.xml
- stage: Code Quality
php: 7.1
@ -57,3 +63,4 @@ jobs:
env: STATIC_ANALYSIS
install: travis_retry composer install --prefer-dist
script: composer static-analysis

View File

@ -14,7 +14,7 @@ composer require webonyx/graphql-php
```
## Documentation
Full documentation is available on the [Documentation site](http://webonyx.github.io/graphql-php/) as well
Full documentation is available on the [Documentation site](https://webonyx.github.io/graphql-php/) as well
as in the [docs](docs/) folder of the distribution.
If you don't know what GraphQL is, visit this [official website](http://graphql.org)

View File

@ -78,6 +78,9 @@ Parser::parse($source, [ 'allowLegacySDLImplementsInterfaces' => true])
- `AbstractQuerySecurity` renamed to `QuerySecurityRule` (NS `GraphQL\Validator\Rules`)
- `FindBreakingChanges` renamed to `BreakingChangesFinder` (NS `GraphQL\Utils`)
### Breaking: new constructors
`GraphQL\Type\Definition\ResolveInfo` now takes 10 arguments instead of one array.
## Upgrade v0.11.x > v0.12.x

View File

@ -9,7 +9,7 @@
"API"
],
"require": {
"php": "^7.1",
"php": "^7.1||^8.0",
"ext-json": "*",
"ext-mbstring": "*"
},
@ -19,6 +19,7 @@
"phpstan/phpstan": "0.10.5",
"phpstan/phpstan-phpunit": "0.10.0",
"phpstan/phpstan-strict-rules": "0.10.1",
"phpunit/phpcov": "^5.0",
"phpunit/phpunit": "^7.2",
"psr/http-message": "^1.0",
"react/promise": "2.*"

View File

@ -1,22 +1,28 @@
# Integrations
* [Integration with Relay](https://github.com/ivome/graphql-relay-php)
* [Integration with Laravel 5](https://github.com/Folkloreatelier/laravel-graphql) + [Relay Helpers for Laravel](https://github.com/nuwave/laravel-graphql-relay) + [Nuwave Lighthouse](https://github.com/nuwave/lighthouse)
* [Symfony Bundle](https://github.com/overblog/GraphQLBundle) by Overblog
* Out of the box integration with any PSR-7 compatible framework (like [Slim](http://slimframework.com) or [Zend Expressive](http://zendframework.github.io/zend-expressive/)) via [Standard Server](executing-queries.md/#using-server)
* [Standard Server](executing-queries.md/#using-server) Out of the box integration with any PSR-7 compatible framework (like [Slim](http://slimframework.com) or [Zend Expressive](http://zendframework.github.io/zend-expressive/)).
* [Relay Library for graphql-php](https://github.com/ivome/graphql-relay-php) Helps construct Relay related schema definitions.
* Laravel
- [Laravel GraphQL](https://github.com/Folkloreatelier/laravel-graphql) Integration with Laravel 5
- [laravel-graphql-relay](https://github.com/nuwave/laravel-graphql-relay) Relay Helpers for Laravel
- [Lighthouse](https://github.com/nuwave/lighthouse) GraphQL Server for Laravel
* [OverblogGraphQLBundle](https://github.com/overblog/GraphQLBundle) Bundle for Symfony
* [WP-GraphQL](https://github.com/wp-graphql/wp-graphql) - GraphQL API for WordPress
# GraphQL PHP Tools
* Define types with Doctrine ORM annotations ([for PHP7.1](https://github.com/Ecodev/graphql-doctrine), for [earlier PHP versions](https://github.com/rahuljayaraman/doctrine-graphql))
* [DataLoader PHP](https://github.com/overblog/dataloader-php) - as a ready implementation for [deferred resolvers](data-fetching.md#solving-n1-problem)
* [PSR 15 compliant middleware](https://github.com/phps-cans/psr7-middleware-graphql) for the Standard Server (experimental)
* [GraphQL Uploads](https://github.com/Ecodev/graphql-upload) for the Standard Server
* [GraphQL Batch Processor](https://github.com/vasily-kartashov/graphql-batch-processing) - Simple library that provides a builder interface for defining collection, querying, filtering, and post-processing logic of batched data fetches.
* [GraphQLite](https://graphqlite.thecodingmachine.io) Define your complete schema with annotations
* [GraphQL Doctrine](https://github.com/Ecodev/graphql-doctrine) Define types with Doctrine ORM annotations
* [DataLoaderPHP](https://github.com/overblog/dataloader-php) as a ready implementation for [deferred resolvers](data-fetching.md#solving-n1-problem)
* [GraphQL Uploads](https://github.com/Ecodev/graphql-upload) A PSR-15 middleware to support file uploads in GraphQL.
* [GraphQL Batch Processor](https://github.com/vasily-kartashov/graphql-batch-processing) Provides a builder interface for defining collection, querying, filtering, and post-processing logic of batched data fetches.
* [GraphQL Utils](https://github.com/simPod/GraphQL-Utils) Objective schema definition builders (no need for arrays anymore) and `DateTime` scalar
* [PSR 15 compliant middleware](https://github.com/phps-cans/psr7-middleware-graphql) for the Standard Server _(experimental)_
# General GraphQL Tools
* [GraphiQL](https://github.com/graphql/graphiql) - An in-browser IDE for exploring GraphQL
* [GraphQL Playground](https://github.com/prismagraphql/graphql-playground) GraphQL IDE for better development workflows (GraphQL Subscriptions, interactive docs & collaboration).
* [GraphiQL](https://github.com/graphql/graphiql) An in-browser IDE for exploring GraphQL
* [ChromeiQL](https://chrome.google.com/webstore/detail/chromeiql/fkkiamalmpiidkljmicmjfbieiclmeij)
or [GraphiQL Feen](https://chrome.google.com/webstore/detail/graphiql-feen/mcbfdonlkfpbfdpimkjilhdneikhfklp) -
or [GraphiQL Feen](https://chrome.google.com/webstore/detail/graphiql-feen/mcbfdonlkfpbfdpimkjilhdneikhfklp)
GraphiQL as Google Chrome extension
* [GraphQL Playground](https://github.com/prismagraphql/graphql-playground) - GraphQL IDE for better development workflows (GraphQL Subscriptions, interactive docs & collaboration).

View File

@ -7,7 +7,7 @@
# About GraphQL
GraphQL is a modern way to build HTTP APIs consumed by the web and mobile clients.
It is intended to be a replacement for REST and SOAP APIs (even for **existing applications**).
It is intended to be an alternative to REST and SOAP APIs (even for **existing applications**).
GraphQL itself is a [specification](https://github.com/facebook/graphql) designed by Facebook
engineers. Various implementations of this specification were written

View File

@ -11,7 +11,7 @@ use Throwable;
class Deferred
{
/** @var SplQueue */
/** @var SplQueue|null */
private static $queue;
/** @var callable */
@ -20,21 +20,6 @@ class Deferred
/** @var SyncPromise */
public $promise;
public static function getQueue()
{
return self::$queue ?: self::$queue = new SplQueue();
}
public static function runQueue()
{
$q = self::$queue;
while ($q && ! $q->isEmpty()) {
/** @var self $dfd */
$dfd = $q->dequeue();
$dfd->run();
}
}
public function __construct(callable $callback)
{
$this->callback = $callback;
@ -42,6 +27,25 @@ class Deferred
self::getQueue()->enqueue($this);
}
public static function getQueue() : SplQueue
{
if (self::$queue === null) {
self::$queue = new SplQueue();
}
return self::$queue;
}
public static function runQueue() : void
{
$queue = self::getQueue();
while (! $queue->isEmpty()) {
/** @var self $dequeuedNodeValue */
$dequeuedNodeValue = $queue->dequeue();
$dequeuedNodeValue->run();
}
}
public function then($onFulfilled = null, $onRejected = null)
{
return $this->promise->then($onFulfilled, $onRejected);

View File

@ -74,7 +74,7 @@ class Executor
* execution are collected in `$result->errors`.
*
* @param mixed|null $rootValue
* @param mixed[]|null $contextValue
* @param mixed|null $contextValue
* @param mixed[]|ArrayAccess|null $variableValues
* @param string|null $operationName
*
@ -119,8 +119,8 @@ class Executor
*
* Useful for async PHP platforms.
*
* @param mixed[]|null $rootValue
* @param mixed[]|null $contextValue
* @param mixed|null $rootValue
* @param mixed|null $contextValue
* @param mixed[]|null $variableValues
* @param string|null $operationName
*
@ -161,9 +161,9 @@ class Executor
* and returns it as the result, or if it's a function, returns the result
* of calling that function while passing along args and context.
*
* @param mixed $source
* @param mixed[] $args
* @param mixed[]|null $context
* @param mixed $source
* @param mixed[] $args
* @param mixed|null $context
*
* @return mixed|null
*/

View File

@ -115,8 +115,8 @@ class ReferenceExecutor implements ExecutorImplementation
* Constructs an ExecutionContext object from the arguments passed to
* execute, which we will pass throughout the other execution methods.
*
* @param mixed[] $rootValue
* @param mixed[] $contextValue
* @param mixed $rootValue
* @param mixed $contextValue
* @param mixed[]|Traversable $rawVariableValues
* @param string|null $operationName
*
@ -153,7 +153,7 @@ class ReferenceExecutor implements ExecutorImplementation
break;
}
}
if (! $operation) {
if ($operation === null) {
if ($operationName) {
$errors[] = new Error(sprintf('Unknown operation named "%s".', $operationName));
} else {
@ -165,7 +165,7 @@ class ReferenceExecutor implements ExecutorImplementation
);
}
$variableValues = null;
if ($operation) {
if ($operation !== null) {
[$coercionErrors, $coercedVariableValues] = Values::getVariableValues(
$schema,
$operation->variableDefinitions ?: [],

View File

@ -152,8 +152,8 @@ class CoroutineExecutor implements Runtime, ExecutorImplementation
if (is_array($value)) {
$array = [];
foreach ($value as $item) {
$array[] = self::resultToArray($item);
foreach ($value as $key => $item) {
$array[$key] = self::resultToArray($item);
}
return $array;
}

View File

@ -40,7 +40,7 @@ abstract class Node
public $loc;
/**
* @param (string|NameNode|NodeList|SelectionSetNode|Location|null)[] $vars
* @param (NameNode|NodeList|SelectionSetNode|Location|string|int|bool|float|null)[] $vars
*/
public function __construct(array $vars)
{

View File

@ -46,6 +46,12 @@ class OperationParams
*/
public $variables;
/**
* @api
* @var mixed[]|null
*/
public $extensions;
/** @var mixed[] */
private $originalInput;
@ -76,24 +82,38 @@ class OperationParams
'id' => null, // alias to queryid
'operationname' => null,
'variables' => null,
'extensions' => null,
];
if ($params['variables'] === '') {
$params['variables'] = null;
}
if (is_string($params['variables'])) {
$tmp = json_decode($params['variables'], true);
if (! json_last_error()) {
$params['variables'] = $tmp;
// Some parameters could be provided as serialized JSON.
foreach (['extensions', 'variables'] as $param) {
if (! is_string($params[$param])) {
continue;
}
$tmp = json_decode($params[$param], true);
if (json_last_error()) {
continue;
}
$params[$param] = $tmp;
}
$instance->query = $params['query'];
$instance->queryId = $params['queryid'] ?: $params['documentid'] ?: $params['id'];
$instance->operation = $params['operationname'];
$instance->variables = $params['variables'];
$instance->readOnly = (bool) $readonly;
$instance->query = $params['query'];
$instance->queryId = $params['queryid'] ?: $params['documentid'] ?: $params['id'];
$instance->operation = $params['operationname'];
$instance->variables = $params['variables'];
$instance->extensions = $params['extensions'];
$instance->readOnly = (bool) $readonly;
// Apollo server/client compatibility: look for the queryid in extensions
if (isset($instance->extensions['persistedQuery']['sha256Hash']) && empty($instance->query) && empty($instance->queryId)) {
$instance->queryId = $instance->extensions['persistedQuery']['sha256Hash'];
}
return $instance;
}

View File

@ -22,7 +22,7 @@ use function sprintf;
/**
* Class EnumType
*/
class EnumType extends Type implements InputType, OutputType, LeafType, NamedType
class EnumType extends Type implements InputType, OutputType, LeafType, NullableType, NamedType
{
/** @var EnumTypeDefinitionNode|null */
public $astNode;

View File

@ -18,7 +18,7 @@ use function sprintf;
/**
* Class InputObjectType
*/
class InputObjectType extends Type implements InputType, NamedType
class InputObjectType extends Type implements InputType, NullableType, NamedType
{
/** @var InputObjectTypeDefinitionNode|null */
public $astNode;

View File

@ -15,7 +15,7 @@ use function sprintf;
/**
* Class InterfaceType
*/
class InterfaceType extends Type implements AbstractType, OutputType, CompositeType, NamedType
class InterfaceType extends Type implements AbstractType, OutputType, CompositeType, NullableType, NamedType
{
/** @var InterfaceTypeDefinitionNode|null */
public $astNode;
@ -107,7 +107,7 @@ class InterfaceType extends Type implements AbstractType, OutputType, CompositeT
* @param object $objectValue
* @param mixed[] $context
*
* @return callable|null
* @return Type|null
*/
public function resolveType($objectValue, $context, ResolveInfo $info)
{

View File

@ -7,7 +7,7 @@ namespace GraphQL\Type\Definition;
/**
* Class ListOfType
*/
class ListOfType extends Type implements WrappingType, OutputType, InputType
class ListOfType extends Type implements WrappingType, OutputType, NullableType, InputType
{
/** @var ObjectType|InterfaceType|UnionType|ScalarType|InputObjectType|EnumType */
public $ofType;

View File

@ -4,8 +4,6 @@ declare(strict_types=1);
namespace GraphQL\Type\Definition;
use Exception;
use GraphQL\Error\InvariantViolation;
use GraphQL\Utils\Utils;
/**
@ -13,13 +11,11 @@ use GraphQL\Utils\Utils;
*/
class NonNull extends Type implements WrappingType, OutputType, InputType
{
/** @var ObjectType|InterfaceType|UnionType|ScalarType|InputObjectType|EnumType|ListOfType */
/** @var NullableType */
private $ofType;
/**
* @param callable|Type $type
*
* @throws Exception
* @param NullableType $type
*/
public function __construct($type)
{
@ -29,7 +25,7 @@ class NonNull extends Type implements WrappingType, OutputType, InputType
/**
* @param mixed $type
*
* @return ObjectType|InterfaceType|UnionType|ScalarType|InputObjectType|EnumType|ListOfType
* @return NullableType
*/
public static function assertNullableType($type)
{
@ -67,9 +63,7 @@ class NonNull extends Type implements WrappingType, OutputType, InputType
/**
* @param bool $recurse
*
* @return ObjectType|InterfaceType|UnionType|ScalarType|InputObjectType|EnumType|ListOfType
*
* @throws InvariantViolation
* @return Type
*/
public function getWrappedType($recurse = false)
{

View File

@ -0,0 +1,20 @@
<?php
declare(strict_types=1);
namespace GraphQL\Type\Definition;
/*
export type GraphQLNullableType =
| GraphQLScalarType
| GraphQLObjectType
| GraphQLInterfaceType
| GraphQLUnionType
| GraphQLEnumType
| GraphQLInputObjectType
| GraphQLList<any>;
*/
interface NullableType
{
}

View File

@ -54,7 +54,7 @@ use function sprintf;
* }
* ]);
*/
class ObjectType extends Type implements OutputType, CompositeType, NamedType
class ObjectType extends Type implements OutputType, CompositeType, NullableType, NamedType
{
/** @var ObjectTypeDefinitionNode|null */
public $astNode;

View File

@ -23,7 +23,7 @@ class ResolveInfo
* The name of the field being resolved
*
* @api
* @var string|null
* @var string
*/
public $fieldName;
@ -31,9 +31,9 @@ class ResolveInfo
* AST of all nodes referencing this field in the query.
*
* @api
* @var FieldNode[]|null
* @var FieldNode[]
*/
public $fieldNodes;
public $fieldNodes = [];
/**
* Expected return type of the field being resolved
@ -47,7 +47,7 @@ class ResolveInfo
* Parent type of the field being resolved
*
* @api
* @var ObjectType|null
* @var ObjectType
*/
public $parentType;
@ -55,7 +55,7 @@ class ResolveInfo
* Path to this field from the very root value
*
* @api
* @var string[]
* @var string[][]
*/
public $path;
@ -71,9 +71,9 @@ class ResolveInfo
* AST of all fragments defined in query
*
* @api
* @var FragmentDefinitionNode[]|null
* @var FragmentDefinitionNode[]
*/
public $fragments;
public $fragments = [];
/**
* Root value passed to query execution
@ -95,21 +95,29 @@ class ResolveInfo
* Array of variables passed to query execution
*
* @api
* @var mixed[]|null
* @var mixed[]
*/
public $variableValues;
public $variableValues = [];
/**
* @param FieldNode[] $fieldNodes
* @param ScalarType|ObjectType|InterfaceType|UnionType|EnumType|ListOfType|NonNull $returnType
* @param string[][] $path
* @param FragmentDefinitionNode[] $fragments
* @param mixed|null $rootValue
* @param mixed[] $variableValues
*/
public function __construct(
string $fieldName,
$fieldNodes,
$returnType,
ObjectType $parentType,
$path,
array $path,
Schema $schema,
$fragments,
array $fragments,
$rootValue,
?OperationDefinitionNode $operation,
$variableValues
array $variableValues
) {
$this->fieldName = $fieldName;
$this->fieldNodes = $fieldNodes;

View File

@ -27,7 +27,7 @@ use function is_string;
* }
* }
*/
abstract class ScalarType extends Type implements OutputType, InputType, LeafType, NamedType
abstract class ScalarType extends Type implements OutputType, InputType, LeafType, NullableType, NamedType
{
/** @var ScalarTypeDefinitionNode|null */
public $astNode;

View File

@ -137,7 +137,7 @@ abstract class Type implements JsonSerializable
}
/**
* @param ObjectType|InterfaceType|UnionType|ScalarType|InputObjectType|EnumType|ListOfType $wrappedType
* @param NullableType $wrappedType
*
* @return NonNull
*
@ -338,7 +338,7 @@ abstract class Type implements JsonSerializable
/**
* @param Type $type
*
* @return ObjectType|InterfaceType|UnionType|ScalarType|InputObjectType|EnumType|ListOfType
* @return NullableType
*
* @api
*/

View File

@ -17,7 +17,7 @@ use function sprintf;
/**
* Class UnionType
*/
class UnionType extends Type implements AbstractType, OutputType, CompositeType, NamedType
class UnionType extends Type implements AbstractType, OutputType, CompositeType, NullableType, NamedType
{
/** @var UnionTypeDefinitionNode */
public $astNode;

View File

@ -112,27 +112,31 @@ class BreakingChangesFinder
* @return string[][]
*/
public static function findTypesThatChangedKind(
Schema $oldSchema,
Schema $newSchema
) {
$oldTypeMap = $oldSchema->getTypeMap();
$newTypeMap = $newSchema->getTypeMap();
Schema $schemaA,
Schema $schemaB
) : iterable {
$schemaATypeMap = $schemaA->getTypeMap();
$schemaBTypeMap = $schemaB->getTypeMap();
$breakingChanges = [];
foreach ($oldTypeMap as $typeName => $oldType) {
if (! isset($newTypeMap[$typeName])) {
foreach ($schemaATypeMap as $typeName => $schemaAType) {
if (! isset($schemaBTypeMap[$typeName])) {
continue;
}
$newType = $newTypeMap[$typeName];
if ($oldType instanceof $newType) {
$schemaBType = $schemaBTypeMap[$typeName];
if ($schemaAType instanceof $schemaBType) {
continue;
}
$oldTypeKindName = self::typeKindName($oldType);
$newTypeKindName = self::typeKindName($newType);
$breakingChanges[] = [
if ($schemaBType instanceof $schemaAType) {
continue;
}
$schemaATypeKindName = self::typeKindName($schemaAType);
$schemaBTypeKindName = self::typeKindName($schemaBType);
$breakingChanges[] = [
'type' => self::BREAKING_CHANGE_TYPE_CHANGED_KIND,
'description' => "${typeName} changed from ${oldTypeKindName} to ${newTypeKindName}.",
'description' => "${typeName} changed from ${schemaATypeKindName} to ${schemaBTypeKindName}.",
];
}

View File

@ -513,7 +513,14 @@ class SchemaExtender
$schemaExtensions[] = $def;
} elseif ($def instanceof TypeDefinitionNode) {
$typeName = isset($def->name) ? $def->name->value : null;
if ($schema->getType($typeName)) {
try {
$type = $schema->getType($typeName);
} catch (Error $error) {
$type = null;
}
if ($type) {
throw new Error('Type "' . $typeName . '" already exists in the schema. It cannot also be defined in this type definition.', [$def]);
}
$typeDefinitionMap[$typeName] = $def;

View File

@ -8,6 +8,7 @@ use Exception;
use GraphQL\Error\Error;
use GraphQL\Language\AST\BooleanValueNode;
use GraphQL\Language\AST\EnumValueNode;
use GraphQL\Language\AST\FieldNode;
use GraphQL\Language\AST\FloatValueNode;
use GraphQL\Language\AST\IntValueNode;
use GraphQL\Language\AST\ListValueNode;
@ -21,6 +22,7 @@ use GraphQL\Language\Printer;
use GraphQL\Language\Visitor;
use GraphQL\Type\Definition\EnumType;
use GraphQL\Type\Definition\EnumValueDefinition;
use GraphQL\Type\Definition\FieldArgument;
use GraphQL\Type\Definition\InputObjectType;
use GraphQL\Type\Definition\ListOfType;
use GraphQL\Type\Definition\NonNull;
@ -46,8 +48,14 @@ class ValuesOfCorrectType extends ValidationRule
{
public function getVisitor(ValidationContext $context)
{
$fieldName = '';
return [
NodeKind::NULL => static function (NullValueNode $node) use ($context) {
NodeKind::FIELD => [
'enter' => static function (FieldNode $node) use (&$fieldName) {
$fieldName = $node->name->value;
},
],
NodeKind::NULL => static function (NullValueNode $node) use ($context, &$fieldName) {
$type = $context->getInputType();
if (! ($type instanceof NonNull)) {
return;
@ -55,30 +63,31 @@ class ValuesOfCorrectType extends ValidationRule
$context->reportError(
new Error(
self::badValueMessage((string) $type, Printer::doPrint($node)),
self::getBadValueMessage((string) $type, Printer::doPrint($node), null, $context, $fieldName),
$node
)
);
},
NodeKind::LST => function (ListValueNode $node) use ($context) {
NodeKind::LST => function (ListValueNode $node) use ($context, &$fieldName) {
// Note: TypeInfo will traverse into a list's item type, so look to the
// parent input type to check if it is a list.
$type = Type::getNullableType($context->getParentInputType());
if (! $type instanceof ListOfType) {
$this->isValidScalar($context, $node);
$this->isValidScalar($context, $node, $fieldName);
return Visitor::skipNode();
}
},
NodeKind::OBJECT => function (ObjectValueNode $node) use ($context) {
NodeKind::OBJECT => function (ObjectValueNode $node) use ($context, &$fieldName) {
// Note: TypeInfo will traverse into a list's item type, so look to the
// parent input type to check if it is a list.
$type = Type::getNamedType($context->getInputType());
if (! $type instanceof InputObjectType) {
$this->isValidScalar($context, $node);
$this->isValidScalar($context, $node, $fieldName);
return Visitor::skipNode();
}
unset($fieldName);
// Ensure every required field exists.
$inputFields = $type->getFields();
$nodeFields = iterator_to_array($node->fields);
@ -127,34 +136,36 @@ class ValuesOfCorrectType extends ValidationRule
)
);
},
NodeKind::ENUM => function (EnumValueNode $node) use ($context) {
NodeKind::ENUM => function (EnumValueNode $node) use ($context, &$fieldName) {
$type = Type::getNamedType($context->getInputType());
if (! $type instanceof EnumType) {
$this->isValidScalar($context, $node);
$this->isValidScalar($context, $node, $fieldName);
} elseif (! $type->getValue($node->value)) {
$context->reportError(
new Error(
self::badValueMessage(
self::getBadValueMessage(
$type->name,
Printer::doPrint($node),
$this->enumTypeSuggestion($type, $node)
$this->enumTypeSuggestion($type, $node),
$context,
$fieldName
),
$node
)
);
}
},
NodeKind::INT => function (IntValueNode $node) use ($context) {
$this->isValidScalar($context, $node);
NodeKind::INT => function (IntValueNode $node) use ($context, &$fieldName) {
$this->isValidScalar($context, $node, $fieldName);
},
NodeKind::FLOAT => function (FloatValueNode $node) use ($context) {
$this->isValidScalar($context, $node);
NodeKind::FLOAT => function (FloatValueNode $node) use ($context, &$fieldName) {
$this->isValidScalar($context, $node, $fieldName);
},
NodeKind::STRING => function (StringValueNode $node) use ($context) {
$this->isValidScalar($context, $node);
NodeKind::STRING => function (StringValueNode $node) use ($context, &$fieldName) {
$this->isValidScalar($context, $node, $fieldName);
},
NodeKind::BOOLEAN => function (BooleanValueNode $node) use ($context) {
$this->isValidScalar($context, $node);
NodeKind::BOOLEAN => function (BooleanValueNode $node) use ($context, &$fieldName) {
$this->isValidScalar($context, $node, $fieldName);
},
];
}
@ -165,7 +176,7 @@ class ValuesOfCorrectType extends ValidationRule
($message ? "; ${message}" : '.');
}
private function isValidScalar(ValidationContext $context, ValueNode $node)
private function isValidScalar(ValidationContext $context, ValueNode $node, $fieldName)
{
// Report any error at the full type expected by the location.
$locationType = $context->getInputType();
@ -179,10 +190,12 @@ class ValuesOfCorrectType extends ValidationRule
if (! $type instanceof ScalarType) {
$context->reportError(
new Error(
self::badValueMessage(
self::getBadValueMessage(
(string) $locationType,
Printer::doPrint($node),
$this->enumTypeSuggestion($type, $node)
$this->enumTypeSuggestion($type, $node),
$context,
$fieldName
),
$node
)
@ -199,32 +212,28 @@ class ValuesOfCorrectType extends ValidationRule
// Ensure a reference to the original error is maintained.
$context->reportError(
new Error(
self::badValueMessage(
self::getBadValueMessage(
(string) $locationType,
Printer::doPrint($node),
$error->getMessage()
$error->getMessage(),
$context,
$fieldName
),
$node,
null,
null,
null,
$error
$node
)
);
} catch (Throwable $error) {
// Ensure a reference to the original error is maintained.
$context->reportError(
new Error(
self::badValueMessage(
self::getBadValueMessage(
(string) $locationType,
Printer::doPrint($node),
$error->getMessage()
$error->getMessage(),
$context,
$fieldName
),
$node,
null,
null,
null,
$error
$node
)
);
}
@ -247,6 +256,12 @@ class ValuesOfCorrectType extends ValidationRule
}
}
public static function badArgumentValueMessage($typeName, $valueName, $fieldName, $argName, $message = null)
{
return sprintf('Field "%s" argument "%s" requires type %s, found %s', $fieldName, $argName, $typeName, $valueName) .
($message ? sprintf('; %s', $message) : '.');
}
public static function requiredFieldMessage($typeName, $fieldName, $fieldTypeName)
{
return sprintf('Field %s.%s of required type %s was not provided.', $typeName, $fieldName, $fieldTypeName);
@ -257,4 +272,15 @@ class ValuesOfCorrectType extends ValidationRule
return sprintf('Field "%s" is not defined by type %s', $fieldName, $typeName) .
($message ? sprintf('; %s', $message) : '.');
}
private static function getBadValueMessage($typeName, $valueName, $message = null, $context = null, $fieldName = null)
{
if ($context) {
$arg = $context->getArgument();
if ($arg) {
return self::badArgumentValueMessage($typeName, $valueName, $fieldName, $arg->name, $message);
}
}
return self::badValueMessage($typeName, $valueName, $message);
}
}

View File

@ -6,6 +6,7 @@ namespace GraphQL\Tests\Executor;
use GraphQL\Executor\Executor;
use GraphQL\Language\Parser;
use GraphQL\Type\Definition\CustomScalarType;
use GraphQL\Type\Definition\ObjectType;
use GraphQL\Type\Definition\Type;
use GraphQL\Type\Schema;
@ -20,6 +21,13 @@ class ExecutorSchemaTest extends TestCase
*/
public function testExecutesUsingASchema() : void
{
$BlogSerializableValueType = new CustomScalarType([
'name' => 'JsonSerializableValueScalar',
'serialize' => static function ($value) {
return $value;
},
]);
$BlogArticle = null;
$BlogImage = new ObjectType([
'name' => 'Image',
@ -57,6 +65,7 @@ class ExecutorSchemaTest extends TestCase
'title' => ['type' => Type::string()],
'body' => ['type' => Type::string()],
'keywords' => ['type' => Type::listOf(Type::string())],
'meta' => ['type' => $BlogSerializableValueType],
],
]);
@ -113,6 +122,7 @@ class ExecutorSchemaTest extends TestCase
keywords
}
}
meta
}
}
@ -191,6 +201,7 @@ class ExecutorSchemaTest extends TestCase
'keywords' => ['foo', 'bar', '1', 'true', null],
],
],
'meta' => [ 'title' => 'My Article 1 | My Blog' ],
],
],
];
@ -210,6 +221,7 @@ class ExecutorSchemaTest extends TestCase
'body' => 'This is a post',
'hidden' => 'This data is not exposed in the schema',
'keywords' => ['foo', 'bar', 1, true, null],
'meta' => ['title' => 'My Article 1 | My Blog'],
];
};

View File

@ -25,7 +25,7 @@ class RequestParsingTest extends TestCase
];
foreach ($parsed as $source => $parsedBody) {
self::assertValidOperationParams($parsedBody, $query, null, null, null, $source);
self::assertValidOperationParams($parsedBody, $query, null, null, null, null, $source);
self::assertFalse($parsedBody->isReadOnly(), $source);
}
}
@ -91,6 +91,7 @@ class RequestParsingTest extends TestCase
$queryId = null,
$variables = null,
$operation = null,
$extensions = null,
$message = ''
) {
self::assertInstanceOf(OperationParams::class, $params, $message);
@ -99,6 +100,7 @@ class RequestParsingTest extends TestCase
self::assertSame($queryId, $params->queryId, $message);
self::assertSame($variables, $params->variables, $message);
self::assertSame($operation, $params->operation, $message);
self::assertSame($extensions, $params->extensions, $message);
}
public function testParsesUrlencodedRequest() : void
@ -118,7 +120,7 @@ class RequestParsingTest extends TestCase
];
foreach ($parsed as $method => $parsedBody) {
self::assertValidOperationParams($parsedBody, $query, null, $variables, $operation, $method);
self::assertValidOperationParams($parsedBody, $query, null, $variables, $operation, null, $method);
self::assertFalse($parsedBody->isReadOnly(), $method);
}
}
@ -175,7 +177,7 @@ class RequestParsingTest extends TestCase
];
foreach ($parsed as $method => $parsedBody) {
self::assertValidOperationParams($parsedBody, $query, null, $variables, $operation, $method);
self::assertValidOperationParams($parsedBody, $query, null, $variables, $operation, null, $method);
self::assertTrue($parsedBody->isReadonly(), $method);
}
}
@ -230,7 +232,7 @@ class RequestParsingTest extends TestCase
];
foreach ($parsed as $method => $parsedBody) {
self::assertValidOperationParams($parsedBody, $query, null, $variables, $operation, $method);
self::assertValidOperationParams($parsedBody, $query, null, $variables, $operation, null, $method);
self::assertFalse($parsedBody->isReadOnly(), $method);
}
}
@ -286,19 +288,21 @@ class RequestParsingTest extends TestCase
'psr' => $this->parsePsrRequest('application/json', json_encode($body)),
];
foreach ($parsed as $method => $parsedBody) {
self::assertValidOperationParams($parsedBody, $query, null, $variables, $operation, $method);
self::assertValidOperationParams($parsedBody, $query, null, $variables, $operation, null, $method);
self::assertFalse($parsedBody->isReadOnly(), $method);
}
}
public function testParsesVariablesAsJSON() : void
public function testParsesParamsAsJSON() : void
{
$query = '{my query}';
$variables = ['test' => 1, 'test2' => 2];
$operation = 'op';
$query = '{my query}';
$variables = ['test1' => 1, 'test2' => 2];
$extensions = ['test3' => 3, 'test4' => 4];
$operation = 'op';
$body = [
'query' => $query,
'extensions' => json_encode($extensions),
'variables' => json_encode($variables),
'operationName' => $operation,
];
@ -307,7 +311,7 @@ class RequestParsingTest extends TestCase
'psr' => $this->parsePsrRequest('application/json', json_encode($body)),
];
foreach ($parsed as $method => $parsedBody) {
self::assertValidOperationParams($parsedBody, $query, null, $variables, $operation, $method);
self::assertValidOperationParams($parsedBody, $query, null, $variables, $operation, $extensions, $method);
self::assertFalse($parsedBody->isReadOnly(), $method);
}
}
@ -328,7 +332,29 @@ class RequestParsingTest extends TestCase
'psr' => $this->parsePsrRequest('application/json', json_encode($body)),
];
foreach ($parsed as $method => $parsedBody) {
self::assertValidOperationParams($parsedBody, $query, null, $variables, $operation, $method);
self::assertValidOperationParams($parsedBody, $query, null, $variables, $operation, null, $method);
self::assertFalse($parsedBody->isReadOnly(), $method);
}
}
public function testParsesApolloPersistedQueryJSONRequest() : void
{
$queryId = 'my-query-id';
$extensions = ['persistedQuery' => ['sha256Hash' => $queryId]];
$variables = ['test' => 1, 'test2' => 2];
$operation = 'op';
$body = [
'extensions' => $extensions,
'variables' => $variables,
'operationName' => $operation,
];
$parsed = [
'raw' => $this->parseRawRequest('application/json', json_encode($body)),
'psr' => $this->parsePsrRequest('application/json', json_encode($body)),
];
foreach ($parsed as $method => $parsedBody) {
self::assertValidOperationParams($parsedBody, null, $queryId, $variables, $operation, $extensions, $method);
self::assertFalse($parsedBody->isReadOnly(), $method);
}
}
@ -360,6 +386,7 @@ class RequestParsingTest extends TestCase
null,
$body[0]['variables'],
$body[0]['operationName'],
null,
$method
);
self::assertValidOperationParams(
@ -368,6 +395,7 @@ class RequestParsingTest extends TestCase
$body[1]['queryId'],
$body[1]['variables'],
$body[1]['operationName'],
null,
$method
);
}

View File

@ -231,7 +231,7 @@ class EnumTypeTest extends TestCase
'{ colorEnum(fromEnum: "GREEN") }',
null,
[
'message' => 'Expected type Color, found "GREEN"; Did you mean the enum value GREEN?',
'message' => 'Field "colorEnum" argument "fromEnum" requires type Color, found "GREEN"; Did you mean the enum value GREEN?',
'locations' => [new SourceLocation(1, 23)],
]
);
@ -268,7 +268,7 @@ class EnumTypeTest extends TestCase
'{ colorEnum(fromEnum: GREENISH) }',
null,
[
'message' => 'Expected type Color, found GREENISH; Did you mean the enum value GREEN?',
'message' => 'Field "colorEnum" argument "fromEnum" requires type Color, found GREENISH; Did you mean the enum value GREEN?',
'locations' => [new SourceLocation(1, 23)],
]
);
@ -283,7 +283,7 @@ class EnumTypeTest extends TestCase
'{ colorEnum(fromEnum: green) }',
null,
[
'message' => 'Expected type Color, found green; Did you mean the enum value GREEN?',
'message' => 'Field "colorEnum" argument "fromEnum" requires type Color, found green; Did you mean the enum value GREEN?',
'locations' => [new SourceLocation(1, 23)],
]
);
@ -313,7 +313,7 @@ class EnumTypeTest extends TestCase
$this->expectFailure(
'{ colorEnum(fromEnum: 1) }',
null,
'Expected type Color, found 1.'
'Field "colorEnum" argument "fromEnum" requires type Color, found 1.'
);
}
@ -325,7 +325,7 @@ class EnumTypeTest extends TestCase
$this->expectFailure(
'{ colorEnum(fromInt: GREEN) }',
null,
'Expected type Int, found GREEN.'
'Field "colorEnum" argument "fromInt" requires type Int, found GREEN.'
);
}

View File

@ -125,6 +125,47 @@ class BreakingChangesFinderTest extends TestCase
);
}
/**
* We need to compare type of class A (old type) and type of class B (new type)
* Class B extends A but are evaluated as same types (if all properties match).
* The reason is that when constructing schema from remote schema,
* we have no certain way to get information about our classes.
* Thus object types from remote schema are constructed as Object Type
* while their local counterparts are usually a subclass of Object Type.
*
* @see https://github.com/webonyx/graphql-php/pull/431
*/
public function testShouldNotMarkTypesWithInheritedClassesAsChanged() : void
{
$objectTypeConstructedFromRemoteSchema = new ObjectType([
'name' => 'ObjectType',
'fields' => [
'field1' => ['type' => Type::string()],
],
]);
$localObjectType = new class([
'name' => 'ObjectType',
'fields' => [
'field1' => ['type' => Type::string()],
],
]) extends ObjectType{
};
$schemaA = new Schema([
'query' => $this->queryType,
'types' => [$objectTypeConstructedFromRemoteSchema],
]);
$schemaB = new Schema([
'query' => $this->queryType,
'types' => [$localObjectType],
]);
self::assertEmpty(BreakingChangesFinder::findTypesThatChangedKind($schemaA, $schemaB));
self::assertEmpty(BreakingChangesFinder::findTypesThatChangedKind($schemaB, $schemaA));
}
/**
* @see it('should detect if a field on a type was deleted or changed type')
*/

View File

@ -24,6 +24,7 @@ use GraphQL\Type\Definition\ScalarType;
use GraphQL\Type\Definition\Type;
use GraphQL\Type\Definition\UnionType;
use GraphQL\Type\Schema;
use GraphQL\Utils\BuildSchema;
use GraphQL\Utils\SchemaExtender;
use GraphQL\Utils\SchemaPrinter;
use PHPUnit\Framework\TestCase;
@ -1962,4 +1963,48 @@ extend type Query {
$result = GraphQL::executeQuery($extendedSchema, $query);
self::assertSame(['data' => ['hello' => 'Hello World!']], $result->toArray());
}
/**
* @see https://github.com/webonyx/graphql-php/issues/180
*/
public function testShouldBeAbleToIntroduceNewTypesThroughExtension()
{
$sdl = '
type Query {
defaultValue: String
}
type Foo {
value: Int
}
';
$documentNode = Parser::parse($sdl);
$schema = BuildSchema::build($documentNode);
$extensionSdl = '
type Bar {
foo: Foo
}
';
$extendedDocumentNode = Parser::parse($extensionSdl);
$extendedSchema = SchemaExtender::extend($schema, $extendedDocumentNode);
$expected = '
type Bar {
foo: Foo
}
type Foo {
value: Int
}
type Query {
defaultValue: String
}
';
static::assertEquals($this->dedent($expected), SchemaPrinter::doPrint($extendedSchema));
}
}

View File

@ -38,7 +38,7 @@ class ValidationTest extends ValidatorTestCase
';
$expectedError = [
'message' => 'Expected type Invalid, found "bad value"; Invalid scalar is always invalid: bad value',
'message' => 'Field "invalidArg" argument "arg" requires type Invalid, found "bad value"; Invalid scalar is always invalid: bad value',
'locations' => [['line' => 3, 'column' => 25]],
];

View File

@ -240,7 +240,7 @@ class ValuesOfCorrectTypeTest extends ValidatorTestCase
}
',
[
$this->badValue('String', '1', 4, 39),
$this->badValueWithMessage('Field "stringArgField" argument "stringArg" requires type String, found 1.', 4, 39),
]
);
}
@ -257,6 +257,11 @@ class ValuesOfCorrectTypeTest extends ValidatorTestCase
);
}
private function badValueWithMessage($message, $line, $column)
{
return FormattedError::create($message, [new SourceLocation($line, $column)]);
}
/**
* @see it('Float into String')
*/
@ -272,7 +277,7 @@ class ValuesOfCorrectTypeTest extends ValidatorTestCase
}
',
[
$this->badValue('String', '1.0', 4, 39),
$this->badValueWithMessage('Field "stringArgField" argument "stringArg" requires type String, found 1.0.', 4, 39),
]
);
}
@ -294,7 +299,7 @@ class ValuesOfCorrectTypeTest extends ValidatorTestCase
}
',
[
$this->badValue('String', 'true', 4, 39),
$this->badValueWithMessage('Field "stringArgField" argument "stringArg" requires type String, found true.', 4, 39),
]
);
}
@ -314,7 +319,7 @@ class ValuesOfCorrectTypeTest extends ValidatorTestCase
}
',
[
$this->badValue('String', 'BAR', 4, 39),
$this->badValueWithMessage('Field "stringArgField" argument "stringArg" requires type String, found BAR.', 4, 39),
]
);
}
@ -334,7 +339,7 @@ class ValuesOfCorrectTypeTest extends ValidatorTestCase
}
',
[
$this->badValue('Int', '"3"', 4, 33),
$this->badValueWithMessage('Field "intArgField" argument "intArg" requires type Int, found "3".', 4, 33),
]
);
}
@ -354,7 +359,7 @@ class ValuesOfCorrectTypeTest extends ValidatorTestCase
}
',
[
$this->badValue('Int', '829384293849283498239482938', 4, 33),
$this->badValueWithMessage('Field "intArgField" argument "intArg" requires type Int, found 829384293849283498239482938.', 4, 33),
]
);
}
@ -376,7 +381,7 @@ class ValuesOfCorrectTypeTest extends ValidatorTestCase
}
',
[
$this->badValue('Int', 'FOO', 4, 33),
$this->badValueWithMessage('Field "intArgField" argument "intArg" requires type Int, found FOO.', 4, 33),
]
);
}
@ -396,7 +401,7 @@ class ValuesOfCorrectTypeTest extends ValidatorTestCase
}
',
[
$this->badValue('Int', '3.0', 4, 33),
$this->badValueWithMessage('Field "intArgField" argument "intArg" requires type Int, found 3.0.', 4, 33),
]
);
}
@ -416,7 +421,7 @@ class ValuesOfCorrectTypeTest extends ValidatorTestCase
}
',
[
$this->badValue('Int', '3.333', 4, 33),
$this->badValueWithMessage('Field "intArgField" argument "intArg" requires type Int, found 3.333.', 4, 33),
]
);
}
@ -436,7 +441,7 @@ class ValuesOfCorrectTypeTest extends ValidatorTestCase
}
',
[
$this->badValue('Float', '"3.333"', 4, 37),
$this->badValueWithMessage('Field "floatArgField" argument "floatArg" requires type Float, found "3.333".', 4, 37),
]
);
}
@ -456,7 +461,7 @@ class ValuesOfCorrectTypeTest extends ValidatorTestCase
}
',
[
$this->badValue('Float', 'true', 4, 37),
$this->badValueWithMessage('Field "floatArgField" argument "floatArg" requires type Float, found true.', 4, 37),
]
);
}
@ -478,7 +483,7 @@ class ValuesOfCorrectTypeTest extends ValidatorTestCase
}
',
[
$this->badValue('Float', 'FOO', 4, 37),
$this->badValueWithMessage('Field "floatArgField" argument "floatArg" requires type Float, found FOO.', 4, 37),
]
);
}
@ -498,7 +503,7 @@ class ValuesOfCorrectTypeTest extends ValidatorTestCase
}
',
[
$this->badValue('Boolean', '2', 4, 41),
$this->badValueWithMessage('Field "booleanArgField" argument "booleanArg" requires type Boolean, found 2.', 4, 41),
]
);
}
@ -518,7 +523,7 @@ class ValuesOfCorrectTypeTest extends ValidatorTestCase
}
',
[
$this->badValue('Boolean', '1.0', 4, 41),
$this->badValueWithMessage('Field "booleanArgField" argument "booleanArg" requires type Boolean, found 1.0.', 4, 41),
]
);
}
@ -540,7 +545,7 @@ class ValuesOfCorrectTypeTest extends ValidatorTestCase
}
',
[
$this->badValue('Boolean', '"true"', 4, 41),
$this->badValueWithMessage('Field "booleanArgField" argument "booleanArg" requires type Boolean, found "true".', 4, 41),
]
);
}
@ -560,7 +565,7 @@ class ValuesOfCorrectTypeTest extends ValidatorTestCase
}
',
[
$this->badValue('Boolean', 'TRUE', 4, 41),
$this->badValueWithMessage('Field "booleanArgField" argument "booleanArg" requires type Boolean, found TRUE.', 4, 41),
]
);
}
@ -580,7 +585,7 @@ class ValuesOfCorrectTypeTest extends ValidatorTestCase
}
',
[
$this->badValue('ID', '1.0', 4, 31),
$this->badValueWithMessage('Field "idArgField" argument "idArg" requires type ID, found 1.0.', 4, 31),
]
);
}
@ -600,7 +605,7 @@ class ValuesOfCorrectTypeTest extends ValidatorTestCase
}
',
[
$this->badValue('ID', 'true', 4, 31),
$this->badValueWithMessage('Field "idArgField" argument "idArg" requires type ID, found true.', 4, 31),
]
);
}
@ -622,7 +627,7 @@ class ValuesOfCorrectTypeTest extends ValidatorTestCase
}
',
[
$this->badValue('ID', 'SOMETHING', 4, 31),
$this->badValueWithMessage('Field "idArgField" argument "idArg" requires type ID, found SOMETHING.', 4, 31),
]
);
}
@ -642,7 +647,7 @@ class ValuesOfCorrectTypeTest extends ValidatorTestCase
}
',
[
$this->badValue('DogCommand', '2', 4, 41),
$this->badValueWithMessage('Field "doesKnowCommand" argument "dogCommand" requires type DogCommand, found 2.', 4, 41),
]
);
}
@ -662,7 +667,7 @@ class ValuesOfCorrectTypeTest extends ValidatorTestCase
}
',
[
$this->badValue('DogCommand', '1.0', 4, 41),
$this->badValueWithMessage('Field "doesKnowCommand" argument "dogCommand" requires type DogCommand, found 1.0.', 4, 41),
]
);
}
@ -684,13 +689,7 @@ class ValuesOfCorrectTypeTest extends ValidatorTestCase
}
',
[
$this->badValue(
'DogCommand',
'"SIT"',
4,
41,
'Did you mean the enum value SIT?'
),
$this->badValueWithMessage('Field "doesKnowCommand" argument "dogCommand" requires type DogCommand, found "SIT"; Did you mean the enum value SIT?', 4, 41),
]
);
}
@ -710,7 +709,7 @@ class ValuesOfCorrectTypeTest extends ValidatorTestCase
}
',
[
$this->badValue('DogCommand', 'true', 4, 41),
$this->badValueWithMessage('Field "doesKnowCommand" argument "dogCommand" requires type DogCommand, found true.', 4, 41),
]
);
}
@ -730,7 +729,7 @@ class ValuesOfCorrectTypeTest extends ValidatorTestCase
}
',
[
$this->badValue('DogCommand', 'JUGGLE', 4, 41),
$this->badValueWithMessage('Field "doesKnowCommand" argument "dogCommand" requires type DogCommand, found JUGGLE.', 4, 41),
]
);
}
@ -750,13 +749,7 @@ class ValuesOfCorrectTypeTest extends ValidatorTestCase
}
',
[
$this->badValue(
'DogCommand',
'sit',
4,
41,
'Did you mean the enum value SIT?'
),
$this->badValueWithMessage('Field "doesKnowCommand" argument "dogCommand" requires type DogCommand, found sit; Did you mean the enum value SIT?', 4, 41),
]
);
}
@ -846,7 +839,7 @@ class ValuesOfCorrectTypeTest extends ValidatorTestCase
}
',
[
$this->badValue('String', '2', 4, 55),
$this->badValueWithMessage('Field "stringListArgField" argument "stringListArg" requires type String, found 2.', 4, 55),
]
);
}
@ -866,7 +859,7 @@ class ValuesOfCorrectTypeTest extends ValidatorTestCase
}
',
[
$this->badValue('[String]', '1', 4, 47),
$this->badValueWithMessage('Field "stringListArgField" argument "stringListArg" requires type [String], found 1.', 4, 47),
]
);
}
@ -1060,8 +1053,8 @@ class ValuesOfCorrectTypeTest extends ValidatorTestCase
}
',
[
$this->badValue('Int!', '"two"', 4, 32),
$this->badValue('Int!', '"one"', 4, 45),
$this->badValueWithMessage('Field "multipleReqs" argument "req2" requires type Int!, found "two".', 4, 32),
$this->badValueWithMessage('Field "multipleReqs" argument "req1" requires type Int!, found "one".', 4, 45),
]
);
}
@ -1081,7 +1074,7 @@ class ValuesOfCorrectTypeTest extends ValidatorTestCase
}
',
[
$this->badValue('Int!', '"one"', 4, 32),
$this->badValueWithMessage('Field "multipleReqs" argument "req1" requires type Int!, found "one".', 4, 32),
]
);
}
@ -1103,7 +1096,7 @@ class ValuesOfCorrectTypeTest extends ValidatorTestCase
}
',
[
$this->badValue('Int!', 'null', 4, 32),
$this->badValueWithMessage('Field "multipleReqs" argument "req1" requires type Int!, found null.', 4, 32),
]
);
}
@ -1277,7 +1270,7 @@ class ValuesOfCorrectTypeTest extends ValidatorTestCase
}
',
[
$this->badValue('String', '2', 5, 40),
$this->badValueWithMessage('Field "complexArgField" argument "complexArg" requires type String, found 2.', 5, 40),
]
);
}
@ -1340,19 +1333,13 @@ class ValuesOfCorrectTypeTest extends ValidatorTestCase
}
',
[
$this->badValue(
'Invalid',
'123',
3,
27,
'Invalid scalar is always invalid: 123'
),
$this->badValueWithMessage('Field "invalidArg" argument "arg" requires type Invalid, found 123; Invalid scalar is always invalid: 123', 3, 27),
]
);
self::assertEquals(
'Invalid scalar is always invalid: 123',
$errors[0]->getPrevious()->getMessage()
'Field "invalidArg" argument "arg" requires type Invalid, found 123; Invalid scalar is always invalid: 123',
$errors[0]->getMessage()
);
}
@ -1411,8 +1398,8 @@ class ValuesOfCorrectTypeTest extends ValidatorTestCase
}
',
[
$this->badValue('Boolean!', '"yes"', 3, 28),
$this->badValue('Boolean!', 'ENUM', 4, 28),
$this->badValueWithMessage('Field "dog" argument "if" requires type Boolean!, found "yes".', 3, 28),
$this->badValueWithMessage('Field "name" argument "if" requires type Boolean!, found ENUM.', 4, 28),
]
);
}