-
-
Notifications
You must be signed in to change notification settings - Fork 1.6k
/
apollo.ts
101 lines (85 loc) · 2.63 KB
/
apollo.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
import { Hub } from '@sentry/hub';
import { EventProcessor, Integration } from '@sentry/types';
import { arrayify, fill, isThenable, loadModule, logger } from '@sentry/utils';
type ApolloResolverGroup = {
[key: string]: () => unknown;
};
type ApolloModelResolvers = {
[key: string]: ApolloResolverGroup;
};
/** Tracing integration for Apollo */
export class Apollo implements Integration {
/**
* @inheritDoc
*/
public static id: string = 'Apollo';
/**
* @inheritDoc
*/
public name: string = Apollo.id;
/**
* @inheritDoc
*/
public setupOnce(_: (callback: EventProcessor) => void, getCurrentHub: () => Hub): void {
const pkg = loadModule<{
ApolloServerBase: {
prototype: {
constructSchema: () => unknown;
};
};
}>('apollo-server-core');
if (!pkg) {
logger.error('Apollo Integration was unable to require apollo-server-core package.');
return;
}
/**
* Iterate over resolvers of the ApolloServer instance before schemas are constructed.
*/
fill(pkg.ApolloServerBase.prototype, 'constructSchema', function (orig: () => unknown) {
return function (this: { config: { resolvers: ApolloModelResolvers[] } }) {
const resolvers = arrayify(this.config.resolvers);
this.config.resolvers = resolvers.map(model => {
Object.keys(model).forEach(resolverGroupName => {
Object.keys(model[resolverGroupName]).forEach(resolverName => {
if (typeof model[resolverGroupName][resolverName] !== 'function') {
return;
}
wrapResolver(model, resolverGroupName, resolverName, getCurrentHub);
});
});
return model;
});
return orig.call(this);
};
});
}
}
/**
* Wrap a single resolver which can be a parent of other resolvers and/or db operations.
*/
function wrapResolver(
model: ApolloModelResolvers,
resolverGroupName: string,
resolverName: string,
getCurrentHub: () => Hub,
): void {
fill(model[resolverGroupName], resolverName, function (orig: () => unknown | Promise<unknown>) {
return function (this: unknown, ...args: unknown[]) {
const scope = getCurrentHub().getScope();
const parentSpan = scope?.getSpan();
const span = parentSpan?.startChild({
description: `${resolverGroupName}.${resolverName}`,
op: 'db.graphql.apollo',
});
const rv = orig.call(this, ...args);
if (isThenable(rv)) {
return rv.then((res: unknown) => {
span?.finish();
return res;
});
}
span?.finish();
return rv;
};
});
}