diff --git a/packages/apollo-engine-reporting/src/__tests__/contextualizedStats.test.ts b/packages/apollo-engine-reporting/src/__tests__/contextualizedStats.test.ts index e82678913fe..45e96f3f283 100644 --- a/packages/apollo-engine-reporting/src/__tests__/contextualizedStats.test.ts +++ b/packages/apollo-engine-reporting/src/__tests__/contextualizedStats.test.ts @@ -1,42 +1,42 @@ -import { Trace } from 'apollo-engine-reporting-protobuf'; -import { dateToProtoTimestamp } from '../treeBuilder'; -import { ContextualizedStats } from '../contextualizedStats'; -import { DurationHistogram } from '../durationHistogram'; +import { Trace, TypeStat } from "apollo-engine-reporting-protobuf"; +import { dateToProtoTimestamp } from "../treeBuilder"; +import { ContextualizedStats } from "../contextualizedStats"; +import { DurationHistogram } from "../durationHistogram"; -describe('Check query latency stats when', () => { - const statsContext = { - clientReferenceId: 'reference', - clientVersion: 'version', - }; +const statsContext = { + clientReferenceId: "reference", + clientVersion: "version" +}; - const baseDate = new Date(); - const duration = 30 * 1000; - const nonFederatedTrace = new Trace({ - startTime: dateToProtoTimestamp(baseDate), - endTime: dateToProtoTimestamp(new Date(baseDate.getTime() + duration)), - durationNs: duration, - root: null, - signature: 'signature', - details: null, - }); - - it('adding a single trace', () => { +const baseDate = new Date(); +const duration = 30 * 1000; +const baseTrace = new Trace({ + startTime: dateToProtoTimestamp(baseDate), + endTime: dateToProtoTimestamp(new Date(baseDate.getTime() + duration)), + durationNs: duration, + root: null, + signature: "signature", + details: null +}); +// TODO: add a federated trace +describe("Check query latency stats when", () => { + it("adding a single trace", () => { const contextualizedStats = new ContextualizedStats(statsContext); - contextualizedStats.addTrace(nonFederatedTrace); + contextualizedStats.addTrace(baseTrace); expect(contextualizedStats.queryLatencyStats.requestCount).toBe(1); expect(contextualizedStats.queryLatencyStats.latencyCount).toStrictEqual( - new DurationHistogram().incrementDuration(duration), + new DurationHistogram().incrementDuration(duration) ); expect(contextualizedStats.queryLatencyStats.requestsWithErrorsCount).toBe( - 0, + 0 ); }); it('adding a fully cached trace', () => { const contextualizedStats = new ContextualizedStats(statsContext); contextualizedStats.addTrace( new Trace({ - ...nonFederatedTrace, - fullQueryCacheHit: true, + ...baseTrace, + fullQueryCacheHit: true }), ); expect(contextualizedStats.queryLatencyStats.requestCount).toBe(1); @@ -49,12 +49,12 @@ describe('Check query latency stats when', () => { const contextualizedStats = new ContextualizedStats(statsContext); contextualizedStats.addTrace( new Trace({ - ...nonFederatedTrace, + ...baseTrace, fullQueryCacheHit: false, cachePolicy: { scope: Trace.CachePolicy.Scope.PRIVATE, - maxAgeNs: 1000, - }, + maxAgeNs: 1000 + } }), ); expect(contextualizedStats.queryLatencyStats.requestCount).toBe(1); @@ -66,12 +66,12 @@ describe('Check query latency stats when', () => { const contextualizedStats = new ContextualizedStats(statsContext); contextualizedStats.addTrace( new Trace({ - ...nonFederatedTrace, + ...baseTrace, fullQueryCacheHit: false, cachePolicy: { scope: Trace.CachePolicy.Scope.PUBLIC, - maxAgeNs: 1000, - }, + maxAgeNs: 1000 + } }), ); expect(contextualizedStats.queryLatencyStats.requestCount).toBe(1); @@ -83,8 +83,8 @@ describe('Check query latency stats when', () => { const contextualizedStats = new ContextualizedStats(statsContext); contextualizedStats.addTrace( new Trace({ - ...nonFederatedTrace, - persistedQueryHit: true, + ...baseTrace, + persistedQueryHit: true }), ); expect(contextualizedStats.queryLatencyStats.requestCount).toBe(1); @@ -94,8 +94,8 @@ describe('Check query latency stats when', () => { const contextualizedStats = new ContextualizedStats(statsContext); contextualizedStats.addTrace( new Trace({ - ...nonFederatedTrace, - persistedQueryRegister: true, + ...baseTrace, + persistedQueryRegister: true }), ); expect(contextualizedStats.queryLatencyStats.requestCount).toBe(1); @@ -105,8 +105,8 @@ describe('Check query latency stats when', () => { const contextualizedStats = new ContextualizedStats(statsContext); contextualizedStats.addTrace( new Trace({ - ...nonFederatedTrace, - forbiddenOperation: true, + ...baseTrace, + forbiddenOperation: true }), ); expect(contextualizedStats.queryLatencyStats.requestCount).toBe(1); @@ -118,8 +118,8 @@ describe('Check query latency stats when', () => { const contextualizedStats = new ContextualizedStats(statsContext); contextualizedStats.addTrace( new Trace({ - ...nonFederatedTrace, - registeredOperation: true, + ...baseTrace, + registeredOperation: true }), ); expect(contextualizedStats.queryLatencyStats.requestCount).toBe(1); @@ -131,20 +131,20 @@ describe('Check query latency stats when', () => { const contextualizedStats = new ContextualizedStats(statsContext); contextualizedStats.addTrace( new Trace({ - ...nonFederatedTrace, + ...baseTrace, registeredOperation: true, root: { child: [ { - responseName: 'user', - parentType: 'Query', - type: 'User!', + responseName: "user", + parentType: "Query", + type: "User!", error: [ { - message: 'error 1', - }, - ], - }, + message: "error 1" + } + ] + } ], }, }), @@ -163,45 +163,45 @@ describe('Check query latency stats when', () => { const contextualizedStats = new ContextualizedStats(statsContext); contextualizedStats.addTrace( new Trace({ - ...nonFederatedTrace, + ...baseTrace, registeredOperation: true, root: { child: [ { - responseName: 'user', - parentType: 'Query', - type: 'User!', + responseName: "user", + parentType: "Query", + type: "User!", error: [ { - message: 'error 1', - }, - ], - }, + message: "error 1" + } + ] + } ], }, }), ); contextualizedStats.addTrace( new Trace({ - ...nonFederatedTrace, + ...baseTrace, registeredOperation: true, root: { child: [ { - responseName: 'account', - parentType: 'Query', - type: 'Account!', + responseName: "account", + parentType: "Query", + type: "Account!", child: [ { - responseName: 'name', - parentType: 'Account', - type: 'String!', + responseName: "name", + parentType: "Account", + type: "String!", error: [ { - message: 'has error', - }, - ], - }, + message: "has error" + } + ] + } ], }, ], @@ -211,25 +211,25 @@ describe('Check query latency stats when', () => { for (let _ in [1, 2]) { contextualizedStats.addTrace( new Trace({ - ...nonFederatedTrace, + ...baseTrace, registeredOperation: true, root: { child: [ { - responseName: 'user', - parentType: 'Query', - type: 'User!', + responseName: "user", + parentType: "Query", + type: "User!", child: [ { - responseName: 'email', - parentType: 'User', - type: 'String!', + responseName: "email", + parentType: "User", + type: "String!", error: [ { - message: 'has error', - }, - ], - }, + message: "has error" + } + ] + } ], }, ], @@ -274,33 +274,33 @@ describe('Check query latency stats when', () => { }); it('merging non-errored traces', () => { const contextualizedStats = new ContextualizedStats(statsContext); - contextualizedStats.addTrace(nonFederatedTrace); - contextualizedStats.addTrace(nonFederatedTrace); + contextualizedStats.addTrace(baseTrace); + contextualizedStats.addTrace(baseTrace); contextualizedStats.addTrace( new Trace({ - ...nonFederatedTrace, + ...baseTrace, fullQueryCacheHit: false, cachePolicy: { scope: Trace.CachePolicy.Scope.PRIVATE, - maxAgeNs: 1000, - }, - }), + maxAgeNs: 1000 + } + }) ); contextualizedStats.addTrace( new Trace({ - ...nonFederatedTrace, + ...baseTrace, fullQueryCacheHit: false, cachePolicy: { scope: Trace.CachePolicy.Scope.PRIVATE, - maxAgeNs: 1000, - }, + maxAgeNs: 1000 + } }), ); for (let _ in [1, 2]) { contextualizedStats.addTrace( new Trace({ - ...nonFederatedTrace, - fullQueryCacheHit: true, + ...baseTrace, + fullQueryCacheHit: true }), ); } @@ -322,11 +322,240 @@ describe('Check query latency stats when', () => { ); expect(contextualizedStats.queryLatencyStats.cacheHits).toBe(2); expect( - contextualizedStats.queryLatencyStats.cacheLatencyCount, + contextualizedStats.queryLatencyStats.cacheLatencyCount ).toStrictEqual( new DurationHistogram() .incrementDuration(duration) - .incrementDuration(duration), + .incrementDuration(duration) ); }); }); + +describe("Check type stats", () => { + const trace = new Trace({ + ...baseTrace, + registeredOperation: true, + root: { + child: [ + { + originalFieldName: "user", + responseName: "user", + parentType: "Query", + type: "User!", + startTime: 0, + endTime: 100 * 1000, + child: [{ + originalFieldName: "email", + responseName: "email", + parentType: "User", + type: "String!", + startTime: 1000, + endTime: 1005 + }, + { + originalFieldName: "friends", + responseName: "friends", + parentType: "User", + type: "[String!]!", + startTime: 1000, + endTime: 1005 + } + ] + } + ] + } + }); + + const federatedTrace = new Trace({ + ...baseTrace, + registeredOperation: true, + queryPlan: new Trace.QueryPlanNode({ + fetch: new Trace.QueryPlanNode.FetchNode({ + serviceName: "A" , + trace: trace, + sentTime: dateToProtoTimestamp(baseDate), + receivedTime: dateToProtoTimestamp(new Date(baseDate.getTime() + duration)), + + }); + }) + }); + + const errorTrace = new Trace({ + ...baseTrace, + registeredOperation: true, + root: { + child: [ + { + originalFieldName: "user", + responseName: "user", + parentType: "Query", + type: "User!", + startTime: 0, + endTime: 100 * 1000, + child: [{ + originalFieldName: "email", + responseName: "email", + parentType: "User", + type: "String!", + startTime: 1000, + endTime: 1005, + error: [{ message: "error message"}, {message: "error2"}], + }, + { + originalFieldName: "friends", + responseName: "friends", + parentType: "User", + type: "[String!]!", + startTime: 1000, + endTime: 1005 + } + ] + } + ] + } + }); + + it("add single non-federated trace", () => { + const contextualizedStats = new ContextualizedStats(statsContext); + contextualizedStats.addTrace(trace); + expect(contextualizedStats.perTypeStat).toEqual({ + User: new TypeStat({ + perFieldStat: { + "email": { + returnType: "String!", + errorsCount: 0, + count: 1, + requestsWithErrorsCount: 0, + latencyCount: new DurationHistogram().incrementDuration(5) + }, + "friends" : { + returnType: "[String!]!", + errorsCount: 0, + count: 1, + requestsWithErrorsCount: 0, + latencyCount: new DurationHistogram().incrementDuration(5) + } + } + }), + Query: new TypeStat({ + perFieldStat: { + user: { + returnType: "User!", + errorsCount: 0, + count: 1, + requestsWithErrorsCount: 0, + latencyCount: new DurationHistogram().incrementDuration(100*1000) + } + } + }) + }); + }); + it("add multiple non-federated trace", () => { + const contextualizedStats = new ContextualizedStats(statsContext); + contextualizedStats.addTrace(trace); + contextualizedStats.addTrace(trace); + expect(contextualizedStats.perTypeStat).toEqual({ + User: new TypeStat({ + perFieldStat: { + "email": { + returnType: "String!", + errorsCount: 0, + count: 2, + requestsWithErrorsCount: 0, + latencyCount: new DurationHistogram().incrementDuration(5).incrementDuration(5) + }, + "friends" : { + returnType: "[String!]!", + errorsCount: 0, + count: 2, + requestsWithErrorsCount: 0, + latencyCount: new DurationHistogram().incrementDuration(5).incrementDuration(5) + } + } + }), + Query: new TypeStat({ + perFieldStat: { + user: { + returnType: "User!", + errorsCount: 0, + count: 2, + requestsWithErrorsCount: 0, + latencyCount: new DurationHistogram().incrementDuration(100*1000).incrementDuration(100*1000) + } + } + }) + }); + }); + + it("add multiple federated trace", () => { + const contextualizedStats = new ContextualizedStats(statsContext); + contextualizedStats.addTrace(federatedTrace); + contextualizedStats.addTrace(federatedTrace); + expect(contextualizedStats.perTypeStat).toEqual({ + User: new TypeStat({ + perFieldStat: { + "email": { + returnType: "String!", + errorsCount: 0, + count: 2, + requestsWithErrorsCount: 0, + latencyCount: new DurationHistogram().incrementDuration(5).incrementDuration(5) + }, + "friends" : { + returnType: "[String!]!", + errorsCount: 0, + count: 2, + requestsWithErrorsCount: 0, + latencyCount: new DurationHistogram().incrementDuration(5).incrementDuration(5) + } + } + }), + Query: new TypeStat({ + perFieldStat: { + user: { + returnType: "User!", + errorsCount: 0, + count: 2, + requestsWithErrorsCount: 0, + latencyCount: new DurationHistogram().incrementDuration(100*1000).incrementDuration(100*1000) + } + } + }) + }); + }); + it("add multiple federated trace", () => { + const contextualizedStats = new ContextualizedStats(statsContext); + contextualizedStats.addTrace(errorTrace); + expect(contextualizedStats.perTypeStat).toEqual({ + User: new TypeStat({ + perFieldStat: { + "email": { + returnType: "String!", + errorsCount: 2, + count: 1, + requestsWithErrorsCount: 1, + latencyCount: new DurationHistogram().incrementDuration(5) + }, + "friends" : { + returnType: "[String!]!", + errorsCount: 0, + count: 1, + requestsWithErrorsCount: 0, + latencyCount: new DurationHistogram().incrementDuration(5) + } + } + }), + Query: new TypeStat({ + perFieldStat: { + user: { + returnType: "User!", + errorsCount: 0, + count: 1, + requestsWithErrorsCount: 0, + latencyCount: new DurationHistogram().incrementDuration(100*1000) + } + } + }) + }); + }); +}); diff --git a/packages/apollo-engine-reporting/src/contextualizedStats.ts b/packages/apollo-engine-reporting/src/contextualizedStats.ts index 2182c73806c..70cbf7f5abf 100644 --- a/packages/apollo-engine-reporting/src/contextualizedStats.ts +++ b/packages/apollo-engine-reporting/src/contextualizedStats.ts @@ -109,11 +109,11 @@ export class ContextualizedStats { } if ( - node.parentType && - node.originalFieldName && - node.type && - node.endTime && - node.startTime + node.parentType != null&& + node.originalFieldName != null && + node.type != null && + node.endTime != null && + node.startTime != null ) { let typeStat = typeStats[node.parentType]; if (!typeStat) {