Skip to content

Commit f8b196e

Browse files
feat: add support lazy operation type
1 parent 0a153e7 commit f8b196e

File tree

3 files changed

+80
-19
lines changed

3 files changed

+80
-19
lines changed

src/Execution.php

+30-11
Original file line numberDiff line numberDiff line change
@@ -47,18 +47,29 @@ public static function delegate(
4747
Schema $schema,
4848
DelegatorInterface $delegator,
4949
ErrorsReporterInterface $errorsReporter = null,
50-
): void {
50+
): Schema {
5151
$execution = new Execution($delegator, $errorsReporter);
52+
$schemaConfig = $schema->getConfig();
5253

5354
foreach (['query', 'mutation', 'subscription'] as $operation) {
54-
$operationType = $schema->getOperationType($operation);
55+
$operationConfig = $schemaConfig->{$operation};
5556

56-
if (null === $operationType) {
57-
continue;
57+
if ($operationConfig instanceof ObjectType) {
58+
$execution->prepareType($operationConfig);
5859
}
5960

60-
$execution->prepareType($operationType);
61+
if (is_callable($operationConfig)) {
62+
$schemaConfig->{$operation} = function () use ($operationConfig, $execution) {
63+
$type = $operationConfig();
64+
65+
$execution->prepareType($type);
66+
67+
return $type;
68+
};
69+
}
6170
}
71+
72+
return $schema;
6273
}
6374

6475
private function prepareType(Type $type): void
@@ -81,7 +92,7 @@ private function prepareType(Type $type): void
8192
}
8293

