Allow null values for enums

This commit is contained in:
Vladimir Razuvaev 2017-07-04 20:19:52 +07:00
parent f569c6de2d
commit b47c87f793
10 changed files with 99 additions and 15 deletions

View File

@ -247,7 +247,7 @@ class Values
// Scalar/Enum input checks to ensure the type can parse the value to
// a non-null value.
$parseResult = $type->parseValue($value);
if (null === $parseResult) {
if (null === $parseResult && !$type->isValidValue($value)) {
$v = Utils::printSafe($value);
return [
"Expected type \"{$type->name}\", found $v."

View File

@ -91,6 +91,24 @@ class EnumType extends Type implements InputType, OutputType, LeafType
return isset($lookup[$value]) ? $lookup[$value]->name : null;
}
/**
* @param string $value
* @return bool
*/
public function isValidValue($value)
{
return is_string($value) && $this->getNameLookup()->offsetExists($value);
}
/**
* @param $valueNode
* @return bool
*/
public function isValidLiteral($valueNode)
{
return $valueNode instanceof EnumValueNode && $this->getNameLookup()->offsetExists($valueNode->value);
}
/**
* @param $value
* @return null

View File

@ -31,4 +31,16 @@ interface LeafType
* @return mixed
*/
public function parseLiteral($valueNode);
/**
* @param string $value
* @return bool
*/
public function isValidValue($value);
/**
* @param \GraphQL\Language\AST\Node $valueNode
* @return mixed
*/
public function isValidLiteral($valueNode);
}

View File

@ -34,4 +34,28 @@ abstract class ScalarType extends Type implements OutputType, InputType, LeafTyp
Utils::assertValidName($this->name);
}
/**
* Determines if an internal value is valid for this type.
* Equivalent to checking for if the parsedValue is nullish.
*
* @param $value
* @return bool
*/
public function isValidValue($value)
{
return null !== $this->parseValue($value);
}
/**
* Determines if an internal value is valid for this type.
* Equivalent to checking for if the parsedLiteral is nullish.
*
* @param $valueNode
* @return bool
*/
public function isValidLiteral($valueNode)
{
return null !== $this->parseLiteral($valueNode);
}
}

View File

@ -307,9 +307,9 @@ class AST
if ($type instanceof LeafType) {
$parsed = $type->parseLiteral($valueNode);
if (null === $parsed) {
// null represent a failure to parse correctly,
// in which case no value is returned.
if (null === $parsed && !$type->isValidLiteral($valueNode)) {
// Invalid values represent a failure to parse correctly, in which case
// no value is returned.
return $undefined;
}

View File

@ -43,6 +43,16 @@ class MixedStore implements \ArrayAccess
*/
private $lastArrayValue;
/**
* @var mixed
*/
private $nullValue;
/**
* @var bool
*/
private $nullValueIsSet = false;
/**
* MixedStore constructor.
*/
@ -83,6 +93,9 @@ class MixedStore implements \ArrayAccess
}
}
}
if (null === $offset) {
return $this->nullValueIsSet;
}
return false;
}
@ -114,6 +127,9 @@ class MixedStore implements \ArrayAccess
}
}
}
if (null === $offset) {
return $this->nullValue;
}
return null;
}
@ -138,6 +154,9 @@ class MixedStore implements \ArrayAccess
} else if (is_array($offset)) {
$this->arrayKeys[] = $offset;
$this->arrayValues[] = $value;
} else if (null === $offset) {
$this->nullValue = $value;
$this->nullValueIsSet = true;
} else {
throw new \InvalidArgumentException("Unexpected offset type: " . Utils::printSafe($offset));
}
@ -165,6 +184,9 @@ class MixedStore implements \ArrayAccess
array_splice($this->arrayKeys, $index, 1);
array_splice($this->arrayValues, $index, 1);
}
} else if (null === $offset) {
$this->nullValue = null;
$this->nullValueIsSet = false;
}
}
}

View File

@ -1,22 +1,16 @@
<?php
namespace GraphQL\Validator;
use GraphQL\Error\Error;
use GraphQL\Error\InvariantViolation;
use GraphQL\Language\AST\ListValueNode;
use GraphQL\Language\AST\DocumentNode;
use GraphQL\Language\AST\FragmentSpreadNode;
use GraphQL\Language\AST\Node;
use GraphQL\Language\AST\NodeKind;
use GraphQL\Language\AST\NullValueNode;
use GraphQL\Language\AST\ValueNode;
use GraphQL\Language\AST\VariableNode;
use GraphQL\Language\Printer;
use GraphQL\Language\Visitor;
use GraphQL\Language\VisitorOperation;
use GraphQL\Schema;
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;
@ -231,11 +225,8 @@ class DocumentValidator
}
if ($type instanceof LeafType) {
// Scalar/Enum input checks to ensure the type can parse the value to
// a non-null value.
$parseResult = $type->parseLiteral($valueNode);
if (null === $parseResult) {
// Scalars must parse to a non-null value
if (!$type->isValidLiteral($valueNode)) {
$printed = Printer::doPrint($valueNode);
return [ "Expected type \"{$type->name}\", found $printed." ];
}

View File

@ -72,6 +72,7 @@ class ValueFromAstTest extends \PHPUnit_Framework_TestCase
'RED' => ['value' => 1],
'GREEN' => ['value' => 2],
'BLUE' => ['value' => 3],
'NULL' => ['value' => null],
]
]);
@ -80,6 +81,7 @@ class ValueFromAstTest extends \PHPUnit_Framework_TestCase
$this->runTestCase($testEnum, '3', Utils::undefined());
$this->runTestCase($testEnum, '"BLUE"', Utils::undefined());
$this->runTestCase($testEnum, 'null', null);
$this->runTestCase($testEnum, 'NULL', null);
}
/**

View File

@ -132,6 +132,20 @@ class ArgumentsOfCorrectTypeTest extends TestCase
');
}
/**
* @it Enum with null value
*/
public function testEnumWithNullValue()
{
$this->expectPassesRule(new ArgumentsOfCorrectType(), '
{
complicatedArgs {
enumArgField(enumArg: NO_FUR)
}
}
');
}
/**
* @it null into nullable type
*/

View File

@ -183,6 +183,7 @@ abstract class TestCase extends \PHPUnit_Framework_TestCase
'BLACK' => [ 'value' => 1 ],
'TAN' => [ 'value' => 2 ],
'SPOTTED' => [ 'value' => 3 ],
'NO_FUR' => [ 'value' => null ],
],
]);