Removed previously deprecated classes/methods

This commit is contained in:
Vladimir Razuvaev 2018-05-27 19:13:32 +07:00
parent 2580750d4c
commit 1b22f95a86
41 changed files with 86 additions and 2611 deletions

View File

@ -25,7 +25,6 @@
"sort-packages": true
},
"autoload": {
"files": ["src/deprecated.php"],
"psr-4": {
"GraphQL\\": "src/"
}

View File

@ -1,18 +0,0 @@
<?php
namespace GraphQL;
trigger_error(
'GraphQL\Error was moved to GraphQL\Error\Error and will be deleted on next release',
E_USER_DEPRECATED
);
/**
* Class Error
*
* @deprecated as of 8.0 in favor of GraphQL\Error\Error
* @package GraphQL
*/
class Error extends \GraphQL\Error\Error
{
}

View File

@ -1421,30 +1421,4 @@ class Executor
}
return null;
}
/**
* @deprecated as of v0.8.0 should use self::defaultFieldResolver method
*
* @param $source
* @param $args
* @param $context
* @param ResolveInfo $info
* @return mixed|null
*/
public static function defaultResolveFn($source, $args, $context, ResolveInfo $info)
{
trigger_error(__METHOD__ . ' is renamed to ' . __CLASS__ . '::defaultFieldResolver', E_USER_DEPRECATED);
return self::defaultFieldResolver($source, $args, $context, $info);
}
/**
* @deprecated as of v0.8.0 should use self::setDefaultFieldResolver method
*
* @param callable $fn
*/
public static function setDefaultResolveFn($fn)
{
trigger_error(__METHOD__ . ' is renamed to ' . __CLASS__ . '::setDefaultFieldResolver', E_USER_DEPRECATED);
self::setDefaultFieldResolver($fn);
}
}

View File

