diff --git a/packages/loaders/url/package.json b/packages/loaders/url/package.json index f122673a874..15b5080adac 100644 --- a/packages/loaders/url/package.json +++ b/packages/loaders/url/package.json @@ -23,6 +23,7 @@ "supertest": "4.0.2" }, "dependencies": { + "@graphql-tools/delegate": "6.0.7", "@graphql-tools/wrap": "6.0.7", "@graphql-tools/utils": "6.0.7", "@types/websocket": "1.0.0", @@ -36,4 +37,4 @@ "access": "public", "directory": "dist" } -} \ No newline at end of file +} diff --git a/packages/loaders/url/src/index.ts b/packages/loaders/url/src/index.ts index 541135823f0..fb683946c06 100644 --- a/packages/loaders/url/src/index.ts +++ b/packages/loaders/url/src/index.ts @@ -9,7 +9,8 @@ import { } from '@graphql-tools/utils'; import { isWebUri } from 'valid-url'; import { fetch as crossFetch } from 'cross-fetch'; -import { introspectSchema, makeRemoteExecutableSchema, IMakeRemoteExecutableSchemaOptions } from '@graphql-tools/wrap'; +import { AsyncExecutor, Subscriber, SubschemaConfig } from '@graphql-tools/delegate'; +import { introspectSchema, wrapSchema } from '@graphql-tools/wrap'; import { SubscriptionClient } from 'subscriptions-transport-ws'; import { w3cwebsocket } from 'websocket'; @@ -39,7 +40,60 @@ export class UrlLoader implements DocumentLoader { return !!isWebUri(pointer); } - async load(pointer: SchemaPointerSingle, options: LoadFromUrlOptions): Promise { + buildAsyncExecutor({ + pointer, + fetch, + extraHeaders, + defaultMethod, + useGETForQueries, + }: { + pointer: string; + fetch: typeof crossFetch; + extraHeaders: any; + defaultMethod: 'GET' | 'POST'; + useGETForQueries: boolean; + }): AsyncExecutor { + const HTTP_URL = pointer.replace('ws:', 'http:').replace('wss:', 'https:'); + return async ({ + document, + variables, + info, + }: { + document: DocumentNode; + variables: any; + info: GraphQLResolveInfo; + }) => { + let method = defaultMethod; + if (useGETForQueries && info.operation.operation === 'query') { + method = 'GET'; + } + const fetchResult = await fetch(HTTP_URL, { + method, + ...(method === 'POST' + ? { + body: JSON.stringify({ query: print(document), variables }), + } + : {}), + headers: extraHeaders, + }); + return fetchResult.json(); + }; + } + + buildSubscriber(pointer: string, webSocketImpl: typeof w3cwebsocket): Subscriber { + const WS_URL = pointer.replace('http:', 'ws:').replace('https:', 'wss:'); + const subscriptionClient = new SubscriptionClient(WS_URL, {}, webSocketImpl); + return async ({ document, variables }: { document: DocumentNode; variables: TArgs }) => { + return observableToAsyncIterable( + subscriptionClient.request({ + query: document, + variables, + }) + ) as AsyncIterator>; + }; + } + + async getSubschemaConfig(pointer: SchemaPointerSingle, options: LoadFromUrlOptions): Promise { let headers = {}; let fetch = crossFetch; let defaultMethod: 'GET' | 'POST' = 'POST'; @@ -83,61 +137,31 @@ export class UrlLoader implements DocumentLoader { ...headers, }; - const HTTP_URL = pointer.replace('ws:', 'http:').replace('wss:', 'https:'); - - const executor = async ({ - document, - variables, - info, - }: { - document: DocumentNode; - variables: any; - info: GraphQLResolveInfo; - }) => { - let method = defaultMethod; - if (options.useGETForQueries && info.operation.operation === 'query') { - method = 'GET'; - } - const fetchResult = await fetch(HTTP_URL, { - method, - ...(method === 'POST' - ? { - body: JSON.stringify({ query: print(document), variables }), - } - : {}), - headers: extraHeaders, - }); - return fetchResult.json(); - }; + const executor = this.buildAsyncExecutor({ + pointer, + fetch, + extraHeaders, + defaultMethod, + useGETForQueries: options.useGETForQueries, + }); const schema = await introspectSchema(executor); - const remoteExecutableSchemaOptions: IMakeRemoteExecutableSchemaOptions = { + const remoteExecutableSchemaOptions: SubschemaConfig = { schema, executor, }; if (options.enableSubscriptions) { - const WS_URL = pointer.replace('http:', 'ws:').replace('https:', 'wss:'); - const subscriptionClient = new SubscriptionClient(WS_URL, {}, webSocketImpl); - - remoteExecutableSchemaOptions.subscriber = async ({ - document, - variables, - }: { - document: DocumentNode; - variables: TArgs; - }) => { - return observableToAsyncIterable( - subscriptionClient.request({ - query: document, - variables, - }) - ) as AsyncIterator>; - }; + remoteExecutableSchemaOptions.subscriber = this.buildSubscriber(pointer, webSocketImpl); } - const remoteExecutableSchema = makeRemoteExecutableSchema(remoteExecutableSchemaOptions); + return remoteExecutableSchemaOptions; + } + + async load(pointer: SchemaPointerSingle, options: LoadFromUrlOptions): Promise { + const remoteExecutableSchemaOptions = await this.getSubschemaConfig(pointer, options); + const remoteExecutableSchema = wrapSchema(remoteExecutableSchemaOptions); return { location: pointer,