Skip to content

Commit e1680e1

Browse files
lawmichadrochetti
andcommitted
feat: DataStore and API Lazy Loading with Custom Selection Set (#2583)
* adds selection set customization to api category * fix selection set indent logic * lazy load all commits * Add PrimaryKeyOnly flag to GraphQLRequestBuilders * API - Add initial PostComment4V2 tests * API progress on PostComment4V2 tests * API update and delete tests for PostComment4V2 tests * cancel subscription in subscription tests * Adding copy of DataStore tests and make buildable * updated codegen for postcomment and minor testSave tests * Setting up ProjectTeam codegen schemas and running initial test * API LL3 LL4 testing * API LL5 LL6 testing * API LL2 testing * disable unfinished tests * Update DataStore integ models with modePath * fix unit tests in API and DataStore * test DS integration tests * migrate LazyModel to LazyReference * code clean up * code clean up * Update Amplify-Package.xcscheme * add geo scheme * code clean up * address PR comments 1 - remove duplicate state enum, move primaryKeysOnly check within decorator * fix primaryKey parameter * add L12 codegenerated files * API testing LL12 * DS LL12 test placeholder * enable reduced selection set for DataStore for all operations * disable reduced selection set for DataStore mutations * disable reduced selection set for DataStore mutations 2 * remove print statments * add typename from client side for DataStore MutationSync<AnyModel> * add _deleted to new subscription selection sets only for nested children, more API CommentPost schema integ tests, fix AWSPluginsCore unit tests * fix API integ tests for ProjectTeam 5/6 * optimize list and lazy reference decoding logic * remove public loadingStrategy configuration * fix race case for plugin resets wrt the ModelRegistry being reset * update models and fix tests * test stability - set up datastore only for selection set tests * reduce logging in Decorator * test stability - reduce syncMaxRecords to 100 * test stability - clearOnTearDown false for all tests * API integ tests - subscribe team project LL8/9 * API integ tests - refactor LL112 into 3 separate test classes * PhoneCall testing * fix error handling - remove DataError - return API or DataStore error from respective plugin model providers - return CoreError from core implementation * code clean up - rename shouldDecode to decode - use _ prefix for public but intended internal Co-authored-by: Daniel Rochetti <[email protected]>
1 parent fb24bd3 commit e1680e1

File tree

337 files changed

+23717
-690
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

337 files changed

+23717
-690
lines changed

.swiftpm/xcode/xcshareddata/xcschemes/AWSLocationGeoPlugin.xcscheme

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
<?xml version="1.0" encoding="UTF-8"?>
22
<Scheme
3-
LastUpgradeVersion = "1400"
3+
LastUpgradeVersion = "1410"
44
version = "1.3">
55
<BuildAction
66
parallelizeBuildables = "YES"
@@ -20,6 +20,20 @@
2020
ReferencedContainer = "container:">
2121
</BuildableReference>
2222
</BuildActionEntry>
23+
<BuildActionEntry
24+
buildForTesting = "YES"
25+
buildForRunning = "NO"
26+
buildForProfiling = "NO"
27+
buildForArchiving = "NO"
28+
buildForAnalyzing = "NO">
29+
<BuildableReference
30+
BuildableIdentifier = "primary"
31+
BlueprintIdentifier = "AWSLocationGeoPluginTests"
32+
BuildableName = "AWSLocationGeoPluginTests"
33+
BlueprintName = "AWSLocationGeoPluginTests"
34+
ReferencedContainer = "container:">
35+
</BuildableReference>
36+
</BuildActionEntry>
2337
</BuildActionEntries>
2438
</BuildAction>
2539
<TestAction

Amplify/Categories/API/Internal/APICategory+Resettable.swift

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,6 @@ extension APICategory: Resettable {
2020
}
2121
await taskGroup.waitForAll()
2222
}
23-
log.verbose("Resetting ModelRegistry and ModelListDecoderRegistry")
24-
ModelRegistry.reset()
25-
ModelListDecoderRegistry.reset()
26-
log.verbose("Resetting ModelRegistry and ModelListDecoderRegistry: finished")
2723

2824
isConfigured = false
2925
}

Amplify/Categories/DataStore/Internal/DataStoreCategory+Resettable.swift

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,6 @@ extension DataStoreCategory: Resettable {
2020
}
2121
await taskGroup.waitForAll()
2222
}
23-
log.verbose("Resetting ModelRegistry and ModelListDecoderRegistry")
24-
ModelRegistry.reset()
25-
ModelListDecoderRegistry.reset()
26-
log.verbose("Resetting ModelRegistry and ModelListDecoderRegistry: finished")
2723