@ -182,6 +182,10 @@ class GraphQL
$operationName = null
)
{
trigger_error(
__METHOD__ . ' is deprecated, use GraphQL::executeQuery()->toArray() as a quick replacement',
E_USER_DEPRECATED
);
$result = self::promiseToExecute(
$promiseAdapter = Executor::getPromiseAdapter(),
$schema,
@ -223,6 +227,10 @@ class GraphQL
$operationName = null
)
{
trigger_error(
__METHOD__ . ' is deprecated, use GraphQL::executeQuery() as a quick replacement',
E_USER_DEPRECATED
);
$result = self::promiseToExecute(
$promiseAdapter = Executor::getPromiseAdapter(),
$schema,

View File

@ -108,15 +108,6 @@ class Lexer
return $token;
}
/**
* @return Token
*/
public function nextToken()
{
trigger_error(__METHOD__ . ' is deprecated in favor of advance()', E_USER_DEPRECATED);
return $this->advance();
}
/**
* @param Token $prev
* @return Token

View File

@ -30,40 +30,6 @@ class Token
const BLOCK_STRING = 'BlockString';
const COMMENT = 'Comment';
/**
* @param $kind
* @return mixed
*/
public static function getKindDescription($kind)
{
trigger_error('Deprecated as of 16.10.2016 ($kind itself contains description string now)', E_USER_DEPRECATED);
$description = [];
$description[self::SOF] = '<SOF>';
$description[self::EOF] = '<EOF>';
$description[self::BANG] = '!';
$description[self::DOLLAR] = '$';
$description[self::PAREN_L] = '(';
$description[self::PAREN_R] = ')';
$description[self::SPREAD] = '...';
$description[self::COLON] = ':';
$description[self::EQUALS] = '=';
$description[self::AT] = '@';
$description[self::BRACKET_L] = '[';
$description[self::BRACKET_R] = ']';
$description[self::BRACE_L] = '{';
$description[self::PIPE] = '|';
$description[self::BRACE_R] = '}';
$description[self::NAME] = 'Name';
$description[self::INT] = 'Int';
$description[self::FLOAT] = 'Float';
$description[self::STRING] = 'String';
$description[self::BLOCK_STRING] = 'BlockString';
$description[self::COMMENT] = 'Comment';
return $description[$kind];
}
/**
* The kind of Token (see one of constants above).
*

View File

@ -1,6 +1,10 @@
<?php
namespace GraphQL;
trigger_error(
'GraphQL\Schema is moved to GraphQL\Type\Schema',
E_USER_DEPRECATED
);
/**
* Schema Definition

View File

@ -1,604 +0,0 @@
<?php
namespace GraphQL;
use GraphQL\Error\Error;
use GraphQL\Error\InvariantViolation;
use GraphQL\Executor\ExecutionResult;
use GraphQL\Executor\Executor;
use GraphQL\Executor\Promise\PromiseAdapter;
use GraphQL\Language\AST\DocumentNode;
use GraphQL\Language\Parser;
use GraphQL\Type\Definition\Directive;
use GraphQL\Type\Definition\ObjectType;
use GraphQL\Type\Definition\Type;
use GraphQL\Type\Resolution;
use GraphQL\Validator\DocumentValidator;
use GraphQL\Utils\Utils;
trigger_error(
'GraphQL\Server is deprecated in favor of new implementation: GraphQL\Server\StandardServer and will be removed in next version',
E_USER_DEPRECATED
);
class Server
{
const DEBUG_PHP_ERRORS = 1;
const DEBUG_EXCEPTIONS = 2;
const DEBUG_ALL = 7;
private $queryType;
private $mutationType;
private $subscriptionType;
private $directives;
private $types = [];
private $schema;
private $debug = 0;
private $contextValue;
private $rootValue;
/**
* @var callable
*/
private $phpErrorFormatter = ['GraphQL\Error\FormattedError', 'createFromPHPError'];
/**
* @var callable
*/
private $exceptionFormatter = ['GraphQL\Error\FormattedError', 'createFromException'];
private $unexpectedErrorMessage = 'Unexpected Error';
private $unexpectedErrorStatus = 500;
private $validationRules;
/**
* @var Resolution
*/
private $typeResolutionStrategy;
/**
* @var PromiseAdapter
*/
private $promiseAdapter;
private $phpErrors = [];
/**
* @return static
*/
public static function create()
{
return new static();
}
/**
* @return ObjectType|null
*/
public function getQueryType()
{
return $this->queryType;
}
/**
* @param ObjectType $queryType
* @return Server
*/
public function setQueryType(ObjectType $queryType)
{
$this->assertSchemaNotSet('Query Type', __METHOD__);
$this->queryType = $queryType;
return $this;
}
/**
* @return ObjectType|null
*/
public function getMutationType()
{
return $this->mutationType;
}
/**
* @param ObjectType $mutationType
* @return Server
*/
public function setMutationType(ObjectType $mutationType)
{
$this->assertSchemaNotSet('Mutation Type', __METHOD__);
$this->mutationType = $mutationType;
return $this;
}
/**
* @return ObjectType|null
*/
public function getSubscriptionType()
{
return $this->subscriptionType;
}
/**
* @param ObjectType $subscriptionType
* @return Server
*/
public function setSubscriptionType($subscriptionType)
{
$this->assertSchemaNotSet('Subscription Type', __METHOD__);
$this->subscriptionType = $subscriptionType;
return $this;
}
/**
* @param Type[] $types
* @return Server
*/
public function addTypes(array $types)
{
if (!empty($types)) {
$this->assertSchemaNotSet('Types', __METHOD__);
$this->types = array_merge($this->types, $types);
}
return $this;
}
/**
* @return array
*/
public function getTypes()
{
return $this->types;
}
/**
* @return Directive[]
*/
public function getDirectives()
{
if (null === $this->directives) {
$this->directives = Directive::getInternalDirectives();
}
return $this->directives;
}
/**
* @param Directive[] $directives
* @return Server
*/
public function setDirectives(array $directives)
{
$this->assertSchemaNotSet('Directives', __METHOD__);
$this->directives = $directives;
return $this;
}
/**
* @return int
*/
public function getDebug()
{
return $this->debug;
}
/**
* @param int $debug
* @return Server
*/
public function setDebug($debug = self::DEBUG_ALL)
{
$this->debug = (int) $debug;
return $this;
}
/**
* @return mixed
*/
public function getContext()
{
return $this->contextValue;
}
/**
* @param mixed $context
* @return Server
*/
public function setContext($context)
{
$this->contextValue = $context;
return $this;
}
/**
* @param $rootValue
* @return Server
*/
public function setRootValue($rootValue)
{
$this->rootValue = $rootValue;
return $this;
}
/**
* @return mixed
*/
public function getRootValue()
{
return $this->rootValue;
}
/**
* Set schema instance manually. Mutually exclusive with `setQueryType`, `setMutationType`, `setSubscriptionType`, `setDirectives`, `addTypes`
*
* @param Schema $schema
* @return $this
*/
public function setSchema(Schema $schema)
{
if ($this->queryType) {
$err = 'Query Type is already set';
$errMethod = __CLASS__ . '::setQueryType';
} else if ($this->mutationType) {
$err = 'Mutation Type is already set';
$errMethod = __CLASS__ . '::setMutationType';
} else if ($this->subscriptionType) {
$err = 'Subscription Type is already set';
$errMethod = __CLASS__ . '::setSubscriptionType';
} else if ($this->directives) {
$err = 'Directives are already set';
$errMethod = __CLASS__ . '::setDirectives';
} else if ($this->types) {
$err = 'Additional types are already set';
$errMethod = __CLASS__ . '::addTypes';
} else if ($this->typeResolutionStrategy) {
$err = 'Type Resolution Strategy is already set';
$errMethod = __CLASS__ . '::setTypeResolutionStrategy';
} else if ($this->schema && $this->schema !== $schema) {
$err = 'Different schema is already set';
}
if (isset($err)) {
if (isset($errMethod)) {
$err .= " ($errMethod is mutually exclusive with " . __METHOD__ . ")";
}
throw new InvariantViolation("Cannot set Schema on Server: $err");
}
$this->schema = $schema;
return $this;
}
/**
* @param $entry
* @param $mutuallyExclusiveMethod
*/
private function assertSchemaNotSet($entry, $mutuallyExclusiveMethod)
{
if ($this->schema) {
$schemaMethod = __CLASS__ . '::setSchema';
throw new InvariantViolation("Cannot set $entry on Server: Schema is already set ($mutuallyExclusiveMethod is mutually exclusive with $schemaMethod)");
}
}
/**
* @return Schema
*/
public function getSchema()
{
if (null === $this->schema) {
$this->schema = new Schema([
'query' => $this->queryType,
'mutation' => $this->mutationType,
'subscription' => $this->subscriptionType,
'directives' => $this->directives,
'types' => $this->types,
'typeResolution' => $this->typeResolutionStrategy
]);
}
return $this->schema;
}
/**
* @return callable
*/
public function getPhpErrorFormatter()
{
return $this->phpErrorFormatter;
}
/**
* Expects function(\ErrorException $e) : array
*
* @param callable $phpErrorFormatter
* @return $this
*/
public function setPhpErrorFormatter(callable $phpErrorFormatter)
{
$this->phpErrorFormatter = $phpErrorFormatter;
return $this;
}
/**
* @return callable
*/
public function getExceptionFormatter()
{
return $this->exceptionFormatter;
}
/**
* Expects function(Exception $e) : array
*
* @param callable $exceptionFormatter
* @return $this
*/
public function setExceptionFormatter(callable $exceptionFormatter)
{
$this->exceptionFormatter = $exceptionFormatter;
return $this;
}
/**
* @return string
*/
public function getUnexpectedErrorMessage()
{
return $this->unexpectedErrorMessage;
}
/**
* @param string $unexpectedErrorMessage
* @return $this
*/
public function setUnexpectedErrorMessage($unexpectedErrorMessage)
{
$this->unexpectedErrorMessage = $unexpectedErrorMessage;
return $this;
}
/**
* @return int
*/
public function getUnexpectedErrorStatus()
{
return $this->unexpectedErrorStatus;
}
/**
* @param int $unexpectedErrorStatus
* @return $this
*/
public function setUnexpectedErrorStatus($unexpectedErrorStatus)
{
$this->unexpectedErrorStatus = $unexpectedErrorStatus;
return $this;
}
/**
* Parses GraphQL query string and returns Abstract Syntax Tree for this query
*
* @param string $query
* @return Language\AST\DocumentNode
* @throws \GraphQL\Error\SyntaxError
*/
public function parse($query)
{
return Parser::parse($query);
}
/**
* @return array
*/
public function getValidationRules()
{
if (null === $this->validationRules) {
$this->validationRules = DocumentValidator::allRules();
}
return $this->validationRules;
}
/**
* @param array $validationRules
* @return $this
*/
public function setValidationRules(array $validationRules)
{
$this->validationRules = $validationRules;
return $this;
}
/**
* EXPERIMENTAL!
* This method can be removed or changed in future versions without a prior notice.
*
* @return Resolution
*/
public function getTypeResolutionStrategy()
{
return $this->typeResolutionStrategy;
}
/**
* EXPERIMENTAL!
* This method can be removed or changed in future versions without a prior notice.
*
* @param Resolution $typeResolutionStrategy
* @return Server
*/
public function setTypeResolutionStrategy(Resolution $typeResolutionStrategy)
{
$this->assertSchemaNotSet('Type Resolution Strategy', __METHOD__);
$this->typeResolutionStrategy = $typeResolutionStrategy;
return $this;
}
/**
* @return PromiseAdapter|null
*/
public function getPromiseAdapter()
{
return $this->promiseAdapter;
}
/**
* See /docs/data-fetching.md#async-php
*
* @param PromiseAdapter $promiseAdapter
* @return Server
*/
public function setPromiseAdapter(PromiseAdapter $promiseAdapter)
{
if ($this->promiseAdapter && $promiseAdapter !== $this->promiseAdapter) {
throw new InvariantViolation("Cannot set promise adapter: Different adapter is already set");
}
$this->promiseAdapter = $promiseAdapter;
return $this;
}
/**
* Returns array with validation errors (empty when there are no validation errors)
*
* @param DocumentNode $query
* @return array
*/
public function validate(DocumentNode $query)
{
try {
$schema = $this->getSchema();
$schema->assertValid();
} catch (InvariantViolation $e) {
throw new InvariantViolation("Cannot validate, schema contains errors: {$e->getMessage()}", null, $e);
}
return DocumentValidator::validate($schema, $query, $this->validationRules);
}
/**
* Executes GraphQL query against this server and returns execution result
*
* @param string|DocumentNode $query
* @param array|null $variables
* @param string|null $operationName
* @return ExecutionResult
*/
public function executeQuery($query, array $variables = null, $operationName = null)
{
$this->phpErrors = [];
if ($this->debug & static::DEBUG_PHP_ERRORS) {
// Catch custom errors (to report them in query results)
set_error_handler(function($severity, $message, $file, $line) {
$this->phpErrors[] = new \ErrorException($message, 0, $severity, $file, $line);
});
}
if ($this->promiseAdapter) {
// TODO: inline GraphQL::executeAndReturnResult and pass promise adapter to executor constructor directly
$promiseAdapter = Executor::getPromiseAdapter();
Executor::setPromiseAdapter($this->promiseAdapter);
}
$result = GraphQL::executeAndReturnResult(
$this->getSchema(),
$query,
$this->getRootValue(),
$this->getContext(),
$variables,
$operationName
);
if (isset($promiseAdapter)) {
Executor::setPromiseAdapter($promiseAdapter);
}
// Add details about original exception in error entry (if any)
if ($this->debug & static::DEBUG_EXCEPTIONS) {
$result->setErrorFormatter([$this, 'formatError']);
}
// Add reported PHP errors to result (if any)
if (!empty($this->phpErrors) && ($this->debug & static::DEBUG_PHP_ERRORS)) {
$result->extensions['phpErrors'] = array_map($this->phpErrorFormatter, $this->phpErrors);
}
if ($this->debug & static::DEBUG_PHP_ERRORS) {
restore_error_handler();
}
return $result;
}
/**
* GraphQL HTTP endpoint compatible with express-graphql
*/
public function handleRequest()
{
try {
$httpStatus = 200;
if (isset($_SERVER['CONTENT_TYPE']) && strpos($_SERVER['CONTENT_TYPE'], 'application/json') !== false) {
$raw = $this->readInput();
$data = json_decode($raw, true);
Utils::invariant(
is_array($data),
"GraphQL Server expects JSON object with keys 'query' and 'variables', but got " . Utils::getVariableType($data)
);
} else {
$data = $_REQUEST ?: [];
}
$data += ['query' => null, 'variables' => null];
$result = $this->executeQuery($data['query'], (array) $data['variables'])->toArray();
} catch (\Exception $e) {
// This is only possible for schema creation errors and some very unpredictable errors,
// (all errors which occur during query execution are caught and included in final response)
$httpStatus = $this->unexpectedErrorStatus;
$error = new Error($this->unexpectedErrorMessage, null, null, null, null, $e);
$result = ['errors' => [$this->formatError($error)]];
} catch (\Throwable $e) {
$httpStatus = $this->unexpectedErrorStatus;
$error = new Error($this->unexpectedErrorMessage, null, null, null, null, $e);
$result = ['errors' => [$this->formatError($error)]];
}
$this->produceOutput($result, $httpStatus);
}
/**
* @param \Throwable $e
*/
private function formatException($e)
{
$formatter = $this->exceptionFormatter;
return $formatter($e);
}
/**
* @param Error $e
* @return array
*/
public function formatError(\GraphQL\Error\Error $e)
{
$result = $e->toSerializableArray();
if (($this->debug & static::DEBUG_EXCEPTIONS) && $e->getPrevious()) {
$result['exception'] = $this->formatException($e->getPrevious());
}
return $result;
}
protected function readInput()
{
return file_get_contents('php://input') ?: '';
}
protected function produceOutput(array $result, $httpStatus)
{
header('Content-Type: application/json', true, $httpStatus);
echo json_encode($result);
}
}

View File

@ -1,307 +0,0 @@
<?php
namespace GraphQL\Type\Definition;
use GraphQL\Error\InvariantViolation;
use GraphQL\Error\Warning;
use GraphQL\Utils\Utils;
/**
* Class Config
* @package GraphQL\Type\Definition
* @deprecated See https://github.com/webonyx/graphql-php/issues/148 for alternatives
*/
class Config
{
const BOOLEAN = 1;
const STRING = 2;
const INT = 4;
const FLOAT = 8;
const NUMERIC = 16;
const SCALAR = 32;
const CALLBACK = 64;
const ANY = 128;
const NAME = 256;
const OUTPUT_TYPE = 2048;
const INPUT_TYPE = 4096;
const INTERFACE_TYPE = 8192;
const OBJECT_TYPE = 16384;
const REQUIRED = 65536;
const KEY_AS_NAME = 131072;
const MAYBE_THUNK = 262144;
const MAYBE_TYPE = 524288;
const MAYBE_NAME = 1048576;
/**
* @var bool
*/
private static $enableValidation = false;
/**
* @var bool
*/
private static $allowCustomOptions = true;
/**
*
* Disables config validation
*
* @deprecated See https://github.com/webonyx/graphql-php/issues/148 for alternatives
*/
public static function disableValidation()
{
self::$enableValidation = false;
}
/**
* Enable deep config validation (disabled by default because it creates significant performance overhead).
* Useful only at development to catch type definition errors quickly.
*
* @deprecated See https://github.com/webonyx/graphql-php/issues/148 for alternatives
*/
public static function enableValidation($allowCustomOptions = true)
{
Warning::warnOnce(
'GraphQL\Type\Defintion\Config is deprecated and will be removed in the next version. ' .
'See https://github.com/webonyx/graphql-php/issues/148 for alternatives',
Warning::WARNING_CONFIG_DEPRECATION,
E_USER_DEPRECATED
);
self::$enableValidation = true;
self::$allowCustomOptions = $allowCustomOptions;
}
/**
* @return bool
*/
public static function isValidationEnabled()
{
return self::$enableValidation;
}
/**
* @param array $config
* @param array $definition
*/
public static function validate(array $config, array $definition)
{
if (self::$enableValidation) {
$name = isset($config['name']) ? $config['name'] : '(Unnamed Type)';
self::validateMap($name, $config, $definition);
}
}
/**
* @param $typeName
* @param array $config
* @param array $definition
*/
public static function validateField($typeName, array $config, array $definition)
{
if (self::$enableValidation) {
if (!isset($config['name'])) {
$pathStr = isset($config['type'])
? '(Unknown Field of type: ' . Utils::printSafe($config['type']) . ')'
: '(Unknown Field)';
} else {
$pathStr = '';
}
self::validateMap($typeName ?: '(Unnamed Type)', $config, $definition, $pathStr);
}
}
/**
* @param array|int $definition
* @param int $flags
* @return \stdClass
*/
public static function arrayOf($definition, $flags = 0)
{
$tmp = new \stdClass();
$tmp->isArray = true;
$tmp->definition = $definition;
$tmp->flags = (int) $flags;
return $tmp;
}
/**
* @param $typeName
* @param array $map
* @param array $definitions
* @param null $pathStr
*/
private static function validateMap($typeName, array $map, array $definitions, $pathStr = null)
{
$suffix = $pathStr ? " at $pathStr" : '';
// Make sure there are no unexpected keys in map
$unexpectedKeys = array_keys(array_diff_key($map, $definitions));
if (!empty($unexpectedKeys)) {
if (!self::$allowCustomOptions) {
Warning::warnOnce(
sprintf('Error in "%s" type definition: Non-standard keys "%s" ' . $suffix, $typeName, implode(', ', $unexpectedKeys)),
Warning::WARNING_CONFIG
);
}
$map = array_intersect_key($map, $definitions);
}
// Make sure that all required keys are present in map
$requiredKeys = array_filter($definitions, function($def) {return (self::getFlags($def) & self::REQUIRED) > 0;});
$missingKeys = array_keys(array_diff_key($requiredKeys, $map));
Utils::invariant(
empty($missingKeys),
'Error in "' . $typeName . '" type definition: Required keys missing: "%s" %s', implode(', ', $missingKeys), $suffix
);
// Make sure that every map value is valid given the definition
foreach ($map as $key => $value) {
self::validateEntry($typeName, $key, $value, $definitions[$key], $pathStr ? "$pathStr:$key" : $key);
}
}
/**
* @param $typeName
* @param $key
* @param $value
* @param $def
* @param $pathStr
* @throws \Exception
*/
private static function validateEntry($typeName, $key, $value, $def, $pathStr)
{
$type = Utils::getVariableType($value);
$err = 'Error in "'.$typeName.'" type definition: expecting "%s" at "' . $pathStr . '", but got "' . $type . '"';
if ($def instanceof \stdClass) {
if (($def->flags & self::REQUIRED) === 0 && $value === null) {
return ;
}
if (($def->flags & self::MAYBE_THUNK) > 0) {
// TODO: consider wrapping thunk with other function to force validation of value returned by thunk
Utils::invariant(is_array($value) || is_callable($value), $err, 'array or callable');
} else {
Utils::invariant(is_array($value), $err, 'array');
}
if (!empty($def->isArray)) {
if ($def->flags & self::REQUIRED) {
Utils::invariant(!empty($value), 'Error in "'.$typeName.'" type definition: ' . "Value at '$pathStr' cannot be empty array");
}
$err = 'Error in "'.$typeName.'" type definition: ' . "Each entry at '$pathStr' must be an array, but entry at '%s' is '%s'";
foreach ($value as $arrKey => $arrValue) {
if (is_array($def->definition)) {
if ($def->flags & self::MAYBE_TYPE && $arrValue instanceof Type) {
$arrValue = ['type' => $arrValue];
}
if ($def->flags & self::MAYBE_NAME && is_string($arrValue)) {
$arrValue = ['name' => $arrValue];
}
if (!$arrValue instanceof FieldDefinition) {
Utils::invariant(is_array($arrValue), $err, $arrKey, Utils::getVariableType($arrValue));
if ($def->flags & self::KEY_AS_NAME && is_string($arrKey)) {
$arrValue += ['name' => $arrKey];
}
self::validateMap($typeName, $arrValue, $def->definition, "$pathStr:$arrKey");
}
} else {
self::validateEntry($typeName, $arrKey, $arrValue, $def->definition, "$pathStr:$arrKey");
}
}
} else {
throw new InvariantViolation('Error in "'.$typeName.'" type definition: ' . "unexpected definition: " . print_r($def, true));
}
} else {
Utils::invariant(is_int($def), 'Error in "'.$typeName.'" type definition: ' . "Definition for '$pathStr' is expected to be single integer value");
if ($def & self::REQUIRED) {
Utils::invariant($value !== null, 'Error in "'.$typeName.'" type definition: ' . 'Value at "%s" can not be null', $pathStr);
}
if (null === $value) {
return ; // Allow nulls for non-required fields
}
switch (true) {
case $def & self::ANY:
break;
case $def & self::BOOLEAN:
Utils::invariant(is_bool($value), $err, 'boolean');
break;
case $def & self::STRING:
Utils::invariant(is_string($value), $err, 'string');
break;
case $def & self::NUMERIC:
Utils::invariant(is_numeric($value), $err, 'numeric');
break;
case $def & self::FLOAT:
Utils::invariant(is_float($value) || is_int($value), $err, 'float');
break;
case $def & self::INT:
Utils::invariant(is_int($value), $err, 'int');
break;
case $def & self::CALLBACK:
Utils::invariant(is_callable($value), $err, 'callable');
break;
case $def & self::SCALAR:
Utils::invariant(is_scalar($value), $err, 'scalar');
break;
case $def & self::NAME:
Utils::invariant(is_string($value), $err, 'name');
Utils::invariant(
preg_match('~^[_a-zA-Z][_a-zA-Z0-9]*$~', $value),
'Names must match /^[_a-zA-Z][_a-zA-Z0-9]*$/ but "%s" does not.',
$value
);
break;
case $def & self::INPUT_TYPE:
Utils::invariant(
is_callable($value) || $value instanceof InputType,
$err,
'InputType definition'
);
break;
case $def & self::OUTPUT_TYPE:
Utils::invariant(
is_callable($value) || $value instanceof OutputType,
$err,
'OutputType definition'
);
break;
case $def & self::INTERFACE_TYPE:
Utils::invariant(
is_callable($value) || $value instanceof InterfaceType,
$err,
'InterfaceType definition'
);
break;
case $def & self::OBJECT_TYPE:
Utils::invariant(
is_callable($value) || $value instanceof ObjectType,
$err,
'ObjectType definition'
);
break;
default:
throw new InvariantViolation("Unexpected validation rule: " . $def);
}
}
}
/**
* @param $def
* @return mixed
*/
private static function getFlags($def)
{
return is_object($def) ? $def->flags : $def;
}
}

View File

@ -1,17 +0,0 @@
<?php
namespace GraphQL\Type\Definition;
use GraphQL\Language\DirectiveLocation as NewDirectiveLocation;
trigger_error(
'GraphQL\Type\Definition\DirectiveLocation was moved to GraphQL\Language\DirectiveLocation and will be deleted on next release',
E_USER_DEPRECATED
);
/**
* @deprecated moved to GraphQL\Language\DirectiveLocation
*/
class DirectiveLocation extends NewDirectiveLocation
{
}

View File

@ -42,17 +42,6 @@ class EnumType extends Type implements InputType, OutputType, LeafType, NamedTyp
Utils::invariant(is_string($config['name']), 'Must provide name.');
Config::validate($config, [
'name' => Config::NAME | Config::REQUIRED,
'values' => Config::arrayOf([
'name' => Config::NAME | Config::REQUIRED,
'value' => Config::ANY,
'deprecationReason' => Config::STRING,
'description' => Config::STRING
], Config::KEY_AS_NAME | Config::MAYBE_NAME),
'description' => Config::STRING
]);
$this->name = $config['name'];
$this->description = isset($config['description']) ? $config['description'] : null;
$this->astNode = isset($config['astNode']) ? $config['astNode'] : null;

View File

@ -69,28 +69,6 @@ class FieldDefinition
private static $def;
/**
* @return array
*/
public static function getDefinition()
{
return self::$def ?: (self::$def = [
'name' => Config::NAME | Config::REQUIRED,
'type' => Config::OUTPUT_TYPE | Config::REQUIRED,
'args' => Config::arrayOf([
'name' => Config::NAME | Config::REQUIRED,
'type' => Config::INPUT_TYPE | Config::REQUIRED,
'description' => Config::STRING,
'defaultValue' => Config::ANY
], Config::KEY_AS_NAME | Config::MAYBE_TYPE),
'resolve' => Config::CALLBACK,
'map' => Config::CALLBACK,
'description' => Config::STRING,
'deprecationReason' => Config::STRING,
'complexity' => Config::CALLBACK,
]);
}
public static function defineFieldMap(Type $type, $fields)
{
if (is_callable($fields)) {
@ -129,42 +107,6 @@ class FieldDefinition
return $map;
}
/**
* @param array $fields
* @param string $parentTypeName
* @deprecated use defineFieldMap instead
* @return array
*/
public static function createMap(array $fields, $parentTypeName = null)
{
trigger_error(
__METHOD__ . ' is deprecated, use ' . __CLASS__ . '::defineFieldMap() instead',
E_USER_DEPRECATED
);
$map = [];
foreach ($fields as $name => $field) {
if (is_array($field)) {
if (!isset($field['name']) && is_string($name)) {
$field['name'] = $name;
}
$fieldDef = self::create($field);
} else if ($field instanceof FieldDefinition) {
$fieldDef = $field;
} else {
if (is_string($name)) {
$fieldDef = self::create(['name' => $name, 'type' => $field]);
} else {
throw new InvariantViolation(
"Unexpected field definition for type $parentTypeName at field $name: " . Utils::printSafe($field)
);
}
}
$map[$fieldDef->name] = $fieldDef;
}
return $map;
}
/**
* @param array|Config $field
* @param string $typeName
@ -172,9 +114,6 @@ class FieldDefinition
*/
public static function create($field, $typeName = null)
{
if ($typeName) {
Config::validateField($typeName, $field, self::getDefinition());
}
return new self($field);
}

View File

@ -33,17 +33,6 @@ class InputObjectType extends Type implements InputType, NamedType
Utils::invariant(is_string($config['name']), 'Must provide name.');
Config::validate($config, [
'name' => Config::NAME | Config::REQUIRED,
'fields' => Config::arrayOf([
'name' => Config::NAME | Config::REQUIRED,
'type' => Config::INPUT_TYPE | Config::REQUIRED,
'defaultValue' => Config::ANY,
'description' => Config::STRING
], Config::KEY_AS_NAME | Config::MAYBE_THUNK | Config::MAYBE_TYPE),
'description' => Config::STRING
]);
$this->config = $config;
$this->name = $config['name'];
$this->astNode = isset($config['astNode']) ? $config['astNode'] : null;

View File

@ -53,16 +53,6 @@ class InterfaceType extends Type implements AbstractType, OutputType, CompositeT
Utils::invariant(is_string($config['name']), 'Must provide name.');
Config::validate($config, [
'name' => Config::NAME,
'fields' => Config::arrayOf(
FieldDefinition::getDefinition(),
Config::KEY_AS_NAME | Config::MAYBE_THUNK | Config::MAYBE_TYPE
),
'resolveType' => Config::CALLBACK, // function($value, $context, ResolveInfo $info) => ObjectType
'description' => Config::STRING
]);
$this->name = $config['name'];
$this->description = isset($config['description']) ? $config['description'] : null;
$this->astNode = isset($config['astNode']) ? $config['astNode'] : null;

View File

@ -105,23 +105,6 @@ class ObjectType extends Type implements OutputType, CompositeType, NamedType
Utils::invariant(is_string($config['name']), 'Must provide name.');
// Note: this validation is disabled by default, because it is resource-consuming
// TODO: add bin/validate script to check if schema is valid during development
Config::validate($config, [
'name' => Config::NAME | Config::REQUIRED,
'fields' => Config::arrayOf(
FieldDefinition::getDefinition(),
Config::KEY_AS_NAME | Config::MAYBE_THUNK | Config::MAYBE_TYPE
),
'description' => Config::STRING,
'interfaces' => Config::arrayOf(
Config::INTERFACE_TYPE,
Config::MAYBE_THUNK
),
'isTypeOf' => Config::CALLBACK, // ($value, $context, ResolveInfo $info) => boolean
'resolveField' => Config::CALLBACK // ($value, $args, $context, ResolveInfo $info) => $fieldValue
]);
$this->name = $config['name'];
$this->description = isset($config['description']) ? $config['description'] : null;
$this->resolveFieldFn = isset($config['resolveField']) ? $config['resolveField'] : null;

View File

@ -1,7 +1,6 @@
<?php
namespace GraphQL\Type\Definition;
use GraphQL\Error\InvariantViolation;
use GraphQL\Language\AST\FieldNode;
use GraphQL\Language\AST\FragmentDefinitionNode;
use GraphQL\Language\AST\FragmentSpreadNode;
@ -25,12 +24,6 @@ class ResolveInfo
*/
public $fieldName;
/**
* @var FieldNode[]
* @deprecated as of 8.0 (Renamed to $fieldNodes)
*/
public $fieldASTs;
/**
* AST of all nodes referencing this field in the query.
*
@ -106,7 +99,6 @@ class ResolveInfo
public function __construct(array $values)
{
Utils::assign($this, $values);
$this->fieldASTs = $this->fieldNodes;
}
/**
@ -179,13 +171,4 @@ class ResolveInfo
return $fields;
}
public function __get($name)
{
if ('fieldASTs' === $name) {
trigger_error('Property ' . __CLASS__ . '->fieldASTs was renamed to ' . __CLASS__ . '->fieldNodes', E_USER_DEPRECATED);
return $this->fieldNodes;
}
throw new InvariantViolation("Undefined property '$name' in class " . __CLASS__);
}
}

View File

@ -38,13 +38,6 @@ class UnionType extends Type implements AbstractType, OutputType, CompositeType,
Utils::invariant(is_string($config['name']), 'Must provide name.');
Config::validate($config, [
'name' => Config::NAME | Config::REQUIRED,
'types' => Config::arrayOf(Config::OBJECT_TYPE, Config::MAYBE_THUNK | Config::REQUIRED),
'resolveType' => Config::CALLBACK, // function($value, ResolveInfo $info) => ObjectType
'description' => Config::STRING
]);
/**
* Optionally provide a custom type resolver function. If one is not provided,
* the default implemenation will call `isTypeOf` on each implementing
@ -56,15 +49,6 @@ class UnionType extends Type implements AbstractType, OutputType, CompositeType,
$this->config = $config;
}
/**
* @return ObjectType[]
*/
public function getPossibleTypes()
{
trigger_error(__METHOD__ . ' is deprecated in favor of ' . __CLASS__ . '::getTypes()', E_USER_DEPRECATED);
return $this->getTypes();
}
/**
* @return ObjectType[]
*/

View File

@ -48,7 +48,11 @@ class Introspection
public static function getIntrospectionQuery($options = [])
{
if (is_bool($options)) {
trigger_error('Calling Introspection::getIntrospectionQuery(boolean) is deprecated. Please use Introspection::getIntrospectionQuery(["descriptions" => boolean]).', E_USER_DEPRECATED);
trigger_error(
'Calling Introspection::getIntrospectionQuery(boolean) is deprecated. '.
'Please use Introspection::getIntrospectionQuery(["descriptions" => boolean]).',
E_USER_DEPRECATED
);
$descriptions = $options;
} else {
$descriptions = !array_key_exists('descriptions', $options) || $options['descriptions'] === true;

View File

@ -75,21 +75,6 @@ class Schema
*/
public function __construct($config)
{
if (func_num_args() > 1 || $config instanceof Type) {
trigger_error(
'GraphQL\Schema constructor expects config object now instead of types passed as arguments. '.
'See https://github.com/webonyx/graphql-php/issues/36',
E_USER_DEPRECATED
);
list($queryType, $mutationType, $subscriptionType) = func_get_args() + [null, null, null];
$config = [
'query' => $queryType,
'mutation' => $mutationType,
'subscription' => $subscriptionType
];
}
if (is_array($config)) {
$config = SchemaConfig::create($config);
}

View File

@ -96,20 +96,6 @@ class SchemaConfig
$config->setDirectives($options['directives']);
}
if (isset($options['typeResolution'])) {
trigger_error(
'Type resolution strategies are deprecated. Just pass single option `typeLoader` '.
'to schema constructor instead.',
E_USER_DEPRECATED
);
if ($options['typeResolution'] instanceof Resolution && !isset($options['typeLoader'])) {
$strategy = $options['typeResolution'];
$options['typeLoader'] = function($name) use ($strategy) {
return $strategy->resolveType($name);
};
}
}
if (isset($options['typeLoader'])) {
Utils::invariant(
is_callable($options['typeLoader']),

View File

@ -1,14 +0,0 @@
<?php
namespace GraphQL;
trigger_error(
'GraphQL\Utils was moved to GraphQL\Utils\Utils and will be deleted on next release',
E_USER_DEPRECATED
);
/**
* @deprecated Use GraphQL\Utils\Utils
*/
class Utils extends \GraphQL\Utils\Utils
{
}

View File

@ -1,36 +0,0 @@
<?php
if (defined('GRAPHQL_WITH_DEPRECATED') && !GRAPHQL_WITH_DEPRECATED) {
return ;
}
// Renamed as of 8.0:
class_alias('GraphQL\Error\FormattedError', 'GraphQL\FormattedError');
class_alias('GraphQL\Error\SyntaxError', 'GraphQL\SyntaxError');
class_alias('GraphQL\Language\AST\ArgumentNode', 'GraphQL\Language\AST\Argument');
class_alias('GraphQL\Language\AST\BooleanValueNode', 'GraphQL\Language\AST\BooleanValue');
class_alias('GraphQL\Language\AST\DefinitionNode', 'GraphQL\Language\AST\Definition');
class_alias('GraphQL\Language\AST\DirectiveNode', 'GraphQL\Language\AST\Directive');
class_alias('GraphQL\Language\AST\DocumentNode', 'GraphQL\Language\AST\Document');
class_alias('GraphQL\Language\AST\EnumValueNode', 'GraphQL\Language\AST\EnumValue');
class_alias('GraphQL\Language\AST\FieldNode', 'GraphQL\Language\AST\Field');
class_alias('GraphQL\Language\AST\FloatValueNode', 'GraphQL\Language\AST\FloatValue');
class_alias('GraphQL\Language\AST\FragmentDefinitionNode', 'GraphQL\Language\AST\FragmentDefinition');
class_alias('GraphQL\Language\AST\FragmentSpreadNode', 'GraphQL\Language\AST\FragmentSpread');
class_alias('GraphQL\Language\AST\InlineFragmentNode', 'GraphQL\Language\AST\InlineFragment');
class_alias('GraphQL\Language\AST\IntValueNode', 'GraphQL\Language\AST\IntValue');
class_alias('GraphQL\Language\AST\ListTypeNode', 'GraphQL\Language\AST\ListType');
class_alias('GraphQL\Language\AST\ListValueNode', 'GraphQL\Language\AST\ListValue');
class_alias('GraphQL\Language\AST\NamedTypeNode', 'GraphQL\Language\AST\NamedType');
class_alias('GraphQL\Language\AST\NameNode', 'GraphQL\Language\AST\Name');
class_alias('GraphQL\Language\AST\NonNullTypeNode', 'GraphQL\Language\AST\NonNullType');
class_alias('GraphQL\Language\AST\ObjectFieldNode', 'GraphQL\Language\AST\ObjectField');
class_alias('GraphQL\Language\AST\ObjectValueNode', 'GraphQL\Language\AST\ObjectValue');
class_alias('GraphQL\Language\AST\OperationDefinitionNode', 'GraphQL\Language\AST\OperationDefinition');
class_alias('GraphQL\Language\AST\SelectionNode', 'GraphQL\Language\AST\Selection');
class_alias('GraphQL\Language\AST\SelectionSetNode', 'GraphQL\Language\AST\SelectionSet');
class_alias('GraphQL\Language\AST\StringValueNode', 'GraphQL\Language\AST\StringValue');
class_alias('GraphQL\Language\AST\TypeNode', 'GraphQL\Language\AST\Type');
class_alias('GraphQL\Language\AST\ValueNode', 'GraphQL\Language\AST\Value');
class_alias('GraphQL\Language\AST\VariableDefinitionNode', 'GraphQL\Language\AST\VariableDefinition');
class_alias('GraphQL\Language\AST\VariableNode', 'GraphQL\Language\AST\Variable');

View File

@ -5,7 +5,7 @@ use GraphQL\Deferred;
use GraphQL\Error\UserError;
use GraphQL\Error\Warning;
use GraphQL\GraphQL;
use GraphQL\Schema;
use GraphQL\Type\Schema;
use GraphQL\Type\Definition\InterfaceType;
use GraphQL\Type\Definition\ObjectType;
use GraphQL\Type\Definition\Type;
@ -87,7 +87,7 @@ class AbstractPromiseTest extends \PHPUnit_Framework_TestCase
}
}';
$result = GraphQL::execute($schema, $query);
$result = GraphQL::executeQuery($schema, $query)->toArray();
$expected = [
'data' => [
@ -172,7 +172,7 @@ class AbstractPromiseTest extends \PHPUnit_Framework_TestCase
}
}';
$result = GraphQL::execute($schema, $query);
$result = GraphQL::executeQuery($schema, $query)->toArray();
$expected = [
'data' => [
@ -259,7 +259,7 @@ class AbstractPromiseTest extends \PHPUnit_Framework_TestCase
}
}';
$result = GraphQL::execute($schema, $query);
$result = GraphQL::executeQuery($schema, $query)->toArray();
$expected = [
'data' => [
@ -357,7 +357,7 @@ class AbstractPromiseTest extends \PHPUnit_Framework_TestCase
}
}';
$result = GraphQL::executeAndReturnResult($schema, $query)->toArray(true);
$result = GraphQL::executeQuery($schema, $query)->toArray(true);
$expected = [
'data' => [
@ -458,7 +458,7 @@ class AbstractPromiseTest extends \PHPUnit_Framework_TestCase
}
}';
$result = GraphQL::executeAndReturnResult($schema, $query)->toArray(true);
$result = GraphQL::executeQuery($schema, $query)->toArray(true);
$expected = [
'data' => [
@ -552,7 +552,7 @@ class AbstractPromiseTest extends \PHPUnit_Framework_TestCase
}
}';
$result = GraphQL::execute($schema, $query);
$result = GraphQL::executeQuery($schema, $query)->toArray();
$expected = [
'data' => [
@ -631,7 +631,7 @@ class AbstractPromiseTest extends \PHPUnit_Framework_TestCase
}
}';
$result = GraphQL::execute($schema, $query);
$result = GraphQL::executeQuery($schema, $query)->toArray();
$expected = [
'data' => [

View File

@ -256,7 +256,7 @@ class AbstractTest extends \PHPUnit_Framework_TestCase
'path' => ['pets', 2]
]]
];
$actual = GraphQL::executeAndReturnResult($schema, $query)->toArray(true);
$actual = GraphQL::executeQuery($schema, $query)->toArray(true);
$this->assertArraySubset($expected, $actual);
}
@ -336,7 +336,7 @@ class AbstractTest extends \PHPUnit_Framework_TestCase
}
}';
$result = GraphQL::executeAndReturnResult($schema, $query)->toArray(true);
$result = GraphQL::executeQuery($schema, $query)->toArray(true);
$expected = [
'data' => [
'pets' => [
@ -421,7 +421,7 @@ class AbstractTest extends \PHPUnit_Framework_TestCase
}
}';
$result = GraphQL::execute($schema, $query);
$result = GraphQL::executeQuery($schema, $query)->toArray();
$this->assertEquals([
'data' => [

View File

@ -5,7 +5,7 @@ namespace GraphQL\Tests\Executor;
use GraphQL\Deferred;
use GraphQL\Executor\Executor;
use GraphQL\Language\Parser;
use GraphQL\Schema;
use GraphQL\Type\Schema;
use GraphQL\Type\Definition\ObjectType;
use GraphQL\Type\Definition\ResolveInfo;
use GraphQL\Type\Definition\Type;

View File

@ -3,7 +3,7 @@ namespace GraphQL\Tests\Executor;
use GraphQL\Executor\Executor;
use GraphQL\Language\Parser;
use GraphQL\Schema;
use GraphQL\Type\Schema;
use GraphQL\Type\Definition\ObjectType;
use GraphQL\Type\Definition\Type;

View File

@ -8,7 +8,7 @@ use GraphQL\Error\Error;
use GraphQL\Error\UserError;
use GraphQL\Executor\Executor;
use GraphQL\Language\Parser;
use GraphQL\Schema;
use GraphQL\Type\Schema;
use GraphQL\Type\Definition\InputObjectType;
use GraphQL\Type\Definition\InterfaceType;
use GraphQL\Type\Definition\ObjectType;
@ -252,7 +252,6 @@ class ExecutorTest extends \PHPUnit_Framework_TestCase
$this->assertEquals([
'fieldName',
'fieldASTs',
'fieldNodes',
'returnType',
'parentType',

View File

@ -8,7 +8,7 @@ namespace GraphQL\Tests\Executor;
use GraphQL\Executor\Executor;
use GraphQL\Language\Parser;
use GraphQL\Schema;
use GraphQL\Type\Schema;
use GraphQL\Type\Definition\InterfaceType;
use GraphQL\Type\Definition\ObjectType;
use GraphQL\Type\Definition\Type;

View File

@ -8,7 +8,7 @@ use GraphQL\Executor\Executor;
use GraphQL\Error\FormattedError;
use GraphQL\Language\Parser;
use GraphQL\Language\SourceLocation;
use GraphQL\Schema;
use GraphQL\Type\Schema;
use GraphQL\Type\Definition\ObjectType;
use GraphQL\Type\Definition\Type;

View File

@ -7,7 +7,7 @@ use GraphQL\Executor\Executor;
use GraphQL\Error\FormattedError;
use GraphQL\Language\Parser;
use GraphQL\Language\SourceLocation;
use GraphQL\Schema;
use GraphQL\Type\Schema;
use GraphQL\Type\Definition\ObjectType;
use GraphQL\Type\Definition\Type;

View File

@ -8,7 +8,7 @@ use GraphQL\Executor\Executor;
use GraphQL\Error\FormattedError;
use GraphQL\Language\Parser;
use GraphQL\Language\SourceLocation;
use GraphQL\Schema;
use GraphQL\Type\Schema;
use GraphQL\Type\Definition\ObjectType;
use GraphQL\Type\Definition\Type;

View File

@ -2,7 +2,7 @@
namespace GraphQL\Tests\Executor;
use GraphQL\GraphQL;
use GraphQL\Schema;
use GraphQL\Type\Schema;
use GraphQL\Type\Definition\ObjectType;
use GraphQL\Type\Definition\Type;
@ -37,7 +37,7 @@ class ResolveTest extends \PHPUnit_Framework_TestCase
$this->assertEquals(
['data' => ['test' => 'testValue']],
GraphQL::execute($schema, '{ test }', $source)
GraphQL::executeQuery($schema, '{ test }', $source)->toArray()
);
}
@ -56,7 +56,7 @@ class ResolveTest extends \PHPUnit_Framework_TestCase
];
$this->assertEquals(
['data' => ['test' => $_secret]],
GraphQL::execute($schema, '{ test }', $source)
GraphQL::executeQuery($schema, '{ test }', $source)->toArray()
);
}
@ -74,7 +74,7 @@ class ResolveTest extends \PHPUnit_Framework_TestCase
$source = new Adder(700);
$result = GraphQL::execute($schema, '{ test(addend1: 80) }', $source, ['addend2' => 9]);
$result = GraphQL::executeQuery($schema, '{ test(addend1: 80) }', $source, ['addend2' => 9])->toArray();
$this->assertEquals(['data' => ['test' => 789]], $result);
}
@ -96,22 +96,22 @@ class ResolveTest extends \PHPUnit_Framework_TestCase
$this->assertEquals(
['data' => ['test' => '[null,[]]']],
GraphQL::execute($schema, '{ test }')
GraphQL::executeQuery($schema, '{ test }')->toArray()
);
$this->assertEquals(
['data' => ['test' => '["Source!",[]]']],
GraphQL::execute($schema, '{ test }', 'Source!')
GraphQL::executeQuery($schema, '{ test }', 'Source!')->toArray()
);
$this->assertEquals(
['data' => ['test' => '["Source!",{"aStr":"String!"}]']],
GraphQL::execute($schema, '{ test(aStr: "String!") }', 'Source!')
GraphQL::executeQuery($schema, '{ test(aStr: "String!") }', 'Source!')->toArray()
);
$this->assertEquals(
['data' => ['test' => '["Source!",{"aStr":"String!","aInt":-123}]']],
GraphQL::execute($schema, '{ test(aInt: -123, aStr: "String!") }', 'Source!')
GraphQL::executeQuery($schema, '{ test(aInt: -123, aStr: "String!") }', 'Source!')->toArray()
);
}
}

