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
namespace GraphQL\Type\Definition;
use GraphQL\Error\Error;
use GraphQL\Error\InvariantViolation;
use GraphQL\Language\AST\FieldDefinitionNode;
use GraphQL\Language\AST\TypeDefinitionNode;
@ -82,8 +83,15 @@ class FieldDefinition
$map = [];
foreach ($fields as $name => $field) {
if (is_array($field)) {
if (!isset($field['name']) && is_string($name)) {
$field['name'] = $name;
if (!isset($field['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'])) {
throw new InvariantViolation(
@ -185,7 +193,7 @@ class FieldDefinition
{
try {
Utils::assertValidName($this->name);
} catch (InvariantViolation $e) {
} catch (Error $e) {
throw new InvariantViolation("{$parentType->name}.{$this->name}: {$e->getMessage()}");
}
Utils::invariant(

View File

@ -1,6 +1,9 @@
<?php
namespace GraphQL\Type\Definition;
use GraphQL\Error\Error;
use GraphQL\Error\InvariantViolation;
use GraphQL\Language\AST\InputValueDefinitionNode;
use GraphQL\Utils\Utils;
/**
* Class InputObjectField
@ -81,4 +84,30 @@ class InputObjectField
{
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);
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();
$resolveType = $this->config['resolveType'] ?? null;
Utils::invariant(
!isset($this->config['resolveType']) || is_callable($this->config['resolveType']),
"{$this->name} must provide \"resolveType\" as a function."
!isset($resolveType) || is_callable($resolveType),
"{$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)
);
$isTypeOf = $this->config['isTypeOf'] ?? null;
Utils::invariant(
!isset($this->config['isTypeOf']) || is_callable($this->config['isTypeOf']),
"{$this->name} must provide 'isTypeOf' as a function"
!isset($isTypeOf) || is_callable($isTypeOf),
"{$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)
{
return (
$type instanceof ScalarType ||
$type instanceof ObjectType ||
$type instanceof InterfaceType ||
$type instanceof UnionType ||
$type instanceof EnumType ||
$type instanceof InputObjectType ||
$type instanceof ListOfType ||
$type instanceof NonNull
);
return $type instanceof Type;
}
/**

View File

@ -121,7 +121,7 @@ class UnionType extends Type implements AbstractType, OutputType, CompositeType,
if (isset($this->config['resolveType'])) {
Utils::invariant(
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
* @return InvariantViolation[]|Error[]
*/
public function validate() {
public function validate()
{
// If this Schema has already been validated, return the previous results.
if ($this->validationErrors !== null) {
return $this->validationErrors;

View File

@ -49,11 +49,13 @@ class SchemaValidationContext
/**
* @return Error[]
*/
public function getErrors() {
public function getErrors()
{
return $this->errors;
}
public function validateRootTypes() {
public function validateRootTypes()
{
$queryType = $this->schema->getQueryType();
if (!$queryType) {
$this->reportError(
@ -208,7 +210,8 @@ class SchemaValidationContext
/**
* @param ObjectType|InterfaceType $type
*/
private function validateFields($type) {
private function validateFields($type)
{
$fieldMap = $type->getFields();
// 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 = [];
foreach($object->getInterfaces() as $iface) {
if (isset($implementedTypeNames[$iface->name])) {
@ -714,7 +718,8 @@ class SchemaValidationContext
* @param string $message
* @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]);
$this->addError(new Error($message, $nodes));
}
@ -722,7 +727,8 @@ class SchemaValidationContext
/**
* @param Error $error
*/
private function addError($error) {
private function addError($error)
{
$this->errors[] = $error;
}
}

File diff suppressed because it is too large Load Diff