mirror of
https://github.com/retailcrm/graphql-php.git
synced 2024-11-25 06:16:05 +03:00
Added deprecated directive; changed custom directives handling in schema; various minor tweaks
This commit is contained in:
parent
7625d6abf1
commit
236021acf8
24
UPGRADE.md
24
UPGRADE.md
@ -2,7 +2,27 @@
|
||||
|
||||
## Upgrade v0.7.x > v1.0.x
|
||||
|
||||
### 1. Protected property and method naming
|
||||
### 1. Custom directives handling
|
||||
When passing custom directives to schema, default directives (like `@skip` and `@include`)
|
||||
are not added to schema automatically anymore. If you need them - add them explicitly with your other directives
|
||||
|
||||
Before the change:
|
||||
```php
|
||||
$schema = new Schema([
|
||||
// ...
|
||||
'directives' => [$myDirective]
|
||||
]);
|
||||
```
|
||||
|
||||
After the change:
|
||||
```php
|
||||
$schema = new Schema([
|
||||
// ...
|
||||
'directives' => array_merge(GraphQL::getInternalDirectives(), [$myDirective])
|
||||
]);
|
||||
```
|
||||
|
||||
### 2. Protected property and method naming
|
||||
In order to unify coding style, leading underscores were removed from all private and protected properties
|
||||
and methods.
|
||||
|
||||
@ -19,7 +39,7 @@ GraphQL\Schema::$queryType;
|
||||
So if you rely on any protected properties or methods of any GraphQL class, make sure to
|
||||
delete leading underscores.
|
||||
|
||||
### 2. Returning closure from field resolver
|
||||
### 3. Returning closure from field resolver
|
||||
Previously when you returned closure from any resolver, expected signature of this closure
|
||||
was `function($sourceValue)`, new signature is `function($args, $context)`
|
||||
(now mirrors reference graphql-js implementation)
|
||||
|
@ -234,11 +234,10 @@ class Values
|
||||
|
||||
if ($type instanceof ListOfType) {
|
||||
$itemType = $type->getWrappedType();
|
||||
// TODO: support iterable input
|
||||
if (is_array($value)) {
|
||||
return array_map(function ($item) use ($itemType) {
|
||||
if (is_array($value) || $value instanceof \Traversable) {
|
||||
return Utils::map($value, function($item) use ($itemType) {
|
||||
return Values::coerceValue($itemType, $item);
|
||||
}, $value);
|
||||
});
|
||||
} else {
|
||||
return [self::coerceValue($itemType, $value)];
|
||||
}
|
||||
|
@ -6,6 +6,7 @@ use GraphQL\Executor\Executor;
|
||||
use GraphQL\Language\AST\Document;
|
||||
use GraphQL\Language\Parser;
|
||||
use GraphQL\Language\Source;
|
||||
use GraphQL\Type\Definition\Directive;
|
||||
use GraphQL\Validator\DocumentValidator;
|
||||
use GraphQL\Validator\Rules\QueryComplexity;
|
||||
|
||||
@ -57,4 +58,12 @@ class GraphQL
|
||||
return new ExecutionResult(null, [$e]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public static function getInternalDirectives()
|
||||
{
|
||||
return array_values(Directive::getInternalDirectives());
|
||||
}
|
||||
}
|
||||
|
@ -6,7 +6,7 @@ class Document extends Node
|
||||
public $kind = Node::DOCUMENT;
|
||||
|
||||
/**
|
||||
* @var array<Definition>
|
||||
* @var Definition[]
|
||||
*/
|
||||
public $definitions;
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
<?php
|
||||
namespace GraphQL\Language;
|
||||
|
||||
class SourceLocation
|
||||
class SourceLocation implements \JsonSerializable
|
||||
{
|
||||
public $line;
|
||||
public $column;
|
||||
@ -12,6 +12,9 @@ class SourceLocation
|
||||
$this->column = $col;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function toArray()
|
||||
{
|
||||
return [
|
||||
@ -19,4 +22,24 @@ class SourceLocation
|
||||
'column' => $this->column
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function toSerializableArray()
|
||||
{
|
||||
return $this->toArray();
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify data which should be serialized to JSON
|
||||
* @link http://php.net/manual/en/jsonserializable.jsonserialize.php
|
||||
* @return mixed data which can be serialized by <b>json_encode</b>,
|
||||
* which is a value of any type other than a resource.
|
||||
* @since 5.4.0
|
||||
*/
|
||||
function jsonSerialize()
|
||||
{
|
||||
return $this->toSerializableArray();
|
||||
}
|
||||
}
|
||||
|
@ -12,7 +12,30 @@ use GraphQL\Type\Definition\WrappingType;
|
||||
use GraphQL\Type\Introspection;
|
||||
|
||||
/**
|
||||
* Class Schema
|
||||
* Schema Definition
|
||||
*
|
||||
* A Schema is created by supplying the root types of each type of operation:
|
||||
* query, mutation (optional) and subscription (optional). A schema definition is
|
||||
* then supplied to the validator and executor.
|
||||
*
|
||||
* Example:
|
||||
*
|
||||
* $schema = new GraphQL\Schema([
|
||||
* 'query' => $MyAppQueryRootType,
|
||||
* 'mutation' => $MyAppMutationRootType,
|
||||
* ]);
|
||||
*
|
||||
* Note: If an array of `directives` are provided to GraphQL\Schema, that will be
|
||||
* the exact list of directives represented and allowed. If `directives` is not
|
||||
* provided then a default set of the specified directives (e.g. @include and
|
||||
* @skip) will be used. If you wish to provide *additional* directives to these
|
||||
* specified directives, you must explicitly declare them. Example:
|
||||
*
|
||||
* $mySchema = new GraphQL\Schema([
|
||||
* ...
|
||||
* 'directives' => array_merge(GraphQL::getInternalDirectives(), [ $myCustomDirective ]),
|
||||
* ])
|
||||
*
|
||||
* @package GraphQL
|
||||
*/
|
||||
class Schema
|
||||
@ -119,10 +142,7 @@ class Schema
|
||||
"Schema directives must be Directive[] if provided but got " . Utils::getVariableType($config['directives'])
|
||||
);
|
||||
|
||||
$this->directives = array_merge($config['directives'], [
|
||||
Directive::includeDirective(),
|
||||
Directive::skipDirective()
|
||||
]);
|
||||
$this->directives = $config['directives'] ?: GraphQL::getInternalDirectives();
|
||||
|
||||
// Build type map now to detect any errors within this schema.
|
||||
$initialTypes = [
|
||||
@ -222,6 +242,15 @@ class Schema
|
||||
foreach ($this->getPossibleTypes($abstractType) as $type) {
|
||||
$tmp[$type->name] = true;
|
||||
}
|
||||
|
||||
Utils::invariant(
|
||||
!empty($tmp),
|
||||
'Could not find possible implementing types for $%s ' .
|
||||
'in schema. Check that schema.types is defined and is an array of ' .
|
||||
'all possible types in the schema.',
|
||||
$abstractType->name
|
||||
);
|
||||
|
||||
$this->possibleTypeMap[$abstractType->name] = $tmp;
|
||||
}
|
||||
|
||||
|
@ -7,6 +7,8 @@ namespace GraphQL\Type\Definition;
|
||||
*/
|
||||
class Directive
|
||||
{
|
||||
const DEFAULT_DEPRECATION_REASON = 'No longer supported';
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
@ -16,6 +18,7 @@ class Directive
|
||||
* @var array
|
||||
*/
|
||||
public static $directiveLocations = [
|
||||
// Operations:
|
||||
'QUERY' => 'QUERY',
|
||||
'MUTATION' => 'MUTATION',
|
||||
'SUBSCRIPTION' => 'SUBSCRIPTION',
|
||||
@ -23,6 +26,19 @@ class Directive
|
||||
'FRAGMENT_DEFINITION' => 'FRAGMENT_DEFINITION',
|
||||
'FRAGMENT_SPREAD' => 'FRAGMENT_SPREAD',
|
||||
'INLINE_FRAGMENT' => 'INLINE_FRAGMENT',
|
||||
|
||||
// Schema Definitions
|
||||
'SCHEMA' => 'SCHEMA',
|
||||
'SCALAR' => 'SCALAR',
|
||||
'OBJECT' => 'OBJECT',
|
||||
'FIELD_DEFINITION' => 'FIELD_DEFINITION',
|
||||
'ARGUMENT_DEFINITION' => 'ARGUMENT_DEFINITION',
|
||||
'INTERFACE' => 'INTERFACE',
|
||||
'UNION' => 'UNION',
|
||||
'ENUM' => 'ENUM',
|
||||
'ENUM_VALUE' => 'ENUM_VALUE',
|
||||
'INPUT_OBJECT' => 'INPUT_OBJECT',
|
||||
'INPUT_FIELD_DEFINITION' => 'INPUT_FIELD_DEFINITION'
|
||||
];
|
||||
|
||||
/**
|
||||
@ -43,6 +59,15 @@ class Directive
|
||||
return $internal['skip'];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Directive
|
||||
*/
|
||||
public static function deprecatedDirective()
|
||||
{
|
||||
$internal = self::getInternalDirectives();
|
||||
return $internal['deprecated'];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
@ -81,6 +106,25 @@ class Directive
|
||||
'description' => 'Skipped when true'
|
||||
])
|
||||
]
|
||||
]),
|
||||
'deprecated' => new self([
|
||||
'name' => 'deprecated',
|
||||
'description' => 'Marks an element of a GraphQL schema as no longer supported.',
|
||||
'locations' => [
|
||||
self::$directiveLocations['FIELD_DEFINITION'],
|
||||
self::$directiveLocations['ENUM_VALUE']
|
||||
],
|
||||
'args' => [
|
||||
new FieldArgument([
|
||||
'name' => 'reason',
|
||||
'type' => Type::string(),
|
||||
'description' =>
|
||||
'Explains why this element was deprecated, usually also including a ' .
|
||||
'suggestion for how to access supported similar data. Formatted ' .
|
||||
'in [Markdown](https://daringfireball.net/projects/markdown/).',
|
||||
'defaultValue' => self::DEFAULT_DEPRECATION_REASON
|
||||
])
|
||||
]
|
||||
])
|
||||
];
|
||||
}
|
||||
|
@ -149,7 +149,6 @@ class FieldDefinition
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated as of 17.10.2016 in favor of setting 'fields' as closure per ObjectType vs setting on field level
|
||||
* @return Type
|
||||
*/
|
||||
public function getType()
|
||||
|
@ -39,7 +39,6 @@ class InputObjectField
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated in favor of defining all object 'fields' as closure vs defining closure per field
|
||||
* @return mixed
|
||||
*/
|
||||
public function getType()
|
||||
|
@ -36,6 +36,11 @@ class ResolveInfo
|
||||
*/
|
||||
public $parentType;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
public $path;
|
||||
|
||||
/**
|
||||
* @var Schema
|
||||
*/
|
||||
|
@ -72,6 +72,11 @@ class UnionType extends Type implements AbstractType, OutputType, CompositeType
|
||||
{
|
||||
if ($this->types instanceof \Closure) {
|
||||
$this->types = call_user_func($this->types);
|
||||
Utils::invariant(
|
||||
is_array($this->types),
|
||||
'Closure for option "types" of union "%s" is expected to return array of types',
|
||||
$this->name
|
||||
);
|
||||
}
|
||||
return $this->types;
|
||||
}
|
||||
|
@ -221,6 +221,31 @@ class Utils
|
||||
return is_object($var) ? get_class($var) : gettype($var);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $var
|
||||
* @return string
|
||||
*/
|
||||
public static function printSafe($var)
|
||||
{
|
||||
if ($var instanceof Type) {
|
||||
// FIXME: Replace with schema printer call
|
||||
if ($var instanceof WrappingType) {
|
||||
$var = $var->getWrappedType(true);
|
||||
}
|
||||
return $var->name;
|
||||
}
|
||||
if (is_object($var)) {
|
||||
return 'instance of ' . get_class($var);
|
||||
}
|
||||
if (is_scalar($var)) {
|
||||
return (string) $var;
|
||||
}
|
||||
if (null === $var) {
|
||||
return 'null';
|
||||
}
|
||||
return gettype($var);
|
||||
}
|
||||
|
||||
/**
|
||||
* UTF-8 compatible chr()
|
||||
*
|
||||
|
@ -225,6 +225,54 @@ class DefinitionTest extends \PHPUnit_Framework_TestCase
|
||||
$this->assertEquals($sub->name, 'articleSubscribe');
|
||||
}
|
||||
|
||||
/**
|
||||
* @it defines an enum type with deprecated value
|
||||
*/
|
||||
public function testDefinesEnumTypeWithDeprecatedValue()
|
||||
{
|
||||
$enumTypeWithDeprecatedValue = new EnumType([
|
||||
'name' => 'EnumWithDeprecatedValue',
|
||||
'values' => [
|
||||
'foo' => ['deprecationReason' => 'Just because']
|
||||
]
|
||||
]);
|
||||
|
||||
$value = $enumTypeWithDeprecatedValue->getValues()[0];
|
||||
|
||||
$this->assertEquals([
|
||||
'name' => 'foo',
|
||||
'description' => null,
|
||||
'deprecationReason' => 'Just because',
|
||||
'value' => 'foo'
|
||||
], (array) $value);
|
||||
|
||||
$this->assertEquals(true, $value->isDeprecated());
|
||||
}
|
||||
|
||||
/**
|
||||
* @it defines an object type with deprecated field
|
||||
*/
|
||||
public function testDefinesAnObjectTypeWithDeprecatedField()
|
||||
{
|
||||
$TypeWithDeprecatedField = new ObjectType([
|
||||
'name' => 'foo',
|
||||
'fields' => [
|
||||
'bar' => [
|
||||
'type' => Type::string(),
|
||||
'deprecationReason' => 'A terrible reason'
|
||||
]
|
||||
]
|
||||
]);
|
||||
|
||||
$field = $TypeWithDeprecatedField->getField('bar');
|
||||
|
||||
$this->assertEquals(Type::string(), $field->getType());
|
||||
$this->assertEquals(true, $field->isDeprecated());
|
||||
$this->assertEquals('A terrible reason', $field->deprecationReason);
|
||||
$this->assertEquals('bar', $field->name);
|
||||
$this->assertEquals([], $field->args);
|
||||
}
|
||||
|
||||
/**
|
||||
* @it includes nested input objects in the map
|
||||
*/
|
||||
@ -424,6 +472,21 @@ class DefinitionTest extends \PHPUnit_Framework_TestCase
|
||||
Config::disableValidation();
|
||||
}
|
||||
|
||||
/**
|
||||
* @it allows a thunk for Union\'s types
|
||||
*/
|
||||
public function testAllowsThunkForUnionTypes()
|
||||
{
|
||||
$union = new UnionType([
|
||||
'name' => 'ThunkUnion',
|
||||
'types' => function() {return [$this->objectType]; }
|
||||
]);
|
||||
|
||||
$types = $union->getTypes();
|
||||
$this->assertEquals(1, count($types));
|
||||
$this->assertSame($this->objectType, $types[0]);
|
||||
}
|
||||
|
||||
public function testAllowsRecursiveDefinitions()
|
||||
{
|
||||
// See https://github.com/webonyx/graphql-php/issues/16
|
||||
|
@ -1,6 +1,7 @@
|
||||
<?php
|
||||
namespace GraphQL\Tests\Validator;
|
||||
|
||||
use GraphQL\GraphQL;
|
||||
use GraphQL\Language\Parser;
|
||||
use GraphQL\Schema;
|
||||
use GraphQL\Type\Definition\Directive;
|
||||
@ -291,12 +292,12 @@ abstract class TestCase extends \PHPUnit_Framework_TestCase
|
||||
|
||||
$defaultSchema = new Schema([
|
||||
'query' => $queryRoot,
|
||||
'directives' => [
|
||||
'directives' => array_merge(GraphQL::getInternalDirectives(), [
|
||||
new Directive([
|
||||
'name' => 'operationOnly',
|
||||
'locations' => [ 'QUERY' ],
|
||||
])
|
||||
]
|
||||
])
|
||||
]);
|
||||
return $defaultSchema;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user