View File

@ -394,7 +394,7 @@ class UnionInterfaceTest extends \PHPUnit_Framework_TestCase
$this->assertEquals(
['data' => ['name' => 'John', 'friends' => [['name' => 'Liz']]]],
GraphQL::execute($schema2, $ast, $john2, $context)
GraphQL::executeQuery($schema2, $ast, $john2, $context)->toArray()
);
$this->assertSame($context, $encounteredContext);
$this->assertSame($schema2, $encounteredSchema);

View File

@ -1,626 +0,0 @@
<?php
namespace GraphQL\Tests;
use GraphQL\Error\Debug;
use GraphQL\Error\FormattedError;
use GraphQL\Error\InvariantViolation;
use GraphQL\Error\SyntaxError;
use GraphQL\Error\UserError;
use GraphQL\Executor\Promise\Adapter\SyncPromiseAdapter;
use GraphQL\Schema;
use GraphQL\Server;
use GraphQL\Type\Definition\Directive;
use GraphQL\Type\Definition\ObjectType;
use GraphQL\Type\Definition\ResolveInfo;
use GraphQL\Type\Definition\Type;
use GraphQL\Type\EagerResolution;
use GraphQL\Validator\DocumentValidator;
class ServerTest extends \PHPUnit_Framework_TestCase
{
public function testDefaults()
{
$server = @new Server();
$this->assertEquals(null, $server->getQueryType());
$this->assertEquals(null, $server->getMutationType());
$this->assertEquals(null, $server->getSubscriptionType());
$this->assertEquals(Directive::getInternalDirectives(), $server->getDirectives());
$this->assertEquals([], $server->getTypes());
$this->assertEquals(null, $server->getTypeResolutionStrategy());
$this->assertEquals(null, $server->getContext());
$this->assertEquals(null, $server->getRootValue());
$this->assertEquals(0, $server->getDebug());
$this->assertEquals(['GraphQL\Error\FormattedError', 'createFromException'], $server->getExceptionFormatter());
$this->assertEquals(['GraphQL\Error\FormattedError', 'createFromPHPError'], $server->getPhpErrorFormatter());
$this->assertEquals(null, $server->getPromiseAdapter());
$this->assertEquals('Unexpected Error', $server->getUnexpectedErrorMessage());
$this->assertEquals(500, $server->getUnexpectedErrorStatus());
$this->assertEquals(DocumentValidator::allRules(), $server->getValidationRules());
$schema = $server->getSchema();
$this->setExpectedException(InvariantViolation::class, 'Query root type must be provided.');
$schema->assertValid();
}
public function testCannotUseSetQueryTypeAndSetSchema()
{
$queryType = new ObjectType(['name' => 'A', 'fields' => ['a' => Type::string()]]);
$schema = new Schema([
'query' => $queryType,
]);
$this->setExpectedException(InvariantViolation::class,
'Cannot set Schema on Server: Query Type is already set ' .
'(GraphQL\Server::setQueryType is mutually exclusive with GraphQL\Server::setSchema)');
Server::create()
->setQueryType($queryType)
->setSchema($schema);
}
public function testCannotUseSetMutationTypeAndSetSchema()
{
$mutationType = $queryType = new ObjectType(['name' => 'A', 'fields' => ['a' => Type::string()]]);
$schema = new Schema([
'query' => $queryType,
]);
$this->setExpectedException(InvariantViolation::class,
'Cannot set Schema on Server: Mutation Type is already set ' .
'(GraphQL\Server::setMutationType is mutually exclusive with GraphQL\Server::setSchema)');
Server::create()
->setMutationType($mutationType)
->setSchema($schema);
}
public function testCannotUseSetSubscriptionTypeAndSetSchema()
{
$subscriptionType = $queryType = new ObjectType(['name' => 'A', 'fields' => ['a' => Type::string()]]);
$schema = new Schema([
'query' => $queryType,
]);
$this->setExpectedException(InvariantViolation::class,
'Cannot set Schema on Server: Subscription Type is already set ' .
'(GraphQL\Server::setSubscriptionType is mutually exclusive with GraphQL\Server::setSchema)');
Server::create()
->setSubscriptionType($subscriptionType)
->setSchema($schema);
}
public function testCannotUseSetDirectivesAndSetSchema()
{
$queryType = new ObjectType(['name' => 'A', 'fields' => ['a' => Type::string()]]);
$schema = new Schema([
'query' => $queryType,
]);
$this->setExpectedException(InvariantViolation::class,
'Cannot set Schema on Server: Directives are already set ' .
'(GraphQL\Server::setDirectives is mutually exclusive with GraphQL\Server::setSchema)');
Server::create()
->setDirectives(Directive::getInternalDirectives())
->setSchema($schema);
}
public function testCannotUseAddTypesAndSetSchema()
{
$mutationType = $queryType = new ObjectType(['name' => 'A', 'fields' => ['a' => Type::string()]]);
$schema = new Schema([
'query' => $queryType,
]);
$this->setExpectedException(InvariantViolation::class,
'Cannot set Schema on Server: Additional types are already set ' .
'(GraphQL\Server::addTypes is mutually exclusive with GraphQL\Server::setSchema)');
Server::create()
->addTypes([$queryType, $mutationType])
->setSchema($schema);
}
public function testCannotUseSetTypeResolutionStrategyAndSetSchema()
{
$mutationType = $queryType = new ObjectType(['name' => 'A', 'fields' => ['a' => Type::string()]]);
$schema = new Schema([
'query' => $queryType,
]);
$this->setExpectedException(InvariantViolation::class,
'Cannot set Schema on Server: Type Resolution Strategy is already set ' .
'(GraphQL\Server::setTypeResolutionStrategy is mutually exclusive with GraphQL\Server::setSchema)');
Server::create()
->setTypeResolutionStrategy(new EagerResolution([$queryType, $mutationType]))
->setSchema($schema);
}
public function testCannotUseSetSchemaAndSetQueryType()
{
$queryType = new ObjectType(['name' => 'A', 'fields' => ['a' => Type::string()]]);
$schema = new Schema([
'query' => $queryType,
]);
$this->setExpectedException(InvariantViolation::class,
'Cannot set Query Type on Server: Schema is already set ' .
'(GraphQL\Server::setQueryType is mutually exclusive with GraphQL\Server::setSchema)');
Server::create()
->setSchema($schema)
->setQueryType($queryType);
}
public function testCannotUseSetSchemaAndSetMutationType()
{
$mutationType = $queryType = new ObjectType(['name' => 'A', 'fields' => ['a' => Type::string()]]);
$schema = new Schema([
'query' => $queryType,
]);
$this->setExpectedException(InvariantViolation::class,
'Cannot set Mutation Type on Server: Schema is already set ' .
'(GraphQL\Server::setMutationType is mutually exclusive with GraphQL\Server::setSchema)');
Server::create()
->setSchema($schema)
->setMutationType($mutationType);
}
public function testCannotUseSetSchemaAndSetSubscriptionType()
{
$subscriptionType = $queryType = new ObjectType(['name' => 'A', 'fields' => ['a' => Type::string()]]);
$schema = new Schema([
'query' => $queryType,
]);
$this->setExpectedException(InvariantViolation::class,
'Cannot set Subscription Type on Server: Schema is already set ' .
'(GraphQL\Server::setSubscriptionType is mutually exclusive with GraphQL\Server::setSchema)');
Server::create()
->setSchema($schema)
->setSubscriptionType($subscriptionType);
}
public function testCannotUseSetSchemaAndSetDirectives()
{
$queryType = new ObjectType(['name' => 'A', 'fields' => ['a' => Type::string()]]);
$schema = new Schema([
'query' => $queryType,
]);
$this->setExpectedException(InvariantViolation::class,
'Cannot set Directives on Server: Schema is already set ' .
'(GraphQL\Server::setDirectives is mutually exclusive with GraphQL\Server::setSchema)');
Server::create()
->setSchema($schema)
->setDirectives([]);
}
public function testCannotUseSetSchemaAndAddTypes()
{
$mutationType = $queryType = new ObjectType(['name' => 'A', 'fields' => ['a' => Type::string()]]);
$schema = new Schema([
'query' => $queryType,
]);
$this->setExpectedException(InvariantViolation::class,
'Cannot set Types on Server: Schema is already set ' .
'(GraphQL\Server::addTypes is mutually exclusive with GraphQL\Server::setSchema)');
Server::create()
->setSchema($schema)
->addTypes([$queryType, $mutationType]);
}
public function testCanUseSetSchemaAndAddEmptyTypes()
{
$queryType = new ObjectType(['name' => 'A', 'fields' => ['a' => Type::string()]]);
$schema = new Schema([
'query' => $queryType,
]);
// But empty types should work (as they don't change anything):
Server::create()
->setSchema($schema)
->addTypes([]);
}
public function testCannotUseSetSchemaAndSetTypeResolutionStrategy()
{
$mutationType = $queryType = new ObjectType(['name' => 'A', 'fields' => ['a' => Type::string()]]);
$schema = new Schema([
'query' => $queryType,
]);
$this->setExpectedException(InvariantViolation::class,
'Cannot set Type Resolution Strategy on Server: Schema is already set ' .
'(GraphQL\Server::setTypeResolutionStrategy is mutually exclusive with GraphQL\Server::setSchema)');
Server::create()
->setSchema($schema)
->setTypeResolutionStrategy(new EagerResolution([$queryType, $mutationType]));
}
public function testCannotUseSetSchemaAndSetSchema()
{
$queryType = new ObjectType(['name' => 'A', 'fields' => ['a' => Type::string()]]);
$schema = new Schema([
'query' => $queryType,
]);
$this->setExpectedException(InvariantViolation::class,
'Cannot set Schema on Server: Different schema is already set');
Server::create()
->setSchema($schema)
->setSchema(new Schema(['query' => $queryType]));
$this->fail('Expected exception not thrown');
}
public function testSchemaDefinition()
{
$mutationType = $queryType = $subscriptionType = new ObjectType(['name' => 'A', 'fields' => ['a' => Type::string()]]);
$schema = new Schema([
'query' => $queryType,
]);
$server = Server::create()
->setSchema($schema);
$this->assertSame($schema, $server->getSchema());
$server = Server::create()
->setQueryType($queryType);
$this->assertSame($queryType, $server->getQueryType());
$this->assertSame($queryType, $server->getSchema()->getQueryType());
$server = Server::create()
->setQueryType($queryType)
->setMutationType($mutationType);
$this->assertSame($mutationType, $server->getMutationType());
$this->assertSame($mutationType, $server->getSchema()->getMutationType());
$server = Server::create()
->setQueryType($queryType)
->setSubscriptionType($subscriptionType);
$this->assertSame($subscriptionType, $server->getSubscriptionType());
$this->assertSame($subscriptionType, $server->getSchema()->getSubscriptionType());
$server = Server::create()
->setQueryType($queryType)
->addTypes($types = [$queryType, $subscriptionType]);
$this->assertSame($types, $server->getTypes());
$server->addTypes([$mutationType]);
$this->assertSame(array_merge($types, [$mutationType]), $server->getTypes());
$server = Server::create()
->setDirectives($directives = []);
$this->assertSame($directives, $server->getDirectives());
}
public function testParse()
{
$server = Server::create();
$ast = $server->parse('{q}');
$this->assertInstanceOf('GraphQL\Language\AST\DocumentNode', $ast);
}
public function testParseFailure()
{
$server = Server::create();
try {
$server->parse('{q');
$this->fail('Expected exception not thrown');
} catch (SyntaxError $error) {
$this->assertContains('{q', (string) $error);
$this->assertEquals('Syntax Error: Expected Name, found <EOF>', $error->getMessage());
}
}
public function testValidate()
{
$server = Server::create()
->setQueryType(new ObjectType(['name' => 'Q', 'fields' => ['a' => Type::string()]]));
$ast = $server->parse('{q}');
$errors = $server->validate($ast);
$this->assertInternalType('array', $errors);
$this->assertNotEmpty($errors);
$server = Server::create();
$this->setExpectedException(InvariantViolation::class, 'Cannot validate, schema contains errors: Query root type must be provided.');
$server->validate($ast);
}
public function testPromiseAdapter()
{
$adapter1 = new SyncPromiseAdapter();
$adapter2 = new SyncPromiseAdapter();
$server = Server::create()
->setPromiseAdapter($adapter1);
$this->assertSame($adapter1, $server->getPromiseAdapter());
$server->setPromiseAdapter($adapter1);
$this->setExpectedException(InvariantViolation::class, 'Cannot set promise adapter: Different adapter is already set');
$server->setPromiseAdapter($adapter2);
}
public function testValidationRules()
{
$rules = [];
$server = Server::create()
->setValidationRules($rules);
$this->assertSame($rules, $server->getValidationRules());
}
public function testExecuteQuery()
{
$called = false;
$queryType = new ObjectType([
'name' => 'Q',
'fields' => [
'field' => [
'type' => Type::string(),
'resolve' => function($value, $args, $context, ResolveInfo $info) use (&$called) {
$called = true;
$this->assertEquals(null, $context);
$this->assertEquals(null, $value);
$this->assertEquals(null, $info->rootValue);
return 'ok';
}
]
]
]);
$server = Server::create()
->setQueryType($queryType);
$result = $server->executeQuery('{field}');
$this->assertEquals(true, $called);
$this->assertInstanceOf('GraphQL\Executor\ExecutionResult', $result);
$this->assertEquals(['data' => ['field' => 'ok']], $result->toArray());
$called = false;
$contextValue = new \stdClass();
$rootValue = new \stdClass();
$queryType = new ObjectType([
'name' => 'QueryType',
'fields' => [
'field' => [
'type' => Type::string(),
'resolve' => function($value, $args, $context, ResolveInfo $info) use (&$called, $contextValue, $rootValue) {
$called = true;
$this->assertSame($rootValue, $value);
$this->assertSame($contextValue, $context);
$this->assertEquals($rootValue, $info->rootValue);
return 'ok';
}
]
]
]);
$server = Server::create()
->setQueryType($queryType)
->setRootValue($rootValue)
->setContext($contextValue);
$result = $server->executeQuery('{field}');
$this->assertEquals(true, $called);
$this->assertInstanceOf('GraphQL\Executor\ExecutionResult', $result);
$this->assertEquals(['data' => ['field' => 'ok']], $result->toArray());
}
public function testDebugPhpErrors()
{
$queryType = new ObjectType([
'name' => 'Query',
'fields' => [
'err' => [
'type' => Type::string(),
'resolve' => function() {
trigger_error('notice', E_USER_NOTICE);
return 'err';
}
]
]
]);
$server = Server::create()
->setDebug(0)
->setQueryType($queryType);
$prevEnabled = \PHPUnit_Framework_Error_Notice::$enabled;
\PHPUnit_Framework_Error_Notice::$enabled = false;
$result = @$server->executeQuery('{err}');
$expected = [
'data' => ['err' => 'err']
];
$this->assertEquals($expected, $result->toArray());
$server->setDebug(Server::DEBUG_PHP_ERRORS);
$result = @$server->executeQuery('{err}');
$expected = [
'data' => ['err' => 'err'],
'extensions' => [
'phpErrors' => [
[
'message' => 'notice',
'severity' => 1024,
// 'trace' => [...]
]
]
]
];
$this->assertArraySubset($expected, $result->toArray());
$server->setPhpErrorFormatter(function(\ErrorException $e) {
return ['test' => $e->getMessage()];
});
$result = $server->executeQuery('{err}');
$expected = [
'data' => ['err' => 'err'],
'extensions' => [
'phpErrors' => [
[
'test' => 'notice'
]
]
]
];
$this->assertEquals($expected, $result->toArray());
\PHPUnit_Framework_Error_Notice::$enabled = $prevEnabled;
}
public function testDebugExceptions()
{
$queryType = new ObjectType([
'name' => 'Query',
'fields' => [
'withException' => [
'type' => Type::string(),
'resolve' => function() {
throw new UserError("Error");
}
]
]
]);
$server = Server::create()
->setDebug(0)
->setQueryType($queryType);
$result = $server->executeQuery('{withException}');
$expected = [
'data' => [
'withException' => null
],
'errors' => [[
'message' => 'Error',
'path' => ['withException'],
'locations' => [[
'line' => 1,
'column' => 2
]],
]]
];
$this->assertArraySubset($expected, $result->toArray());
$server->setDebug(Server::DEBUG_EXCEPTIONS);
$server->setExceptionFormatter(function($e) {
$debug = Debug::INCLUDE_TRACE;
return FormattedError::createFromException($e, $debug);
});
$result = $server->executeQuery('{withException}');
$expected['errors'][0]['exception'] = ['message' => 'Error', 'trace' => []];
$this->assertArraySubset($expected, $result->toArray());
$server->setExceptionFormatter(function(\Exception $e) {
return ['test' => $e->getMessage()];
});
$result = $server->executeQuery('{withException}');
$expected['errors'][0]['exception'] = ['test' => 'Error'];
$this->assertArraySubset($expected, $result->toArray());
}
public function testHandleRequest()
{
$mock = $this->getMockBuilder('GraphQL\Server')
->setMethods(['readInput', 'produceOutput'])
->getMock();
$mock->method('readInput')
->will($this->returnValue(json_encode(['query' => '{err}'])));
$output = null;
$mock->method('produceOutput')
->will($this->returnCallback(function ($a1, $a2) use (&$output) {
$output = func_get_args();
}));
/** @var $mock Server */
$mock->handleRequest();
$this->assertInternalType('array', $output);
$this->assertArraySubset(['errors' => [['message' => 'Schema does not define the required query root type.']]], $output[0]);
$this->assertEquals(200, $output[1]);
$output = null;
$mock->setUnexpectedErrorMessage($newErr = 'Hey! Something went wrong!');
$mock->setUnexpectedErrorStatus(501);
$mock->method('readInput')
->will($this->throwException(new \Exception('test')));
$mock->handleRequest();
$this->assertInternalType('array', $output);
$this->assertEquals(['errors' => [['message' => $newErr]]], $output[0]);
$this->assertEquals(501, $output[1]);
}
public function testHandleRequest2()
{
$mock = $this->getMockBuilder('GraphQL\Server')
->setMethods(['readInput', 'produceOutput'])
->getMock();
$mock->method('readInput')
->will($this->returnValue(json_encode(['query' => '{err}'])));
$output = null;
$mock->method('produceOutput')
->will($this->returnCallback(function ($a1, $a2) use (&$output) {
$output = func_get_args();
}));
$mock->setQueryType(new ObjectType([
'name' => 'Query',
'fields' => [
'test' => [
'type' => Type::string(),
'resolve' => function() {
return 'ok';
}
]
]
]));
$_REQUEST = ['query' => '{err}'];
$output = null;
$mock->handleRequest();
$this->assertInternalType('array', $output);
$expectedOutput = [
['errors' => [[
'message' => 'Cannot query field "err" on type "Query".',
'locations' => [[
'line' => 1,
'column' => 2
]],
'category' => 'graphql',
]]],
200
];
$this->assertEquals($expectedOutput, $output);
$output = null;
$_SERVER['CONTENT_TYPE'] = 'application/json';
$_REQUEST = [];
$mock->handleRequest();
$this->assertInternalType('array', $output);
$this->assertEquals($expectedOutput, $output);
}
}

