UNPKG

5.91 kBJavaScriptView Raw
1"use strict";
2Object.defineProperty(exports, "__esModule", { value: true });
3exports.dateToProtoTimestamp = exports.TraceTreeBuilder = void 0;
4const graphql_1 = require("graphql");
5const apollo_reporting_protobuf_1 = require("apollo-reporting-protobuf");
6function internalError(message) {
7 return new Error(`[internal apollo-server error] ${message}`);
8}
9class TraceTreeBuilder {
10 constructor(options) {
11 this.rootNode = new apollo_reporting_protobuf_1.Trace.Node();
12 this.logger = console;
13 this.trace = new apollo_reporting_protobuf_1.Trace({ root: this.rootNode });
14 this.stopped = false;
15 this.nodes = new Map([
16 [responsePathAsString(), this.rootNode],
17 ]);
18 this.rewriteError = options.rewriteError;
19 if (options.logger)
20 this.logger = options.logger;
21 }
22 startTiming() {
23 if (this.startHrTime) {
24 throw internalError('startTiming called twice!');
25 }
26 if (this.stopped) {
27 throw internalError('startTiming called after stopTiming!');
28 }
29 this.trace.startTime = dateToProtoTimestamp(new Date());
30 this.startHrTime = process.hrtime();
31 }
32 stopTiming() {
33 if (!this.startHrTime) {
34 throw internalError('stopTiming called before startTiming!');
35 }
36 if (this.stopped) {
37 throw internalError('stopTiming called twice!');
38 }
39 this.trace.durationNs = durationHrTimeToNanos(process.hrtime(this.startHrTime));
40 this.trace.endTime = dateToProtoTimestamp(new Date());
41 this.stopped = true;
42 }
43 willResolveField(info) {
44 if (!this.startHrTime) {
45 throw internalError('willResolveField called before startTiming!');
46 }
47 if (this.stopped) {
48 throw internalError('willResolveField called after stopTiming!');
49 }
50 const path = info.path;
51 const node = this.newNode(path);
52 node.type = info.returnType.toString();
53 node.parentType = info.parentType.toString();
54 node.startTime = durationHrTimeToNanos(process.hrtime(this.startHrTime));
55 if (typeof path.key === 'string' && path.key !== info.fieldName) {
56 node.originalFieldName = info.fieldName;
57 }
58 return () => {
59 node.endTime = durationHrTimeToNanos(process.hrtime(this.startHrTime));
60 };
61 }
62 didEncounterErrors(errors) {
63 errors.forEach((err) => {
64 if (err.extensions && err.extensions.serviceName) {
65 return;
66 }
67 const errorForReporting = this.rewriteAndNormalizeError(err);
68 if (errorForReporting === null) {
69 return;
70 }
71 this.addProtobufError(errorForReporting.path, errorToProtobufError(errorForReporting));
72 });
73 }
74 addProtobufError(path, error) {
75 if (!this.startHrTime) {
76 throw internalError('addProtobufError called before startTiming!');
77 }
78 if (this.stopped) {
79 throw internalError('addProtobufError called after stopTiming!');
80 }
81 let node = this.rootNode;
82 if (Array.isArray(path)) {
83 const specificNode = this.nodes.get(path.join('.'));
84 if (specificNode) {
85 node = specificNode;
86 }
87 else {
88 this.logger.warn(`Could not find node with path ${path.join('.')}; defaulting to put errors on root node.`);
89 }
90 }
91 node.error.push(error);
92 }
93 newNode(path) {
94 const node = new apollo_reporting_protobuf_1.Trace.Node();
95 const id = path.key;
96 if (typeof id === 'number') {
97 node.index = id;
98 }
99 else {
100 node.responseName = id;
101 }
102 this.nodes.set(responsePathAsString(path), node);
103 const parentNode = this.ensureParentNode(path);
104 parentNode.child.push(node);
105 return node;
106 }
107 ensureParentNode(path) {
108 const parentPath = responsePathAsString(path.prev);
109 const parentNode = this.nodes.get(parentPath);
110 if (parentNode) {
111 return parentNode;
112 }
113 return this.newNode(path.prev);
114 }
115 rewriteAndNormalizeError(err) {
116 if (this.rewriteError) {
117 const clonedError = Object.assign(Object.create(Object.getPrototypeOf(err)), err);
118 const rewrittenError = this.rewriteError(clonedError);
119 if (rewrittenError === null) {
120 return null;
121 }
122 if (!(rewrittenError instanceof graphql_1.GraphQLError)) {
123 return err;
124 }
125 return new graphql_1.GraphQLError(rewrittenError.message, err.nodes, err.source, err.positions, err.path, err.originalError, rewrittenError.extensions || err.extensions);
126 }
127 return err;
128 }
129}
130exports.TraceTreeBuilder = TraceTreeBuilder;
131function durationHrTimeToNanos(hrtime) {
132 return hrtime[0] * 1e9 + hrtime[1];
133}
134function responsePathAsString(p) {
135 if (p === undefined) {
136 return '';
137 }
138 let res = String(p.key);
139 while ((p = p.prev) !== undefined) {
140 res = `${p.key}.${res}`;
141 }
142 return res;
143}
144function errorToProtobufError(error) {
145 return new apollo_reporting_protobuf_1.Trace.Error({
146 message: error.message,
147 location: (error.locations || []).map(({ line, column }) => new apollo_reporting_protobuf_1.Trace.Location({ line, column })),
148 json: JSON.stringify(error),
149 });
150}
151function dateToProtoTimestamp(date) {
152 const totalMillis = +date;
153 const millis = totalMillis % 1000;
154 return new apollo_reporting_protobuf_1.google.protobuf.Timestamp({
155 seconds: (totalMillis - millis) / 1000,
156 nanos: millis * 1e6,
157 });
158}
159exports.dateToProtoTimestamp = dateToProtoTimestamp;
160//# sourceMappingURL=traceTreeBuilder.js.map
\No newline at end of file