More definition tests (for type validation)

This commit is contained in:
Vladimir Razuvaev 2018-08-07 23:04:39 +07:00
parent 392b567f23
commit 39df711eac
10 changed files with 1153 additions and 29 deletions

View File

@ -1,5 +1,6 @@
<?php <?php
namespace GraphQL\Type\Definition; namespace GraphQL\Type\Definition;
use GraphQL\Error\Error;
use GraphQL\Error\InvariantViolation; use GraphQL\Error\InvariantViolation;
use GraphQL\Language\AST\FieldDefinitionNode; use GraphQL\Language\AST\FieldDefinitionNode;
use GraphQL\Language\AST\TypeDefinitionNode; use GraphQL\Language\AST\TypeDefinitionNode;
@ -82,8 +83,15 @@ class FieldDefinition
$map = []; $map = [];
foreach ($fields as $name => $field) { foreach ($fields as $name => $field) {
if (is_array($field)) { if (is_array($field)) {
if (!isset($field['name']) && is_string($name)) { if (!isset($field['name'])) {
$field['name'] = $name; if (is_string($name)) {
$field['name'] = $name;
} else {
throw new InvariantViolation(
"{$type->name} fields must be an associative array with field names as keys or a " .
"function which returns such an array."
);
}
} }
if (isset($field['args']) && !is_array($field['args'])) { if (isset($field['args']) && !is_array($field['args'])) {
throw new InvariantViolation( throw new InvariantViolation(
@ -185,7 +193,7 @@ class FieldDefinition
{ {
try { try {
Utils::assertValidName($this->name); Utils::assertValidName($this->name);
} catch (InvariantViolation $e) { } catch (Error $e) {
throw new InvariantViolation("{$parentType->name}.{$this->name}: {$e->getMessage()}"); throw new InvariantViolation("{$parentType->name}.{$this->name}: {$e->getMessage()}");
} }
Utils::invariant( Utils::invariant(

View File

@ -1,6 +1,9 @@
<?php <?php
namespace GraphQL\Type\Definition; namespace GraphQL\Type\Definition;
use GraphQL\Error\Error;
use GraphQL\Error\InvariantViolation;
use GraphQL\Language\AST\InputValueDefinitionNode; use GraphQL\Language\AST\InputValueDefinitionNode;
use GraphQL\Utils\Utils;
/** /**
* Class InputObjectField * Class InputObjectField
@ -81,4 +84,30 @@ class InputObjectField
{ {
return $this->defaultValueExists; return $this->defaultValueExists;
} }
/**
* @param Type $parentType
* @throws InvariantViolation
*/
public function assertValid(Type $parentType)
{
try {
Utils::assertValidName($this->name);
} catch (Error $e) {
throw new InvariantViolation("{$parentType->name}.{$this->name}: {$e->getMessage()}");
}
$type = $this->type;
if ($type instanceof WrappingType) {
$type = $type->getWrappedType(true);
}
Utils::invariant(
$type instanceof InputType,
"{$parentType->name}.{$this->name} field type must be Input Type but got: " . Utils::printSafe($this->type)
);
Utils::invariant(
empty($this->config['resolve']),
"{$parentType->name}.{$this->name} field type has a resolve property, " .
'but Input Types cannot define resolvers.'
);
}
} }

View File

@ -80,4 +80,25 @@ class InputObjectType extends Type implements InputType, NamedType
Utils::invariant(isset($this->fields[$name]), "Field '%s' is not defined for type '%s'", $name, $this->name); Utils::invariant(isset($this->fields[$name]), "Field '%s' is not defined for type '%s'", $name, $this->name);
return $this->fields[$name]; return $this->fields[$name];
} }
/**
* Validates type config and throws if one of type options is invalid.
* Note: this method is shallow, it won't validate object fields and their arguments.
*
* @throws InvariantViolation
*/
public function assertValid()
{
parent::assertValid();
Utils::invariant(
!empty($this->getFields()),
"{$this->name} fields must be an associative array with field names as keys or a " .
"callable which returns such an array."
);
foreach ($this->getFields() as $field) {
$field->assertValid($this);
}
}
} }

View File

@ -110,9 +110,11 @@ class InterfaceType extends Type implements AbstractType, OutputType, CompositeT
{ {
parent::assertValid(); parent::assertValid();
$resolveType = $this->config['resolveType'] ?? null;
Utils::invariant( Utils::invariant(
!isset($this->config['resolveType']) || is_callable($this->config['resolveType']), !isset($resolveType) || is_callable($resolveType),
"{$this->name} must provide \"resolveType\" as a function." "{$this->name} must provide \"resolveType\" as a function, but got: " . Utils::printSafe($resolveType)
); );
} }
} }

View File

@ -207,9 +207,15 @@ class ObjectType extends Type implements OutputType, CompositeType, NamedType
"{$this->name} description must be string if set, but it is: " . Utils::printSafe($this->description) "{$this->name} description must be string if set, but it is: " . Utils::printSafe($this->description)
); );
$isTypeOf = $this->config['isTypeOf'] ?? null;
Utils::invariant( Utils::invariant(
!isset($this->config['isTypeOf']) || is_callable($this->config['isTypeOf']), !isset($isTypeOf) || is_callable($isTypeOf),
"{$this->name} must provide 'isTypeOf' as a function" "{$this->name} must provide \"isTypeOf\" as a function, but got: " . Utils::printSafe($isTypeOf)
); );
foreach ($this->getFields() as $field) {
$field->assertValid($this);
}
} }
} }