View File

@ -420,6 +420,6 @@ class StarWarsIntrospectionTest extends \PHPUnit_Framework_TestCase
*/
private function assertValidQuery($query, $expected)
{
$this->assertEquals(['data' => $expected], GraphQL::execute(StarWarsSchema::build(), $query));
$this->assertEquals(['data' => $expected], GraphQL::executeQuery(StarWarsSchema::build(), $query)->toArray());
}
}

View File

@ -382,7 +382,10 @@ class StarWarsQueryTest extends \PHPUnit_Framework_TestCase
*/
private function assertValidQuery($query, $expected)
{
$this->assertEquals(['data' => $expected], GraphQL::execute(StarWarsSchema::build(), $query));
$this->assertEquals(
['data' => $expected],
GraphQL::executeQuery(StarWarsSchema::build(), $query)->toArray()
);
}
/**
@ -390,6 +393,9 @@ class StarWarsQueryTest extends \PHPUnit_Framework_TestCase
*/
private function assertValidQueryWithParams($query, $params, $expected)
{
$this->assertEquals(['data' => $expected], GraphQL::execute(StarWarsSchema::build(), $query, null, null, $params));
$this->assertEquals(
['data' => $expected],
GraphQL::executeQuery(StarWarsSchema::build(), $query, null, null, $params)->toArray()
);
}
}

