aws-amplify / amplify-swift

A declarative library for application development using cloud services.
Apache License 2.0
455 stars 196 forks source link

query not supported through the realtime channel #3885

Closed ChurikiTenna closed 1 month ago

ChurikiTenna commented 1 month ago

Describe the bug

This is not a bug. I think I am misusing the Amplify.API.subscribe.

I want to get UserData with specific id when it's updated.

I tried to use Amplify.API.subscribe like this: let subscription = Amplify.API.subscribe(request: .list(UserData.self, where: UserData.keys.id.eq(userId)))

But it fails with error:

[RetryWithJitter] operation failed with error unknown(message: Optional("query not supported through the realtime channel"), causedBy: nil, payload: Optional(["errorType": Amplify.JSONValue.string("UnsupportedOperation"), "message": Amplify.JSONValue.string("query not supported through the realtime channel")])), retrying(0)
Subscription has terminated with APIError: Subscription item event failed with error
Caused by:
GraphQLResponseError<List<UserData>>: GraphQL service returned a successful response containing errors: [Amplify.GraphQLError(message: "query not supported through the realtime channel", locations: nil, path: nil, extensions: Optional(["errorType": Amplify.JSONValue.string("UnsupportedOperation")]))]
Recovery suggestion: The list of `GraphQLError` contains service-specific messages

Do I need to define something first, or is such functionality not available?

Steps To Reproduce

Steps to reproduce the behavior:
1. Define UserData
2. Call let subscription = Amplify.API.subscribe(request: .list(UserData.self, where: UserData.keys.id.eq(userId)))

Expected behavior

Get UserData.self with specific id when updated.

Amplify Framework Version

2.39.0

Amplify Categories

API

Dependency manager

Swift PM

Swift version

latest

CLI version

12.12.6

Xcode version

15.4

Relevant log output

<details>
<summary>Log Messages</summary>

[AppSyncRealTimeClient] Received subscription request id: 6DA0BD85-DF95-467D-BBB7-649D0FA7D24B, query: {"query":"query ListUserData($filter: ModelUserDataFilterInput, $limit: Int) {\n  listUserData(filter: $filter, limit: $limit) {\n    items {\n      id\n      createdAt\n      description\n      icon_path\n      iris_id\n      sns_urls\n      updatedAt\n      user_name\n      user_type\n      __typename\n    }\n    nextToken\n  }\n}","variables":{"filter":{"id":{"eq":"87148a68-e0b1-70c7-2d08-3c38b063c771"}},"limit":1000}}
[AppSyncRealTimeClient] client start connecting
[WebSocketClient] WebSocket about to connect
[WebSocketClient] Creating new connection and starting read
[WebSocketClient] WebSocket write message string: {"type":"connection_init"}
[WebSocketClient] Websocket connected
[AppSyncRealTimeClient] Received websocket event connected
[AppSyncRealTimeClient] WebSocket connected
[WebSocketClient] WebSocket received message: string("{\"type\":\"connection_ack\",\"payload\":{\"connectionTimeoutMs\":300000}}")
[AppSyncRealTimeClient] Received websocket event string("{\"type\":\"connection_ack\",\"payload\":{\"connectionTimeoutMs\":300000}}")
[WebSocketClient] WebSocket received message: string("{\"type\":\"ka\"}")
[AppSyncRealTimeClient] AppSync connected: Optional(Amplify.JSONValue.object(["connectionTimeoutMs": Amplify.JSONValue.number(300000.0)]))
[AppSyncRealTimeClient] Resuming existing subscriptions
[AppSyncRealTimeClient] Starting heart beat monitor with interval 300000 ms
[AppSyncRealTimeClient] Received websocket event string("{\"type\":\"ka\"}")
[AppSyncRealTimeSubscription-6DA0BD85-DF95-467D-BBB7-649D0FA7D24B] Start subscribing
[AppSyncRealTimeSubscription-6DA0BD85-DF95-467D-BBB7-649D0FA7D24B] Subscription already in subscribing state
[WebSocketClient] WebSocket write message string: {"type":"start","id":"6DA0BD85-DF95-467D-BBB7-649D0FA7D24B","payload":{"extensions":{"authorization":{"host":"qubc7kconrb7bhal3qew7q6fme.appsync-api.ap-northeast-1.amazonaws.com","x-api-key":"da2-udinosqqbbb3pgy4hxqh42y3dy","x-amz-date":"20240924T121614Z"}},"data":"{\"query\":\"query ListUserData($filter: ModelUserDataFilterInput, $limit: Int) {\\n  listUserData(filter: $filter, limit: $limit) {\\n    items {\\n      id\\n      createdAt\\n      description\\n      icon_path\\n      iris_id\\n      sns_urls\\n      updatedAt\\n      user_name\\n      user_type\\n      __typename\\n    }\\n    nextToken\\n  }\\n}\",\"variables\":{\"filter\":{\"id\":{\"eq\":\"87148a68-e0b1-70c7-2d08-3c38b063c771\"}},\"limit\":1000}}"}}
Subscription connect state is connecting
[WebSocketClient] WebSocket received message: string("{\"id\":\"6DA0BD85-DF95-467D-BBB7-649D0FA7D24B\",\"type\":\"error\",\"payload\":{\"errors\":[{\"errorType\":\"UnsupportedOperation\",\"message\":\"query not supported through the realtime channel\"}]}}")
[AppSyncRealTimeClient] Received websocket event string("{\"id\":\"6DA0BD85-DF95-467D-BBB7-649D0FA7D24B\",\"type\":\"error\",\"payload\":{\"errors\":[{\"errorType\":\"UnsupportedOperation\",\"message\":\"query not supported through the realtime channel\"}]}}")
[AppSyncRealTimeClient] AppSync received response: AppSyncRealTimeResponse(id: Optional("6DA0BD85-DF95-467D-BBB7-649D0FA7D24B"), payload: Optional(Amplify.JSONValue.object(["errors": Amplify.JSONValue.array([Amplify.JSONValue.object(["message": Amplify.JSONValue.string("query not supported through the realtime channel"), "errorType": Amplify.JSONValue.string("UnsupportedOperation")])])])), type: AWSAPIPlugin.AppSyncRealTimeResponse.EventType.error)
[RetryWithJitter] operation failed with error unknown(message: Optional("query not supported through the realtime channel"), causedBy: nil, payload: Optional(["errorType": Amplify.JSONValue.string("UnsupportedOperation"), "message": Amplify.JSONValue.string("query not supported through the realtime channel")])), retrying(0)
Subscription has terminated with APIError: Subscription item event failed with error
Caused by:
GraphQLResponseError<List<UserData>>: GraphQL service returned a successful response containing errors: [Amplify.GraphQLError(message: "query not supported through the realtime channel", locations: nil, path: nil, extensions: Optional(["errorType": Amplify.JSONValue.string("UnsupportedOperation")]))]
Recovery suggestion: The list of `GraphQLError` contains service-specific messages
[AppSyncRealTimeSubscription-6DA0BD85-DF95-467D-BBB7-649D0FA7D24B] Failed to subscribe, error: unknown(message: Optional("query not supported through the realtime channel"), causedBy: nil, payload: Optional(["errorType": Amplify.JSONValue.string("UnsupportedOperation"), "message": Amplify.JSONValue.string("query not supported through the realtime channel")]))
[AppSyncRealTimeClient] Failed to resume existing subscription with id: (6DA0BD85-DF95-467D-BBB7-649D0FA7D24B)



