Skip to content

Commit db70142

Browse files
committed
use perform and on-resolve
1 parent 45ae23c commit db70142

File tree

3 files changed

+124
-166
lines changed

3 files changed

+124
-166
lines changed

packages/plugins/apollo-inline-trace/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@
5151
"graphql": "^14.0.0 || ^15.0.0 || ^16.0.0"
5252
},
5353
"dependencies": {
54+
"@envelop/on-resolve": "^1.0.0",
5455
"apollo-reporting-protobuf": "^3.3.2"
5556
},
5657
"buildOptions": {

packages/plugins/apollo-inline-trace/src/index.ts

Lines changed: 50 additions & 120 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
import { isAsyncIterable, Plugin } from '@envelop/core';
2-
import { ExecutionResult, GraphQLError, Kind, OperationTypeNode, ResponsePath } from 'graphql';
2+
import { useOnResolve } from '@envelop/on-resolve';
3+
import { GraphQLError, ResponsePath } from 'graphql';
34
import { google, Trace } from 'apollo-reporting-protobuf';
45

56
const ctxKey = Symbol('ApolloInlineTracePluginContextKey');
6-
const errorsKey = Symbol('ApolloInlineTracePluginErrorsKey');
77

88
interface ApolloInlineTracePluginContext {
99
startHrTime: [number, number];
@@ -49,7 +49,7 @@ export function useApolloInlineTrace<PluginContext extends Record<string, any> =
4949
shouldTrace,
5050
rewriteError,
5151
}: ApolloInlineTracePluginOptions<PluginContext>): Plugin<
52-
PluginContext & { [ctxKey]: ApolloInlineTracePluginContext; [errorsKey]: GraphQLError[] }
52+
PluginContext & { [ctxKey]: ApolloInlineTracePluginContext }
5353
> {
5454
return {
5555
onEnveloped({ context, extendContext }) {
@@ -73,142 +73,72 @@ export function useApolloInlineTrace<PluginContext extends Record<string, any> =
7373
nodes: new Map([[responsePathToString(), rootNode]]),
7474
stopped: false,
7575
},
76-
[errorsKey]: [],
7776
});
7877
}
7978
},
80-
onResolverCalled({ context, info }) {
81-
const ctx = context[ctxKey];
82-
if (!ctx) return;
83-
84-
// result was already shipped (see ApolloInlineTracePluginContext.stopped)
85-
if (ctx.stopped) {
86-
return () => {
87-
// noop
88-
};
89-
}
90-
91-
const node = newTraceNode(ctx, info.path);
92-
node.type = info.returnType.toString();
93-
node.parentType = info.parentType.toString();
94-
node.startTime = hrTimeToDurationInNanos(process.hrtime(ctx.startHrTime));
95-
if (typeof info.path.key === 'string' && info.path.key !== info.fieldName) {
96-
// field was aliased, send the original field name too
97-
node.originalFieldName = info.fieldName;
98-
}
79+
onPluginInit({ addPlugin }) {
80+
addPlugin(
81+
useOnResolve(({ context, info }) => {
82+
const ctx = context[ctxKey];
83+
if (!ctx) return;
9984

100-
return () => {
101-
node.endTime = hrTimeToDurationInNanos(process.hrtime(ctx.startHrTime));
102-
};
103-
},
104-
onParse() {
105-
return ({ context, result, replaceParseResult }) => {
106-
const ctx = context[ctxKey];
107-
if (!ctx) return;
85+
// result was already shipped (see ApolloInlineTracePluginContext.stopped)
86+
if (ctx.stopped) {
87+
return () => {
88+
// noop
89+
};
90+
}
10891

109-
const errors = context[errorsKey];
110-
if (errors && result instanceof Error) {
111-
if (result instanceof GraphQLError) {
112-
errors.push(result);
113-
} else {
114-
errors.push(
115-
new GraphQLError(result.message, {
116-
originalError: result,
117-
})
118-
);
92+
const node = newTraceNode(ctx, info.path);
93+
node.type = info.returnType.toString();
94+
node.parentType = info.parentType.toString();
95+
node.startTime = hrTimeToDurationInNanos(process.hrtime(ctx.startHrTime));
96+
if (typeof info.path.key === 'string' && info.path.key !== info.fieldName) {
97+
// field was aliased, send the original field name too
98+
node.originalFieldName = info.fieldName;
11999
}
120-
replaceParseResult({
121-
kind: Kind.DOCUMENT,
122-
definitions: [
123-
{
124-
kind: Kind.OPERATION_DEFINITION,
125-
operation: OperationTypeNode.QUERY,
126-
selectionSet: {
127-
kind: Kind.SELECTION_SET,
128-
selections: [
129-
{
130-
kind: Kind.FIELD,
131-
name: {
132-
kind: Kind.NAME,
133-
value: '__typename',
134-
},
135-
},
136-
],
137-
},
138-
},
139-
],
140-
});
141-
}
142-
};
143-
},
144-
onValidate({ context, setResult }) {
145-
const errorsInContext = context[errorsKey];
146-
if (errorsInContext?.length) {
147-
setResult([]);
148-
}
149-
return ({ result: errors, setResult }) => {
150-
if (errorsInContext) {
151-
errorsInContext.push(...errors);
152-
setResult([]);
153-
}
154-
};
100+
101+
return () => {
102+
node.endTime = hrTimeToDurationInNanos(process.hrtime(ctx.startHrTime));
103+
};
104+
})
105+
);
155106
},
156-
onExecute({ args: { contextValue }, setResultAndStopExecution }) {
157-
const errors = contextValue[errorsKey];
158-
if (errors?.length) {
159-
const ctx = contextValue[ctxKey];
160-
if (!ctx) return;
161-
const result = { errors };
162-
const updatedResult = updateResult(result, rewriteError, ctx);
163-
setResultAndStopExecution(updatedResult);
164-
return;
165-
}
107+
onPerform({ context }) {
166108
return {
167-
onExecuteDone({ args: { contextValue }, result, setResult }) {
168-
const ctx = contextValue[ctxKey];
109+
onPerformDone({ result }) {
110+
const ctx = context[ctxKey];
169111
if (!ctx) return;
170112

171113
// TODO: should handle streaming results? how?
172114
if (isAsyncIterable(result)) return;
173115

174-
const updatedResult = updateResult(result, rewriteError, ctx);
175-
176-
setResult(updatedResult);
177-
},
178-
};
179-
},
180-
};
181-
}
182-
183-
function updateResult(
184-
result: ExecutionResult,
185-
rewriteError: ApolloInlineTracePluginOptions['rewriteError'],
186-
ctx: ApolloInlineTracePluginContext
187-
) {
188-
if (result.extensions?.ftv1 !== undefined) {
189-
throw new Error('The `ftv1` extension is already present');
190-
}
116+
if (result.extensions?.ftv1 !== undefined) {
117+
throw new Error('The `ftv1` extension is already present');
118+
}
191119

192-
if (result.errors?.length) {
193-
handleErrors(ctx, result.errors, rewriteError);
194-
}
120+
// onResultProcess will be called only once since we disallow async iterables
121+
if (ctx.stopped) throw new Error('Trace stopped multiple times');
195122

196-
// onResultProcess will be called only once since we disallow async iterables
197-
if (ctx.stopped) throw new Error('Trace stopped multiple times');
123+
if (result.errors) {
124+
handleErrors(ctx, result.errors, rewriteError);
125+
}
198126

199-
ctx.stopped = true;
200-
ctx.trace.durationNs = hrTimeToDurationInNanos(process.hrtime(ctx.startHrTime));
201-
ctx.trace.endTime = nowTimestamp();
127+
ctx.stopped = true;
128+
ctx.trace.durationNs = hrTimeToDurationInNanos(process.hrtime(ctx.startHrTime));
129+
ctx.trace.endTime = nowTimestamp();
202130

203-
const encodedUint8Array = Trace.encode(ctx.trace).finish();
204-
const encodedBuffer = Buffer.from(encodedUint8Array, encodedUint8Array.byteOffset, encodedUint8Array.byteLength);
131+
const encodedUint8Array = Trace.encode(ctx.trace).finish();
132+
const base64 = btoa(String.fromCharCode(...encodedUint8Array));
205133

206-
result.extensions = {
207-
...result.extensions,
208-
ftv1: encodedBuffer.toString('base64'),
134+
result.extensions = {
135+
...result.extensions,
136+
ftv1: base64,
137+
};
138+
},
139+
};
140+
},
209141
};
210-
211-
return result;
212142
}
213143

214144
/**

0 commit comments

Comments
 (0)