Introspection for april 2016 spec + fixed tests

This commit is contained in:
vladar 2016-05-01 03:04:16 +06:00
parent e7c7924dc0
commit 183a9d72cf
2 changed files with 448 additions and 207 deletions

View File

@ -2,6 +2,7 @@
namespace GraphQL\Type; namespace GraphQL\Type;
use GraphQL\Language\Printer;
use GraphQL\Schema; use GraphQL\Schema;
use GraphQL\Type\Definition\Directive; use GraphQL\Type\Definition\Directive;
use GraphQL\Type\Definition\EnumType; use GraphQL\Type\Definition\EnumType;
@ -16,6 +17,7 @@ use GraphQL\Type\Definition\ScalarType;
use GraphQL\Type\Definition\Type; use GraphQL\Type\Definition\Type;
use GraphQL\Type\Definition\UnionType; use GraphQL\Type\Definition\UnionType;
use GraphQL\Type\Definition\WrappingType; use GraphQL\Type\Definition\WrappingType;
use GraphQL\Utils\AST;
class TypeKind { class TypeKind {
const SCALAR = 0; const SCALAR = 0;
@ -42,18 +44,17 @@ class Introspection
__schema { __schema {
queryType { name } queryType { name }
mutationType { name } mutationType { name }
subscriptionType { name }
types { types {
...FullType ...FullType
} }
directives { directives {
name name
description description
locations
args { args {
...InputValue ...InputValue
} }
onOperation
onFragment
onField
} }
} }
} }
@ -62,7 +63,7 @@ class Introspection
kind kind
name name
description description
fields { fields(includeDeprecated: true) {
name name
description description
args { args {
@ -80,7 +81,7 @@ class Introspection
interfaces { interfaces {
...TypeRef ...TypeRef
} }
enumValues { enumValues(includeDeprecated: true) {
name name
description description
isDeprecated isDeprecated
@ -120,17 +121,17 @@ EOD;
__schema { __schema {
queryType { name } queryType { name }
mutationType { name } mutationType { name }
subscriptionType { name }
types { types {
...FullType ...FullType
} }
directives { directives {
name name
locations
args { args {
...InputValue ...InputValue
} }
onOperation
onFragment
onField
} }
} }
} }
@ -138,8 +139,10 @@ EOD;
fragment FullType on __Type { fragment FullType on __Type {
kind kind
name name
fields {
fields(includeDeprecated: true) {
name name
args { args {
...InputValue ...InputValue
} }
@ -155,8 +158,9 @@ EOD;
interfaces { interfaces {
...TypeRef ...TypeRef
} }
enumValues { enumValues(includeDeprecated: true) {
name name
isDeprecated isDeprecated
deprecationReason deprecationReason
} }
@ -167,6 +171,7 @@ EOD;
fragment InputValue on __InputValue { fragment InputValue on __InputValue {
name name
type { ...TypeRef } type { ...TypeRef }
defaultValue defaultValue
} }
@ -250,130 +255,223 @@ EOD;
if (!isset(self::$_map['__Directive'])) { if (!isset(self::$_map['__Directive'])) {
self::$_map['__Directive'] = new ObjectType([ self::$_map['__Directive'] = new ObjectType([
'name' => '__Directive', 'name' => '__Directive',
'description' => 'A Directive provides a way to describe alternate runtime execution and ' .
'type validation behavior in a GraphQL document.' .
'\n\nIn some cases, you need to provide options to alter GraphQLs ' .
'execution behavior in ways field arguments will not suffice, such as ' .
'conditionally including or skipping a field. Directives provide this by ' .
'describing additional information to the executor.',
'fields' => [ 'fields' => [
'name' => ['type' => Type::nonNull(Type::string())], 'name' => ['type' => Type::nonNull(Type::string())],
'description' => ['type' => Type::string()], 'description' => ['type' => Type::string()],
'locations' => [
'type' => Type::nonNull(Type::listOf(Type::nonNull(
self::_directiveLocation()
)))
],
'args' => [ 'args' => [
'type' => Type::nonNull(Type::listOf(Type::nonNull(self::_inputValue()))), 'type' => Type::nonNull(Type::listOf(Type::nonNull(self::_inputValue()))),
'resolve' => function(Directive $directive) {return $directive->args ?: [];} 'resolve' => function (Directive $directive) {
return $directive->args ?: [];
}
], ],
'onOperation' => ['type' => Type::nonNull(Type::boolean())],
'onFragment' => ['type' => Type::nonNull(Type::boolean())], // NOTE: the following three fields are deprecated and are no longer part
'onField' => ['type' => Type::nonNull(Type::boolean())] // of the GraphQL specification.
'onOperation' => [
'deprecationReason' => 'Use `locations`.',
'type' => Type::nonNull(Type::boolean()),
'resolve' => function($d) {
return in_array(Directive::$directiveLocations['QUERY'], $d['locations']) ||
in_array(Directive::$directiveLocations['MUTATION'], $d['locations']) ||
in_array(Directive::$directiveLocations['SUBSCRIPTION'], $d['locations']);
}
],
'onFragment' => [
'deprecationReason' => 'Use `locations`.',
'type' => Type::nonNull(Type::boolean()),
'resolve' => function($d) {
return in_array(Directive::$directiveLocations['FRAGMENT_SPREAD'], $d['locations']) ||
in_array(Directive::$directiveLocations['INLINE_FRAGMENT'], $d['locations']) ||
in_array(Directive::$directiveLocations['FRAGMENT_DEFINITION'], $d['locations']);
}
],
'onField' => [
'deprecationReason' => 'Use `locations`.',
'type' => Type::nonNull(Type::boolean()),
'resolve' => function($d) {
return in_array(Directive::$directiveLocations['FIELD'], $d['locations']);
}
]
] ]
]); ]);
} }
return self::$_map['__Directive']; return self::$_map['__Directive'];
} }
public static function _directiveLocation()
{
if (!isset(self::$_map['__DirectiveLocation'])) {
self::$_map['__DirectiveLocation'] = new EnumType([
'name' => '__DirectiveLocation',
'description' =>
'A Directive can be adjacent to many parts of the GraphQL language, a ' .
'__DirectiveLocation describes one such possible adjacencies.',
'values' => [
'QUERY' => [
'value' => Directive::$directiveLocations['QUERY'],
'description' => 'Location adjacent to a query operation.'
],
'MUTATION' => [
'value' => Directive::$directiveLocations['MUTATION'],
'description' => 'Location adjacent to a mutation operation.'
],
'SUBSCRIPTION' => [
'value' => Directive::$directiveLocations['SUBSCRIPTION'],
'description' => 'Location adjacent to a subscription operation.'
],
'FIELD' => [
'value' => Directive::$directiveLocations['FIELD'],
'description' => 'Location adjacent to a field.'
],
'FRAGMENT_DEFINITION' => [
'value' => Directive::$directiveLocations['FRAGMENT_DEFINITION'],
'description' => 'Location adjacent to a fragment definition.'
],
'FRAGMENT_SPREAD' => [
'value' => Directive::$directiveLocations['FRAGMENT_SPREAD'],
'description' => 'Location adjacent to a fragment spread.'
],
'INLINE_FRAGMENT' => [
'value' => Directive::$directiveLocations['INLINE_FRAGMENT'],
'description' => 'Location adjacent to an inline fragment.'
],
]
]);
}
return self::$_map['__DirectiveLocation'];
}
public static function _type() public static function _type()
{ {
if (!isset(self::$_map['__Type'])) { if (!isset(self::$_map['__Type'])) {
self::$_map['__Type'] = new ObjectType([ self::$_map['__Type'] = new ObjectType([
'name' => '__Type', 'name' => '__Type',
'fields' => [ 'description' =>
'kind' => [ 'The fundamental unit of any GraphQL Schema is the type. There are ' .
'type' => Type::nonNull(self::_typeKind()), 'many kinds of types in GraphQL as represented by the `__TypeKind` enum.' .
'resolve' => function (Type $type) { "\n\n".
switch (true) { 'Depending on the kind of a type, certain fields describe ' .
case $type instanceof ListOfType: 'information about that type. Scalar types provide no information ' .
return TypeKind::LIST_KIND; 'beyond a name and description, while Enum types provide their values. ' .
case $type instanceof NonNull: 'Object and Interface types provide the fields they describe. Abstract ' .
return TypeKind::NON_NULL; 'types, Union and Interface, provide the Object types possible ' .
case $type instanceof ScalarType: 'at runtime. List and NonNull types compose other types.',
return TypeKind::SCALAR; 'fields' => function() {
case $type instanceof ObjectType: return [
return TypeKind::OBJECT; 'kind' => [
case $type instanceof EnumType: 'type' => Type::nonNull(self::_typeKind()),
return TypeKind::ENUM; 'resolve' => function (Type $type) {
case $type instanceof InputObjectType: switch (true) {
return TypeKind::INPUT_OBJECT; case $type instanceof ListOfType:
case $type instanceof InterfaceType: return TypeKind::LIST_KIND;
return TypeKind::INTERFACE_KIND; case $type instanceof NonNull:
case $type instanceof UnionType: return TypeKind::NON_NULL;
return TypeKind::UNION; case $type instanceof ScalarType:
default: return TypeKind::SCALAR;
throw new \Exception("Unknown kind of type: " . print_r($type, true)); case $type instanceof ObjectType:
} return TypeKind::OBJECT;
} case $type instanceof EnumType:
], return TypeKind::ENUM;
'name' => ['type' => Type::string()], case $type instanceof InputObjectType:
'description' => ['type' => Type::string()], return TypeKind::INPUT_OBJECT;
'fields' => [ case $type instanceof InterfaceType:
'type' => Type::listOf(Type::nonNull(self::_field())), return TypeKind::INTERFACE_KIND;
'args' => [ case $type instanceof UnionType:
'includeDeprecated' => ['type' => Type::boolean(), 'defaultValue' => false] return TypeKind::UNION;
], default:
'resolve' => function (Type $type, $args) { throw new \Exception("Unknown kind of type: " . print_r($type, true));
if ($type instanceof ObjectType || $type instanceof InterfaceType) {
$fields = $type->getFields();
if (empty($args['includeDeprecated'])) {
$fields = array_filter($fields, function (FieldDefinition $field) {
return !$field->deprecationReason;
});
} }
return array_values($fields);
} }
return null;
}
],
'interfaces' => [
'type' => Type::listOf(Type::nonNull([__CLASS__, '_type'])),
'resolve' => function ($type) {
if ($type instanceof ObjectType) {
return $type->getInterfaces();
}
return null;
}
],
'possibleTypes' => [
'type' => Type::listOf(Type::nonNull([__CLASS__, '_type'])),
'resolve' => function ($type) {
if ($type instanceof InterfaceType || $type instanceof UnionType) {
return $type->getPossibleTypes();
}
return null;
}
],
'enumValues' => [
'type' => Type::listOf(Type::nonNull(self::_enumValue())),
'args' => [
'includeDeprecated' => ['type' => Type::boolean(), 'defaultValue' => false]
], ],
'resolve' => function ($type, $args) { 'name' => ['type' => Type::string()],
if ($type instanceof EnumType) { 'description' => ['type' => Type::string()],
$values = array_values($type->getValues()); 'fields' => [
'type' => Type::listOf(Type::nonNull(self::_field())),
'args' => [
'includeDeprecated' => ['type' => Type::boolean(), 'defaultValue' => false]
],
'resolve' => function (Type $type, $args) {
if ($type instanceof ObjectType || $type instanceof InterfaceType) {
$fields = $type->getFields();
if (empty($args['includeDeprecated'])) { if (empty($args['includeDeprecated'])) {
$values = array_filter($values, function ($value) { $fields = array_filter($fields, function (FieldDefinition $field) {
return !$value->deprecationReason; return !$field->deprecationReason;
}); });
}
return array_values($fields);
} }
return null;
}
],
'interfaces' => [
'type' => Type::listOf(Type::nonNull(self::_type())),
'resolve' => function ($type) {
if ($type instanceof ObjectType) {
return $type->getInterfaces();
}
return null;
}
],
'possibleTypes' => [
'type' => Type::listOf(Type::nonNull([__CLASS__, '_type'])),
'resolve' => function ($type, $args, ResolveInfo $info) {
if ($type instanceof InterfaceType || $type instanceof UnionType) {
return $info->schema->getPossibleTypes($type);
}
return null;
}
],
'enumValues' => [
'type' => Type::listOf(Type::nonNull(self::_enumValue())),
'args' => [
'includeDeprecated' => ['type' => Type::boolean(), 'defaultValue' => false]
],
'resolve' => function ($type, $args) {
if ($type instanceof EnumType) {
$values = array_values($type->getValues());
return $values; if (empty($args['includeDeprecated'])) {
$values = array_filter($values, function ($value) {
return !$value->deprecationReason;
});
}
return $values;
}
return null;
} }
return null; ],
} 'inputFields' => [
], 'type' => Type::listOf(Type::nonNull(self::_inputValue())),
'inputFields' => [ 'resolve' => function ($type) {
'type' => Type::listOf(Type::nonNull(self::_inputValue())), if ($type instanceof InputObjectType) {
'resolve' => function ($type) { return array_values($type->getFields());
if ($type instanceof InputObjectType) { }
return array_values($type->getFields()); return null;
} }
return null; ],
} 'ofType' => [
], 'type' => self::_type(),
'ofType' => [ 'resolve' => function ($type) {
'type' => [__CLASS__, '_type'], if ($type instanceof WrappingType) {
'resolve' => function($type) { return $type->getWrappedType();
if ($type instanceof WrappingType) { }
return $type->getWrappedType(); return null;
} }]
return null; ];
}] }
]
]); ]);
} }
return self::$_map['__Type']; return self::$_map['__Type'];
@ -385,31 +483,36 @@ EOD;
self::$_map['__Field'] = new ObjectType([ self::$_map['__Field'] = new ObjectType([
'name' => '__Field', 'name' => '__Field',
'fields' => [ 'description' =>
'name' => ['type' => Type::nonNull(Type::string())], 'Object and Interface types are described by a list of Fields, each of ' .
'description' => ['type' => Type::string()], 'which has a name, potentially a list of arguments, and a return type.',
'args' => [ 'fields' => function() {
'type' => Type::nonNull(Type::listOf(Type::nonNull(self::_inputValue()))), return [
'resolve' => function (FieldDefinition $field) { 'name' => ['type' => Type::nonNull(Type::string())],
return empty($field->args) ? [] : $field->args; 'description' => ['type' => Type::string()],
} 'args' => [
], 'type' => Type::nonNull(Type::listOf(Type::nonNull(self::_inputValue()))),
'type' => [ 'resolve' => function (FieldDefinition $field) {
'type' => Type::nonNull([__CLASS__, '_type']), return empty($field->args) ? [] : $field->args;
'resolve' => function ($field) { }
return $field->getType(); ],
} 'type' => [
], 'type' => Type::nonNull(self::_type()),
'isDeprecated' => [ 'resolve' => function ($field) {
'type' => Type::nonNull(Type::boolean()), return $field->getType();
'resolve' => function (FieldDefinition $field) { }
return !!$field->deprecationReason; ],
} 'isDeprecated' => [
], 'type' => Type::nonNull(Type::boolean()),
'deprecationReason' => [ 'resolve' => function (FieldDefinition $field) {
'type' => Type::string() return !!$field->deprecationReason;
] }
] ],
'deprecationReason' => [
'type' => Type::string()
]
];
}
]); ]);
} }
return self::$_map['__Field']; return self::$_map['__Field'];
@ -420,22 +523,30 @@ EOD;
if (!isset(self::$_map['__InputValue'])) { if (!isset(self::$_map['__InputValue'])) {
self::$_map['__InputValue'] = new ObjectType([ self::$_map['__InputValue'] = new ObjectType([
'name' => '__InputValue', 'name' => '__InputValue',
'fields' => [ 'description' =>
'name' => ['type' => Type::nonNull(Type::string())], 'Arguments provided to Fields or Directives and the input fields of an ' .
'description' => ['type' => Type::string()], 'InputObject are represented as Input Values which describe their type ' .
'type' => [ 'and optionally a default value.',
'type' => Type::nonNull([__CLASS__, '_type']), 'fields' => function() {
'resolve' => function($value) { return [
return method_exists($value, 'getType') ? $value->getType() : $value->type; 'name' => ['type' => Type::nonNull(Type::string())],
} 'description' => ['type' => Type::string()],
], 'type' => [
'defaultValue' => [ 'type' => Type::nonNull(self::_type()),
'type' => Type::string(), 'resolve' => function ($value) {
'resolve' => function ($inputValue) { return method_exists($value, 'getType') ? $value->getType() : $value->type;
return $inputValue->defaultValue === null ? null : json_encode($inputValue->defaultValue); }
} ],
] 'defaultValue' => [
] 'type' => Type::string(),
'resolve' => function ($inputValue) {
return $inputValue->defaultValue === null
? null
: Printer::doPrint(AST::astFromValue($inputValue->defaultValue, $inputValue->getType()));
}
]
];
}
]); ]);
} }
return self::$_map['__InputValue']; return self::$_map['__InputValue'];
@ -446,6 +557,10 @@ EOD;
if (!isset(self::$_map['__EnumValue'])) { if (!isset(self::$_map['__EnumValue'])) {
self::$_map['__EnumValue'] = new ObjectType([ self::$_map['__EnumValue'] = new ObjectType([
'name' => '__EnumValue', 'name' => '__EnumValue',
'description' =>
'One possible value for a given Enum. Enum values are unique values, not ' .
'a placeholder for a string or numeric value. However an Enum value is ' .
'returned in a JSON response as a string.',
'fields' => [ 'fields' => [
'name' => ['type' => Type::nonNull(Type::string())], 'name' => ['type' => Type::nonNull(Type::string())],
'description' => ['type' => Type::string()], 'description' => ['type' => Type::string()],
@ -469,7 +584,7 @@ EOD;
if (!isset(self::$_map['__TypeKind'])) { if (!isset(self::$_map['__TypeKind'])) {
self::$_map['__TypeKind'] = new EnumType([ self::$_map['__TypeKind'] = new EnumType([
'name' => '__TypeKind', 'name' => '__TypeKind',
'description' => 'An enum describing what kind of type a given __Type is', 'description' => 'An enum describing what kind of type a given __Type is.',
'values' => [ 'values' => [
'SCALAR' => [ 'SCALAR' => [
'value' => TypeKind::SCALAR, 'value' => TypeKind::SCALAR,

View File

@ -14,12 +14,19 @@ use GraphQL\Validator\Rules\ProvidedNonNullArguments;
class IntrospectionTest extends \PHPUnit_Framework_TestCase class IntrospectionTest extends \PHPUnit_Framework_TestCase
{ {
// Describe: Introspection
/**
* @it executes an introspection query
*/
function testExecutesAnIntrospectionQuery() function testExecutesAnIntrospectionQuery()
{ {
$emptySchema = new Schema(new ObjectType([ $emptySchema = new Schema([
'name' => 'QueryRoot', 'query' => new ObjectType([
'fields' => [] 'name' => 'QueryRoot',
])); 'fields' => []
])
]);
$request = Introspection::getIntrospectionQuery(false); $request = Introspection::getIntrospectionQuery(false);
$expected = array ( $expected = array (
@ -28,6 +35,7 @@ class IntrospectionTest extends \PHPUnit_Framework_TestCase
'__schema' => '__schema' =>
array ( array (
'mutationType' => NULL, 'mutationType' => NULL,
'subscriptionType' => NULL,
'queryType' => 'queryType' =>
array ( array (
'name' => 'QueryRoot', 'name' => 'QueryRoot',
@ -44,7 +52,7 @@ class IntrospectionTest extends \PHPUnit_Framework_TestCase
), ),
'enumValues' => NULL, 'enumValues' => NULL,
'possibleTypes' => NULL, 'possibleTypes' => NULL,
'fields' => [] 'fields' => Array ()
), ),
1 => 1 =>
array ( array (
@ -117,13 +125,13 @@ class IntrospectionTest extends \PHPUnit_Framework_TestCase
'deprecationReason' => NULL, 'deprecationReason' => NULL,
), ),
3 => 3 =>
array( array (
'name' => 'subscriptionType', 'name' => 'subscriptionType',
'args' => 'args' =>
array( array (
), ),
'type' => 'type' =>
array( array (
'kind' => 'OBJECT', 'kind' => 'OBJECT',
'name' => '__Type', 'name' => '__Type',
'ofType' => NULL, 'ofType' => NULL,
@ -817,6 +825,35 @@ class IntrospectionTest extends \PHPUnit_Framework_TestCase
'deprecationReason' => NULL, 'deprecationReason' => NULL,
), ),
2 => 2 =>
array (
'name' => 'locations',
'args' =>
array (
),
'type' =>
array (
'kind' => 'NON_NULL',
'name' => NULL,
'ofType' =>
array (
'kind' => 'LIST',
'name' => NULL,
'ofType' =>
array (
'kind' => 'NON_NULL',
'name' => NULL,
'ofType' =>
array (
'kind' => 'ENUM',
'name' => '__DirectiveLocation',
),
),
),
),
'isDeprecated' => false,
'deprecationReason' => NULL,
),
3 =>
array ( array (
'name' => 'args', 'name' => 'args',
'args' => 'args' =>
@ -845,7 +882,7 @@ class IntrospectionTest extends \PHPUnit_Framework_TestCase
'isDeprecated' => false, 'isDeprecated' => false,
'deprecationReason' => NULL, 'deprecationReason' => NULL,
), ),
3 => 4 =>
array ( array (
'name' => 'onOperation', 'name' => 'onOperation',
'args' => 'args' =>
@ -862,10 +899,10 @@ class IntrospectionTest extends \PHPUnit_Framework_TestCase
'ofType' => NULL, 'ofType' => NULL,
), ),
), ),
'isDeprecated' => false, 'isDeprecated' => true,
'deprecationReason' => NULL, 'deprecationReason' => 'Use `locations`.',
), ),
4 => 5 =>
array ( array (
'name' => 'onFragment', 'name' => 'onFragment',
'args' => 'args' =>
@ -882,10 +919,10 @@ class IntrospectionTest extends \PHPUnit_Framework_TestCase
'ofType' => NULL, 'ofType' => NULL,
), ),
), ),
'isDeprecated' => false, 'isDeprecated' => true,
'deprecationReason' => NULL, 'deprecationReason' => 'Use `locations`.',
), ),
5 => 6 =>
array ( array (
'name' => 'onField', 'name' => 'onField',
'args' => 'args' =>
@ -902,8 +939,8 @@ class IntrospectionTest extends \PHPUnit_Framework_TestCase
'ofType' => NULL, 'ofType' => NULL,
), ),
), ),
'isDeprecated' => false, 'isDeprecated' => true,
'deprecationReason' => NULL, 'deprecationReason' => 'Use `locations`.',
), ),
), ),
'inputFields' => NULL, 'inputFields' => NULL,
@ -913,39 +950,99 @@ class IntrospectionTest extends \PHPUnit_Framework_TestCase
'enumValues' => NULL, 'enumValues' => NULL,
'possibleTypes' => NULL, 'possibleTypes' => NULL,
), ),
10 => [ 10 =>
array (
'kind' => 'ENUM',
'name' => '__DirectiveLocation',
'fields' => NULL,
'inputFields' => NULL,
'interfaces' => NULL,
'enumValues' =>
array (
0 =>
array (
'name' => 'QUERY',
'isDeprecated' => false,
'deprecationReason' => null
),
1 =>
array (
'name' => 'MUTATION',
'isDeprecated' => false,
'deprecationReason' => null
),
2 =>
array (
'name' => 'SUBSCRIPTION',
'isDeprecated' => false,
'deprecationReason' => null
),
3 =>
array (
'name' => 'FIELD',
'isDeprecated' => false,
'deprecationReason' => null
),
4 =>
array (
'name' => 'FRAGMENT_DEFINITION',
'isDeprecated' => false,
'deprecationReason' => null
),
5 =>
array (
'name' => 'FRAGMENT_SPREAD',
'isDeprecated' => false,
'deprecationReason' => null
),
6 =>
array (
'name' => 'INLINE_FRAGMENT',
'isDeprecated' => false,
'deprecationReason' => null
),
),
'possibleTypes' => NULL,
),
11 => array (
'kind' => 'SCALAR', 'kind' => 'SCALAR',
'name' => 'ID', 'name' => 'ID',
'fields' => null, 'fields' => NULL,
'inputFields' => null, 'inputFields' => NULL,
'interfaces' => null, 'interfaces' => NULL,
'enumValues' => null, 'enumValues' => NULL,
'possibleTypes' => null 'possibleTypes' => NULL,
], ),
11 => [ 12 => array (
'kind' => 'SCALAR', 'kind' => 'SCALAR',
'name' => 'Float', 'name' => 'Float',
'fields' => null, 'fields' => NULL,
'inputFields' => null, 'inputFields' => NULL,
'interfaces' => null, 'interfaces' => NULL,
'enumValues' => null, 'enumValues' => NULL,
'possibleTypes' => null 'possibleTypes' => NULL,
], ),
12 => [ 13 => array (
'kind' => 'SCALAR', 'kind' => 'SCALAR',
'name' => 'Int', 'name' => 'Int',
'fields' => null, 'fields' => NULL,
'inputFields' => null, 'inputFields' => NULL,
'interfaces' => null, 'interfaces' => NULL,
'enumValues' => null, 'enumValues' => NULL,
'possibleTypes' => null 'possibleTypes' => NULL,
], )
), ),
'directives' => 'directives' =>
array ( array (
0 => 0 =>
array ( array (
'name' => 'include', 'name' => 'include',
'locations' =>
array (
0 => 'FIELD',
1 => 'FRAGMENT_SPREAD',
2 => 'INLINE_FRAGMENT',
),
'args' => 'args' =>
array ( array (
0 => 0 =>
@ -965,13 +1062,16 @@ class IntrospectionTest extends \PHPUnit_Framework_TestCase
), ),
), ),
), ),
'onOperation' => false,
'onFragment' => true,
'onField' => true,
), ),
1 => 1 =>
array ( array (
'name' => 'skip', 'name' => 'skip',
'locations' =>
array (
0 => 'FIELD',
1 => 'FRAGMENT_SPREAD',
2 => 'INLINE_FRAGMENT',
),
'args' => 'args' =>
array ( array (
0 => 0 =>
@ -991,20 +1091,23 @@ class IntrospectionTest extends \PHPUnit_Framework_TestCase
), ),
), ),
), ),
'onOperation' => false,
'onFragment' => true,
'onField' => true,
), ),
), ),
), ),
), )
); );
$actual = GraphQL::execute($emptySchema, $request); $actual = GraphQL::execute($emptySchema, $request);
// print_r($actual);
// exit;
$this->assertEquals($expected, $actual); $this->assertEquals($expected, $actual);
} }
/**
* @it introspects on input object
*/
function testIntrospectsOnInputObject() function testIntrospectsOnInputObject()
{ {
$TestInputObject = new InputObjectType([ $TestInputObject = new InputObjectType([
@ -1028,7 +1131,7 @@ class IntrospectionTest extends \PHPUnit_Framework_TestCase
] ]
]); ]);
$schema = new Schema($TestType); $schema = new Schema(['query' => $TestType]);
$request = ' $request = '
{ {
__schema { __schema {
@ -1088,6 +1191,9 @@ class IntrospectionTest extends \PHPUnit_Framework_TestCase
$this->assertContains($expectedFragment, $result); $this->assertContains($expectedFragment, $result);
} }
/**
* @it supports the __type root field
*/
public function testSupportsThe__typeRootField() public function testSupportsThe__typeRootField()
{ {
@ -1100,7 +1206,7 @@ class IntrospectionTest extends \PHPUnit_Framework_TestCase
] ]
]); ]);
$schema = new Schema($TestType); $schema = new Schema(['query' => $TestType]);
$request = ' $request = '
{ {
__type(name: "TestType") { __type(name: "TestType") {
@ -1118,6 +1224,9 @@ class IntrospectionTest extends \PHPUnit_Framework_TestCase
$this->assertEquals($expected, GraphQL::execute($schema, $request)); $this->assertEquals($expected, GraphQL::execute($schema, $request));
} }
/**
* @it identifies deprecated fields
*/
public function testIdentifiesDeprecatedFields() public function testIdentifiesDeprecatedFields()
{ {
@ -1134,7 +1243,7 @@ class IntrospectionTest extends \PHPUnit_Framework_TestCase
] ]
]); ]);
$schema = new Schema($TestType); $schema = new Schema(['query' => $TestType]);
$request = ' $request = '
{ {
__type(name: "TestType") { __type(name: "TestType") {
@ -1170,6 +1279,9 @@ class IntrospectionTest extends \PHPUnit_Framework_TestCase
$this->assertEquals($expected, GraphQL::execute($schema, $request)); $this->assertEquals($expected, GraphQL::execute($schema, $request));
} }
/**
* @it respects the includeDeprecated parameter for fields
*/
public function testRespectsTheIncludeDeprecatedParameterForFields() public function testRespectsTheIncludeDeprecatedParameterForFields()
{ {
$TestType = new ObjectType([ $TestType = new ObjectType([
@ -1185,7 +1297,7 @@ class IntrospectionTest extends \PHPUnit_Framework_TestCase
] ]
]); ]);
$schema = new Schema($TestType); $schema = new Schema(['query' => $TestType]);
$request = ' $request = '
{ {
__type(name: "TestType") { __type(name: "TestType") {
@ -1232,6 +1344,9 @@ class IntrospectionTest extends \PHPUnit_Framework_TestCase
$this->assertEquals($expected, GraphQL::execute($schema, $request)); $this->assertEquals($expected, GraphQL::execute($schema, $request));
} }
/**
* @it identifies deprecated enum values
*/
public function testIdentifiesDeprecatedEnumValues() public function testIdentifiesDeprecatedEnumValues()
{ {
$TestEnum = new EnumType([ $TestEnum = new EnumType([
@ -1252,7 +1367,7 @@ class IntrospectionTest extends \PHPUnit_Framework_TestCase
] ]
]); ]);
$schema = new Schema($TestType); $schema = new Schema(['query' => $TestType]);
$request = ' $request = '
{ {
__type(name: "TestEnum") { __type(name: "TestEnum") {
@ -1293,6 +1408,9 @@ class IntrospectionTest extends \PHPUnit_Framework_TestCase
$this->assertEquals($expected, GraphQL::execute($schema, $request)); $this->assertEquals($expected, GraphQL::execute($schema, $request));
} }
/**
* @it respects the includeDeprecated parameter for enum values
*/
public function testRespectsTheIncludeDeprecatedParameterForEnumValues() public function testRespectsTheIncludeDeprecatedParameterForEnumValues()
{ {
$TestEnum = new EnumType([ $TestEnum = new EnumType([
@ -1313,7 +1431,7 @@ class IntrospectionTest extends \PHPUnit_Framework_TestCase
] ]
]); ]);
$schema = new Schema($TestType); $schema = new Schema(['query' => $TestType]);
$request = ' $request = '
{ {
__type(name: "TestEnum") { __type(name: "TestEnum") {
@ -1353,9 +1471,11 @@ class IntrospectionTest extends \PHPUnit_Framework_TestCase
$this->assertEquals($expected, GraphQL::execute($schema, $request)); $this->assertEquals($expected, GraphQL::execute($schema, $request));
} }
/**
* @it fails as expected on the __type root field without an arg
*/
public function testFailsAsExpectedOnThe__typeRootFieldWithoutAnArg() public function testFailsAsExpectedOnThe__typeRootFieldWithoutAnArg()
{ {
$TestType = new ObjectType([ $TestType = new ObjectType([
'name' => 'TestType', 'name' => 'TestType',
'fields' => [ 'fields' => [
@ -1365,7 +1485,7 @@ class IntrospectionTest extends \PHPUnit_Framework_TestCase
] ]
]); ]);
$schema = new Schema($TestType); $schema = new Schema(['query' => $TestType]);
$request = ' $request = '
{ {
__type { __type {
@ -1384,6 +1504,9 @@ class IntrospectionTest extends \PHPUnit_Framework_TestCase
$this->assertEquals($expected, GraphQL::execute($schema, $request)); $this->assertEquals($expected, GraphQL::execute($schema, $request));
} }
/**
* @it exposes descriptions on types and fields
*/
public function testExposesDescriptionsOnTypesAndFields() public function testExposesDescriptionsOnTypesAndFields()
{ {
$QueryRoot = new ObjectType([ $QueryRoot = new ObjectType([
@ -1391,7 +1514,7 @@ class IntrospectionTest extends \PHPUnit_Framework_TestCase
'fields' => [] 'fields' => []
]); ]);
$schema = new Schema($QueryRoot); $schema = new Schema(['query' => $QueryRoot]);
$request = ' $request = '
{ {
schemaType: __type(name: "__Schema") { schemaType: __type(name: "__Schema") {
@ -1441,6 +1564,9 @@ class IntrospectionTest extends \PHPUnit_Framework_TestCase
$this->assertEquals($expected, GraphQL::execute($schema, $request)); $this->assertEquals($expected, GraphQL::execute($schema, $request));
} }
/**
* @it exposes descriptions on enums
*/
public function testExposesDescriptionsOnEnums() public function testExposesDescriptionsOnEnums()
{ {
$QueryRoot = new ObjectType([ $QueryRoot = new ObjectType([
@ -1448,7 +1574,7 @@ class IntrospectionTest extends \PHPUnit_Framework_TestCase
'fields' => [] 'fields' => []
]); ]);
$schema = new Schema($QueryRoot); $schema = new Schema(['query' => $QueryRoot]);
$request = ' $request = '
{ {
typeKindType: __type(name: "__TypeKind") { typeKindType: __type(name: "__TypeKind") {
@ -1465,7 +1591,7 @@ class IntrospectionTest extends \PHPUnit_Framework_TestCase
'data' => [ 'data' => [
'typeKindType' => [ 'typeKindType' => [
'name' => '__TypeKind', 'name' => '__TypeKind',
'description' => 'An enum describing what kind of type a given __Type is', 'description' => 'An enum describing what kind of type a given __Type is.',
'enumValues' => [ 'enumValues' => [
[ [
'description' => 'Indicates this type is a scalar.', 'description' => 'Indicates this type is a scalar.',