View File

@ -11,7 +11,7 @@ namespace GraphQL\Tests;
* NOTE: This may contain spoilers for the original Star
* Wars trilogy.
*/
use GraphQL\Schema;
use GraphQL\Type\Schema;
use GraphQL\Type\Definition\EnumType;
use GraphQL\Type\Definition\InterfaceType;
use GraphQL\Type\Definition\NonNull;

View File

@ -1,682 +0,0 @@
<?php
namespace GraphQL\Tests\Type;
use GraphQL\Error\InvariantViolation;
use GraphQL\Error\Warning;
use GraphQL\Type\Definition\Config;
use GraphQL\Type\Definition\EnumType;
use GraphQL\Type\Definition\InputObjectType;
use GraphQL\Type\Definition\InterfaceType;
use GraphQL\Type\Definition\ObjectType;
use GraphQL\Type\Definition\Type;
use GraphQL\Utils\Utils;
class ConfigTest extends \PHPUnit_Framework_TestCase
{
public function setUp()
{
Warning::suppress(Warning::WARNING_CONFIG_DEPRECATION);
}
public static function tearDownAfterClass()
{
Config::disableValidation();
Warning::enable(Warning::WARNING_CONFIG_DEPRECATION);
}
public function testToggling()
{
// Disabled by default
$this->assertEquals(false, Config::isValidationEnabled());
Config::validate(['test' => []], ['test' => Config::STRING]); // must not throw
Config::enableValidation();
$this->assertEquals(true, Config::isValidationEnabled());
try {
Config::validate(['test' => []], ['test' => Config::STRING]);
$this->fail('Expected exception not thrown');
} catch (\Exception $e) {
}
Config::disableValidation();
$this->assertEquals(false, Config::isValidationEnabled());
Config::validate(['test' => []], ['test' => Config::STRING]);
}
public function testValidateString()
{
$this->expectValidationPasses(
[
'test' => 'string',
'empty' => ''
],
[
'test' => Config::STRING,
'empty' => Config::STRING
]
);
$this->expectValidationThrows(
['test' => 1],
['test' => Config::STRING],
$this->typeError('expecting "string" at "test", but got "integer"')
);
}
public function testArray()
{
$this->expectValidationPasses(
['test' => [
[],
['nested' => 'A'],
['nested' => null]
]],
['test' => Config::arrayOf(
['nested' => Config::STRING]
)]
);
$this->expectValidationThrows(
['test' => [
null
]],
['test' => Config::arrayOf(
['nested' => Config::STRING]
)],
$this->typeError("Each entry at 'test' must be an array, but entry at '0' is 'NULL'")
);
$this->expectValidationPasses(
['test' => null],
['test' => Config::arrayOf(
['nested' => Config::STRING]
)]
);
$this->expectValidationThrows(
['test' => null],
['test' => Config::arrayOf(
['nested' => Config::STRING],
Config::REQUIRED
)],
$this->typeError('expecting "array" at "test", but got "NULL"')
);
// Check validation nesting:
$this->expectValidationPasses(
['nest' => [
['nest' => [
['test' => 'value']
]]
]],
['nest' => Config::arrayOf([
'nest' => Config::arrayOf([
'test' => Config::STRING
])
])]
);
$this->expectValidationThrows(
['nest' => [
['nest' => [
['test' => 'notInt']
]]
]],
['nest' => Config::arrayOf([
'nest' => Config::arrayOf([
'test' => Config::INT
])
])],
$this->typeError('expecting "int" at "nest:0:nest:0:test", but got "string"')
);
// Check arrays of types:
$this->expectValidationPasses(
['nest' => [
Type::string(),
Type::int()
]],
['nest' => Config::arrayOf(
Config::OUTPUT_TYPE, Config::REQUIRED
)]
);
// Check arrays of types:
$this->expectValidationThrows(
['nest' => [
Type::string(),
new InputObjectType(['name' => 'test', 'fields' => []])
]],
['nest' => Config::arrayOf(
Config::OUTPUT_TYPE, Config::REQUIRED
)],
$this->typeError('expecting "OutputType definition" at "nest:1", but got "test"')
);
}
public function testRequired()
{
$this->expectValidationPasses(
['required' => ''],
['required' => Config::STRING | Config::REQUIRED]
);
$this->expectValidationThrows(
[],
['required' => Config::STRING | Config::REQUIRED],
$this->typeError('Required keys missing: "required" ')
);
$this->expectValidationThrows(
['required' => null],
['required' => Config::STRING | Config::REQUIRED],
$this->typeError('Value at "required" can not be null')
);
$this->expectValidationPasses(
['test' => [
['nested' => '']
]],
['test' => Config::arrayOf([
'nested' => Config::STRING | Config::REQUIRED
])]
);
$this->expectValidationThrows(
['test' => [
[]
]],
['test' => Config::arrayOf([
'nested' => Config::STRING | Config::REQUIRED
])],
$this->typeError('Required keys missing: "nested" at test:0')
);
$this->expectValidationThrows(
['test' => [
['nested' => null]
]],
['test' => Config::arrayOf([
'nested' => Config::STRING | Config::REQUIRED
])],
$this->typeError('Value at "test:0:nested" can not be null')
);
$this->expectValidationPasses(
['test' => [
['nested' => null]
]],
['test' => Config::arrayOf(
['nested' => Config::STRING],
Config::REQUIRED
)]
);
$this->expectValidationThrows(
['test' => [
]],
['test' => Config::arrayOf(
['nested' => Config::STRING],
Config::REQUIRED
)],
$this->typeError("Value at 'test' cannot be empty array")
);
}
public function testKeyAsName()
{
$this->expectValidationPasses(
['test' => [
'name1' => ['key1' => null],
['name' => 'name1'],
]],
['test' => Config::arrayOf(
['name' => Config::STRING | Config::REQUIRED, 'key1' => Config::STRING],
Config::KEY_AS_NAME
)]
);
$this->expectValidationThrows(
['test' => [
'name1' => ['key1' => null]
]],
['test' => Config::arrayOf(
['name' => Config::STRING | Config::REQUIRED, 'key1' => Config::STRING]
)],
$this->typeError('Required keys missing: "name" at test:name1')
);
$this->expectValidationThrows(
['test' => [
['key1' => null]
]],
['test' => Config::arrayOf(
['name' => Config::STRING | Config::REQUIRED, 'key1' => Config::STRING],
Config::KEY_AS_NAME
)],
$this->typeError('Required keys missing: "name" at test:0')
);
}
public function testMaybeThunk()
{
$this->expectValidationPasses(
[
'test' => [
['nested' => ''],
['nested' => '1'],
],
'testThunk' => function() {
// Currently config won't validate thunk return value
}
],
[
'test' => Config::arrayOf(
['nested' => Config::STRING | Config::REQUIRED],
Config::MAYBE_THUNK
),
'testThunk' => Config::arrayOf(
['nested' => Config::STRING | Config::REQUIRED],
Config::MAYBE_THUNK
)
]
);
$this->expectValidationThrows(
[
'testThunk' => $closure = function() {}
],
[
'testThunk' => Config::arrayOf(
['nested' => Config::STRING | Config::REQUIRED]
)
],
$this->typeError('expecting "array" at "testThunk", but got "' . Utils::getVariableType($closure) . '"')
);
$this->expectValidationThrows(
[
'testThunk' => 1
],
[
'testThunk' => Config::arrayOf(
['nested' => Config::STRING | Config::REQUIRED],
Config::MAYBE_THUNK
)
],
$this->typeError('expecting "array or callable" at "testThunk", but got "integer"')
);
}
public function testMaybeType()
{
$type = new ObjectType([
'name' => 'Test',
'fields' => []
]);
$this->expectValidationPasses(
['test' => [
$type,
['type' => $type],
]],
['test' => Config::arrayOf(
['type' => Config::OBJECT_TYPE | Config::REQUIRED],
Config::MAYBE_TYPE
)]
);
$this->expectValidationThrows(
['test' => [
['type' => 'str']
]],
['test' => Config::arrayOf(
['type' => Config::OBJECT_TYPE | Config::REQUIRED],
Config::MAYBE_TYPE
)],
$this->typeError('expecting "ObjectType definition" at "test:0:type", but got "string"')
);
$this->expectValidationThrows(
['test' => [
$type
]],
['test' => Config::arrayOf(
['name' => Config::OBJECT_TYPE | Config::REQUIRED]
)],
$this->typeError("Each entry at 'test' must be an array, but entry at '0' is 'Test'")
);
}
public function testMaybeName()
{
$this->expectValidationPasses(
['test' => [
'some-name',
['name' => 'other-name'],
]],
['test' => Config::arrayOf(
['name' => Config::STRING | Config::REQUIRED],
Config::MAYBE_NAME
)]
);
$this->expectValidationThrows(
['test' => [
'some-name'
]],
['test' => Config::arrayOf(
['name' => Config::OBJECT_TYPE | Config::REQUIRED]
)],
$this->typeError("Each entry at 'test' must be an array, but entry at '0' is 'string'")
);
$this->expectValidationPasses(
['test' => [
'some-key' => 'some-name',
'some-name'
]],
['test' => Config::arrayOf(
['name' => Config::STRING | Config::REQUIRED],
Config::MAYBE_NAME | Config::KEY_AS_NAME
)]
);
}
public function getValidValues()
{
return [
// $type, $validValue
[Config::ANY, null],
[Config::ANY, 0],
[Config::ANY, ''],
[Config::ANY, '0'],
[Config::ANY, 1],
[Config::ANY, function() {}],
[Config::ANY, []],
[Config::ANY, new \stdClass()],
[Config::STRING, null],
[Config::STRING, ''],
[Config::STRING, '0'],
[Config::STRING, 'anything'],
[Config::BOOLEAN, null],
[Config::BOOLEAN, false],
[Config::BOOLEAN, true],
[Config::INT, null],
[Config::INT, 0],
[Config::INT, 1],
[Config::INT, -1],
[Config::INT, 5000000],
[Config::INT, -5000000],
[Config::FLOAT, null],
[Config::FLOAT, 0],
[Config::FLOAT, 0.0],
[Config::FLOAT, 0.1],
[Config::FLOAT, -12.5],
[Config::NUMERIC, null],
[Config::NUMERIC, '0'],
[Config::NUMERIC, 0],
[Config::NUMERIC, 1],
[Config::NUMERIC, 0.0],
[Config::NUMERIC, 1.0],
[Config::NUMERIC, -1.0],
[Config::NUMERIC, -1],
[Config::NUMERIC, 1],
[Config::CALLBACK, null],
[Config::CALLBACK, function() {}],
[Config::CALLBACK, [$this, 'getValidValues']],
[Config::SCALAR, null],
[Config::SCALAR, 0],
[Config::SCALAR, 1],
[Config::SCALAR, 0.0],
[Config::SCALAR, 1.0],
[Config::SCALAR, true],
[Config::SCALAR, false],
[Config::SCALAR, ''],
[Config::SCALAR, '0'],
[Config::SCALAR, 'anything'],
[Config::NAME, null],
[Config::NAME, 'CamelCaseIsOk'],
[Config::NAME, 'underscore_is_ok'],
[Config::NAME, 'numbersAreOk0123456789'],
[Config::INPUT_TYPE, null],
[Config::INPUT_TYPE, new InputObjectType(['name' => 'test', 'fields' => []])],
[Config::INPUT_TYPE, new EnumType(['name' => 'test2', 'values' => ['A', 'B', 'C']])],
[Config::INPUT_TYPE, Type::string()],
[Config::INPUT_TYPE, Type::int()],
[Config::INPUT_TYPE, Type::float()],
[Config::INPUT_TYPE, Type::boolean()],
[Config::INPUT_TYPE, Type::id()],
[Config::INPUT_TYPE, Type::listOf(Type::string())],
[Config::INPUT_TYPE, Type::nonNull(Type::string())],
[Config::OUTPUT_TYPE, null],
[Config::OUTPUT_TYPE, new ObjectType(['name' => 'test3', 'fields' => []])],
[Config::OUTPUT_TYPE, new EnumType(['name' => 'test4', 'values' => ['A', 'B', 'C']])],
[Config::OUTPUT_TYPE, Type::string()],
[Config::OUTPUT_TYPE, Type::int()],
[Config::OUTPUT_TYPE, Type::float()],
[Config::OUTPUT_TYPE, Type::boolean()],
[Config::OUTPUT_TYPE, Type::id()],
[Config::OBJECT_TYPE, null],
[Config::OBJECT_TYPE, new ObjectType(['name' => 'test6', 'fields' => []])],
[Config::INTERFACE_TYPE, null],
[Config::INTERFACE_TYPE, new InterfaceType(['name' => 'test7', 'fields' => []])],
];
}
/**
* @dataProvider getValidValues
*/
public function testValidValues($type, $validValue)
{
$this->expectValidationPasses(
['test' => $validValue],
['test' => $type]
);
}
public function getInvalidValues()
{
return [
// $type, $typeLabel, $invalidValue, $actualTypeLabel
[Config::STRING, 'string', 1, 'integer'],
[Config::STRING, 'string', 0, 'integer'],
[Config::STRING, 'string', false, 'boolean'],
[Config::STRING, 'string', $tmp = function() {}, Utils::getVariableType($tmp)], // Note: can't use "Closure" as HHVM returns different string
[Config::STRING, 'string', [], 'array'],
[Config::STRING, 'string', new \stdClass(), 'stdClass'],
[Config::BOOLEAN, 'boolean', '', 'string'],
[Config::BOOLEAN, 'boolean', 1, 'integer'],
[Config::BOOLEAN, 'boolean', $tmp = function() {}, Utils::getVariableType($tmp)],
[Config::BOOLEAN, 'boolean', [], 'array'],
[Config::BOOLEAN, 'boolean', new \stdClass(), 'stdClass'],
[Config::INT, 'int', false, 'boolean'],
[Config::INT, 'int', '', 'string'],
[Config::INT, 'int', '0', 'string'],
[Config::INT, 'int', '1', 'string'],
[Config::INT, 'int', $tmp = function() {}, Utils::getVariableType($tmp)],
[Config::INT, 'int', [], 'array'],
[Config::INT, 'int', new \stdClass(), 'stdClass'],
[Config::FLOAT, 'float', '', 'string'],
[Config::FLOAT, 'float', '0', 'string'],
[Config::FLOAT, 'float', $tmp = function() {}, Utils::getVariableType($tmp)],
[Config::FLOAT, 'float', [], 'array'],
[Config::FLOAT, 'float', new \stdClass(), 'stdClass'],
[Config::NUMERIC, 'numeric', '', 'string'],
[Config::NUMERIC, 'numeric', 'tmp', 'string'],
[Config::NUMERIC, 'numeric', [], 'array'],
[Config::NUMERIC, 'numeric', new \stdClass(), 'stdClass'],
[Config::NUMERIC, 'numeric', $tmp = function() {}, Utils::getVariableType($tmp)],
[Config::CALLBACK, 'callable', 1, 'integer'],
[Config::CALLBACK, 'callable', '', 'string'],
[Config::CALLBACK, 'callable', [], 'array'],
[Config::CALLBACK, 'callable', new \stdClass(), 'stdClass'],
[Config::SCALAR, 'scalar', [], 'array'],
[Config::SCALAR, 'scalar', new \stdClass(), 'stdClass'],
[Config::SCALAR, 'scalar', $tmp = function() {}, Utils::getVariableType($tmp)],
[Config::NAME, 'name', 5, 'integer'],
[Config::NAME, 'name', $tmp = function() {}, Utils::getVariableType($tmp)],
[Config::NAME, 'name', [], 'array'],
[Config::NAME, 'name', new \stdClass(), 'stdClass'],
[Config::NAME, 'name', '', null, 'Names must match /^[_a-zA-Z][_a-zA-Z0-9]*$/ but "" does not.'],
[Config::NAME, 'name', '0', null, 'Names must match /^[_a-zA-Z][_a-zA-Z0-9]*$/ but "0" does not.'],
[Config::NAME, 'name', '4abc', null, 'Names must match /^[_a-zA-Z][_a-zA-Z0-9]*$/ but "4abc" does not.'],
[Config::NAME, 'name', 'specialCharsAreBad!', null, 'Names must match /^[_a-zA-Z][_a-zA-Z0-9]*$/ but "specialCharsAreBad!" does not.'],
[Config::INPUT_TYPE, 'InputType definition', new ObjectType(['name' => 'test3', 'fields' => []]), 'test3'],
[Config::INPUT_TYPE, 'InputType definition', '', 'string'],
[Config::INPUT_TYPE, 'InputType definition', 'test', 'string'],
[Config::INPUT_TYPE, 'InputType definition', 1, 'integer'],
[Config::INPUT_TYPE, 'InputType definition', 0.5, 'double'],
[Config::INPUT_TYPE, 'InputType definition', false, 'boolean'],
[Config::INPUT_TYPE, 'InputType definition', [], 'array'],
[Config::INPUT_TYPE, 'InputType definition', new \stdClass(), 'stdClass'],
[Config::OUTPUT_TYPE, 'OutputType definition', new InputObjectType(['name' => 'InputTypeTest']), 'InputTypeTest'],
[Config::OBJECT_TYPE, 'ObjectType definition', '', 'string'],
[Config::OBJECT_TYPE, 'ObjectType definition', new InputObjectType(['name' => 'InputTypeTest2']), 'InputTypeTest2'],
[Config::INTERFACE_TYPE, 'InterfaceType definition', new ObjectType(['name' => 'ObjectTypeTest']), 'ObjectTypeTest'],
[Config::INTERFACE_TYPE, 'InterfaceType definition', 'InputTypeTest2', 'string'],
];
}
/**
* @dataProvider getInvalidValues
*/
public function testInvalidValues($type, $typeLabel, $invalidValue, $actualTypeLabel = null, $expectedFullError = null)
{
if (!$expectedFullError) {
$expectedFullError = $this->typeError(
$invalidValue === null ?
'Value at "test" can not be null' :
'expecting "' . $typeLabel . '" at "test", but got "' . $actualTypeLabel . '"'
);
}
$this->expectValidationThrows(
['test' => $invalidValue],
['test' => $type],
$expectedFullError
);
}
public function testErrorMessageContainsTypeName()
{
$this->expectValidationThrows(
[
'name' => 'TypeName',
'test' => 'notInt'
],
[
'name' => Config::STRING | Config::REQUIRED,
'test' => Config::INT
],
$this->typeError('expecting "int" at "test", but got "string"', 'TypeName')
);
}
public function testValidateField()
{
Config::enableValidation();
// Should just validate:
Config::validateField(
'TypeName',
['test' => 'value'],
['test' => Config::STRING]
);
// Should include type name in error
try {
Config::validateField(
'TypeName',
['test' => 'notInt'],
['test' => Config::INT]
);
$this->fail('Expected exception not thrown');
} catch (InvariantViolation $e) {
$this->assertEquals(
$this->typeError('expecting "int" at "(Unknown Field):test", but got "string"', 'TypeName'),
$e->getMessage()
);
}
// Should include field type in error when field name is unknown:
try {
Config::validateField(
'TypeName',
['type' => Type::string()],
['name' => Config::STRING | Config::REQUIRED, 'type' => Config::OUTPUT_TYPE]
);
$this->fail('Expected exception not thrown');
} catch (InvariantViolation $e) {
$this->assertEquals(
$this->typeError('Required keys missing: "name" at (Unknown Field of type: String)', 'TypeName'),
$e->getMessage()
);
}
// Should include field name in error when field name is set:
try {
Config::validateField(
'TypeName',
['name' => 'fieldName', 'test' => 'notInt'],
['name' => Config::STRING, 'test' => Config::INT]
);
$this->fail('Expected exception not thrown');
} catch (InvariantViolation $e) {
$this->assertEquals(
$this->typeError('expecting "int" at "test", but got "string"', 'TypeName'),
$e->getMessage()
);
}
}
public function testAllowCustomOptions()
{
// Disabled by default when validation is enabled
Config::enableValidation(true);
Config::validate(
['test' => 'value', 'test2' => 'value'],
['test' => Config::STRING]
);
Config::enableValidation(false);
try {
Config::validate(
['test' => 'value', 'test2' => 'value'],
['test' => Config::STRING]
);
$this->fail('Expected exception not thrown');
} catch (\PHPUnit_Framework_Error_Warning $e) {
$this->assertEquals(
$this->typeError('Non-standard keys "test2" '),
$e->getMessage()
);
}
}
private function expectValidationPasses($config, $definition)
{
Config::enableValidation(false);
Config::validate($config, $definition);
}
private function expectValidationThrows($config, $definition, $expectedError)
{
Config::enableValidation(false);
try {
Config::validate($config, $definition);
$this->fail('Expected exception not thrown: ' . $expectedError);
} catch (InvariantViolation $e) {
$this->assertEquals($expectedError, $e->getMessage());
}
}
private function typeError($err, $typeName = null)
{
return 'Error in "'. ($typeName ?: '(Unnamed Type)') . '" type definition: ' . $err;
}
}