### Is this a regression?

Yes

### Regression additional context

_No response_

### Platforms

iOS

### OS Version

iOS 18.1

### Device

iPhone 15

### Specific to simulators

_No response_

### Additional context

_No response_
phantumcode commented 1 month ago

@ChurikiTenna Thanks for submitting the issue, we'll take a look and provide updates here.

5d commented 1 month ago

Hi @ChurikiTenna ,

The subscribe API is designed to send the .subscription GraphQL request. However, we currently do not support adding conditions to subscription GraphQL requests. As an alternative, you can apply filtering on the onUpdate subscription of the model using a specific identifier. For example:

let onUpdateSubscription = Amplify.Publisher.create(Amplify.API.subscribe(
    request: .subscription(to: Post.self, subscriptionType: .onUpdate)
))
let cancellable = onUpdateSubscription.filter {
    if case let .data(.success(result)) = $0 {
        result.model.identifier == "specific-id"
    } else {
        false
    }
}.sink { complete in
    // on subscription end
} receiveValue: { onUpdateEvent in
    // your logic
}
ChurikiTenna commented 1 month ago

Hi @5d !

Thank you for the suggestion. Does that mean it listens all the Post.self updates? Isn't it going to require too much traffics when there are thousands of users?

5d commented 1 month ago

Hi @ChurikiTenna ,

Does that mean it listens all the Post.self updates? Isn't it going to require too much traffics when there are thousands of users?

It will listen to all authorized Post.self updates. If that's a concern, there's another approach you can try, building a custom GraphQL query. In your case, it might look something like this:

let updateOnSpecificPostIdRequest = GraphQLRequest(
    document: """
    subscription OnSpecificPostIdUpdated {
        onUpdatePost(filter: { id: { eq: "\(postId)" } }) {
            id
            otherFiled0
            otherFiled1
            ...
        }
    }
    """,
    responseType: JSONValue.self
)

let subscription = Amplify.API.subscribe(request: updateOnSpecificPostIdRequest)

We recommend thoroughly reviewing the AWS AppSync documentation before attempting to write your own custom queries.

ChurikiTenna commented 1 month ago

Thank you @5d ! I'll try the custom GraphQL query approach.

github-actions[bot] commented 1 month ago

This issue is now closed. Comments on closed issues are hard for our team to see. If you need more assistance, please open a new issue that references this one. If you wish to keep having a conversation with other community members under this issue feel free to do so.