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
Conversation
I'll fix the tests after work 🤦♂ |
Would you need some help with this feature ? |
I've made our own package as PRs take far too long to get accepted or even reviewed. |
I'm very interested in this pull request. Is it possible to give this pull request a bit more priority? |
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? |
We're currently planning to remove the superficial integration of the unmaintained |
@glasser makes sense! I follow up with the above PR! Thanks for the quick response! 🙂 |
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 };
} |
-- Added generics support for variables.
-- Promisify an async iterator or reject the errors.