UNPKG

msw

Version:

The industry standard for API mocking in JavaScript.

180 lines (168 loc) • 5.56 kB
import type { ResponseResolver, RequestHandlerOptions, } from '#core/handlers/request-handler' import { GraphQLHandler, type GraphQLVariables, type GraphQLOperationType, type GraphQLResolverExtras, type GraphQLResponseBody, type GraphQLQuery, type GraphQLPredicate, } from './graphql-handler' import { createRequestId } from '@mswjs/interceptors' import { getAllRequestCookies } from '#core/utils/request/get-request-cookies' import type { Path } from '#core/utils/matching/match-request-url' import { attachSiblingHandlers } from '#core/utils/internal/attach-sibling-handlers' import { createGraphQLSubscriptionHandler, type GraphQLSubscriptionHandlerFactory, } from './graphql-subscription' export type GraphQLRequestHandler = < Query extends GraphQLQuery = GraphQLQuery, Variables extends GraphQLVariables = GraphQLVariables, >( predicate: GraphQLPredicate<Query, Variables>, resolver: GraphQLResponseResolver< [Query] extends [never] ? GraphQLQuery : Query, Variables >, options?: RequestHandlerOptions, ) => GraphQLHandler export type GraphQLOperationHandler = < Query extends GraphQLQuery = GraphQLQuery, Variables extends GraphQLVariables = GraphQLVariables, >( resolver: GraphQLResponseResolver< [Query] extends [never] ? GraphQLQuery : Query, Variables >, options?: RequestHandlerOptions, ) => GraphQLHandler export type GraphQLResponseResolver< Query extends GraphQLQuery = GraphQLQuery, Variables extends GraphQLVariables = GraphQLVariables, > = ResponseResolver< GraphQLResolverExtras<Variables>, null, GraphQLResponseBody<[Query] extends [never] ? GraphQLQuery : Query> > function createScopedGraphQLHandler( operationType: GraphQLOperationType, url: Path, ): GraphQLRequestHandler { return (predicate, resolver, options = {}) => { return new GraphQLHandler(operationType, predicate, url, resolver, options) } } function createGraphQLOperationHandler( url: Path, subscriptionFactory?: GraphQLSubscriptionHandlerFactory, ): GraphQLOperationHandler { /** * @note An explicitly generic function so the subscription sibling * can be created with the same `Query`/`Variables` types as the * operation resolver, without casting its resolver info. */ return < Query extends GraphQLQuery = GraphQLQuery, Variables extends GraphQLVariables = GraphQLVariables, >( resolver: GraphQLResponseResolver< [Query] extends [never] ? GraphQLQuery : Query, Variables >, options?: RequestHandlerOptions, ): GraphQLHandler => { const handler = new GraphQLHandler( 'all', new RegExp('.*'), url, resolver, options, ) if (!subscriptionFactory) { return handler } // Attach a catch-all subscription handler as a sibling so the // operation handler also matches GraphQL subscriptions over WebSocket. // The subscription transport guarantees only actual GraphQL // subscriptions are dispatched to it. const subscriptionCatchAllHandler = subscriptionFactory<Query, Variables>( new RegExp('.*'), ({ operationName, subscription, request, finalize }) => { /** * @note Subscriptions are resolved imperatively, so the return * value of the resolver is ignored. The request describes the * WebSocket connection this subscription is multiplexed over. */ resolver({ operationName, query: subscription.query, variables: subscription.variables, cookies: getAllRequestCookies(request), request, requestId: createRequestId(), finalize, }) }, // Forward the handler options so a one-time operation handler is // also consumed by the first subscription it matches. options, ) return attachSiblingHandlers(handler, [subscriptionCatchAllHandler]) } } export interface GraphQLLink { query: GraphQLRequestHandler mutation: GraphQLRequestHandler operation: GraphQLOperationHandler /** * Intercept a GraphQL subscription. * * @example * const api = graphql.link('https://api.example.com/graphql') * api.subscription('OnPostAdded', ({ subscription }) => { * subscription.publish({ * data: { postAdded: { id: 'abc-123' } }, * }) * }) */ subscription: GraphQLSubscriptionHandlerFactory } /** * A namespace to intercept and mock GraphQL operations * * @example * const api = graphql.link('https://api.example.com/graphql') * api.query('GetUser', resolver) * api.mutation('DeletePost', resolver) * * @see {@link https://mswjs.io/docs/api/graphql `graphql` API reference} */ export const graphql = { /** * Intercepts GraphQL operations scoped by the given URL. * * @example * const github = graphql.link('https://api.github.com/graphql') * github.query('GetRepo', resolver) * * @see {@link https://mswjs.io/docs/api/graphql#graphqllinkurl `graphql.link()` API reference} */ link(url: Path): GraphQLLink { /** * @note Create the subscription handler factory once per link so * the `subscription()` and `operation()` handlers share the same * underlying subscription transport (deduped by reference). */ const subscription = createGraphQLSubscriptionHandler(url) return { operation: createGraphQLOperationHandler(url, subscription), query: createScopedGraphQLHandler('query', url), mutation: createScopedGraphQLHandler('mutation', url), subscription, } }, }