Support for NullValue

This commit is contained in:
vladar 2016-11-18 23:59:28 +07:00
parent 9bf8e82d7c
commit 8a676cde99
21 changed files with 408 additions and 75 deletions

View File

@ -5,17 +5,16 @@ namespace GraphQL\Executor;
use GraphQL\Error\Error;
use GraphQL\Error\InvariantViolation;
use GraphQL\Language\AST\Argument;
use GraphQL\Language\AST\NullValue;
use GraphQL\Language\AST\VariableDefinition;
use GraphQL\Language\Printer;
use GraphQL\Schema;
use GraphQL\Type\Definition\EnumType;
use GraphQL\Type\Definition\FieldArgument;
use GraphQL\Type\Definition\InputObjectType;
use GraphQL\Type\Definition\InputType;
use GraphQL\Type\Definition\LeafType;
use GraphQL\Type\Definition\ListOfType;
use GraphQL\Type\Definition\NonNull;
use GraphQL\Type\Definition\ScalarType;
use GraphQL\Type\Definition\Type;
use GraphQL\Utils;
@ -65,16 +64,29 @@ class Values
$valueAST = isset($argASTMap[$name]) ? $argASTMap[$name]->value : null;
$value = Utils\AST::valueFromAST($valueAST, $argDef->getType(), $variableValues);
if (null === $value && null === $argDef->defaultValue && !$argDef->defaultValueExists()) {
continue;
}
if (null === $value) {
$value = $argDef->defaultValue;
}
if (null !== $value) {
$result[$name] = $value;
if (NullValue::getNullValue() === $value) {
$value = null;
}
$result[$name] = $value;
}
return $result;
}
/**
* @deprecated Moved to Utils\AST::valueFromAST
*
* @param $valueAST
* @param InputType $type
* @param null $variables
* @return array|null|\stdClass
*/
public static function valueFromAST($valueAST, InputType $type, $variables = null)
{
return Utils\AST::valueFromAST($valueAST, $type, $variables);
@ -105,7 +117,8 @@ class Values
if (null === $input) {
$defaultValue = $definitionAST->defaultValue;
if ($defaultValue) {
return Utils\AST::valueFromAST($defaultValue, $inputType);
$value = Utils\AST::valueFromAST($defaultValue, $inputType);
return $value === NullValue::getNullValue() ? null : $value;
}
}
return self::coerceValue($inputType, $input);

View File

@ -31,6 +31,7 @@ class NodeType
const STRING = 'StringValue';
const BOOLEAN = 'BooleanValue';
const ENUM = 'EnumValue';
const NULL = 'NullValue';
const LST = 'ListValue';
const OBJECT = 'ObjectValue';
const OBJECT_FIELD = 'ObjectField';

View File

@ -0,0 +1,13 @@
<?php
namespace GraphQL\Language\AST;
class NullValue extends Node implements Value
{
public $kind = NodeType::NULL;
public static function getNullValue()
{
static $nullValue;
return $nullValue ?: $nullValue = new \stdClass();
}
}

View File

@ -27,6 +27,7 @@ use GraphQL\Language\AST\Location;
use GraphQL\Language\AST\Name;
use GraphQL\Language\AST\NamedType;
use GraphQL\Language\AST\NonNullType;
use GraphQL\Language\AST\NullValue;
use GraphQL\Language\AST\ObjectField;
use GraphQL\Language\AST\ObjectTypeDefinition;
use GraphQL\Language\AST\ObjectValue;
@ -610,16 +611,19 @@ class Parser
* - FloatValue
* - StringValue
* - BooleanValue
* - NullValue
* - EnumValue
* - ListValue[?Const]
* - ObjectValue[?Const]
*
* BooleanValue : one of `true` `false`
*
* NullValue : `null`
*
* EnumValue : Name but not `true`, `false` or `null`
*
* @param $isConst
* @return BooleanValue|EnumValue|FloatValue|IntValue|StringValue|Variable|ListValue|ObjectValue
* @return BooleanValue|EnumValue|FloatValue|IntValue|StringValue|Variable|ListValue|ObjectValue|NullValue
* @throws SyntaxError
*/
function parseValueLiteral($isConst)
@ -655,7 +659,12 @@ class Parser
'value' => $token->value === 'true',
'loc' => $this->loc($token)
]);
} else if ($token->value !== 'null') {
} else if ($token->value === 'null') {
$this->lexer->advance();
return new NullValue([
'loc' => $this->loc($token)
]);
} else {
$this->lexer->advance();
return new EnumValue([
'value' => $token->value,

View File

@ -21,11 +21,11 @@ use GraphQL\Language\AST\FragmentSpread;
use GraphQL\Language\AST\InlineFragment;
use GraphQL\Language\AST\IntValue;
use GraphQL\Language\AST\ListType;
use GraphQL\Language\AST\Name;
use GraphQL\Language\AST\NamedType;
use GraphQL\Language\AST\Node;
use GraphQL\Language\AST\NodeType;
use GraphQL\Language\AST\NonNullType;
use GraphQL\Language\AST\NullValue;
use GraphQL\Language\AST\ObjectField;
use GraphQL\Language\AST\ObjectTypeDefinition;
use GraphQL\Language\AST\ObjectValue;
@ -112,14 +112,33 @@ class Printer
},
// Value
NodeType::INT => function(IntValue $node) {return $node->value;},
NodeType::FLOAT => function(FloatValue $node) {return $node->value;},
NodeType::STRING => function(StringValue $node) {return json_encode($node->value);},
NodeType::BOOLEAN => function(BooleanValue $node) {return $node->value ? 'true' : 'false';},
NodeType::ENUM => function(EnumValue $node) {return $node->value;},
NodeType::LST => function(ListValue $node) {return '[' . $this->join($node->values, ', ') . ']';},
NodeType::OBJECT => function(ObjectValue $node) {return '{' . $this->join($node->fields, ', ') . '}';},
NodeType::OBJECT_FIELD => function(ObjectField $node) {return $node->name . ': ' . $node->value;},
NodeType::INT => function(IntValue $node) {
return $node->value;
},
NodeType::FLOAT => function(FloatValue $node) {
return $node->value;
},
NodeType::STRING => function(StringValue $node) {
return json_encode($node->value);
},
NodeType::BOOLEAN => function(BooleanValue $node) {
return $node->value ? 'true' : 'false';
},
NodeType::NULL => function(NullValue $node) {
return 'null';
},
NodeType::ENUM => function(EnumValue $node) {
return $node->value;
},
NodeType::LST => function(ListValue $node) {
return '[' . $this->join($node->values, ', ') . ']';
},
NodeType::OBJECT => function(ObjectValue $node) {
return '{' . $this->join($node->fields, ', ') . '}';
},
NodeType::OBJECT_FIELD => function(ObjectField $node) {
return $node->name . ': ' . $node->value;
},
// Directive
NodeType::DIRECTIVE => function(Directive $node) {
@ -127,9 +146,15 @@ class Printer
},
// Type
NodeType::NAMED_TYPE => function(NamedType $node) {return $node->name;},
NodeType::LIST_TYPE => function(ListType $node) {return '[' . $node->type . ']';},
NodeType::NON_NULL_TYPE => function(NonNullType $node) {return $node->type . '!';},
NodeType::NAMED_TYPE => function(NamedType $node) {
return $node->name;
},
NodeType::LIST_TYPE => function(ListType $node) {
return '[' . $node->type . ']';
},
NodeType::NON_NULL_TYPE => function(NonNullType $node) {
return $node->type . '!';
},
// Type System Definitions
NodeType::SCHEMA_DEFINITION => function(SchemaDefinition $def) {
@ -139,7 +164,9 @@ class Printer
$this->block($def->operationTypes)
], ' ');
},
NodeType::OPERATION_TYPE_DEFINITION => function(OperationTypeDefinition $def) {return $def->operation . ': ' . $def->type;},
NodeType::OPERATION_TYPE_DEFINITION => function(OperationTypeDefinition $def) {
return $def->operation . ': ' . $def->type;
},
NodeType::SCALAR_TYPE_DEFINITION => function(ScalarTypeDefinition $def) {
return $this->join(['scalar', $def->name, $this->join($def->directives, ' ')], ' ');

View File

@ -65,6 +65,7 @@ class Visitor
NodeType::FLOAT => [],
NodeType::STRING => [],
NodeType::BOOLEAN => [],
NodeType::NULL => [],
NodeType::ENUM => [],
NodeType::LST => ['values'],
NodeType::OBJECT => ['fields'],

View File

@ -40,6 +40,11 @@ class FieldArgument
*/
private $resolvedType;
/**
* @var bool
*/
private $defaultValueExists = false;
/**
* @param array $config
* @return array
@ -62,17 +67,23 @@ class FieldArgument
*/
public function __construct(array $def)
{
$def += [
'type' => null,
'name' => null,
'defaultValue' => null,
'description' => null
];
$this->type = $def['type'];
$this->name = $def['name'];
$this->description = $def['description'];
$this->defaultValue = $def['defaultValue'];
foreach ($def as $key => $value) {
switch ($key) {
case 'type':
$this->type = $value;
break;
case 'name':
$this->name = $value;
break;
case 'defaultValue':
$this->defaultValue = $value;
$this->defaultValueExists = true;
break;
case 'description':
$this->description = $value;
break;
}
}
$this->config = $def;
}
@ -87,4 +98,12 @@ class FieldArgument
}
return $this->resolvedType;
}
/**
* @return bool
*/
public function defaultValueExists()
{
return $this->defaultValueExists;
}
}

