graphql-php/tests/Validator/OverlappingFieldsCanBeMergedTest.php
2018-07-29 19:01:39 +02:00

1147 lines
32 KiB
PHP

<?php
namespace GraphQL\Tests\Validator;
use GraphQL\Error\FormattedError;
use GraphQL\Language\SourceLocation;
use GraphQL\Type\Schema;
use GraphQL\Type\Definition\InterfaceType;
use GraphQL\Type\Definition\ObjectType;
use GraphQL\Type\Definition\Type;
use GraphQL\Validator\Rules\OverlappingFieldsCanBeMerged;
class OverlappingFieldsCanBeMergedTest extends ValidatorTestCase
{
// Validate: Overlapping fields can be merged
/**
* @it unique fields
*/
public function testUniqueFields()
{
$this->expectPassesRule(new OverlappingFieldsCanBeMerged(), '
fragment uniqueFields on Dog {
name
nickname
}
');
}
/**
* @it identical fields
*/
public function testIdenticalFields()
{
$this->expectPassesRule(new OverlappingFieldsCanBeMerged, '
fragment mergeIdenticalFields on Dog {
name
name
}
');
}
/**
* @it identical fields with identical args
*/
public function testIdenticalFieldsWithIdenticalArgs()
{
$this->expectPassesRule(new OverlappingFieldsCanBeMerged, '
fragment mergeIdenticalFieldsWithIdenticalArgs on Dog {
doesKnowCommand(dogCommand: SIT)
doesKnowCommand(dogCommand: SIT)
}
');
}
/**
* @it identical fields with identical directives
*/
public function testIdenticalFieldsWithIdenticalDirectives()
{
$this->expectPassesRule(new OverlappingFieldsCanBeMerged, '
fragment mergeSameFieldsWithSameDirectives on Dog {
name @include(if: true)
name @include(if: true)
}
');
}
/**
* @it different args with different aliases
*/
public function testDifferentArgsWithDifferentAliases()
{
$this->expectPassesRule(new OverlappingFieldsCanBeMerged, '
fragment differentArgsWithDifferentAliases on Dog {
knowsSit : doesKnowCommand(dogCommand: SIT)
knowsDown : doesKnowCommand(dogCommand: DOWN)
}
');
}
/**
* @it different directives with different aliases
*/
public function testDifferentDirectivesWithDifferentAliases()
{
$this->expectPassesRule(new OverlappingFieldsCanBeMerged, '
fragment differentDirectivesWithDifferentAliases on Dog {
nameIfTrue : name @include(if: true)
nameIfFalse : name @include(if: false)
}
');
}
/**
* @it different skip/include directives accepted
*/
public function testDifferentSkipIncludeDirectivesAccepted()
{
// Note: Differing skip/include directives don't create an ambiguous return
// value and are acceptable in conditions where differing runtime values
// may have the same desired effect of including or skipping a field.
$this->expectPassesRule(new OverlappingFieldsCanBeMerged, '
fragment differentDirectivesWithDifferentAliases on Dog {
name @include(if: true)
name @include(if: false)
}
');
}
/**
* @it Same aliases with different field targets
*/
public function testSameAliasesWithDifferentFieldTargets()
{
$this->expectFailsRule(new OverlappingFieldsCanBeMerged, '
fragment sameAliasesWithDifferentFieldTargets on Dog {
fido : name
fido : nickname
}
', [
FormattedError::create(
OverlappingFieldsCanBeMerged::fieldsConflictMessage('fido', 'name and nickname are different fields'),
[new SourceLocation(3, 9), new SourceLocation(4, 9)]
)
]);
}
/**
* @it Same aliases allowed on non-overlapping fields
*/
public function testSameAliasesAllowedOnNonOverlappingFields()
{
// This is valid since no object can be both a "Dog" and a "Cat", thus
// these fields can never overlap.
$this->expectPassesRule(new OverlappingFieldsCanBeMerged, '
fragment sameAliasesWithDifferentFieldTargets on Pet {
... on Dog {
name
}
... on Cat {
name: nickname
}
}
');
}
/**
* @it Alias masking direct field access
*/
public function testAliasMaskingDirectFieldAccess()
{
$this->expectFailsRule(new OverlappingFieldsCanBeMerged, '
fragment aliasMaskingDirectFieldAccess on Dog {
name : nickname
name
}
', [
FormattedError::create(
OverlappingFieldsCanBeMerged::fieldsConflictMessage('name', 'nickname and name are different fields'),
[new SourceLocation(3, 9), new SourceLocation(4, 9)]
)
]);
}
/**
* @it different args, second adds an argument
*/
public function testDifferentArgsSecondAddsAnArgument()
{
$this->expectFailsRule(new OverlappingFieldsCanBeMerged, '
fragment conflictingArgs on Dog {
doesKnowCommand
doesKnowCommand(dogCommand: HEEL)
}
', [
FormattedError::create(
OverlappingFieldsCanBeMerged::fieldsConflictMessage('doesKnowCommand', 'they have differing arguments'),
[new SourceLocation(3, 9), new SourceLocation(4, 9)]
)
]);
}
/**
* @it different args, second missing an argument
*/
public function testDifferentArgsSecondMissingAnArgument()
{
$this->expectFailsRule(new OverlappingFieldsCanBeMerged, '
fragment conflictingArgs on Dog {
doesKnowCommand(dogCommand: SIT)
doesKnowCommand
}
',
[
FormattedError::create(
OverlappingFieldsCanBeMerged::fieldsConflictMessage('doesKnowCommand', 'they have differing arguments'),
[new SourceLocation(3, 9), new SourceLocation(4, 9)]
)
]
);
}
/**
* @it conflicting args
*/
public function testConflictingArgs()
{
$this->expectFailsRule(new OverlappingFieldsCanBeMerged, '
fragment conflictingArgs on Dog {
doesKnowCommand(dogCommand: SIT)
doesKnowCommand(dogCommand: HEEL)
}
', [
FormattedError::create(
OverlappingFieldsCanBeMerged::fieldsConflictMessage('doesKnowCommand', 'they have differing arguments'),
[new SourceLocation(3,9), new SourceLocation(4,9)]
)
]);
}
/**
* @it allows different args where no conflict is possible
*/
public function testAllowsDifferentArgsWhereNoConflictIsPossible()
{
// This is valid since no object can be both a "Dog" and a "Cat", thus
// these fields can never overlap.
$this->expectPassesRule(new OverlappingFieldsCanBeMerged, '
fragment conflictingArgs on Pet {
... on Dog {
name(surname: true)
}
... on Cat {
name
}
}
');
}
/**
* @it encounters conflict in fragments
*/
public function testEncountersConflictInFragments()
{
$this->expectFailsRule(new OverlappingFieldsCanBeMerged, '
{
...A
...B
}
fragment A on Type {
x: a
}
fragment B on Type {
x: b
}
', [
FormattedError::create(
OverlappingFieldsCanBeMerged::fieldsConflictMessage('x', 'a and b are different fields'),
[new SourceLocation(7, 9), new SourceLocation(10, 9)]
)
]);
}
/**
* @it reports each conflict once
*/
public function testReportsEachConflictOnce()
{
$this->expectFailsRule(new OverlappingFieldsCanBeMerged, '
{
f1 {
...A
...B
}
f2 {
...B
...A
}
f3 {
...A
...B
x: c
}
}
fragment A on Type {
x: a
}
fragment B on Type {
x: b
}
', [
FormattedError::create(
OverlappingFieldsCanBeMerged::fieldsConflictMessage('x', 'a and b are different fields'),
[new SourceLocation(18, 9), new SourceLocation(21, 9)]
),
FormattedError::create(
OverlappingFieldsCanBeMerged::fieldsConflictMessage('x', 'c and a are different fields'),
[new SourceLocation(14, 11), new SourceLocation(18, 9)]
),
FormattedError::create(
OverlappingFieldsCanBeMerged::fieldsConflictMessage('x', 'c and b are different fields'),
[new SourceLocation(14, 11), new SourceLocation(21, 9)]
)
]);
}
/**
* @it deep conflict
*/
public function testDeepConflict()
{
$this->expectFailsRule(new OverlappingFieldsCanBeMerged, '
{
field {
x: a
},
field {
x: b
}
}
', [
FormattedError::create(
OverlappingFieldsCanBeMerged::fieldsConflictMessage('field', [['x', 'a and b are different fields']]),
[
new SourceLocation(3, 9),
new SourceLocation(4, 11),
new SourceLocation(6,9),
new SourceLocation(7, 11)
]
)
]);
}
/**
* @it deep conflict with multiple issues
*/
public function testDeepConflictWithMultipleIssues()
{
$this->expectFailsRule(new OverlappingFieldsCanBeMerged, '
{
field {
x: a
y: c
},
field {
x: b
y: d
}
}
', [
FormattedError::create(
OverlappingFieldsCanBeMerged::fieldsConflictMessage('field', [
['x', 'a and b are different fields'],
['y', 'c and d are different fields']
]),
[
new SourceLocation(3,9),
new SourceLocation(4,11),
new SourceLocation(5,11),
new SourceLocation(7,9),
new SourceLocation(8,11),
new SourceLocation(9,11)
]
)
]);
}
/**
* @it very deep conflict
*/
public function testVeryDeepConflict()
{
$this->expectFailsRule(new OverlappingFieldsCanBeMerged, '
{
field {
deepField {
x: a
}
},
field {
deepField {
x: b
}
}
}
', [
FormattedError::create(
OverlappingFieldsCanBeMerged::fieldsConflictMessage('field', [['deepField', [['x', 'a and b are different fields']]]]),
[
new SourceLocation(3,9),
new SourceLocation(4,11),
new SourceLocation(5,13),
new SourceLocation(8,9),
new SourceLocation(9,11),
new SourceLocation(10,13)
]
)
]);
}
/**
* @it reports deep conflict to nearest common ancestor
*/
public function testReportsDeepConflictToNearestCommonAncestor()
{
$this->expectFailsRule(new OverlappingFieldsCanBeMerged, '
{
field {
deepField {
x: a
}
deepField {
x: b
}
},
field {
deepField {
y
}
}
}
', [
FormattedError::create(
OverlappingFieldsCanBeMerged::fieldsConflictMessage('deepField', [['x', 'a and b are different fields']]),
[
new SourceLocation(4,11),
new SourceLocation(5,13),
new SourceLocation(7,11),
new SourceLocation(8,13)
]
)
]);
}
/**
* @it reports deep conflict to nearest common ancestor in fragments
*/
public function testReportsDeepConflictToNearestCommonAncestorInFragments()
{
$this->expectFailsRule(new OverlappingFieldsCanBeMerged, '
{
field {
...F
}
field {
...F
}
}
fragment F on T {
deepField {
deeperField {
x: a
}
deeperField {
x: b
}
}
deepField {
deeperField {
y
}
}
}
', [
FormattedError::create(
OverlappingFieldsCanBeMerged::fieldsConflictMessage('deeperField', [['x', 'a and b are different fields']]),
[
new SourceLocation(12,11),
new SourceLocation(13,13),
new SourceLocation(15,11),
new SourceLocation(16,13),
]
)
]);
}
/**
* @it reports deep conflict in nested fragments
*/
public function testReportsDeepConflictInNestedFragments()
{
$this->expectFailsRule(new OverlappingFieldsCanBeMerged, '
{
field {
...F
}
field {
...I
}
}
fragment F on T {
x: a
...G
}
fragment G on T {
y: c
}
fragment I on T {
y: d
...J
}
fragment J on T {
x: b
}
', [
FormattedError::create(
OverlappingFieldsCanBeMerged::fieldsConflictMessage('field', [
['x', 'a and b are different fields'],
['y', 'c and d are different fields'],
]),
[
new SourceLocation(3,9),
new SourceLocation(11,9),
new SourceLocation(15,9),
new SourceLocation(6,9),
new SourceLocation(22,9),
new SourceLocation(18,9),
]
)
]);
}
/**
* @it ignores unknown fragments
*/
public function testIgnoresUnknownFragments()
{
$this->expectPassesRule(new OverlappingFieldsCanBeMerged, '
{
field {
...Unknown
...Known
}
}
fragment Known on T {
field
...OtherUnknown
}
');
}
// Describe: return types must be unambiguous
/**
* @it conflicting return types which potentially overlap
*/
public function testConflictingReturnTypesWhichPotentiallyOverlap()
{
// This is invalid since an object could potentially be both the Object
// type IntBox and the interface type NonNullStringBox1. While that
// condition does not exist in the current schema, the schema could
// expand in the future to allow this. Thus it is invalid.
$this->expectFailsRuleWithSchema($this->getSchema(), new OverlappingFieldsCanBeMerged, '
{
someBox {
...on IntBox {
scalar
}
...on NonNullStringBox1 {
scalar
}
}
}
', [
FormattedError::create(
OverlappingFieldsCanBeMerged::fieldsConflictMessage(
'scalar',
'they return conflicting types Int and String!'
),
[new SourceLocation(5, 15),
new SourceLocation(8, 15)]
)
]);
}
/**
* @it compatible return shapes on different return types
*/
public function testCompatibleReturnShapesOnDifferentReturnTypes()
{
// In this case `deepBox` returns `SomeBox` in the first usage, and
// `StringBox` in the second usage. These return types are not the same!
// however this is valid because the return *shapes* are compatible.
$this->expectPassesRuleWithSchema($this->getSchema(), new OverlappingFieldsCanBeMerged, '
{
someBox {
... on SomeBox {
deepBox {
unrelatedField
}
}
... on StringBox {
deepBox {
unrelatedField
}
}
}
}
');
}
/**
* @it disallows differing return types despite no overlap
*/
public function testDisallowsDifferingReturnTypesDespiteNoOverlap()
{
$this->expectFailsRuleWithSchema($this->getSchema(), new OverlappingFieldsCanBeMerged, '
{
someBox {
... on IntBox {
scalar
}
... on StringBox {
scalar
}
}
}
', [
FormattedError::create(
OverlappingFieldsCanBeMerged::fieldsConflictMessage(
'scalar',
'they return conflicting types Int and String'
),
[ new SourceLocation(5, 15),
new SourceLocation(8, 15)]
)
]);
}
/**
* @it reports correctly when a non-exclusive follows an exclusive
*/
public function testReportsCorrectlyWhenANonExclusiveFollowsAnExclusive()
{
$this->expectFailsRuleWithSchema($this->getSchema(), new OverlappingFieldsCanBeMerged, '
{
someBox {
... on IntBox {
deepBox {
...X
}
}
}
someBox {
... on StringBox {
deepBox {
...Y
}
}
}
memoed: someBox {
... on IntBox {
deepBox {
...X
}
}
}
memoed: someBox {
... on StringBox {
deepBox {
...Y
}
}
}
other: someBox {
...X
}
other: someBox {
...Y
}
}
fragment X on SomeBox {
scalar
}
fragment Y on SomeBox {
scalar: unrelatedField
}
', [
FormattedError::create(
OverlappingFieldsCanBeMerged::fieldsConflictMessage(
'other',
[['scalar', 'scalar and unrelatedField are different fields']]
),
[
new SourceLocation(31, 11),
new SourceLocation(39, 11),
new SourceLocation(34, 11),
new SourceLocation(42, 11),
]
)
]);
}
/**
* @it disallows differing return type nullability despite no overlap
*/
public function testDisallowsDifferingReturnTypeNullabilityDespiteNoOverlap()
{
$this->expectFailsRuleWithSchema($this->getSchema(), new OverlappingFieldsCanBeMerged, '
{
someBox {
... on NonNullStringBox1 {
scalar
}
... on StringBox {
scalar
}
}
}
', [
FormattedError::create(
OverlappingFieldsCanBeMerged::fieldsConflictMessage(
'scalar',
'they return conflicting types String! and String'
),
[new SourceLocation(5, 15),
new SourceLocation(8, 15)]
)
]);
}
/**
* @it disallows differing return type list despite no overlap
*/
public function testDisallowsDifferingReturnTypeListDespiteNoOverlap()
{
$this->expectFailsRuleWithSchema($this->getSchema(), new OverlappingFieldsCanBeMerged, '
{
someBox {
... on IntBox {
box: listStringBox {
scalar
}
}
... on StringBox {
box: stringBox {
scalar
}
}
}
}
', [
FormattedError::create(
OverlappingFieldsCanBeMerged::fieldsConflictMessage(
'box',
'they return conflicting types [StringBox] and StringBox'
),
[new SourceLocation(5, 15),
new SourceLocation(10, 15)]
)
]);
$this->expectFailsRuleWithSchema($this->getSchema(), new OverlappingFieldsCanBeMerged, '
{
someBox {
... on IntBox {
box: stringBox {
scalar
}
}
... on StringBox {
box: listStringBox {
scalar
}
}
}
}
', [
FormattedError::create(
OverlappingFieldsCanBeMerged::fieldsConflictMessage(
'box',
'they return conflicting types StringBox and [StringBox]'
),
[new SourceLocation(5, 15),
new SourceLocation(10, 15)]
)
]);
}
public function testDisallowsDifferingSubfields()
{
$this->expectFailsRuleWithSchema($this->getSchema(), new OverlappingFieldsCanBeMerged, '
{
someBox {
... on IntBox {
box: stringBox {
val: scalar
val: unrelatedField
}
}
... on StringBox {
box: stringBox {
val: scalar
}
}
}
}
', [
FormattedError::create(
OverlappingFieldsCanBeMerged::fieldsConflictMessage(
'val',
'scalar and unrelatedField are different fields'
),
[new SourceLocation(6, 17),
new SourceLocation(7, 17)]
)
]);
}
/**
* @it disallows differing deep return types despite no overlap
*/
public function testDisallowsDifferingDeepReturnTypesDespiteNoOverlap()
{
$this->expectFailsRuleWithSchema($this->getSchema(), new OverlappingFieldsCanBeMerged, '
{
someBox {
... on IntBox {
box: stringBox {
scalar
}
}
... on StringBox {
box: intBox {
scalar
}
}
}
}
', [
FormattedError::create(
OverlappingFieldsCanBeMerged::fieldsConflictMessage(
'box',
[ [ 'scalar', 'they return conflicting types String and Int' ] ]
),
[
new SourceLocation(5, 15),
new SourceLocation(6, 17),
new SourceLocation(10, 15),
new SourceLocation(11, 17)
]
)
]);
}
/**
* @it allows non-conflicting overlaping types
*/
public function testAllowsNonConflictingOverlapingTypes()
{
$this->expectPassesRuleWithSchema($this->getSchema(), new OverlappingFieldsCanBeMerged, '
{
someBox {
... on IntBox {
scalar: unrelatedField
}
... on StringBox {
scalar
}
}
}
');
}
/**
* @it same wrapped scalar return types
*/
public function testSameWrappedScalarReturnTypes()
{
$this->expectPassesRuleWithSchema($this->getSchema(), new OverlappingFieldsCanBeMerged, '
{
someBox {
...on NonNullStringBox1 {
scalar
}
...on NonNullStringBox2 {
scalar
}
}
}
');
}
/**
* @it allows inline typeless fragments
*/
public function testAllowsInlineTypelessFragments()
{
$this->expectPassesRuleWithSchema($this->getSchema(), new OverlappingFieldsCanBeMerged, '
{
a
... {
a
}
}
');
}
/**
* @it compares deep types including list
*/
public function testComparesDeepTypesIncludingList()
{
$this->expectFailsRuleWithSchema($this->getSchema(), new OverlappingFieldsCanBeMerged, '
{
connection {
...edgeID
edges {
node {
id: name
}
}
}
}
fragment edgeID on Connection {
edges {
node {
id
}
}
}
', [
FormattedError::create(
OverlappingFieldsCanBeMerged::fieldsConflictMessage('edges', [['node', [['id', 'name and id are different fields']]]]),
[
new SourceLocation(5, 13),
new SourceLocation(6, 15),
new SourceLocation(7, 17),
new SourceLocation(14, 11),
new SourceLocation(15, 13),
new SourceLocation(16, 15),
]
)
]);
}
/**
* @it ignores unknown types
*/
public function testIgnoresUnknownTypes()
{
$this->expectPassesRuleWithSchema($this->getSchema(), new OverlappingFieldsCanBeMerged, '
{
someBox {
...on UnknownType {
scalar
}
...on NonNullStringBox2 {
scalar
}
}
}
');
}
/**
* @it error message contains hint for alias conflict
*/
public function testErrorMessageContainsHintForAliasConflict()
{
// The error template should end with a hint for the user to try using
// different aliases.
$error = OverlappingFieldsCanBeMerged::fieldsConflictMessage('x', 'a and b are different fields');
$hint = 'Use different aliases on the fields to fetch both if this was intentional.';
$this->assertStringEndsWith($hint, $error);
}
/**
* @it does not infinite loop on recursive fragment
*/
public function testDoesNotInfiniteLoopOnRecursiveFragment()
{
$this->expectPassesRule(new OverlappingFieldsCanBeMerged, '
fragment fragA on Human { name, relatives { name, ...fragA } }
');
}
/**
* @it does not infinite loop on immediately recursive fragment
*/
public function testDoesNotInfiniteLoopOnImmeditelyRecursiveFragment()
{
$this->expectPassesRule(new OverlappingFieldsCanBeMerged, '
fragment fragA on Human { name, ...fragA }
');
}
/**
* @it does not infinite loop on transitively recursive fragment
*/
public function testDoesNotInfiniteLoopOnTransitivelyRecursiveFragment()
{
$this->expectPassesRule(new OverlappingFieldsCanBeMerged, '
fragment fragA on Human { name, ...fragB }
fragment fragB on Human { name, ...fragC }
fragment fragC on Human { name, ...fragA }
');
}
/**
* @it find invalid case even with immediately recursive fragment
*/
public function testFindInvalidCaseEvenWithImmediatelyRecursiveFragment()
{
$this->expectFailsRule(new OverlappingFieldsCanBeMerged, '
fragment sameAliasesWithDifferentFieldTargets on Dob {
...sameAliasesWithDifferentFieldTargets
fido: name
fido: nickname
}
',
[
FormattedError::create(
OverlappingFieldsCanBeMerged::fieldsConflictMessage(
'fido',
'name and nickname are different fields'
),
[
new SourceLocation(4, 9),
new SourceLocation(5, 9),
]
)
]);
}
private function getSchema()
{
$StringBox = null;
$IntBox = null;
$SomeBox = null;
$SomeBox = new InterfaceType([
'name' => 'SomeBox',
'fields' => function() use (&$SomeBox) {
return [
'deepBox' => ['type' => $SomeBox],
'unrelatedField' => ['type' => Type::string()]
];
}
]);
$StringBox = new ObjectType([
'name' => 'StringBox',
'interfaces' => [$SomeBox],
'fields' => function() use (&$StringBox, &$IntBox) {
return [
'scalar' => ['type' => Type::string()],
'deepBox' => ['type' => $StringBox],
'unrelatedField' => ['type' => Type::string()],
'listStringBox' => ['type' => Type::listOf($StringBox)],
'stringBox' => ['type' => $StringBox],
'intBox' => ['type' => $IntBox],
];
}
]);
$IntBox = new ObjectType([
'name' => 'IntBox',
'interfaces' => [$SomeBox],
'fields' => function() use (&$StringBox, &$IntBox) {
return [
'scalar' => ['type' => Type::int()],
'deepBox' => ['type' => $IntBox],
'unrelatedField' => ['type' => Type::string()],
'listStringBox' => ['type' => Type::listOf($StringBox)],
'stringBox' => ['type' => $StringBox],
'intBox' => ['type' => $IntBox],
];
}
]);
$NonNullStringBox1 = new InterfaceType([
'name' => 'NonNullStringBox1',
'fields' => [
'scalar' => [ 'type' => Type::nonNull(Type::string()) ]
]
]);
$NonNullStringBox1Impl = new ObjectType([
'name' => 'NonNullStringBox1Impl',
'interfaces' => [ $SomeBox, $NonNullStringBox1 ],
'fields' => [
'scalar' => [ 'type' => Type::nonNull(Type::string()) ],
'unrelatedField' => ['type' => Type::string() ],
'deepBox' => [ 'type' => $SomeBox ],
]
]);
$NonNullStringBox2 = new InterfaceType([
'name' => 'NonNullStringBox2',
'fields' => [
'scalar' => ['type' => Type::nonNull(Type::string())]
]
]);
$NonNullStringBox2Impl = new ObjectType([
'name' => 'NonNullStringBox2Impl',
'interfaces' => [ $SomeBox, $NonNullStringBox2 ],
'fields' => [
'scalar' => [ 'type' => Type::nonNull(Type::string()) ],
'unrelatedField' => [ 'type' => Type::string() ],
'deepBox' => [ 'type' => $SomeBox ],
]
]);
$Connection = new ObjectType([
'name' => 'Connection',
'fields' => [
'edges' => [
'type' => Type::listOf(new ObjectType([
'name' => 'Edge',
'fields' => [
'node' => [
'type' => new ObjectType([
'name' => 'Node',
'fields' => [
'id' => ['type' => Type::id()],
'name' => ['type' => Type::string()]
]
])
]
]
]))
]
]
]);
$schema = new Schema([
'query' => new ObjectType([
'name' => 'QueryRoot',
'fields' => [
'someBox' => ['type' => $SomeBox],
'connection' => ['type' => $Connection]
]
]),
'types' => [$IntBox, $StringBox, $NonNullStringBox1Impl, $NonNullStringBox2Impl]
]);
return $schema;
}
}