View File

@ -216,16 +216,7 @@ abstract class Type implements \JsonSerializable
*/ */
public static function isType($type) public static function isType($type)
{ {
return ( return $type instanceof Type;
$type instanceof ScalarType ||
$type instanceof ObjectType ||
$type instanceof InterfaceType ||
$type instanceof UnionType ||
$type instanceof EnumType ||
$type instanceof InputObjectType ||
$type instanceof ListOfType ||
$type instanceof NonNull
);
} }
/** /**

View File

@ -121,7 +121,7 @@ class UnionType extends Type implements AbstractType, OutputType, CompositeType,
if (isset($this->config['resolveType'])) { if (isset($this->config['resolveType'])) {
Utils::invariant( Utils::invariant(
is_callable($this->config['resolveType']), is_callable($this->config['resolveType']),
"{$this->name} must provide \"resolveType\" as a function." "{$this->name} must provide \"resolveType\" as a function, but got: " . Utils::printSafe($this->config['resolveType'])
); );
} }
} }

View File

@ -406,7 +406,8 @@ class Schema
* @api * @api
* @return InvariantViolation[]|Error[] * @return InvariantViolation[]|Error[]
*/ */
public function validate() { public function validate()
{
// If this Schema has already been validated, return the previous results. // If this Schema has already been validated, return the previous results.
if ($this->validationErrors !== null) { if ($this->validationErrors !== null) {
return $this->validationErrors; return $this->validationErrors;

View File

@ -49,11 +49,13 @@ class SchemaValidationContext
/** /**
* @return Error[] * @return Error[]
*/ */
public function getErrors() { public function getErrors()
{
return $this->errors; return $this->errors;
} }
public function validateRootTypes() { public function validateRootTypes()
{
$queryType = $this->schema->getQueryType(); $queryType = $this->schema->getQueryType();
if (!$queryType) { if (!$queryType) {
$this->reportError( $this->reportError(
@ -208,7 +210,8 @@ class SchemaValidationContext
/** /**
* @param ObjectType|InterfaceType $type * @param ObjectType|InterfaceType $type
*/ */
private function validateFields($type) { private function validateFields($type)
{
$fieldMap = $type->getFields(); $fieldMap = $type->getFields();
// Objects and Interfaces both must define one or more fields. // Objects and Interfaces both must define one or more fields.
@ -271,7 +274,8 @@ class SchemaValidationContext
} }
} }
private function validateObjectInterfaces(ObjectType $object) { private function validateObjectInterfaces(ObjectType $object)
{
$implementedTypeNames = []; $implementedTypeNames = [];
foreach($object->getInterfaces() as $iface) { foreach($object->getInterfaces() as $iface) {
if (isset($implementedTypeNames[$iface->name])) { if (isset($implementedTypeNames[$iface->name])) {
@ -714,7 +718,8 @@ class SchemaValidationContext
* @param string $message * @param string $message
* @param array|Node|TypeNode|TypeDefinitionNode $nodes * @param array|Node|TypeNode|TypeDefinitionNode $nodes
*/ */
private function reportError($message, $nodes = null) { private function reportError($message, $nodes = null)
{
$nodes = array_filter($nodes && is_array($nodes) ? $nodes : [$nodes]); $nodes = array_filter($nodes && is_array($nodes) ? $nodes : [$nodes]);
$this->addError(new Error($message, $nodes)); $this->addError(new Error($message, $nodes));
} }
@ -722,7 +727,8 @@ class SchemaValidationContext
/** /**
* @param Error $error * @param Error $error
*/ */
private function addError($error) { private function addError($error)
{
$this->errors[] = $error; $this->errors[] = $error;
} }
} }

File diff suppressed because it is too large Load Diff