Skip to content

Commit 51b1d94

Browse files
authored
chore: kickoff release
2 parents 23e5555 + feeb054 commit 51b1d94

File tree

13 files changed

+318
-124
lines changed

13 files changed

+318
-124
lines changed

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

Lines changed: 27 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -50,19 +50,13 @@ public struct AuthRuleDecorator: ModelBasedGraphQLDocumentDecorator {
5050

5151
public func decorate(_ document: SingleDirectiveGraphQLDocument,
5252
modelSchema: ModelSchema) -> SingleDirectiveGraphQLDocument {
53-
let authRules = modelSchema.authRules.filterBy(authType: authType)
53+
let authRules = modelSchema.authRules
54+
.filterBy(authType: authType)
55+
.filterBy(ownerFieldType: .string, modelSchema: modelSchema)
5456
guard !authRules.isEmpty else {
5557
return document
5658
}
5759
var decorateDocument = document
58-
if authRules.readRestrictingOwnerRules().count > 1 {
59-
log.error("""
60-
Detected multiple owner type auth rules \
61-
with a READ operation. We currently do not support this use case. Please \
62-
limit your type to just one owner auth rule with a READ operation restriction.
63-
""")
64-
return decorateDocument
65-
}
6660

6761
let readRestrictingStaticGroups = authRules.groupClaimsToReadRestrictingStaticGroups()
6862
authRules.forEach { authRule in
@@ -179,6 +173,15 @@ public struct AuthRuleDecorator: ModelBasedGraphQLDocumentDecorator {
179173
}
180174
}
181175

176+
private extension AuthRule {
177+
func ownerField(inSchema schema: ModelSchema) -> ModelField? {
178+
guard let fieldName = self.ownerField else {
179+
return nil
180+
}
181+
return schema.field(withName: fieldName)
182+
}
183+
}
184+
182185
private extension AuthRules {
183186
func filterBy(authType: AWSAuthorizationType?) -> AuthRules {
184187
guard let authType = authType else {
@@ -195,6 +198,21 @@ private extension AuthRules {
195198
return authType == provider.toAWSAuthorizationType()
196199
}
197200
}
201+
202+
func filterBy(ownerFieldType: ModelFieldType,
203+
modelSchema: ModelSchema) -> AuthRules {
204+
return filter {
205+
guard let modelField = $0.ownerField(inSchema: modelSchema) else {
206+
// if we couldn't find the owner field means it has been implicitly
207+
// declared in the model schema, therefore has the correct type "string"
208+
return true
209+
}
210+
if case .string = modelField.type {
211+
return true
212+
}
213+
return false
214+
}
215+
}
198216
}
199217

200218
extension AuthRuleDecorator: DefaultLogger {

AmplifyPlugins/Core/AWSPluginsCoreTests/Model/Decorator/AuthRuleDecorator/ModelMultipleOwnerAuthRuleTests.swift

Lines changed: 10 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -71,31 +71,7 @@ class ModelMultipleOwnerAuthRuleTests: XCTestCase {
7171
override func tearDown() {
7272
ModelRegistry.reset()
7373
}
74-
// This is a test case to demostrate if we attempt to use a model with multiple auth rules
75-
// with a read operation, we effectively create a subscription without decorating it with auth.
76-
// We should delete this use case when the AppSync service supports this use case.
77-
func testUnsupportedModelMultipleOwner_CreateMutation() {
78-
var documentBuilder = ModelBasedGraphQLDocumentBuilder(modelType: ModelMultipleOwner.self,
79-
operationType: .mutation)
80-
documentBuilder.add(decorator: DirectiveNameDecorator(type: .create))
81-
documentBuilder.add(decorator: AuthRuleDecorator(.mutation))
82-
let document = documentBuilder.build()
83-
let expectedQueryDocument = """
84-
mutation CreateModelMultipleOwner {
85-
createModelMultipleOwner {
86-
id
87-
content
88-
editors
89-
__typename
90-
}
91-
}
92-
"""
93-
XCTAssertEqual(document.name, "createModelMultipleOwner")
94-
XCTAssertEqual(document.stringValue, expectedQueryDocument)
95-
XCTAssertTrue(document.variables.isEmpty)
96-
}
97-
98-
/*
74+
9975
// Ensure that the `owner` field is added to the model fields
10076
func testModelMultipleOwner_CreateMutation() {
10177
var documentBuilder = ModelBasedGraphQLDocumentBuilder(modelSchema: ModelMultipleOwner.schema,
@@ -219,7 +195,7 @@ class ModelMultipleOwnerAuthRuleTests: XCTestCase {
219195
operationType: .query)
220196
documentBuilder.add(decorator: DirectiveNameDecorator(type: .sync))
221197
documentBuilder.add(decorator: PaginationDecorator())
222-
documentBuilder.add(decorator: ConflictResolutionDecorator())
198+
documentBuilder.add(decorator: ConflictResolutionDecorator(graphQLType: .query))
223199
documentBuilder.add(decorator: AuthRuleDecorator(.query))
224200
let document = documentBuilder.build()
225201
let expectedQueryDocument = """
@@ -243,7 +219,7 @@ class ModelMultipleOwnerAuthRuleTests: XCTestCase {
243219
XCTAssertEqual(document.name, "syncModelMultipleOwners")
244220
XCTAssertEqual(document.stringValue, expectedQueryDocument)
245221
}
246-
222+
247223
// Only the 'owner' inherently has `.create` operation, requiring the subscription operation to contain the input
248224
func testModelMultipleOwner_OnCreateSubscription() {
249225
let claims = ["username": "user1",
@@ -254,8 +230,8 @@ class ModelMultipleOwnerAuthRuleTests: XCTestCase {
254230
documentBuilder.add(decorator: AuthRuleDecorator(.subscription(.onCreate, claims)))
255231
let document = documentBuilder.build()
256232
let expectedQueryDocument = """
257-
subscription OnCreateModelMultipleOwner($editors: String!, $owner: String!) {
258-
onCreateModelMultipleOwner(editors: $editors, owner: $owner) {
233+
subscription OnCreateModelMultipleOwner($owner: String!) {
234+
onCreateModelMultipleOwner(owner: $owner) {
259235
id
260236
content
261237
editors
@@ -283,8 +259,8 @@ class ModelMultipleOwnerAuthRuleTests: XCTestCase {
283259
documentBuilder.add(decorator: AuthRuleDecorator(.subscription(.onUpdate, claims)))
284260
let document = documentBuilder.build()
285261
let expectedQueryDocument = """
286-
subscription OnUpdateModelMultipleOwner($editors: String!, $owner: String!) {
287-
onUpdateModelMultipleOwner(editors: $editors, owner: $owner) {
262+
subscription OnUpdateModelMultipleOwner($owner: String!) {
263+
onUpdateModelMultipleOwner(owner: $owner) {
288264
id
289265
content
290266
editors
@@ -300,7 +276,6 @@ class ModelMultipleOwnerAuthRuleTests: XCTestCase {
300276
return
301277
}
302278
XCTAssertEqual(variables["owner"] as? String, "user1")
303-
XCTAssertEqual(variables["editors"] as? String, "user1")
304279
}
305280

306281
// Only the 'owner' inherently has `.delete` operation, requiring the subscription operation to contain the input
@@ -313,8 +288,8 @@ class ModelMultipleOwnerAuthRuleTests: XCTestCase {
313288
documentBuilder.add(decorator: AuthRuleDecorator(.subscription(.onDelete, claims)))
314289
let document = documentBuilder.build()
315290
let expectedQueryDocument = """
316-
subscription OnDeleteModelMultipleOwner($editors: String!, $owner: String!) {
317-
onDeleteModelMultipleOwner(editors: $editors, owner: $owner) {
291+
subscription OnDeleteModelMultipleOwner($owner: String!) {
292+
onDeleteModelMultipleOwner(owner: $owner) {
318293
id
319294
content
320295
editors
@@ -331,5 +306,5 @@ class ModelMultipleOwnerAuthRuleTests: XCTestCase {
331306
}
332307
XCTAssertEqual(variables["owner"] as? String, "user1")
333308
}
334-
*/
309+
335310
}

AmplifyPlugins/DataStore/Tests/DataStoreHostApp/AWSDataStorePluginAuthCognitoTests/AWSDataStoreCategoryPluginAuthOwnerIntegrationTests.swift

Lines changed: 41 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -92,15 +92,47 @@ class AWSDataStoreCategoryPluginAuthOwnerIntegrationTests: AWSDataStoreAuthBaseT
9292
}
9393

9494
let todo = TodoExplicitOwnerField(content: "content")
95-
95+
9696
// Mutations
9797
try await assertMutations(model: todo, expectations) { error in
9898
XCTFail("Error mutation \(error)")
9999
}
100-
100+
101+
assertUsedAuthTypes([.amazonCognitoUserPools])
102+
}
103+
104+
/// Given: a user signed in with CognitoUserPools, a model with multiple rules with
105+
/// explicit owner field
106+
/// When: DataStore query/mutation operations are sent with CognitoUserPools
107+
/// Then: DataStore is successfully initialized, query returns a result,
108+
/// mutation is processed for an authenticated users
109+
func testExplicitMultipleOwner() async throws {
110+
try await setup(withModels: ExplicitMultipleOwnerModelRegistration(),
111+
testType: .defaultAuthCognito)
112+
113+
try await signIn(user: user1)
114+
115+
let expectations = makeExpectations()
116+
117+
try await assertDataStoreReady(expectations)
118+
119+
// Query
120+
try await assertQuerySuccess(modelType: TodoCognitoMultiOwner.self,
121+
expectations) { error in
122+
XCTFail("Error query \(error)")
123+
}
124+
125+
let post = TodoCognitoMultiOwner(title: "title")
126+
127+
// Mutations
128+
try await assertMutations(model: post, expectations) { error in
129+
XCTFail("Error mutation \(error)")
130+
}
131+
101132
assertUsedAuthTypes([.amazonCognitoUserPools])
102133
}
103134

135+
104136
/// Given: a user signed in with CognitoUserPools, a model with an implicit owner field
105137
/// When: DataStore query/mutation operations are sent with CognitoUserPools
106138
/// Then: DataStore is successfully initialized, query returns a result,
@@ -187,6 +219,13 @@ extension AWSDataStoreCategoryPluginAuthOwnerIntegrationTests {
187219
}
188220
}
189221

222+
struct ExplicitMultipleOwnerModelRegistration: AmplifyModelRegistration {
223+
public let version: String = "version"
224+
func registerModels(registry: ModelRegistry.Type) {
225+
ModelRegistry.register(modelType: TodoCognitoMultiOwner.self)
226+
}
227+
}
228+
190229
struct ImplicitOwnerModelRegistration: AmplifyModelRegistration {
191230
public let version: String = "version"
192231
func registerModels(registry: ModelRegistry.Type) {
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
//
2+
// Copyright Amazon.com Inc. or its affiliates.
3+
// All Rights Reserved.
4+
//
5+
// SPDX-License-Identifier: Apache-2.0
6+
//
7+
8+
// swiftlint:disable all
9+
import Amplify
10+
import Foundation
11+
12+
extension TodoCognitoMultiOwner {
13+
// MARK: - CodingKeys
14+
public enum CodingKeys: String, ModelKey {
15+
case id
16+
case title
17+
case content
18+
case owner
19+
case editors
20+
case createdAt
21+
case updatedAt
22+
}
23+
24+
public static let keys = CodingKeys.self
25+
// MARK: - ModelSchema
26+
27+
public static let schema = defineSchema { model in
28+
let todoCognitoMultiOwner = TodoCognitoMultiOwner.keys
29+
30+
model.authRules = [
31+
rule(allow: .owner, ownerField: "owner", identityClaim: "cognito:username", provider: .userPools, operations: [.create, .update, .delete, .read]),
32+
rule(allow: .owner, ownerField: "editors", identityClaim: "cognito:username", provider: .userPools, operations: [.update, .read])
33+
]
34+
35+
model.listPluralName = "TodoCognitoMultiOwners"
36+
model.syncPluralName = "TodoCognitoMultiOwners"
37+
38+
model.attributes(
39+
.primaryKey(fields: [todoCognitoMultiOwner.id])
40+
)
41+
42+
model.fields(
43+
.field(todoCognitoMultiOwner.id, is: .required, ofType: .string),
44+
.field(todoCognitoMultiOwner.title, is: .required, ofType: .string),
45+
.field(todoCognitoMultiOwner.content, is: .optional, ofType: .string),
46+
.field(todoCognitoMultiOwner.owner, is: .optional, ofType: .string),
47+
.field(todoCognitoMultiOwner.editors, is: .optional, ofType: .embeddedCollection(of: String.self)),
48+
.field(todoCognitoMultiOwner.createdAt, is: .optional, isReadOnly: true, ofType: .dateTime),
49+
.field(todoCognitoMultiOwner.updatedAt, is: .optional, isReadOnly: true, ofType: .dateTime)
50+
)
51+
}
52+
}
53+
54+
extension TodoCognitoMultiOwner: ModelIdentifiable {
55+
public typealias IdentifierFormat = ModelIdentifierFormat.Default
56+
public typealias IdentifierProtocol = DefaultModelIdentifier<Self>
57+
}
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
//
2+
// Copyright Amazon.com Inc. or its affiliates.
3+
// All Rights Reserved.
4+
//
5+
// SPDX-License-Identifier: Apache-2.0
6+
//
7+
8+
// swiftlint:disable all
9+
import Amplify
10+
import Foundation
11+
12+
public struct TodoCognitoMultiOwner: Model {
13+
public let id: String
14+
public var title: String
15+
public var content: String?
16+
public var owner: String?
17+
public var editors: [String?]?
18+
public var createdAt: Temporal.DateTime?
19+
public var updatedAt: Temporal.DateTime?
20+
21+
public init(id: String = UUID().uuidString,
22+
title: String,
23+
content: String? = nil,
24+
owner: String? = nil,
25+
editors: [String?]? = nil) {
26+
self.init(id: id,
27+
title: title,
28+
content: content,
29+
owner: owner,
30+
editors: editors,
31+
createdAt: nil,
32+
updatedAt: nil)
33+
}
34+
internal init(id: String = UUID().uuidString,
35+
title: String,
36+
content: String? = nil,
37+
owner: String? = nil,
38+
editors: [String?]? = nil,
39+
createdAt: Temporal.DateTime? = nil,
40+
updatedAt: Temporal.DateTime? = nil) {
41+
self.id = id
42+
self.title = title
43+
self.content = content
44+
self.owner = owner
45+
self.editors = editors
46+
self.createdAt = createdAt
47+
self.updatedAt = updatedAt
48+
}
49+
}

AmplifyPlugins/DataStore/Tests/DataStoreHostApp/AWSDataStorePluginAuthCognitoTests/Models/TodoCognitoPrivate+Schema.swift

Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -17,24 +17,34 @@ extension TodoCognitoPrivate {
1717
case createdAt
1818
case updatedAt
1919
}
20-
20+
2121
public static let keys = CodingKeys.self
2222
// MARK: - ModelSchema
23-
23+
2424
public static let schema = defineSchema { model in
2525
let todoCognitoPrivate = TodoCognitoPrivate.keys
26-
26+
2727
model.authRules = [
28-
rule(allow: .private, provider: .userPools, operations: [.create, .update, .delete, .read])
28+
rule(allow: .private, operations: [.create, .update, .delete, .read])
2929
]
30-
31-
model.pluralName = "TodoCognitoPrivates"
32-
30+
31+
model.listPluralName = "TodoCognitoPrivates"
32+
model.syncPluralName = "TodoCognitoPrivates"
33+
34+
model.attributes(
35+
.primaryKey(fields: [todoCognitoPrivate.id])
36+
)
37+
3338
model.fields(
34-
.id(),
39+
.field(todoCognitoPrivate.id, is: .required, ofType: .string),
3540
.field(todoCognitoPrivate.title, is: .required, ofType: .string),
3641
.field(todoCognitoPrivate.createdAt, is: .optional, isReadOnly: true, ofType: .dateTime),
3742
.field(todoCognitoPrivate.updatedAt, is: .optional, isReadOnly: true, ofType: .dateTime)
3843
)
3944
}
4045
}
46+
47+
extension TodoCognitoPrivate: ModelIdentifiable {
48+
public typealias IdentifierFormat = ModelIdentifierFormat.Default
49+
public typealias IdentifierProtocol = DefaultModelIdentifier<Self>
50+
}

0 commit comments

Comments
 (0)