4
4
5
5
namespace Apollo \Federation ;
6
6
7
+ use Apollo \Federation \Types \AnyType ;
7
8
use GraphQL \Type \Schema ;
8
- use GraphQL \Type \Definition \CustomScalarType ;
9
9
use GraphQL \Type \Definition \Directive ;
10
10
use GraphQL \Type \Definition \ObjectType ;
11
- use GraphQL \Type \Definition \UnionType ;
12
11
use GraphQL \Type \Definition \Type ;
13
12
use GraphQL \Utils \TypeInfo ;
14
13
use GraphQL \Utils \Utils ;
15
14
16
15
use Apollo \Federation \Types \EntityObjectType ;
17
- use Apollo \Federation \Utils \FederatedSchemaPrinter ;
16
+ use Apollo \Federation \Types \EntityUnionType ;
17
+ use Apollo \Federation \Types \ServiceDefinitionType ;
18
18
19
19
/**
20
20
* A federated GraphQL schema definition (see [related docs](https://www.apollographql.com/docs/apollo-server/federation/introduction))
55
55
*/
56
56
class FederatedSchema extends Schema
57
57
{
58
- /** @var EntityObjectType[] */
58
+ /** @var EntityObjectType[]|callable: EntityObjectType[] */
59
59
protected $ entityTypes ;
60
60
61
61
/** @var Directive[] */
62
62
protected $ entityDirectives ;
63
63
64
+ protected ServiceDefinitionType $ serviceDefinitionType ;
65
+ protected EntityUnionType $ entityUnionType ;
66
+ protected AnyType $ anyType ;
67
+
68
+ /**
69
+ *
70
+ * We will provide the parts that we need to operate against.
71
+ *
72
+ * @param array{?entityTypes: array<EntityObjectType>, ?typeLoader: callable, query: array} $config
73
+ */
64
74
public function __construct ($ config )
65
75
{
66
- $ this ->entityTypes = $ this ->extractEntityTypes ($ config );
76
+ $ this ->entityTypes = $ config [ ' entityTypes ' ] ?? $ this ->lazyEntityTypeExtractor ($ config );
67
77
$ this ->entityDirectives = array_merge (Directives::getDirectives (), Directive::getInternalDirectives ());
68
-
69
- $ config = array_merge ($ config , $ this ->getEntityDirectivesConfig ($ config ), $ this ->getQueryTypeConfig ($ config ));
78
+
79
+ $ this ->serviceDefinitionType = new ServiceDefinitionType ($ this );
80
+ $ this ->entityUnionType = new EntityUnionType ($ this ->entityTypes );
81
+ $ this ->anyType = new AnyType ();
82
+
83
+ $ config = array_merge ($ config ,
84
+ $ this ->getEntityDirectivesConfig ($ config ),
85
+ $ this ->getQueryTypeConfig ($ config ),
86
+ $ this ->supplementTypeLoader ($ config )
87
+ );
70
88
71
89
parent ::__construct ($ config );
72
90
}
@@ -78,7 +96,9 @@ public function __construct($config)
78
96
*/
79
97
public function getEntityTypes (): array
80
98
{
81
- return $ this ->entityTypes ;
99
+ return is_callable ($ this ->entityTypes )
100
+ ? ($ this ->entityTypes )()
101
+ : $ this ->entityTypes ;
82
102
}
83
103
84
104
/**
@@ -121,24 +141,42 @@ private function getQueryTypeConfig(array $config): array
121
141
];
122
142
}
123
143
144
+ /**
145
+ * Add type loading functionality for the types required for the federated schema to function.
146
+ */
147
+ private function supplementTypeLoader (array $ config ): array
148
+ {
149
+ if (!array_key_exists ('typeLoader ' , $ config ) || !is_callable ($ config ['typeLoader ' ])) {
150
+ return [];
151
+ }
152
+
153
+ return [
154
+ 'typeLoader ' => function ($ typeName ) use ($ config ) {
155
+ $ map = $ this ->builtInTypeMap ();
156
+ if (array_key_exists ($ typeName , $ map )) {
157
+ return $ map [$ typeName ];
158
+ }
159
+
160
+ return $ config ['typeLoader ' ]($ typeName );
161
+ }
162
+ ];
163
+ }
164
+
165
+ private function builtInTypeMap (): array
166
+ {
167
+ return [
168
+ EntityUnionType::getTypeName () => $ this ->entityUnionType ,
169
+ ServiceDefinitionType::getTypeName () => $ this ->serviceDefinitionType ,
170
+ AnyType::getTypeName () => $ this ->anyType
171
+ ];
172
+ }
173
+
124
174
/** @var array */
125
175
private function getQueryTypeServiceFieldConfig (): array
126
176
{
127
- $ serviceType = new ObjectType ([
128
- 'name ' => '_Service ' ,
129
- 'fields ' => [
130
- 'sdl ' => [
131
- 'type ' => Type::string (),
132
- 'resolve ' => function () {
133
- return FederatedSchemaPrinter::doPrint ($ this );
134
- }
135
- ]
136
- ]
137
- ]);
138
-
139
177
return [
140
178
'_service ' => [
141
- 'type ' => Type::nonNull ($ serviceType ),
179
+ 'type ' => Type::nonNull ($ this -> serviceDefinitionType ),
142
180
'resolve ' => function () {
143
181
return [];
144
182
}
@@ -149,28 +187,12 @@ private function getQueryTypeServiceFieldConfig(): array
149
187
/** @var array */
150
188
private function getQueryTypeEntitiesFieldConfig (?array $ config ): array
151
189
{
152
- if (!$ this ->hasEntityTypes ()) {
153
- return [];
154
- }
155
-
156
- $ entityType = new UnionType ([
157
- 'name ' => '_Entity ' ,
158
- 'types ' => array_values ($ this ->getEntityTypes ())
159
- ]);
160
-
161
- $ anyType = new CustomScalarType ([
162
- 'name ' => '_Any ' ,
163
- 'serialize ' => function ($ value ) {
164
- return $ value ;
165
- }
166
- ]);
167
-
168
190
return [
169
191
'_entities ' => [
170
- 'type ' => Type::listOf ($ entityType ),
192
+ 'type ' => Type::listOf ($ this -> entityUnionType ),
171
193
'args ' => [
172
194
'representations ' => [
173
- 'type ' => Type::nonNull (Type::listOf (Type::nonNull ($ anyType )))
195
+ 'type ' => Type::nonNull (Type::listOf (Type::nonNull ($ this -> anyType )))
174
196
]
175
197
],
176
198
'resolve ' => function ($ root , $ args , $ context , $ info ) use ($ config ) {
@@ -208,22 +230,25 @@ private function resolve($root, $args, $context, $info)
208
230
return $ r ;
209
231
}, $ args ['representations ' ]);
210
232
}
233
+
211
234
/**
212
235
* @param array $config
213
236
*
214
- * @return EntityObjectType[]
237
+ * @return callable: EntityObjectType[]
215
238
*/
216
- private function extractEntityTypes (array $ config ): array
239
+ private function lazyEntityTypeExtractor (array $ config ): callable
217
240
{
218
- $ resolvedTypes = TypeInfo::extractTypes ($ config ['query ' ]);
219
- $ entityTypes = [];
220
-
221
- foreach ($ resolvedTypes as $ type ) {
222
- if ($ type instanceof EntityObjectType) {
223
- $ entityTypes [$ type ->name ] = $ type ;
241
+ return function () use ($ config ) {
242
+ $ resolvedTypes = TypeInfo::extractTypes ($ config ['query ' ]);
243
+ $ entityTypes = [];
244
+
245
+ foreach ($ resolvedTypes as $ type ) {
246
+ if ($ type instanceof EntityObjectType) {
247
+ $ entityTypes [$ type ->name ] = $ type ;
248
+ }
224
249
}
225
- }
226
250
227
- return $ entityTypes ;
251
+ return $ entityTypes ;
252
+ };
228
253
}
229
254
}
0 commit comments