Skip to content

Commit a588c90

Browse files
authored
fix(api): build selection set bottom up (#2763)
* fix(api): build selection set bottom up * chore(doc): add more documentation
1 parent b2a1fcb commit a588c90

File tree

2 files changed

+59
-29
lines changed

2 files changed

+59
-29
lines changed

AmplifyPlugins/API/Tests/APIHostApp/AWSAPIPluginLazyLoadTests/LL1/GraphQLLazyLoadPostComment4V2Tests.swift

Lines changed: 16 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,8 @@ final class GraphQLLazyLoadPostComment4V2Tests: GraphQLLazyLoadBaseTest {
1919
await setup(withModels: PostComment4V2Models())
2020
let post = Post(title: "title")
2121
let comment = Comment(content: "content", post: post)
22-
let createdPost = try await mutate(.create(post))
23-
let createdComment = try await mutate(.create(comment))
22+
try await mutate(.create(post))
23+
try await mutate(.create(comment))
2424
}
2525

2626
// Without `includes` and latest codegenerated types with the model path, the post should be lazy loaded
@@ -117,7 +117,16 @@ final class GraphQLLazyLoadPostComment4V2Tests: GraphQLLazyLoadBaseTest {
117117
assertLazyReference(comments.first!._post, state: .notLoaded(identifiers: [.init(name: "id", value: post.identifier)]))
118118
}
119119

120-
// This looks broken
120+
121+
/*
122+
- Given: Api plugin is cleared
123+
- When:
124+
- Initialize with PostComment4V2Models
125+
- Create post
126+
- Create comment with [comment.post.comments.post] association path
127+
- Then:
128+
- The comment creation request GraphQL Selection Set represents the assocation path
129+
*/
121130
func testCommentWithEagerLoadPostAndPostCommentsAndPostCommentsPost() async throws {
122131
await setup(withModels: PostComment4V2Models())
123132
let post = Post(title: "title")
@@ -145,25 +154,21 @@ final class GraphQLLazyLoadPostComment4V2Tests: GraphQLLazyLoadBaseTest {
145154
updatedAt
146155
post {
147156
id
157+
createdAt
158+
title
159+
updatedAt
148160
__typename
149161
}
150162
__typename
151163
}
152164
}
153-
post {
154-
id
155-
createdAt
156-
title
157-
updatedAt
158-
__typename
159-
}
160165
}
161166
__typename
162167
}
163168
}
164169
"""
165170
XCTAssertEqual(request.document, expectedDocument)
166-
let createdComment = try await mutate(request)
171+
try await mutate(request)
167172
}
168173

169174
// Without `includes` and latest codegenerated types with the model path, the post's comments should be lazy loaded

AmplifyPlugins/Core/AWSPluginsCore/Model/Decorator/IncludeAssociationDecorator.swift

Lines changed: 43 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -35,8 +35,9 @@ public struct IncludeAssociationDecorator: ModelBasedGraphQLDocumentDecorator {
3535
includedAssociations.forEach { association in
3636
// we don't include the root reference because it refers to the root model
3737
// fields in the selection set, only the nested/included ones are needed
38-
let associationSelectionSet = association.asSelectionSet(includeRoot: false)
39-
selectionSet.merge(with: associationSelectionSet)
38+
if let associationSelectionSet = association.asSelectionSet(includeRoot: false) {
39+
selectionSet.merge(with: associationSelectionSet)
40+
}
4041
}
4142

4243
return document.copy(selectionSet: selectionSet)
@@ -45,24 +46,48 @@ public struct IncludeAssociationDecorator: ModelBasedGraphQLDocumentDecorator {
4546
}
4647

4748
extension PropertyContainerPath {
48-
49-
func asSelectionSet(includeRoot: Bool = true) -> SelectionSet {
50-
let metadata = getMetadata()
51-
let modelName = getModelType().modelName
52-
guard let schema = ModelRegistry.modelSchema(from: modelName) else {
53-
fatalError("Schema for model \(modelName) could not be found.")
49+
/// Build GraphQL Selection Set based on Model Associations
50+
/// `PropertyContainerPath` is a tree path with leaf node pointer that
51+
/// represents the associations from bottom to the top.
52+
///
53+
/// - Returns: A Optional<SelectionSet> represents GraphQL Selection Set from top to bottom.
54+
func asSelectionSet(includeRoot: Bool = true) -> SelectionSet? {
55+
func getSelectionSet(node: PropertyContainerPath) -> SelectionSet {
56+
let metadata = node.getMetadata()
57+
let modelName = node.getModelType().modelName
58+
59+
guard let schema = ModelRegistry.modelSchema(from: modelName) else {
60+
fatalError("Schema for model \(modelName) could not be found.")
61+
}
62+
let fieldType: SelectionSetFieldType = metadata.isCollection ? .collection : .model
63+
64+
let selectionSet = SelectionSet(value: .init(name: metadata.name, fieldType: fieldType))
65+
selectionSet.withModelFields(schema.graphQLFields, primaryKeysOnly: true)
66+
return selectionSet
67+
}
68+
69+
func shouldProcessNode(node: PropertyContainerPath) -> Bool {
70+
includeRoot || node.getMetadata().name != "root"
5471
}
55-
let fieldType: SelectionSetFieldType = metadata.isCollection ? .collection : .model
56-
57-
var selectionSet = SelectionSet(value: .init(name: metadata.name, fieldType: fieldType))
58-
selectionSet.withModelFields(schema.graphQLFields, primaryKeysOnly: true)
59-
if let parent = metadata.parent as? PropertyContainerPath,
60-
parent.getMetadata().parent != nil || includeRoot {
61-
let parentSelectionSet = parent.asSelectionSet(includeRoot: includeRoot)
62-
parentSelectionSet.replaceChild(selectionSet)
63-
selectionSet = parentSelectionSet
72+
73+
func nodesInPath(node: PropertyContainerPath) -> [PropertyContainerPath] {
74+
var parent: PropertyContainerPath? = node
75+
var path = [PropertyContainerPath]()
76+
while let currentNode = parent, shouldProcessNode(node: currentNode) {
77+
path.append(currentNode)
78+
parent = currentNode.getMetadata().parent as? PropertyContainerPath
79+
}
80+
return path
81+
}
82+
83+
let selectionSets = nodesInPath(node: self).map(getSelectionSet(node:))
84+
return selectionSets.dropFirst().reduce(selectionSets.first) { partialResult, selectionSet in
85+
guard let partialResult = partialResult else {
86+
return selectionSet
87+
}
88+
selectionSet.replaceChild(partialResult)
89+
return selectionSet
6490
}
65-
return selectionSet
6691
}
6792

6893
}

0 commit comments

Comments
 (0)