Skip to content

Commit 56bd855

Browse files
feature: Adds defer metadata template for codegen [2/2] (#328)
1 parent eb76487 commit 56bd855

File tree

2 files changed

+143
-1
lines changed

2 files changed

+143
-1
lines changed
Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
import GraphQLCompiler
2+
import IR
3+
import TemplateString
4+
5+
struct DeferredFragmentsMetadataTemplate {
6+
7+
let operation: IR.Operation
8+
let config: ApolloCodegen.ConfigurationContext
9+
let renderAccessControl: () -> String
10+
11+
init(
12+
operation: IR.Operation,
13+
config: ApolloCodegen.ConfigurationContext,
14+
renderAccessControl: @autoclosure @escaping () -> String
15+
) {
16+
self.operation = operation
17+
self.config = config
18+
self.renderAccessControl = renderAccessControl
19+
}
20+
21+
// MARK: Templates
22+
23+
/// Renders metadata definitions for the deferred fragments of an Operation.
24+
///
25+
/// - Returns: The `TemplateString` for the deferred fragments metadata definitions.
26+
func render() -> TemplateString {
27+
let deferredFragmentPathTypeInfo = DeferredFragmentsPathTypeInfo(
28+
from: operation.rootField.selectionSet.selections
29+
)
30+
guard !deferredFragmentPathTypeInfo.isEmpty else { return "" }
31+
32+
return """
33+
34+
// MARK: Deferred Fragment Metadata
35+
36+
\(renderAccessControl())extension \(operation.generatedDefinitionName) {
37+
\(DeferredFragmentIdentifiersTemplate(deferredFragmentPathTypeInfo))
38+
39+
\(DeferredFragmentsPropertyTemplate(deferredFragmentPathTypeInfo))
40+
}
41+
"""
42+
}
43+
44+
fileprivate func DeferredFragmentIdentifiersTemplate(
45+
_ deferredFragmentPathTypeInfo: [DeferredPathTypeInfo]
46+
) -> TemplateString {
47+
"""
48+
enum DeferredFragmentIdentifiers {
49+
\(deferredFragmentPathTypeInfo.map {
50+
return """
51+
static let \($0.deferCondition.label) = DeferredFragmentIdentifier(label: \"\($0.deferCondition.label)\", fieldPath: [\
52+
\($0.path.map { "\"\($0)\"" }, separator: ", ")\
53+
])
54+
"""
55+
}, separator: "\n")
56+
}
57+
"""
58+
}
59+
60+
fileprivate func DeferredFragmentsPropertyTemplate(
61+
_ deferredFragmentPathTypeInfo: [DeferredPathTypeInfo]
62+
) -> TemplateString {
63+
"""
64+
static var deferredFragments: [DeferredFragmentIdentifier: any \(config.ApolloAPITargetName).SelectionSet.Type]? {[
65+
\(deferredFragmentPathTypeInfo.map {
66+
return """
67+
DeferredFragmentIdentifiers.\($0.deferCondition.label): \($0.typeName).self,
68+
"""
69+
}, separator: "\n")
70+
]}
71+
"""
72+
}
73+
74+
// MARK: Helpers
75+
76+
fileprivate struct DeferredPathTypeInfo {
77+
let path: [String]
78+
let deferCondition: CompilationResult.DeferCondition
79+
let typeName: String
80+
}
81+
82+
fileprivate func DeferredFragmentsPathTypeInfo(
83+
from directSelections: DirectSelections?,
84+
path: [String] = []
85+
) -> [DeferredPathTypeInfo] {
86+
guard let directSelections, !directSelections.isEmpty else { return [] }
87+
88+
var deferredPathTypeInfo: [DeferredPathTypeInfo] = []
89+
90+
for field in directSelections.fields.values {
91+
if let field = field as? EntityField {
92+
let fieldPath = path + [(field.alias ?? field.name)]
93+
deferredPathTypeInfo.append(contentsOf:
94+
DeferredFragmentsPathTypeInfo(from: field.selectionSet.selections, path: fieldPath)
95+
)
96+
}
97+
}
98+
99+
for fragment in directSelections.inlineFragments.values {
100+
if let deferCondition = fragment.typeInfo.deferCondition {
101+
let selectionSetName = SelectionSetNameGenerator.generatedSelectionSetName(
102+
for: fragment.typeInfo,
103+
format: .omittingRoot,
104+
pluralizer: config.pluralizer
105+
)
106+
107+
deferredPathTypeInfo.append(DeferredPathTypeInfo(
108+
path: path,
109+
deferCondition: deferCondition,
110+
typeName: "Data.\(selectionSetName)"
111+
))
112+
}
113+
114+
deferredPathTypeInfo.append(contentsOf:
115+
DeferredFragmentsPathTypeInfo(from: fragment.selectionSet.selections, path: path)
116+
)
117+
}
118+
119+
for fragment in directSelections.namedFragments.values {
120+
if let deferCondition = fragment.typeInfo.deferCondition {
121+
deferredPathTypeInfo.append(DeferredPathTypeInfo(
122+
path: path,
123+
deferCondition: deferCondition,
124+
typeName: fragment.definition.name.asFragmentName
125+
))
126+
}
127+
128+
deferredPathTypeInfo.append(contentsOf:
129+
DeferredFragmentsPathTypeInfo(
130+
from: fragment.fragment.rootField.selectionSet.selections,
131+
path: path
132+
)
133+
)
134+
}
135+
136+
return deferredPathTypeInfo
137+
}
138+
}

Sources/ApolloCodegenLib/Templates/OperationDefinitionTemplate.swift

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,11 @@ struct OperationDefinitionTemplate: OperationTemplateRenderer {
4242
).renderBody())
4343
}
4444
}
45-
45+
\(section: DeferredFragmentsMetadataTemplate(
46+
operation: operation,
47+
config: config,
48+
renderAccessControl: { accessControlModifier(for: .parent) }()
49+
).render())
4650
""")
4751
}
4852

0 commit comments

Comments
 (0)