Skip to content

Commit d8f83ee

Browse files
Merge pull request #103 from NeedleInAJayStack/fix/federation-introspection
Fix: Federation Introspection
2 parents 14f378c + b86c46a commit d8f83ee

File tree

4 files changed

+55
-65
lines changed

4 files changed

+55
-65
lines changed

Sources/Graphiti/Federation/Queries.swift

Lines changed: 7 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
11
import GraphQL
22
import NIO
33

4-
let resolveReferenceFieldName = "__resolveReference"
5-
64
func serviceQuery(for sdl: String) -> GraphQLField {
75
return GraphQLField(
86
type: GraphQLNonNull(serviceType),
@@ -14,26 +12,25 @@ func serviceQuery(for sdl: String) -> GraphQLField {
1412
)
1513
}
1614

17-
func entitiesQuery(for federatedTypes: [GraphQLObjectType], entityType: GraphQLUnionType, coders: Coders) -> GraphQLField {
15+
func entitiesQuery(
16+
for federatedResolvers: [String: GraphQLFieldResolve],
17+
entityType: GraphQLUnionType,
18+
coders: Coders
19+
) -> GraphQLField {
1820
return GraphQLField(
1921
type: GraphQLNonNull(GraphQLList(entityType)),
2022
description: "Return all entities matching the provided representations.",
21-
args: ["representations": GraphQLArgument(type: GraphQLList(anyType))],
23+
args: ["representations": GraphQLArgument(type: GraphQLNonNull(GraphQLList(GraphQLNonNull(anyType))))],
2224
resolve: { source, args, context, eventLoopGroup, info in
2325
let arguments = try coders.decoder.decode(EntityArguments.self, from: args)
2426
let futures: [EventLoopFuture<Any?>] = try arguments.representations.map { (representationMap: Map) in
2527
let representation = try coders.decoder.decode(
2628
EntityRepresentation.self,
2729
from: representationMap
2830
)
29-
guard let type = federatedTypes.first(where: { value in value.name == representation.__typename }) else {
31+
guard let resolve = federatedResolvers[representation.__typename] else {
3032
throw GraphQLError(message: "Federated type not found: \(representation.__typename)")
3133
}
32-
guard let resolve = type.fields[resolveReferenceFieldName]?.resolve else {
33-
throw GraphQLError(
34-
message: "Federated type has no '__resolveReference' field resolver: \(type.name)"
35-
)
36-
}
3734
return try resolve(
3835
source,
3936
representationMap,

Sources/Graphiti/Query/Query.swift

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,11 @@ public final class Query<Resolver, Context>: Component<Resolver, Context> {
2424
typeProvider.types.append(entity)
2525

2626
// Add subgraph queries (_entities, _service)
27-
queryFields["_entities"] = entitiesQuery(for: federatedTypes, entityType: entity, coders: coders)
27+
queryFields["_entities"] = entitiesQuery(
28+
for: typeProvider.federatedResolvers,
29+
entityType: entity,
30+
coders: coders
31+
)
2832
queryFields["_service"] = serviceQuery(for: sdl)
2933
}
3034

Sources/Graphiti/Schema/SchemaTypeProvider.swift

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ final class SchemaTypeProvider: TypeProvider {
1616
]
1717

1818
var federatedTypes: [GraphQLObjectType] = []
19+
var federatedResolvers: [String: GraphQLFieldResolve] = [:]
1920
var federatedSDL: String? = nil
2021

2122
var query: GraphQLObjectType?
@@ -28,9 +29,4 @@ final class SchemaTypeProvider: TypeProvider {
2829
try map(type, to: graphQLType)
2930
types.append(graphQLType)
3031
}
31-
32-
func addFederated(type: Any.Type, as graphQLType: GraphQLObjectType) throws {
33-
try add(type: type, as: graphQLType)
34-
federatedTypes.append(graphQLType)
35-
}
3632
}

Sources/Graphiti/Type/Type.swift

Lines changed: 42 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,20 @@ public final class Type<Resolver, Context, ObjectType: Encodable>: TypeComponent
1313
}
1414

1515
override func update(typeProvider: SchemaTypeProvider, coders: Coders) throws {
16-
var fieldDefs = try fields(typeProvider: typeProvider, coders: coders)
16+
let fieldDefs = try fields(typeProvider: typeProvider, coders: coders)
17+
let objectType = try GraphQLObjectType(
18+
name: name,
19+
description: description,
20+
fields: fieldDefs,
21+
interfaces: interfaces.map {
22+
try typeProvider.getInterfaceType(from: $0)
23+
},
24+
isTypeOf: isTypeOf
25+
)
1726

27+
try typeProvider.add(type: ObjectType.self, as: objectType)
28+
29+
// If federation keys are included, validate and create resolver closure
1830
if !keys.isEmpty {
1931
let fieldNames = Array(fieldDefs.keys)
2032
for key in keys {
@@ -25,58 +37,39 @@ public final class Type<Resolver, Context, ObjectType: Encodable>: TypeComponent
2537
)
2638
}
2739

28-
fieldDefs[resolveReferenceFieldName] = GraphQLField(
29-
type: GraphQLNonNull(GraphQLTypeReference(name)), // Self-referential
30-
description: "Return the entity of this object type that matches the provided representation. Used by Query._entities.",
31-
args: [
32-
"representations": GraphQLArgument(type: GraphQLList(anyType))
33-
],
34-
resolve: { source, args, context, eventLoopGroup, info in
35-
guard let s = source as? Resolver else {
36-
throw GraphQLError(
37-
message: "Expected source type \(ObjectType.self) but got \(type(of: source))"
38-
)
39-
}
40-
41-
guard let c = context as? Context else {
42-
throw GraphQLError(
43-
message: "Expected context type \(Context.self) but got \(type(of: context))"
44-
)
45-
}
46-
47-
let keyMatch = self.keys.first { key in
48-
key.mapMatchesArguments(args, coders: coders)
49-
}
50-
guard let key = keyMatch else {
51-
throw GraphQLError(
52-
message: "No matching key was found for representation \(args)."
53-
)
54-
}
55-
56-
return try key.resolveMap(
57-
resolver: s,
58-
context: c,
59-
map: args,
60-
eventLoopGroup: eventLoopGroup,
61-
coders: coders
40+
let resolve: GraphQLFieldResolve = { source, args, context, eventLoopGroup, info in
41+
guard let s = source as? Resolver else {
42+
throw GraphQLError(
43+
message: "Expected source type \(ObjectType.self) but got \(type(of: source))"
6244
)
6345
}
64-
)
65-
}
66-
67-
let objectType = try GraphQLObjectType(
68-
name: name,
69-
description: description,
70-
fields: fieldDefs,
71-
interfaces: interfaces.map {
72-
try typeProvider.getInterfaceType(from: $0)
73-
},
74-
isTypeOf: isTypeOf
75-
)
7646

77-
try typeProvider.add(type: ObjectType.self, as: objectType)
78-
if !keys.isEmpty {
47+
guard let c = context as? Context else {
48+
throw GraphQLError(
49+
message: "Expected context type \(Context.self) but got \(type(of: context))"
50+
)
51+
}
52+
53+
let keyMatch = self.keys.first { key in
54+
key.mapMatchesArguments(args, coders: coders)
55+
}
56+
guard let key = keyMatch else {
57+
throw GraphQLError(
58+
message: "No matching key was found for representation \(args)."
59+
)
60+
}
61+
62+
return try key.resolveMap(
63+
resolver: s,
64+
context: c,
65+
map: args,
66+
eventLoopGroup: eventLoopGroup,
67+
coders: coders
68+
)
69+
}
70+
7971
typeProvider.federatedTypes.append(objectType)
72+
typeProvider.federatedResolvers[name] = resolve
8073
}
8174
}
8275

0 commit comments

Comments
 (0)