View File

@ -27,6 +27,13 @@ class InputObjectField
*/
public $type;
/**
* Helps to differentiate when `defaultValue` is `null` and when it was not even set initially
*
* @var bool
*/
private $defaultValueExists = false;
/**
* InputObjectField constructor.
* @param array $opts
@ -34,9 +41,18 @@ class InputObjectField
public function __construct(array $opts)
{
foreach ($opts as $k => $v) {
switch ($k) {
case 'defaultValue':
$this->defaultValue = $v;
$this->defaultValueExists = true;
break;
case 'defaultValueExists':
break;
default:
$this->{$k} = $v;
}
}
}
/**
* @return mixed
@ -45,4 +61,12 @@ class InputObjectField
{
return Type::resolve($this->type);
}
/**
* @return bool
*/
public function defaultValueExists()
{
return $this->defaultValueExists;
}
}

View File

@ -8,6 +8,7 @@ use GraphQL\Language\AST\FloatValue;
use GraphQL\Language\AST\IntValue;
use GraphQL\Language\AST\ListValue;
use GraphQL\Language\AST\Name;
use GraphQL\Language\AST\NullValue;
use GraphQL\Language\AST\ObjectField;
use GraphQL\Language\AST\ObjectValue;
use GraphQL\Language\AST\StringValue;
@ -43,21 +44,24 @@ class AST
* | Int | Int |
* | Float | Int / Float |
* | Mixed | Enum Value |
* | null | NullValue |
*
* @param $value
* @param InputType $type
* @return ObjectValue|ListValue|BooleanValue|IntValue|FloatValue|EnumValue|StringValue
* @return ObjectValue|ListValue|BooleanValue|IntValue|FloatValue|EnumValue|StringValue|NullValue
*/
static function astFromValue($value, InputType $type)
{
if ($type instanceof NonNull) {
// Note: we're not checking that the result is non-null.
// This function is not responsible for validating the input value.
return self::astFromValue($value, $type->getWrappedType());
$astValue = self::astFromValue($value, $type->getWrappedType());
if ($astValue instanceof NullValue) {
return null;
}
return $astValue;
}
if ($value === null) {
return null;
return new NullValue([]);
}
// Convert PHP array to GraphQL list. If the GraphQLType is a list, but
@ -80,7 +84,8 @@ class AST
// Populate the fields of the input object by creating ASTs from each value
// in the PHP object according to the fields in the input type.
if ($type instanceof InputObjectType) {
$isArrayLike = is_array($value) || $value instanceof \ArrayAccess;
$isArray = is_array($value);
$isArrayLike = $isArray || $value instanceof \ArrayAccess;
if ($value === null || (!$isArrayLike && !is_object($value))) {
return null;
}
@ -92,15 +97,31 @@ class AST
} else {
$fieldValue = isset($value->{$fieldName}) ? $value->{$fieldName} : null;
}
$fieldValue = self::astFromValue($fieldValue, $field->getType());
if ($fieldValue) {
// Have to check additionally if key exists, since we differentiate between
// "no key" and "value is null":
if (null !== $fieldValue) {
$fieldExists = true;
} else if ($isArray) {
$fieldExists = array_key_exists($fieldName, $value);
} else if ($isArrayLike) {
/** @var \ArrayAccess $value */
$fieldExists = $value->offsetExists($fieldName);
} else {
$fieldExists = property_exists($value, $fieldName);
}
if ($fieldExists) {
$fieldNode = self::astFromValue($fieldValue, $field->getType());
if ($fieldNode) {
$fieldASTs[] = new ObjectField([
'name' => new Name(['value' => $fieldName]),
'value' => $fieldValue
'value' => $fieldNode
]);
}
}
}
return new ObjectValue(['fields' => $fieldASTs]);
}
@ -165,11 +186,12 @@ class AST
* | String | String |
* | Int / Float | Int / Float |
* | Enum Value | Mixed |
* | Null Value | null |
*
* @param $valueAST
* @param InputType $type
* @param null $variables
* @return array|null
* @return array|null|\stdClass
* @throws \Exception
*/
public static function valueFromAST($valueAST, InputType $type, $variables = null)
@ -182,14 +204,22 @@ class AST
}
if (!$valueAST) {
return null;
// When there is no AST, then there is also no value.
// Importantly, this is different from returning the GraphQL null value.
return ;
}
if ($valueAST instanceof NullValue) {
// This is explicitly returning the value null.
return NullValue::getNullValue();
}
if ($valueAST instanceof Variable) {
$variableName = $valueAST->name->value;
if (!$variables || !isset($variables[$variableName])) {
return null;
// No valid return value.
return ;
}
// Note: we're not doing any checking that this variable is correct. We're
// assuming that this query has been validated and the variable usage here
@ -199,19 +229,23 @@ class AST
if ($type instanceof ListOfType) {
$itemType = $type->getWrappedType();
if ($valueAST instanceof ListValue) {
return array_map(function($itemAST) use ($itemType, $variables) {
return self::valueFromAST($itemAST, $itemType, $variables);
}, $valueAST->values);
} else {
return [self::valueFromAST($valueAST, $itemType, $variables)];
$items = $valueAST instanceof ListValue ? $valueAST->values : [$valueAST];
$result = [];
foreach ($items as $itemAST) {
$value = self::valueFromAST($itemAST, $itemType, $variables);
if ($value === NullValue::getNullValue()) {
$value = null;
}
$result[] = $value;
}
return $result;
}
if ($type instanceof InputObjectType) {
$fields = $type->getFields();
if (!$valueAST instanceof ObjectValue) {
return null;
// No valid return value.
return ;
}
$fieldASTs = Utils::keyMap($valueAST->fields, function($field) {return $field->name->value;});
$values = [];
@ -219,13 +253,20 @@ class AST
$fieldAST = isset($fieldASTs[$field->name]) ? $fieldASTs[$field->name] : null;
$fieldValue = self::valueFromAST($fieldAST ? $fieldAST->value : null, $field->getType(), $variables);
if (null === $fieldValue) {
// If field is not in AST and defaultValue was never set for this field - do not include it in result
if (null === $fieldValue && null === $field->defaultValue && !$field->defaultValueExists()) {
continue;
}
// Set Explicit null value or default value:
if (NullValue::getNullValue() === $fieldValue) {
$fieldValue = null;
} else if (null === $fieldValue) {
$fieldValue = $field->defaultValue;
}
if (null !== $fieldValue) {
$values[$field->name] = $fieldValue;
}
}
return $values;
}

View File

@ -8,6 +8,7 @@ use GraphQL\Language\AST\Document;
use GraphQL\Language\AST\FragmentSpread;
use GraphQL\Language\AST\Node;
use GraphQL\Language\AST\NodeType;
use GraphQL\Language\AST\NullValue;
use GraphQL\Language\AST\Value;
use GraphQL\Language\AST\Variable;
use GraphQL\Language\Printer;
@ -155,7 +156,7 @@ class DocumentValidator
// A value must be provided if the type is non-null.
if ($type instanceof NonNull) {
$wrappedType = $type->getWrappedType();
if (!$valueAST) {
if (!$valueAST || $valueAST instanceof NullValue) {
if ($wrappedType->name) {
return [ "Expected \"{$wrappedType->name}!\", found null." ];
}
@ -164,7 +165,7 @@ class DocumentValidator
return static::isValidLiteralValue($wrappedType, $valueAST);
}
if (!$valueAST) {
if (!$valueAST || $valueAST instanceof NullValue) {
return [];
}

View File

@ -836,7 +836,7 @@ class ExecutorTest extends \PHPUnit_Framework_TestCase
$result = Executor::execute($schema, $query);
$expected = [
'data' => [
'field' => '{"a":1,"c":0,"d":false,"e":"0","f":"some-string","h":{"a":1,"b":"test"}}'
'field' => '{"a":1,"b":null,"c":0,"d":false,"e":"0","f":"some-string","h":{"a":1,"b":"test"}}'
]
];

View File

@ -49,6 +49,28 @@ class VariablesTest extends \PHPUnit_Framework_TestCase
$this->assertEquals($expected, Executor::execute($this->schema(), $ast)->toArray());
// properly parses null value to null
$doc = '
{
fieldWithObjectInput(input: {a: null, b: null, c: "C", d: null})
}
';
$ast = Parser::parse($doc);
$expected = ['data' => ['fieldWithObjectInput' => '{"a":null,"b":null,"c":"C","d":null}']];
$this->assertEquals($expected, Executor::execute($this->schema(), $ast)->toArray());
// properly parses null value in list
$doc = '
{
fieldWithObjectInput(input: {b: ["A",null,"C"], c: "C"})
}
';
$ast = Parser::parse($doc);
$expected = ['data' => ['fieldWithObjectInput' => '{"b":["A",null,"C"],"c":"C"}']];
$this->assertEquals($expected, Executor::execute($this->schema(), $ast)->toArray());
// does not use incorrect value
$doc = '
{

View File

@ -6,6 +6,7 @@ use GraphQL\Language\AST\Field;
use GraphQL\Language\AST\Name;
use GraphQL\Language\AST\Node;
use GraphQL\Language\AST\NodeType;
use GraphQL\Language\AST\NullValue;
use GraphQL\Language\AST\SelectionSet;
use GraphQL\Language\AST\StringValue;
use GraphQL\Language\Parser;
@ -106,15 +107,6 @@ fragment MissingOn Type
Parser::parse('{ ...on }');
}
/**
* @it does not allow null as value
*/
public function testDoesNotAllowNullAsValue()
{
$this->setExpectedException('GraphQL\Error\SyntaxError', 'Syntax Error GraphQL (1:39) Unexpected Name "null"');
Parser::parse('{ fieldWithNullableStringInput(input: null) }');
}
/**
* @it parses multi-byte characters
*/
@ -393,6 +385,17 @@ fragment $fragmentName on Type {
// Describe: parseValue
/**
* @it parses null value
*/
public function testParsesNullValues()
{
$this->assertEquals([
'kind' => NodeType::NULL,
'loc' => ['start' => 0, 'end' => 4]
], $this->nodeToArray(Parser::parseValue('null')));
}
/**
* @it parses list values
*/

View File

@ -154,7 +154,7 @@ fragment frag on Friend {
}
{
unnamed(truthy: true, falsey: false)
unnamed(truthy: true, falsey: false, nullish: null)
query
}

View File

@ -63,6 +63,7 @@ type Foo implements Bar {
four(argument: String = "string"): String
five(argument: [String] = ["string", "string"]): String
six(argument: InputType = {key: "value"}): Type
seven(argument: Int = null): Type
}
type AnnotatedObject @onObject(arg: "value") {

View File

@ -636,6 +636,12 @@ class VisitorTest extends \PHPUnit_Framework_TestCase
[ 'enter', 'BooleanValue', 'value', 'Argument' ],
[ 'leave', 'BooleanValue', 'value', 'Argument' ],
[ 'leave', 'Argument', 1, null ],
[ 'enter', 'Argument', 2, null ],
[ 'enter', 'Name', 'name', 'Argument' ],
[ 'leave', 'Name', 'name', 'Argument' ],
[ 'enter', 'NullValue', 'value', 'Argument' ],
[ 'leave', 'NullValue', 'value', 'Argument' ],
[ 'leave', 'Argument', 2, null ],
[ 'leave', 'Field', 0, null ],
[ 'enter', 'Field', 1, null ],
[ 'enter', 'Name', 'name', 'Field' ],

View File

@ -52,6 +52,6 @@ fragment frag on Friend {
}
{
unnamed(truthy: true, falsey: false),
unnamed(truthy: true, falsey: false, nullish: null),
query
}

View File

@ -17,6 +17,7 @@ type Foo implements Bar {
four(argument: String = "string"): String
five(argument: [String] = ["string", "string"]): String
six(argument: InputType = {key: "value"}): Type
seven(argument: Int = null): Type
}
type AnnotatedObject @onObject(arg: "value") {

View File

@ -7,6 +7,7 @@ use GraphQL\Language\AST\FloatValue;
use GraphQL\Language\AST\IntValue;
use GraphQL\Language\AST\ListValue;
use GraphQL\Language\AST\Name;
use GraphQL\Language\AST\NullValue;
use GraphQL\Language\AST\ObjectField;
use GraphQL\Language\AST\ObjectValue;
use GraphQL\Language\AST\StringValue;
@ -26,9 +27,11 @@ class ASTFromValueTest extends \PHPUnit_Framework_TestCase
{
$this->assertEquals(new BooleanValue(['value' => true]), AST::astFromValue(true, Type::boolean()));
$this->assertEquals(new BooleanValue(['value' => false]), AST::astFromValue(false, Type::boolean()));
$this->assertEquals(null, AST::astFromValue(null, Type::boolean()));
$this->assertEquals(new NullValue([]), AST::astFromValue(null, Type::boolean()));
$this->assertEquals(new BooleanValue(['value' => false]), AST::astFromValue(0, Type::boolean()));
$this->assertEquals(new BooleanValue(['value' => true]), AST::astFromValue(1, Type::boolean()));
$this->assertEquals(new BooleanValue(['value' => false]), AST::astFromValue(0, Type::nonNull(Type::boolean())));
$this->assertEquals(null, AST::astFromValue(null, Type::nonNull(Type::boolean()))); // Note: null means that AST cannot
}
/**
@ -70,7 +73,8 @@ class ASTFromValueTest extends \PHPUnit_Framework_TestCase
$this->assertEquals(new StringValue(['value' => 'VA\\nLUE']), AST::astFromValue("VA\nLUE", Type::string()));
$this->assertEquals(new StringValue(['value' => '123']), AST::astFromValue(123, Type::string()));
$this->assertEquals(new StringValue(['value' => 'false']), AST::astFromValue(false, Type::string()));
$this->assertEquals(null, AST::astFromValue(null, Type::string()));
$this->assertEquals(new NullValue([]), AST::astFromValue(null, Type::string()));
$this->assertEquals(null, AST::astFromValue(null, Type::nonNull(Type::string())));
}
/**
@ -83,7 +87,16 @@ class ASTFromValueTest extends \PHPUnit_Framework_TestCase
$this->assertEquals(new StringValue(['value' => 'VA\\nLUE']), AST::astFromValue("VA\nLUE", Type::id()));
$this->assertEquals(new IntValue(['value' => '123']), AST::astFromValue(123, Type::id()));
$this->assertEquals(new StringValue(['value' => 'false']), AST::astFromValue(false, Type::id()));
$this->assertEquals(null, AST::astFromValue(null, Type::id()));
$this->assertEquals(new NullValue([]), AST::astFromValue(null, Type::id()));
$this->assertEquals(null, AST::astFromValue(null, Type::nonNull(Type::id())));
}
/**
* @it does not converts NonNull values to NullValue
*/
public function testDoesNotConvertsNonNullValuestoNullValue()
{
$this->assertSame(null, AST::astFromValue(null, Type::nonNull(Type::boolean())));
}
/**
@ -156,6 +169,44 @@ class ASTFromValueTest extends \PHPUnit_Framework_TestCase
$this->assertEquals($expected, AST::astFromValue((object) $data, $inputObj));
}
public function testConvertsInputObjectsWithExplicitNulls()
{
$inputObj = new InputObjectType([
'name' => 'MyInputObj',
'fields' => [
'foo' => Type::float(),
'bar' => $this->myEnum()
]
]);
$this->assertEquals(new ObjectValue([
'fields' => [
$this->objectField('foo', new NullValue([]))
]
]), AST::astFromValue(['foo' => null], $inputObj));
/*
const inputObj = new GraphQLInputObjectType({
name: 'MyInputObj',
fields: {
foo: { type: GraphQLFloat },
bar: { type: myEnum },
}
});
expect(astFromValue(
{ foo: null },
inputObj
)).to.deep.equal(
{ kind: 'ObjectValue',
fields: [
{ kind: 'ObjectField',
name: { kind: 'Name', value: 'foo' },
value: { kind: 'NullValue' } } ] }
);
});
*/
}
private $complexValue;
private function complexValue()

View File

@ -132,6 +132,28 @@ class ArgumentsOfCorrectTypeTest extends TestCase
');
}
/**
* @it null into nullable type
*/
public function testNullIntoNullableType()
{
$this->expectPassesRule(new ArgumentsOfCorrectType(), '
{
complicatedArgs {
intArgField(intArg: null)
}
}
');
$this->expectPassesRule(new ArgumentsOfCorrectType(), '
{
dog(a: null, b: null, c:{ requiredField: true, intField: null }) {
name
}
}
');
}
// Invalid String values
/**
@ -574,6 +596,20 @@ class ArgumentsOfCorrectTypeTest extends TestCase
');
}
/**
* @it Null value
*/
public function testNullValue()
{
$this->expectPassesRule(new ArgumentsOfCorrectType(), '
{
complicatedArgs {
stringListArgField(stringListArg: null)
}
}
');
}
/**
* @it Single value into List
*/
@ -801,6 +837,24 @@ class ArgumentsOfCorrectTypeTest extends TestCase
]);
}
/**
* @it Null value
*/
public function testNullValue2()
{
$this->expectFailsRule(new ArgumentsOfCorrectType(), '
{
complicatedArgs {
multipleReqs(req1: null)
}
}
', [
$this->badValue('req1', 'Int!', 'null', 4, 32, [
'Expected "Int!", found null.'
]),
]);
}
// Valid input object value

View File

@ -50,6 +50,52 @@ class DefaultValuesOfCorrectTypeTest extends TestCase
');
}
/**
* @it variables with valid default null values
*/
public function testVariablesWithValidDefaultNullValues()
{
$this->expectPassesRule(new DefaultValuesOfCorrectType(), '
query WithDefaultValues(
$a: Int = null,
$b: String = null,
$c: ComplexInput = { requiredField: true, intField: null }
) {
dog { name }
}
');
}
/**
* @it variables with invalid default null values
*/
public function testVariablesWithInvalidDefaultNullValues()
{
$this->expectFailsRule(new DefaultValuesOfCorrectType(), '
query WithDefaultValues(
$a: Int! = null,
$b: String! = null,
$c: ComplexInput = { requiredField: null, intField: null }
) {
dog { name }
}
', [
$this->defaultForNonNullArg('a', 'Int!', 'Int', 3, 20),
$this->badValue('a', 'Int!', 'null', 3, 20, [
'Expected "Int!", found null.'
]),
$this->defaultForNonNullArg('b', 'String!', 'String', 4, 23),
$this->badValue('b', 'String!', 'null', 4, 23, [
'Expected "String!", found null.'
]),
$this->badValue('c', 'ComplexInput', '{requiredField: null, intField: null}',
5, 28, [
'In field "requiredField": Expected "Boolean!", found null.'
]
),
]);
}
/**
* @it no required variables with default values
*/