View File

@ -3,7 +3,7 @@ namespace GraphQL\Tests\Type;
use GraphQL\GraphQL;
use GraphQL\Language\SourceLocation;
use GraphQL\Schema;
use GraphQL\Type\Schema;
use GraphQL\Type\Definition\EnumType;
use GraphQL\Type\Definition\ObjectType;
use GraphQL\Type\Definition\Type;
@ -185,7 +185,7 @@ class EnumTypeTest extends \PHPUnit_Framework_TestCase
{
$this->assertEquals(
['data' => ['colorInt' => 1]],
GraphQL::execute($this->schema, '{ colorInt(fromEnum: GREEN) }')
GraphQL::executeQuery($this->schema, '{ colorInt(fromEnum: GREEN) }')->toArray()
);
}
@ -196,7 +196,7 @@ class EnumTypeTest extends \PHPUnit_Framework_TestCase
{
$this->assertEquals(
['data' => ['colorEnum' => 'GREEN']],
GraphQL::execute($this->schema, '{ colorEnum(fromInt: 1) }')
GraphQL::executeQuery($this->schema, '{ colorEnum(fromInt: 1) }')->toArray()
);
}
@ -207,7 +207,7 @@ class EnumTypeTest extends \PHPUnit_Framework_TestCase
{
$this->assertEquals(
['data' => ['colorEnum' => 'GREEN']],
GraphQL::execute($this->schema, '{ colorEnum(fromEnum: GREEN) }')
GraphQL::executeQuery($this->schema, '{ colorEnum(fromEnum: GREEN) }')->toArray()
);
}
@ -303,13 +303,13 @@ class EnumTypeTest extends \PHPUnit_Framework_TestCase
{
$this->assertEquals(
['data' => ['colorEnum' => 'BLUE']],
GraphQL::execute(
GraphQL::executeQuery(
$this->schema,
'query test($color: Color!) { colorEnum(fromEnum: $color) }',
null,
null,
['color' => 'BLUE']
)
)->toArray()
);
}
@ -320,13 +320,13 @@ class EnumTypeTest extends \PHPUnit_Framework_TestCase
{
$this->assertEquals(
['data' => ['favoriteEnum' => 'GREEN']],
GraphQL::execute(
GraphQL::executeQuery(
$this->schema,
'mutation x($color: Color!) { favoriteEnum(color: $color) }',
null,
null,
['color' => 'GREEN']
)
)->toArray()
);
}
@ -338,13 +338,13 @@ class EnumTypeTest extends \PHPUnit_Framework_TestCase
{
$this->assertEquals(
['data' => ['subscribeToEnum' => 'GREEN']],
GraphQL::execute(
GraphQL::executeQuery(
$this->schema,
'subscription x($color: Color!) { subscribeToEnum(color: $color) }',
null,
null,
['color' => 'GREEN']
)
)->toArray()
);
}
@ -391,10 +391,10 @@ class EnumTypeTest extends \PHPUnit_Framework_TestCase
{
$this->assertEquals(
['data' => ['colorEnum' => 'RED', 'colorInt' => 0]],
GraphQL::execute($this->schema, "{
GraphQL::executeQuery($this->schema, "{
colorEnum(fromEnum: RED)
colorInt(fromEnum: RED)
}")
}")->toArray()
);
}
@ -405,10 +405,10 @@ class EnumTypeTest extends \PHPUnit_Framework_TestCase
{
$this->assertEquals(
['data' => ['colorEnum' => null, 'colorInt' => null]],
GraphQL::execute($this->schema, "{
GraphQL::executeQuery($this->schema, "{
colorEnum
colorInt
}")
}")->toArray()
);
}
@ -445,7 +445,7 @@ class EnumTypeTest extends \PHPUnit_Framework_TestCase
*/
public function testMayBeInternallyRepresentedWithComplexValues()
{
$result = GraphQL::executeAndReturnResult($this->schema, '{
$result = GraphQL::executeQuery($this->schema, '{
first: complexEnum
second: complexEnum(fromEnum: TWO)
good: complexEnum(provideGoodValue: true)
@ -474,7 +474,7 @@ class EnumTypeTest extends \PHPUnit_Framework_TestCase
*/
public function testCanBeIntrospectedWithoutError()
{
$result = GraphQL::execute($this->schema, Introspection::getIntrospectionQuery());
$result = GraphQL::executeQuery($this->schema, Introspection::getIntrospectionQuery())->toArray();
$this->assertArrayNotHasKey('errors', $result);
}
@ -494,13 +494,13 @@ class EnumTypeTest extends \PHPUnit_Framework_TestCase
'locations' => [['line' => 4, 'column' => 13]]
]]
],
GraphQL::executeAndReturnResult($this->schema, $q)->toArray(true)
GraphQL::executeQuery($this->schema, $q)->toArray(true)
);
}
private function expectFailure($query, $vars, $err)
{
$result = GraphQL::executeAndReturnResult($this->schema, $query, null, null, $vars);
$result = GraphQL::executeQuery($this->schema, $query, null, null, $vars);
$this->assertEquals(1, count($result->errors));
if (is_array($err)) {

View File

@ -3,7 +3,7 @@ namespace GraphQL\Tests\Type;
use GraphQL\Error\FormattedError;
use GraphQL\Language\SourceLocation;
use GraphQL\Schema;
use GraphQL\Type\Schema;
use GraphQL\GraphQL;
use GraphQL\Type\Definition\EnumType;
use GraphQL\Type\Definition\InputObjectType;
@ -1062,7 +1062,7 @@ class IntrospectionTest extends \PHPUnit_Framework_TestCase
)
);
$actual = GraphQL::execute($emptySchema, $request);
$actual = GraphQL::executeQuery($emptySchema, $request)->toArray();
// $this->assertEquals($expected, $actual);
$this->assertArraySubset($expected, $actual);
@ -1164,7 +1164,7 @@ class IntrospectionTest extends \PHPUnit_Framework_TestCase
]
];
$result = GraphQL::execute($schema, $request);
$result = GraphQL::executeQuery($schema, $request)->toArray();
$result = $result['data']['__schema']['types'];
// $this->assertEquals($expectedFragment, $result[1]);
$this->assertContains($expectedFragment, $result);
@ -1200,7 +1200,7 @@ class IntrospectionTest extends \PHPUnit_Framework_TestCase
]
]];
$this->assertEquals($expected, GraphQL::execute($schema, $request));
$this->assertEquals($expected, GraphQL::executeQuery($schema, $request)->toArray());
}
/**
@ -1255,7 +1255,7 @@ class IntrospectionTest extends \PHPUnit_Framework_TestCase
]
]
];
$this->assertEquals($expected, GraphQL::execute($schema, $request));
$this->assertEquals($expected, GraphQL::executeQuery($schema, $request)->toArray());
}
/**
@ -1320,7 +1320,7 @@ class IntrospectionTest extends \PHPUnit_Framework_TestCase
]
];
$this->assertEquals($expected, GraphQL::execute($schema, $request));
$this->assertEquals($expected, GraphQL::executeQuery($schema, $request)->toArray());
}
/**
@ -1384,7 +1384,7 @@ class IntrospectionTest extends \PHPUnit_Framework_TestCase
]
]
];
$this->assertEquals($expected, GraphQL::execute($schema, $request));
$this->assertEquals($expected, GraphQL::executeQuery($schema, $request)->toArray());
}
/**
@ -1447,7 +1447,7 @@ class IntrospectionTest extends \PHPUnit_Framework_TestCase
]
]
];
$this->assertEquals($expected, GraphQL::execute($schema, $request));
$this->assertEquals($expected, GraphQL::executeQuery($schema, $request)->toArray());
}
/**
@ -1479,7 +1479,7 @@ class IntrospectionTest extends \PHPUnit_Framework_TestCase
)
]
];
$this->assertArraySubset($expected, GraphQL::execute($schema, $request));
$this->assertArraySubset($expected, GraphQL::executeQuery($schema, $request)->toArray());
}
/**
@ -1539,7 +1539,7 @@ class IntrospectionTest extends \PHPUnit_Framework_TestCase
]
]
];
$this->assertEquals($expected, GraphQL::execute($schema, $request));
$this->assertEquals($expected, GraphQL::executeQuery($schema, $request)->toArray());
}
/**
@ -1615,6 +1615,6 @@ class IntrospectionTest extends \PHPUnit_Framework_TestCase
]
];
$this->assertEquals($expected, GraphQL::execute($schema, $request));
$this->assertEquals($expected, GraphQL::executeQuery($schema, $request)->toArray());
}
}

View File

@ -2,7 +2,7 @@
namespace GraphQL\Tests\Type;
use GraphQL\GraphQL;
use GraphQL\Schema;
use GraphQL\Type\Schema;
use GraphQL\Type\Definition\ObjectType;
use GraphQL\Type\Definition\ResolveInfo;
use GraphQL\Type\Definition\Type;
@ -155,7 +155,7 @@ class ResolveInfoTest extends \PHPUnit_Framework_TestCase
]);
$schema = new Schema(['query' => $blogQuery]);
$result = GraphQL::execute($schema, $doc);
$result = GraphQL::executeQuery($schema, $doc)->toArray();
$this->assertTrue($hasCalled);
$this->assertEquals(['data' => ['article' => null]], $result);
@ -313,7 +313,7 @@ class ResolveInfoTest extends \PHPUnit_Framework_TestCase
]);
$schema = new Schema(['query' => $blogQuery]);
$result = GraphQL::execute($schema, $doc);
$result = GraphQL::executeQuery($schema, $doc)->toArray();
$this->assertTrue($hasCalled);
$this->assertEquals(['data' => ['article' => null]], $result);