Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

AsyncIterator.return() #130

Open
timoshisa opened this issue Jan 17, 2021 · 4 comments
Open

AsyncIterator.return() #130

timoshisa opened this issue Jan 17, 2021 · 4 comments
Labels
question Further information is requested

Comments

@timoshisa
Copy link

How do I get AsyncIterator.return() to get called onDisconnect and on unsubscribe? I'm looking through the code and it seems this will get bypassed whenever the web socket is disconnected or a subscription is unsubscribed...

I'm trying to publish a presence event to a room topic whenever a user goes offline, using this code I found on google:

const withCancel = (asyncIteratorFn, onCancel) => {
  return async (rootValue, args, context, info) => {
    const asyncIterator = await asyncIteratorFn(rootValue, args, context, info);
    const asyncIteratorReturn = asyncIterator.return;

    asyncIterator.return = () => {
      onCancel();
      return asyncIteratorReturn
        ? asyncIteratorReturn.call(asyncIterator)
        : Promise.resolve({ value: undefined, done: true });
    };

    return asyncIterator;
  };
};

But asyncIterator.return is never called, and so it doesn't invoke my onCancel event to publish.

Would be willing to submit a PR if you can point me in the right direction.

@michalkvasnicak
Copy link
Owner

The async iterator is available only on subscribe because lambda is stateless so if you want to handle presence you should use onDisconnect event handler (see subscriptions.onDisconnect and subscriptions.onOperationComplete (unsubscribe), https://github.com/michalkvasnicak/aws-lambda-graphql/tree/master/packages/aws-lambda-graphql#options)

@michalkvasnicak michalkvasnicak added the question Further information is requested label Jan 18, 2021
@timoshisa
Copy link
Author

I managed to get this to work actually... even though lambda is stateless it's possible to call the return handler when a client disconnects by iterating through the subscribers returned from unsubscribeAllByConnectionId. Do you want me to submit a PR for your review?

@michalkvasnicak
Copy link
Owner

@tbehrsin that'd be nice! Thank you.

@timoshisa
Copy link
Author

By returning subscribers iterated from unsubscribeAllByConnectionId and then in turn connectionManager.unregisterConnection:

          case '$disconnect': {
            const { onDisconnect } = this.subscriptionOptions || {};
            // this event is called eventually by AWS APIGateway v2
            // we actualy don't care about a result of this operation because client is already
            // disconnected, it is meant only for clean up purposes
            // hydrate connection
            const connection = await this.connectionManager.hydrateConnection(
              event.requestContext.connectionId,
            );

            if (onDisconnect) {
              onDisconnect(connection);
            }

            const subscribers = await this.connectionManager.unregisterConnection(
              connection,
            );

            const promises = subscribers.map(async (subscriber) => {
              const pubSub = new PubSub();

              const options = await this.createGraphQLServerOptions(
                event,
                lambdaContext,
                {
                  connection,
                  operation: subscriber.operation,
                  pubSub,
                },
              );

              const iterable = await execute(
                Object.assign(Object.assign({}, options), {
                  connection,
                  connectionManager: this.connectionManager,
                  event,
                  lambdaContext,
                  operation: subscriber.operation,
                  pubSub,
                  registerSubscriptions: false,
                  subscriptionManager: this.subscriptionManager,
                }),
              );

              if (!isAsyncIterable(iterable)) {
                return;
              }

              const iterator = getAsyncIterator(iterable);
              await iterator.return?.();
            });
            await Promise.all(promises);

            return {
              body: '',
              statusCode: 200,
            };
          }

I'll submit a PR.

timoshisa pushed a commit to tutorflow/aws-lambda-graphql that referenced this issue Jan 18, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
question Further information is requested
Projects
None yet
Development

No branches or pull requests

2 participants