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

Add subscription support for testing client & improved typings #3296

Closed
wants to merge 2 commits into from

Conversation

Pruxis
Copy link
Contributor

@Pruxis Pruxis commented Sep 16, 2019

  • Improved typescript types to no longer have overlapping types.
    -- Added generics support for variables.
  • Added subscription support.
    -- Promisify an async iterator or reject the errors.

@Pruxis
Copy link
Contributor Author

Pruxis commented Sep 16, 2019

I'll fix the tests after work 🤦‍♂

@Lolobstant
Copy link

Would you need some help with this feature ?

@Pruxis
Copy link
Contributor Author

Pruxis commented Oct 25, 2019

I've made our own package as PRs take far too long to get accepted or even reviewed.
I'll fix the tests whenever I feel like it, unless you'd want to ofcourse :)

@aznguy
Copy link

aznguy commented Nov 6, 2019

I'm very interested in this pull request. Is it possible to give this pull request a bit more priority?

@abernix abernix closed this Jun 24, 2020
@abernix abernix reopened this Jun 25, 2020
@abernix abernix changed the base branch from master to main June 25, 2020 09:04
@mariasoul
Copy link

Hey peeps!

I am also very interested in this PR! It will be also nice to have, for other people around, based on this issue

What is the status of it? Would it be okay for you if I push a commit with some tests?
cc @Pruxis

@glasser
Copy link
Member

glasser commented Mar 19, 2021

We're currently planning to remove the superficial integration of the unmaintained subscriptions-transport-ws package into Apollo Server when we finish our work on AS3. (We will provide documentation on how to use that package yourself, or perhaps suggest the more actively maintained graphql-ws.) I hope at some point in the future we are able to incorporate subscriptions more deeply into the entire server-side Apollo platform, but our current thin wrapper doesn't help much. (Additionally, we're considering removing apollo-server-testing as a standalone package in AS3 as well (#4952), as it's really just a thin wrapper around ApolloServer.executeOperation; rather than need to install a new package and ensure it's at the right version, we should just document and maybe slightly improve executeOperation.)

@glasser glasser closed this Mar 19, 2021
@mariasoulblack
Copy link

@glasser makes sense! I follow up with the above PR!

Thanks for the quick response! 🙂

@Pruxis
Copy link
Contributor Author

Pruxis commented Mar 20, 2021

For those wondering, this is what I've been using for the past year or two to test my GraphQL including subscriptions.

type StringOrAst = string | DocumentNode;
interface Query<T = any> {
  query: StringOrAst;
  variables?: T;
  user?: UserGetPayload<{}>;
}

interface Mutation<T = any> {
  mutation: StringOrAst;
  variables?: T;
  user?: UserGetPayload<{}>;
}

interface Subscription<T = any> {
  subscription: StringOrAst;
  variables?: T;
  user?: UserGetPayload<{}>;
}

export interface ApolloServerClient {
  query: <TData = any, TVariables = any>(query: Query<TVariables>) => Promise<ApolloCurrentQueryResult<TData>>;
  mutate: <TData = any, TVariables = any>(
    mutation: Mutation<TVariables>
  ) => Promise<Omit<ApolloCurrentResult<TData>, 'data'> & { data: TData }>;
  subscription: <TData = any, TVariables = any>(
    subscription: Subscription<TVariables>
  ) => Promise<(params?: { maxWait?: number }) => Promise<TData>>;
}

const isAsyncIterable = (iterator: any): iterator is AsyncIterableIterator<any> => {
  return typeof iterator[Symbol.asyncIterator] === 'function';
};

const sleep = (ms: number) => {
  if (ms <= 0) return false;
  return new Promise(resolve => {
    setTimeout(resolve, ms);
  });
};

const waitForSubscription = async (server: ApolloServerBase, subscription: DocumentNode, variables?: any) => {
  // @ts-ignore
  const iterator = await subscribe(server.schema, subscription, {}, server.context, variables);
  await sleep(500); // Else subscription doesn't apply
  return (params?: { maxWait: number }) =>
    new Promise<any | ReadonlyArray<GraphQLError>>(async (resolve, reject) => {
      let asyncIteratorDone = false;
      params &&
        params.maxWait &&
        setTimeout(() => {
          if (!asyncIteratorDone) resolve(undefined);
        }, params.maxWait);
      if (isAsyncIterable(iterator)) {
        for await (const next of iterator) {
          asyncIteratorDone = true;
          const val: ExecutionResult = next;
          if (val.errors) reject(val.errors);
          const result = val.data;
          if (result) resolve(result);
        }
      }
      if ('errors' in iterator) {
        reject(iterator.errors);
      }
    });
};

export function createGraphQLClient(server: ApolloServerBase): ApolloServerClient {
  const executeOperation = server.executeOperation.bind(server);
  const handler = async ({ query, mutation, subscription, user, ...args }: Query & Mutation & Subscription) => {
    if (subscription) return waitForSubscription(server, subscription as DocumentNode, args.variables);
    const operation = query || mutation;
    if (!operation) throw new Error('Either `query` or `mutation`must be passed.');
    return executeOperation({
      query: typeof operation === 'string' ? operation : print(operation),
      ...args,
    });
  };
  return { query: handler, mutate: handler, subscription: handler };
}

@github-actions github-actions bot locked as resolved and limited conversation to collaborators Apr 21, 2023
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

7 participants