2824
isConfigured = false
2925
}

Amplify/Categories/DataStore/Model/Internal/ModelListDecoder.swift

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,5 @@ extension ModelListDecoderRegistry {
3636
/// application making any change to these `public` types should be backward compatible, otherwise it will be a breaking
3737
/// change.
3838
public protocol ModelListDecoder {
39-
static func shouldDecode<ModelType: Model>(modelType: ModelType.Type, decoder: Decoder) -> Bool
40-
static func makeListProvider<ModelType: Model>(
41-
modelType: ModelType.Type, decoder: Decoder) throws -> AnyModelListProvider<ModelType>
39+
static func decode<ModelType: Model>(modelType: ModelType.Type, decoder: Decoder) -> AnyModelListProvider<ModelType>?
4240
}

Amplify/Categories/DataStore/Model/Internal/ModelListProvider.swift

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66
//
77

88
import Foundation
9-
import Combine
109

1110
/// Empty protocol used as a marker to detect when the type is a `List`
1211
///
@@ -23,7 +22,13 @@ public protocol ModelListMarker { }
2322
/// application making any change to these `public` types should be backward compatible, otherwise it will be a breaking
2423
/// change.
2524
public enum ModelListProviderState<Element: Model> {
26-
case notLoaded
25+
/// If the list represents an association between two models, the `associatedIdentifiers` will
26+
/// hold the information necessary to query the associated elements (e.g. comments of a post)
27+
///
28+
/// The associatedField represents the field to which the owner of the `List` is linked to.
29+
/// For example, if `Post.comments` is associated with `Comment.post` the `List<Comment>`
30+
/// of `Post` will have a reference to the `post` field in `Comment`.
31+
case notLoaded(associatedIdentifiers: [String], associatedField: String)
2732
case loaded([Element])
2833
}
2934

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
//
2+
// Copyright Amazon.com Inc. or its affiliates.
3+
// All Rights Reserved.
4+
//
5+
// SPDX-License-Identifier: Apache-2.0
6+
//
7+
8+
import Foundation
9+
10+
/// Protocol used as a marker to detect when the type is a `LazyReference`.
11+
/// Used to retrieve either the `reference` or the `identifiers` of the Model directly, without having load a not
12+
/// loaded LazyReference. This is useful when translating the model object over to the payload required for the
13+
/// underlying storage, such as storing the values in DataStore's local database or AppSync GraphQL request payload.
14+
///
15+
///
16+
/// - Warning: Although this has `public` access, it is intended for internal & codegen use and should not be used
17+
/// directly by host applications. The behavior of this may change without warning. Though it is not used by host
18+
/// application making any change to these `public` types should be backward compatible, otherwise it will be a breaking
19+
/// change.
20+
public protocol _LazyReferenceValue {
21+
var _state: _LazyReferenceValueState { get }
22+
}
23+
24+
public enum _LazyReferenceValueState {
25+
case notLoaded(identifiers: [LazyReferenceIdentifier]?)
26+
case loaded(model: Model?)
27+
}
28+
29+
/// State of the ModelProvider
30+
///
31+
/// - Warning: Although this has `public` access, it is intended for internal & codegen use and should not be used
32+
/// directly by host applications. The behavior of this may change without warning. Though it is not used by host
33+
/// application making any change to these `public` types should be backward compatible, otherwise it will be a breaking
34+
/// change.
35+
public enum ModelProviderState<Element: Model> {
36+
case notLoaded(identifiers: [LazyReferenceIdentifier]?)
37+
case loaded(model: Element?)
38+
}
39+
40+
/// - Warning: Although this has `public` access, it is intended for internal & codegen use and should not be used
41+
/// directly by host applications. The behavior of this may change without warning. Though it is not used by host
42+
/// application making any change to these `public` types should be backward compatible, otherwise it will be a breaking
43+
/// change.
44+
public protocol ModelProvider {
45+
associatedtype Element: Model
46+
47+
func load() async throws -> Element?
48+
49+
func getState() -> ModelProviderState<Element>
50+
}
51+
52+
/// - Warning: Although this has `public` access, it is intended for internal & codegen use and should not be used
53+
/// directly by host applications. The behavior of this may change without warning. Though it is not used by host
54+
/// application making any change to these `public` types should be backward compatible, otherwise it will be a breaking
55+
/// change.
56+
public struct AnyModelProvider<Element: Model>: ModelProvider {
57+
58+
private let loadAsync: () async throws -> Element?
59+
private let getStateClosure: () -> ModelProviderState<Element>
60+
61+
public init<Provider: ModelProvider>(provider: Provider) where Provider.Element == Self.Element {
62+
self.loadAsync = provider.load
63+
self.getStateClosure = provider.getState
64+
}
65+
public func load() async throws -> Element? {
66+
try await loadAsync()
67+
}
68+
69+
public func getState() -> ModelProviderState<Element> {
70+
getStateClosure()
71+
}
72+
}
73+
74+
/// - Warning: Although this has `public` access, it is intended for internal & codegen use and should not be used
75+
/// directly by host applications. The behavior of this may change without warning. Though it is not used by host
76+
/// application making any change to these `public` types should be backward compatible, otherwise it will be a breaking
77+
/// change.
78+
public extension ModelProvider {
79+
func eraseToAnyModelProvider() -> AnyModelProvider<Element> {
80+
AnyModelProvider(provider: self)
81+
}
82+
}
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
//
2+
// Copyright Amazon.com Inc. or its affiliates.
3+
// All Rights Reserved.
4+
//
5+
// SPDX-License-Identifier: Apache-2.0
6+
//
7+
8+
import Foundation
9+
10+
/// Registry of `ModelProviderDecoder`'s used to retrieve decoders that can create `ModelProvider`s to perform
11+
/// LazyReference functionality.
12+
///
13+
/// - Warning: Although this has `public` access, it is intended for internal & codegen use and should not be used
14+
/// directly by host applications. The behavior of this may change without warning. Though it is not used by host
15+
/// application making any change to these `public` types should be backward compatible, otherwise it will be a breaking
16+
/// change.
17+
public struct ModelProviderRegistry {
18+
static var decoders = AtomicValue(initialValue: [ModelProviderDecoder.Type]())
19+
20+
/// Register a decoder during plugin configuration time, to allow runtime retrievals of model providers.
21+
public static func registerDecoder(_ decoder: ModelProviderDecoder.Type) {
22+
decoders.append(decoder)
23+
}
24+
}
25+
26+
extension ModelProviderRegistry {
27+
static func reset() {
28+
decoders.set([ModelProviderDecoder.Type]())
29+
}
30+
}
31+
32+
/// `ModelProviderDecoder` provides decoding and lazy reference functionality.
33+
///
34+
/// - Warning: Although this has `public` access, it is intended for internal & codegen use and should not be used
35+
/// directly by host applications. The behavior of this may change without warning. Though it is not used by host
36+
/// application making any change to these `public` types should be backward compatible, otherwise it will be a breaking
37+
/// change.
38+
public protocol ModelProviderDecoder {
39+
static func decode<ModelType: Model>(modelType: ModelType.Type, decoder: Decoder) -> AnyModelProvider<ModelType>?
40+
}

Amplify/Categories/DataStore/Model/Internal/Schema/ModelField+Association.swift

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -225,6 +225,19 @@ extension ModelField {
225225
}
226226
return true
227227
}
228+
229+
/// - Warning: Although this has `public` access, it is intended for internal & codegen use and should not be used
230+
/// directly by host applications. The behavior of this may change without warning. Though it is not used by host
231+
/// application making any change to these `public` types should be backward compatible, otherwise it will be a
232+
/// breaking change.
233+
public var _isBelongsToOrHasOne: Bool {
234+
switch association {
235+
case .belongsTo, .hasOne:
236+
return true
237+
case .hasMany, .none:
238+
return false
239+
}
240+
}
228241

