diff --git a/CHANGELOG.md b/CHANGELOG.md index f5afd3b5352..b3a7cca6552 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ All of the packages in the `apollo-server` repo are released with the same versi ### vNEXT - Update link inside Authentication Docs [PR #1682](https://github.com/apollographql/apollo-server/pull/1682) +- Provide ability to specify client info in traces [#1631](https://github.com/apollographql/apollo-server/pull/1631) ### v2.0.8 diff --git a/docs/source/api/apollo-server.md b/docs/source/api/apollo-server.md index b0db2db34aa..40103674e79 100644 --- a/docs/source/api/apollo-server.md +++ b/docs/source/api/apollo-server.md @@ -350,3 +350,13 @@ addMockFunctionsToSchema({ * `maskErrorDetails`: boolean Set to true to remove error details from the traces sent to Apollo's servers. Defaults to false. + +* generateClientInfo?: (o: { context: any, extensions?: Record}) => ClientInfo; + + Creates the client information that is attached to the traces sent to the + Apollo backend. The context field is the execution context passed to the + resolvers and the extensions field corresponds to the same value in the POST + body or GET parameters. `ClientInfo` contains fields for `clientName` and + `clientVersion`, which are both optional. The default generation copies the + respective fields from `extensions.clientInfo`. If `clientName` or + `clientVersion` is not present, the values are set to the empty string. diff --git a/packages/apollo-engine-reporting/src/agent.ts b/packages/apollo-engine-reporting/src/agent.ts index 379ab085597..ea5b9b17a3d 100644 --- a/packages/apollo-engine-reporting/src/agent.ts +++ b/packages/apollo-engine-reporting/src/agent.ts @@ -34,6 +34,11 @@ Traces.encode = function(message, originalWriter) { return writer; }; +export interface ClientInfo { + clientName?: string; + clientVersion?: string; +} + export interface EngineReportingOptions { // API key for the service. Get this from // [Engine](https://engine.apollographql.com) by logging in and creating @@ -83,6 +88,14 @@ export interface EngineReportingOptions { sendReportsImmediately?: boolean; // To remove the error message from traces, set this to true. Defaults to false maskErrorDetails?: boolean; + // Creates the client information attached to the traces sent to the Apollo + // backend + generateClientInfo?: ( + o: { + context: any; + extensions?: Record; + }, + ) => ClientInfo; // XXX Provide a way to set client_name, client_version, client_address, // service, and service_version fields. They are currently not revealed in the diff --git a/packages/apollo-engine-reporting/src/extension.ts b/packages/apollo-engine-reporting/src/extension.ts index 624985dab0c..bf9b992d569 100644 --- a/packages/apollo-engine-reporting/src/extension.ts +++ b/packages/apollo-engine-reporting/src/extension.ts @@ -15,7 +15,7 @@ import { } from 'graphql-extensions'; import { Trace, google } from 'apollo-engine-reporting-protobuf'; -import { EngineReportingOptions } from './agent'; +import { EngineReportingOptions, ClientInfo } from './agent'; import { defaultSignature } from './signature'; // EngineReportingExtension is the per-request GraphQLExtension which creates a @@ -38,6 +38,12 @@ export class EngineReportingExtension operationName: string, trace: Trace, ) => void; + private generateClientInfo: ( + o: { + context: any; + extensions?: Record; + }, + ) => ClientInfo; public constructor( options: EngineReportingOptions, @@ -51,6 +57,11 @@ export class EngineReportingExtension const root = new Trace.Node(); this.trace.root = root; this.nodes.set(responsePathAsString(undefined), root); + this.generateClientInfo = + options.generateClientInfo || + // Default to using the clientInfo field of the request's extensions, when + // the ClientInfo fields are undefined, we send the empty string + (({ extensions }) => (extensions && extensions.clientInfo) || {}); } public requestDidStart(o: { @@ -60,6 +71,8 @@ export class EngineReportingExtension variables?: Record; persistedQueryHit?: boolean; persistedQueryRegister?: boolean; + context: any; + extensions?: Record; }): EndHandler { this.trace.startTime = dateToTimestamp(new Date()); this.startHrTime = process.hrtime(); @@ -154,6 +167,16 @@ export class EngineReportingExtension }); } + // While clientAddress could be a part of the protobuf, we'll ignore it for + // now, since the backend does not group by it and Engine frontend will not + // support it in the short term + const { clientName, clientVersion } = this.generateClientInfo({ + context: o.context, + extensions: o.extensions, + }); + this.trace.clientName = clientName || ''; + this.trace.clientVersion = clientVersion || ''; + return () => { this.trace.durationNs = durationHrTimeToNanos( process.hrtime(this.startHrTime), diff --git a/packages/apollo-server-core/src/runHttpQuery.ts b/packages/apollo-server-core/src/runHttpQuery.ts index 08964abd6aa..e49d1a7b7ad 100644 --- a/packages/apollo-server-core/src/runHttpQuery.ts +++ b/packages/apollo-server-core/src/runHttpQuery.ts @@ -402,6 +402,7 @@ export async function runHttpQuery( : false, request: request.request, extensions: optionsObject.extensions, + queryExtensions: extensions, persistedQueryHit, persistedQueryRegister, }; diff --git a/packages/apollo-server-core/src/runQuery.ts b/packages/apollo-server-core/src/runQuery.ts index 516750dfd78..4eabf053a36 100644 --- a/packages/apollo-server-core/src/runQuery.ts +++ b/packages/apollo-server-core/src/runQuery.ts @@ -66,6 +66,7 @@ export interface QueryOptions { cacheControl?: boolean | CacheControlExtensionOptions; request: Pick; extensions?: Array<() => GraphQLExtension>; + queryExtensions?: Record; persistedQueryHit?: boolean; persistedQueryRegister?: boolean; } @@ -136,6 +137,7 @@ function doRunQuery(options: QueryOptions): Promise { persistedQueryHit: options.persistedQueryHit, persistedQueryRegister: options.persistedQueryRegister, context, + extensions: options.queryExtensions, }); return Promise.resolve() .then( diff --git a/packages/graphql-extensions/src/index.ts b/packages/graphql-extensions/src/index.ts index b7f358f6da8..b4455109da6 100644 --- a/packages/graphql-extensions/src/index.ts +++ b/packages/graphql-extensions/src/index.ts @@ -81,6 +81,7 @@ export class GraphQLExtensionStack { persistedQueryHit?: boolean; persistedQueryRegister?: boolean; context: TContext; + extensions?: Record; }): EndHandler { return this.handleDidStart( ext => ext.requestDidStart && ext.requestDidStart(o),