amplify-swift
amplify-swift copied to clipboard
@belongsTo field is not received in subscription causing failure to process event.
Describe the bug
I have schema with @hasMany and @belongsTo directives using cognito-pool dynamic group level authorisation. When I create subscription in Swift, the field with @belongTo is coming as null, due to which event is not consumed. The same scenario is happening on AppSync AWS console as well. In android, the null part is ignored and event is consumed. However when I send item from Swift @belongTo object is received on AppSync AWS console.
What can I do to make the subscription work?
Following is my schema:
type Message @model @auth(rules: [
{allow: groups, groupsField: "permittedTo"}
]) {
id: ID!
type: Int!
content: String!
senderId: Int!
senderName: String!
groupId: String! @index(name:"byGroup", sortKeyFields: ["sentAt"])
groupName: String!
status: MessageStatus!
role: String
tag: String
comment: String
sentAt: AWSDateTime!
orgId: Int! @index(name:"byOrgId")
recipients: [MessageRecipient] @hasMany(indexName: "byMessage", fields: ["id"])
permittedTo: [String!]
}
type MessageRecipient @model @auth(rules: [
{allow: groups, groupsField: "permittedTo"}
]) {
id: ID!
messageId: ID @index(name:"byMessage")
groupId: String! @index(name: "byGroup")
message: Message @belongsTo(fields: ["messageId"])
userId: Int! @index(name:"byUser")
status: MessageStatus!
sentAt: AWSDateTime!
readAt: AWSDateTime
permittedTo: [String!]
}
enum MessageStatus {
OFFLINE
SENT
DELIVERED
READ
}
type Subscription {
onUpdateMessageStatus(status: String!): Message @aws_subscribe(mutations: ["updateMessage"])
}
Steps To Reproduce
1. Create DataStore Subscription in Swift.
2. Run DataStore.save on Android.
3. Error occurs.
Expected behavior
Subscription should work either by ignoring null @belongTo field or should return @belongTo field.
Amplify Framework Version
2.17.1
Amplify Categories
Auth, DataStore
Dependency manager
Swift PM
Swift version
5.8.1
CLI version
12.4.0
Xcode version
14.3.1
Relevant log output
<details>
<summary>Log Messages</summary>
[DataStoreIncomingAsyncSubscriptionEventToAnyModelMapper] receive(_:): data(Swift.Result<AWSPluginsCore.MutationSync<AWSPluginsCore.AnyModel>, Amplify.GraphQLResponseError<AWSPluginsCore.MutationSync<AWSPluginsCore.AnyModel>>>.failure(GraphQLResponseError<MutationSync<AnyModel>>: GraphQL service returned a partially-successful response containing errors: [Amplify.GraphQLError(message: "Cannot return null for non-nullable type: \'String\' within parent \'Message\' (/onCreateMessageRecipient/message/content)", locations: nil, path: Optional([Amplify.JSONValue.string("onCreateMessageRecipient"), Amplify.JSONValue.string("message"), Amplify.JSONValue.string("content")]), extensions: nil), Amplify.GraphQLError(message: "Cannot return null for non-nullable type: \'AWSDateTime\' within parent \'Message\' (/onCreateMessageRecipient/message/createdAt)", locations: nil, path: Optional([Amplify.JSONValue.string("onCreateMessageRecipient"), Amplify.JSONValue.string("message"), Amplify.JSONValue.string("createdAt")]), extensions: nil), Amplify.GraphQLError(message: "Cannot return null for non-nullable type: \'String\' within parent \'Message\' (/onCreateMessageRecipient/message/groupId)", locations: nil, path: Optional([Amplify.JSONValue.string("onCreateMessageRecipient"), Amplify.JSONValue.string("message"), Amplify.JSONValue.string("groupId")]), extensions: nil), Amplify.GraphQLError(message: "Cannot return null for non-nullable type: \'String\' within parent \'Message\' (/onCreateMessageRecipient/message/groupName)", locations: nil, path: Optional([Amplify.JSONValue.string("onCreateMessageRecipient"), Amplify.JSONValue.string("message"), Amplify.JSONValue.string("groupName")]), extensions: nil), Amplify.GraphQLError(message: "Cannot return null for non-nullable type: \'Int\' within parent \'Message\' (/onCreateMessageRecipient/message/orgId)", locations: nil, path: Optional([Amplify.JSONValue.string("onCreateMessageRecipient"), Amplify.JSONValue.string("message"), Amplify.JSONValue.string("orgId")]), extensions: nil), Amplify.GraphQLError(message: "Cannot return null for non-nullable type: \'Int\' within parent \'Message\' (/onCreateMessageRecipient/message/senderId)", locations: nil, path: Optional([Amplify.JSONValue.string("onCreateMessageRecipient"), Amplify.JSONValue.string("message"), Amplify.JSONValue.string("senderId")]), extensions: nil), Amplify.GraphQLError(message: "Cannot return null for non-nullable type: \'String\' within parent \'Message\' (/onCreateMessageRecipient/message/senderName)", locations: nil, path: Optional([Amplify.JSONValue.string("onCreateMessageRecipient"), Amplify.JSONValue.string("message"), Amplify.JSONValue.string("senderName")]), extensions: nil), Amplify.GraphQLError(message: "Cannot return null for non-nullable type: \'AWSDateTime\' within parent \'Message\' (/onCreateMessageRecipient/message/sentAt)", locations: nil, path: Optional([Amplify.JSONValue.string("onCreateMessageRecipient"), Amplify.JSONValue.string("message"), Amplify.JSONValue.string("sentAt")]), extensions: nil), Amplify.GraphQLError(message: "Cannot return null for non-nullable type: \'MessageStatus\' within parent \'Message\' (/onCreateMessageRecipient/message/status)", locations: nil, path: Optional([Amplify.JSONValue.string("onCreateMessageRecipient"), Amplify.JSONValue.string("message"), Amplify.JSONValue.string("status")]), extensions: nil), Amplify.GraphQLError(message: "Cannot return null for non-nullable type: \'Int\' within parent \'Message\' (/onCreateMessageRecipient/message/type)", locations: nil, path: Optional([Amplify.JSONValue.string("onCreateMessageRecipient"), Amplify.JSONValue.string("message"), Amplify.JSONValue.string("type")]), extensions: nil), Amplify.GraphQLError(message: "Cannot return null for non-nullable type: \'AWSDateTime\' within parent \'Message\' (/onCreateMessageRecipient/message/updatedAt)", locations: nil, path: Optional([Amplify.JSONValue.string("onCreateMessageRecipient"), Amplify.JSONValue.string("message"), Amplify.JSONValue.string("updatedAt")]), extensions: nil), Amplify.GraphQLError(message: "Cannot return null for non-nullable type: \'Int\' within parent \'Message\' (/onCreateMessageRecipient/message/_version)", locations: nil, path: Optional([Amplify.JSONValue.string("onCreateMessageRecipient"), Amplify.JSONValue.string("message"), Amplify.JSONValue.string("_version")]), extensions: nil), Amplify.GraphQLError(message: "Cannot return null for non-nullable type: \'AWSTimestamp\' within parent \'Message\' (/onCreateMessageRecipient/message/_lastChangedAt)", locations: nil, path: Optional([Amplify.JSONValue.string("onCreateMessageRecipient"), Amplify.JSONValue.string("message"), Amplify.JSONValue.string("_lastChangedAt")]), extensions: nil)]
```
Is this a regression?
No
Regression additional context
No response
Platforms
iOS
OS Version
16.4
Device
Simulator
Specific to simulators
No response
Additional context
No response
@swaroop-at-dockare Thanks for opening the issue. We will investigate and get back to you soon.
Thanks @phantumcode for replying. Can you tell me what we can do as we have a release in next week. I have a workaround but I want to keep it as my last option.
@swaroop-at-dockare Unfortunately we are not able to provide a timeline but rest assured our team is actively prioritizing and working on issues. We will provide an update as soon as we have one on the issue.
Hello,
Currently amplify-swift only supports static group authorization. https://docs.amplify.aws/swift/build-a-backend/graphqlapi/customize-authorization-rules/#user-group-based-data-access
However, when I tried to reproduce the issue in the following versions with the below schema, the subscription was received successfully.
Amplify Android 2.14.10
Amplify Swift 2.26.1
Amplify CLI 12.10.1
// schema without dynamic group auth
type Message @model @auth(rules: [{ allow: public }]) {
id: ID!
type: Int!
content: String!
senderId: Int!
senderName: String!
groupId: String! @index(name:"byGroup", sortKeyFields: ["sentAt"])
groupName: String!
status: MessageStatus!
role: String
tag: String
comment: String
sentAt: AWSDateTime!
orgId: Int! @index(name:"byOrgId")
recipients: [MessageRecipient] @hasMany(indexName: "byMessage", fields: ["id"])
permittedTo: [String!]
}
type MessageRecipient @model @auth(rules: [{ allow: public }]) {
id: ID!
messageId: ID @index(name:"byMessage")
groupId: String! @index(name: "byGroup")
message: Message @belongsTo(fields: ["messageId"])
userId: Int! @index(name:"byUser")
status: MessageStatus!
sentAt: AWSDateTime!
readAt: AWSDateTime
permittedTo: [String!]
}
enum MessageStatus {
OFFLINE
SENT
DELIVERED
READ
}
type Subscription {
onUpdateMessageStatus(status: String!): Message @aws_subscribe(mutations: ["updateMessage"])
}
Steps followed:
- On Swift, set up subscription on the
MessageRecipientmodel - On Android, create a
MessageRecipientmodel with a nullMessageand callDatastore.save()
Here are the code snippets I used:
/// Android
// Create a MessageRecipient without Message object
val messageRecipient = MessageRecipient.builder()
.groupId("groupID")
.userId(1)
.status(MessageStatus.READ)
.sentAt(Temporal.DateTime(java.util.Date(), 0))
.build()
Amplify.DataStore.save(messageRecipient,
{ Log.i("GH3212", "Saved a messageRecipient") },
{ Log.e("GH3212", "Save failed", it) }
/// Swift
let recipientSubscription = Amplify.DataStore.observe(MessageRecipient.self)
do {
for try await changes in recipientSubscription {
// handle incoming changes
print("[GH3212App]Subscription received mutation: \(changes)")
}
} catch {
print("[GH3212App]Subscription received error: \(error)")
}
Observed logs on Swift platform:
/// Websocket received the payload with "message":"null"
[StarscreamAdapter] websocketDidReceiveMessage: - {"id":"EE861044-ADCB-40B8-ACE3-201FEB5431A0","type":"data","payload":{"data":{"onCreateMessageRecipient":{"id":"960bc5cd-07cf-4959-987a-9bc27e4119b9","createdAt":"2024-02-12T21:35:05.197Z","groupId":"groupID","permittedTo":null,"readAt":null,"sentAt":"2024-02-12T21:34:59.777Z","status":"READ","updatedAt":"2024-02-12T21:35:05.197Z","userId":1,"message":null,"__typename":"MessageRecipient","_version":1,"_deleted":null,"_lastChangedAt":1707773705221}}}}
// model reconciliation is successful
onCreateValueListener: data(Swift.Result<AWSPluginsCore.MutationSync<AWSPluginsCore.AnyModel>, Amplify.GraphQLResponseError<AWSPluginsCore.MutationSync<AWSPluginsCore.AnyModel>>>.success(AWSPluginsCore.MutationSync<AWSPluginsCore.AnyModel>(model: AWSPluginsCore.AnyModel(id: "960bc5cd-07cf-4959-987a-9bc27e4119b9", instance: GH3212.MessageRecipient(id: "960bc5cd-07cf-4959-987a-9bc27e4119b9", groupId: "groupID", message: nil, userId: 1, status: GH3212.MessageStatus.read, sentAt: Amplify.Temporal.DateTime(foundationDate: 2024-02-12 21:34:59 +0000, timeZone: Optional(GMT)), readAt: nil, permittedTo: nil, createdAt: Optional(Amplify.Temporal.DateTime(foundationDate: 2024-02-12 21:35:05 +0000, timeZone: Optional(GMT))), updatedAt: Optional(Amplify.Temporal.DateTime(foundationDate: 2024-02-12 21:35:05 +0000, timeZone: Optional(GMT)))), modelName: "MessageRecipient"), syncMetadata: AWSPluginsCore.MutationSyncMetadata(id: "MessageRecipient|960bc5cd-07cf-4959-987a-9bc27e4119b9", deleted: false, lastChangedAt: 1707773705221, version: 1))))
receive(_:): data(Swift.Result<AWSPluginsCore.MutationSync<AWSPluginsCore.AnyModel>, Amplify.GraphQLResponseError<AWSPluginsCore.MutationSync<AWSPluginsCore.AnyModel>>>.success(AWSPluginsCore.MutationSync<AWSPluginsCore.AnyModel>(model: AWSPluginsCore.AnyModel(id: "960bc5cd-07cf-4959-987a-9bc27e4119b9", instance: GH3212.MessageRecipient(id: "960bc5cd-07cf-4959-987a-9bc27e4119b9", groupId: "groupID", message: nil, userId: 1, status: GH3212.MessageStatus.read, sentAt: Amplify.Temporal.DateTime(foundationDate: 2024-02-12 21:34:59 +0000, timeZone: Optional(GMT)), readAt: nil, permittedTo: nil, createdAt: Optional(Amplify.Temporal.DateTime(foundationDate: 2024-02-12 21:35:05 +0000, timeZone: Optional(GMT))), updatedAt: Optional(Amplify.Temporal.DateTime(foundationDate: 2024-02-12 21:35:05 +0000, timeZone: Optional(GMT)))), modelName: "MessageRecipient"), syncMetadata: AWSPluginsCore.MutationSyncMetadata(id: "MessageRecipient|960bc5cd-07cf-4959-987a-9bc27e4119b9", deleted: false, lastChangedAt: 1707773705221, version: 1))))
dispose(of subscriptionEvent): data(Swift.Result<AWSPluginsCore.MutationSync<AWSPluginsCore.AnyModel>, Amplify.GraphQLResponseError<AWSPluginsCore.MutationSync<AWSPluginsCore.AnyModel>>>.success(AWSPluginsCore.MutationSync<AWSPluginsCore.AnyModel>(model: AWSPluginsCore.AnyModel(id: "960bc5cd-07cf-4959-987a-9bc27e4119b9", instance: GH3212.MessageRecipient(id: "960bc5cd-07cf-4959-987a-9bc27e4119b9", groupId: "groupID", message: nil, userId: 1, status: GH3212.MessageStatus.read, sentAt: Amplify.Temporal.DateTime(foundationDate: 2024-02-12 21:34:59 +0000, timeZone: Optional(GMT)), readAt: nil, permittedTo: nil, createdAt: Optional(Amplify.Temporal.DateTime(foundationDate: 2024-02-12 21:35:05 +0000, timeZone: Optional(GMT))), updatedAt: Optional(Amplify.Temporal.DateTime(foundationDate: 2024-02-12 21:35:05 +0000, timeZone: Optional(GMT)))), modelName: "MessageRecipient"), syncMetadata: AWSPluginsCore.MutationSyncMetadata(id: "MessageRecipient|960bc5cd-07cf-4959-987a-9bc27e4119b9", deleted: false, lastChangedAt: 1707773705221, version: 1))))
dispose(of graphQLResponse): success(AWSPluginsCore.MutationSync<AWSPluginsCore.AnyModel>(model: AWSPluginsCore.AnyModel(id: "960bc5cd-07cf-4959-987a-9bc27e4119b9", instance: GH3212.MessageRecipient(id: "960bc5cd-07cf-4959-987a-9bc27e4119b9", groupId: "groupID", message: nil, userId: 1, status: GH3212.MessageStatus.read, sentAt: Amplify.Temporal.DateTime(foundationDate: 2024-02-12 21:34:59 +0000, timeZone: Optional(GMT)), readAt: nil, permittedTo: nil, createdAt: Optional(Amplify.Temporal.DateTime(foundationDate: 2024-02-12 21:35:05 +0000, timeZone: Optional(GMT))), updatedAt: Optional(Amplify.Temporal.DateTime(foundationDate: 2024-02-12 21:35:05 +0000, timeZone: Optional(GMT)))), modelName: "MessageRecipient"), syncMetadata: AWSPluginsCore.MutationSyncMetadata(id: "MessageRecipient|960bc5cd-07cf-4959-987a-9bc27e4119b9", deleted: false, lastChangedAt: 1707773705221, version: 1)))
New state: waiting
respond(to:): waiting
main()
Notifying: started([AWSPluginsCore.MutationSync<AWSPluginsCore.AnyModel>(model: AWSPluginsCore.AnyModel(id: "960bc5cd-07cf-4959-987a-9bc27e4119b9", instance: GH3212.MessageRecipient(id: "960bc5cd-07cf-4959-987a-9bc27e4119b9", groupId: "groupID", message: nil, userId: 1, status: GH3212.MessageStatus.read, sentAt: Amplify.Temporal.DateTime(foundationDate: 2024-02-12 21:34:59 +0000, timeZone: Optional(GMT)), readAt: nil, permittedTo: nil, createdAt: Optional(Amplify.Temporal.DateTime(foundationDate: 2024-02-12 21:35:05 +0000, timeZone: Optional(GMT))), updatedAt: Optional(Amplify.Temporal.DateTime(foundationDate: 2024-02-12 21:35:05 +0000, timeZone: Optional(GMT)))), modelName: "MessageRecipient"), syncMetadata: AWSPluginsCore.MutationSyncMetadata(id: "MessageRecipient|960bc5cd-07cf-4959-987a-9bc27e4119b9", deleted: false, lastChangedAt: 1707773705221, version: 1))])
resolve(waiting, started([AWSPluginsCore.MutationSync<AWSPluginsCore.AnyModel>(model: AWSPluginsCore.AnyModel(id: "960bc5cd-07cf-4959-987a-9bc27e4119b9", instance: GH3212.MessageRecipient(id: "960bc5cd-07cf-4959-987a-9bc27e4119b9", groupId: "groupID", message: nil, userId: 1, status: GH3212.MessageStatus.read, sentAt: Amplify.Temporal.DateTime(foundationDate: 2024-02-12 21:34:59 +0000, timeZone: Optional(GMT)), readAt: nil, permittedTo: nil, createdAt: Optional(Amplify.Temporal.DateTime(foundationDate: 2024-02-12 21:35:05 +0000, timeZone: Optional(GMT))), updatedAt: Optional(Amplify.Temporal.DateTime(foundationDate: 2024-02-12 21:35:05 +0000, timeZone: Optional(GMT)))), modelName: "MessageRecipient"), syncMetadata: AWSPluginsCore.MutationSyncMetadata(id: "MessageRecipient|960bc5cd-07cf-4959-987a-9bc27e4119b9", deleted: false, lastChangedAt: 1707773705221, version: 1))])) -> reconciling([AWSPluginsCore.MutationSync<AWSPluginsCore.AnyModel>(model: AWSPluginsCore.AnyModel(id: "960bc5cd-07cf-4959-987a-9bc27e4119b9", instance: GH3212.MessageRecipient(id: "960bc5cd-07cf-4959-987a-9bc27e4119b9", groupId: "groupID", message: nil, userId: 1, status: GH3212.MessageStatus.read, sentAt: Amplify.Temporal.DateTime(foundationDate: 2024-02-12 21:34:59 +0000, timeZone: Optional(GMT)), readAt: nil, permittedTo: nil, createdAt: Optional(Amplify.Temporal.DateTime(foundationDate: 2024-02-12 21:35:05 +0000, timeZone: Optional(GMT))), updatedAt: Optional(Amplify.Temporal.DateTime(foundationDate: 2024-02-12 21:35:05 +0000, timeZone: Optional(GMT)))), modelName: "MessageRecipient"), syncMetadata: AWSPluginsCore.MutationSyncMetadata(id: "MessageRecipient|960bc5cd-07cf-4959-987a-9bc27e4119b9", deleted: false, lastChangedAt: 1707773705221, version: 1))])
New state: reconciling([AWSPluginsCore.MutationSync<AWSPluginsCore.AnyModel>(model: AWSPluginsCore.AnyModel(id: "960bc5cd-07cf-4959-987a-9bc27e4119b9", instance: GH3212.MessageRecipient(id: "960bc5cd-07cf-4959-987a-9bc27e4119b9", groupId: "groupID", message: nil, userId: 1, status: GH3212.MessageStatus.read, sentAt: Amplify.Temporal.DateTime(foundationDate: 2024-02-12 21:34:59 +0000, timeZone: Optional(GMT)), readAt: nil, permittedTo: nil, createdAt: Optional(Amplify.Temporal.DateTime(foundationDate: 2024-02-12 21:35:05 +0000, timeZone: Optional(GMT))), updatedAt: Optional(Amplify.Temporal.DateTime(foundationDate: 2024-02-12 21:35:05 +0000, timeZone: Optional(GMT)))), modelName: "MessageRecipient"), syncMetadata: AWSPluginsCore.MutationSyncMetadata(id: "MessageRecipient|960bc5cd-07cf-4959-987a-9bc27e4119b9", deleted: false, lastChangedAt: 1707773705221, version: 1))])
respond(to:): reconciling([AWSPluginsCore.MutationSync<AWSPluginsCore.AnyModel>(model: AWSPluginsCore.AnyModel(id: "960bc5cd-07cf-4959-987a-9bc27e4119b9", instance: GH3212.MessageRecipient(id: "960bc5cd-07cf-4959-987a-9bc27e4119b9", groupId: "groupID", message: nil, userId: 1, status: GH3212.MessageStatus.read, sentAt: Amplify.Temporal.DateTime(foundationDate: 2024-02-12 21:34:59 +0000, timeZone: Optional(GMT)), readAt: nil, permittedTo: nil, createdAt: Optional(Amplify.Temporal.DateTime(foundationDate: 2024-02-12 21:35:05 +0000, timeZone: Optional(GMT))), updatedAt: Optional(Amplify.Temporal.DateTime(foundationDate: 2024-02-12 21:35:05 +0000, timeZone: Optional(GMT)))), modelName: "MessageRecipient"), syncMetadata: AWSPluginsCore.MutationSyncMetadata(id: "MessageRecipient|960bc5cd-07cf-4959-987a-9bc27e4119b9", deleted: false, lastChangedAt: 1707773705221, version: 1))])
Notifying: reconciled
resolve(reconciling([AWSPluginsCore.MutationSync<AWSPluginsCore.AnyModel>(model: AWSPluginsCore.AnyModel(id: "960bc5cd-07cf-4959-987a-9bc27e4119b9", instance: GH3212.MessageRecipient(id: "960bc5cd-07cf-4959-987a-9bc27e4119b9", groupId: "groupID", message: nil, userId: 1, status: GH3212.MessageStatus.read, sentAt: Amplify.Temporal.DateTime(foundationDate: 2024-02-12 21:34:59 +0000, timeZone: Optional(GMT)), readAt: nil, permittedTo: nil, createdAt: Optional(Amplify.Temporal.DateTime(foundationDate: 2024-02-12 21:35:05 +0000, timeZone: Optional(GMT))), updatedAt: Optional(Amplify.Temporal.DateTime(foundationDate: 2024-02-12 21:35:05 +0000, timeZone: Optional(GMT)))), modelName: "MessageRecipient"), syncMetadata: AWSPluginsCore.MutationSyncMetadata(id: "MessageRecipient|960bc5cd-07cf-4959-987a-9bc27e4119b9", deleted: false, lastChangedAt: 1707773705221, version: 1))]), reconciled) -> finished
New state: finished
respond(to:): finished
/// Received subscription
[GH3212App]Subscription received mutation: MutationEvent(id: "2E9F6A79-B6BF-4C07-A7FB-82501CFB0A26", modelId: "960bc5cd-07cf-4959-987a-9bc27e4119b9", modelName: "MessageRecipient", json: "{\"createdAt\":\"2024-02-12T21:35:05.197Z\",\"groupId\":\"groupID\",\"id\":\"960bc5cd-07cf-4959-987a-9bc27e4119b9\",\"sentAt\":\"2024-02-12T21:34:59.777Z\",\"status\":\"READ\",\"updatedAt\":\"2024-02-12T21:35:05.197Z\",\"userId\":1}", mutationType: "create", createdAt: Amplify.Temporal.DateTime(foundationDate: 2024-02-12 21:35:05 +0000, timeZone: Optional(GMT)), version: Optional(1), inProcess: false, graphQLFilterJSON: nil)
Could you please update to the latest release to check if your issue still exists?