mirror of
https://github.com/retailcrm/graphql-php.git
synced 2024-11-25 14:26:08 +03:00
Ability to override internal types (closes #401)
This commit is contained in:
parent
bdbb30c604
commit
e1b4d438db
@ -1,9 +1,13 @@
|
|||||||
# Changelog
|
# Changelog
|
||||||
## dev-master
|
## dev-master
|
||||||
- Spec compliance: error extensions are displayed under `extensions` key
|
This release brings several breaking changes. Please refer to [UPGRADE](UPGRADE.md) document for details.
|
||||||
|
|
||||||
|
New features and notable changes:
|
||||||
|
- Spec compliance: error category, debug information and 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`)
|
- `FindBreakingChanges` renamed to `BreakingChangesFinder` (NS `GraphQL\Utils`)
|
||||||
|
- Added ability to override standard types via `GraphQL::overrideStandardTypes(array $types)`
|
||||||
|
|
||||||
#### v0.12.5
|
#### v0.12.5
|
||||||
- Execution performance optimization for lists
|
- Execution performance optimization for lists
|
||||||
|
@ -276,7 +276,20 @@ class GraphQL
|
|||||||
*/
|
*/
|
||||||
public static function getStandardTypes() : array
|
public static function getStandardTypes() : array
|
||||||
{
|
{
|
||||||
return array_values(Type::getInternalTypes());
|
return array_values(Type::getStandardTypes());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Replaces standard types with types from this list (matching by name)
|
||||||
|
* Standard types not listed here remain untouched.
|
||||||
|
*
|
||||||
|
* @param Type[] $types
|
||||||
|
*
|
||||||
|
* @api
|
||||||
|
*/
|
||||||
|
public static function overrideStandardTypes(array $types)
|
||||||
|
{
|
||||||
|
Type::overrideStandardTypes($types);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -291,6 +304,11 @@ class GraphQL
|
|||||||
return array_values(DocumentValidator::defaultRules());
|
return array_values(DocumentValidator::defaultRules());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set default resolver implementation
|
||||||
|
*
|
||||||
|
* @api
|
||||||
|
*/
|
||||||
public static function setDefaultFieldResolver(callable $fn) : void
|
public static function setDefaultFieldResolver(callable $fn) : void
|
||||||
{
|
{
|
||||||
Executor::setDefaultFieldResolver($fn);
|
Executor::setDefaultFieldResolver($fn);
|
||||||
|
@ -15,8 +15,11 @@ use ReflectionClass;
|
|||||||
use Throwable;
|
use Throwable;
|
||||||
use function array_keys;
|
use function array_keys;
|
||||||
use function array_merge;
|
use function array_merge;
|
||||||
|
use function implode;
|
||||||
use function in_array;
|
use function in_array;
|
||||||
use function preg_replace;
|
use function preg_replace;
|
||||||
|
use function trigger_error;
|
||||||
|
use const E_USER_DEPRECATED;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Registry of standard GraphQL types
|
* Registry of standard GraphQL types
|
||||||
@ -31,7 +34,7 @@ abstract class Type implements JsonSerializable
|
|||||||
public const ID = 'ID';
|
public const ID = 'ID';
|
||||||
|
|
||||||
/** @var Type[] */
|
/** @var Type[] */
|
||||||
private static $internalTypes;
|
private static $standardTypes;
|
||||||
|
|
||||||
/** @var Type[] */
|
/** @var Type[] */
|
||||||
private static $builtInTypes;
|
private static $builtInTypes;
|
||||||
@ -58,7 +61,7 @@ abstract class Type implements JsonSerializable
|
|||||||
*/
|
*/
|
||||||
public static function id()
|
public static function id()
|
||||||
{
|
{
|
||||||
return self::getInternalType(self::ID);
|
return self::getStandardType(self::ID);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -66,10 +69,10 @@ abstract class Type implements JsonSerializable
|
|||||||
*
|
*
|
||||||
* @return (IDType|StringType|FloatType|IntType|BooleanType)[]|IDType|StringType|FloatType|IntType|BooleanType
|
* @return (IDType|StringType|FloatType|IntType|BooleanType)[]|IDType|StringType|FloatType|IntType|BooleanType
|
||||||
*/
|
*/
|
||||||
private static function getInternalType($name = null)
|
private static function getStandardType($name = null)
|
||||||
{
|
{
|
||||||
if (self::$internalTypes === null) {
|
if (self::$standardTypes === null) {
|
||||||
self::$internalTypes = [
|
self::$standardTypes = [
|
||||||
self::ID => new IDType(),
|
self::ID => new IDType(),
|
||||||
self::STRING => new StringType(),
|
self::STRING => new StringType(),
|
||||||
self::FLOAT => new FloatType(),
|
self::FLOAT => new FloatType(),
|
||||||
@ -78,7 +81,7 @@ abstract class Type implements JsonSerializable
|
|||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
return $name ? self::$internalTypes[$name] : self::$internalTypes;
|
return $name ? self::$standardTypes[$name] : self::$standardTypes;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -88,7 +91,7 @@ abstract class Type implements JsonSerializable
|
|||||||
*/
|
*/
|
||||||
public static function string()
|
public static function string()
|
||||||
{
|
{
|
||||||
return self::getInternalType(self::STRING);
|
return self::getStandardType(self::STRING);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -98,7 +101,7 @@ abstract class Type implements JsonSerializable
|
|||||||
*/
|
*/
|
||||||
public static function boolean()
|
public static function boolean()
|
||||||
{
|
{
|
||||||
return self::getInternalType(self::BOOLEAN);
|
return self::getStandardType(self::BOOLEAN);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -108,7 +111,7 @@ abstract class Type implements JsonSerializable
|
|||||||
*/
|
*/
|
||||||
public static function int()
|
public static function int()
|
||||||
{
|
{
|
||||||
return self::getInternalType(self::INT);
|
return self::getStandardType(self::INT);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -118,7 +121,7 @@ abstract class Type implements JsonSerializable
|
|||||||
*/
|
*/
|
||||||
public static function float()
|
public static function float()
|
||||||
{
|
{
|
||||||
return self::getInternalType(self::FLOAT);
|
return self::getStandardType(self::FLOAT);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -166,7 +169,7 @@ abstract class Type implements JsonSerializable
|
|||||||
if (self::$builtInTypes === null) {
|
if (self::$builtInTypes === null) {
|
||||||
self::$builtInTypes = array_merge(
|
self::$builtInTypes = array_merge(
|
||||||
Introspection::getTypes(),
|
Introspection::getTypes(),
|
||||||
self::getInternalTypes()
|
self::getStandardTypes()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -178,9 +181,44 @@ abstract class Type implements JsonSerializable
|
|||||||
*
|
*
|
||||||
* @return Type[]
|
* @return Type[]
|
||||||
*/
|
*/
|
||||||
|
public static function getStandardTypes()
|
||||||
|
{
|
||||||
|
return self::getStandardType();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated Use method getStandardTypes() instead
|
||||||
|
*
|
||||||
|
* @return Type[]
|
||||||
|
*/
|
||||||
public static function getInternalTypes()
|
public static function getInternalTypes()
|
||||||
{
|
{
|
||||||
return self::getInternalType();
|
trigger_error(__METHOD__ . ' is deprecated. Use Type::getStandardTypes() instead', E_USER_DEPRECATED);
|
||||||
|
return self::getStandardTypes();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param Type[] $types
|
||||||
|
*/
|
||||||
|
public static function overrideStandardTypes(array $types)
|
||||||
|
{
|
||||||
|
$standardTypes = self::getStandardTypes();
|
||||||
|
foreach ($types as $type) {
|
||||||
|
Utils::invariant(
|
||||||
|
$type instanceof Type,
|
||||||
|
'Expecting instance of %s, got %s',
|
||||||
|
self::class,
|
||||||
|
Utils::printSafe($type)
|
||||||
|
);
|
||||||
|
Utils::invariant(
|
||||||
|
isset($type->name, $standardTypes[$type->name]),
|
||||||
|
'Expecting one of the following names for a standard type: %s, got %s',
|
||||||
|
implode(', ', array_keys($standardTypes)),
|
||||||
|
Utils::printSafe($type->name ?? null)
|
||||||
|
);
|
||||||
|
$standardTypes[$type->name] = $type;
|
||||||
|
}
|
||||||
|
self::$standardTypes = $standardTypes;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -143,7 +143,7 @@ class Schema
|
|||||||
$this->resolvedTypes[$type->name] = $type;
|
$this->resolvedTypes[$type->name] = $type;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
$this->resolvedTypes += Type::getInternalTypes() + Introspection::getTypes();
|
$this->resolvedTypes += Type::getStandardTypes() + Introspection::getTypes();
|
||||||
|
|
||||||
if ($this->config->typeLoader) {
|
if ($this->config->typeLoader) {
|
||||||
return;
|
return;
|
||||||
@ -472,7 +472,7 @@ class Schema
|
|||||||
throw new InvariantViolation(implode("\n\n", $this->validationErrors));
|
throw new InvariantViolation(implode("\n\n", $this->validationErrors));
|
||||||
}
|
}
|
||||||
|
|
||||||
$internalTypes = Type::getInternalTypes() + Introspection::getTypes();
|
$internalTypes = Type::getStandardTypes() + Introspection::getTypes();
|
||||||
foreach ($this->getTypeMap() as $name => $type) {
|
foreach ($this->getTypeMap() as $name => $type) {
|
||||||
if (isset($internalTypes[$name])) {
|
if (isset($internalTypes[$name])) {
|
||||||
continue;
|
continue;
|
||||||
|
132
tests/Type/StandardTypesTest.php
Normal file
132
tests/Type/StandardTypesTest.php
Normal file
@ -0,0 +1,132 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace GraphQL\Tests\Type;
|
||||||
|
|
||||||
|
use GraphQL\Error\InvariantViolation;
|
||||||
|
use GraphQL\Type\Definition\CustomScalarType;
|
||||||
|
use GraphQL\Type\Definition\Type;
|
||||||
|
use PHPUnit\Framework\TestCase;
|
||||||
|
use stdClass;
|
||||||
|
|
||||||
|
class StandardTypesTest extends TestCase
|
||||||
|
{
|
||||||
|
/** @var Type[] */
|
||||||
|
private static $originalStandardTypes;
|
||||||
|
|
||||||
|
public static function setUpBeforeClass()
|
||||||
|
{
|
||||||
|
self::$originalStandardTypes = Type::getStandardTypes();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function tearDown()
|
||||||
|
{
|
||||||
|
parent::tearDown();
|
||||||
|
Type::overrideStandardTypes(self::$originalStandardTypes);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testAllowsOverridingStandardTypes()
|
||||||
|
{
|
||||||
|
$originalTypes = Type::getStandardTypes();
|
||||||
|
self::assertCount(5, $originalTypes);
|
||||||
|
self::assertSame(self::$originalStandardTypes, $originalTypes);
|
||||||
|
|
||||||
|
$newBooleanType = $this->createCustomScalarType(Type::BOOLEAN);
|
||||||
|
$newFloatType = $this->createCustomScalarType(Type::FLOAT);
|
||||||
|
$newIDType = $this->createCustomScalarType(Type::ID);
|
||||||
|
$newIntType = $this->createCustomScalarType(Type::INT);
|
||||||
|
$newStringType = $this->createCustomScalarType(Type::STRING);
|
||||||
|
|
||||||
|
Type::overrideStandardTypes([
|
||||||
|
$newStringType,
|
||||||
|
$newBooleanType,
|
||||||
|
$newIDType,
|
||||||
|
$newIntType,
|
||||||
|
$newFloatType,
|
||||||
|
]);
|
||||||
|
|
||||||
|
$types = Type::getStandardTypes();
|
||||||
|
self::assertCount(5, $types);
|
||||||
|
|
||||||
|
self::assertSame($newBooleanType, $types[Type::BOOLEAN]);
|
||||||
|
self::assertSame($newFloatType, $types[Type::FLOAT]);
|
||||||
|
self::assertSame($newIDType, $types[Type::ID]);
|
||||||
|
self::assertSame($newIntType, $types[Type::INT]);
|
||||||
|
self::assertSame($newStringType, $types[Type::STRING]);
|
||||||
|
|
||||||
|
self::assertSame($newBooleanType, Type::boolean());
|
||||||
|
self::assertSame($newFloatType, Type::float());
|
||||||
|
self::assertSame($newIDType, Type::id());
|
||||||
|
self::assertSame($newIntType, Type::int());
|
||||||
|
self::assertSame($newStringType, Type::string());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testPreservesOriginalStandardTypes()
|
||||||
|
{
|
||||||
|
$originalTypes = Type::getStandardTypes();
|
||||||
|
self::assertCount(5, $originalTypes);
|
||||||
|
self::assertSame(self::$originalStandardTypes, $originalTypes);
|
||||||
|
|
||||||
|
$newIDType = $this->createCustomScalarType(Type::ID);
|
||||||
|
$newStringType = $this->createCustomScalarType(Type::STRING);
|
||||||
|
|
||||||
|
Type::overrideStandardTypes([
|
||||||
|
$newStringType,
|
||||||
|
$newIDType,
|
||||||
|
]);
|
||||||
|
|
||||||
|
$types = Type::getStandardTypes();
|
||||||
|
self::assertCount(5, $types);
|
||||||
|
|
||||||
|
self::assertSame($originalTypes[Type::BOOLEAN], $types[Type::BOOLEAN]);
|
||||||
|
self::assertSame($originalTypes[Type::FLOAT], $types[Type::FLOAT]);
|
||||||
|
self::assertSame($originalTypes[Type::INT], $types[Type::INT]);
|
||||||
|
|
||||||
|
self::assertSame($originalTypes[Type::BOOLEAN], Type::boolean());
|
||||||
|
self::assertSame($originalTypes[Type::FLOAT], Type::float());
|
||||||
|
self::assertSame($originalTypes[Type::INT], Type::int());
|
||||||
|
|
||||||
|
self::assertSame($newIDType, $types[Type::ID]);
|
||||||
|
self::assertSame($newStringType, $types[Type::STRING]);
|
||||||
|
|
||||||
|
self::assertSame($newIDType, Type::id());
|
||||||
|
self::assertSame($newStringType, Type::string());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getInvalidStandardTypes()
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
[null, 'Expecting instance of GraphQL\Type\Definition\Type, got null'],
|
||||||
|
[5, 'Expecting instance of GraphQL\Type\Definition\Type, got 5'],
|
||||||
|
['', 'Expecting instance of GraphQL\Type\Definition\Type, got (empty string)'],
|
||||||
|
[new stdClass(), 'Expecting instance of GraphQL\Type\Definition\Type, got instance of stdClass'],
|
||||||
|
[[], 'Expecting instance of GraphQL\Type\Definition\Type, got []'],
|
||||||
|
[$this->createCustomScalarType('NonStandardName'), 'Expecting one of the following names for a standard type: ID, String, Float, Int, Boolean, got NonStandardName'],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dataProvider getInvalidStandardTypes
|
||||||
|
*/
|
||||||
|
public function testStandardTypesOverrideDoesSanityChecks($type, string $expectedMessage)
|
||||||
|
{
|
||||||
|
$this->expectException(InvariantViolation::class);
|
||||||
|
$this->expectExceptionMessage($expectedMessage);
|
||||||
|
|
||||||
|
Type::overrideStandardTypes([ $type ]);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function createCustomScalarType($name)
|
||||||
|
{
|
||||||
|
return new CustomScalarType([
|
||||||
|
'name' => $name,
|
||||||
|
'serialize' => static function () {
|
||||||
|
},
|
||||||
|
'parseValue' => static function () {
|
||||||
|
},
|
||||||
|
'parseLiteral' => static function () {
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user