awslabs / aws-mobile-appsync-sdk-js

JavaScript library files for Offline, Sync, Sigv4. includes support for React Native
Apache License 2.0
921 stars 266 forks source link

Subscription confirmation #524

Open LanderN opened 4 years ago

LanderN commented 4 years ago

Do you want to request a feature or report a bug? feature

What is the current behavior? client.subscribe(query: ...) call does not provide a way to wait for the start_ack message, there is no way of knowing when the subscription is actually active.

My use case is this: Client A can initiate an operation that might cause Client B to perform certain mutations asynchronously on an AppSync API. Client A would like to get notified of these mutations, thus a subscription is created before initiating the connection.

Order of operations is thus:

  1. client a subscribes to client b events (through subscription)
  2. client a initiates operation
  3. client b performs mutations
  4. client a receives events from graphql subscription

Because there is no way of knowing when the subscription in 1. is active, client b might have performed the mutation before client a's subscription was active. As a temporary workaround, I have added a sleep before initiating the operation, which is, of course, not ideal.

Is there a way to receive AppSync's start_ack message as a user?

sisir-hellosivi commented 1 year ago

Any updates on this?

sis-dk commented 1 year ago

Hope this helps

/**
 * Assumptions that the example code makes:
 * 1. You are using the Apollo client v3 way of setup
 * 2. You are calling the apolloClient.subscribe way of subscription instead of React components way
 * 3. aws-appsync-subscription-link version - 3.1.2
 */

import { createSubscriptionHandshakeLink } from 'aws-appsync-subscription-link'
// Your remaining imports

const link = ApolloLink.from([
  setContext(() => {
    return {
      controlMessages: {
        '@@controlEvents': true //This enables aws-appsync-subscription-link to send the control messages such as start_ack 
      },
    }
  }),
  createSubscriptionHandshakeLink(/* params here */),
  // Your other links here
])

const client = new ApolloClient({
  link
})

async function subscribe(query, variables) {
  const handler = await client.subscribe({
    query,
    variables
  });

  return new Promise(resolve => {
    handler.subscribe({
      next: async (event) => {
        if(event.extensions.controlMsgType == 'CONNECTED') {
          //This means start_ack signal has come
          resolve(true)
          return true
        }
        //Handle other events here
      },
      error: (error) => {
        // Resolve with error here so client can handle the error
        resolve(false)
      },
    })
  })  
}

const subscriptionSuccessful = await subscribe('Your subscription query here', { 'You variables': 'here' })
if(subscriptionSuccessful) {
  //Continue with your code
}

/**
 * Note:
 * 1. I've not handled all the cases. Please go through the references to handle the connection error cases also to avoid the Promise being held up forever.
 *    You could also implement a timeout
 * 
 * References:
 * 1. Code that handles sending this event: https://github.com/awslabs/aws-mobile-appsync-sdk-js/blob/8aa3884309720be9b01edf6042f36ae5ff4368e7/packages/aws-appsync-subscription-link/src/realtime-subscription-handshake-link.ts#L169
 * 2. Code that takes the context we are passing that enables above condition: https://github.com/awslabs/aws-mobile-appsync-sdk-js/blob/8aa3884309720be9b01edf6042f36ae5ff4368e7/packages/aws-appsync-subscription-link/src/realtime-subscription-handshake-link.ts#L104
 * 3. Code where this event (start_ack) comes: https://github.com/awslabs/aws-mobile-appsync-sdk-js/blob/8aa3884309720be9b01edf6042f36ae5ff4368e7/packages/aws-appsync-subscription-link/src/realtime-subscription-handshake-link.ts#L706
 * 4. Doc that explains subscription flow: https://docs.aws.amazon.com/appsync/latest/devguide/real-time-websocket-client.html
 */