8394
if ($type instanceof AbstractType) {
84-
$resolveType = fn(array $value, mixed $context, ResolveInfo $info) => $this->resolveAbstractType(
95+
$resolveType = fn (array $value, mixed $context, ResolveInfo $info) => $this->resolveAbstractType(
8596
$type,
8697
$value,
8798
$context,
@@ -94,8 +105,12 @@ private function prepareType(Type $type): void
94105
$this->preparedTypes[$type] = true;
95106
}
96107

97-
private function resolveAbstractType(AbstractType $abstractType, array $value, mixed $context, ResolveInfo $info): Type
98-
{
108+
private function resolveAbstractType(
109+
AbstractType $abstractType,
110+
array $value,
111+
mixed $context,
112+
ResolveInfo $info
113+
): Type {
99114
/// __typename field should be existed in $value
100115
/// because we have added it to delegated query
101116
$typename = $value[Introspection::TYPE_NAME_FIELD_NAME];
@@ -140,12 +155,16 @@ function (ExecutionResult $result) use ($info): mixed {
140155
* @param array<string, mixed> $variables
141156
* @return Promise
142157
*/
143-
private function delegateToExecute(Schema $schema, OperationDefinitionNode $operation, array $fragments, array $variables): Promise
144-
{
158+
private function delegateToExecute(
159+
Schema $schema,
160+
OperationDefinitionNode $operation,
161+
array $fragments,
162+
array $variables
163+
): Promise {
145164
try {
146165
/// We need to clone all fragments and operation to make sure it can not be mutated by delegator.
147166
$delegateOperation = $operation->cloneDeep();
148-
$delegateFragments = array_map(fn(FragmentDefinitionNode $fragment) => $fragment->cloneDeep(), $fragments);
167+
$delegateFragments = array_map(fn (FragmentDefinitionNode $fragment) => $fragment->cloneDeep(), $fragments);
149168

150169
/// Add typename for detecting object type of interface or union
151170
SelectionSet::addTypename($delegateOperation->getSelectionSet());

tests/DummySchemaTrait.php

+5-5
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ private function createDummySchema(): Schema
1919
'fields' => [
2020
'dummy' => Type::nonNull(Type::string()),
2121
],
22-
'resolveType' => fn() => 'DummyObject'
22+
'resolveType' => fn () => 'DummyObject'
2323
]);
2424

2525
return new Schema([
@@ -28,15 +28,15 @@ private function createDummySchema(): Schema
2828
'fields' => [
2929
'dummy' => [
3030
'type' => Type::string(),
31-
'resolve' => fn() => 'dummy'
31+
'resolve' => fn () => 'dummy'
3232
],
3333
'dummy_error' => [
3434
'type' => Type::string(),
35-
'resolve' => fn() => throw new Error('Dummy error')
35+
'resolve' => fn () => throw new Error('Dummy error')
3636
],
3737
'dummy_object' => [
3838
'type' => $dummyInterface,
39-
'resolve' => fn() => [
39+
'resolve' => fn () => [
4040
'dummy' => 'dummy object field'
4141
]
4242
]
@@ -47,7 +47,7 @@ private function createDummySchema(): Schema
4747
'fields' => [
4848
'dummy' => [
4949
'type' => Type::string(),
50-
'resolve' => fn() => 'dummy'
50+
'resolve' => fn () => 'dummy'
5151
]
5252
]
5353
]),

tests/ExecutionTest.php

+45-3
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,39 @@ interface CustomInterface {
9191
$this->assertEquals('Dummy error', $result->errors[0]->getMessage());
9292
}
9393

94+
public function testLazyOperationTypeExecution(): void
95+
{
96+
$delegateSchema = $this->createDummySchema();
97+
$delegator = new SchemaDelegator($delegateSchema);
98+
$schema = BuildSchema::build(
99+
<<<'SDL'
100+
type Query {
101+
dummy: String!
102+
}
103+
SDL
104+
);
105+
$queryType = $schema->getConfig()->query;
106+
$schema->getConfig()->query = fn () => $queryType;
107+
108+
Execution::delegate($schema, $delegator);
109+
110+
$result = GraphQL::executeQuery(
111+
$schema,
112+
<<<'GQL'
113+
query {
114+
dummy
115+
}
116+
GQL
117+
);
118+
119+
$this->assertEquals(
120+
[
121+
'dummy' => 'dummy'
122+
],
123+
$result->data
124+
);
125+
}
126+
94127
public function testLimitAccessFieldOfDelegateSchema(): void
95128
{
96129
$delegateSchema = $this->createDummySchema();
@@ -141,7 +174,10 @@ public function testSchemaConflictFieldDefinitionWithDelegateSchema(): void
141174
GQL
142175
);
143176

144-
$this->assertEquals('Delegated execution result is missing field value at path: `conflict_field`', $result->errors[0]->getMessage());
177+
$this->assertEquals(
178+
'Delegated execution result is missing field value at path: `conflict_field`',
179+
$result->errors[0]->getMessage()
180+
);
145181
}
146182

147183
public function testSchemaConflictAbstractTypeWithDelegateSchema(): void
@@ -176,7 +212,10 @@ interface Unknown {
176212
GQL
177213
);
178214

179-
$this->assertEquals('Expect type: `DummyObject` implementing `Unknown` should be exist in schema', $result->errors[0]->getMessage());
215+
$this->assertEquals(
216+
'Expect type: `DummyObject` implementing `Unknown` should be exist in schema',
217+
$result->errors[0]->getMessage()
218+
);
180219
}
181220

182221
public function testErrorDuringDelegateExecution(): void
@@ -214,6 +253,9 @@ function (array $errors) use (&$delegatedErrors): void {
214253
$this->assertEquals('Error during delegate execution', $delegatedErrors[0]->getMessage());
215254
$this->assertNotNull($delegatedErrors[0]->getPrevious());
216255
$this->assertEquals('Bad execution delegator', $delegatedErrors[0]->getPrevious()->getMessage());
217-
$this->assertEquals('Delegated execution result is missing field value at path: `dummy`', $result->errors[0]->getMessage());
256+
$this->assertEquals(
257+
'Delegated execution result is missing field value at path: `dummy`',
258+
$result->errors[0]->getMessage()
259+
);
218260
}
219261
}

0 commit comments

Comments
 (0)