public function testPassesOnTheIntrospectionSchema() { $schema = new Schema(Introspection::_schema()); $validationResult = SchemaValidator::validate($schema); $this->assertSame(true, $validationResult->isValid); $this->assertSame(null, $validationResult->errors); }
function testExecutesAnIntrospectionQuery() { $emptySchema = new Schema(new ObjectType(['name' => 'QueryRoot', 'fields' => []])); $request = Introspection::getIntrospectionQuery(false); $expected = array('data' => array('__schema' => array('mutationType' => NULL, 'queryType' => array('name' => 'QueryRoot'), 'types' => array(0 => array('kind' => 'OBJECT', 'name' => 'QueryRoot', 'inputFields' => NULL, 'interfaces' => array(), 'enumValues' => NULL, 'possibleTypes' => NULL, 'fields' => []), 1 => array('kind' => 'OBJECT', 'name' => '__Schema', 'fields' => array(0 => array('name' => 'types', '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' => 'OBJECT', 'name' => '__Type')))), 'isDeprecated' => false, 'deprecationReason' => NULL), 1 => array('name' => 'queryType', 'args' => array(), 'type' => array('kind' => 'NON_NULL', 'name' => NULL, 'ofType' => array('kind' => 'OBJECT', 'name' => '__Type', 'ofType' => NULL)), 'isDeprecated' => false, 'deprecationReason' => NULL), 2 => array('name' => 'mutationType', 'args' => array(), 'type' => array('kind' => 'OBJECT', 'name' => '__Type', 'ofType' => NULL), 'isDeprecated' => false, 'deprecationReason' => NULL), 3 => array('name' => 'subscriptionType', 'args' => array(), 'type' => array('kind' => 'OBJECT', 'name' => '__Type', 'ofType' => NULL), 'isDeprecated' => false, 'deprecationReason' => NULL), 4 => array('name' => 'directives', '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' => 'OBJECT', 'name' => '__Directive')))), 'isDeprecated' => false, 'deprecationReason' => NULL)), 'inputFields' => NULL, 'interfaces' => array(), 'enumValues' => NULL, 'possibleTypes' => NULL), 2 => array('kind' => 'OBJECT', 'name' => '__Type', 'fields' => array(0 => array('name' => 'kind', 'args' => array(), 'type' => array('kind' => 'NON_NULL', 'name' => NULL, 'ofType' => array('kind' => 'ENUM', 'name' => '__TypeKind', 'ofType' => NULL)), 'isDeprecated' => false, 'deprecationReason' => NULL), 1 => array('name' => 'name', 'args' => array(), 'type' => array('kind' => 'SCALAR', 'name' => 'String', 'ofType' => NULL), 'isDeprecated' => false, 'deprecationReason' => NULL), 2 => array('name' => 'description', 'args' => array(), 'type' => array('kind' => 'SCALAR', 'name' => 'String', 'ofType' => NULL), 'isDeprecated' => false, 'deprecationReason' => NULL), 3 => array('name' => 'fields', 'args' => array(0 => array('name' => 'includeDeprecated', 'type' => array('kind' => 'SCALAR', 'name' => 'Boolean', 'ofType' => NULL), 'defaultValue' => 'false')), 'type' => array('kind' => 'LIST', 'name' => NULL, 'ofType' => array('kind' => 'NON_NULL', 'name' => NULL, 'ofType' => array('kind' => 'OBJECT', 'name' => '__Field', 'ofType' => NULL))), 'isDeprecated' => false, 'deprecationReason' => NULL), 4 => array('name' => 'interfaces', 'args' => array(), 'type' => array('kind' => 'LIST', 'name' => NULL, 'ofType' => array('kind' => 'NON_NULL', 'name' => NULL, 'ofType' => array('kind' => 'OBJECT', 'name' => '__Type', 'ofType' => NULL))), 'isDeprecated' => false, 'deprecationReason' => NULL), 5 => array('name' => 'possibleTypes', 'args' => array(), 'type' => array('kind' => 'LIST', 'name' => NULL, 'ofType' => array('kind' => 'NON_NULL', 'name' => NULL, 'ofType' => array('kind' => 'OBJECT', 'name' => '__Type', 'ofType' => NULL))), 'isDeprecated' => false, 'deprecationReason' => NULL), 6 => array('name' => 'enumValues', 'args' => array(0 => array('name' => 'includeDeprecated', 'type' => array('kind' => 'SCALAR', 'name' => 'Boolean', 'ofType' => NULL), 'defaultValue' => 'false')), 'type' => array('kind' => 'LIST', 'name' => NULL, 'ofType' => array('kind' => 'NON_NULL', 'name' => NULL, 'ofType' => array('kind' => 'OBJECT', 'name' => '__EnumValue', 'ofType' => NULL))), 'isDeprecated' => false, 'deprecationReason' => NULL), 7 => array('name' => 'inputFields', 'args' => array(), 'type' => array('kind' => 'LIST', 'name' => NULL, 'ofType' => array('kind' => 'NON_NULL', 'name' => NULL, 'ofType' => array('kind' => 'OBJECT', 'name' => '__InputValue', 'ofType' => NULL))), 'isDeprecated' => false, 'deprecationReason' => NULL), 8 => array('name' => 'ofType', 'args' => array(), 'type' => array('kind' => 'OBJECT', 'name' => '__Type', 'ofType' => NULL), 'isDeprecated' => false, 'deprecationReason' => NULL)), 'inputFields' => NULL, 'interfaces' => array(), 'enumValues' => NULL, 'possibleTypes' => NULL), 3 => array('kind' => 'ENUM', 'name' => '__TypeKind', 'fields' => NULL, 'inputFields' => NULL, 'interfaces' => NULL, 'enumValues' => array(0 => array('name' => 'SCALAR', 'isDeprecated' => false, 'deprecationReason' => NULL), 1 => array('name' => 'OBJECT', 'isDeprecated' => false, 'deprecationReason' => NULL), 2 => array('name' => 'INTERFACE', 'isDeprecated' => false, 'deprecationReason' => NULL), 3 => array('name' => 'UNION', 'isDeprecated' => false, 'deprecationReason' => NULL), 4 => array('name' => 'ENUM', 'isDeprecated' => false, 'deprecationReason' => NULL), 5 => array('name' => 'INPUT_OBJECT', 'isDeprecated' => false, 'deprecationReason' => NULL), 6 => array('name' => 'LIST', 'isDeprecated' => false, 'deprecationReason' => NULL), 7 => array('name' => 'NON_NULL', 'isDeprecated' => false, 'deprecationReason' => NULL)), 'possibleTypes' => NULL), 4 => array('kind' => 'SCALAR', 'name' => 'String', 'fields' => NULL, 'inputFields' => NULL, 'interfaces' => NULL, 'enumValues' => NULL, 'possibleTypes' => NULL), 5 => array('kind' => 'SCALAR', 'name' => 'Boolean', 'fields' => NULL, 'inputFields' => NULL, 'interfaces' => NULL, 'enumValues' => NULL, 'possibleTypes' => NULL), 6 => array('kind' => 'OBJECT', 'name' => '__Field', 'fields' => array(0 => array('name' => 'name', 'args' => array(), 'type' => array('kind' => 'NON_NULL', 'name' => NULL, 'ofType' => array('kind' => 'SCALAR', 'name' => 'String', 'ofType' => NULL)), 'isDeprecated' => false, 'deprecationReason' => NULL), 1 => array('name' => 'description', 'args' => array(), 'type' => array('kind' => 'SCALAR', 'name' => 'String', 'ofType' => NULL), 'isDeprecated' => false, 'deprecationReason' => NULL), 2 => array('name' => 'args', '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' => 'OBJECT', 'name' => '__InputValue')))), 'isDeprecated' => false, 'deprecationReason' => NULL), 3 => array('name' => 'type', 'args' => array(), 'type' => array('kind' => 'NON_NULL', 'name' => NULL, 'ofType' => array('kind' => 'OBJECT', 'name' => '__Type', 'ofType' => NULL)), 'isDeprecated' => false, 'deprecationReason' => NULL), 4 => array('name' => 'isDeprecated', 'args' => array(), 'type' => array('kind' => 'NON_NULL', 'name' => NULL, 'ofType' => array('kind' => 'SCALAR', 'name' => 'Boolean', 'ofType' => NULL)), 'isDeprecated' => false, 'deprecationReason' => NULL), 5 => array('name' => 'deprecationReason', 'args' => array(), 'type' => array('kind' => 'SCALAR', 'name' => 'String', 'ofType' => NULL), 'isDeprecated' => false, 'deprecationReason' => NULL)), 'inputFields' => NULL, 'interfaces' => array(), 'enumValues' => NULL, 'possibleTypes' => NULL), 7 => array('kind' => 'OBJECT', 'name' => '__InputValue', 'fields' => array(0 => array('name' => 'name', 'args' => array(), 'type' => array('kind' => 'NON_NULL', 'name' => NULL, 'ofType' => array('kind' => 'SCALAR', 'name' => 'String', 'ofType' => NULL)), 'isDeprecated' => false, 'deprecationReason' => NULL), 1 => array('name' => 'description', 'args' => array(), 'type' => array('kind' => 'SCALAR', 'name' => 'String', 'ofType' => NULL), 'isDeprecated' => false, 'deprecationReason' => NULL), 2 => array('name' => 'type', 'args' => array(), 'type' => array('kind' => 'NON_NULL', 'name' => NULL, 'ofType' => array('kind' => 'OBJECT', 'name' => '__Type', 'ofType' => NULL)), 'isDeprecated' => false, 'deprecationReason' => NULL), 3 => array('name' => 'defaultValue', 'args' => array(), 'type' => array('kind' => 'SCALAR', 'name' => 'String', 'ofType' => NULL), 'isDeprecated' => false, 'deprecationReason' => NULL)), 'inputFields' => NULL, 'interfaces' => array(), 'enumValues' => NULL, 'possibleTypes' => NULL), 8 => array('kind' => 'OBJECT', 'name' => '__EnumValue', 'fields' => array(0 => array('name' => 'name', 'args' => array(), 'type' => array('kind' => 'NON_NULL', 'name' => NULL, 'ofType' => array('kind' => 'SCALAR', 'name' => 'String', 'ofType' => NULL)), 'isDeprecated' => false, 'deprecationReason' => NULL), 1 => array('name' => 'description', 'args' => array(), 'type' => array('kind' => 'SCALAR', 'name' => 'String', 'ofType' => NULL), 'isDeprecated' => false, 'deprecationReason' => NULL), 2 => array('name' => 'isDeprecated', 'args' => array(), 'type' => array('kind' => 'NON_NULL', 'name' => NULL, 'ofType' => array('kind' => 'SCALAR', 'name' => 'Boolean', 'ofType' => NULL)), 'isDeprecated' => false, 'deprecationReason' => NULL), 3 => array('name' => 'deprecationReason', 'args' => array(), 'type' => array('kind' => 'SCALAR', 'name' => 'String', 'ofType' => NULL), 'isDeprecated' => false, 'deprecationReason' => NULL)), 'inputFields' => NULL, 'interfaces' => array(), 'enumValues' => NULL, 'possibleTypes' => NULL), 9 => array('kind' => 'OBJECT', 'name' => '__Directive', 'fields' => array(0 => array('name' => 'name', 'args' => array(), 'type' => array('kind' => 'NON_NULL', 'name' => NULL, 'ofType' => array('kind' => 'SCALAR', 'name' => 'String', 'ofType' => NULL)), 'isDeprecated' => false, 'deprecationReason' => NULL), 1 => array('name' => 'description', 'args' => array(), 'type' => array('kind' => 'SCALAR', 'name' => 'String', 'ofType' => NULL), 'isDeprecated' => false, 'deprecationReason' => NULL), 2 => array('name' => 'args', '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' => 'OBJECT', 'name' => '__InputValue')))), 'isDeprecated' => false, 'deprecationReason' => NULL), 3 => array('name' => 'onOperation', 'args' => array(), 'type' => array('kind' => 'NON_NULL', 'name' => NULL, 'ofType' => array('kind' => 'SCALAR', 'name' => 'Boolean', 'ofType' => NULL)), 'isDeprecated' => false, 'deprecationReason' => NULL), 4 => array('name' => 'onFragment', 'args' => array(), 'type' => array('kind' => 'NON_NULL', 'name' => NULL, 'ofType' => array('kind' => 'SCALAR', 'name' => 'Boolean', 'ofType' => NULL)), 'isDeprecated' => false, 'deprecationReason' => NULL), 5 => array('name' => 'onField', 'args' => array(), 'type' => array('kind' => 'NON_NULL', 'name' => NULL, 'ofType' => array('kind' => 'SCALAR', 'name' => 'Boolean', 'ofType' => NULL)), 'isDeprecated' => false, 'deprecationReason' => NULL)), 'inputFields' => NULL, 'interfaces' => array(), 'enumValues' => NULL, 'possibleTypes' => NULL), 10 => ['kind' => 'SCALAR', 'name' => 'ID', 'fields' => null, 'inputFields' => null, 'interfaces' => null, 'enumValues' => null, 'possibleTypes' => null], 11 => ['kind' => 'SCALAR', 'name' => 'Float', 'fields' => null, 'inputFields' => null, 'interfaces' => null, 'enumValues' => null, 'possibleTypes' => null], 12 => ['kind' => 'SCALAR', 'name' => 'Int', 'fields' => null, 'inputFields' => null, 'interfaces' => null, 'enumValues' => null, 'possibleTypes' => null]), 'directives' => array(0 => array('name' => 'include', 'args' => array(0 => array('defaultValue' => NULL, 'name' => 'if', 'type' => array('kind' => 'NON_NULL', 'name' => NULL, 'ofType' => array('kind' => 'SCALAR', 'name' => 'Boolean', 'ofType' => NULL)))), 'onOperation' => false, 'onFragment' => true, 'onField' => true), 1 => array('name' => 'skip', 'args' => array(0 => array('defaultValue' => NULL, 'name' => 'if', 'type' => array('kind' => 'NON_NULL', 'name' => NULL, 'ofType' => array('kind' => 'SCALAR', 'name' => 'Boolean', 'ofType' => NULL)))), 'onOperation' => false, 'onFragment' => true, 'onField' => true))))); $actual = GraphQL::execute($emptySchema, $request); $this->assertEquals($expected, $actual); }
public function getTypeMap() { if (null === $this->_typeMap) { $map = []; foreach ([$this->getQueryType(), $this->getMutationType(), Introspection::_schema()] as $type) { $this->_extractTypes($type, $map); } $this->_typeMap = $map + Type::getInternalTypes(); } return $this->_typeMap; }
public function __construct(Type $querySchema = null, Type $mutationSchema = null) { Utils::invariant($querySchema || $mutationSchema, "Either query or mutation type must be set"); $this->querySchema = $querySchema; $this->mutationSchema = $mutationSchema; // Build type map now to detect any errors within this schema. $map = []; foreach ([$this->getQueryType(), $this->getMutationType(), Introspection::_schema()] as $type) { $this->_extractTypes($type, $map); } $this->_typeMap = $map + Type::getInternalTypes(); // Enforce correct interface implementations foreach ($this->_typeMap as $typeName => $type) { if ($type instanceof ObjectType) { foreach ($type->getInterfaces() as $iface) { $this->assertObjectImplementsInterface($type, $iface); } } } }
/** * Not exactly the same as the executor's definition of getFieldDef, in this * statically evaluated environment we do not always have an Object type, * and need to handle Interface and Union types. * * @return FieldDefinition */ private static function _getFieldDef(Schema $schema, Type $parentType, Field $fieldAST) { $name = $fieldAST->name->value; $schemaMeta = Introspection::schemaMetaFieldDef(); if ($name === $schemaMeta->name && $schema->getQueryType() === $parentType) { return $schemaMeta; } $typeMeta = Introspection::typeMetaFieldDef(); if ($name === $typeMeta->name && $schema->getQueryType() === $parentType) { return $typeMeta; } $typeNameMeta = Introspection::typeNameMetaFieldDef(); if ($name === $typeNameMeta->name && ($parentType instanceof ObjectType || $parentType instanceof InterfaceType || $parentType instanceof UnionType)) { return $typeNameMeta; } if ($parentType instanceof ObjectType || $parentType instanceof InterfaceType) { $fields = $parentType->getFields(); return isset($fields[$name]) ? $fields[$name] : null; } return null; }
/** * This method looks up the field on the given type defintion. * It has special casing for the two introspection fields, __schema * and __typename. __typename is special because it can always be * queried as a field, even in situations where no other fields * are allowed, like on a Union. __schema could get automatically * added to the query type, but that would require mutating type * definitions, which would cause issues. * * @param Schema $schema * @param ObjectType $parentType * @param $fieldName * * @return FieldDefinition */ private static function getFieldDef(Schema $schema, ObjectType $parentType, $fieldName) { static $schemaMetaFieldDef, $typeMetaFieldDef, $typeNameMetaFieldDef; $schemaMetaFieldDef = $schemaMetaFieldDef ?: Introspection::schemaMetaFieldDef(); $typeMetaFieldDef = $typeMetaFieldDef ?: Introspection::typeMetaFieldDef(); $typeNameMetaFieldDef = $typeNameMetaFieldDef ?: Introspection::typeNameMetaFieldDef(); if ($fieldName === $schemaMetaFieldDef->name && $schema->getQueryType() === $parentType) { return $schemaMetaFieldDef; } else { if ($fieldName === $typeMetaFieldDef->name && $schema->getQueryType() === $parentType) { return $typeMetaFieldDef; } else { if ($fieldName === $typeNameMetaFieldDef->name) { return $typeNameMetaFieldDef; } } } $tmp = $parentType->getFields(); return isset($tmp[$fieldName]) ? $tmp[$fieldName] : null; }
/** * @it can be introspected without error */ public function testCanBeIntrospectedWithoutError() { $result = GraphQL::execute($this->schema, Introspection::getIntrospectionQuery()); $this->assertArrayNotHasKey('errors', $result); }
/** * Given a selectionSet, adds all of the fields in that selection to * the passed in map of fields, and returns it at the end. * * Note: This is not the same as execution's collectFields because at static * time we do not know what object type will be used, so we unconditionally * spread in all fragments. * * @see GraphQL\Validator\Rules\OverlappingFieldsCanBeMerged * * @param ValidationContext $context * @param Type|null $parentType * @param SelectionSetNode $selectionSet * @param \ArrayObject $visitedFragmentNames * @param \ArrayObject $astAndDefs * * @return \ArrayObject */ protected function collectFieldASTsAndDefs(ValidationContext $context, $parentType, SelectionSetNode $selectionSet, \ArrayObject $visitedFragmentNames = null, \ArrayObject $astAndDefs = null) { $_visitedFragmentNames = $visitedFragmentNames ?: new \ArrayObject(); $_astAndDefs = $astAndDefs ?: new \ArrayObject(); foreach ($selectionSet->selections as $selection) { switch ($selection->kind) { case NodeKind::FIELD: /* @var FieldNode $selection */ $fieldName = $selection->name->value; $fieldDef = null; if ($parentType && method_exists($parentType, 'getFields')) { $tmp = $parentType->getFields(); $schemaMetaFieldDef = Introspection::schemaMetaFieldDef(); $typeMetaFieldDef = Introspection::typeMetaFieldDef(); $typeNameMetaFieldDef = Introspection::typeNameMetaFieldDef(); if ($fieldName === $schemaMetaFieldDef->name && $context->getSchema()->getQueryType() === $parentType) { $fieldDef = $schemaMetaFieldDef; } elseif ($fieldName === $typeMetaFieldDef->name && $context->getSchema()->getQueryType() === $parentType) { $fieldDef = $typeMetaFieldDef; } elseif ($fieldName === $typeNameMetaFieldDef->name) { $fieldDef = $typeNameMetaFieldDef; } elseif (isset($tmp[$fieldName])) { $fieldDef = $tmp[$fieldName]; } } $responseName = $this->getFieldName($selection); if (!isset($_astAndDefs[$responseName])) { $_astAndDefs[$responseName] = new \ArrayObject(); } // create field context $_astAndDefs[$responseName][] = [$selection, $fieldDef]; break; case NodeKind::INLINE_FRAGMENT: /* @var InlineFragmentNode $selection */ $_astAndDefs = $this->collectFieldASTsAndDefs($context, TypeInfo::typeFromAST($context->getSchema(), $selection->typeCondition), $selection->selectionSet, $_visitedFragmentNames, $_astAndDefs); break; case NodeKind::FRAGMENT_SPREAD: /* @var FragmentSpreadNode $selection */ $fragName = $selection->name->value; if (empty($_visitedFragmentNames[$fragName])) { $_visitedFragmentNames[$fragName] = true; $fragment = $context->getFragment($fragName); if ($fragment) { $_astAndDefs = $this->collectFieldASTsAndDefs($context, TypeInfo::typeFromAST($context->getSchema(), $fragment->typeCondition), $fragment->selectionSet, $_visitedFragmentNames, $_astAndDefs); } } break; } } return $_astAndDefs; }
/** * @param array $config */ protected function init(array $config) { $config += ['query' => null, 'mutation' => null, 'subscription' => null, 'types' => [], 'directives' => [], 'validate' => true]; if ($config['query'] instanceof DefinitionContainer) { $config['query'] = $config['query']->getDefinition(); } if ($config['mutation'] instanceof DefinitionContainer) { $config['mutation'] = $config['mutation']->getDefinition(); } if ($config['subscription'] instanceof DefinitionContainer) { $config['subscription'] = $config['subscription']->getDefinition(); } Utils::invariant($config['query'] instanceof ObjectType, "Schema query must be Object Type but got: " . Utils::getVariableType($config['query'])); $this->queryType = $config['query']; Utils::invariant(!$config['mutation'] || $config['mutation'] instanceof ObjectType, "Schema mutation must be Object Type if provided but got: " . Utils::getVariableType($config['mutation'])); $this->mutationType = $config['mutation']; Utils::invariant(!$config['subscription'] || $config['subscription'] instanceof ObjectType, "Schema subscription must be Object Type if provided but got: " . Utils::getVariableType($config['subscription'])); $this->subscriptionType = $config['subscription']; Utils::invariant(!$config['types'] || is_array($config['types']), "Schema types must be Array if provided but got: " . Utils::getVariableType($config['types'])); Utils::invariant(!$config['directives'] || is_array($config['directives']) && Utils::every($config['directives'], function ($d) { return $d instanceof Directive; }), "Schema directives must be Directive[] if provided but got " . Utils::getVariableType($config['directives'])); $this->directives = $config['directives'] ?: GraphQL::getInternalDirectives(); // Build type map now to detect any errors within this schema. $initialTypes = [$config['query'], $config['mutation'], $config['subscription'], Introspection::_schema()]; if (!empty($config['types'])) { $initialTypes = array_merge($initialTypes, $config['types']); } foreach ($initialTypes as $type) { $this->extractTypes($type); } $this->typeMap += Type::getInternalTypes(); // Keep track of all implementations by interface name. $this->implementations = []; foreach ($this->typeMap as $typeName => $type) { if ($type instanceof ObjectType) { foreach ($type->getInterfaces() as $iface) { $this->implementations[$iface->name][] = $type; } } } }
public function testPassesOnTheIntrospectionSchema() { $schema = new Schema(Introspection::_schema()); $errors = SchemaValidator::validate($schema); $this->assertEmpty($errors); }
public function testPassesOnTheIntrospectionSchema() { $this->expectPasses(['query' => Introspection::_schema()]); }
public function setUp() { $this->introQuery = new Source(Introspection::getIntrospectionQuery()); }
protected function assertIntrospectionQuery($maxExpected) { $query = Introspection::getIntrospectionQuery(true); $this->assertMaxValue($query, $maxExpected); }