mirror of
https://github.com/retailcrm/graphql-php.git
synced 2024-11-25 14:26:08 +03:00
Fix CS in Utils
This commit is contained in:
parent
a3ef1be1ab
commit
49f34d3243
@ -3,7 +3,7 @@ build:
|
|||||||
analysis:
|
analysis:
|
||||||
environment:
|
environment:
|
||||||
php:
|
php:
|
||||||
version: 5.6
|
version: 7.1
|
||||||
cache:
|
cache:
|
||||||
disabled: false
|
disabled: false
|
||||||
directories:
|
directories:
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
- Spec compliance: error extensions are displayed under `extensions` key
|
- Spec compliance: error extensions are displayed under `extensions` key
|
||||||
- `AbstractValidationRule` renamed to `ValidationRule` (NS `GraphQL\Validator\Rules`)
|
- `AbstractValidationRule` renamed to `ValidationRule` (NS `GraphQL\Validator\Rules`)
|
||||||
- `AbstractQuerySecurity` renamed to `QuerySecurityRule` (NS `GraphQL\Validator\Rules`)
|
- `AbstractQuerySecurity` renamed to `QuerySecurityRule` (NS `GraphQL\Validator\Rules`)
|
||||||
|
- `FindBreakingChanges` renamed to `BreakingChangesFinder` (NS `GraphQL\Utils`)
|
||||||
|
|
||||||
#### v0.12.5
|
#### v0.12.5
|
||||||
- Execution performance optimization for lists
|
- Execution performance optimization for lists
|
||||||
|
@ -81,9 +81,8 @@ class AST
|
|||||||
*
|
*
|
||||||
* @api
|
* @api
|
||||||
* @param mixed[] $node
|
* @param mixed[] $node
|
||||||
* @return Node
|
|
||||||
*/
|
*/
|
||||||
public static function fromArray(array $node)
|
public static function fromArray(array $node) : Node
|
||||||
{
|
{
|
||||||
if (! isset($node['kind']) || ! isset(NodeKind::$classMap[$node['kind']])) {
|
if (! isset($node['kind']) || ! isset(NodeKind::$classMap[$node['kind']])) {
|
||||||
throw new InvariantViolation('Unexpected node structure: ' . Utils::printSafeJson($node));
|
throw new InvariantViolation('Unexpected node structure: ' . Utils::printSafeJson($node));
|
||||||
@ -457,6 +456,19 @@ class AST
|
|||||||
throw new Error('Unknown type: ' . Utils::printSafe($type) . '.');
|
throw new Error('Unknown type: ' . Utils::printSafe($type) . '.');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if the provided valueNode is a variable which is not defined
|
||||||
|
* in the set of variables.
|
||||||
|
* @param ValueNode $valueNode
|
||||||
|
* @param mixed[] $variables
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
private static function isMissingVariable($valueNode, $variables)
|
||||||
|
{
|
||||||
|
return $valueNode instanceof VariableNode &&
|
||||||
|
(count($variables) === 0 || ! array_key_exists($valueNode->name->value, $variables));
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Produces a PHP value given a GraphQL Value AST.
|
* Produces a PHP value given a GraphQL Value AST.
|
||||||
*
|
*
|
||||||
@ -552,19 +564,6 @@ class AST
|
|||||||
throw new Error('Unexpected type kind: ' . $inputTypeNode->kind . '.');
|
throw new Error('Unexpected type kind: ' . $inputTypeNode->kind . '.');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns true if the provided valueNode is a variable which is not defined
|
|
||||||
* in the set of variables.
|
|
||||||
* @param ValueNode $valueNode
|
|
||||||
* @param mixed[] $variables
|
|
||||||
* @return bool
|
|
||||||
*/
|
|
||||||
private static function isMissingVariable($valueNode, $variables)
|
|
||||||
{
|
|
||||||
return $valueNode instanceof VariableNode &&
|
|
||||||
(count($variables) === 0 || ! array_key_exists($valueNode->name->value, $variables));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns operation type ("query", "mutation" or "subscription") given a document and operation name
|
* Returns operation type ("query", "mutation" or "subscription") given a document and operation name
|
||||||
*
|
*
|
||||||
|
@ -75,36 +75,112 @@ class ASTDefinitionBuilder
|
|||||||
$this->cache = Type::getAllBuiltInTypes();
|
$this->cache = Type::getAllBuiltInTypes();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
public function buildDirective(DirectiveDefinitionNode $directiveNode)
|
||||||
* @param TypeNode|ListTypeNode|NonNullTypeNode $inputTypeNode
|
|
||||||
* @return Type
|
|
||||||
*/
|
|
||||||
private function buildWrappedType(Type $innerType, TypeNode $inputTypeNode)
|
|
||||||
{
|
{
|
||||||
if ($inputTypeNode->kind === NodeKind::LIST_TYPE) {
|
return new Directive([
|
||||||
return Type::listOf($this->buildWrappedType($innerType, $inputTypeNode->type));
|
'name' => $directiveNode->name->value,
|
||||||
}
|
'description' => $this->getDescription($directiveNode),
|
||||||
if ($inputTypeNode->kind === NodeKind::NON_NULL_TYPE) {
|
'locations' => Utils::map(
|
||||||
$wrappedType = $this->buildWrappedType($innerType, $inputTypeNode->type);
|
$directiveNode->locations,
|
||||||
|
function ($node) {
|
||||||
return Type::nonNull(NonNull::assertNullableType($wrappedType));
|
return $node->value;
|
||||||
}
|
}
|
||||||
|
),
|
||||||
return $innerType;
|
'args' => $directiveNode->arguments ? FieldArgument::createMap($this->makeInputValues($directiveNode->arguments)) : null,
|
||||||
|
'astNode' => $directiveNode,
|
||||||
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param TypeNode|ListTypeNode|NonNullTypeNode $typeNode
|
* Given an ast node, returns its string description.
|
||||||
* @return TypeNode
|
|
||||||
*/
|
*/
|
||||||
private function getNamedTypeNode(TypeNode $typeNode)
|
private function getDescription($node)
|
||||||
{
|
{
|
||||||
$namedType = $typeNode;
|
if ($node->description) {
|
||||||
while ($namedType->kind === NodeKind::LIST_TYPE || $namedType->kind === NodeKind::NON_NULL_TYPE) {
|
return $node->description->value;
|
||||||
$namedType = $namedType->type;
|
}
|
||||||
|
if (isset($this->options['commentDescriptions'])) {
|
||||||
|
$rawValue = $this->getLeadingCommentBlock($node);
|
||||||
|
if ($rawValue !== null) {
|
||||||
|
return BlockString::value("\n" . $rawValue);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return $namedType;
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function getLeadingCommentBlock($node)
|
||||||
|
{
|
||||||
|
$loc = $node->loc;
|
||||||
|
if (! $loc || ! $loc->startToken) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
$comments = [];
|
||||||
|
$token = $loc->startToken->prev;
|
||||||
|
while ($token &&
|
||||||
|
$token->kind === Token::COMMENT &&
|
||||||
|
$token->next && $token->prev &&
|
||||||
|
$token->line + 1 === $token->next->line &&
|
||||||
|
$token->line !== $token->prev->line
|
||||||
|
) {
|
||||||
|
$value = $token->value;
|
||||||
|
$comments[] = $value;
|
||||||
|
$token = $token->prev;
|
||||||
|
}
|
||||||
|
|
||||||
|
return implode("\n", array_reverse($comments));
|
||||||
|
}
|
||||||
|
|
||||||
|
private function makeInputValues($values)
|
||||||
|
{
|
||||||
|
return Utils::keyValMap(
|
||||||
|
$values,
|
||||||
|
function ($value) {
|
||||||
|
return $value->name->value;
|
||||||
|
},
|
||||||
|
function ($value) {
|
||||||
|
// Note: While this could make assertions to get the correctly typed
|
||||||
|
// value, that would throw immediately while type system validation
|
||||||
|
// with validateSchema() will produce more actionable results.
|
||||||
|
$type = $this->internalBuildWrappedType($value->type);
|
||||||
|
$config = [
|
||||||
|
'name' => $value->name->value,
|
||||||
|
'type' => $type,
|
||||||
|
'description' => $this->getDescription($value),
|
||||||
|
'astNode' => $value,
|
||||||
|
];
|
||||||
|
if (isset($value->defaultValue)) {
|
||||||
|
$config['defaultValue'] = AST::valueFromAST($value->defaultValue, $type);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $config;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return Type|InputType
|
||||||
|
* @throws Error
|
||||||
|
*/
|
||||||
|
private function internalBuildWrappedType(TypeNode $typeNode)
|
||||||
|
{
|
||||||
|
$typeDef = $this->buildType($this->getNamedTypeNode($typeNode));
|
||||||
|
|
||||||
|
return $this->buildWrappedType($typeDef, $typeNode);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string|NamedTypeNode $ref
|
||||||
|
* @return Type
|
||||||
|
* @throws Error
|
||||||
|
*/
|
||||||
|
public function buildType($ref)
|
||||||
|
{
|
||||||
|
if (is_string($ref)) {
|
||||||
|
return $this->internalBuildType($ref);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->internalBuildType($ref->name->value, $ref);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -154,61 +230,6 @@ class ASTDefinitionBuilder
|
|||||||
return $this->cache[$typeName];
|
return $this->cache[$typeName];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @param string|NamedTypeNode $ref
|
|
||||||
* @return Type
|
|
||||||
* @throws Error
|
|
||||||
*/
|
|
||||||
public function buildType($ref)
|
|
||||||
{
|
|
||||||
if (is_string($ref)) {
|
|
||||||
return $this->internalBuildType($ref);
|
|
||||||
}
|
|
||||||
|
|
||||||
return $this->internalBuildType($ref->name->value, $ref);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return Type|InputType
|
|
||||||
* @throws Error
|
|
||||||
*/
|
|
||||||
private function internalBuildWrappedType(TypeNode $typeNode)
|
|
||||||
{
|
|
||||||
$typeDef = $this->buildType($this->getNamedTypeNode($typeNode));
|
|
||||||
|
|
||||||
return $this->buildWrappedType($typeDef, $typeNode);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function buildDirective(DirectiveDefinitionNode $directiveNode)
|
|
||||||
{
|
|
||||||
return new Directive([
|
|
||||||
'name' => $directiveNode->name->value,
|
|
||||||
'description' => $this->getDescription($directiveNode),
|
|
||||||
'locations' => Utils::map(
|
|
||||||
$directiveNode->locations,
|
|
||||||
function ($node) {
|
|
||||||
return $node->value;
|
|
||||||
}
|
|
||||||
),
|
|
||||||
'args' => $directiveNode->arguments ? FieldArgument::createMap($this->makeInputValues($directiveNode->arguments)) : null,
|
|
||||||
'astNode' => $directiveNode,
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function buildField(FieldDefinitionNode $field)
|
|
||||||
{
|
|
||||||
return [
|
|
||||||
// Note: While this could make assertions to get the correctly typed
|
|
||||||
// value, that would throw immediately while type system validation
|
|
||||||
// with validateSchema() will produce more actionable results.
|
|
||||||
'type' => $this->internalBuildWrappedType($field->type),
|
|
||||||
'description' => $this->getDescription($field),
|
|
||||||
'args' => $field->arguments ? $this->makeInputValues($field->arguments) : null,
|
|
||||||
'deprecationReason' => $this->getDeprecationReason($field),
|
|
||||||
'astNode' => $field,
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param ObjectTypeDefinitionNode|InterfaceTypeDefinitionNode|EnumTypeDefinitionNode|ScalarTypeDefinitionNode|InputObjectTypeDefinitionNode|UnionTypeDefinitionNode $def
|
* @param ObjectTypeDefinitionNode|InterfaceTypeDefinitionNode|EnumTypeDefinitionNode|ScalarTypeDefinitionNode|InputObjectTypeDefinitionNode|UnionTypeDefinitionNode $def
|
||||||
* @return CustomScalarType|EnumType|InputObjectType|InterfaceType|ObjectType|UnionType
|
* @return CustomScalarType|EnumType|InputObjectType|InterfaceType|ObjectType|UnionType
|
||||||
@ -237,35 +258,6 @@ class ASTDefinitionBuilder
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @param ObjectTypeDefinitionNode|InterfaceTypeDefinitionNode|EnumTypeExtensionNode|ScalarTypeDefinitionNode|InputObjectTypeDefinitionNode $def
|
|
||||||
* @param mixed[] $config
|
|
||||||
* @return CustomScalarType|EnumType|InputObjectType|InterfaceType|ObjectType|UnionType
|
|
||||||
* @throws Error
|
|
||||||
*/
|
|
||||||
private function makeSchemaDefFromConfig($def, array $config)
|
|
||||||
{
|
|
||||||
if (! $def) {
|
|
||||||
throw new Error('def must be defined.');
|
|
||||||
}
|
|
||||||
switch ($def->kind) {
|
|
||||||
case NodeKind::OBJECT_TYPE_DEFINITION:
|
|
||||||
return new ObjectType($config);
|
|
||||||
case NodeKind::INTERFACE_TYPE_DEFINITION:
|
|
||||||
return new InterfaceType($config);
|
|
||||||
case NodeKind::ENUM_TYPE_DEFINITION:
|
|
||||||
return new EnumType($config);
|
|
||||||
case NodeKind::UNION_TYPE_DEFINITION:
|
|
||||||
return new UnionType($config);
|
|
||||||
case NodeKind::SCALAR_TYPE_DEFINITION:
|
|
||||||
return new CustomScalarType($config);
|
|
||||||
case NodeKind::INPUT_OBJECT_TYPE_DEFINITION:
|
|
||||||
return new InputObjectType($config);
|
|
||||||
default:
|
|
||||||
throw new Error(sprintf('Type kind of %s not supported.', $def->kind));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private function makeTypeDef(ObjectTypeDefinitionNode $def)
|
private function makeTypeDef(ObjectTypeDefinitionNode $def)
|
||||||
{
|
{
|
||||||
$typeName = $def->name->value;
|
$typeName = $def->name->value;
|
||||||
@ -298,6 +290,34 @@ class ASTDefinitionBuilder
|
|||||||
: [];
|
: [];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function buildField(FieldDefinitionNode $field)
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
// Note: While this could make assertions to get the correctly typed
|
||||||
|
// value, that would throw immediately while type system validation
|
||||||
|
// with validateSchema() will produce more actionable results.
|
||||||
|
'type' => $this->internalBuildWrappedType($field->type),
|
||||||
|
'description' => $this->getDescription($field),
|
||||||
|
'args' => $field->arguments ? $this->makeInputValues($field->arguments) : null,
|
||||||
|
'deprecationReason' => $this->getDeprecationReason($field),
|
||||||
|
'astNode' => $field,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Given a collection of directives, returns the string value for the
|
||||||
|
* deprecation reason.
|
||||||
|
*
|
||||||
|
* @param EnumValueDefinitionNode | FieldDefinitionNode $node
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
private function getDeprecationReason($node)
|
||||||
|
{
|
||||||
|
$deprecated = Values::getDirectiveValues(Directive::deprecatedDirective(), $node);
|
||||||
|
|
||||||
|
return $deprecated['reason'] ?? null;
|
||||||
|
}
|
||||||
|
|
||||||
private function makeImplementedInterfaces(ObjectTypeDefinitionNode $def)
|
private function makeImplementedInterfaces(ObjectTypeDefinitionNode $def)
|
||||||
{
|
{
|
||||||
if ($def->interfaces) {
|
if ($def->interfaces) {
|
||||||
@ -315,33 +335,6 @@ class ASTDefinitionBuilder
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private function makeInputValues($values)
|
|
||||||
{
|
|
||||||
return Utils::keyValMap(
|
|
||||||
$values,
|
|
||||||
function ($value) {
|
|
||||||
return $value->name->value;
|
|
||||||
},
|
|
||||||
function ($value) {
|
|
||||||
// Note: While this could make assertions to get the correctly typed
|
|
||||||
// value, that would throw immediately while type system validation
|
|
||||||
// with validateSchema() will produce more actionable results.
|
|
||||||
$type = $this->internalBuildWrappedType($value->type);
|
|
||||||
$config = [
|
|
||||||
'name' => $value->name->value,
|
|
||||||
'type' => $type,
|
|
||||||
'description' => $this->getDescription($value),
|
|
||||||
'astNode' => $value,
|
|
||||||
];
|
|
||||||
if (isset($value->defaultValue)) {
|
|
||||||
$config['defaultValue'] = AST::valueFromAST($value->defaultValue, $type);
|
|
||||||
}
|
|
||||||
|
|
||||||
return $config;
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
private function makeInterfaceDef(InterfaceTypeDefinitionNode $def)
|
private function makeInterfaceDef(InterfaceTypeDefinitionNode $def)
|
||||||
{
|
{
|
||||||
$typeName = $def->name->value;
|
$typeName = $def->name->value;
|
||||||
@ -427,56 +420,63 @@ class ASTDefinitionBuilder
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Given a collection of directives, returns the string value for the
|
* @param ObjectTypeDefinitionNode|InterfaceTypeDefinitionNode|EnumTypeExtensionNode|ScalarTypeDefinitionNode|InputObjectTypeDefinitionNode $def
|
||||||
* deprecation reason.
|
* @param mixed[] $config
|
||||||
*
|
* @return CustomScalarType|EnumType|InputObjectType|InterfaceType|ObjectType|UnionType
|
||||||
* @param EnumValueDefinitionNode | FieldDefinitionNode $node
|
* @throws Error
|
||||||
* @return string
|
|
||||||
*/
|
*/
|
||||||
private function getDeprecationReason($node)
|
private function makeSchemaDefFromConfig($def, array $config)
|
||||||
{
|
{
|
||||||
$deprecated = Values::getDirectiveValues(Directive::deprecatedDirective(), $node);
|
if (! $def) {
|
||||||
|
throw new Error('def must be defined.');
|
||||||
return $deprecated['reason'] ?? null;
|
}
|
||||||
|
switch ($def->kind) {
|
||||||
|
case NodeKind::OBJECT_TYPE_DEFINITION:
|
||||||
|
return new ObjectType($config);
|
||||||
|
case NodeKind::INTERFACE_TYPE_DEFINITION:
|
||||||
|
return new InterfaceType($config);
|
||||||
|
case NodeKind::ENUM_TYPE_DEFINITION:
|
||||||
|
return new EnumType($config);
|
||||||
|
case NodeKind::UNION_TYPE_DEFINITION:
|
||||||
|
return new UnionType($config);
|
||||||
|
case NodeKind::SCALAR_TYPE_DEFINITION:
|
||||||
|
return new CustomScalarType($config);
|
||||||
|
case NodeKind::INPUT_OBJECT_TYPE_DEFINITION:
|
||||||
|
return new InputObjectType($config);
|
||||||
|
default:
|
||||||
|
throw new Error(sprintf('Type kind of %s not supported.', $def->kind));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Given an ast node, returns its string description.
|
* @param TypeNode|ListTypeNode|NonNullTypeNode $typeNode
|
||||||
|
* @return TypeNode
|
||||||
*/
|
*/
|
||||||
private function getDescription($node)
|
private function getNamedTypeNode(TypeNode $typeNode)
|
||||||
{
|
{
|
||||||
if ($node->description) {
|
$namedType = $typeNode;
|
||||||
return $node->description->value;
|
while ($namedType->kind === NodeKind::LIST_TYPE || $namedType->kind === NodeKind::NON_NULL_TYPE) {
|
||||||
}
|
$namedType = $namedType->type;
|
||||||
if (isset($this->options['commentDescriptions'])) {
|
|
||||||
$rawValue = $this->getLeadingCommentBlock($node);
|
|
||||||
if ($rawValue !== null) {
|
|
||||||
return BlockString::value("\n" . $rawValue);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return $namedType;
|
||||||
}
|
}
|
||||||
|
|
||||||
private function getLeadingCommentBlock($node)
|
/**
|
||||||
|
* @param TypeNode|ListTypeNode|NonNullTypeNode $inputTypeNode
|
||||||
|
* @return Type
|
||||||
|
*/
|
||||||
|
private function buildWrappedType(Type $innerType, TypeNode $inputTypeNode)
|
||||||
{
|
{
|
||||||
$loc = $node->loc;
|
if ($inputTypeNode->kind === NodeKind::LIST_TYPE) {
|
||||||
if (! $loc || ! $loc->startToken) {
|
return Type::listOf($this->buildWrappedType($innerType, $inputTypeNode->type));
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
$comments = [];
|
if ($inputTypeNode->kind === NodeKind::NON_NULL_TYPE) {
|
||||||
$token = $loc->startToken->prev;
|
$wrappedType = $this->buildWrappedType($innerType, $inputTypeNode->type);
|
||||||
while ($token &&
|
|
||||||
$token->kind === Token::COMMENT &&
|
return Type::nonNull(NonNull::assertNullableType($wrappedType));
|
||||||
$token->next && $token->prev &&
|
|
||||||
$token->line + 1 === $token->next->line &&
|
|
||||||
$token->line !== $token->prev->line
|
|
||||||
) {
|
|
||||||
$value = $token->value;
|
|
||||||
$comments[] = $value;
|
|
||||||
$token = $token->prev;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return implode("\n", array_reverse($comments));
|
return $innerType;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,39 +1,54 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace GraphQL\Utils;
|
namespace GraphQL\Utils;
|
||||||
|
|
||||||
class BlockString {
|
use function array_pop;
|
||||||
|
use function array_shift;
|
||||||
|
use function count;
|
||||||
|
use function implode;
|
||||||
|
use function mb_strlen;
|
||||||
|
use function mb_substr;
|
||||||
|
use function preg_split;
|
||||||
|
use function trim;
|
||||||
|
|
||||||
|
class BlockString
|
||||||
|
{
|
||||||
/**
|
/**
|
||||||
* Produces the value of a block string from its parsed raw value, similar to
|
* Produces the value of a block string from its parsed raw value, similar to
|
||||||
* Coffeescript's block string, Python's docstring trim or Ruby's strip_heredoc.
|
* Coffeescript's block string, Python's docstring trim or Ruby's strip_heredoc.
|
||||||
*
|
*
|
||||||
* This implements the GraphQL spec's BlockStringValue() static algorithm.
|
* This implements the GraphQL spec's BlockStringValue() static algorithm.
|
||||||
*/
|
*/
|
||||||
public static function value($rawString) {
|
public static function value($rawString)
|
||||||
|
{
|
||||||
// Expand a block string's raw value into independent lines.
|
// Expand a block string's raw value into independent lines.
|
||||||
$lines = preg_split("/\\r\\n|[\\n\\r]/", $rawString);
|
$lines = preg_split("/\\r\\n|[\\n\\r]/", $rawString);
|
||||||
|
|
||||||
// Remove common indentation from all lines but first.
|
// Remove common indentation from all lines but first.
|
||||||
$commonIndent = null;
|
$commonIndent = null;
|
||||||
$linesLength = count($lines);
|
$linesLength = count($lines);
|
||||||
|
|
||||||
for ($i = 1; $i < $linesLength; $i++) {
|
for ($i = 1; $i < $linesLength; $i++) {
|
||||||
$line = $lines[$i];
|
$line = $lines[$i];
|
||||||
$indent = self::leadingWhitespace($line);
|
$indent = self::leadingWhitespace($line);
|
||||||
|
|
||||||
if (
|
if ($indent >= mb_strlen($line) ||
|
||||||
$indent < mb_strlen($line) &&
|
($commonIndent !== null && $indent >= $commonIndent)
|
||||||
($commonIndent === null || $indent < $commonIndent)
|
|
||||||
) {
|
) {
|
||||||
$commonIndent = $indent;
|
continue;
|
||||||
if ($commonIndent === 0) {
|
}
|
||||||
break;
|
|
||||||
}
|
$commonIndent = $indent;
|
||||||
|
if ($commonIndent === 0) {
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($commonIndent) {
|
if ($commonIndent) {
|
||||||
for ($i = 1; $i < $linesLength; $i++) {
|
for ($i = 1; $i < $linesLength; $i++) {
|
||||||
$line = $lines[$i];
|
$line = $lines[$i];
|
||||||
$lines[$i] = mb_substr($line, $commonIndent);
|
$lines[$i] = mb_substr($line, $commonIndent);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -50,7 +65,8 @@ class BlockString {
|
|||||||
return implode("\n", $lines);
|
return implode("\n", $lines);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static function leadingWhitespace($str) {
|
private static function leadingWhitespace($str)
|
||||||
|
{
|
||||||
$i = 0;
|
$i = 0;
|
||||||
while ($i < mb_strlen($str) && ($str[$i] === ' ' || $str[$i] === '\t')) {
|
while ($i < mb_strlen($str) && ($str[$i] === ' ' || $str[$i] === '\t')) {
|
||||||
$i++;
|
$i++;
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -1,14 +1,21 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace GraphQL\Utils;
|
namespace GraphQL\Utils;
|
||||||
|
|
||||||
use GraphQL\Error\Error;
|
use GraphQL\Error\Error;
|
||||||
use GraphQL\Language\AST\DocumentNode;
|
use GraphQL\Language\AST\DocumentNode;
|
||||||
|
use GraphQL\Language\AST\Node;
|
||||||
use GraphQL\Language\AST\NodeKind;
|
use GraphQL\Language\AST\NodeKind;
|
||||||
use GraphQL\Language\AST\SchemaDefinitionNode;
|
use GraphQL\Language\AST\SchemaDefinitionNode;
|
||||||
use GraphQL\Language\Parser;
|
use GraphQL\Language\Parser;
|
||||||
use GraphQL\Language\Source;
|
use GraphQL\Language\Source;
|
||||||
use GraphQL\Type\Schema;
|
|
||||||
use GraphQL\Type\Definition\Directive;
|
use GraphQL\Type\Definition\Directive;
|
||||||
|
use GraphQL\Type\Schema;
|
||||||
|
use function array_map;
|
||||||
|
use function array_reduce;
|
||||||
|
use function sprintf;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Build instance of `GraphQL\Type\Schema` out of type language definition (string or parsed AST)
|
* Build instance of `GraphQL\Type\Schema` out of type language definition (string or parsed AST)
|
||||||
@ -16,6 +23,44 @@ use GraphQL\Type\Definition\Directive;
|
|||||||
*/
|
*/
|
||||||
class BuildSchema
|
class BuildSchema
|
||||||
{
|
{
|
||||||
|
/** @var DocumentNode */
|
||||||
|
private $ast;
|
||||||
|
|
||||||
|
/** @var Node[] */
|
||||||
|
private $nodeMap;
|
||||||
|
|
||||||
|
/** @var callable|null */
|
||||||
|
private $typeConfigDecorator;
|
||||||
|
|
||||||
|
/** @var bool[] */
|
||||||
|
private $options;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param bool[] $options
|
||||||
|
*/
|
||||||
|
public function __construct(DocumentNode $ast, ?callable $typeConfigDecorator = null, array $options = [])
|
||||||
|
{
|
||||||
|
$this->ast = $ast;
|
||||||
|
$this->typeConfigDecorator = $typeConfigDecorator;
|
||||||
|
$this->options = $options;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A helper function to build a GraphQLSchema directly from a source
|
||||||
|
* document.
|
||||||
|
*
|
||||||
|
* @api
|
||||||
|
* @param DocumentNode|Source|string $source
|
||||||
|
* @param bool[] $options
|
||||||
|
* @return Schema
|
||||||
|
*/
|
||||||
|
public static function build($source, ?callable $typeConfigDecorator = null, array $options = [])
|
||||||
|
{
|
||||||
|
$doc = $source instanceof DocumentNode ? $source : Parser::parse($source);
|
||||||
|
|
||||||
|
return self::buildAST($doc, $typeConfigDecorator, $options);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This takes the ast of a schema document produced by the parse function in
|
* This takes the ast of a schema document produced by the parse function in
|
||||||
* GraphQL\Language\Parser.
|
* GraphQL\Language\Parser.
|
||||||
@ -33,35 +78,22 @@ class BuildSchema
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* @api
|
* @api
|
||||||
* @param DocumentNode $ast
|
* @param bool[] $options
|
||||||
* @param callable $typeConfigDecorator
|
|
||||||
* @param array $options
|
|
||||||
* @return Schema
|
* @return Schema
|
||||||
* @throws Error
|
* @throws Error
|
||||||
*/
|
*/
|
||||||
public static function buildAST(DocumentNode $ast, callable $typeConfigDecorator = null, array $options = [])
|
public static function buildAST(DocumentNode $ast, ?callable $typeConfigDecorator = null, array $options = [])
|
||||||
{
|
{
|
||||||
$builder = new self($ast, $typeConfigDecorator, $options);
|
$builder = new self($ast, $typeConfigDecorator, $options);
|
||||||
|
|
||||||
return $builder->buildSchema();
|
return $builder->buildSchema();
|
||||||
}
|
}
|
||||||
|
|
||||||
private $ast;
|
|
||||||
private $nodeMap;
|
|
||||||
private $typeConfigDecorator;
|
|
||||||
private $options;
|
|
||||||
|
|
||||||
public function __construct(DocumentNode $ast, callable $typeConfigDecorator = null, array $options = [])
|
|
||||||
{
|
|
||||||
$this->ast = $ast;
|
|
||||||
$this->typeConfigDecorator = $typeConfigDecorator;
|
|
||||||
$this->options = $options;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function buildSchema()
|
public function buildSchema()
|
||||||
{
|
{
|
||||||
/** @var SchemaDefinitionNode $schemaDef */
|
/** @var SchemaDefinitionNode $schemaDef */
|
||||||
$schemaDef = null;
|
$schemaDef = null;
|
||||||
$typeDefs = [];
|
$typeDefs = [];
|
||||||
$this->nodeMap = [];
|
$this->nodeMap = [];
|
||||||
$directiveDefs = [];
|
$directiveDefs = [];
|
||||||
foreach ($this->ast->definitions as $d) {
|
foreach ($this->ast->definitions as $d) {
|
||||||
@ -79,10 +111,10 @@ class BuildSchema
|
|||||||
case NodeKind::UNION_TYPE_DEFINITION:
|
case NodeKind::UNION_TYPE_DEFINITION:
|
||||||
case NodeKind::INPUT_OBJECT_TYPE_DEFINITION:
|
case NodeKind::INPUT_OBJECT_TYPE_DEFINITION:
|
||||||
$typeName = $d->name->value;
|
$typeName = $d->name->value;
|
||||||
if (!empty($this->nodeMap[$typeName])) {
|
if (! empty($this->nodeMap[$typeName])) {
|
||||||
throw new Error("Type \"$typeName\" was defined more than once.");
|
throw new Error(sprintf('Type "%s" was defined more than once.', $typeName));
|
||||||
}
|
}
|
||||||
$typeDefs[] = $d;
|
$typeDefs[] = $d;
|
||||||
$this->nodeMap[$typeName] = $d;
|
$this->nodeMap[$typeName] = $d;
|
||||||
break;
|
break;
|
||||||
case NodeKind::DIRECTIVE_DEFINITION:
|
case NodeKind::DIRECTIVE_DEFINITION:
|
||||||
@ -94,41 +126,55 @@ class BuildSchema
|
|||||||
$operationTypes = $schemaDef
|
$operationTypes = $schemaDef
|
||||||
? $this->getOperationTypes($schemaDef)
|
? $this->getOperationTypes($schemaDef)
|
||||||
: [
|
: [
|
||||||
'query' => isset($this->nodeMap['Query']) ? 'Query' : null,
|
'query' => isset($this->nodeMap['Query']) ? 'Query' : null,
|
||||||
'mutation' => isset($this->nodeMap['Mutation']) ? 'Mutation' : null,
|
'mutation' => isset($this->nodeMap['Mutation']) ? 'Mutation' : null,
|
||||||
'subscription' => isset($this->nodeMap['Subscription']) ? 'Subscription' : null,
|
'subscription' => isset($this->nodeMap['Subscription']) ? 'Subscription' : null,
|
||||||
];
|
];
|
||||||
|
|
||||||
$defintionBuilder = new ASTDefinitionBuilder(
|
$defintionBuilder = new ASTDefinitionBuilder(
|
||||||
$this->nodeMap,
|
$this->nodeMap,
|
||||||
$this->options,
|
$this->options,
|
||||||
function($typeName) { throw new Error('Type "'. $typeName . '" not found in document.'); },
|
function ($typeName) {
|
||||||
|
throw new Error('Type "' . $typeName . '" not found in document.');
|
||||||
|
},
|
||||||
$this->typeConfigDecorator
|
$this->typeConfigDecorator
|
||||||
);
|
);
|
||||||
|
|
||||||
$directives = array_map(function($def) use ($defintionBuilder) {
|
$directives = array_map(
|
||||||
return $defintionBuilder->buildDirective($def);
|
function ($def) use ($defintionBuilder) {
|
||||||
}, $directiveDefs);
|
return $defintionBuilder->buildDirective($def);
|
||||||
|
},
|
||||||
|
$directiveDefs
|
||||||
|
);
|
||||||
|
|
||||||
// If specified directives were not explicitly declared, add them.
|
// If specified directives were not explicitly declared, add them.
|
||||||
$skip = array_reduce($directives, function ($hasSkip, $directive) {
|
$skip = array_reduce(
|
||||||
return $hasSkip || $directive->name == 'skip';
|
$directives,
|
||||||
});
|
function ($hasSkip, $directive) {
|
||||||
if (!$skip) {
|
return $hasSkip || $directive->name === 'skip';
|
||||||
|
}
|
||||||
|
);
|
||||||
|
if (! $skip) {
|
||||||
$directives[] = Directive::skipDirective();
|
$directives[] = Directive::skipDirective();
|
||||||
}
|
}
|
||||||
|
|
||||||
$include = array_reduce($directives, function ($hasInclude, $directive) {
|
$include = array_reduce(
|
||||||
return $hasInclude || $directive->name == 'include';
|
$directives,
|
||||||
});
|
function ($hasInclude, $directive) {
|
||||||
if (!$include) {
|
return $hasInclude || $directive->name === 'include';
|
||||||
|
}
|
||||||
|
);
|
||||||
|
if (! $include) {
|
||||||
$directives[] = Directive::includeDirective();
|
$directives[] = Directive::includeDirective();
|
||||||
}
|
}
|
||||||
|
|
||||||
$deprecated = array_reduce($directives, function ($hasDeprecated, $directive) {
|
$deprecated = array_reduce(
|
||||||
return $hasDeprecated || $directive->name == 'deprecated';
|
$directives,
|
||||||
});
|
function ($hasDeprecated, $directive) {
|
||||||
if (!$deprecated) {
|
return $hasDeprecated || $directive->name === 'deprecated';
|
||||||
|
}
|
||||||
|
);
|
||||||
|
if (! $deprecated) {
|
||||||
$directives[] = Directive::deprecatedDirective();
|
$directives[] = Directive::deprecatedDirective();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -137,27 +183,28 @@ class BuildSchema
|
|||||||
// validation with validateSchema() will produce more actionable results.
|
// validation with validateSchema() will produce more actionable results.
|
||||||
|
|
||||||
$schema = new Schema([
|
$schema = new Schema([
|
||||||
'query' => isset($operationTypes['query'])
|
'query' => isset($operationTypes['query'])
|
||||||
? $defintionBuilder->buildType($operationTypes['query'])
|
? $defintionBuilder->buildType($operationTypes['query'])
|
||||||
: null,
|
: null,
|
||||||
'mutation' => isset($operationTypes['mutation'])
|
'mutation' => isset($operationTypes['mutation'])
|
||||||
? $defintionBuilder->buildType($operationTypes['mutation'])
|
? $defintionBuilder->buildType($operationTypes['mutation'])
|
||||||
: null,
|
: null,
|
||||||
'subscription' => isset($operationTypes['subscription'])
|
'subscription' => isset($operationTypes['subscription'])
|
||||||
? $defintionBuilder->buildType($operationTypes['subscription'])
|
? $defintionBuilder->buildType($operationTypes['subscription'])
|
||||||
: null,
|
: null,
|
||||||
'typeLoader' => function ($name) use ($defintionBuilder) {
|
'typeLoader' => function ($name) use ($defintionBuilder) {
|
||||||
return $defintionBuilder->buildType($name);
|
return $defintionBuilder->buildType($name);
|
||||||
},
|
},
|
||||||
'directives' => $directives,
|
'directives' => $directives,
|
||||||
'astNode' => $schemaDef,
|
'astNode' => $schemaDef,
|
||||||
'types' => function () use ($defintionBuilder) {
|
'types' => function () use ($defintionBuilder) {
|
||||||
$types = [];
|
$types = [];
|
||||||
foreach ($this->nodeMap as $name => $def) {
|
foreach ($this->nodeMap as $name => $def) {
|
||||||
$types[] = $defintionBuilder->buildType($def->name->value);
|
$types[] = $defintionBuilder->buildType($def->name->value);
|
||||||
}
|
}
|
||||||
|
|
||||||
return $types;
|
return $types;
|
||||||
}
|
},
|
||||||
]);
|
]);
|
||||||
|
|
||||||
return $schema;
|
return $schema;
|
||||||
@ -165,7 +212,7 @@ class BuildSchema
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @param SchemaDefinitionNode $schemaDef
|
* @param SchemaDefinitionNode $schemaDef
|
||||||
* @return array
|
* @return string[]
|
||||||
* @throws Error
|
* @throws Error
|
||||||
*/
|
*/
|
||||||
private function getOperationTypes($schemaDef)
|
private function getOperationTypes($schemaDef)
|
||||||
@ -173,15 +220,15 @@ class BuildSchema
|
|||||||
$opTypes = [];
|
$opTypes = [];
|
||||||
|
|
||||||
foreach ($schemaDef->operationTypes as $operationType) {
|
foreach ($schemaDef->operationTypes as $operationType) {
|
||||||
$typeName = $operationType->type->name->value;
|
$typeName = $operationType->type->name->value;
|
||||||
$operation = $operationType->operation;
|
$operation = $operationType->operation;
|
||||||
|
|
||||||
if (isset($opTypes[$operation])) {
|
if (isset($opTypes[$operation])) {
|
||||||
throw new Error("Must provide only one $operation type in schema.");
|
throw new Error(sprintf('Must provide only one %s type in schema.', $operation));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!isset($this->nodeMap[$typeName])) {
|
if (! isset($this->nodeMap[$typeName])) {
|
||||||
throw new Error("Specified $operation type \"$typeName\" not found in document.");
|
throw new Error(sprintf('Specified %s type "%s" not found in document.', $operation, $typeName));
|
||||||
}
|
}
|
||||||
|
|
||||||
$opTypes[$operation] = $typeName;
|
$opTypes[$operation] = $typeName;
|
||||||
@ -189,20 +236,4 @@ class BuildSchema
|
|||||||
|
|
||||||
return $opTypes;
|
return $opTypes;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* A helper function to build a GraphQLSchema directly from a source
|
|
||||||
* document.
|
|
||||||
*
|
|
||||||
* @api
|
|
||||||
* @param DocumentNode|Source|string $source
|
|
||||||
* @param callable $typeConfigDecorator
|
|
||||||
* @param array $options
|
|
||||||
* @return Schema
|
|
||||||
*/
|
|
||||||
public static function build($source, callable $typeConfigDecorator = null, array $options = [])
|
|
||||||
{
|
|
||||||
$doc = $source instanceof DocumentNode ? $source : Parser::parse($source);
|
|
||||||
return self::buildAST($doc, $typeConfigDecorator, $options);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,19 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace GraphQL\Utils;
|
namespace GraphQL\Utils;
|
||||||
|
|
||||||
|
use GraphQL\Type\Definition\EnumValueDefinition;
|
||||||
|
use function array_key_exists;
|
||||||
|
use function array_search;
|
||||||
|
use function array_splice;
|
||||||
|
use function is_array;
|
||||||
|
use function is_float;
|
||||||
|
use function is_int;
|
||||||
|
use function is_object;
|
||||||
|
use function is_string;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Similar to PHP array, but allows any type of data to act as key (including arrays, objects, scalars)
|
* Similar to PHP array, but allows any type of data to act as key (including arrays, objects, scalars)
|
||||||
*
|
*
|
||||||
@ -8,87 +21,57 @@ namespace GraphQL\Utils;
|
|||||||
* (yet this should be really rare case and should be avoided when possible)
|
* (yet this should be really rare case and should be avoided when possible)
|
||||||
*
|
*
|
||||||
* Class MixedStore
|
* Class MixedStore
|
||||||
* @package GraphQL\Utils
|
|
||||||
*/
|
*/
|
||||||
class MixedStore implements \ArrayAccess
|
class MixedStore implements \ArrayAccess
|
||||||
{
|
{
|
||||||
/**
|
/** @var EnumValueDefinition[] */
|
||||||
* @var array
|
|
||||||
*/
|
|
||||||
private $standardStore;
|
private $standardStore;
|
||||||
|
|
||||||
/**
|
/** @var mixed[] */
|
||||||
* @var array
|
|
||||||
*/
|
|
||||||
private $floatStore;
|
private $floatStore;
|
||||||
|
|
||||||
/**
|
/** @var \SplObjectStorage */
|
||||||
* @var \SplObjectStorage
|
|
||||||
*/
|
|
||||||
private $objectStore;
|
private $objectStore;
|
||||||
|
|
||||||
/**
|
/** @var callable[] */
|
||||||
* @var array
|
|
||||||
*/
|
|
||||||
private $arrayKeys;
|
private $arrayKeys;
|
||||||
|
|
||||||
/**
|
/** @var EnumValueDefinition[] */
|
||||||
* @var array
|
|
||||||
*/
|
|
||||||
private $arrayValues;
|
private $arrayValues;
|
||||||
|
|
||||||
/**
|
/** @var callable[] */
|
||||||
* @var array
|
|
||||||
*/
|
|
||||||
private $lastArrayKey;
|
private $lastArrayKey;
|
||||||
|
|
||||||
/**
|
/** @var mixed */
|
||||||
* @var mixed
|
|
||||||
*/
|
|
||||||
private $lastArrayValue;
|
private $lastArrayValue;
|
||||||
|
|
||||||
/**
|
/** @var mixed */
|
||||||
* @var mixed
|
|
||||||
*/
|
|
||||||
private $nullValue;
|
private $nullValue;
|
||||||
|
|
||||||
/**
|
/** @var bool */
|
||||||
* @var bool
|
|
||||||
*/
|
|
||||||
private $nullValueIsSet;
|
private $nullValueIsSet;
|
||||||
|
|
||||||
/**
|
/** @var mixed */
|
||||||
* @var mixed
|
|
||||||
*/
|
|
||||||
private $trueValue;
|
private $trueValue;
|
||||||
|
|
||||||
/**
|
/** @var bool */
|
||||||
* @var bool
|
|
||||||
*/
|
|
||||||
private $trueValueIsSet;
|
private $trueValueIsSet;
|
||||||
|
|
||||||
/**
|
/** @var mixed */
|
||||||
* @var mixed
|
|
||||||
*/
|
|
||||||
private $falseValue;
|
private $falseValue;
|
||||||
|
|
||||||
/**
|
/** @var bool */
|
||||||
* @var bool
|
|
||||||
*/
|
|
||||||
private $falseValueIsSet;
|
private $falseValueIsSet;
|
||||||
|
|
||||||
/**
|
|
||||||
* MixedStore constructor.
|
|
||||||
*/
|
|
||||||
public function __construct()
|
public function __construct()
|
||||||
{
|
{
|
||||||
$this->standardStore = [];
|
$this->standardStore = [];
|
||||||
$this->floatStore = [];
|
$this->floatStore = [];
|
||||||
$this->objectStore = new \SplObjectStorage();
|
$this->objectStore = new \SplObjectStorage();
|
||||||
$this->arrayKeys = [];
|
$this->arrayKeys = [];
|
||||||
$this->arrayValues = [];
|
$this->arrayValues = [];
|
||||||
$this->nullValueIsSet = false;
|
$this->nullValueIsSet = false;
|
||||||
$this->trueValueIsSet = false;
|
$this->trueValueIsSet = false;
|
||||||
$this->falseValueIsSet = false;
|
$this->falseValueIsSet = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -98,18 +81,17 @@ class MixedStore implements \ArrayAccess
|
|||||||
* @param mixed $offset <p>
|
* @param mixed $offset <p>
|
||||||
* An offset to check for.
|
* An offset to check for.
|
||||||
* </p>
|
* </p>
|
||||||
* @return boolean true on success or false on failure.
|
* @return bool true on success or false on failure.
|
||||||
* </p>
|
* </p>
|
||||||
* <p>
|
* <p>
|
||||||
* The return value will be casted to boolean if non-boolean was returned.
|
* The return value will be casted to boolean if non-boolean was returned.
|
||||||
* @since 5.0.0
|
|
||||||
*/
|
*/
|
||||||
public function offsetExists($offset)
|
public function offsetExists($offset)
|
||||||
{
|
{
|
||||||
if (false === $offset) {
|
if ($offset === false) {
|
||||||
return $this->falseValueIsSet;
|
return $this->falseValueIsSet;
|
||||||
}
|
}
|
||||||
if (true === $offset) {
|
if ($offset === true) {
|
||||||
return $this->trueValueIsSet;
|
return $this->trueValueIsSet;
|
||||||
}
|
}
|
||||||
if (is_int($offset) || is_string($offset)) {
|
if (is_int($offset) || is_string($offset)) {
|
||||||
@ -124,15 +106,17 @@ class MixedStore implements \ArrayAccess
|
|||||||
if (is_array($offset)) {
|
if (is_array($offset)) {
|
||||||
foreach ($this->arrayKeys as $index => $entry) {
|
foreach ($this->arrayKeys as $index => $entry) {
|
||||||
if ($entry === $offset) {
|
if ($entry === $offset) {
|
||||||
$this->lastArrayKey = $offset;
|
$this->lastArrayKey = $offset;
|
||||||
$this->lastArrayValue = $this->arrayValues[$index];
|
$this->lastArrayValue = $this->arrayValues[$index];
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (null === $offset) {
|
if ($offset === null) {
|
||||||
return $this->nullValueIsSet;
|
return $this->nullValueIsSet;
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -143,21 +127,20 @@ class MixedStore implements \ArrayAccess
|
|||||||
* The offset to retrieve.
|
* The offset to retrieve.
|
||||||
* </p>
|
* </p>
|
||||||
* @return mixed Can return all value types.
|
* @return mixed Can return all value types.
|
||||||
* @since 5.0.0
|
|
||||||
*/
|
*/
|
||||||
public function offsetGet($offset)
|
public function offsetGet($offset)
|
||||||
{
|
{
|
||||||
if (true === $offset) {
|
if ($offset === true) {
|
||||||
return $this->trueValue;
|
return $this->trueValue;
|
||||||
}
|
}
|
||||||
if (false === $offset) {
|
if ($offset === false) {
|
||||||
return $this->falseValue;
|
return $this->falseValue;
|
||||||
}
|
}
|
||||||
if (is_int($offset) || is_string($offset)) {
|
if (is_int($offset) || is_string($offset)) {
|
||||||
return $this->standardStore[$offset];
|
return $this->standardStore[$offset];
|
||||||
}
|
}
|
||||||
if (is_float($offset)) {
|
if (is_float($offset)) {
|
||||||
return $this->floatStore[(string)$offset];
|
return $this->floatStore[(string) $offset];
|
||||||
}
|
}
|
||||||
if (is_object($offset)) {
|
if (is_object($offset)) {
|
||||||
return $this->objectStore->offsetGet($offset);
|
return $this->objectStore->offsetGet($offset);
|
||||||
@ -173,9 +156,10 @@ class MixedStore implements \ArrayAccess
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (null === $offset) {
|
if ($offset === null) {
|
||||||
return $this->nullValue;
|
return $this->nullValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -185,34 +169,33 @@ class MixedStore implements \ArrayAccess
|
|||||||
* @param mixed $offset <p>
|
* @param mixed $offset <p>
|
||||||
* The offset to assign the value to.
|
* The offset to assign the value to.
|
||||||
* </p>
|
* </p>
|
||||||
* @param mixed $value <p>
|
* @param mixed $value <p>
|
||||||
* The value to set.
|
* The value to set.
|
||||||
* </p>
|
* </p>
|
||||||
* @return void
|
* @return void
|
||||||
* @since 5.0.0
|
|
||||||
*/
|
*/
|
||||||
public function offsetSet($offset, $value)
|
public function offsetSet($offset, $value)
|
||||||
{
|
{
|
||||||
if (false === $offset) {
|
if ($offset === false) {
|
||||||
$this->falseValue = $value;
|
$this->falseValue = $value;
|
||||||
$this->falseValueIsSet = true;
|
$this->falseValueIsSet = true;
|
||||||
} else if (true === $offset) {
|
} elseif ($offset === true) {
|
||||||
$this->trueValue = $value;
|
$this->trueValue = $value;
|
||||||
$this->trueValueIsSet = true;
|
$this->trueValueIsSet = true;
|
||||||
} else if (is_int($offset) || is_string($offset)) {
|
} elseif (is_int($offset) || is_string($offset)) {
|
||||||
$this->standardStore[$offset] = $value;
|
$this->standardStore[$offset] = $value;
|
||||||
} else if (is_float($offset)) {
|
} elseif (is_float($offset)) {
|
||||||
$this->floatStore[(string)$offset] = $value;
|
$this->floatStore[(string) $offset] = $value;
|
||||||
} else if (is_object($offset)) {
|
} elseif (is_object($offset)) {
|
||||||
$this->objectStore[$offset] = $value;
|
$this->objectStore[$offset] = $value;
|
||||||
} else if (is_array($offset)) {
|
} elseif (is_array($offset)) {
|
||||||
$this->arrayKeys[] = $offset;
|
$this->arrayKeys[] = $offset;
|
||||||
$this->arrayValues[] = $value;
|
$this->arrayValues[] = $value;
|
||||||
} else if (null === $offset) {
|
} elseif ($offset === null) {
|
||||||
$this->nullValue = $value;
|
$this->nullValue = $value;
|
||||||
$this->nullValueIsSet = true;
|
$this->nullValueIsSet = true;
|
||||||
} else {
|
} else {
|
||||||
throw new \InvalidArgumentException("Unexpected offset type: " . Utils::printSafe($offset));
|
throw new \InvalidArgumentException('Unexpected offset type: ' . Utils::printSafe($offset));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -223,31 +206,30 @@ class MixedStore implements \ArrayAccess
|
|||||||
* The offset to unset.
|
* The offset to unset.
|
||||||
* </p>
|
* </p>
|
||||||
* @return void
|
* @return void
|
||||||
* @since 5.0.0
|
|
||||||
*/
|
*/
|
||||||
public function offsetUnset($offset)
|
public function offsetUnset($offset)
|
||||||
{
|
{
|
||||||
if (true === $offset) {
|
if ($offset === true) {
|
||||||
$this->trueValue = null;
|
$this->trueValue = null;
|
||||||
$this->trueValueIsSet = false;
|
$this->trueValueIsSet = false;
|
||||||
} else if (false === $offset) {
|
} elseif ($offset === false) {
|
||||||
$this->falseValue = null;
|
$this->falseValue = null;
|
||||||
$this->falseValueIsSet = false;
|
$this->falseValueIsSet = false;
|
||||||
} else if (is_int($offset) || is_string($offset)) {
|
} elseif (is_int($offset) || is_string($offset)) {
|
||||||
unset($this->standardStore[$offset]);
|
unset($this->standardStore[$offset]);
|
||||||
} else if (is_float($offset)) {
|
} elseif (is_float($offset)) {
|
||||||
unset($this->floatStore[(string)$offset]);
|
unset($this->floatStore[(string) $offset]);
|
||||||
} else if (is_object($offset)) {
|
} elseif (is_object($offset)) {
|
||||||
$this->objectStore->offsetUnset($offset);
|
$this->objectStore->offsetUnset($offset);
|
||||||
} else if (is_array($offset)) {
|
} elseif (is_array($offset)) {
|
||||||
$index = array_search($offset, $this->arrayKeys, true);
|
$index = array_search($offset, $this->arrayKeys, true);
|
||||||
|
|
||||||
if (false !== $index) {
|
if ($index !== false) {
|
||||||
array_splice($this->arrayKeys, $index, 1);
|
array_splice($this->arrayKeys, $index, 1);
|
||||||
array_splice($this->arrayValues, $index, 1);
|
array_splice($this->arrayValues, $index, 1);
|
||||||
}
|
}
|
||||||
} else if (null === $offset) {
|
} elseif ($offset === null) {
|
||||||
$this->nullValue = null;
|
$this->nullValue = null;
|
||||||
$this->nullValueIsSet = false;
|
$this->nullValueIsSet = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,7 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace GraphQL\Utils;
|
namespace GraphQL\Utils;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -7,14 +10,9 @@ namespace GraphQL\Utils;
|
|||||||
*/
|
*/
|
||||||
class PairSet
|
class PairSet
|
||||||
{
|
{
|
||||||
/**
|
/** @var bool[][] */
|
||||||
* @var array
|
|
||||||
*/
|
|
||||||
private $data;
|
private $data;
|
||||||
|
|
||||||
/**
|
|
||||||
* PairSet constructor.
|
|
||||||
*/
|
|
||||||
public function __construct()
|
public function __construct()
|
||||||
{
|
{
|
||||||
$this->data = [];
|
$this->data = [];
|
||||||
@ -23,12 +21,12 @@ class PairSet
|
|||||||
/**
|
/**
|
||||||
* @param string $a
|
* @param string $a
|
||||||
* @param string $b
|
* @param string $b
|
||||||
* @param bool $areMutuallyExclusive
|
* @param bool $areMutuallyExclusive
|
||||||
* @return bool
|
* @return bool
|
||||||
*/
|
*/
|
||||||
public function has($a, $b, $areMutuallyExclusive)
|
public function has($a, $b, $areMutuallyExclusive)
|
||||||
{
|
{
|
||||||
$first = isset($this->data[$a]) ? $this->data[$a] : null;
|
$first = $this->data[$a] ?? null;
|
||||||
$result = ($first && isset($first[$b])) ? $first[$b] : null;
|
$result = ($first && isset($first[$b])) ? $first[$b] : null;
|
||||||
if ($result === null) {
|
if ($result === null) {
|
||||||
return false;
|
return false;
|
||||||
@ -39,13 +37,14 @@ class PairSet
|
|||||||
if ($areMutuallyExclusive === false) {
|
if ($areMutuallyExclusive === false) {
|
||||||
return $result === false;
|
return $result === false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param string $a
|
* @param string $a
|
||||||
* @param string $b
|
* @param string $b
|
||||||
* @param bool $areMutuallyExclusive
|
* @param bool $areMutuallyExclusive
|
||||||
*/
|
*/
|
||||||
public function add($a, $b, $areMutuallyExclusive)
|
public function add($a, $b, $areMutuallyExclusive)
|
||||||
{
|
{
|
||||||
@ -56,11 +55,11 @@ class PairSet
|
|||||||
/**
|
/**
|
||||||
* @param string $a
|
* @param string $a
|
||||||
* @param string $b
|
* @param string $b
|
||||||
* @param bool $areMutuallyExclusive
|
* @param bool $areMutuallyExclusive
|
||||||
*/
|
*/
|
||||||
private function pairSetAdd($a, $b, $areMutuallyExclusive)
|
private function pairSetAdd($a, $b, $areMutuallyExclusive)
|
||||||
{
|
{
|
||||||
$this->data[$a] = isset($this->data[$a]) ? $this->data[$a] : [];
|
$this->data[$a] = $this->data[$a] ?? [];
|
||||||
$this->data[$a][$b] = $areMutuallyExclusive;
|
$this->data[$a][$b] = $areMutuallyExclusive;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,10 +1,12 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace GraphQL\Utils;
|
namespace GraphQL\Utils;
|
||||||
|
|
||||||
use GraphQL\Error\Error;
|
use GraphQL\Error\Error;
|
||||||
use GraphQL\Language\Printer;
|
use GraphQL\Language\Printer;
|
||||||
use GraphQL\Type\Introspection;
|
use GraphQL\Type\Definition\Directive;
|
||||||
use GraphQL\Type\Schema;
|
|
||||||
use GraphQL\Type\Definition\EnumType;
|
use GraphQL\Type\Definition\EnumType;
|
||||||
use GraphQL\Type\Definition\InputObjectType;
|
use GraphQL\Type\Definition\InputObjectType;
|
||||||
use GraphQL\Type\Definition\InterfaceType;
|
use GraphQL\Type\Definition\InterfaceType;
|
||||||
@ -12,7 +14,23 @@ use GraphQL\Type\Definition\ObjectType;
|
|||||||
use GraphQL\Type\Definition\ScalarType;
|
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\Directive;
|
use GraphQL\Type\Introspection;
|
||||||
|
use GraphQL\Type\Schema;
|
||||||
|
use function array_filter;
|
||||||
|
use function array_keys;
|
||||||
|
use function array_map;
|
||||||
|
use function array_merge;
|
||||||
|
use function array_values;
|
||||||
|
use function count;
|
||||||
|
use function explode;
|
||||||
|
use function implode;
|
||||||
|
use function ksort;
|
||||||
|
use function mb_strlen;
|
||||||
|
use function preg_match_all;
|
||||||
|
use function sprintf;
|
||||||
|
use function str_replace;
|
||||||
|
use function strlen;
|
||||||
|
use function substr;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Given an instance of Schema, prints it in GraphQL type language.
|
* Given an instance of Schema, prints it in GraphQL type language.
|
||||||
@ -25,52 +43,62 @@ class SchemaPrinter
|
|||||||
* - commentDescriptions:
|
* - commentDescriptions:
|
||||||
* Provide true to use preceding comments as the description.
|
* Provide true to use preceding comments as the description.
|
||||||
* @api
|
* @api
|
||||||
* @param Schema $schema
|
* @param bool[] $options
|
||||||
* @return string
|
* @return string
|
||||||
*/
|
*/
|
||||||
public static function doPrint(Schema $schema, array $options = [])
|
public static function doPrint(Schema $schema, array $options = [])
|
||||||
{
|
{
|
||||||
return self::printFilteredSchema(
|
return self::printFilteredSchema(
|
||||||
$schema,
|
$schema,
|
||||||
function($type) {
|
function ($type) {
|
||||||
return !Directive::isSpecifiedDirective($type);
|
return ! Directive::isSpecifiedDirective($type);
|
||||||
},
|
},
|
||||||
function ($type) {
|
function ($type) {
|
||||||
return !Type::isBuiltInType($type);
|
return ! Type::isBuiltInType($type);
|
||||||
},
|
},
|
||||||
$options
|
$options
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @api
|
* @param bool[] $options
|
||||||
* @param Schema $schema
|
|
||||||
* @return string
|
|
||||||
*/
|
*/
|
||||||
public static function printIntrosepctionSchema(Schema $schema, array $options = [])
|
|
||||||
{
|
|
||||||
return self::printFilteredSchema(
|
|
||||||
$schema,
|
|
||||||
[Directive::class, 'isSpecifiedDirective'],
|
|
||||||
[Introspection::class, 'isIntrospectionType'],
|
|
||||||
$options
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static function printFilteredSchema(Schema $schema, $directiveFilter, $typeFilter, $options)
|
private static function printFilteredSchema(Schema $schema, $directiveFilter, $typeFilter, $options)
|
||||||
{
|
{
|
||||||
$directives = array_filter($schema->getDirectives(), function($directive) use ($directiveFilter) {
|
$directives = array_filter(
|
||||||
return $directiveFilter($directive);
|
$schema->getDirectives(),
|
||||||
});
|
function ($directive) use ($directiveFilter) {
|
||||||
|
return $directiveFilter($directive);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
$types = $schema->getTypeMap();
|
$types = $schema->getTypeMap();
|
||||||
ksort($types);
|
ksort($types);
|
||||||
$types = array_filter($types, $typeFilter);
|
$types = array_filter($types, $typeFilter);
|
||||||
|
|
||||||
return implode("\n\n", array_filter(array_merge(
|
return sprintf(
|
||||||
[self::printSchemaDefinition($schema)],
|
"%s\n",
|
||||||
array_map(function($directive) use ($options) { return self::printDirective($directive, $options); }, $directives),
|
implode(
|
||||||
array_map(function($type) use ($options) { return self::printType($type, $options); }, $types)
|
"\n\n",
|
||||||
))) . "\n";
|
array_filter(
|
||||||
|
array_merge(
|
||||||
|
[self::printSchemaDefinition($schema)],
|
||||||
|
array_map(
|
||||||
|
function ($directive) use ($options) {
|
||||||
|
return self::printDirective($directive, $options);
|
||||||
|
},
|
||||||
|
$directives
|
||||||
|
),
|
||||||
|
array_map(
|
||||||
|
function ($type) use ($options) {
|
||||||
|
return self::printType($type, $options);
|
||||||
|
},
|
||||||
|
$types
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static function printSchemaDefinition(Schema $schema)
|
private static function printSchemaDefinition(Schema $schema)
|
||||||
@ -83,20 +111,20 @@ class SchemaPrinter
|
|||||||
|
|
||||||
$queryType = $schema->getQueryType();
|
$queryType = $schema->getQueryType();
|
||||||
if ($queryType) {
|
if ($queryType) {
|
||||||
$operationTypes[] = " query: {$queryType->name}";
|
$operationTypes[] = sprintf(' query: %s', $queryType->name);
|
||||||
}
|
}
|
||||||
|
|
||||||
$mutationType = $schema->getMutationType();
|
$mutationType = $schema->getMutationType();
|
||||||
if ($mutationType) {
|
if ($mutationType) {
|
||||||
$operationTypes[] = " mutation: {$mutationType->name}";
|
$operationTypes[] = sprintf(' mutation: %s', $mutationType->name);
|
||||||
}
|
}
|
||||||
|
|
||||||
$subscriptionType = $schema->getSubscriptionType();
|
$subscriptionType = $schema->getSubscriptionType();
|
||||||
if ($subscriptionType) {
|
if ($subscriptionType) {
|
||||||
$operationTypes[] = " subscription: {$subscriptionType->name}";
|
$operationTypes[] = sprintf(' subscription: %s', $subscriptionType->name);
|
||||||
}
|
}
|
||||||
|
|
||||||
return "schema {\n" . implode("\n", $operationTypes) . "\n}";
|
return sprintf("schema {\n%s\n}", implode("\n", $operationTypes));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -131,120 +159,6 @@ class SchemaPrinter
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function printType(Type $type, array $options = [])
|
|
||||||
{
|
|
||||||
if ($type instanceof ScalarType) {
|
|
||||||
return self::printScalar($type, $options);
|
|
||||||
} else if ($type instanceof ObjectType) {
|
|
||||||
return self::printObject($type, $options);
|
|
||||||
} else if ($type instanceof InterfaceType) {
|
|
||||||
return self::printInterface($type, $options);
|
|
||||||
} else if ($type instanceof UnionType) {
|
|
||||||
return self::printUnion($type, $options);
|
|
||||||
} else if ($type instanceof EnumType) {
|
|
||||||
return self::printEnum($type, $options);
|
|
||||||
} else if ($type instanceof InputObjectType) {
|
|
||||||
return self::printInputObject($type, $options);
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new Error('Unknown type: ' . Utils::printSafe($type) . '.');
|
|
||||||
}
|
|
||||||
|
|
||||||
private static function printScalar(ScalarType $type, array $options)
|
|
||||||
{
|
|
||||||
return self::printDescription($options, $type) . "scalar {$type->name}";
|
|
||||||
}
|
|
||||||
|
|
||||||
private static function printObject(ObjectType $type, array $options)
|
|
||||||
{
|
|
||||||
$interfaces = $type->getInterfaces();
|
|
||||||
$implementedInterfaces = !empty($interfaces) ?
|
|
||||||
' implements ' . implode(', ', array_map(function($i) {
|
|
||||||
return $i->name;
|
|
||||||
}, $interfaces)) : '';
|
|
||||||
return self::printDescription($options, $type) .
|
|
||||||
"type {$type->name}$implementedInterfaces {\n" .
|
|
||||||
self::printFields($options, $type) . "\n" .
|
|
||||||
"}";
|
|
||||||
}
|
|
||||||
|
|
||||||
private static function printInterface(InterfaceType $type, array $options)
|
|
||||||
{
|
|
||||||
return self::printDescription($options, $type) .
|
|
||||||
"interface {$type->name} {\n" .
|
|
||||||
self::printFields($options, $type) . "\n" .
|
|
||||||
"}";
|
|
||||||
}
|
|
||||||
|
|
||||||
private static function printUnion(UnionType $type, array $options)
|
|
||||||
{
|
|
||||||
return self::printDescription($options, $type) .
|
|
||||||
"union {$type->name} = " . implode(" | ", $type->getTypes());
|
|
||||||
}
|
|
||||||
|
|
||||||
private static function printEnum(EnumType $type, array $options)
|
|
||||||
{
|
|
||||||
return self::printDescription($options, $type) .
|
|
||||||
"enum {$type->name} {\n" .
|
|
||||||
self::printEnumValues($type->getValues(), $options) . "\n" .
|
|
||||||
"}";
|
|
||||||
}
|
|
||||||
|
|
||||||
private static function printEnumValues($values, $options)
|
|
||||||
{
|
|
||||||
return implode("\n", array_map(function($value, $i) use ($options) {
|
|
||||||
return self::printDescription($options, $value, ' ', !$i) . ' ' .
|
|
||||||
$value->name . self::printDeprecated($value);
|
|
||||||
}, $values, array_keys($values)));
|
|
||||||
}
|
|
||||||
|
|
||||||
private static function printInputObject(InputObjectType $type, array $options)
|
|
||||||
{
|
|
||||||
$fields = array_values($type->getFields());
|
|
||||||
return self::printDescription($options, $type) .
|
|
||||||
"input {$type->name} {\n" .
|
|
||||||
implode("\n", array_map(function($f, $i) use ($options) {
|
|
||||||
return self::printDescription($options, $f, ' ', !$i) . ' ' . self::printInputValue($f);
|
|
||||||
}, $fields, array_keys($fields))) . "\n" .
|
|
||||||
"}";
|
|
||||||
}
|
|
||||||
|
|
||||||
private static function printFields($options, $type)
|
|
||||||
{
|
|
||||||
$fields = array_values($type->getFields());
|
|
||||||
return implode("\n", array_map(function($f, $i) use ($options) {
|
|
||||||
return self::printDescription($options, $f, ' ', !$i) . ' ' .
|
|
||||||
$f->name . self::printArgs($options, $f->args, ' ') . ': ' .
|
|
||||||
(string) $f->getType() . self::printDeprecated($f);
|
|
||||||
}, $fields, array_keys($fields)));
|
|
||||||
}
|
|
||||||
|
|
||||||
private static function printArgs($options, $args, $indentation = '')
|
|
||||||
{
|
|
||||||
if (!$args) {
|
|
||||||
return '';
|
|
||||||
}
|
|
||||||
|
|
||||||
// If every arg does not have a description, print them on one line.
|
|
||||||
if (Utils::every($args, function($arg) { return empty($arg->description); })) {
|
|
||||||
return '(' . implode(', ', array_map('self::printInputValue', $args)) . ')';
|
|
||||||
}
|
|
||||||
|
|
||||||
return "(\n" . implode("\n", array_map(function($arg, $i) use ($indentation, $options) {
|
|
||||||
return self::printDescription($options, $arg, ' ' . $indentation, !$i) . ' ' . $indentation .
|
|
||||||
self::printInputValue($arg);
|
|
||||||
}, $args, array_keys($args))) . "\n" . $indentation . ')';
|
|
||||||
}
|
|
||||||
|
|
||||||
private static function printInputValue($arg)
|
|
||||||
{
|
|
||||||
$argDecl = $arg->name . ': ' . (string) $arg->getType();
|
|
||||||
if ($arg->defaultValueExists()) {
|
|
||||||
$argDecl .= ' = ' . Printer::doPrint(AST::astFromValue($arg->defaultValue, $arg->getType()));
|
|
||||||
}
|
|
||||||
return $argDecl;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static function printDirective($directive, $options)
|
private static function printDirective($directive, $options)
|
||||||
{
|
{
|
||||||
return self::printDescription($options, $directive) .
|
return self::printDescription($options, $directive) .
|
||||||
@ -252,22 +166,9 @@ class SchemaPrinter
|
|||||||
' on ' . implode(' | ', $directive->locations);
|
' on ' . implode(' | ', $directive->locations);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static function printDeprecated($fieldOrEnumVal)
|
|
||||||
{
|
|
||||||
$reason = $fieldOrEnumVal->deprecationReason;
|
|
||||||
if (empty($reason)) {
|
|
||||||
return '';
|
|
||||||
}
|
|
||||||
if ($reason === '' || $reason === Directive::DEFAULT_DEPRECATION_REASON) {
|
|
||||||
return ' @deprecated';
|
|
||||||
}
|
|
||||||
return ' @deprecated(reason: ' .
|
|
||||||
Printer::doPrint(AST::astFromValue($reason, Type::string())) . ')';
|
|
||||||
}
|
|
||||||
|
|
||||||
private static function printDescription($options, $def, $indentation = '', $firstInBlock = true)
|
private static function printDescription($options, $def, $indentation = '', $firstInBlock = true)
|
||||||
{
|
{
|
||||||
if (!$def->description) {
|
if (! $def->description) {
|
||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
$lines = self::descriptionLines($def->description, 120 - strlen($indentation));
|
$lines = self::descriptionLines($def->description, 120 - strlen($indentation));
|
||||||
@ -275,13 +176,12 @@ class SchemaPrinter
|
|||||||
return self::printDescriptionWithComments($lines, $indentation, $firstInBlock);
|
return self::printDescriptionWithComments($lines, $indentation, $firstInBlock);
|
||||||
}
|
}
|
||||||
|
|
||||||
$description = ($indentation && !$firstInBlock)
|
$description = ($indentation && ! $firstInBlock)
|
||||||
? "\n" . $indentation . '"""'
|
? "\n" . $indentation . '"""'
|
||||||
: $indentation . '"""';
|
: $indentation . '"""';
|
||||||
|
|
||||||
// In some circumstances, a single line can be used for the description.
|
// In some circumstances, a single line can be used for the description.
|
||||||
if (
|
if (count($lines) === 1 &&
|
||||||
count($lines) === 1 &&
|
|
||||||
mb_strlen($lines[0]) < 70 &&
|
mb_strlen($lines[0]) < 70 &&
|
||||||
substr($lines[0], -1) !== '"'
|
substr($lines[0], -1) !== '"'
|
||||||
) {
|
) {
|
||||||
@ -294,13 +194,13 @@ class SchemaPrinter
|
|||||||
substr($lines[0], 0, 1) === ' ' ||
|
substr($lines[0], 0, 1) === ' ' ||
|
||||||
substr($lines[0], 0, 1) === '\t'
|
substr($lines[0], 0, 1) === '\t'
|
||||||
);
|
);
|
||||||
if (!$hasLeadingSpace) {
|
if (! $hasLeadingSpace) {
|
||||||
$description .= "\n";
|
$description .= "\n";
|
||||||
}
|
}
|
||||||
|
|
||||||
$lineLength = count($lines);
|
$lineLength = count($lines);
|
||||||
for ($i = 0; $i < $lineLength; $i++) {
|
for ($i = 0; $i < $lineLength; $i++) {
|
||||||
if ($i !== 0 || !$hasLeadingSpace) {
|
if ($i !== 0 || ! $hasLeadingSpace) {
|
||||||
$description .= $indentation;
|
$description .= $indentation;
|
||||||
}
|
}
|
||||||
$description .= self::escapeQuote($lines[$i]) . "\n";
|
$description .= self::escapeQuote($lines[$i]) . "\n";
|
||||||
@ -310,29 +210,11 @@ class SchemaPrinter
|
|||||||
return $description;
|
return $description;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static function escapeQuote($line)
|
private static function descriptionLines($description, $maxLen)
|
||||||
{
|
{
|
||||||
return str_replace('"""', '\\"""', $line);
|
$lines = [];
|
||||||
}
|
|
||||||
|
|
||||||
private static function printDescriptionWithComments($lines, $indentation, $firstInBlock)
|
|
||||||
{
|
|
||||||
$description = $indentation && !$firstInBlock ? "\n" : '';
|
|
||||||
foreach ($lines as $line) {
|
|
||||||
if ($line === '') {
|
|
||||||
$description .= $indentation . "#\n";
|
|
||||||
} else {
|
|
||||||
$description .= $indentation . '# ' . $line . "\n";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return $description;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static function descriptionLines($description, $maxLen) {
|
|
||||||
$lines = [];
|
|
||||||
$rawLines = explode("\n", $description);
|
$rawLines = explode("\n", $description);
|
||||||
foreach($rawLines as $line) {
|
foreach ($rawLines as $line) {
|
||||||
if ($line === '') {
|
if ($line === '') {
|
||||||
$lines[] = $line;
|
$lines[] = $line;
|
||||||
} else {
|
} else {
|
||||||
@ -344,6 +226,7 @@ class SchemaPrinter
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return $lines;
|
return $lines;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -352,10 +235,251 @@ class SchemaPrinter
|
|||||||
if (strlen($line) < $maxLen + 5) {
|
if (strlen($line) < $maxLen + 5) {
|
||||||
return [$line];
|
return [$line];
|
||||||
}
|
}
|
||||||
preg_match_all("/((?: |^).{15," . ($maxLen - 40) . "}(?= |$))/", $line, $parts);
|
preg_match_all('/((?: |^).{15,' . ($maxLen - 40) . '}(?= |$))/', $line, $parts);
|
||||||
$parts = $parts[0];
|
$parts = $parts[0];
|
||||||
return array_map(function($part) {
|
|
||||||
return trim($part);
|
return array_map('trim', $parts);
|
||||||
}, $parts);
|
}
|
||||||
|
|
||||||
|
private static function printDescriptionWithComments($lines, $indentation, $firstInBlock)
|
||||||
|
{
|
||||||
|
$description = $indentation && ! $firstInBlock ? "\n" : '';
|
||||||
|
foreach ($lines as $line) {
|
||||||
|
if ($line === '') {
|
||||||
|
$description .= $indentation . "#\n";
|
||||||
|
} else {
|
||||||
|
$description .= $indentation . '# ' . $line . "\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $description;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static function escapeQuote($line)
|
||||||
|
{
|
||||||
|
return str_replace('"""', '\\"""', $line);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static function printArgs($options, $args, $indentation = '')
|
||||||
|
{
|
||||||
|
if (! $args) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
// If every arg does not have a description, print them on one line.
|
||||||
|
if (Utils::every(
|
||||||
|
$args,
|
||||||
|
function ($arg) {
|
||||||
|
return empty($arg->description);
|
||||||
|
}
|
||||||
|
)) {
|
||||||
|
return '(' . implode(', ', array_map('self::printInputValue', $args)) . ')';
|
||||||
|
}
|
||||||
|
|
||||||
|
return sprintf(
|
||||||
|
"(\n%s\n%s)",
|
||||||
|
implode(
|
||||||
|
"\n",
|
||||||
|
array_map(
|
||||||
|
function ($arg, $i) use ($indentation, $options) {
|
||||||
|
return self::printDescription($options, $arg, ' ' . $indentation, ! $i) . ' ' . $indentation .
|
||||||
|
self::printInputValue($arg);
|
||||||
|
},
|
||||||
|
$args,
|
||||||
|
array_keys($args)
|
||||||
|
)
|
||||||
|
),
|
||||||
|
$indentation
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static function printInputValue($arg)
|
||||||
|
{
|
||||||
|
$argDecl = $arg->name . ': ' . (string) $arg->getType();
|
||||||
|
if ($arg->defaultValueExists()) {
|
||||||
|
$argDecl .= ' = ' . Printer::doPrint(AST::astFromValue($arg->defaultValue, $arg->getType()));
|
||||||
|
}
|
||||||
|
|
||||||
|
return $argDecl;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param bool[] $options
|
||||||
|
*/
|
||||||
|
public static function printType(Type $type, array $options = [])
|
||||||
|
{
|
||||||
|
if ($type instanceof ScalarType) {
|
||||||
|
return self::printScalar($type, $options);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($type instanceof ObjectType) {
|
||||||
|
return self::printObject($type, $options);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($type instanceof InterfaceType) {
|
||||||
|
return self::printInterface($type, $options);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($type instanceof UnionType) {
|
||||||
|
return self::printUnion($type, $options);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($type instanceof EnumType) {
|
||||||
|
return self::printEnum($type, $options);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($type instanceof InputObjectType) {
|
||||||
|
return self::printInputObject($type, $options);
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new Error(sprintf('Unknown type: %s.', Utils::printSafe($type)));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param bool[] $options
|
||||||
|
*/
|
||||||
|
private static function printScalar(ScalarType $type, array $options)
|
||||||
|
{
|
||||||
|
return sprintf('%sscalar %s', self::printDescription($options, $type), $type->name);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param bool[] $options
|
||||||
|
*/
|
||||||
|
private static function printObject(ObjectType $type, array $options)
|
||||||
|
{
|
||||||
|
$interfaces = $type->getInterfaces();
|
||||||
|
$implementedInterfaces = ! empty($interfaces) ?
|
||||||
|
' implements ' . implode(
|
||||||
|
', ',
|
||||||
|
array_map(
|
||||||
|
function ($i) {
|
||||||
|
return $i->name;
|
||||||
|
},
|
||||||
|
$interfaces
|
||||||
|
)
|
||||||
|
) : '';
|
||||||
|
|
||||||
|
return self::printDescription($options, $type) .
|
||||||
|
sprintf("type %s%s {\n%s\n}", $type->name, $implementedInterfaces, self::printFields($options, $type));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param bool[] $options
|
||||||
|
*/
|
||||||
|
private static function printFields($options, $type)
|
||||||
|
{
|
||||||
|
$fields = array_values($type->getFields());
|
||||||
|
|
||||||
|
return implode(
|
||||||
|
"\n",
|
||||||
|
array_map(
|
||||||
|
function ($f, $i) use ($options) {
|
||||||
|
return self::printDescription($options, $f, ' ', ! $i) . ' ' .
|
||||||
|
$f->name . self::printArgs($options, $f->args, ' ') . ': ' .
|
||||||
|
(string) $f->getType() . self::printDeprecated($f);
|
||||||
|
},
|
||||||
|
$fields,
|
||||||
|
array_keys($fields)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static function printDeprecated($fieldOrEnumVal)
|
||||||
|
{
|
||||||
|
$reason = $fieldOrEnumVal->deprecationReason;
|
||||||
|
if (empty($reason)) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
if ($reason === '' || $reason === Directive::DEFAULT_DEPRECATION_REASON) {
|
||||||
|
return ' @deprecated';
|
||||||
|
}
|
||||||
|
|
||||||
|
return ' @deprecated(reason: ' .
|
||||||
|
Printer::doPrint(AST::astFromValue($reason, Type::string())) . ')';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param bool[] $options
|
||||||
|
*/
|
||||||
|
private static function printInterface(InterfaceType $type, array $options)
|
||||||
|
{
|
||||||
|
return self::printDescription($options, $type) .
|
||||||
|
sprintf("interface %s {\n%s\n}", $type->name, self::printFields($options, $type));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param bool[] $options
|
||||||
|
*/
|
||||||
|
private static function printUnion(UnionType $type, array $options)
|
||||||
|
{
|
||||||
|
return self::printDescription($options, $type) .
|
||||||
|
sprintf('union %s = %s', $type->name, implode(' | ', $type->getTypes()));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param bool[] $options
|
||||||
|
*/
|
||||||
|
private static function printEnum(EnumType $type, array $options)
|
||||||
|
{
|
||||||
|
return self::printDescription($options, $type) .
|
||||||
|
sprintf("enum %s {\n%s\n}", $type->name, self::printEnumValues($type->getValues(), $options));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param bool[] $options
|
||||||
|
*/
|
||||||
|
private static function printEnumValues($values, $options)
|
||||||
|
{
|
||||||
|
return implode(
|
||||||
|
"\n",
|
||||||
|
array_map(
|
||||||
|
function ($value, $i) use ($options) {
|
||||||
|
return self::printDescription($options, $value, ' ', ! $i) . ' ' .
|
||||||
|
$value->name . self::printDeprecated($value);
|
||||||
|
},
|
||||||
|
$values,
|
||||||
|
array_keys($values)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param bool[] $options
|
||||||
|
*/
|
||||||
|
private static function printInputObject(InputObjectType $type, array $options)
|
||||||
|
{
|
||||||
|
$fields = array_values($type->getFields());
|
||||||
|
|
||||||
|
return self::printDescription($options, $type) .
|
||||||
|
sprintf(
|
||||||
|
"input %s {\n%s\n}",
|
||||||
|
$type->name,
|
||||||
|
implode(
|
||||||
|
"\n",
|
||||||
|
array_map(
|
||||||
|
function ($f, $i) use ($options) {
|
||||||
|
return self::printDescription($options, $f, ' ', ! $i) . ' ' . self::printInputValue($f);
|
||||||
|
},
|
||||||
|
$fields,
|
||||||
|
array_keys($fields)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @api
|
||||||
|
* @param bool[] $options
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public static function printIntrosepctionSchema(Schema $schema, array $options = [])
|
||||||
|
{
|
||||||
|
return self::printFilteredSchema(
|
||||||
|
$schema,
|
||||||
|
[Directive::class, 'isSpecifiedDirective'],
|
||||||
|
[Introspection::class, 'isIntrospectionType'],
|
||||||
|
$options
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,21 +1,22 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace GraphQL\Utils;
|
namespace GraphQL\Utils;
|
||||||
|
|
||||||
use GraphQL\Type\Schema;
|
|
||||||
use GraphQL\Type\Definition\AbstractType;
|
use GraphQL\Type\Definition\AbstractType;
|
||||||
use GraphQL\Type\Definition\CompositeType;
|
use GraphQL\Type\Definition\CompositeType;
|
||||||
use GraphQL\Type\Definition\ListOfType;
|
use GraphQL\Type\Definition\ListOfType;
|
||||||
use GraphQL\Type\Definition\NonNull;
|
use GraphQL\Type\Definition\NonNull;
|
||||||
use GraphQL\Type\Definition\ObjectType;
|
use GraphQL\Type\Definition\ObjectType;
|
||||||
use GraphQL\Type\Definition\Type;
|
use GraphQL\Type\Definition\Type;
|
||||||
|
use GraphQL\Type\Schema;
|
||||||
|
|
||||||
class TypeComparators
|
class TypeComparators
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* Provided two types, return true if the types are equal (invariant).
|
* Provided two types, return true if the types are equal (invariant).
|
||||||
*
|
*
|
||||||
* @param Type $typeA
|
|
||||||
* @param Type $typeB
|
|
||||||
* @return bool
|
* @return bool
|
||||||
*/
|
*/
|
||||||
public static function isEqualType(Type $typeA, Type $typeB)
|
public static function isEqualType(Type $typeA, Type $typeB)
|
||||||
@ -43,12 +44,11 @@ class TypeComparators
|
|||||||
* Provided a type and a super type, return true if the first type is either
|
* Provided a type and a super type, return true if the first type is either
|
||||||
* equal or a subset of the second super type (covariant).
|
* equal or a subset of the second super type (covariant).
|
||||||
*
|
*
|
||||||
* @param Schema $schema
|
* @param AbstractType $maybeSubType
|
||||||
* @param Type $maybeSubType
|
* @param AbstractType $superType
|
||||||
* @param Type $superType
|
|
||||||
* @return bool
|
* @return bool
|
||||||
*/
|
*/
|
||||||
static function isTypeSubTypeOf(Schema $schema, $maybeSubType, $superType)
|
public static function isTypeSubTypeOf(Schema $schema, $maybeSubType, $superType)
|
||||||
{
|
{
|
||||||
// Equivalent type is a valid subtype
|
// Equivalent type is a valid subtype
|
||||||
if ($maybeSubType === $superType) {
|
if ($maybeSubType === $superType) {
|
||||||
@ -60,8 +60,11 @@ class TypeComparators
|
|||||||
if ($maybeSubType instanceof NonNull) {
|
if ($maybeSubType instanceof NonNull) {
|
||||||
return self::isTypeSubTypeOf($schema, $maybeSubType->getWrappedType(), $superType->getWrappedType());
|
return self::isTypeSubTypeOf($schema, $maybeSubType->getWrappedType(), $superType->getWrappedType());
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
} else if ($maybeSubType instanceof NonNull) {
|
}
|
||||||
|
|
||||||
|
if ($maybeSubType instanceof NonNull) {
|
||||||
// If superType is nullable, maybeSubType may be non-null.
|
// If superType is nullable, maybeSubType may be non-null.
|
||||||
return self::isTypeSubTypeOf($schema, $maybeSubType->getWrappedType(), $superType);
|
return self::isTypeSubTypeOf($schema, $maybeSubType->getWrappedType(), $superType);
|
||||||
}
|
}
|
||||||
@ -71,15 +74,24 @@ class TypeComparators
|
|||||||
if ($maybeSubType instanceof ListOfType) {
|
if ($maybeSubType instanceof ListOfType) {
|
||||||
return self::isTypeSubTypeOf($schema, $maybeSubType->getWrappedType(), $superType->getWrappedType());
|
return self::isTypeSubTypeOf($schema, $maybeSubType->getWrappedType(), $superType->getWrappedType());
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
} else if ($maybeSubType instanceof ListOfType) {
|
}
|
||||||
|
|
||||||
|
if ($maybeSubType instanceof ListOfType) {
|
||||||
// If superType is not a list, maybeSubType must also be not a list.
|
// If superType is not a list, maybeSubType must also be not a list.
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// If superType type is an abstract type, maybeSubType type may be a currently
|
// If superType type is an abstract type, maybeSubType type may be a currently
|
||||||
// possible object type.
|
// possible object type.
|
||||||
if (Type::isAbstractType($superType) && $maybeSubType instanceof ObjectType && $schema->isPossibleType($superType, $maybeSubType)) {
|
if (Type::isAbstractType($superType) &&
|
||||||
|
$maybeSubType instanceof ObjectType &&
|
||||||
|
$schema->isPossibleType(
|
||||||
|
$superType,
|
||||||
|
$maybeSubType
|
||||||
|
)
|
||||||
|
) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -96,12 +108,9 @@ class TypeComparators
|
|||||||
*
|
*
|
||||||
* This function is commutative.
|
* This function is commutative.
|
||||||
*
|
*
|
||||||
* @param Schema $schema
|
|
||||||
* @param CompositeType $typeA
|
|
||||||
* @param CompositeType $typeB
|
|
||||||
* @return bool
|
* @return bool
|
||||||
*/
|
*/
|
||||||
static function doTypesOverlap(Schema $schema, CompositeType $typeA, CompositeType $typeB)
|
public static function doTypesOverlap(Schema $schema, CompositeType $typeA, CompositeType $typeB)
|
||||||
{
|
{
|
||||||
// Equivalent types overlap
|
// Equivalent types overlap
|
||||||
if ($typeA === $typeB) {
|
if ($typeA === $typeB) {
|
||||||
@ -117,6 +126,7 @@ class TypeComparators
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,4 +1,7 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace GraphQL\Utils;
|
namespace GraphQL\Utils;
|
||||||
|
|
||||||
use GraphQL\Error\InvariantViolation;
|
use GraphQL\Error\InvariantViolation;
|
||||||
@ -9,7 +12,6 @@ use GraphQL\Language\AST\NamedTypeNode;
|
|||||||
use GraphQL\Language\AST\Node;
|
use GraphQL\Language\AST\Node;
|
||||||
use GraphQL\Language\AST\NodeKind;
|
use GraphQL\Language\AST\NodeKind;
|
||||||
use GraphQL\Language\AST\NonNullTypeNode;
|
use GraphQL\Language\AST\NonNullTypeNode;
|
||||||
use GraphQL\Type\Schema;
|
|
||||||
use GraphQL\Type\Definition\CompositeType;
|
use GraphQL\Type\Definition\CompositeType;
|
||||||
use GraphQL\Type\Definition\Directive;
|
use GraphQL\Type\Definition\Directive;
|
||||||
use GraphQL\Type\Definition\EnumType;
|
use GraphQL\Type\Definition\EnumType;
|
||||||
@ -24,13 +26,70 @@ 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\Type\Introspection;
|
use GraphQL\Type\Introspection;
|
||||||
|
use GraphQL\Type\Schema;
|
||||||
|
use function array_map;
|
||||||
|
use function array_merge;
|
||||||
|
use function array_pop;
|
||||||
|
use function count;
|
||||||
|
use function sprintf;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Class TypeInfo
|
* Class TypeInfo
|
||||||
* @package GraphQL\Utils
|
|
||||||
*/
|
*/
|
||||||
class TypeInfo
|
class TypeInfo
|
||||||
{
|
{
|
||||||
|
/** @var Schema */
|
||||||
|
private $schema;
|
||||||
|
|
||||||
|
/** @var \SplStack<OutputType> */
|
||||||
|
private $typeStack;
|
||||||
|
|
||||||
|
/** @var \SplStack<CompositeType> */
|
||||||
|
private $parentTypeStack;
|
||||||
|
|
||||||
|
/** @var \SplStack<InputType> */
|
||||||
|
private $inputTypeStack;
|
||||||
|
|
||||||
|
/** @var \SplStack<FieldDefinition> */
|
||||||
|
private $fieldDefStack;
|
||||||
|
|
||||||
|
/** @var Directive */
|
||||||
|
private $directive;
|
||||||
|
|
||||||
|
/** @var FieldArgument */
|
||||||
|
private $argument;
|
||||||
|
|
||||||
|
/** @var mixed */
|
||||||
|
private $enumValue;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param Type|null $initialType
|
||||||
|
*/
|
||||||
|
public function __construct(Schema $schema, $initialType = null)
|
||||||
|
{
|
||||||
|
$this->schema = $schema;
|
||||||
|
$this->typeStack = [];
|
||||||
|
$this->parentTypeStack = [];
|
||||||
|
$this->inputTypeStack = [];
|
||||||
|
$this->fieldDefStack = [];
|
||||||
|
if (! $initialType) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Type::isInputType($initialType)) {
|
||||||
|
$this->inputTypeStack[] = $initialType;
|
||||||
|
}
|
||||||
|
if (Type::isCompositeType($initialType)) {
|
||||||
|
$this->parentTypeStack[] = $initialType;
|
||||||
|
}
|
||||||
|
if (! Type::isOutputType($initialType)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->typeStack[] = $initialType;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @deprecated moved to GraphQL\Utils\TypeComparators
|
* @deprecated moved to GraphQL\Utils\TypeComparators
|
||||||
*/
|
*/
|
||||||
@ -42,7 +101,7 @@ class TypeInfo
|
|||||||
/**
|
/**
|
||||||
* @deprecated moved to GraphQL\Utils\TypeComparators
|
* @deprecated moved to GraphQL\Utils\TypeComparators
|
||||||
*/
|
*/
|
||||||
static function isTypeSubTypeOf(Schema $schema, Type $maybeSubType, Type $superType)
|
public static function isTypeSubTypeOf(Schema $schema, Type $maybeSubType, Type $superType)
|
||||||
{
|
{
|
||||||
return TypeComparators::isTypeSubTypeOf($schema, $maybeSubType, $superType);
|
return TypeComparators::isTypeSubTypeOf($schema, $maybeSubType, $superType);
|
||||||
}
|
}
|
||||||
@ -50,22 +109,11 @@ class TypeInfo
|
|||||||
/**
|
/**
|
||||||
* @deprecated moved to GraphQL\Utils\TypeComparators
|
* @deprecated moved to GraphQL\Utils\TypeComparators
|
||||||
*/
|
*/
|
||||||
static function doTypesOverlap(Schema $schema, CompositeType $typeA, CompositeType $typeB)
|
public static function doTypesOverlap(Schema $schema, CompositeType $typeA, CompositeType $typeB)
|
||||||
{
|
{
|
||||||
return TypeComparators::doTypesOverlap($schema, $typeA, $typeB);
|
return TypeComparators::doTypesOverlap($schema, $typeA, $typeB);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @param Schema $schema
|
|
||||||
* @param NamedTypeNode|ListTypeNode|NonNullTypeNode $inputTypeNode
|
|
||||||
* @return Type|null
|
|
||||||
* @throws InvariantViolation
|
|
||||||
*/
|
|
||||||
public static function typeFromAST(Schema $schema, $inputTypeNode)
|
|
||||||
{
|
|
||||||
return AST::typeFromAST($schema, $inputTypeNode);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Given root type scans through all fields to find nested types. Returns array where keys are for type name
|
* Given root type scans through all fields to find nested types. Returns array where keys are for type name
|
||||||
* and value contains corresponding type instance.
|
* and value contains corresponding type instance.
|
||||||
@ -77,37 +125,39 @@ class TypeInfo
|
|||||||
* ...
|
* ...
|
||||||
* ]
|
* ]
|
||||||
*
|
*
|
||||||
* @param Type $type
|
* @param Type|null $type
|
||||||
* @param array|null $typeMap
|
* @param Type[]|null $typeMap
|
||||||
* @return array
|
* @return Type[]|null
|
||||||
*/
|
*/
|
||||||
public static function extractTypes($type, array $typeMap = null)
|
public static function extractTypes($type, ?array $typeMap = null)
|
||||||
{
|
{
|
||||||
if (!$typeMap) {
|
if (! $typeMap) {
|
||||||
$typeMap = [];
|
$typeMap = [];
|
||||||
}
|
}
|
||||||
if (!$type) {
|
if (! $type) {
|
||||||
return $typeMap;
|
return $typeMap;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($type instanceof WrappingType) {
|
if ($type instanceof WrappingType) {
|
||||||
return self::extractTypes($type->getWrappedType(true), $typeMap);
|
return self::extractTypes($type->getWrappedType(true), $typeMap);
|
||||||
}
|
}
|
||||||
if (!$type instanceof Type) {
|
if (! $type instanceof Type) {
|
||||||
Warning::warnOnce(
|
Warning::warnOnce(
|
||||||
'One of the schema types is not a valid type definition instance. '.
|
'One of the schema types is not a valid type definition instance. ' .
|
||||||
'Try running $schema->assertValid() to find out the cause of this warning.',
|
'Try running $schema->assertValid() to find out the cause of this warning.',
|
||||||
Warning::WARNING_NOT_A_TYPE
|
Warning::WARNING_NOT_A_TYPE
|
||||||
);
|
);
|
||||||
|
|
||||||
return $typeMap;
|
return $typeMap;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!empty($typeMap[$type->name])) {
|
if (! empty($typeMap[$type->name])) {
|
||||||
Utils::invariant(
|
Utils::invariant(
|
||||||
$typeMap[$type->name] === $type,
|
$typeMap[$type->name] === $type,
|
||||||
"Schema must contain unique named types but contains multiple types named \"$type\" ".
|
sprintf('Schema must contain unique named types but contains multiple types named "%s" ', $type) .
|
||||||
"(see http://webonyx.github.io/graphql-php/type-system/#type-registry)."
|
'(see http://webonyx.github.io/graphql-php/type-system/#type-registry).'
|
||||||
);
|
);
|
||||||
|
|
||||||
return $typeMap;
|
return $typeMap;
|
||||||
}
|
}
|
||||||
$typeMap[$type->name] = $type;
|
$typeMap[$type->name] = $type;
|
||||||
@ -122,8 +172,14 @@ class TypeInfo
|
|||||||
}
|
}
|
||||||
if ($type instanceof ObjectType || $type instanceof InterfaceType) {
|
if ($type instanceof ObjectType || $type instanceof InterfaceType) {
|
||||||
foreach ((array) $type->getFields() as $fieldName => $field) {
|
foreach ((array) $type->getFields() as $fieldName => $field) {
|
||||||
if (!empty($field->args)) {
|
if (! empty($field->args)) {
|
||||||
$fieldArgTypes = array_map(function(FieldArgument $arg) { return $arg->getType(); }, $field->args);
|
$fieldArgTypes = array_map(
|
||||||
|
function (FieldArgument $arg) {
|
||||||
|
return $arg->getType();
|
||||||
|
},
|
||||||
|
$field->args
|
||||||
|
);
|
||||||
|
|
||||||
$nestedTypes = array_merge($nestedTypes, $fieldArgTypes);
|
$nestedTypes = array_merge($nestedTypes, $fieldArgTypes);
|
||||||
}
|
}
|
||||||
$nestedTypes[] = $field->getType();
|
$nestedTypes[] = $field->getType();
|
||||||
@ -137,9 +193,169 @@ class TypeInfo
|
|||||||
foreach ($nestedTypes as $type) {
|
foreach ($nestedTypes as $type) {
|
||||||
$typeMap = self::extractTypes($type, $typeMap);
|
$typeMap = self::extractTypes($type, $typeMap);
|
||||||
}
|
}
|
||||||
|
|
||||||
return $typeMap;
|
return $typeMap;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return InputType|null
|
||||||
|
*/
|
||||||
|
public function getParentInputType()
|
||||||
|
{
|
||||||
|
$inputTypeStackLength = count($this->inputTypeStack);
|
||||||
|
if ($inputTypeStackLength > 1) {
|
||||||
|
return $this->inputTypeStack[$inputTypeStackLength - 2];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return FieldArgument|null
|
||||||
|
*/
|
||||||
|
public function getArgument()
|
||||||
|
{
|
||||||
|
return $this->argument;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
public function getEnumValue()
|
||||||
|
{
|
||||||
|
return $this->enumValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function enter(Node $node)
|
||||||
|
{
|
||||||
|
$schema = $this->schema;
|
||||||
|
|
||||||
|
// Note: many of the types below are explicitly typed as "mixed" to drop
|
||||||
|
// any assumptions of a valid schema to ensure runtime types are properly
|
||||||
|
// checked before continuing since TypeInfo is used as part of validation
|
||||||
|
// which occurs before guarantees of schema and document validity.
|
||||||
|
switch ($node->kind) {
|
||||||
|
case NodeKind::SELECTION_SET:
|
||||||
|
$namedType = Type::getNamedType($this->getType());
|
||||||
|
$this->parentTypeStack[] = Type::isCompositeType($namedType) ? $namedType : null;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case NodeKind::FIELD:
|
||||||
|
$parentType = $this->getParentType();
|
||||||
|
$fieldDef = null;
|
||||||
|
if ($parentType) {
|
||||||
|
$fieldDef = self::getFieldDefinition($schema, $parentType, $node);
|
||||||
|
}
|
||||||
|
$fieldType = null;
|
||||||
|
if ($fieldDef) {
|
||||||
|
$fieldType = $fieldDef->getType();
|
||||||
|
}
|
||||||
|
$this->fieldDefStack[] = $fieldDef;
|
||||||
|
$this->typeStack[] = Type::isOutputType($fieldType) ? $fieldType : null;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case NodeKind::DIRECTIVE:
|
||||||
|
$this->directive = $schema->getDirective($node->name->value);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case NodeKind::OPERATION_DEFINITION:
|
||||||
|
$type = null;
|
||||||
|
if ($node->operation === 'query') {
|
||||||
|
$type = $schema->getQueryType();
|
||||||
|
} elseif ($node->operation === 'mutation') {
|
||||||
|
$type = $schema->getMutationType();
|
||||||
|
} elseif ($node->operation === 'subscription') {
|
||||||
|
$type = $schema->getSubscriptionType();
|
||||||
|
}
|
||||||
|
$this->typeStack[] = Type::isOutputType($type) ? $type : null;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case NodeKind::INLINE_FRAGMENT:
|
||||||
|
case NodeKind::FRAGMENT_DEFINITION:
|
||||||
|
$typeConditionNode = $node->typeCondition;
|
||||||
|
$outputType = $typeConditionNode ? self::typeFromAST(
|
||||||
|
$schema,
|
||||||
|
$typeConditionNode
|
||||||
|
) : Type::getNamedType($this->getType());
|
||||||
|
$this->typeStack[] = Type::isOutputType($outputType) ? $outputType : null;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case NodeKind::VARIABLE_DEFINITION:
|
||||||
|
$inputType = self::typeFromAST($schema, $node->type);
|
||||||
|
$this->inputTypeStack[] = Type::isInputType($inputType) ? $inputType : null; // push
|
||||||
|
break;
|
||||||
|
|
||||||
|
case NodeKind::ARGUMENT:
|
||||||
|
$fieldOrDirective = $this->getDirective() ?: $this->getFieldDef();
|
||||||
|
$argDef = $argType = null;
|
||||||
|
if ($fieldOrDirective) {
|
||||||
|
$argDef = Utils::find(
|
||||||
|
$fieldOrDirective->args,
|
||||||
|
function ($arg) use ($node) {
|
||||||
|
return $arg->name === $node->name->value;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
if ($argDef) {
|
||||||
|
$argType = $argDef->getType();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$this->argument = $argDef;
|
||||||
|
$this->inputTypeStack[] = Type::isInputType($argType) ? $argType : null;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case NodeKind::LST:
|
||||||
|
$listType = Type::getNullableType($this->getInputType());
|
||||||
|
$itemType = $listType instanceof ListOfType
|
||||||
|
? $listType->getWrappedType()
|
||||||
|
: $listType;
|
||||||
|
$this->inputTypeStack[] = Type::isInputType($itemType) ? $itemType : null;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case NodeKind::OBJECT_FIELD:
|
||||||
|
$objectType = Type::getNamedType($this->getInputType());
|
||||||
|
$fieldType = null;
|
||||||
|
$inputFieldType = null;
|
||||||
|
if ($objectType instanceof InputObjectType) {
|
||||||
|
$tmp = $objectType->getFields();
|
||||||
|
$inputField = $tmp[$node->name->value] ?? null;
|
||||||
|
$inputFieldType = $inputField ? $inputField->getType() : null;
|
||||||
|
}
|
||||||
|
$this->inputTypeStack[] = Type::isInputType($inputFieldType) ? $inputFieldType : null;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case NodeKind::ENUM:
|
||||||
|
$enumType = Type::getNamedType($this->getInputType());
|
||||||
|
$enumValue = null;
|
||||||
|
if ($enumType instanceof EnumType) {
|
||||||
|
$enumValue = $enumType->getValue($node->value);
|
||||||
|
}
|
||||||
|
$this->enumValue = $enumValue;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return Type
|
||||||
|
*/
|
||||||
|
public function getType()
|
||||||
|
{
|
||||||
|
if (! empty($this->typeStack)) {
|
||||||
|
return $this->typeStack[count($this->typeStack) - 1];
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return Type
|
||||||
|
*/
|
||||||
|
public function getParentType()
|
||||||
|
{
|
||||||
|
if (! empty($this->parentTypeStack)) {
|
||||||
|
return $this->parentTypeStack[count($this->parentTypeStack) - 1];
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Not exactly the same as the executor's definition of getFieldDef, in this
|
* Not exactly the same as the executor's definition of getFieldDef, in this
|
||||||
* statically evaluated environment we do not always have an Object type,
|
* statically evaluated environment we do not always have an Object type,
|
||||||
@ -147,9 +363,9 @@ class TypeInfo
|
|||||||
*
|
*
|
||||||
* @return FieldDefinition
|
* @return FieldDefinition
|
||||||
*/
|
*/
|
||||||
static private function getFieldDefinition(Schema $schema, Type $parentType, FieldNode $fieldNode)
|
private static function getFieldDefinition(Schema $schema, Type $parentType, FieldNode $fieldNode)
|
||||||
{
|
{
|
||||||
$name = $fieldNode->name->value;
|
$name = $fieldNode->name->value;
|
||||||
$schemaMeta = Introspection::schemaMetaFieldDef();
|
$schemaMeta = Introspection::schemaMetaFieldDef();
|
||||||
if ($name === $schemaMeta->name && $schema->getQueryType() === $parentType) {
|
if ($name === $schemaMeta->name && $schema->getQueryType() === $parentType) {
|
||||||
return $schemaMeta;
|
return $schemaMeta;
|
||||||
@ -166,263 +382,56 @@ class TypeInfo
|
|||||||
if ($parentType instanceof ObjectType ||
|
if ($parentType instanceof ObjectType ||
|
||||||
$parentType instanceof InterfaceType) {
|
$parentType instanceof InterfaceType) {
|
||||||
$fields = $parentType->getFields();
|
$fields = $parentType->getFields();
|
||||||
return isset($fields[$name]) ? $fields[$name] : null;
|
|
||||||
|
return $fields[$name] ?? null;
|
||||||
}
|
}
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @var Schema
|
|
||||||
*/
|
|
||||||
private $schema;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @var \SplStack<OutputType>
|
|
||||||
*/
|
|
||||||
private $typeStack;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @var \SplStack<CompositeType>
|
|
||||||
*/
|
|
||||||
private $parentTypeStack;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @var \SplStack<InputType>
|
|
||||||
*/
|
|
||||||
private $inputTypeStack;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @var \SplStack<FieldDefinition>
|
|
||||||
*/
|
|
||||||
private $fieldDefStack;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @var Directive
|
|
||||||
*/
|
|
||||||
private $directive;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @var FieldArgument
|
|
||||||
*/
|
|
||||||
private $argument;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @var mixed
|
|
||||||
*/
|
|
||||||
private $enumValue;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* TypeInfo constructor.
|
|
||||||
* @param Schema $schema
|
|
||||||
* @param Type|null $initialType
|
|
||||||
*/
|
|
||||||
public function __construct(Schema $schema, $initialType = null)
|
|
||||||
{
|
|
||||||
$this->schema = $schema;
|
|
||||||
$this->typeStack = [];
|
|
||||||
$this->parentTypeStack = [];
|
|
||||||
$this->inputTypeStack = [];
|
|
||||||
$this->fieldDefStack = [];
|
|
||||||
if ($initialType) {
|
|
||||||
if (Type::isInputType($initialType)) {
|
|
||||||
$this->inputTypeStack[] = $initialType;
|
|
||||||
}
|
|
||||||
if (Type::isCompositeType($initialType)) {
|
|
||||||
$this->parentTypeStack[] = $initialType;
|
|
||||||
}
|
|
||||||
if (Type::isOutputType($initialType)) {
|
|
||||||
$this->typeStack[] = $initialType;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return Type
|
|
||||||
*/
|
|
||||||
function getType()
|
|
||||||
{
|
|
||||||
if (!empty($this->typeStack)) {
|
|
||||||
return $this->typeStack[count($this->typeStack) - 1];
|
|
||||||
}
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return Type
|
* @param NamedTypeNode|ListTypeNode|NonNullTypeNode $inputTypeNode
|
||||||
|
* @return Type|null
|
||||||
|
* @throws InvariantViolation
|
||||||
*/
|
*/
|
||||||
function getParentType()
|
public static function typeFromAST(Schema $schema, $inputTypeNode)
|
||||||
{
|
{
|
||||||
if (!empty($this->parentTypeStack)) {
|
return AST::typeFromAST($schema, $inputTypeNode);
|
||||||
return $this->parentTypeStack[count($this->parentTypeStack) - 1];
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return Directive|null
|
||||||
|
*/
|
||||||
|
public function getDirective()
|
||||||
|
{
|
||||||
|
return $this->directive;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return FieldDefinition
|
||||||
|
*/
|
||||||
|
public function getFieldDef()
|
||||||
|
{
|
||||||
|
if (! empty($this->fieldDefStack)) {
|
||||||
|
return $this->fieldDefStack[count($this->fieldDefStack) - 1];
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return InputType
|
* @return InputType
|
||||||
*/
|
*/
|
||||||
function getInputType()
|
public function getInputType()
|
||||||
{
|
{
|
||||||
if (!empty($this->inputTypeStack)) {
|
if (! empty($this->inputTypeStack)) {
|
||||||
return $this->inputTypeStack[count($this->inputTypeStack) - 1];
|
return $this->inputTypeStack[count($this->inputTypeStack) - 1];
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
public function leave(Node $node)
|
||||||
* @return InputType|null
|
|
||||||
*/
|
|
||||||
public function getParentInputType()
|
|
||||||
{
|
|
||||||
$inputTypeStackLength = count($this->inputTypeStack);
|
|
||||||
if ($inputTypeStackLength > 1) {
|
|
||||||
return $this->inputTypeStack[$inputTypeStackLength - 2];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return FieldDefinition
|
|
||||||
*/
|
|
||||||
function getFieldDef()
|
|
||||||
{
|
|
||||||
if (!empty($this->fieldDefStack)) {
|
|
||||||
return $this->fieldDefStack[count($this->fieldDefStack) - 1];
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return Directive|null
|
|
||||||
*/
|
|
||||||
function getDirective()
|
|
||||||
{
|
|
||||||
return $this->directive;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return FieldArgument|null
|
|
||||||
*/
|
|
||||||
function getArgument()
|
|
||||||
{
|
|
||||||
return $this->argument;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return mixed
|
|
||||||
*/
|
|
||||||
function getEnumValue()
|
|
||||||
{
|
|
||||||
return $this->enumValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param Node $node
|
|
||||||
*/
|
|
||||||
function enter(Node $node)
|
|
||||||
{
|
|
||||||
$schema = $this->schema;
|
|
||||||
|
|
||||||
// Note: many of the types below are explicitly typed as "mixed" to drop
|
|
||||||
// any assumptions of a valid schema to ensure runtime types are properly
|
|
||||||
// checked before continuing since TypeInfo is used as part of validation
|
|
||||||
// which occurs before guarantees of schema and document validity.
|
|
||||||
switch ($node->kind) {
|
|
||||||
case NodeKind::SELECTION_SET:
|
|
||||||
$namedType = Type::getNamedType($this->getType());
|
|
||||||
$this->parentTypeStack[] = Type::isCompositeType($namedType) ? $namedType : null;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case NodeKind::FIELD:
|
|
||||||
$parentType = $this->getParentType();
|
|
||||||
$fieldDef = null;
|
|
||||||
if ($parentType) {
|
|
||||||
$fieldDef = self::getFieldDefinition($schema, $parentType, $node);
|
|
||||||
}
|
|
||||||
$fieldType = null;
|
|
||||||
if ($fieldDef) {
|
|
||||||
$fieldType = $fieldDef->getType();
|
|
||||||
}
|
|
||||||
$this->fieldDefStack[] = $fieldDef;
|
|
||||||
$this->typeStack[] = Type::isOutputType($fieldType) ? $fieldType : null;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case NodeKind::DIRECTIVE:
|
|
||||||
$this->directive = $schema->getDirective($node->name->value);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case NodeKind::OPERATION_DEFINITION:
|
|
||||||
$type = null;
|
|
||||||
if ($node->operation === 'query') {
|
|
||||||
$type = $schema->getQueryType();
|
|
||||||
} else if ($node->operation === 'mutation') {
|
|
||||||
$type = $schema->getMutationType();
|
|
||||||
} else if ($node->operation === 'subscription') {
|
|
||||||
$type = $schema->getSubscriptionType();
|
|
||||||
}
|
|
||||||
$this->typeStack[] = Type::isOutputType($type) ? $type : null;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case NodeKind::INLINE_FRAGMENT:
|
|
||||||
case NodeKind::FRAGMENT_DEFINITION:
|
|
||||||
$typeConditionNode = $node->typeCondition;
|
|
||||||
$outputType = $typeConditionNode ? self::typeFromAST($schema, $typeConditionNode) : Type::getNamedType($this->getType());
|
|
||||||
$this->typeStack[] = Type::isOutputType($outputType) ? $outputType : null;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case NodeKind::VARIABLE_DEFINITION:
|
|
||||||
$inputType = self::typeFromAST($schema, $node->type);
|
|
||||||
$this->inputTypeStack[] = Type::isInputType($inputType) ? $inputType : null; // push
|
|
||||||
break;
|
|
||||||
|
|
||||||
case NodeKind::ARGUMENT:
|
|
||||||
$fieldOrDirective = $this->getDirective() ?: $this->getFieldDef();
|
|
||||||
$argDef = $argType = null;
|
|
||||||
if ($fieldOrDirective) {
|
|
||||||
$argDef = Utils::find($fieldOrDirective->args, function($arg) use ($node) {return $arg->name === $node->name->value;});
|
|
||||||
if ($argDef) {
|
|
||||||
$argType = $argDef->getType();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
$this->argument = $argDef;
|
|
||||||
$this->inputTypeStack[] = Type::isInputType($argType) ? $argType : null;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case NodeKind::LST:
|
|
||||||
$listType = Type::getNullableType($this->getInputType());
|
|
||||||
$itemType = $listType instanceof ListOfType
|
|
||||||
? $listType->getWrappedType()
|
|
||||||
: $listType;
|
|
||||||
$this->inputTypeStack[] = Type::isInputType($itemType) ? $itemType : null;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case NodeKind::OBJECT_FIELD:
|
|
||||||
$objectType = Type::getNamedType($this->getInputType());
|
|
||||||
$fieldType = null;
|
|
||||||
$inputFieldType = null;
|
|
||||||
if ($objectType instanceof InputObjectType) {
|
|
||||||
$tmp = $objectType->getFields();
|
|
||||||
$inputField = isset($tmp[$node->name->value]) ? $tmp[$node->name->value] : null;
|
|
||||||
$inputFieldType = $inputField ? $inputField->getType() : null;
|
|
||||||
}
|
|
||||||
$this->inputTypeStack[] = Type::isInputType($inputFieldType) ? $inputFieldType : null;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case NodeKind::ENUM:
|
|
||||||
$enumType = Type::getNamedType($this->getInputType());
|
|
||||||
$enumValue = null;
|
|
||||||
if ($enumType instanceof EnumType) {
|
|
||||||
$enumValue = $enumType->getValue($node->value);
|
|
||||||
}
|
|
||||||
$this->enumValue = $enumValue;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param Node $node
|
|
||||||
*/
|
|
||||||
function leave(Node $node)
|
|
||||||
{
|
{
|
||||||
switch ($node->kind) {
|
switch ($node->kind) {
|
||||||
case NodeKind::SELECTION_SET:
|
case NodeKind::SELECTION_SET:
|
||||||
|
@ -1,4 +1,7 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace GraphQL\Utils;
|
namespace GraphQL\Utils;
|
||||||
|
|
||||||
use GraphQL\Error\Error;
|
use GraphQL\Error\Error;
|
||||||
@ -7,13 +10,51 @@ use GraphQL\Error\Warning;
|
|||||||
use GraphQL\Language\AST\Node;
|
use GraphQL\Language\AST\Node;
|
||||||
use GraphQL\Type\Definition\Type;
|
use GraphQL\Type\Definition\Type;
|
||||||
use GraphQL\Type\Definition\WrappingType;
|
use GraphQL\Type\Definition\WrappingType;
|
||||||
use \Traversable, \InvalidArgumentException;
|
use InvalidArgumentException;
|
||||||
|
use Traversable;
|
||||||
|
use function array_keys;
|
||||||
|
use function array_map;
|
||||||
|
use function array_reduce;
|
||||||
|
use function array_shift;
|
||||||
|
use function array_slice;
|
||||||
|
use function array_values;
|
||||||
|
use function asort;
|
||||||
|
use function chr;
|
||||||
|
use function count;
|
||||||
|
use function dechex;
|
||||||
|
use function func_get_args;
|
||||||
|
use function func_num_args;
|
||||||
|
use function get_class;
|
||||||
|
use function gettype;
|
||||||
|
use function is_array;
|
||||||
|
use function is_int;
|
||||||
|
use function is_object;
|
||||||
|
use function is_scalar;
|
||||||
|
use function is_string;
|
||||||
|
use function json_encode;
|
||||||
|
use function levenshtein;
|
||||||
|
use function max;
|
||||||
|
use function mb_convert_encoding;
|
||||||
|
use function mb_strlen;
|
||||||
|
use function mb_substr;
|
||||||
|
use function method_exists;
|
||||||
|
use function ord;
|
||||||
|
use function pack;
|
||||||
|
use function preg_match;
|
||||||
|
use function property_exists;
|
||||||
|
use function range;
|
||||||
|
use function restore_error_handler;
|
||||||
|
use function set_error_handler;
|
||||||
|
use function sprintf;
|
||||||
|
use function strtolower;
|
||||||
|
use function unpack;
|
||||||
|
|
||||||
class Utils
|
class Utils
|
||||||
{
|
{
|
||||||
public static function undefined()
|
public static function undefined()
|
||||||
{
|
{
|
||||||
static $undefined;
|
static $undefined;
|
||||||
|
|
||||||
return $undefined ?: $undefined = new \stdClass();
|
return $undefined ?: $undefined = new \stdClass();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -29,136 +70,154 @@ class Utils
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param object $obj
|
* @param object $obj
|
||||||
* @param array $vars
|
* @param mixed[] $vars
|
||||||
* @param array $requiredKeys
|
* @param string[] $requiredKeys
|
||||||
*
|
*
|
||||||
* @return object
|
* @return object
|
||||||
*/
|
*/
|
||||||
public static function assign($obj, array $vars, array $requiredKeys = [])
|
public static function assign($obj, array $vars, array $requiredKeys = [])
|
||||||
{
|
{
|
||||||
foreach ($requiredKeys as $key) {
|
foreach ($requiredKeys as $key) {
|
||||||
if (!isset($vars[$key])) {
|
if (! isset($vars[$key])) {
|
||||||
throw new InvalidArgumentException("Key {$key} is expected to be set and not to be null");
|
throw new InvalidArgumentException(sprintf('Key %s is expected to be set and not to be null', $key));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach ($vars as $key => $value) {
|
foreach ($vars as $key => $value) {
|
||||||
if (!property_exists($obj, $key)) {
|
if (! property_exists($obj, $key)) {
|
||||||
$cls = get_class($obj);
|
$cls = get_class($obj);
|
||||||
Warning::warn(
|
Warning::warn(
|
||||||
"Trying to set non-existing property '$key' on class '$cls'",
|
sprintf("Trying to set non-existing property '%s' on class '%s'", $key, $cls),
|
||||||
Warning::WARNING_ASSIGN
|
Warning::WARNING_ASSIGN
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
$obj->{$key} = $value;
|
$obj->{$key} = $value;
|
||||||
}
|
}
|
||||||
|
|
||||||
return $obj;
|
return $obj;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param array|Traversable $traversable
|
* @param mixed|Traversable $traversable
|
||||||
* @param callable $predicate
|
|
||||||
* @return mixed|null
|
* @return mixed|null
|
||||||
*/
|
*/
|
||||||
public static function find($traversable, callable $predicate)
|
public static function find($traversable, callable $predicate)
|
||||||
{
|
{
|
||||||
self::invariant(is_array($traversable) || $traversable instanceof \Traversable, __METHOD__ . ' expects array or Traversable');
|
self::invariant(
|
||||||
|
is_array($traversable) || $traversable instanceof \Traversable,
|
||||||
|
__METHOD__ . ' expects array or Traversable'
|
||||||
|
);
|
||||||
|
|
||||||
foreach ($traversable as $key => $value) {
|
foreach ($traversable as $key => $value) {
|
||||||
if ($predicate($value, $key)) {
|
if ($predicate($value, $key)) {
|
||||||
return $value;
|
return $value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param $traversable
|
* @param mixed|Traversable $traversable
|
||||||
* @param callable $predicate
|
* @return mixed[]
|
||||||
* @return array
|
|
||||||
* @throws \Exception
|
* @throws \Exception
|
||||||
*/
|
*/
|
||||||
public static function filter($traversable, callable $predicate)
|
public static function filter($traversable, callable $predicate)
|
||||||
{
|
{
|
||||||
self::invariant(is_array($traversable) || $traversable instanceof \Traversable, __METHOD__ . ' expects array or Traversable');
|
self::invariant(
|
||||||
|
is_array($traversable) || $traversable instanceof \Traversable,
|
||||||
|
__METHOD__ . ' expects array or Traversable'
|
||||||
|
);
|
||||||
|
|
||||||
$result = [];
|
$result = [];
|
||||||
$assoc = false;
|
$assoc = false;
|
||||||
foreach ($traversable as $key => $value) {
|
foreach ($traversable as $key => $value) {
|
||||||
if (!$assoc && !is_int($key)) {
|
if (! $assoc && ! is_int($key)) {
|
||||||
$assoc = true;
|
$assoc = true;
|
||||||
}
|
}
|
||||||
if ($predicate($value, $key)) {
|
if (! $predicate($value, $key)) {
|
||||||
$result[$key] = $value;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$result[$key] = $value;
|
||||||
}
|
}
|
||||||
|
|
||||||
return $assoc ? $result : array_values($result);
|
return $assoc ? $result : array_values($result);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param array|\Traversable $traversable
|
* @param mixed|\Traversable $traversable
|
||||||
* @param callable $fn function($value, $key) => $newValue
|
* @return int[][]
|
||||||
* @return array
|
|
||||||
* @throws \Exception
|
* @throws \Exception
|
||||||
*/
|
*/
|
||||||
public static function map($traversable, callable $fn)
|
public static function map($traversable, callable $fn)
|
||||||
{
|
{
|
||||||
self::invariant(is_array($traversable) || $traversable instanceof \Traversable, __METHOD__ . ' expects array or Traversable');
|
self::invariant(
|
||||||
|
is_array($traversable) || $traversable instanceof \Traversable,
|
||||||
|
__METHOD__ . ' expects array or Traversable'
|
||||||
|
);
|
||||||
|
|
||||||
$map = [];
|
$map = [];
|
||||||
foreach ($traversable as $key => $value) {
|
foreach ($traversable as $key => $value) {
|
||||||
$map[$key] = $fn($value, $key);
|
$map[$key] = $fn($value, $key);
|
||||||
}
|
}
|
||||||
|
|
||||||
return $map;
|
return $map;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param $traversable
|
* @param mixed|Traversable $traversable
|
||||||
* @param callable $fn
|
* @return mixed[]
|
||||||
* @return array
|
|
||||||
* @throws \Exception
|
* @throws \Exception
|
||||||
*/
|
*/
|
||||||
public static function mapKeyValue($traversable, callable $fn)
|
public static function mapKeyValue($traversable, callable $fn)
|
||||||
{
|
{
|
||||||
self::invariant(is_array($traversable) || $traversable instanceof \Traversable, __METHOD__ . ' expects array or Traversable');
|
self::invariant(
|
||||||
|
is_array($traversable) || $traversable instanceof \Traversable,
|
||||||
|
__METHOD__ . ' expects array or Traversable'
|
||||||
|
);
|
||||||
|
|
||||||
$map = [];
|
$map = [];
|
||||||
foreach ($traversable as $key => $value) {
|
foreach ($traversable as $key => $value) {
|
||||||
list($newKey, $newValue) = $fn($value, $key);
|
list($newKey, $newValue) = $fn($value, $key);
|
||||||
$map[$newKey] = $newValue;
|
$map[$newKey] = $newValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
return $map;
|
return $map;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param $traversable
|
* @param mixed|Traversable $traversable
|
||||||
* @param callable $keyFn function($value, $key) => $newKey
|
* @return mixed[]
|
||||||
* @return array
|
|
||||||
* @throws \Exception
|
* @throws \Exception
|
||||||
*/
|
*/
|
||||||
public static function keyMap($traversable, callable $keyFn)
|
public static function keyMap($traversable, callable $keyFn)
|
||||||
{
|
{
|
||||||
self::invariant(is_array($traversable) || $traversable instanceof \Traversable, __METHOD__ . ' expects array or Traversable');
|
self::invariant(
|
||||||
|
is_array($traversable) || $traversable instanceof \Traversable,
|
||||||
|
__METHOD__ . ' expects array or Traversable'
|
||||||
|
);
|
||||||
|
|
||||||
$map = [];
|
$map = [];
|
||||||
foreach ($traversable as $key => $value) {
|
foreach ($traversable as $key => $value) {
|
||||||
$newKey = $keyFn($value, $key);
|
$newKey = $keyFn($value, $key);
|
||||||
if (is_scalar($newKey)) {
|
if (! is_scalar($newKey)) {
|
||||||
$map[$newKey] = $value;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$map[$newKey] = $value;
|
||||||
}
|
}
|
||||||
|
|
||||||
return $map;
|
return $map;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @param $traversable
|
|
||||||
* @param callable $fn
|
|
||||||
*/
|
|
||||||
public static function each($traversable, callable $fn)
|
public static function each($traversable, callable $fn)
|
||||||
{
|
{
|
||||||
self::invariant(is_array($traversable) || $traversable instanceof \Traversable, __METHOD__ . ' expects array or Traversable');
|
self::invariant(
|
||||||
|
is_array($traversable) || $traversable instanceof \Traversable,
|
||||||
|
__METHOD__ . ' expects array or Traversable'
|
||||||
|
);
|
||||||
|
|
||||||
foreach ($traversable as $key => $item) {
|
foreach ($traversable as $key => $item) {
|
||||||
$fn($item, $key);
|
$fn($item, $key);
|
||||||
@ -177,13 +236,15 @@ class Utils
|
|||||||
*
|
*
|
||||||
* $keyFn is also allowed to return array of keys. Then value will be added to all arrays with given keys
|
* $keyFn is also allowed to return array of keys. Then value will be added to all arrays with given keys
|
||||||
*
|
*
|
||||||
* @param $traversable
|
* @param mixed[]|Traversable $traversable
|
||||||
* @param callable $keyFn function($value, $key) => $newKey(s)
|
* @return mixed[]
|
||||||
* @return array
|
|
||||||
*/
|
*/
|
||||||
public static function groupBy($traversable, callable $keyFn)
|
public static function groupBy($traversable, callable $keyFn)
|
||||||
{
|
{
|
||||||
self::invariant(is_array($traversable) || $traversable instanceof \Traversable, __METHOD__ . ' expects array or Traversable');
|
self::invariant(
|
||||||
|
is_array($traversable) || $traversable instanceof \Traversable,
|
||||||
|
__METHOD__ . ' expects array or Traversable'
|
||||||
|
);
|
||||||
|
|
||||||
$grouped = [];
|
$grouped = [];
|
||||||
foreach ($traversable as $key => $value) {
|
foreach ($traversable as $key => $value) {
|
||||||
@ -197,10 +258,8 @@ class Utils
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param array|Traversable $traversable
|
* @param mixed[]|Traversable $traversable
|
||||||
* @param callable $keyFn
|
* @return mixed[][]
|
||||||
* @param callable $valFn
|
|
||||||
* @return array
|
|
||||||
*/
|
*/
|
||||||
public static function keyValMap($traversable, callable $keyFn, callable $valFn)
|
public static function keyValMap($traversable, callable $keyFn, callable $valFn)
|
||||||
{
|
{
|
||||||
@ -208,38 +267,36 @@ class Utils
|
|||||||
foreach ($traversable as $item) {
|
foreach ($traversable as $item) {
|
||||||
$map[$keyFn($item)] = $valFn($item);
|
$map[$keyFn($item)] = $valFn($item);
|
||||||
}
|
}
|
||||||
|
|
||||||
return $map;
|
return $map;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param $traversable
|
* @param mixed[] $traversable
|
||||||
* @param callable $predicate
|
|
||||||
* @return bool
|
* @return bool
|
||||||
*/
|
*/
|
||||||
public static function every($traversable, callable $predicate)
|
public static function every($traversable, callable $predicate)
|
||||||
{
|
{
|
||||||
foreach ($traversable as $key => $value) {
|
foreach ($traversable as $key => $value) {
|
||||||
if (!$predicate($value, $key)) {
|
if (! $predicate($value, $key)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param $test
|
* @param bool $test
|
||||||
* @param string $message
|
* @param string $message
|
||||||
* @param mixed $sprintfParam1
|
|
||||||
* @param mixed $sprintfParam2 ...
|
|
||||||
* @throws Error
|
|
||||||
*/
|
*/
|
||||||
public static function invariant($test, $message = '')
|
public static function invariant($test, $message = '')
|
||||||
{
|
{
|
||||||
if (!$test) {
|
if (! $test) {
|
||||||
if (func_num_args() > 2) {
|
if (func_num_args() > 2) {
|
||||||
$args = func_get_args();
|
$args = func_get_args();
|
||||||
array_shift($args);
|
array_shift($args);
|
||||||
$message = call_user_func_array('sprintf', $args);
|
$message = sprintf(...$args);
|
||||||
}
|
}
|
||||||
// TODO switch to Error here
|
// TODO switch to Error here
|
||||||
throw new InvariantViolation($message);
|
throw new InvariantViolation($message);
|
||||||
@ -247,7 +304,7 @@ class Utils
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param $var
|
* @param Type|mixed $var
|
||||||
* @return string
|
* @return string
|
||||||
*/
|
*/
|
||||||
public static function getVariableType($var)
|
public static function getVariableType($var)
|
||||||
@ -257,8 +314,10 @@ class Utils
|
|||||||
if ($var instanceof WrappingType) {
|
if ($var instanceof WrappingType) {
|
||||||
$var = $var->getWrappedType(true);
|
$var = $var->getWrappedType(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
return $var->name;
|
return $var->name;
|
||||||
}
|
}
|
||||||
|
|
||||||
return is_object($var) ? get_class($var) : gettype($var);
|
return is_object($var) ? get_class($var) : gettype($var);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -274,29 +333,30 @@ class Utils
|
|||||||
if (is_array($var)) {
|
if (is_array($var)) {
|
||||||
return json_encode($var);
|
return json_encode($var);
|
||||||
}
|
}
|
||||||
if ('' === $var) {
|
if ($var === '') {
|
||||||
return '(empty string)';
|
return '(empty string)';
|
||||||
}
|
}
|
||||||
if (null === $var) {
|
if ($var === null) {
|
||||||
return 'null';
|
return 'null';
|
||||||
}
|
}
|
||||||
if (false === $var) {
|
if ($var === false) {
|
||||||
return 'false';
|
return 'false';
|
||||||
}
|
}
|
||||||
if (true === $var) {
|
if ($var === true) {
|
||||||
return 'true';
|
return 'true';
|
||||||
}
|
}
|
||||||
if (is_string($var)) {
|
if (is_string($var)) {
|
||||||
return "\"$var\"";
|
return sprintf('"%s"', $var);
|
||||||
}
|
}
|
||||||
if (is_scalar($var)) {
|
if (is_scalar($var)) {
|
||||||
return (string) $var;
|
return (string) $var;
|
||||||
}
|
}
|
||||||
|
|
||||||
return gettype($var);
|
return gettype($var);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param $var
|
* @param Type|mixed $var
|
||||||
* @return string
|
* @return string
|
||||||
*/
|
*/
|
||||||
public static function printSafe($var)
|
public static function printSafe($var)
|
||||||
@ -307,23 +367,23 @@ class Utils
|
|||||||
if (is_object($var)) {
|
if (is_object($var)) {
|
||||||
if (method_exists($var, '__toString')) {
|
if (method_exists($var, '__toString')) {
|
||||||
return (string) $var;
|
return (string) $var;
|
||||||
} else {
|
|
||||||
return 'instance of ' . get_class($var);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return 'instance of ' . get_class($var);
|
||||||
}
|
}
|
||||||
if (is_array($var)) {
|
if (is_array($var)) {
|
||||||
return json_encode($var);
|
return json_encode($var);
|
||||||
}
|
}
|
||||||
if ('' === $var) {
|
if ($var === '') {
|
||||||
return '(empty string)';
|
return '(empty string)';
|
||||||
}
|
}
|
||||||
if (null === $var) {
|
if ($var === null) {
|
||||||
return 'null';
|
return 'null';
|
||||||
}
|
}
|
||||||
if (false === $var) {
|
if ($var === false) {
|
||||||
return 'false';
|
return 'false';
|
||||||
}
|
}
|
||||||
if (true === $var) {
|
if ($var === true) {
|
||||||
return 'true';
|
return 'true';
|
||||||
}
|
}
|
||||||
if (is_string($var)) {
|
if (is_string($var)) {
|
||||||
@ -332,6 +392,7 @@ class Utils
|
|||||||
if (is_scalar($var)) {
|
if (is_scalar($var)) {
|
||||||
return (string) $var;
|
return (string) $var;
|
||||||
}
|
}
|
||||||
|
|
||||||
return gettype($var);
|
return gettype($var);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -348,10 +409,10 @@ class Utils
|
|||||||
return chr($ord);
|
return chr($ord);
|
||||||
}
|
}
|
||||||
if ($encoding === 'UCS-4BE') {
|
if ($encoding === 'UCS-4BE') {
|
||||||
return pack("N", $ord);
|
return pack('N', $ord);
|
||||||
} else {
|
|
||||||
return mb_convert_encoding(self::chr($ord, 'UCS-4BE'), $encoding, 'UCS-4BE');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return mb_convert_encoding(self::chr($ord, 'UCS-4BE'), $encoding, 'UCS-4BE');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -363,44 +424,46 @@ class Utils
|
|||||||
*/
|
*/
|
||||||
public static function ord($char, $encoding = 'UTF-8')
|
public static function ord($char, $encoding = 'UTF-8')
|
||||||
{
|
{
|
||||||
if (!$char && '0' !== $char) {
|
if (! $char && $char !== '0') {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
if (!isset($char[1])) {
|
if (! isset($char[1])) {
|
||||||
return ord($char);
|
return ord($char);
|
||||||
}
|
}
|
||||||
if ($encoding !== 'UCS-4BE') {
|
if ($encoding !== 'UCS-4BE') {
|
||||||
$char = mb_convert_encoding($char, 'UCS-4BE', $encoding);
|
$char = mb_convert_encoding($char, 'UCS-4BE', $encoding);
|
||||||
}
|
}
|
||||||
list(, $ord) = unpack('N', $char);
|
|
||||||
return $ord;
|
return unpack('N', $char)[1];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns UTF-8 char code at given $positing of the $string
|
* Returns UTF-8 char code at given $positing of the $string
|
||||||
*
|
*
|
||||||
* @param $string
|
* @param string $string
|
||||||
* @param $position
|
* @param int $position
|
||||||
* @return mixed
|
* @return mixed
|
||||||
*/
|
*/
|
||||||
public static function charCodeAt($string, $position)
|
public static function charCodeAt($string, $position)
|
||||||
{
|
{
|
||||||
$char = mb_substr($string, $position, 1, 'UTF-8');
|
$char = mb_substr($string, $position, 1, 'UTF-8');
|
||||||
|
|
||||||
return self::ord($char);
|
return self::ord($char);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param $code
|
* @param int|null $code
|
||||||
* @return string
|
* @return string
|
||||||
*/
|
*/
|
||||||
public static function printCharCode($code)
|
public static function printCharCode($code)
|
||||||
{
|
{
|
||||||
if (null === $code) {
|
if ($code === null) {
|
||||||
return '<EOF>';
|
return '<EOF>';
|
||||||
}
|
}
|
||||||
|
|
||||||
return $code < 0x007F
|
return $code < 0x007F
|
||||||
// Trust JSON for ASCII.
|
// Trust JSON for ASCII.
|
||||||
? json_encode(Utils::chr($code))
|
? json_encode(self::chr($code))
|
||||||
// Otherwise print the escaped form.
|
// Otherwise print the escaped form.
|
||||||
: '"\\u' . dechex($code) . '"';
|
: '"\\u' . dechex($code) . '"';
|
||||||
}
|
}
|
||||||
@ -408,7 +471,7 @@ class Utils
|
|||||||
/**
|
/**
|
||||||
* Upholds the spec rules about naming.
|
* Upholds the spec rules about naming.
|
||||||
*
|
*
|
||||||
* @param $name
|
* @param string $name
|
||||||
* @throws Error
|
* @throws Error
|
||||||
*/
|
*/
|
||||||
public static function assertValidName($name)
|
public static function assertValidName($name)
|
||||||
@ -422,25 +485,25 @@ class Utils
|
|||||||
/**
|
/**
|
||||||
* Returns an Error if a name is invalid.
|
* Returns an Error if a name is invalid.
|
||||||
*
|
*
|
||||||
* @param string $name
|
* @param string $name
|
||||||
* @param Node|null $node
|
* @param Node|null $node
|
||||||
* @return Error|null
|
* @return Error|null
|
||||||
*/
|
*/
|
||||||
public static function isValidNameError($name, $node = null)
|
public static function isValidNameError($name, $node = null)
|
||||||
{
|
{
|
||||||
Utils::invariant(is_string($name), 'Expected string');
|
self::invariant(is_string($name), 'Expected string');
|
||||||
|
|
||||||
if (isset($name[1]) && $name[0] === '_' && $name[1] === '_') {
|
if (isset($name[1]) && $name[0] === '_' && $name[1] === '_') {
|
||||||
return new Error(
|
return new Error(
|
||||||
"Name \"{$name}\" must not begin with \"__\", which is reserved by " .
|
sprintf('Name "%s" must not begin with "__", which is reserved by ', $name) .
|
||||||
"GraphQL introspection.",
|
'GraphQL introspection.',
|
||||||
$node
|
$node
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!preg_match('/^[_a-zA-Z][_a-zA-Z0-9]*$/', $name)) {
|
if (! preg_match('/^[_a-zA-Z][_a-zA-Z0-9]*$/', $name)) {
|
||||||
return new Error(
|
return new Error(
|
||||||
"Names must match /^[_a-zA-Z][_a-zA-Z0-9]*\$/ but \"{$name}\" does not.",
|
sprintf('Names must match /^[_a-zA-Z][_a-zA-Z0-9]*$/ but "%s" does not.', $name),
|
||||||
$node
|
$node
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -452,13 +515,12 @@ class Utils
|
|||||||
* Wraps original closure with PHP error handling (using set_error_handler).
|
* Wraps original closure with PHP error handling (using set_error_handler).
|
||||||
* Resulting closure will collect all PHP errors that occur during the call in $errors array.
|
* Resulting closure will collect all PHP errors that occur during the call in $errors array.
|
||||||
*
|
*
|
||||||
* @param callable $fn
|
|
||||||
* @param \ErrorException[] $errors
|
* @param \ErrorException[] $errors
|
||||||
* @return \Closure
|
* @return \Closure
|
||||||
*/
|
*/
|
||||||
public static function withErrorHandling(callable $fn, array &$errors)
|
public static function withErrorHandling(callable $fn, array &$errors)
|
||||||
{
|
{
|
||||||
return function() use ($fn, &$errors) {
|
return function () use ($fn, &$errors) {
|
||||||
// Catch custom errors (to report them in query results)
|
// Catch custom errors (to report them in query results)
|
||||||
set_error_handler(function ($severity, $message, $file, $line) use (&$errors) {
|
set_error_handler(function ($severity, $message, $file, $line) use (&$errors) {
|
||||||
$errors[] = new \ErrorException($message, 0, $severity, $file, $line);
|
$errors[] = new \ErrorException($message, 0, $severity, $file, $line);
|
||||||
@ -472,25 +534,34 @@ class Utils
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param string[] $items
|
* @param string[] $items
|
||||||
* @return string
|
* @return string
|
||||||
*/
|
*/
|
||||||
public static function quotedOrList(array $items)
|
public static function quotedOrList(array $items)
|
||||||
{
|
{
|
||||||
$items = array_map(function($item) { return "\"$item\""; }, $items);
|
$items = array_map(
|
||||||
|
function ($item) {
|
||||||
|
return sprintf('"%s"', $item);
|
||||||
|
},
|
||||||
|
$items
|
||||||
|
);
|
||||||
|
|
||||||
return self::orList($items);
|
return self::orList($items);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string[] $items
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
public static function orList(array $items)
|
public static function orList(array $items)
|
||||||
{
|
{
|
||||||
if (!$items) {
|
if (count($items) === 0) {
|
||||||
throw new \LogicException('items must not need to be empty.');
|
throw new \LogicException('items must not need to be empty.');
|
||||||
}
|
}
|
||||||
$selected = array_slice($items, 0, 5);
|
$selected = array_slice($items, 0, 5);
|
||||||
$selectedLength = count($selected);
|
$selectedLength = count($selected);
|
||||||
$firstSelected = $selected[0];
|
$firstSelected = $selected[0];
|
||||||
|
|
||||||
if ($selectedLength === 1) {
|
if ($selectedLength === 1) {
|
||||||
return $firstSelected;
|
return $firstSelected;
|
||||||
@ -499,7 +570,7 @@ class Utils
|
|||||||
return array_reduce(
|
return array_reduce(
|
||||||
range(1, $selectedLength - 1),
|
range(1, $selectedLength - 1),
|
||||||
function ($list, $index) use ($selected, $selectedLength) {
|
function ($list, $index) use ($selected, $selectedLength) {
|
||||||
return $list.
|
return $list .
|
||||||
($selectedLength > 2 ? ', ' : ' ') .
|
($selectedLength > 2 ? ', ' : ' ') .
|
||||||
($index === $selectedLength - 1 ? 'or ' : '') .
|
($index === $selectedLength - 1 ? 'or ' : '') .
|
||||||
$selected[$index];
|
$selected[$index];
|
||||||
@ -515,24 +586,28 @@ class Utils
|
|||||||
* Includes a custom alteration from Damerau-Levenshtein to treat case changes
|
* Includes a custom alteration from Damerau-Levenshtein to treat case changes
|
||||||
* as a single edit which helps identify mis-cased values with an edit distance
|
* as a single edit which helps identify mis-cased values with an edit distance
|
||||||
* of 1
|
* of 1
|
||||||
* @param string $input
|
* @param string $input
|
||||||
* @param array $options
|
* @param string[] $options
|
||||||
* @return string[]
|
* @return string[]
|
||||||
*/
|
*/
|
||||||
public static function suggestionList($input, array $options)
|
public static function suggestionList($input, array $options)
|
||||||
{
|
{
|
||||||
$optionsByDistance = [];
|
$optionsByDistance = [];
|
||||||
$inputThreshold = mb_strlen($input) / 2;
|
$inputThreshold = mb_strlen($input) / 2;
|
||||||
foreach ($options as $option) {
|
foreach ($options as $option) {
|
||||||
$distance = $input === $option
|
if ($input === $option) {
|
||||||
? 0
|
$distance = 0;
|
||||||
: (strtolower($input) === strtolower($option)
|
} else {
|
||||||
|
$distance = (strtolower($input) === strtolower($option)
|
||||||
? 1
|
? 1
|
||||||
: levenshtein($input, $option));
|
: levenshtein($input, $option));
|
||||||
$threshold = max($inputThreshold, mb_strlen($option) / 2, 1);
|
|
||||||
if ($distance <= $threshold) {
|
|
||||||
$optionsByDistance[$option] = $distance;
|
|
||||||
}
|
}
|
||||||
|
$threshold = max($inputThreshold, mb_strlen($option) / 2, 1);
|
||||||
|
if ($distance > $threshold) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$optionsByDistance[$option] = $distance;
|
||||||
}
|
}
|
||||||
|
|
||||||
asort($optionsByDistance);
|
asort($optionsByDistance);
|
||||||
|
@ -1,4 +1,7 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace GraphQL\Utils;
|
namespace GraphQL\Utils;
|
||||||
|
|
||||||
use GraphQL\Error\Error;
|
use GraphQL\Error\Error;
|
||||||
@ -9,6 +12,14 @@ use GraphQL\Type\Definition\InputType;
|
|||||||
use GraphQL\Type\Definition\ListOfType;
|
use GraphQL\Type\Definition\ListOfType;
|
||||||
use GraphQL\Type\Definition\NonNull;
|
use GraphQL\Type\Definition\NonNull;
|
||||||
use GraphQL\Type\Definition\ScalarType;
|
use GraphQL\Type\Definition\ScalarType;
|
||||||
|
use function array_key_exists;
|
||||||
|
use function array_keys;
|
||||||
|
use function array_map;
|
||||||
|
use function array_merge;
|
||||||
|
use function is_array;
|
||||||
|
use function is_object;
|
||||||
|
use function is_string;
|
||||||
|
use function sprintf;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Coerces a PHP value given a GraphQL Type.
|
* Coerces a PHP value given a GraphQL Type.
|
||||||
@ -20,23 +31,26 @@ class Value
|
|||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* Given a type and any value, return a runtime value coerced to match the type.
|
* Given a type and any value, return a runtime value coerced to match the type.
|
||||||
|
*
|
||||||
|
* @param mixed[] $path
|
||||||
*/
|
*/
|
||||||
public static function coerceValue($value, InputType $type, $blameNode = null, array $path = null)
|
public static function coerceValue($value, InputType $type, $blameNode = null, ?array $path = null)
|
||||||
{
|
{
|
||||||
if ($type instanceof NonNull) {
|
if ($type instanceof NonNull) {
|
||||||
if ($value === null) {
|
if ($value === null) {
|
||||||
return self::ofErrors([
|
return self::ofErrors([
|
||||||
self::coercionError(
|
self::coercionError(
|
||||||
"Expected non-nullable type $type not to be null",
|
sprintf('Expected non-nullable type %s not to be null', $type),
|
||||||
$blameNode,
|
$blameNode,
|
||||||
$path
|
$path
|
||||||
),
|
),
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
return self::coerceValue($value, $type->getWrappedType(), $blameNode, $path);
|
return self::coerceValue($value, $type->getWrappedType(), $blameNode, $path);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (null === $value) {
|
if ($value === null) {
|
||||||
// Explicitly return the value null.
|
// Explicitly return the value null.
|
||||||
return self::ofValue(null);
|
return self::ofValue(null);
|
||||||
}
|
}
|
||||||
@ -50,7 +64,7 @@ class Value
|
|||||||
} catch (\Exception $error) {
|
} catch (\Exception $error) {
|
||||||
return self::ofErrors([
|
return self::ofErrors([
|
||||||
self::coercionError(
|
self::coercionError(
|
||||||
"Expected type {$type->name}",
|
sprintf('Expected type %s', $type->name),
|
||||||
$blameNode,
|
$blameNode,
|
||||||
$path,
|
$path,
|
||||||
$error->getMessage(),
|
$error->getMessage(),
|
||||||
@ -60,7 +74,7 @@ class Value
|
|||||||
} catch (\Throwable $error) {
|
} catch (\Throwable $error) {
|
||||||
return self::ofErrors([
|
return self::ofErrors([
|
||||||
self::coercionError(
|
self::coercionError(
|
||||||
"Expected type {$type->name}",
|
sprintf('Expected type %s', $type->name),
|
||||||
$blameNode,
|
$blameNode,
|
||||||
$path,
|
$path,
|
||||||
$error->getMessage(),
|
$error->getMessage(),
|
||||||
@ -80,15 +94,21 @@ class Value
|
|||||||
|
|
||||||
$suggestions = Utils::suggestionList(
|
$suggestions = Utils::suggestionList(
|
||||||
Utils::printSafe($value),
|
Utils::printSafe($value),
|
||||||
array_map(function($enumValue) { return $enumValue->name; }, $type->getValues())
|
array_map(
|
||||||
|
function ($enumValue) {
|
||||||
|
return $enumValue->name;
|
||||||
|
},
|
||||||
|
$type->getValues()
|
||||||
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
$didYouMean = $suggestions
|
$didYouMean = $suggestions
|
||||||
? "did you mean " . Utils::orList($suggestions) . "?"
|
? 'did you mean ' . Utils::orList($suggestions) . '?'
|
||||||
: null;
|
: null;
|
||||||
|
|
||||||
return self::ofErrors([
|
return self::ofErrors([
|
||||||
self::coercionError(
|
self::coercionError(
|
||||||
"Expected type {$type->name}",
|
sprintf('Expected type %s', $type->name),
|
||||||
$blameNode,
|
$blameNode,
|
||||||
$path,
|
$path,
|
||||||
$didYouMean
|
$didYouMean
|
||||||
@ -99,7 +119,7 @@ class Value
|
|||||||
if ($type instanceof ListOfType) {
|
if ($type instanceof ListOfType) {
|
||||||
$itemType = $type->getWrappedType();
|
$itemType = $type->getWrappedType();
|
||||||
if (is_array($value) || $value instanceof \Traversable) {
|
if (is_array($value) || $value instanceof \Traversable) {
|
||||||
$errors = [];
|
$errors = [];
|
||||||
$coercedValue = [];
|
$coercedValue = [];
|
||||||
foreach ($value as $index => $itemValue) {
|
foreach ($value as $index => $itemValue) {
|
||||||
$coercedItem = self::coerceValue(
|
$coercedItem = self::coerceValue(
|
||||||
@ -114,44 +134,32 @@ class Value
|
|||||||
$coercedValue[] = $coercedItem['value'];
|
$coercedValue[] = $coercedItem['value'];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return $errors ? self::ofErrors($errors) : self::ofValue($coercedValue);
|
return $errors ? self::ofErrors($errors) : self::ofValue($coercedValue);
|
||||||
}
|
}
|
||||||
// Lists accept a non-list value as a list of one.
|
// Lists accept a non-list value as a list of one.
|
||||||
$coercedItem = self::coerceValue($value, $itemType, $blameNode);
|
$coercedItem = self::coerceValue($value, $itemType, $blameNode);
|
||||||
|
|
||||||
return $coercedItem['errors'] ? $coercedItem : self::ofValue([$coercedItem['value']]);
|
return $coercedItem['errors'] ? $coercedItem : self::ofValue([$coercedItem['value']]);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($type instanceof InputObjectType) {
|
if ($type instanceof InputObjectType) {
|
||||||
if (!is_object($value) && !is_array($value) && !$value instanceof \Traversable) {
|
if (! is_object($value) && ! is_array($value) && ! $value instanceof \Traversable) {
|
||||||
return self::ofErrors([
|
return self::ofErrors([
|
||||||
self::coercionError(
|
self::coercionError(
|
||||||
"Expected type {$type->name} to be an object",
|
sprintf('Expected type %s to be an object', $type->name),
|
||||||
$blameNode,
|
$blameNode,
|
||||||
$path
|
$path
|
||||||
),
|
),
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
$errors = [];
|
$errors = [];
|
||||||
$coercedValue = [];
|
$coercedValue = [];
|
||||||
$fields = $type->getFields();
|
$fields = $type->getFields();
|
||||||
foreach ($fields as $fieldName => $field) {
|
foreach ($fields as $fieldName => $field) {
|
||||||
if (!array_key_exists($fieldName, $value)) {
|
if (array_key_exists($fieldName, $value)) {
|
||||||
if ($field->defaultValueExists()) {
|
$fieldValue = $value[$fieldName];
|
||||||
$coercedValue[$fieldName] = $field->defaultValue;
|
|
||||||
} else if ($field->getType() instanceof NonNull) {
|
|
||||||
$fieldPath = self::printPath(self::atPath($path, $fieldName));
|
|
||||||
$errors = self::add(
|
|
||||||
$errors,
|
|
||||||
self::coercionError(
|
|
||||||
"Field {$fieldPath} of required " .
|
|
||||||
"type {$field->type} was not provided",
|
|
||||||
$blameNode
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
$fieldValue = $value[$fieldName];
|
|
||||||
$coercedField = self::coerceValue(
|
$coercedField = self::coerceValue(
|
||||||
$fieldValue,
|
$fieldValue,
|
||||||
$field->getType(),
|
$field->getType(),
|
||||||
@ -163,63 +171,76 @@ class Value
|
|||||||
} else {
|
} else {
|
||||||
$coercedValue[$fieldName] = $coercedField['value'];
|
$coercedValue[$fieldName] = $coercedField['value'];
|
||||||
}
|
}
|
||||||
}
|
} elseif ($field->defaultValueExists()) {
|
||||||
}
|
$coercedValue[$fieldName] = $field->defaultValue;
|
||||||
|
} elseif ($field->getType() instanceof NonNull) {
|
||||||
// Ensure every provided field is defined.
|
$fieldPath = self::printPath(self::atPath($path, $fieldName));
|
||||||
foreach ($value as $fieldName => $field) {
|
$errors = self::add(
|
||||||
if (!array_key_exists($fieldName, $fields)) {
|
|
||||||
$suggestions = Utils::suggestionList(
|
|
||||||
$fieldName,
|
|
||||||
array_keys($fields)
|
|
||||||
);
|
|
||||||
$didYouMean = $suggestions
|
|
||||||
? "did you mean " . Utils::orList($suggestions) . "?"
|
|
||||||
: null;
|
|
||||||
$errors = self::add(
|
|
||||||
$errors,
|
$errors,
|
||||||
self::coercionError(
|
self::coercionError(
|
||||||
"Field \"{$fieldName}\" is not defined by type {$type->name}",
|
sprintf(
|
||||||
$blameNode,
|
'Field %s of required type %s was not provided',
|
||||||
$path,
|
$fieldPath,
|
||||||
$didYouMean
|
$field->type->toString()
|
||||||
|
),
|
||||||
|
$blameNode
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Ensure every provided field is defined.
|
||||||
|
foreach ($value as $fieldName => $field) {
|
||||||
|
if (array_key_exists($fieldName, $fields)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$suggestions = Utils::suggestionList(
|
||||||
|
$fieldName,
|
||||||
|
array_keys($fields)
|
||||||
|
);
|
||||||
|
$didYouMean = $suggestions
|
||||||
|
? 'did you mean ' . Utils::orList($suggestions) . '?'
|
||||||
|
: null;
|
||||||
|
$errors = self::add(
|
||||||
|
$errors,
|
||||||
|
self::coercionError(
|
||||||
|
sprintf('Field "%s" is not defined by type %s', $fieldName, $type->name),
|
||||||
|
$blameNode,
|
||||||
|
$path,
|
||||||
|
$didYouMean
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
return $errors ? self::ofErrors($errors) : self::ofValue($coercedValue);
|
return $errors ? self::ofErrors($errors) : self::ofValue($coercedValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new Error("Unexpected type {$type}");
|
throw new Error(sprintf('Unexpected type %s', $type->name));
|
||||||
}
|
}
|
||||||
|
|
||||||
private static function ofValue($value) {
|
private static function ofErrors($errors)
|
||||||
return ['errors' => null, 'value' => $value];
|
{
|
||||||
}
|
|
||||||
|
|
||||||
private static function ofErrors($errors) {
|
|
||||||
return ['errors' => $errors, 'value' => Utils::undefined()];
|
return ['errors' => $errors, 'value' => Utils::undefined()];
|
||||||
}
|
}
|
||||||
|
|
||||||
private static function add($errors, $moreErrors) {
|
|
||||||
return array_merge($errors, is_array($moreErrors) ? $moreErrors : [$moreErrors]);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static function atPath($prev, $key) {
|
|
||||||
return ['prev' => $prev, 'key' => $key];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param string $message
|
* @param string $message
|
||||||
* @param Node $blameNode
|
* @param Node $blameNode
|
||||||
* @param array|null $path
|
* @param mixed[]|null $path
|
||||||
* @param string $subMessage
|
* @param string $subMessage
|
||||||
* @param \Exception|\Throwable|null $originalError
|
* @param \Exception|\Throwable|null $originalError
|
||||||
* @return Error
|
* @return Error
|
||||||
*/
|
*/
|
||||||
private static function coercionError($message, $blameNode, array $path = null, $subMessage = null, $originalError = null) {
|
private static function coercionError(
|
||||||
|
$message,
|
||||||
|
$blameNode,
|
||||||
|
?array $path = null,
|
||||||
|
$subMessage = null,
|
||||||
|
$originalError = null
|
||||||
|
) {
|
||||||
$pathStr = self::printPath($path);
|
$pathStr = self::printPath($path);
|
||||||
|
|
||||||
// Return a GraphQLError instance
|
// Return a GraphQLError instance
|
||||||
return new Error(
|
return new Error(
|
||||||
$message .
|
$message .
|
||||||
@ -236,19 +257,50 @@ class Value
|
|||||||
/**
|
/**
|
||||||
* Build a string describing the path into the value where the error was found
|
* Build a string describing the path into the value where the error was found
|
||||||
*
|
*
|
||||||
* @param $path
|
* @param mixed[]|null $path
|
||||||
* @return string
|
* @return string
|
||||||
*/
|
*/
|
||||||
private static function printPath(array $path = null) {
|
private static function printPath(?array $path = null)
|
||||||
$pathStr = '';
|
{
|
||||||
|
$pathStr = '';
|
||||||
$currentPath = $path;
|
$currentPath = $path;
|
||||||
while($currentPath) {
|
while ($currentPath) {
|
||||||
$pathStr =
|
$pathStr =
|
||||||
(is_string($currentPath['key'])
|
(is_string($currentPath['key'])
|
||||||
? '.' . $currentPath['key']
|
? '.' . $currentPath['key']
|
||||||
: '[' . $currentPath['key'] . ']') . $pathStr;
|
: '[' . $currentPath['key'] . ']') . $pathStr;
|
||||||
$currentPath = $currentPath['prev'];
|
$currentPath = $currentPath['prev'];
|
||||||
}
|
}
|
||||||
|
|
||||||
return $pathStr ? 'value' . $pathStr : '';
|
return $pathStr ? 'value' . $pathStr : '';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param mixed $value
|
||||||
|
* @return (mixed|null)[]
|
||||||
|
*/
|
||||||
|
private static function ofValue($value)
|
||||||
|
{
|
||||||
|
return ['errors' => null, 'value' => $value];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param mixed|null $prev
|
||||||
|
* @param mixed|null $key
|
||||||
|
* @return (mixed|null)[]
|
||||||
|
*/
|
||||||
|
private static function atPath($prev, $key)
|
||||||
|
{
|
||||||
|
return ['prev' => $prev, 'key' => $key];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param Error[] $errors
|
||||||
|
* @param Error|Error[] $moreErrors
|
||||||
|
* @return Error[]
|
||||||
|
*/
|
||||||
|
private static function add($errors, $moreErrors)
|
||||||
|
{
|
||||||
|
return array_merge($errors, is_array($moreErrors) ? $moreErrors : [$moreErrors]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
namespace GraphQL\Tests\Utils;
|
namespace GraphQL\Tests\Utils;
|
||||||
|
|
||||||
use GraphQL\Language\DirectiveLocation;
|
use GraphQL\Language\DirectiveLocation;
|
||||||
@ -11,10 +12,10 @@ use GraphQL\Type\Definition\ObjectType;
|
|||||||
use GraphQL\Type\Definition\Type;
|
use GraphQL\Type\Definition\Type;
|
||||||
use GraphQL\Type\Definition\UnionType;
|
use GraphQL\Type\Definition\UnionType;
|
||||||
use GraphQL\Type\Schema;
|
use GraphQL\Type\Schema;
|
||||||
use GraphQL\Utils\FindBreakingChanges;
|
use GraphQL\Utils\BreakingChangesFinder;
|
||||||
use PHPUnit\Framework\TestCase;
|
use PHPUnit\Framework\TestCase;
|
||||||
|
|
||||||
class FindBreakingChangesTest extends TestCase
|
class BreakingChangesFinderTest extends TestCase
|
||||||
{
|
{
|
||||||
private $queryType;
|
private $queryType;
|
||||||
|
|
||||||
@ -60,17 +61,17 @@ class FindBreakingChangesTest extends TestCase
|
|||||||
|
|
||||||
$expected = [
|
$expected = [
|
||||||
[
|
[
|
||||||
'type' => FindBreakingChanges::BREAKING_CHANGE_TYPE_REMOVED,
|
'type' => BreakingChangesFinder::BREAKING_CHANGE_TYPE_REMOVED,
|
||||||
'description' => 'Type1 was removed.'
|
'description' => 'Type1 was removed.'
|
||||||
]
|
]
|
||||||
];
|
];
|
||||||
|
|
||||||
$this->assertEquals(
|
$this->assertEquals(
|
||||||
$expected,
|
$expected,
|
||||||
FindBreakingChanges::findRemovedTypes($oldSchema, $newSchema)
|
BreakingChangesFinder::findRemovedTypes($oldSchema, $newSchema)
|
||||||
);
|
);
|
||||||
|
|
||||||
$this->assertEquals([], FindBreakingChanges::findRemovedTypes($oldSchema, $oldSchema));
|
$this->assertEquals([], BreakingChangesFinder::findRemovedTypes($oldSchema, $oldSchema));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -109,14 +110,14 @@ class FindBreakingChangesTest extends TestCase
|
|||||||
|
|
||||||
$expected = [
|
$expected = [
|
||||||
[
|
[
|
||||||
'type' => FindBreakingChanges::BREAKING_CHANGE_TYPE_CHANGED_KIND,
|
'type' => BreakingChangesFinder::BREAKING_CHANGE_TYPE_CHANGED_KIND,
|
||||||
'description' => 'Type1 changed from an Interface type to a Union type.'
|
'description' => 'Type1 changed from an Interface type to a Union type.'
|
||||||
]
|
]
|
||||||
];
|
];
|
||||||
|
|
||||||
$this->assertEquals(
|
$this->assertEquals(
|
||||||
$expected,
|
$expected,
|
||||||
FindBreakingChanges::findTypesThatChangedKind($oldSchema, $newSchema)
|
BreakingChangesFinder::findTypesThatChangedKind($oldSchema, $newSchema)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -208,60 +209,60 @@ class FindBreakingChangesTest extends TestCase
|
|||||||
|
|
||||||
$expectedFieldChanges = [
|
$expectedFieldChanges = [
|
||||||
[
|
[
|
||||||
'type' => FindBreakingChanges::BREAKING_CHANGE_FIELD_REMOVED,
|
'type' => BreakingChangesFinder::BREAKING_CHANGE_FIELD_REMOVED,
|
||||||
'description' => 'Type1.field2 was removed.',
|
'description' => 'Type1.field2 was removed.',
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
'type' => FindBreakingChanges::BREAKING_CHANGE_FIELD_CHANGED_KIND,
|
'type' => BreakingChangesFinder::BREAKING_CHANGE_FIELD_CHANGED_KIND,
|
||||||
'description' => 'Type1.field3 changed type from String to Boolean.',
|
'description' => 'Type1.field3 changed type from String to Boolean.',
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
'type' => FindBreakingChanges::BREAKING_CHANGE_FIELD_CHANGED_KIND,
|
'type' => BreakingChangesFinder::BREAKING_CHANGE_FIELD_CHANGED_KIND,
|
||||||
'description' => 'Type1.field4 changed type from TypeA to TypeB.',
|
'description' => 'Type1.field4 changed type from TypeA to TypeB.',
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
'type' => FindBreakingChanges::BREAKING_CHANGE_FIELD_CHANGED_KIND,
|
'type' => BreakingChangesFinder::BREAKING_CHANGE_FIELD_CHANGED_KIND,
|
||||||
'description' => 'Type1.field6 changed type from String to [String].',
|
'description' => 'Type1.field6 changed type from String to [String].',
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
'type' => FindBreakingChanges::BREAKING_CHANGE_FIELD_CHANGED_KIND,
|
'type' => BreakingChangesFinder::BREAKING_CHANGE_FIELD_CHANGED_KIND,
|
||||||
'description' => 'Type1.field7 changed type from [String] to String.',
|
'description' => 'Type1.field7 changed type from [String] to String.',
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
'type' => FindBreakingChanges::BREAKING_CHANGE_FIELD_CHANGED_KIND,
|
'type' => BreakingChangesFinder::BREAKING_CHANGE_FIELD_CHANGED_KIND,
|
||||||
'description' => 'Type1.field9 changed type from Int! to Int.',
|
'description' => 'Type1.field9 changed type from Int! to Int.',
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
'type' => FindBreakingChanges::BREAKING_CHANGE_FIELD_CHANGED_KIND,
|
'type' => BreakingChangesFinder::BREAKING_CHANGE_FIELD_CHANGED_KIND,
|
||||||
'description' => 'Type1.field10 changed type from [Int]! to [Int].',
|
'description' => 'Type1.field10 changed type from [Int]! to [Int].',
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
'type' => FindBreakingChanges::BREAKING_CHANGE_FIELD_CHANGED_KIND,
|
'type' => BreakingChangesFinder::BREAKING_CHANGE_FIELD_CHANGED_KIND,
|
||||||
'description' => 'Type1.field11 changed type from Int to [Int]!.',
|
'description' => 'Type1.field11 changed type from Int to [Int]!.',
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
'type' => FindBreakingChanges::BREAKING_CHANGE_FIELD_CHANGED_KIND,
|
'type' => BreakingChangesFinder::BREAKING_CHANGE_FIELD_CHANGED_KIND,
|
||||||
'description' => 'Type1.field13 changed type from [Int!] to [Int].',
|
'description' => 'Type1.field13 changed type from [Int!] to [Int].',
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
'type' => FindBreakingChanges::BREAKING_CHANGE_FIELD_CHANGED_KIND,
|
'type' => BreakingChangesFinder::BREAKING_CHANGE_FIELD_CHANGED_KIND,
|
||||||
'description' => 'Type1.field14 changed type from [Int] to [[Int]].',
|
'description' => 'Type1.field14 changed type from [Int] to [[Int]].',
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
'type' => FindBreakingChanges::BREAKING_CHANGE_FIELD_CHANGED_KIND,
|
'type' => BreakingChangesFinder::BREAKING_CHANGE_FIELD_CHANGED_KIND,
|
||||||
'description' => 'Type1.field15 changed type from [[Int]] to [Int].',
|
'description' => 'Type1.field15 changed type from [[Int]] to [Int].',
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
'type' => FindBreakingChanges::BREAKING_CHANGE_FIELD_CHANGED_KIND,
|
'type' => BreakingChangesFinder::BREAKING_CHANGE_FIELD_CHANGED_KIND,
|
||||||
'description' => 'Type1.field16 changed type from Int! to [Int]!.',
|
'description' => 'Type1.field16 changed type from Int! to [Int]!.',
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
'type' => FindBreakingChanges::BREAKING_CHANGE_FIELD_CHANGED_KIND,
|
'type' => BreakingChangesFinder::BREAKING_CHANGE_FIELD_CHANGED_KIND,
|
||||||
'description' => 'Type1.field18 changed type from [[Int!]!] to [[Int!]].',
|
'description' => 'Type1.field18 changed type from [[Int!]!] to [[Int!]].',
|
||||||
],
|
],
|
||||||
];
|
];
|
||||||
|
|
||||||
$this->assertEquals($expectedFieldChanges, FindBreakingChanges::findFieldsThatChangedTypeOnObjectOrInterfaceTypes($oldSchema, $newSchema));
|
$this->assertEquals($expectedFieldChanges, BreakingChangesFinder::findFieldsThatChangedTypeOnObjectOrInterfaceTypes($oldSchema, $newSchema));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -380,52 +381,52 @@ class FindBreakingChangesTest extends TestCase
|
|||||||
|
|
||||||
$expectedFieldChanges = [
|
$expectedFieldChanges = [
|
||||||
[
|
[
|
||||||
'type' => FindBreakingChanges::BREAKING_CHANGE_FIELD_CHANGED_KIND,
|
'type' => BreakingChangesFinder::BREAKING_CHANGE_FIELD_CHANGED_KIND,
|
||||||
'description' => 'InputType1.field1 changed type from String to Int.',
|
'description' => 'InputType1.field1 changed type from String to Int.',
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
'type' => FindBreakingChanges::BREAKING_CHANGE_FIELD_REMOVED,
|
'type' => BreakingChangesFinder::BREAKING_CHANGE_FIELD_REMOVED,
|
||||||
'description' => 'InputType1.field2 was removed.',
|
'description' => 'InputType1.field2 was removed.',
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
'type' => FindBreakingChanges::BREAKING_CHANGE_FIELD_CHANGED_KIND,
|
'type' => BreakingChangesFinder::BREAKING_CHANGE_FIELD_CHANGED_KIND,
|
||||||
'description' => 'InputType1.field3 changed type from [String] to String.',
|
'description' => 'InputType1.field3 changed type from [String] to String.',
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
'type' => FindBreakingChanges::BREAKING_CHANGE_FIELD_CHANGED_KIND,
|
'type' => BreakingChangesFinder::BREAKING_CHANGE_FIELD_CHANGED_KIND,
|
||||||
'description' => 'InputType1.field5 changed type from String to String!.',
|
'description' => 'InputType1.field5 changed type from String to String!.',
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
'type' => FindBreakingChanges::BREAKING_CHANGE_FIELD_CHANGED_KIND,
|
'type' => BreakingChangesFinder::BREAKING_CHANGE_FIELD_CHANGED_KIND,
|
||||||
'description' => 'InputType1.field6 changed type from [Int] to [Int]!.',
|
'description' => 'InputType1.field6 changed type from [Int] to [Int]!.',
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
'type' => FindBreakingChanges::BREAKING_CHANGE_FIELD_CHANGED_KIND,
|
'type' => BreakingChangesFinder::BREAKING_CHANGE_FIELD_CHANGED_KIND,
|
||||||
'description' => 'InputType1.field8 changed type from Int to [Int]!.',
|
'description' => 'InputType1.field8 changed type from Int to [Int]!.',
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
'type' => FindBreakingChanges::BREAKING_CHANGE_FIELD_CHANGED_KIND,
|
'type' => BreakingChangesFinder::BREAKING_CHANGE_FIELD_CHANGED_KIND,
|
||||||
'description' => 'InputType1.field9 changed type from [Int] to [Int!].',
|
'description' => 'InputType1.field9 changed type from [Int] to [Int!].',
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
'type' => FindBreakingChanges::BREAKING_CHANGE_FIELD_CHANGED_KIND,
|
'type' => BreakingChangesFinder::BREAKING_CHANGE_FIELD_CHANGED_KIND,
|
||||||
'description' => 'InputType1.field11 changed type from [Int] to [[Int]].',
|
'description' => 'InputType1.field11 changed type from [Int] to [[Int]].',
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
'type' => FindBreakingChanges::BREAKING_CHANGE_FIELD_CHANGED_KIND,
|
'type' => BreakingChangesFinder::BREAKING_CHANGE_FIELD_CHANGED_KIND,
|
||||||
'description' => 'InputType1.field12 changed type from [[Int]] to [Int].',
|
'description' => 'InputType1.field12 changed type from [[Int]] to [Int].',
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
'type' => FindBreakingChanges::BREAKING_CHANGE_FIELD_CHANGED_KIND,
|
'type' => BreakingChangesFinder::BREAKING_CHANGE_FIELD_CHANGED_KIND,
|
||||||
'description' => 'InputType1.field13 changed type from Int! to [Int]!.',
|
'description' => 'InputType1.field13 changed type from Int! to [Int]!.',
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
'type' => FindBreakingChanges::BREAKING_CHANGE_FIELD_CHANGED_KIND,
|
'type' => BreakingChangesFinder::BREAKING_CHANGE_FIELD_CHANGED_KIND,
|
||||||
'description' => 'InputType1.field15 changed type from [[Int]!] to [[Int!]!].',
|
'description' => 'InputType1.field15 changed type from [[Int]!] to [[Int!]!].',
|
||||||
],
|
],
|
||||||
];
|
];
|
||||||
|
|
||||||
$this->assertEquals($expectedFieldChanges, FindBreakingChanges::findFieldsThatChangedTypeOnInputObjectTypes($oldSchema, $newSchema)['breakingChanges']);
|
$this->assertEquals($expectedFieldChanges, BreakingChangesFinder::findFieldsThatChangedTypeOnInputObjectTypes($oldSchema, $newSchema)['breakingChanges']);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -461,14 +462,14 @@ class FindBreakingChangesTest extends TestCase
|
|||||||
|
|
||||||
$expected = [
|
$expected = [
|
||||||
[
|
[
|
||||||
'type' => FindBreakingChanges::BREAKING_CHANGE_NON_NULL_INPUT_FIELD_ADDED,
|
'type' => BreakingChangesFinder::BREAKING_CHANGE_NON_NULL_INPUT_FIELD_ADDED,
|
||||||
'description' => 'A non-null field requiredField on input type InputType1 was added.'
|
'description' => 'A non-null field requiredField on input type InputType1 was added.'
|
||||||
],
|
],
|
||||||
];
|
];
|
||||||
|
|
||||||
$this->assertEquals(
|
$this->assertEquals(
|
||||||
$expected,
|
$expected,
|
||||||
FindBreakingChanges::findFieldsThatChangedTypeOnInputObjectTypes($oldSchema, $newSchema)['breakingChanges']
|
BreakingChangesFinder::findFieldsThatChangedTypeOnInputObjectTypes($oldSchema, $newSchema)['breakingChanges']
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -524,14 +525,14 @@ class FindBreakingChangesTest extends TestCase
|
|||||||
|
|
||||||
$expected = [
|
$expected = [
|
||||||
[
|
[
|
||||||
'type' => FindBreakingChanges::BREAKING_CHANGE_TYPE_REMOVED_FROM_UNION,
|
'type' => BreakingChangesFinder::BREAKING_CHANGE_TYPE_REMOVED_FROM_UNION,
|
||||||
'description' => 'Type2 was removed from union type UnionType1.'
|
'description' => 'Type2 was removed from union type UnionType1.'
|
||||||
]
|
]
|
||||||
];
|
];
|
||||||
|
|
||||||
$this->assertEquals(
|
$this->assertEquals(
|
||||||
$expected,
|
$expected,
|
||||||
FindBreakingChanges::findTypesRemovedFromUnions($oldSchema, $newSchema)
|
BreakingChangesFinder::findTypesRemovedFromUnions($oldSchema, $newSchema)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -569,14 +570,14 @@ class FindBreakingChangesTest extends TestCase
|
|||||||
|
|
||||||
$expected = [
|
$expected = [
|
||||||
[
|
[
|
||||||
'type' => FindBreakingChanges::BREAKING_CHANGE_VALUE_REMOVED_FROM_ENUM,
|
'type' => BreakingChangesFinder::BREAKING_CHANGE_VALUE_REMOVED_FROM_ENUM,
|
||||||
'description' => 'VALUE1 was removed from enum type EnumType1.'
|
'description' => 'VALUE1 was removed from enum type EnumType1.'
|
||||||
]
|
]
|
||||||
];
|
];
|
||||||
|
|
||||||
$this->assertEquals(
|
$this->assertEquals(
|
||||||
$expected,
|
$expected,
|
||||||
FindBreakingChanges::findValuesRemovedFromEnums($oldSchema, $newSchema)
|
BreakingChangesFinder::findValuesRemovedFromEnums($oldSchema, $newSchema)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -646,20 +647,20 @@ class FindBreakingChangesTest extends TestCase
|
|||||||
|
|
||||||
$expectedChanges = [
|
$expectedChanges = [
|
||||||
[
|
[
|
||||||
'type' => FindBreakingChanges::BREAKING_CHANGE_ARG_REMOVED,
|
'type' => BreakingChangesFinder::BREAKING_CHANGE_ARG_REMOVED,
|
||||||
'description' => 'Type1.field1 arg name was removed',
|
'description' => 'Type1.field1 arg name was removed',
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
'type' => FindBreakingChanges::BREAKING_CHANGE_ARG_REMOVED,
|
'type' => BreakingChangesFinder::BREAKING_CHANGE_ARG_REMOVED,
|
||||||
'description' => 'Interface1.field1 arg arg1 was removed',
|
'description' => 'Interface1.field1 arg arg1 was removed',
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
'type' => FindBreakingChanges::BREAKING_CHANGE_ARG_REMOVED,
|
'type' => BreakingChangesFinder::BREAKING_CHANGE_ARG_REMOVED,
|
||||||
'description' => 'Interface1.field1 arg objectArg was removed',
|
'description' => 'Interface1.field1 arg objectArg was removed',
|
||||||
]
|
]
|
||||||
];
|
];
|
||||||
|
|
||||||
$this->assertEquals($expectedChanges, FindBreakingChanges::findArgChanges($oldSchema, $newSchema)['breakingChanges']);
|
$this->assertEquals($expectedChanges, BreakingChangesFinder::findArgChanges($oldSchema, $newSchema)['breakingChanges']);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -731,56 +732,56 @@ class FindBreakingChangesTest extends TestCase
|
|||||||
|
|
||||||
$expectedChanges = [
|
$expectedChanges = [
|
||||||
[
|
[
|
||||||
'type' => FindBreakingChanges::BREAKING_CHANGE_ARG_CHANGED_KIND,
|
'type' => BreakingChangesFinder::BREAKING_CHANGE_ARG_CHANGED_KIND,
|
||||||
'description' => 'Type1.field1 arg arg1 has changed type from String to Int',
|
'description' => 'Type1.field1 arg arg1 has changed type from String to Int',
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
'type' => FindBreakingChanges::BREAKING_CHANGE_ARG_CHANGED_KIND,
|
'type' => BreakingChangesFinder::BREAKING_CHANGE_ARG_CHANGED_KIND,
|
||||||
'description' => 'Type1.field1 arg arg2 has changed type from String to [String]'
|
'description' => 'Type1.field1 arg arg2 has changed type from String to [String]'
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
'type' => FindBreakingChanges::BREAKING_CHANGE_ARG_CHANGED_KIND,
|
'type' => BreakingChangesFinder::BREAKING_CHANGE_ARG_CHANGED_KIND,
|
||||||
'description' => 'Type1.field1 arg arg3 has changed type from [String] to String',
|
'description' => 'Type1.field1 arg arg3 has changed type from [String] to String',
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
'type' => FindBreakingChanges::BREAKING_CHANGE_ARG_CHANGED_KIND,
|
'type' => BreakingChangesFinder::BREAKING_CHANGE_ARG_CHANGED_KIND,
|
||||||
'description' => 'Type1.field1 arg arg4 has changed type from String to String!',
|
'description' => 'Type1.field1 arg arg4 has changed type from String to String!',
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
'type' => FindBreakingChanges::BREAKING_CHANGE_ARG_CHANGED_KIND,
|
'type' => BreakingChangesFinder::BREAKING_CHANGE_ARG_CHANGED_KIND,
|
||||||
'description' => 'Type1.field1 arg arg5 has changed type from String! to Int',
|
'description' => 'Type1.field1 arg arg5 has changed type from String! to Int',
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
'type' => FindBreakingChanges::BREAKING_CHANGE_ARG_CHANGED_KIND,
|
'type' => BreakingChangesFinder::BREAKING_CHANGE_ARG_CHANGED_KIND,
|
||||||
'description' => 'Type1.field1 arg arg6 has changed type from String! to Int!',
|
'description' => 'Type1.field1 arg arg6 has changed type from String! to Int!',
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
'type' => FindBreakingChanges::BREAKING_CHANGE_ARG_CHANGED_KIND,
|
'type' => BreakingChangesFinder::BREAKING_CHANGE_ARG_CHANGED_KIND,
|
||||||
'description' => 'Type1.field1 arg arg8 has changed type from Int to [Int]!',
|
'description' => 'Type1.field1 arg arg8 has changed type from Int to [Int]!',
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
'type' => FindBreakingChanges::BREAKING_CHANGE_ARG_CHANGED_KIND,
|
'type' => BreakingChangesFinder::BREAKING_CHANGE_ARG_CHANGED_KIND,
|
||||||
'description' => 'Type1.field1 arg arg9 has changed type from [Int] to [Int!]',
|
'description' => 'Type1.field1 arg arg9 has changed type from [Int] to [Int!]',
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
'type' => FindBreakingChanges::BREAKING_CHANGE_ARG_CHANGED_KIND,
|
'type' => BreakingChangesFinder::BREAKING_CHANGE_ARG_CHANGED_KIND,
|
||||||
'description' => 'Type1.field1 arg arg11 has changed type from [Int] to [[Int]]',
|
'description' => 'Type1.field1 arg arg11 has changed type from [Int] to [[Int]]',
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
'type' => FindBreakingChanges::BREAKING_CHANGE_ARG_CHANGED_KIND,
|
'type' => BreakingChangesFinder::BREAKING_CHANGE_ARG_CHANGED_KIND,
|
||||||
'description' => 'Type1.field1 arg arg12 has changed type from [[Int]] to [Int]',
|
'description' => 'Type1.field1 arg arg12 has changed type from [[Int]] to [Int]',
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
'type' => FindBreakingChanges::BREAKING_CHANGE_ARG_CHANGED_KIND,
|
'type' => BreakingChangesFinder::BREAKING_CHANGE_ARG_CHANGED_KIND,
|
||||||
'description' => 'Type1.field1 arg arg13 has changed type from Int! to [Int]!',
|
'description' => 'Type1.field1 arg arg13 has changed type from Int! to [Int]!',
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
'type' => FindBreakingChanges::BREAKING_CHANGE_ARG_CHANGED_KIND,
|
'type' => BreakingChangesFinder::BREAKING_CHANGE_ARG_CHANGED_KIND,
|
||||||
'description' => 'Type1.field1 arg arg15 has changed type from [[Int]!] to [[Int!]!]',
|
'description' => 'Type1.field1 arg arg15 has changed type from [[Int]!] to [[Int!]!]',
|
||||||
],
|
],
|
||||||
];
|
];
|
||||||
|
|
||||||
$this->assertEquals($expectedChanges, FindBreakingChanges::findArgChanges($oldSchema, $newSchema)['breakingChanges']);
|
$this->assertEquals($expectedChanges, BreakingChangesFinder::findArgChanges($oldSchema, $newSchema)['breakingChanges']);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -821,14 +822,15 @@ class FindBreakingChangesTest extends TestCase
|
|||||||
|
|
||||||
$expected = [
|
$expected = [
|
||||||
[
|
[
|
||||||
'type' => FindBreakingChanges::BREAKING_CHANGE_NON_NULL_ARG_ADDED,
|
'type' => BreakingChangesFinder::BREAKING_CHANGE_NON_NULL_ARG_ADDED,
|
||||||
'description' => 'A non-null arg newRequiredArg on Type1.field1 was added'
|
'description' => 'A non-null arg newRequiredArg on Type1.field1 was added'
|
||||||
]
|
]
|
||||||
];
|
];
|
||||||
|
|
||||||
$this->assertEquals(
|
$this->assertEquals(
|
||||||
$expected,
|
$expected,
|
||||||
FindBreakingChanges::findArgChanges($oldSchema, $newSchema)['breakingChanges']);
|
BreakingChangesFinder::findArgChanges($oldSchema, $newSchema)['breakingChanges']
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -885,7 +887,7 @@ class FindBreakingChangesTest extends TestCase
|
|||||||
'types' => [$newType],
|
'types' => [$newType],
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$this->assertEquals([], FindBreakingChanges::findArgChanges($oldSchema, $newSchema)['breakingChanges']);
|
$this->assertEquals([], BreakingChangesFinder::findArgChanges($oldSchema, $newSchema)['breakingChanges']);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -925,7 +927,7 @@ class FindBreakingChangesTest extends TestCase
|
|||||||
'types' => [$newType],
|
'types' => [$newType],
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$this->assertEquals([], FindBreakingChanges::findArgChanges($oldSchema, $newSchema)['breakingChanges']);
|
$this->assertEquals([], BreakingChangesFinder::findArgChanges($oldSchema, $newSchema)['breakingChanges']);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -964,14 +966,15 @@ class FindBreakingChangesTest extends TestCase
|
|||||||
|
|
||||||
$expected = [
|
$expected = [
|
||||||
[
|
[
|
||||||
'type' => FindBreakingChanges::BREAKING_CHANGE_INTERFACE_REMOVED_FROM_OBJECT,
|
'type' => BreakingChangesFinder::BREAKING_CHANGE_INTERFACE_REMOVED_FROM_OBJECT,
|
||||||
'description' => 'Type1 no longer implements interface Interface1.'
|
'description' => 'Type1 no longer implements interface Interface1.'
|
||||||
],
|
],
|
||||||
];
|
];
|
||||||
|
|
||||||
$this->assertEquals(
|
$this->assertEquals(
|
||||||
$expected,
|
$expected,
|
||||||
FindBreakingChanges::findInterfacesRemovedFromObjectTypes($oldSchema, $newSchema));
|
BreakingChangesFinder::findInterfacesRemovedFromObjectTypes($oldSchema, $newSchema)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -1178,11 +1181,11 @@ class FindBreakingChangesTest extends TestCase
|
|||||||
|
|
||||||
$expectedBreakingChanges = [
|
$expectedBreakingChanges = [
|
||||||
[
|
[
|
||||||
'type' => FindBreakingChanges::BREAKING_CHANGE_TYPE_REMOVED,
|
'type' => BreakingChangesFinder::BREAKING_CHANGE_TYPE_REMOVED,
|
||||||
'description' => 'TypeThatGetsRemoved was removed.',
|
'description' => 'TypeThatGetsRemoved was removed.',
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
'type' => FindBreakingChanges::BREAKING_CHANGE_TYPE_REMOVED,
|
'type' => BreakingChangesFinder::BREAKING_CHANGE_TYPE_REMOVED,
|
||||||
'description' => 'TypeInUnion2 was removed.',
|
'description' => 'TypeInUnion2 was removed.',
|
||||||
],
|
],
|
||||||
/* This is reported in the js version because builtin sclar types are added on demand
|
/* This is reported in the js version because builtin sclar types are added on demand
|
||||||
@ -1192,52 +1195,52 @@ class FindBreakingChangesTest extends TestCase
|
|||||||
'description' => 'Int was removed.'
|
'description' => 'Int was removed.'
|
||||||
],*/
|
],*/
|
||||||
[
|
[
|
||||||
'type' => FindBreakingChanges::BREAKING_CHANGE_TYPE_CHANGED_KIND,
|
'type' => BreakingChangesFinder::BREAKING_CHANGE_TYPE_CHANGED_KIND,
|
||||||
'description' => 'TypeThatChangesType changed from an Object type to an Interface type.',
|
'description' => 'TypeThatChangesType changed from an Object type to an Interface type.',
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
'type' => FindBreakingChanges::BREAKING_CHANGE_FIELD_REMOVED,
|
'type' => BreakingChangesFinder::BREAKING_CHANGE_FIELD_REMOVED,
|
||||||
'description' => 'TypeThatHasBreakingFieldChanges.field1 was removed.',
|
'description' => 'TypeThatHasBreakingFieldChanges.field1 was removed.',
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
'type' => FindBreakingChanges::BREAKING_CHANGE_FIELD_CHANGED_KIND,
|
'type' => BreakingChangesFinder::BREAKING_CHANGE_FIELD_CHANGED_KIND,
|
||||||
'description' => 'TypeThatHasBreakingFieldChanges.field2 changed type from String to Boolean.',
|
'description' => 'TypeThatHasBreakingFieldChanges.field2 changed type from String to Boolean.',
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
'type' => FindBreakingChanges::BREAKING_CHANGE_TYPE_REMOVED_FROM_UNION,
|
'type' => BreakingChangesFinder::BREAKING_CHANGE_TYPE_REMOVED_FROM_UNION,
|
||||||
'description' => 'TypeInUnion2 was removed from union type UnionTypeThatLosesAType.',
|
'description' => 'TypeInUnion2 was removed from union type UnionTypeThatLosesAType.',
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
'type' => FindBreakingChanges::BREAKING_CHANGE_VALUE_REMOVED_FROM_ENUM,
|
'type' => BreakingChangesFinder::BREAKING_CHANGE_VALUE_REMOVED_FROM_ENUM,
|
||||||
'description' => 'VALUE0 was removed from enum type EnumTypeThatLosesAValue.',
|
'description' => 'VALUE0 was removed from enum type EnumTypeThatLosesAValue.',
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
'type' => FindBreakingChanges::BREAKING_CHANGE_ARG_CHANGED_KIND,
|
'type' => BreakingChangesFinder::BREAKING_CHANGE_ARG_CHANGED_KIND,
|
||||||
'description' => 'ArgThatChanges.field1 arg id has changed type from Int to String',
|
'description' => 'ArgThatChanges.field1 arg id has changed type from Int to String',
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
'type' => FindBreakingChanges::BREAKING_CHANGE_INTERFACE_REMOVED_FROM_OBJECT,
|
'type' => BreakingChangesFinder::BREAKING_CHANGE_INTERFACE_REMOVED_FROM_OBJECT,
|
||||||
'description' => 'TypeThatLosesInterface1 no longer implements interface Interface1.',
|
'description' => 'TypeThatLosesInterface1 no longer implements interface Interface1.',
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
'type' => FindBreakingChanges::BREAKING_CHANGE_DIRECTIVE_REMOVED,
|
'type' => BreakingChangesFinder::BREAKING_CHANGE_DIRECTIVE_REMOVED,
|
||||||
'description' => 'skip was removed',
|
'description' => 'skip was removed',
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
'type' => FindBreakingChanges::BREAKING_CHANGE_DIRECTIVE_ARG_REMOVED,
|
'type' => BreakingChangesFinder::BREAKING_CHANGE_DIRECTIVE_ARG_REMOVED,
|
||||||
'description' => 'arg1 was removed from DirectiveThatRemovesArg',
|
'description' => 'arg1 was removed from DirectiveThatRemovesArg',
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
'type' => FindBreakingChanges::BREAKING_CHANGE_NON_NULL_DIRECTIVE_ARG_ADDED,
|
'type' => BreakingChangesFinder::BREAKING_CHANGE_NON_NULL_DIRECTIVE_ARG_ADDED,
|
||||||
'description' => 'A non-null arg arg1 on directive NonNullDirectiveAdded was added',
|
'description' => 'A non-null arg arg1 on directive NonNullDirectiveAdded was added',
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
'type' => FindBreakingChanges::BREAKING_CHANGE_DIRECTIVE_LOCATION_REMOVED,
|
'type' => BreakingChangesFinder::BREAKING_CHANGE_DIRECTIVE_LOCATION_REMOVED,
|
||||||
'description' => 'QUERY was removed from Directive Name',
|
'description' => 'QUERY was removed from Directive Name',
|
||||||
]
|
]
|
||||||
];
|
];
|
||||||
|
|
||||||
$this->assertEquals($expectedBreakingChanges, FindBreakingChanges::findBreakingChanges($oldSchema, $newSchema));
|
$this->assertEquals($expectedBreakingChanges, BreakingChangesFinder::findBreakingChanges($oldSchema, $newSchema));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -1257,12 +1260,12 @@ class FindBreakingChangesTest extends TestCase
|
|||||||
|
|
||||||
$expectedBreakingChanges = [
|
$expectedBreakingChanges = [
|
||||||
[
|
[
|
||||||
'type' => FindBreakingChanges::BREAKING_CHANGE_DIRECTIVE_REMOVED,
|
'type' => BreakingChangesFinder::BREAKING_CHANGE_DIRECTIVE_REMOVED,
|
||||||
'description' => "{$includeDirective->name} was removed",
|
'description' => "{$includeDirective->name} was removed",
|
||||||
]
|
]
|
||||||
];
|
];
|
||||||
|
|
||||||
$this->assertEquals($expectedBreakingChanges, FindBreakingChanges::findRemovedDirectives($oldSchema, $newSchema));
|
$this->assertEquals($expectedBreakingChanges, BreakingChangesFinder::findRemovedDirectives($oldSchema, $newSchema));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -1280,12 +1283,12 @@ class FindBreakingChangesTest extends TestCase
|
|||||||
|
|
||||||
$expectedBreakingChanges = [
|
$expectedBreakingChanges = [
|
||||||
[
|
[
|
||||||
'type' => FindBreakingChanges::BREAKING_CHANGE_DIRECTIVE_REMOVED,
|
'type' => BreakingChangesFinder::BREAKING_CHANGE_DIRECTIVE_REMOVED,
|
||||||
'description' => "{$deprecatedDirective->name} was removed",
|
'description' => "{$deprecatedDirective->name} was removed",
|
||||||
]
|
]
|
||||||
];
|
];
|
||||||
|
|
||||||
$this->assertEquals($expectedBreakingChanges, FindBreakingChanges::findRemovedDirectives($oldSchema, $newSchema));
|
$this->assertEquals($expectedBreakingChanges, BreakingChangesFinder::findRemovedDirectives($oldSchema, $newSchema));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -1318,12 +1321,12 @@ class FindBreakingChangesTest extends TestCase
|
|||||||
|
|
||||||
$expectedBreakingChanges = [
|
$expectedBreakingChanges = [
|
||||||
[
|
[
|
||||||
'type' => FindBreakingChanges::BREAKING_CHANGE_DIRECTIVE_ARG_REMOVED,
|
'type' => BreakingChangesFinder::BREAKING_CHANGE_DIRECTIVE_ARG_REMOVED,
|
||||||
'description' => "arg1 was removed from DirectiveWithArg",
|
'description' => "arg1 was removed from DirectiveWithArg",
|
||||||
]
|
]
|
||||||
];
|
];
|
||||||
|
|
||||||
$this->assertEquals($expectedBreakingChanges, FindBreakingChanges::findRemovedDirectiveArgs($oldSchema, $newSchema));
|
$this->assertEquals($expectedBreakingChanges, BreakingChangesFinder::findRemovedDirectiveArgs($oldSchema, $newSchema));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -1357,12 +1360,12 @@ class FindBreakingChangesTest extends TestCase
|
|||||||
|
|
||||||
$expectedBreakingChanges = [
|
$expectedBreakingChanges = [
|
||||||
[
|
[
|
||||||
'type' => FindBreakingChanges::BREAKING_CHANGE_NON_NULL_DIRECTIVE_ARG_ADDED,
|
'type' => BreakingChangesFinder::BREAKING_CHANGE_NON_NULL_DIRECTIVE_ARG_ADDED,
|
||||||
'description' => "A non-null arg arg1 on directive DirectiveName was added",
|
'description' => "A non-null arg arg1 on directive DirectiveName was added",
|
||||||
]
|
]
|
||||||
];
|
];
|
||||||
|
|
||||||
$this->assertEquals($expectedBreakingChanges, FindBreakingChanges::findAddedNonNullDirectiveArgs($oldSchema, $newSchema));
|
$this->assertEquals($expectedBreakingChanges, BreakingChangesFinder::findAddedNonNullDirectiveArgs($oldSchema, $newSchema));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -1380,7 +1383,7 @@ class FindBreakingChangesTest extends TestCase
|
|||||||
'locations' => [DirectiveLocation::FIELD_DEFINITION],
|
'locations' => [DirectiveLocation::FIELD_DEFINITION],
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$this->assertEquals([DirectiveLocation::QUERY], FindBreakingChanges::findRemovedLocationsForDirective($d1, $d2));
|
$this->assertEquals([DirectiveLocation::QUERY], BreakingChangesFinder::findRemovedLocationsForDirective($d1, $d2));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -1411,12 +1414,12 @@ class FindBreakingChangesTest extends TestCase
|
|||||||
|
|
||||||
$expectedBreakingChanges = [
|
$expectedBreakingChanges = [
|
||||||
[
|
[
|
||||||
'type' => FindBreakingChanges::BREAKING_CHANGE_DIRECTIVE_LOCATION_REMOVED,
|
'type' => BreakingChangesFinder::BREAKING_CHANGE_DIRECTIVE_LOCATION_REMOVED,
|
||||||
'description' => "QUERY was removed from Directive Name",
|
'description' => "QUERY was removed from Directive Name",
|
||||||
]
|
]
|
||||||
];
|
];
|
||||||
|
|
||||||
$this->assertEquals($expectedBreakingChanges, FindBreakingChanges::findRemovedDirectiveLocations($oldSchema, $newSchema));
|
$this->assertEquals($expectedBreakingChanges, BreakingChangesFinder::findRemovedDirectiveLocations($oldSchema, $newSchema));
|
||||||
}
|
}
|
||||||
|
|
||||||
// DESCRIBE: findDangerousChanges
|
// DESCRIBE: findDangerousChanges
|
||||||
@ -1469,14 +1472,14 @@ class FindBreakingChangesTest extends TestCase
|
|||||||
|
|
||||||
$expected = [
|
$expected = [
|
||||||
[
|
[
|
||||||
'type' => FindBreakingChanges::DANGEROUS_CHANGE_ARG_DEFAULT_VALUE_CHANGED,
|
'type' => BreakingChangesFinder::DANGEROUS_CHANGE_ARG_DEFAULT_VALUE_CHANGED,
|
||||||
'description' => 'Type1.field1 arg name has changed defaultValue'
|
'description' => 'Type1.field1 arg name has changed defaultValue'
|
||||||
]
|
]
|
||||||
];
|
];
|
||||||
|
|
||||||
$this->assertEquals(
|
$this->assertEquals(
|
||||||
$expected,
|
$expected,
|
||||||
FindBreakingChanges::findArgChanges($oldSchema, $newSchema)['dangerousChanges']
|
BreakingChangesFinder::findArgChanges($oldSchema, $newSchema)['dangerousChanges']
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1513,14 +1516,14 @@ class FindBreakingChangesTest extends TestCase
|
|||||||
|
|
||||||
$expected = [
|
$expected = [
|
||||||
[
|
[
|
||||||
'type' => FindBreakingChanges::DANGEROUS_CHANGE_VALUE_ADDED_TO_ENUM,
|
'type' => BreakingChangesFinder::DANGEROUS_CHANGE_VALUE_ADDED_TO_ENUM,
|
||||||
'description' => 'VALUE2 was added to enum type EnumType1.'
|
'description' => 'VALUE2 was added to enum type EnumType1.'
|
||||||
]
|
]
|
||||||
];
|
];
|
||||||
|
|
||||||
$this->assertEquals(
|
$this->assertEquals(
|
||||||
$expected,
|
$expected,
|
||||||
FindBreakingChanges::findValuesAddedToEnums($oldSchema, $newSchema)
|
BreakingChangesFinder::findValuesAddedToEnums($oldSchema, $newSchema)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1562,14 +1565,14 @@ class FindBreakingChangesTest extends TestCase
|
|||||||
|
|
||||||
$expected = [
|
$expected = [
|
||||||
[
|
[
|
||||||
'type' => FindBreakingChanges::DANGEROUS_CHANGE_INTERFACE_ADDED_TO_OBJECT,
|
'type' => BreakingChangesFinder::DANGEROUS_CHANGE_INTERFACE_ADDED_TO_OBJECT,
|
||||||
'description' => 'Interface1 added to interfaces implemented by Type1.'
|
'description' => 'Interface1 added to interfaces implemented by Type1.'
|
||||||
]
|
]
|
||||||
];
|
];
|
||||||
|
|
||||||
$this->assertEquals(
|
$this->assertEquals(
|
||||||
$expected,
|
$expected,
|
||||||
FindBreakingChanges::findInterfacesAddedToObjectTypes($oldSchema, $newSchema)
|
BreakingChangesFinder::findInterfacesAddedToObjectTypes($oldSchema, $newSchema)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1620,14 +1623,14 @@ class FindBreakingChangesTest extends TestCase
|
|||||||
|
|
||||||
$expected = [
|
$expected = [
|
||||||
[
|
[
|
||||||
'type' => FindBreakingChanges::DANGEROUS_CHANGE_TYPE_ADDED_TO_UNION,
|
'type' => BreakingChangesFinder::DANGEROUS_CHANGE_TYPE_ADDED_TO_UNION,
|
||||||
'description' => 'Type2 was added to union type UnionType1.'
|
'description' => 'Type2 was added to union type UnionType1.'
|
||||||
]
|
]
|
||||||
];
|
];
|
||||||
|
|
||||||
$this->assertEquals(
|
$this->assertEquals(
|
||||||
$expected,
|
$expected,
|
||||||
FindBreakingChanges::findTypesAddedToUnions($oldSchema, $newSchema)
|
BreakingChangesFinder::findTypesAddedToUnions($oldSchema, $newSchema)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1673,11 +1676,11 @@ class FindBreakingChangesTest extends TestCase
|
|||||||
$expectedFieldChanges = [
|
$expectedFieldChanges = [
|
||||||
[
|
[
|
||||||
'description' => 'A nullable field field2 on input type InputType1 was added.',
|
'description' => 'A nullable field field2 on input type InputType1 was added.',
|
||||||
'type' => FindBreakingChanges::DANGEROUS_CHANGE_NULLABLE_INPUT_FIELD_ADDED
|
'type' => BreakingChangesFinder::DANGEROUS_CHANGE_NULLABLE_INPUT_FIELD_ADDED,
|
||||||
],
|
],
|
||||||
];
|
];
|
||||||
|
|
||||||
$this->assertEquals($expectedFieldChanges, FindBreakingChanges::findFieldsThatChangedTypeOnInputObjectTypes($oldSchema, $newSchema)['dangerousChanges']);
|
$this->assertEquals($expectedFieldChanges, BreakingChangesFinder::findFieldsThatChangedTypeOnInputObjectTypes($oldSchema, $newSchema)['dangerousChanges']);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -1797,23 +1800,23 @@ class FindBreakingChangesTest extends TestCase
|
|||||||
$expectedDangerousChanges = [
|
$expectedDangerousChanges = [
|
||||||
[
|
[
|
||||||
'description' => 'Type1.field1 arg name has changed defaultValue',
|
'description' => 'Type1.field1 arg name has changed defaultValue',
|
||||||
'type' => FindBreakingChanges::DANGEROUS_CHANGE_ARG_DEFAULT_VALUE_CHANGED
|
'type' => BreakingChangesFinder::DANGEROUS_CHANGE_ARG_DEFAULT_VALUE_CHANGED,
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
'description' => 'VALUE2 was added to enum type EnumType1.',
|
'description' => 'VALUE2 was added to enum type EnumType1.',
|
||||||
'type' => FindBreakingChanges::DANGEROUS_CHANGE_VALUE_ADDED_TO_ENUM
|
'type' => BreakingChangesFinder::DANGEROUS_CHANGE_VALUE_ADDED_TO_ENUM,
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
'type' => FindBreakingChanges::DANGEROUS_CHANGE_INTERFACE_ADDED_TO_OBJECT,
|
'type' => BreakingChangesFinder::DANGEROUS_CHANGE_INTERFACE_ADDED_TO_OBJECT,
|
||||||
'description' => 'Interface1 added to interfaces implemented by TypeThatGainsInterface1.',
|
'description' => 'Interface1 added to interfaces implemented by TypeThatGainsInterface1.',
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
'type' => FindBreakingChanges::DANGEROUS_CHANGE_TYPE_ADDED_TO_UNION,
|
'type' => BreakingChangesFinder::DANGEROUS_CHANGE_TYPE_ADDED_TO_UNION,
|
||||||
'description' => 'TypeInUnion2 was added to union type UnionTypeThatGainsAType.',
|
'description' => 'TypeInUnion2 was added to union type UnionTypeThatGainsAType.',
|
||||||
]
|
]
|
||||||
];
|
];
|
||||||
|
|
||||||
$this->assertEquals($expectedDangerousChanges, FindBreakingChanges::findDangerousChanges($oldSchema, $newSchema));
|
$this->assertEquals($expectedDangerousChanges, BreakingChangesFinder::findDangerousChanges($oldSchema, $newSchema));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -1865,10 +1868,10 @@ class FindBreakingChangesTest extends TestCase
|
|||||||
$expectedFieldChanges = [
|
$expectedFieldChanges = [
|
||||||
[
|
[
|
||||||
'description' => 'A nullable arg arg2 on Type1.field1 was added',
|
'description' => 'A nullable arg arg2 on Type1.field1 was added',
|
||||||
'type' => FindBreakingChanges::DANGEROUS_CHANGE_NULLABLE_ARG_ADDED
|
'type' => BreakingChangesFinder::DANGEROUS_CHANGE_NULLABLE_ARG_ADDED,
|
||||||
],
|
],
|
||||||
];
|
];
|
||||||
|
|
||||||
$this->assertEquals($expectedFieldChanges, FindBreakingChanges::findArgChanges($oldSchema, $newSchema)['dangerousChanges']);
|
$this->assertEquals($expectedFieldChanges, BreakingChangesFinder::findArgChanges($oldSchema, $newSchema)['dangerousChanges']);
|
||||||
}
|
}
|
||||||
}
|
}
|
Loading…
Reference in New Issue
Block a user