mirror of
https://github.com/retailcrm/graphql-php.git
synced 2024-11-21 20:36:05 +03:00
Merge branch 'master' of https://github.com/webonyx/graphql-php
This commit is contained in:
commit
e6e9d9ea22
@ -20,7 +20,7 @@ build:
|
|||||||
|
|
||||||
tools:
|
tools:
|
||||||
external_code_coverage:
|
external_code_coverage:
|
||||||
timeout: 600
|
timeout: 900
|
||||||
|
|
||||||
build_failure_conditions:
|
build_failure_conditions:
|
||||||
- 'elements.rating(<= C).new.exists' # No new classes/methods with a rating of C or worse allowed
|
- 'elements.rating(<= C).new.exists' # No new classes/methods with a rating of C or worse allowed
|
||||||
|
19
.travis.yml
19
.travis.yml
@ -4,12 +4,16 @@ language: php
|
|||||||
php:
|
php:
|
||||||
- 7.1
|
- 7.1
|
||||||
- 7.2
|
- 7.2
|
||||||
|
- 7.3
|
||||||
|
- 7.4snapshot
|
||||||
- nightly
|
- nightly
|
||||||
|
|
||||||
env:
|
env:
|
||||||
matrix:
|
matrix:
|
||||||
- EXECUTOR=coroutine
|
- EXECUTOR= DEPENDENCIES=--prefer-lowest
|
||||||
|
- EXECUTOR=coroutine DEPENDENCIES=--prefer-lowest
|
||||||
- EXECUTOR=
|
- EXECUTOR=
|
||||||
|
- EXECUTOR=coroutine
|
||||||
|
|
||||||
|
|
||||||
cache:
|
cache:
|
||||||
@ -26,13 +30,13 @@ script: ./vendor/bin/phpunit --group default,ReactPromise
|
|||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
allow_failures:
|
allow_failures:
|
||||||
|
- php: 7.4snapshot
|
||||||
- php: nightly
|
- php: nightly
|
||||||
|
|
||||||
include:
|
include:
|
||||||
- stage: Test
|
- stage: Test
|
||||||
env: DEPENDENCIES=low
|
|
||||||
install:
|
install:
|
||||||
- travis_retry composer update --prefer-dist --prefer-lowest
|
- travis_retry composer update --prefer-dist {$DEPENDENCIES}
|
||||||
|
|
||||||
- stage: Test
|
- stage: Test
|
||||||
env: COVERAGE
|
env: COVERAGE
|
||||||
@ -40,10 +44,12 @@ jobs:
|
|||||||
- mv ~/.phpenv/versions/$(phpenv version-name)/etc/conf.d/xdebug.ini{.disabled,}
|
- 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
|
- if [[ ! $(php -m | grep -si xdebug) ]]; then echo "xdebug required for coverage"; exit 1; fi
|
||||||
script:
|
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:
|
after_script:
|
||||||
- wget https://scrutinizer-ci.com/ocular.phar
|
- ./vendor/bin/phpcov merge /tmp/coverage --clover /tmp/clover.xml
|
||||||
- php ocular.phar code-coverage:upload --format=php-clover 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
|
- stage: Code Quality
|
||||||
php: 7.1
|
php: 7.1
|
||||||
@ -57,3 +63,4 @@ jobs:
|
|||||||
env: STATIC_ANALYSIS
|
env: STATIC_ANALYSIS
|
||||||
install: travis_retry composer install --prefer-dist
|
install: travis_retry composer install --prefer-dist
|
||||||
script: composer static-analysis
|
script: composer static-analysis
|
||||||
|
|
||||||
|
@ -14,7 +14,7 @@ composer require webonyx/graphql-php
|
|||||||
```
|
```
|
||||||
|
|
||||||
## Documentation
|
## 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.
|
as in the [docs](docs/) folder of the distribution.
|
||||||
|
|
||||||
If you don't know what GraphQL is, visit this [official website](http://graphql.org)
|
If you don't know what GraphQL is, visit this [official website](http://graphql.org)
|
||||||
|
@ -78,6 +78,9 @@ Parser::parse($source, [ 'allowLegacySDLImplementsInterfaces' => true])
|
|||||||
- `AbstractQuerySecurity` renamed to `QuerySecurityRule` (NS `GraphQL\Validator\Rules`)
|
- `AbstractQuerySecurity` renamed to `QuerySecurityRule` (NS `GraphQL\Validator\Rules`)
|
||||||
- `FindBreakingChanges` renamed to `BreakingChangesFinder` (NS `GraphQL\Utils`)
|
- `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
|
## Upgrade v0.11.x > v0.12.x
|
||||||
|
|
||||||
|
@ -9,7 +9,7 @@
|
|||||||
"API"
|
"API"
|
||||||
],
|
],
|
||||||
"require": {
|
"require": {
|
||||||
"php": "^7.1",
|
"php": "^7.1||^8.0",
|
||||||
"ext-json": "*",
|
"ext-json": "*",
|
||||||
"ext-mbstring": "*"
|
"ext-mbstring": "*"
|
||||||
},
|
},
|
||||||
@ -19,6 +19,7 @@
|
|||||||
"phpstan/phpstan": "0.10.5",
|
"phpstan/phpstan": "0.10.5",
|
||||||
"phpstan/phpstan-phpunit": "0.10.0",
|
"phpstan/phpstan-phpunit": "0.10.0",
|
||||||
"phpstan/phpstan-strict-rules": "0.10.1",
|
"phpstan/phpstan-strict-rules": "0.10.1",
|
||||||
|
"phpunit/phpcov": "^5.0",
|
||||||
"phpunit/phpunit": "^7.2",
|
"phpunit/phpunit": "^7.2",
|
||||||
"psr/http-message": "^1.0",
|
"psr/http-message": "^1.0",
|
||||||
"react/promise": "2.*"
|
"react/promise": "2.*"
|
||||||
|
@ -1,22 +1,28 @@
|
|||||||
# Integrations
|
# Integrations
|
||||||
|
|
||||||
* [Integration with Relay](https://github.com/ivome/graphql-relay-php)
|
* [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/)).
|
||||||
* [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)
|
* [Relay Library for graphql-php](https://github.com/ivome/graphql-relay-php) – Helps construct Relay related schema definitions.
|
||||||
* [Symfony Bundle](https://github.com/overblog/GraphQLBundle) by Overblog
|
* Laravel
|
||||||
* 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)
|
- [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
|
# 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))
|
* [GraphQLite](https://graphqlite.thecodingmachine.io) – Define your complete schema with annotations
|
||||||
* [DataLoader PHP](https://github.com/overblog/dataloader-php) - as a ready implementation for [deferred resolvers](data-fetching.md#solving-n1-problem)
|
* [GraphQL Doctrine](https://github.com/Ecodev/graphql-doctrine) – Define types with Doctrine ORM annotations
|
||||||
* [PSR 15 compliant middleware](https://github.com/phps-cans/psr7-middleware-graphql) for the Standard Server (experimental)
|
* [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) for the Standard Server
|
* [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) - Simple library that provides a builder interface for defining collection, querying, filtering, and post-processing logic of batched data fetches.
|
* [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
|
# 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)
|
* [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
|
GraphiQL as Google Chrome extension
|
||||||
* [GraphQL Playground](https://github.com/prismagraphql/graphql-playground) - GraphQL IDE for better development workflows (GraphQL Subscriptions, interactive docs & collaboration).
|
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
# About GraphQL
|
# About GraphQL
|
||||||
|
|
||||||
GraphQL is a modern way to build HTTP APIs consumed by the web and mobile clients.
|
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
|
GraphQL itself is a [specification](https://github.com/facebook/graphql) designed by Facebook
|
||||||
engineers. Various implementations of this specification were written
|
engineers. Various implementations of this specification were written
|
||||||
|
@ -11,7 +11,7 @@ use Throwable;
|
|||||||
|
|
||||||
class Deferred
|
class Deferred
|
||||||
{
|
{
|
||||||
/** @var SplQueue */
|
/** @var SplQueue|null */
|
||||||
private static $queue;
|
private static $queue;
|
||||||
|
|
||||||
/** @var callable */
|
/** @var callable */
|
||||||
@ -20,21 +20,6 @@ class Deferred
|
|||||||
/** @var SyncPromise */
|
/** @var SyncPromise */
|
||||||
public $promise;
|
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)
|
public function __construct(callable $callback)
|
||||||
{
|
{
|
||||||
$this->callback = $callback;
|
$this->callback = $callback;
|
||||||
@ -42,6 +27,25 @@ class Deferred
|
|||||||
self::getQueue()->enqueue($this);
|
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)
|
public function then($onFulfilled = null, $onRejected = null)
|
||||||
{
|
{
|
||||||
return $this->promise->then($onFulfilled, $onRejected);
|
return $this->promise->then($onFulfilled, $onRejected);
|
||||||
|
@ -74,7 +74,7 @@ class Executor
|
|||||||
* execution are collected in `$result->errors`.
|
* execution are collected in `$result->errors`.
|
||||||
*
|
*
|
||||||
* @param mixed|null $rootValue
|
* @param mixed|null $rootValue
|
||||||
* @param mixed[]|null $contextValue
|
* @param mixed|null $contextValue
|
||||||
* @param mixed[]|ArrayAccess|null $variableValues
|
* @param mixed[]|ArrayAccess|null $variableValues
|
||||||
* @param string|null $operationName
|
* @param string|null $operationName
|
||||||
*
|
*
|
||||||
@ -119,8 +119,8 @@ class Executor
|
|||||||
*
|
*
|
||||||
* Useful for async PHP platforms.
|
* Useful for async PHP platforms.
|
||||||
*
|
*
|
||||||
* @param mixed[]|null $rootValue
|
* @param mixed|null $rootValue
|
||||||
* @param mixed[]|null $contextValue
|
* @param mixed|null $contextValue
|
||||||
* @param mixed[]|null $variableValues
|
* @param mixed[]|null $variableValues
|
||||||
* @param string|null $operationName
|
* @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
|
* 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.
|
* of calling that function while passing along args and context.
|
||||||
*
|
*
|
||||||
* @param mixed $source
|
* @param mixed $source
|
||||||
* @param mixed[] $args
|
* @param mixed[] $args
|
||||||
* @param mixed[]|null $context
|
* @param mixed|null $context
|
||||||
*
|
*
|
||||||
* @return mixed|null
|
* @return mixed|null
|
||||||
*/
|
*/
|
||||||
|
@ -115,8 +115,8 @@ class ReferenceExecutor implements ExecutorImplementation
|
|||||||
* Constructs an ExecutionContext object from the arguments passed to
|
* Constructs an ExecutionContext object from the arguments passed to
|
||||||
* execute, which we will pass throughout the other execution methods.
|
* execute, which we will pass throughout the other execution methods.
|
||||||
*
|
*
|
||||||
* @param mixed[] $rootValue
|
* @param mixed $rootValue
|
||||||
* @param mixed[] $contextValue
|
* @param mixed $contextValue
|
||||||
* @param mixed[]|Traversable $rawVariableValues
|
* @param mixed[]|Traversable $rawVariableValues
|
||||||
* @param string|null $operationName
|
* @param string|null $operationName
|
||||||
*
|
*
|
||||||
@ -153,7 +153,7 @@ class ReferenceExecutor implements ExecutorImplementation
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (! $operation) {
|
if ($operation === null) {
|
||||||
if ($operationName) {
|
if ($operationName) {
|
||||||
$errors[] = new Error(sprintf('Unknown operation named "%s".', $operationName));
|
$errors[] = new Error(sprintf('Unknown operation named "%s".', $operationName));
|
||||||
} else {
|
} else {
|
||||||
@ -165,7 +165,7 @@ class ReferenceExecutor implements ExecutorImplementation
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
$variableValues = null;
|
$variableValues = null;
|
||||||
if ($operation) {
|
if ($operation !== null) {
|
||||||
[$coercionErrors, $coercedVariableValues] = Values::getVariableValues(
|
[$coercionErrors, $coercedVariableValues] = Values::getVariableValues(
|
||||||
$schema,
|
$schema,
|
||||||
$operation->variableDefinitions ?: [],
|
$operation->variableDefinitions ?: [],
|
||||||
|
@ -152,8 +152,8 @@ class CoroutineExecutor implements Runtime, ExecutorImplementation
|
|||||||
|
|
||||||
if (is_array($value)) {
|
if (is_array($value)) {
|
||||||
$array = [];
|
$array = [];
|
||||||
foreach ($value as $item) {
|
foreach ($value as $key => $item) {
|
||||||
$array[] = self::resultToArray($item);
|
$array[$key] = self::resultToArray($item);
|
||||||
}
|
}
|
||||||
return $array;
|
return $array;
|
||||||
}
|
}
|
||||||
|
@ -40,7 +40,7 @@ abstract class Node
|
|||||||
public $loc;
|
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)
|
public function __construct(array $vars)
|
||||||
{
|
{
|
||||||
|
@ -46,6 +46,12 @@ class OperationParams
|
|||||||
*/
|
*/
|
||||||
public $variables;
|
public $variables;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @api
|
||||||
|
* @var mixed[]|null
|
||||||
|
*/
|
||||||
|
public $extensions;
|
||||||
|
|
||||||
/** @var mixed[] */
|
/** @var mixed[] */
|
||||||
private $originalInput;
|
private $originalInput;
|
||||||
|
|
||||||
@ -76,24 +82,38 @@ class OperationParams
|
|||||||
'id' => null, // alias to queryid
|
'id' => null, // alias to queryid
|
||||||
'operationname' => null,
|
'operationname' => null,
|
||||||
'variables' => null,
|
'variables' => null,
|
||||||
|
'extensions' => null,
|
||||||
];
|
];
|
||||||
|
|
||||||
if ($params['variables'] === '') {
|
if ($params['variables'] === '') {
|
||||||
$params['variables'] = null;
|
$params['variables'] = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (is_string($params['variables'])) {
|
// Some parameters could be provided as serialized JSON.
|
||||||
$tmp = json_decode($params['variables'], true);
|
foreach (['extensions', 'variables'] as $param) {
|
||||||
if (! json_last_error()) {
|
if (! is_string($params[$param])) {
|
||||||
$params['variables'] = $tmp;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$tmp = json_decode($params[$param], true);
|
||||||
|
if (json_last_error()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$params[$param] = $tmp;
|
||||||
}
|
}
|
||||||
|
|
||||||
$instance->query = $params['query'];
|
$instance->query = $params['query'];
|
||||||
$instance->queryId = $params['queryid'] ?: $params['documentid'] ?: $params['id'];
|
$instance->queryId = $params['queryid'] ?: $params['documentid'] ?: $params['id'];
|
||||||
$instance->operation = $params['operationname'];
|
$instance->operation = $params['operationname'];
|
||||||
$instance->variables = $params['variables'];
|
$instance->variables = $params['variables'];
|
||||||
$instance->readOnly = (bool) $readonly;
|
$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;
|
return $instance;
|
||||||
}
|
}
|
||||||
|
@ -22,7 +22,7 @@ use function sprintf;
|
|||||||
/**
|
/**
|
||||||
* Class EnumType
|
* Class EnumType
|
||||||
*/
|
*/
|
||||||
class EnumType extends Type implements InputType, OutputType, LeafType, NamedType
|
class EnumType extends Type implements InputType, OutputType, LeafType, NullableType, NamedType
|
||||||
{
|
{
|
||||||
/** @var EnumTypeDefinitionNode|null */
|
/** @var EnumTypeDefinitionNode|null */
|
||||||
public $astNode;
|
public $astNode;
|
||||||
|
@ -18,7 +18,7 @@ use function sprintf;
|
|||||||
/**
|
/**
|
||||||
* Class InputObjectType
|
* Class InputObjectType
|
||||||
*/
|
*/
|
||||||
class InputObjectType extends Type implements InputType, NamedType
|
class InputObjectType extends Type implements InputType, NullableType, NamedType
|
||||||
{
|
{
|
||||||
/** @var InputObjectTypeDefinitionNode|null */
|
/** @var InputObjectTypeDefinitionNode|null */
|
||||||
public $astNode;
|
public $astNode;
|
||||||
|
@ -15,7 +15,7 @@ use function sprintf;
|
|||||||
/**
|
/**
|
||||||
* Class InterfaceType
|
* Class InterfaceType
|
||||||
*/
|
*/
|
||||||
class InterfaceType extends Type implements AbstractType, OutputType, CompositeType, NamedType
|
class InterfaceType extends Type implements AbstractType, OutputType, CompositeType, NullableType, NamedType
|
||||||
{
|
{
|
||||||
/** @var InterfaceTypeDefinitionNode|null */
|
/** @var InterfaceTypeDefinitionNode|null */
|
||||||
public $astNode;
|
public $astNode;
|
||||||
@ -107,7 +107,7 @@ class InterfaceType extends Type implements AbstractType, OutputType, CompositeT
|
|||||||
* @param object $objectValue
|
* @param object $objectValue
|
||||||
* @param mixed[] $context
|
* @param mixed[] $context
|
||||||
*
|
*
|
||||||
* @return callable|null
|
* @return Type|null
|
||||||
*/
|
*/
|
||||||
public function resolveType($objectValue, $context, ResolveInfo $info)
|
public function resolveType($objectValue, $context, ResolveInfo $info)
|
||||||
{
|
{
|
||||||
|
@ -7,7 +7,7 @@ namespace GraphQL\Type\Definition;
|
|||||||
/**
|
/**
|
||||||
* Class ListOfType
|
* 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 */
|
/** @var ObjectType|InterfaceType|UnionType|ScalarType|InputObjectType|EnumType */
|
||||||
public $ofType;
|
public $ofType;
|
||||||
|
@ -4,8 +4,6 @@ declare(strict_types=1);
|
|||||||
|
|
||||||
namespace GraphQL\Type\Definition;
|
namespace GraphQL\Type\Definition;
|
||||||
|
|
||||||
use Exception;
|
|
||||||
use GraphQL\Error\InvariantViolation;
|
|
||||||
use GraphQL\Utils\Utils;
|
use GraphQL\Utils\Utils;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -13,13 +11,11 @@ use GraphQL\Utils\Utils;
|
|||||||
*/
|
*/
|
||||||
class NonNull extends Type implements WrappingType, OutputType, InputType
|
class NonNull extends Type implements WrappingType, OutputType, InputType
|
||||||
{
|
{
|
||||||
/** @var ObjectType|InterfaceType|UnionType|ScalarType|InputObjectType|EnumType|ListOfType */
|
/** @var NullableType */
|
||||||
private $ofType;
|
private $ofType;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param callable|Type $type
|
* @param NullableType $type
|
||||||
*
|
|
||||||
* @throws Exception
|
|
||||||
*/
|
*/
|
||||||
public function __construct($type)
|
public function __construct($type)
|
||||||
{
|
{
|
||||||
@ -29,7 +25,7 @@ class NonNull extends Type implements WrappingType, OutputType, InputType
|
|||||||
/**
|
/**
|
||||||
* @param mixed $type
|
* @param mixed $type
|
||||||
*
|
*
|
||||||
* @return ObjectType|InterfaceType|UnionType|ScalarType|InputObjectType|EnumType|ListOfType
|
* @return NullableType
|
||||||
*/
|
*/
|
||||||
public static function assertNullableType($type)
|
public static function assertNullableType($type)
|
||||||
{
|
{
|
||||||
@ -67,9 +63,7 @@ class NonNull extends Type implements WrappingType, OutputType, InputType
|
|||||||
/**
|
/**
|
||||||
* @param bool $recurse
|
* @param bool $recurse
|
||||||
*
|
*
|
||||||
* @return ObjectType|InterfaceType|UnionType|ScalarType|InputObjectType|EnumType|ListOfType
|
* @return Type
|
||||||
*
|
|
||||||
* @throws InvariantViolation
|
|
||||||
*/
|
*/
|
||||||
public function getWrappedType($recurse = false)
|
public function getWrappedType($recurse = false)
|
||||||
{
|
{
|
||||||
|
20
src/Type/Definition/NullableType.php
Normal file
20
src/Type/Definition/NullableType.php
Normal 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
|
||||||
|
{
|
||||||
|
}
|
@ -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 */
|
/** @var ObjectTypeDefinitionNode|null */
|
||||||
public $astNode;
|
public $astNode;
|
||||||
|
@ -23,7 +23,7 @@ class ResolveInfo
|
|||||||
* The name of the field being resolved
|
* The name of the field being resolved
|
||||||
*
|
*
|
||||||
* @api
|
* @api
|
||||||
* @var string|null
|
* @var string
|
||||||
*/
|
*/
|
||||||
public $fieldName;
|
public $fieldName;
|
||||||
|
|
||||||
@ -31,9 +31,9 @@ class ResolveInfo
|
|||||||
* AST of all nodes referencing this field in the query.
|
* AST of all nodes referencing this field in the query.
|
||||||
*
|
*
|
||||||
* @api
|
* @api
|
||||||
* @var FieldNode[]|null
|
* @var FieldNode[]
|
||||||
*/
|
*/
|
||||||
public $fieldNodes;
|
public $fieldNodes = [];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Expected return type of the field being resolved
|
* Expected return type of the field being resolved
|
||||||
@ -47,7 +47,7 @@ class ResolveInfo
|
|||||||
* Parent type of the field being resolved
|
* Parent type of the field being resolved
|
||||||
*
|
*
|
||||||
* @api
|
* @api
|
||||||
* @var ObjectType|null
|
* @var ObjectType
|
||||||
*/
|
*/
|
||||||
public $parentType;
|
public $parentType;
|
||||||
|
|
||||||
@ -55,7 +55,7 @@ class ResolveInfo
|
|||||||
* Path to this field from the very root value
|
* Path to this field from the very root value
|
||||||
*
|
*
|
||||||
* @api
|
* @api
|
||||||
* @var string[]
|
* @var string[][]
|
||||||
*/
|
*/
|
||||||
public $path;
|
public $path;
|
||||||
|
|
||||||
@ -71,9 +71,9 @@ class ResolveInfo
|
|||||||
* AST of all fragments defined in query
|
* AST of all fragments defined in query
|
||||||
*
|
*
|
||||||
* @api
|
* @api
|
||||||
* @var FragmentDefinitionNode[]|null
|
* @var FragmentDefinitionNode[]
|
||||||
*/
|
*/
|
||||||
public $fragments;
|
public $fragments = [];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Root value passed to query execution
|
* Root value passed to query execution
|
||||||
@ -95,21 +95,29 @@ class ResolveInfo
|
|||||||
* Array of variables passed to query execution
|
* Array of variables passed to query execution
|
||||||
*
|
*
|
||||||
* @api
|
* @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(
|
public function __construct(
|
||||||
string $fieldName,
|
string $fieldName,
|
||||||
$fieldNodes,
|
$fieldNodes,
|
||||||
$returnType,
|
$returnType,
|
||||||
ObjectType $parentType,
|
ObjectType $parentType,
|
||||||
$path,
|
array $path,
|
||||||
Schema $schema,
|
Schema $schema,
|
||||||
$fragments,
|
array $fragments,
|
||||||
$rootValue,
|
$rootValue,
|
||||||
?OperationDefinitionNode $operation,
|
?OperationDefinitionNode $operation,
|
||||||
$variableValues
|
array $variableValues
|
||||||
) {
|
) {
|
||||||
$this->fieldName = $fieldName;
|
$this->fieldName = $fieldName;
|
||||||
$this->fieldNodes = $fieldNodes;
|
$this->fieldNodes = $fieldNodes;
|
||||||
|
@ -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 */
|
/** @var ScalarTypeDefinitionNode|null */
|
||||||
public $astNode;
|
public $astNode;
|
||||||
|
@ -137,7 +137,7 @@ abstract class Type implements JsonSerializable
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param ObjectType|InterfaceType|UnionType|ScalarType|InputObjectType|EnumType|ListOfType $wrappedType
|
* @param NullableType $wrappedType
|
||||||
*
|
*
|
||||||
* @return NonNull
|
* @return NonNull
|
||||||
*
|
*
|
||||||
@ -338,7 +338,7 @@ abstract class Type implements JsonSerializable
|
|||||||
/**
|
/**
|
||||||
* @param Type $type
|
* @param Type $type
|
||||||
*
|
*
|
||||||
* @return ObjectType|InterfaceType|UnionType|ScalarType|InputObjectType|EnumType|ListOfType
|
* @return NullableType
|
||||||
*
|
*
|
||||||
* @api
|
* @api
|
||||||
*/
|
*/
|
||||||
|
@ -17,7 +17,7 @@ use function sprintf;
|
|||||||
/**
|
/**
|
||||||
* Class UnionType
|
* Class UnionType
|
||||||
*/
|
*/
|
||||||
class UnionType extends Type implements AbstractType, OutputType, CompositeType, NamedType
|
class UnionType extends Type implements AbstractType, OutputType, CompositeType, NullableType, NamedType
|
||||||
{
|
{
|
||||||
/** @var UnionTypeDefinitionNode */
|
/** @var UnionTypeDefinitionNode */
|
||||||
public $astNode;
|
public $astNode;
|
||||||
|
@ -112,27 +112,31 @@ class BreakingChangesFinder
|
|||||||
* @return string[][]
|
* @return string[][]
|
||||||
*/
|
*/
|
||||||
public static function findTypesThatChangedKind(
|
public static function findTypesThatChangedKind(
|
||||||
Schema $oldSchema,
|
Schema $schemaA,
|
||||||
Schema $newSchema
|
Schema $schemaB
|
||||||
) {
|
) : iterable {
|
||||||
$oldTypeMap = $oldSchema->getTypeMap();
|
$schemaATypeMap = $schemaA->getTypeMap();
|
||||||
$newTypeMap = $newSchema->getTypeMap();
|
$schemaBTypeMap = $schemaB->getTypeMap();
|
||||||
|
|
||||||
$breakingChanges = [];
|
$breakingChanges = [];
|
||||||
foreach ($oldTypeMap as $typeName => $oldType) {
|
foreach ($schemaATypeMap as $typeName => $schemaAType) {
|
||||||
if (! isset($newTypeMap[$typeName])) {
|
if (! isset($schemaBTypeMap[$typeName])) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
$newType = $newTypeMap[$typeName];
|
$schemaBType = $schemaBTypeMap[$typeName];
|
||||||
if ($oldType instanceof $newType) {
|
if ($schemaAType instanceof $schemaBType) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
$oldTypeKindName = self::typeKindName($oldType);
|
if ($schemaBType instanceof $schemaAType) {
|
||||||
$newTypeKindName = self::typeKindName($newType);
|
continue;
|
||||||
$breakingChanges[] = [
|
}
|
||||||
|
|
||||||
|
$schemaATypeKindName = self::typeKindName($schemaAType);
|
||||||
|
$schemaBTypeKindName = self::typeKindName($schemaBType);
|
||||||
|
$breakingChanges[] = [
|
||||||
'type' => self::BREAKING_CHANGE_TYPE_CHANGED_KIND,
|
'type' => self::BREAKING_CHANGE_TYPE_CHANGED_KIND,
|
||||||
'description' => "${typeName} changed from ${oldTypeKindName} to ${newTypeKindName}.",
|
'description' => "${typeName} changed from ${schemaATypeKindName} to ${schemaBTypeKindName}.",
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -513,7 +513,14 @@ class SchemaExtender
|
|||||||
$schemaExtensions[] = $def;
|
$schemaExtensions[] = $def;
|
||||||
} elseif ($def instanceof TypeDefinitionNode) {
|
} elseif ($def instanceof TypeDefinitionNode) {
|
||||||
$typeName = isset($def->name) ? $def->name->value : null;
|
$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]);
|
throw new Error('Type "' . $typeName . '" already exists in the schema. It cannot also be defined in this type definition.', [$def]);
|
||||||
}
|
}
|
||||||
$typeDefinitionMap[$typeName] = $def;
|
$typeDefinitionMap[$typeName] = $def;
|
||||||
|
@ -8,6 +8,7 @@ use Exception;
|
|||||||
use GraphQL\Error\Error;
|
use GraphQL\Error\Error;
|
||||||
use GraphQL\Language\AST\BooleanValueNode;
|
use GraphQL\Language\AST\BooleanValueNode;
|
||||||
use GraphQL\Language\AST\EnumValueNode;
|
use GraphQL\Language\AST\EnumValueNode;
|
||||||
|
use GraphQL\Language\AST\FieldNode;
|
||||||
use GraphQL\Language\AST\FloatValueNode;
|
use GraphQL\Language\AST\FloatValueNode;
|
||||||
use GraphQL\Language\AST\IntValueNode;
|
use GraphQL\Language\AST\IntValueNode;
|
||||||
use GraphQL\Language\AST\ListValueNode;
|
use GraphQL\Language\AST\ListValueNode;
|
||||||
@ -21,6 +22,7 @@ use GraphQL\Language\Printer;
|
|||||||
use GraphQL\Language\Visitor;
|
use GraphQL\Language\Visitor;
|
||||||
use GraphQL\Type\Definition\EnumType;
|
use GraphQL\Type\Definition\EnumType;
|
||||||
use GraphQL\Type\Definition\EnumValueDefinition;
|
use GraphQL\Type\Definition\EnumValueDefinition;
|
||||||
|
use GraphQL\Type\Definition\FieldArgument;
|
||||||
use GraphQL\Type\Definition\InputObjectType;
|
use GraphQL\Type\Definition\InputObjectType;
|
||||||
use GraphQL\Type\Definition\ListOfType;
|
use GraphQL\Type\Definition\ListOfType;
|
||||||
use GraphQL\Type\Definition\NonNull;
|
use GraphQL\Type\Definition\NonNull;
|
||||||
@ -46,8 +48,14 @@ class ValuesOfCorrectType extends ValidationRule
|
|||||||
{
|
{
|
||||||
public function getVisitor(ValidationContext $context)
|
public function getVisitor(ValidationContext $context)
|
||||||
{
|
{
|
||||||
|
$fieldName = '';
|
||||||
return [
|
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();
|
$type = $context->getInputType();
|
||||||
if (! ($type instanceof NonNull)) {
|
if (! ($type instanceof NonNull)) {
|
||||||
return;
|
return;
|
||||||
@ -55,30 +63,31 @@ class ValuesOfCorrectType extends ValidationRule
|
|||||||
|
|
||||||
$context->reportError(
|
$context->reportError(
|
||||||
new Error(
|
new Error(
|
||||||
self::badValueMessage((string) $type, Printer::doPrint($node)),
|
self::getBadValueMessage((string) $type, Printer::doPrint($node), null, $context, $fieldName),
|
||||||
$node
|
$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
|
// Note: TypeInfo will traverse into a list's item type, so look to the
|
||||||
// parent input type to check if it is a list.
|
// parent input type to check if it is a list.
|
||||||
$type = Type::getNullableType($context->getParentInputType());
|
$type = Type::getNullableType($context->getParentInputType());
|
||||||
if (! $type instanceof ListOfType) {
|
if (! $type instanceof ListOfType) {
|
||||||
$this->isValidScalar($context, $node);
|
$this->isValidScalar($context, $node, $fieldName);
|
||||||
|
|
||||||
return Visitor::skipNode();
|
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
|
// Note: TypeInfo will traverse into a list's item type, so look to the
|
||||||
// parent input type to check if it is a list.
|
// parent input type to check if it is a list.
|
||||||
$type = Type::getNamedType($context->getInputType());
|
$type = Type::getNamedType($context->getInputType());
|
||||||
if (! $type instanceof InputObjectType) {
|
if (! $type instanceof InputObjectType) {
|
||||||
$this->isValidScalar($context, $node);
|
$this->isValidScalar($context, $node, $fieldName);
|
||||||
|
|
||||||
return Visitor::skipNode();
|
return Visitor::skipNode();
|
||||||
}
|
}
|
||||||
|
unset($fieldName);
|
||||||
// Ensure every required field exists.
|
// Ensure every required field exists.
|
||||||
$inputFields = $type->getFields();
|
$inputFields = $type->getFields();
|
||||||
$nodeFields = iterator_to_array($node->fields);
|
$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());
|
$type = Type::getNamedType($context->getInputType());
|
||||||
if (! $type instanceof EnumType) {
|
if (! $type instanceof EnumType) {
|
||||||
$this->isValidScalar($context, $node);
|
$this->isValidScalar($context, $node, $fieldName);
|
||||||
} elseif (! $type->getValue($node->value)) {
|
} elseif (! $type->getValue($node->value)) {
|
||||||
$context->reportError(
|
$context->reportError(
|
||||||
new Error(
|
new Error(
|
||||||
self::badValueMessage(
|
self::getBadValueMessage(
|
||||||
$type->name,
|
$type->name,
|
||||||
Printer::doPrint($node),
|
Printer::doPrint($node),
|
||||||
$this->enumTypeSuggestion($type, $node)
|
$this->enumTypeSuggestion($type, $node),
|
||||||
|
$context,
|
||||||
|
$fieldName
|
||||||
),
|
),
|
||||||
$node
|
$node
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
NodeKind::INT => function (IntValueNode $node) use ($context) {
|
NodeKind::INT => function (IntValueNode $node) use ($context, &$fieldName) {
|
||||||
$this->isValidScalar($context, $node);
|
$this->isValidScalar($context, $node, $fieldName);
|
||||||
},
|
},
|
||||||
NodeKind::FLOAT => function (FloatValueNode $node) use ($context) {
|
NodeKind::FLOAT => function (FloatValueNode $node) use ($context, &$fieldName) {
|
||||||
$this->isValidScalar($context, $node);
|
$this->isValidScalar($context, $node, $fieldName);
|
||||||
},
|
},
|
||||||
NodeKind::STRING => function (StringValueNode $node) use ($context) {
|
NodeKind::STRING => function (StringValueNode $node) use ($context, &$fieldName) {
|
||||||
$this->isValidScalar($context, $node);
|
$this->isValidScalar($context, $node, $fieldName);
|
||||||
},
|
},
|
||||||
NodeKind::BOOLEAN => function (BooleanValueNode $node) use ($context) {
|
NodeKind::BOOLEAN => function (BooleanValueNode $node) use ($context, &$fieldName) {
|
||||||
$this->isValidScalar($context, $node);
|
$this->isValidScalar($context, $node, $fieldName);
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
@ -165,7 +176,7 @@ class ValuesOfCorrectType extends ValidationRule
|
|||||||
($message ? "; ${message}" : '.');
|
($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.
|
// Report any error at the full type expected by the location.
|
||||||
$locationType = $context->getInputType();
|
$locationType = $context->getInputType();
|
||||||
@ -179,10 +190,12 @@ class ValuesOfCorrectType extends ValidationRule
|
|||||||
if (! $type instanceof ScalarType) {
|
if (! $type instanceof ScalarType) {
|
||||||
$context->reportError(
|
$context->reportError(
|
||||||
new Error(
|
new Error(
|
||||||
self::badValueMessage(
|
self::getBadValueMessage(
|
||||||
(string) $locationType,
|
(string) $locationType,
|
||||||
Printer::doPrint($node),
|
Printer::doPrint($node),
|
||||||
$this->enumTypeSuggestion($type, $node)
|
$this->enumTypeSuggestion($type, $node),
|
||||||
|
$context,
|
||||||
|
$fieldName
|
||||||
),
|
),
|
||||||
$node
|
$node
|
||||||
)
|
)
|
||||||
@ -199,32 +212,28 @@ class ValuesOfCorrectType extends ValidationRule
|
|||||||
// Ensure a reference to the original error is maintained.
|
// Ensure a reference to the original error is maintained.
|
||||||
$context->reportError(
|
$context->reportError(
|
||||||
new Error(
|
new Error(
|
||||||
self::badValueMessage(
|
self::getBadValueMessage(
|
||||||
(string) $locationType,
|
(string) $locationType,
|
||||||
Printer::doPrint($node),
|
Printer::doPrint($node),
|
||||||
$error->getMessage()
|
$error->getMessage(),
|
||||||
|
$context,
|
||||||
|
$fieldName
|
||||||
),
|
),
|
||||||
$node,
|
$node
|
||||||
null,
|
|
||||||
null,
|
|
||||||
null,
|
|
||||||
$error
|
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
} catch (Throwable $error) {
|
} catch (Throwable $error) {
|
||||||
// Ensure a reference to the original error is maintained.
|
// Ensure a reference to the original error is maintained.
|
||||||
$context->reportError(
|
$context->reportError(
|
||||||
new Error(
|
new Error(
|
||||||
self::badValueMessage(
|
self::getBadValueMessage(
|
||||||
(string) $locationType,
|
(string) $locationType,
|
||||||
Printer::doPrint($node),
|
Printer::doPrint($node),
|
||||||
$error->getMessage()
|
$error->getMessage(),
|
||||||
|
$context,
|
||||||
|
$fieldName
|
||||||
),
|
),
|
||||||
$node,
|
$node
|
||||||
null,
|
|
||||||
null,
|
|
||||||
null,
|
|
||||||
$error
|
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -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)
|
public static function requiredFieldMessage($typeName, $fieldName, $fieldTypeName)
|
||||||
{
|
{
|
||||||
return sprintf('Field %s.%s of required type %s was not provided.', $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) .
|
return sprintf('Field "%s" is not defined by type %s', $fieldName, $typeName) .
|
||||||
($message ? sprintf('; %s', $message) : '.');
|
($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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,6 +6,7 @@ namespace GraphQL\Tests\Executor;
|
|||||||
|
|
||||||
use GraphQL\Executor\Executor;
|
use GraphQL\Executor\Executor;
|
||||||
use GraphQL\Language\Parser;
|
use GraphQL\Language\Parser;
|
||||||
|
use GraphQL\Type\Definition\CustomScalarType;
|
||||||
use GraphQL\Type\Definition\ObjectType;
|
use GraphQL\Type\Definition\ObjectType;
|
||||||
use GraphQL\Type\Definition\Type;
|
use GraphQL\Type\Definition\Type;
|
||||||
use GraphQL\Type\Schema;
|
use GraphQL\Type\Schema;
|
||||||
@ -20,6 +21,13 @@ class ExecutorSchemaTest extends TestCase
|
|||||||
*/
|
*/
|
||||||
public function testExecutesUsingASchema() : void
|
public function testExecutesUsingASchema() : void
|
||||||
{
|
{
|
||||||
|
$BlogSerializableValueType = new CustomScalarType([
|
||||||
|
'name' => 'JsonSerializableValueScalar',
|
||||||
|
'serialize' => static function ($value) {
|
||||||
|
return $value;
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
|
||||||
$BlogArticle = null;
|
$BlogArticle = null;
|
||||||
$BlogImage = new ObjectType([
|
$BlogImage = new ObjectType([
|
||||||
'name' => 'Image',
|
'name' => 'Image',
|
||||||
@ -57,6 +65,7 @@ class ExecutorSchemaTest extends TestCase
|
|||||||
'title' => ['type' => Type::string()],
|
'title' => ['type' => Type::string()],
|
||||||
'body' => ['type' => Type::string()],
|
'body' => ['type' => Type::string()],
|
||||||
'keywords' => ['type' => Type::listOf(Type::string())],
|
'keywords' => ['type' => Type::listOf(Type::string())],
|
||||||
|
'meta' => ['type' => $BlogSerializableValueType],
|
||||||
],
|
],
|
||||||
]);
|
]);
|
||||||
|
|
||||||
@ -113,6 +122,7 @@ class ExecutorSchemaTest extends TestCase
|
|||||||
keywords
|
keywords
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
meta
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -191,6 +201,7 @@ class ExecutorSchemaTest extends TestCase
|
|||||||
'keywords' => ['foo', 'bar', '1', 'true', null],
|
'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',
|
'body' => 'This is a post',
|
||||||
'hidden' => 'This data is not exposed in the schema',
|
'hidden' => 'This data is not exposed in the schema',
|
||||||
'keywords' => ['foo', 'bar', 1, true, null],
|
'keywords' => ['foo', 'bar', 1, true, null],
|
||||||
|
'meta' => ['title' => 'My Article 1 | My Blog'],
|
||||||
];
|
];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -25,7 +25,7 @@ class RequestParsingTest extends TestCase
|
|||||||
];
|
];
|
||||||
|
|
||||||
foreach ($parsed as $source => $parsedBody) {
|
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);
|
self::assertFalse($parsedBody->isReadOnly(), $source);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -91,6 +91,7 @@ class RequestParsingTest extends TestCase
|
|||||||
$queryId = null,
|
$queryId = null,
|
||||||
$variables = null,
|
$variables = null,
|
||||||
$operation = null,
|
$operation = null,
|
||||||
|
$extensions = null,
|
||||||
$message = ''
|
$message = ''
|
||||||
) {
|
) {
|
||||||
self::assertInstanceOf(OperationParams::class, $params, $message);
|
self::assertInstanceOf(OperationParams::class, $params, $message);
|
||||||
@ -99,6 +100,7 @@ class RequestParsingTest extends TestCase
|
|||||||
self::assertSame($queryId, $params->queryId, $message);
|
self::assertSame($queryId, $params->queryId, $message);
|
||||||
self::assertSame($variables, $params->variables, $message);
|
self::assertSame($variables, $params->variables, $message);
|
||||||
self::assertSame($operation, $params->operation, $message);
|
self::assertSame($operation, $params->operation, $message);
|
||||||
|
self::assertSame($extensions, $params->extensions, $message);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testParsesUrlencodedRequest() : void
|
public function testParsesUrlencodedRequest() : void
|
||||||
@ -118,7 +120,7 @@ class RequestParsingTest extends TestCase
|
|||||||
];
|
];
|
||||||
|
|
||||||
foreach ($parsed as $method => $parsedBody) {
|
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);
|
self::assertFalse($parsedBody->isReadOnly(), $method);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -175,7 +177,7 @@ class RequestParsingTest extends TestCase
|
|||||||
];
|
];
|
||||||
|
|
||||||
foreach ($parsed as $method => $parsedBody) {
|
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);
|
self::assertTrue($parsedBody->isReadonly(), $method);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -230,7 +232,7 @@ class RequestParsingTest extends TestCase
|
|||||||
];
|
];
|
||||||
|
|
||||||
foreach ($parsed as $method => $parsedBody) {
|
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);
|
self::assertFalse($parsedBody->isReadOnly(), $method);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -286,19 +288,21 @@ class RequestParsingTest extends TestCase
|
|||||||
'psr' => $this->parsePsrRequest('application/json', json_encode($body)),
|
'psr' => $this->parsePsrRequest('application/json', json_encode($body)),
|
||||||
];
|
];
|
||||||
foreach ($parsed as $method => $parsedBody) {
|
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);
|
self::assertFalse($parsedBody->isReadOnly(), $method);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testParsesVariablesAsJSON() : void
|
public function testParsesParamsAsJSON() : void
|
||||||
{
|
{
|
||||||
$query = '{my query}';
|
$query = '{my query}';
|
||||||
$variables = ['test' => 1, 'test2' => 2];
|
$variables = ['test1' => 1, 'test2' => 2];
|
||||||
$operation = 'op';
|
$extensions = ['test3' => 3, 'test4' => 4];
|
||||||
|
$operation = 'op';
|
||||||
|
|
||||||
$body = [
|
$body = [
|
||||||
'query' => $query,
|
'query' => $query,
|
||||||
|
'extensions' => json_encode($extensions),
|
||||||
'variables' => json_encode($variables),
|
'variables' => json_encode($variables),
|
||||||
'operationName' => $operation,
|
'operationName' => $operation,
|
||||||
];
|
];
|
||||||
@ -307,7 +311,7 @@ class RequestParsingTest extends TestCase
|
|||||||
'psr' => $this->parsePsrRequest('application/json', json_encode($body)),
|
'psr' => $this->parsePsrRequest('application/json', json_encode($body)),
|
||||||
];
|
];
|
||||||
foreach ($parsed as $method => $parsedBody) {
|
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);
|
self::assertFalse($parsedBody->isReadOnly(), $method);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -328,7 +332,29 @@ class RequestParsingTest extends TestCase
|
|||||||
'psr' => $this->parsePsrRequest('application/json', json_encode($body)),
|
'psr' => $this->parsePsrRequest('application/json', json_encode($body)),
|
||||||
];
|
];
|
||||||
foreach ($parsed as $method => $parsedBody) {
|
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);
|
self::assertFalse($parsedBody->isReadOnly(), $method);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -360,6 +386,7 @@ class RequestParsingTest extends TestCase
|
|||||||
null,
|
null,
|
||||||
$body[0]['variables'],
|
$body[0]['variables'],
|
||||||
$body[0]['operationName'],
|
$body[0]['operationName'],
|
||||||
|
null,
|
||||||
$method
|
$method
|
||||||
);
|
);
|
||||||
self::assertValidOperationParams(
|
self::assertValidOperationParams(
|
||||||
@ -368,6 +395,7 @@ class RequestParsingTest extends TestCase
|
|||||||
$body[1]['queryId'],
|
$body[1]['queryId'],
|
||||||
$body[1]['variables'],
|
$body[1]['variables'],
|
||||||
$body[1]['operationName'],
|
$body[1]['operationName'],
|
||||||
|
null,
|
||||||
$method
|
$method
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -231,7 +231,7 @@ class EnumTypeTest extends TestCase
|
|||||||
'{ colorEnum(fromEnum: "GREEN") }',
|
'{ colorEnum(fromEnum: "GREEN") }',
|
||||||
null,
|
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)],
|
'locations' => [new SourceLocation(1, 23)],
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
@ -268,7 +268,7 @@ class EnumTypeTest extends TestCase
|
|||||||
'{ colorEnum(fromEnum: GREENISH) }',
|
'{ colorEnum(fromEnum: GREENISH) }',
|
||||||
null,
|
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)],
|
'locations' => [new SourceLocation(1, 23)],
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
@ -283,7 +283,7 @@ class EnumTypeTest extends TestCase
|
|||||||
'{ colorEnum(fromEnum: green) }',
|
'{ colorEnum(fromEnum: green) }',
|
||||||
null,
|
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)],
|
'locations' => [new SourceLocation(1, 23)],
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
@ -313,7 +313,7 @@ class EnumTypeTest extends TestCase
|
|||||||
$this->expectFailure(
|
$this->expectFailure(
|
||||||
'{ colorEnum(fromEnum: 1) }',
|
'{ colorEnum(fromEnum: 1) }',
|
||||||
null,
|
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(
|
$this->expectFailure(
|
||||||
'{ colorEnum(fromInt: GREEN) }',
|
'{ colorEnum(fromInt: GREEN) }',
|
||||||
null,
|
null,
|
||||||
'Expected type Int, found GREEN.'
|
'Field "colorEnum" argument "fromInt" requires type Int, found GREEN.'
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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')
|
* @see it('should detect if a field on a type was deleted or changed type')
|
||||||
*/
|
*/
|
||||||
|
@ -24,6 +24,7 @@ use GraphQL\Type\Definition\ScalarType;
|
|||||||
use GraphQL\Type\Definition\Type;
|
use GraphQL\Type\Definition\Type;
|
||||||
use GraphQL\Type\Definition\UnionType;
|
use GraphQL\Type\Definition\UnionType;
|
||||||
use GraphQL\Type\Schema;
|
use GraphQL\Type\Schema;
|
||||||
|
use GraphQL\Utils\BuildSchema;
|
||||||
use GraphQL\Utils\SchemaExtender;
|
use GraphQL\Utils\SchemaExtender;
|
||||||
use GraphQL\Utils\SchemaPrinter;
|
use GraphQL\Utils\SchemaPrinter;
|
||||||
use PHPUnit\Framework\TestCase;
|
use PHPUnit\Framework\TestCase;
|
||||||
@ -1962,4 +1963,48 @@ extend type Query {
|
|||||||
$result = GraphQL::executeQuery($extendedSchema, $query);
|
$result = GraphQL::executeQuery($extendedSchema, $query);
|
||||||
self::assertSame(['data' => ['hello' => 'Hello World!']], $result->toArray());
|
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));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -38,7 +38,7 @@ class ValidationTest extends ValidatorTestCase
|
|||||||
';
|
';
|
||||||
|
|
||||||
$expectedError = [
|
$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]],
|
'locations' => [['line' => 3, 'column' => 25]],
|
||||||
];
|
];
|
||||||
|
|
||||||
|
@ -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')
|
* @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(
|
$this->badValueWithMessage('Field "doesKnowCommand" argument "dogCommand" requires type DogCommand, found "SIT"; Did you mean the enum value SIT?', 4, 41),
|
||||||
'DogCommand',
|
|
||||||
'"SIT"',
|
|
||||||
4,
|
|
||||||
41,
|
|
||||||
'Did you mean the enum value SIT?'
|
|
||||||
),
|
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -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(
|
$this->badValueWithMessage('Field "doesKnowCommand" argument "dogCommand" requires type DogCommand, found sit; Did you mean the enum value SIT?', 4, 41),
|
||||||
'DogCommand',
|
|
||||||
'sit',
|
|
||||||
4,
|
|
||||||
41,
|
|
||||||
'Did you mean the enum value SIT?'
|
|
||||||
),
|
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -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->badValueWithMessage('Field "multipleReqs" argument "req2" requires type Int!, found "two".', 4, 32),
|
||||||
$this->badValue('Int!', '"one"', 4, 45),
|
$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(
|
$this->badValueWithMessage('Field "invalidArg" argument "arg" requires type Invalid, found 123; Invalid scalar is always invalid: 123', 3, 27),
|
||||||
'Invalid',
|
|
||||||
'123',
|
|
||||||
3,
|
|
||||||
27,
|
|
||||||
'Invalid scalar is always invalid: 123'
|
|
||||||
),
|
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
|
|
||||||
self::assertEquals(
|
self::assertEquals(
|
||||||
'Invalid scalar is always invalid: 123',
|
'Field "invalidArg" argument "arg" requires type Invalid, found 123; Invalid scalar is always invalid: 123',
|
||||||
$errors[0]->getPrevious()->getMessage()
|
$errors[0]->getMessage()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1411,8 +1398,8 @@ class ValuesOfCorrectTypeTest extends ValidatorTestCase
|
|||||||
}
|
}
|
||||||
',
|
',
|
||||||
[
|
[
|
||||||
$this->badValue('Boolean!', '"yes"', 3, 28),
|
$this->badValueWithMessage('Field "dog" argument "if" requires type Boolean!, found "yes".', 3, 28),
|
||||||
$this->badValue('Boolean!', 'ENUM', 4, 28),
|
$this->badValueWithMessage('Field "name" argument "if" requires type Boolean!, found ENUM.', 4, 28),
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user