123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194 |
- import { GraphQLError, } from 'graphql';
- import { Trace, google } from '@apollo/usage-reporting-protobuf';
- import { UnreachableCaseError } from '../utils/UnreachableCaseError.js';
- function internalError(message) {
- return new Error(`[internal apollo-server error] ${message}`);
- }
- export class TraceTreeBuilder {
- constructor(options) {
- this.rootNode = new Trace.Node();
- this.trace = new Trace({
- root: this.rootNode,
- fieldExecutionWeight: 1,
- });
- this.stopped = false;
- this.nodes = new Map([
- [responsePathAsString(), this.rootNode],
- ]);
- const { sendErrors, maskedBy } = options;
- if (!sendErrors || 'masked' in sendErrors) {
- this.transformError = () => new GraphQLError('<masked>', {
- extensions: { maskedBy },
- });
- }
- else if ('transform' in sendErrors) {
- this.transformError = sendErrors.transform;
- }
- else if ('unmodified' in sendErrors) {
- this.transformError = null;
- }
- else {
- throw new UnreachableCaseError(sendErrors);
- }
- }
- startTiming() {
- if (this.startHrTime) {
- throw internalError('startTiming called twice!');
- }
- if (this.stopped) {
- throw internalError('startTiming called after stopTiming!');
- }
- this.trace.startTime = dateToProtoTimestamp(new Date());
- this.startHrTime = process.hrtime();
- }
- stopTiming() {
- if (!this.startHrTime) {
- throw internalError('stopTiming called before startTiming!');
- }
- if (this.stopped) {
- throw internalError('stopTiming called twice!');
- }
- this.trace.durationNs = durationHrTimeToNanos(process.hrtime(this.startHrTime));
- this.trace.endTime = dateToProtoTimestamp(new Date());
- this.stopped = true;
- }
- willResolveField(info) {
- if (!this.startHrTime) {
- throw internalError('willResolveField called before startTiming!');
- }
- if (this.stopped) {
- return () => { };
- }
- const path = info.path;
- const node = this.newNode(path);
- node.type = info.returnType.toString();
- node.parentType = info.parentType.toString();
- node.startTime = durationHrTimeToNanos(process.hrtime(this.startHrTime));
- if (typeof path.key === 'string' && path.key !== info.fieldName) {
- node.originalFieldName = info.fieldName;
- }
- return () => {
- node.endTime = durationHrTimeToNanos(process.hrtime(this.startHrTime));
- };
- }
- didEncounterErrors(errors) {
- errors.forEach((err) => {
- if (err.extensions?.serviceName) {
- return;
- }
- const errorForReporting = this.transformAndNormalizeError(err);
- if (errorForReporting === null) {
- return;
- }
- this.addProtobufError(errorForReporting.path, errorToProtobufError(errorForReporting));
- });
- }
- addProtobufError(path, error) {
- if (!this.startHrTime) {
- throw internalError('addProtobufError called before startTiming!');
- }
- if (this.stopped) {
- throw internalError('addProtobufError called after stopTiming!');
- }
- let node = this.rootNode;
- if (Array.isArray(path)) {
- const specificNode = this.nodes.get(path.join('.'));
- if (specificNode) {
- node = specificNode;
- }
- else {
- const responsePath = responsePathFromArray(path, this.rootNode);
- if (!responsePath) {
- throw internalError('addProtobufError called with invalid path!');
- }
- node = this.newNode(responsePath);
- }
- }
- node.error.push(error);
- }
- newNode(path) {
- const node = new Trace.Node();
- const id = path.key;
- if (typeof id === 'number') {
- node.index = id;
- }
- else {
- node.responseName = id;
- }
- this.nodes.set(responsePathAsString(path), node);
- const parentNode = this.ensureParentNode(path);
- parentNode.child.push(node);
- return node;
- }
- ensureParentNode(path) {
- const parentPath = responsePathAsString(path.prev);
- const parentNode = this.nodes.get(parentPath);
- if (parentNode) {
- return parentNode;
- }
- return this.newNode(path.prev);
- }
- transformAndNormalizeError(err) {
- if (this.transformError) {
- const clonedError = Object.assign(Object.create(Object.getPrototypeOf(err)), err);
- const rewrittenError = this.transformError(clonedError);
- if (rewrittenError === null) {
- return null;
- }
- if (!(rewrittenError instanceof GraphQLError)) {
- return err;
- }
- return new GraphQLError(rewrittenError.message, {
- nodes: err.nodes,
- source: err.source,
- positions: err.positions,
- path: err.path,
- originalError: err.originalError,
- extensions: rewrittenError.extensions || err.extensions,
- });
- }
- return err;
- }
- }
- function durationHrTimeToNanos(hrtime) {
- return hrtime[0] * 1e9 + hrtime[1];
- }
- function responsePathAsString(p) {
- if (p === undefined) {
- return '';
- }
- let res = String(p.key);
- while ((p = p.prev) !== undefined) {
- res = `${p.key}.${res}`;
- }
- return res;
- }
- function responsePathFromArray(path, node) {
- let responsePath;
- let nodePtr = node;
- for (const key of path) {
- nodePtr = nodePtr?.child?.find((child) => child.responseName === key);
- responsePath = {
- key,
- prev: responsePath,
- typename: nodePtr?.type ?? undefined,
- };
- }
- return responsePath;
- }
- function errorToProtobufError(error) {
- return new Trace.Error({
- message: error.message,
- location: (error.locations || []).map(({ line, column }) => new Trace.Location({ line, column })),
- json: JSON.stringify(error),
- });
- }
- export function dateToProtoTimestamp(date) {
- const totalMillis = +date;
- const millis = totalMillis % 1000;
- return new google.protobuf.Timestamp({
- seconds: (totalMillis - millis) / 1000,
- nanos: millis * 1e6,
- });
- }
- //# sourceMappingURL=traceTreeBuilder.js.map
|