229242
/// - Warning: Although this has `public` access, it is intended for internal & codegen use and should not be used
230243
/// directly by host applications. The behavior of this may change without warning. Though it is not used by host
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
//
2+
// Copyright Amazon.com Inc. or its affiliates.
3+
// All Rights Reserved.
4+
//
5+
// SPDX-License-Identifier: Apache-2.0
6+
//
7+
8+
import Foundation
9+
10+
// MARK: - DefaultModelProvider
11+
12+
public struct DefaultModelProvider<Element: Model>: ModelProvider {
13+
14+
var loadedState: ModelProviderState<Element>
15+
16+
public init(element: Element? = nil) {
17+
self.loadedState = .loaded(model: element)
18+
}
19+
20+
public init(identifiers: [LazyReferenceIdentifier]?) {
21+
self.loadedState = .notLoaded(identifiers: identifiers)
22+
}
23+
24+
public func load() async throws -> Element? {
25+
switch loadedState {
26+
case .notLoaded:
27+
throw CoreError.clientValidation("DefaultModelProvider does not provide loading capabilities", "")
28+
case .loaded(let model):
29+
return model
30+
}
31+
}
32+
33+
public func getState() -> ModelProviderState<Element> {
34+
loadedState
35+
}
36+
}

0 commit comments

Comments
 (0)