Skip to content

Commit 61e8b31

Browse files
authored
fix(datastore): decode optimistically with paginated list response type (#3474)
* fix(datastore): decode optimistically with paginated list response type * add unit test cases * resolve comments * Update PaginatedListTests.swift
1 parent 3a47dd6 commit 61e8b31

File tree

4 files changed

+91
-2
lines changed

4 files changed

+91
-2
lines changed

Amplify/Categories/API/Response/GraphQLError.swift

+2
Original file line numberDiff line numberDiff line change
@@ -44,3 +44,5 @@ extension GraphQLError {
4444
public let column: Int
4545
}
4646
}
47+
48+
extension GraphQLError: Error { }

AmplifyPlugins/Core/AWSPluginsCore/Sync/PaginatedList.swift

+31
Original file line numberDiff line numberDiff line change
@@ -12,4 +12,35 @@ public struct PaginatedList<ModelType: Model>: Decodable {
1212
public let items: [MutationSync<ModelType>]
1313
public let nextToken: String?
1414
public let startedAt: Int64?
15+
16+
enum CodingKeys: CodingKey {
17+
case items
18+
case nextToken
19+
case startedAt
20+
}
21+
22+
public init(items: [MutationSync<ModelType>], nextToken: String?, startedAt: Int64?) {
23+
self.items = items
24+
self.nextToken = nextToken
25+
self.startedAt = startedAt
26+
}
27+
28+
public init(from decoder: Decoder) throws {
29+
let values = try decoder.container(keyedBy: CodingKeys.self)
30+
let optimisticDecodedResults = try values.decode([OptimisticDecoded<MutationSync<ModelType>>].self, forKey: .items)
31+
items = optimisticDecodedResults.compactMap { try? $0.result.get() }
32+
nextToken = try values.decode(String?.self, forKey: .nextToken)
33+
startedAt = try values.decode(Int64?.self, forKey: .startedAt)
34+
}
35+
}
36+
37+
38+
fileprivate struct OptimisticDecoded<T: Decodable>: Decodable {
39+
let result: Result<T, Error>
40+
41+
init(from decoder: Decoder) throws {
42+
result = Result(catching: {
43+
try T(from: decoder)
44+
})
45+
}
1546
}

AmplifyPlugins/Core/AWSPluginsCoreTests/Sync/PaginatedListTests.swift

+50
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@ class PaginatedListTests: XCTestCase {
6969
XCTAssertNotNil(paginatedList.startedAt)
7070
XCTAssertNotNil(paginatedList.nextToken)
7171
XCTAssertNotNil(paginatedList.items)
72+
XCTAssertEqual(paginatedList.items.count, 2)
7273
XCTAssert(!paginatedList.items.isEmpty)
7374
XCTAssert(paginatedList.items[0].model.title == "title")
7475
XCTAssert(paginatedList.items[0].syncMetadata.version == 10)
@@ -94,4 +95,53 @@ class PaginatedListTests: XCTestCase {
9495
}
9596
}
9697

98+
/// - Given: a `Post` Sync query with items, nextToken, and with sync data (startedAt, _version, etc)
99+
/// - When:
100+
/// - some of the JSON items are not able to be decoded to Post
101+
/// - the JSON is decoded into `PaginatedList<Post>`
102+
/// - Then:
103+
/// - the result should contain only valid items of type MutationSync<Post>, startedAt, nextToken.
104+
func testDecodePaginatedListOptimistically() {
105+
let syncQueryJSON = """
106+
{
107+
"items": [
108+
null,
109+
{
110+
"id": "post-id",
111+
"createdAt": "2019-11-27T23:35:39Z",
112+
"_version": 10,
113+
"_lastChangedAt": 1574897753341,
114+
"_deleted": null
115+
},
116+
{
117+
"id": "post-id",
118+
"title": "title",
119+
"content": "post content",
120+
"createdAt": "2019-11-27T23:35:39Z",
121+
"_version": 11,
122+
"_lastChangedAt": 1574897753341,
123+
"_deleted": null
124+
}
125+
],
126+
"startedAt": 1575322600038,
127+
"nextToken": "token"
128+
}
129+
"""
130+
do {
131+
let decoder = JSONDecoder(dateDecodingStrategy: ModelDateFormatting.decodingStrategy)
132+
let data = Data(syncQueryJSON.utf8)
133+
let paginatedList = try decoder.decode(PaginatedList<Post>.self, from: data)
134+
XCTAssertNotNil(paginatedList)
135+
XCTAssertNotNil(paginatedList.startedAt)
136+
XCTAssertNotNil(paginatedList.nextToken)
137+
XCTAssertNotNil(paginatedList.items)
138+
XCTAssertEqual(paginatedList.items.count, 1)
139+
XCTAssert(paginatedList.items[0].model.title == "title")
140+
XCTAssert(paginatedList.items[0].syncMetadata.version == 11)
141+
XCTAssert(paginatedList.items[0].syncMetadata.lastChangedAt == 1_574_897_753_341)
142+
} catch {
143+
XCTFail(error.localizedDescription)
144+
}
145+
}
146+
97147
}

AmplifyPlugins/DataStore/Sources/AWSDataStorePlugin/Sync/InitialSync/InitialSyncOperation.swift

+8-2
Original file line numberDiff line numberDiff line change
@@ -216,11 +216,17 @@ final class InitialSyncOperation: AsynchronousOperation {
216216

217217
let syncQueryResult: SyncQueryResult
218218
switch graphQLResult {
219+
case .success(let queryResult):
220+
syncQueryResult = queryResult
221+
222+
case .failure(.partial(let queryResult, let errors)):
223+
syncQueryResult = queryResult
224+
errors.map { DataStoreError.api(APIError(errorDescription: $0.message, error: $0)) }
225+
.forEach { dataStoreConfiguration.errorHandler($0) }
226+
219227
case .failure(let graphQLResponseError):
220228
finish(result: .failure(DataStoreError.api(graphQLResponseError)))
221229
return
222-
case .success(let queryResult):
223-
syncQueryResult = queryResult
224230
}
225231

226232
let items = syncQueryResult.items

0 commit comments

Comments
 (0)