Skip to content

Commit 90a8e9c

Browse files
James Baxleyfreiksenet
James Baxley
authored andcommitted
added native support for using apollo-link as the network layer (#409)
* added native support for using apollo-link as the network layer * Minor optimizations to avoid excessive parsing/printing
1 parent 6b7794f commit 90a8e9c

5 files changed

+88
-26
lines changed

CHANGELOG.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
# Change log
22

33
### vNEXT
4+
* Added support for passing an Apollo Link instead of a fetcher
45

56
### v2.0.0
6-
77
* Add schema merging utilities [PR #382](https://github.com/apollographql/graphql-tools/pull/382)
88

99
### v1.2.3

package.json

+1
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@
4848
},
4949
"homepage": "https://github.com/apollostack/graphql-tools#readme",
5050
"dependencies": {
51+
"apollo-link": "^0.7.0",
5152
"deprecated-decorator": "^0.1.6",
5253
"uuid": "^3.1.0"
5354
},

src/stitching/introspectSchema.ts

+15-7
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,23 @@
1-
import { GraphQLSchema } from 'graphql';
1+
import { GraphQLSchema, parse } from 'graphql';
22
import { introspectionQuery, buildClientSchema } from 'graphql';
3-
import { Fetcher } from './makeRemoteExecutableSchema';
3+
import { ApolloLink, execute, makePromise } from 'apollo-link';
4+
import { Fetcher, fetcherToLink } from './makeRemoteExecutableSchema';
5+
6+
const parsedIntrospectionQuery = parse(introspectionQuery);
47

58
export default async function introspectSchema(
6-
fetcher: Fetcher,
9+
link: ApolloLink | Fetcher,
710
context?: { [key: string]: any },
811
): Promise<GraphQLSchema> {
9-
const introspectionResult = await fetcher({
10-
query: introspectionQuery,
11-
context,
12-
});
12+
if (!(link as ApolloLink).request) {
13+
link = fetcherToLink(link as Fetcher);
14+
}
15+
const introspectionResult = await makePromise(
16+
execute(link as ApolloLink, {
17+
query: parsedIntrospectionQuery,
18+
context,
19+
}),
20+
);
1321
if (introspectionResult.errors || !introspectionResult.data.__schema) {
1422
throw introspectionResult.errors;
1523
} else {

src/stitching/makeRemoteExecutableSchema.ts

+47-15
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1-
import { printSchema, print, ExecutionResult, Kind, ValueNode } from 'graphql';
1+
import { printSchema, print, Kind, ValueNode, ExecutionResult } from 'graphql';
2+
import { execute, makePromise, ApolloLink, Observable } from 'apollo-link';
3+
24
import {
35
GraphQLFieldResolver,
46
GraphQLSchema,
@@ -25,25 +27,51 @@ export type Fetcher = (
2527
},
2628
) => Promise<ExecutionResult>;
2729

30+
export const fetcherToLink = (fetcher: Fetcher): ApolloLink => {
31+
return new ApolloLink(operation => {
32+
return new Observable(observer => {
33+
const { query, operationName, variables } = operation;
34+
const context = operation.getContext();
35+
fetcher({
36+
query: typeof query === 'string' ? query : print(query),
37+
operationName,
38+
variables,
39+
context,
40+
})
41+
.then((result: ExecutionResult) => {
42+
observer.next(result);
43+
observer.complete();
44+
})
45+
.catch(observer.error.bind(observer));
46+
});
47+
});
48+
};
49+
2850
export default function makeRemoteExecutableSchema({
2951
schema,
52+
link,
3053
fetcher,
3154
}: {
3255
schema: GraphQLSchema;
33-
fetcher: Fetcher;
56+
link?: ApolloLink;
57+
fetcher?: Fetcher;
3458
}): GraphQLSchema {
59+
if (fetcher && !link) {
60+
link = fetcherToLink(fetcher);
61+
}
62+
3563
const queryType = schema.getQueryType();
3664
const queries = queryType.getFields();
3765
const queryResolvers: IResolverObject = {};
3866
Object.keys(queries).forEach(key => {
39-
queryResolvers[key] = createResolver(fetcher);
67+
queryResolvers[key] = createResolver(link);
4068
});
4169
let mutationResolvers: IResolverObject = {};
4270
const mutationType = schema.getMutationType();
4371
if (mutationType) {
4472
const mutations = mutationType.getFields();
4573
Object.keys(mutations).forEach(key => {
46-
mutationResolvers[key] = createResolver(fetcher);
74+
mutationResolvers[key] = createResolver(link);
4775
});
4876
}
4977

@@ -88,18 +116,22 @@ export default function makeRemoteExecutableSchema({
88116
});
89117
}
90118

91-
function createResolver(fetcher: Fetcher): GraphQLFieldResolver<any, any> {
119+
function createResolver(link: ApolloLink): GraphQLFieldResolver<any, any> {
92120
return async (root, args, context, info) => {
93-
const operation = print(info.operation);
94-
const fragments = Object.keys(info.fragments)
95-
.map(fragment => print(info.fragments[fragment]))
96-
.join('\n');
97-
const query = `${operation}\n${fragments}`;
98-
const result = await fetcher({
99-
query,
100-
variables: info.variableValues,
101-
context,
102-
});
121+
const fragments = Object.keys(info.fragments).map(
122+
fragment => info.fragments[fragment],
123+
);
124+
const document = {
125+
kind: Kind.DOCUMENT,
126+
definitions: [info.operation, ...fragments],
127+
};
128+
const result = await makePromise(
129+
execute(link, {
130+
query: document,
131+
variables: info.variableValues,
132+
context,
133+
}),
134+
);
103135
const fieldName = info.fieldNodes[0].alias
104136
? info.fieldNodes[0].alias.value
105137
: info.fieldName;

src/test/testingSchemas.ts

+24-3
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
import {
22
GraphQLSchema,
33
graphql,
4+
print,
45
Kind,
56
GraphQLScalarType,
67
ValueNode,
78
} from 'graphql';
9+
import { ApolloLink, Observable } from 'apollo-link';
810
import { makeExecutableSchema } from '../schemaGenerator';
911
import { IResolvers } from '../Interfaces';
1012
import makeRemoteExecutableSchema from '../stitching/makeRemoteExecutableSchema';
@@ -485,7 +487,26 @@ export const bookingSchema: GraphQLSchema = makeExecutableSchema({
485487
});
486488

487489
// Pretend this schema is remote
488-
async function makeSchemaRemote(schema: GraphQLSchema) {
490+
async function makeSchemaRemoteFromLink(schema: GraphQLSchema) {
491+
const link = new ApolloLink((operation) => {
492+
return new Observable(observer => {
493+
const { query, operationName, variables } = operation;
494+
const context = operation.getContext();
495+
graphql(schema, print(query), null, context, variables, operationName)
496+
.then((result) => {
497+
observer.next(result);
498+
observer.complete();
499+
})
500+
.catch(observer.error.bind(observer));
501+
});
502+
});
503+
504+
const clientSchema = await introspectSchema(link);
505+
return makeRemoteExecutableSchema({ schema: clientSchema, link });
506+
}
507+
508+
// ensure fetcher support exists from the 2.0 api
509+
async function makeExecutableSchemaFromFetcher(schema: GraphQLSchema) {
489510
const fetcher: Fetcher = ({ query, operationName, variables, context }) => {
490511
return graphql(schema, query, null, context, variables, operationName);
491512
};
@@ -494,5 +515,5 @@ async function makeSchemaRemote(schema: GraphQLSchema) {
494515
return makeRemoteExecutableSchema({ schema: clientSchema, fetcher });
495516
}
496517

497-
export const remotePropertySchema = makeSchemaRemote(propertySchema);
498-
export const remoteBookingSchema = makeSchemaRemote(bookingSchema);
518+
export const remotePropertySchema = makeSchemaRemoteFromLink(propertySchema);
519+
export const remoteBookingSchema = makeExecutableSchemaFromFetcher(bookingSchema);

0 commit comments

Comments
 (0)