-
Notifications
You must be signed in to change notification settings - Fork 242
/
execution-utils.ts
147 lines (126 loc) 路 4 KB
/
execution-utils.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
import {
GraphQLSchemaValidationError,
GraphQLSchemaModule,
GraphQLResolverMap,
} from 'apollo-graphql';
import { GraphQLRequest, GraphQLExecutionResult, Logger } from 'apollo-server-types';
import {
composeAndValidate,
ServiceDefinition,
compositionHasErrors,
} from '@apollo/federation';
import { buildSubgraphSchema } from '@apollo/subgraph';
import {
executeQueryPlan,
buildOperationContext,
} from '@apollo/gateway';
import { buildComposedSchema, QueryPlanner, QueryPlan } from '@apollo/query-planner';
import { LocalGraphQLDataSource } from '../datasources/LocalGraphQLDataSource';
import { mergeDeep } from 'apollo-utilities';
import { queryPlanSerializer, astSerializer } from 'apollo-federation-integration-testsuite';
import gql from 'graphql-tag';
import { fixtures } from 'apollo-federation-integration-testsuite';
import { parse } from 'graphql';
const prettyFormat = require('pretty-format');
export type ServiceDefinitionModule = ServiceDefinition & GraphQLSchemaModule;
export function overrideResolversInService(
module: ServiceDefinitionModule,
resolvers: GraphQLResolverMap,
): ServiceDefinitionModule {
return {
name: module.name,
typeDefs: module.typeDefs,
resolvers: mergeDeep(module.resolvers, resolvers),
};
}
export async function execute(
request: GraphQLRequest,
services: ServiceDefinitionModule[] = fixtures,
logger: Logger = console,
): Promise<GraphQLExecutionResult & { queryPlan: QueryPlan }> {
const serviceMap = Object.fromEntries(
services.map(({ name, typeDefs, resolvers }) => {
return [
name,
new LocalGraphQLDataSource(
buildSubgraphSchema([{ typeDefs, resolvers }]),
),
] as [string, LocalGraphQLDataSource];
}),
);
const { schema, queryPlanner } = getFederatedTestingSchema(services);
const operationContext = buildOperationContext({
schema,
operationDocument: gql`${request.query}`,
});
const queryPlan = queryPlanner.buildQueryPlan(operationContext);
const result = await executeQueryPlan(
queryPlan,
serviceMap,
// @ts-ignore
{
cache: undefined as any,
context: {},
request,
logger
},
operationContext,
);
return { ...result, queryPlan };
}
export function buildLocalService(modules: GraphQLSchemaModule[]) {
const schema = buildSubgraphSchema(modules);
return new LocalGraphQLDataSource(schema);
}
export function getFederatedTestingSchema(services: ServiceDefinitionModule[] = fixtures) {
const serviceMap = Object.fromEntries(
services.map((service) => [
service.name,
buildLocalService([service]),
]),
);
const compositionResult = composeAndValidate(services);
if (compositionHasErrors(compositionResult)) {
throw new GraphQLSchemaValidationError(compositionResult.errors);
}
const schema = buildComposedSchema(parse(compositionResult.supergraphSdl))
const queryPlanner = new QueryPlanner(schema);
return { serviceMap, schema, queryPlanner };
}
export function getTestingSupergraphSdl(services: typeof fixtures = fixtures) {
const compositionResult = composeAndValidate(services);
if (!compositionHasErrors(compositionResult)) {
return compositionResult.supergraphSdl;
}
throw new Error(
`Testing fixtures don't compose properly!\n${compositionResult.errors.join(
'\t\n',
)}`,
);
}
export function wait(ms: number, toResolveTo?: any) {
return new Promise((r) => setTimeout(() => r(toResolveTo), ms));
}
export function waitUntil<T = void>() {
let userResolved: (value: T | PromiseLike<T>) => void;
let userRejected: (reason?: any) => void;
const promise = new Promise<T>(
(r) => ((userResolved = r), (userRejected = r)),
);
return [
promise,
// @ts-ignore
userResolved,
// @ts-ignore
userRejected,
] as [
Promise<T>,
(value: T | PromiseLike<T>) => void,
(reason?: any) => void,
];
}
export function printPlan(queryPlan: QueryPlan): string {
return prettyFormat(queryPlan, {
plugins: [queryPlanSerializer, astSerializer],
});
}