From 8cc77054f8751a6efe6046e8bb1b7ed6297e5b0c Mon Sep 17 00:00:00 2001 From: Ayan Khan Date: Wed, 20 Mar 2024 16:54:58 -0400 Subject: [PATCH] fix apollo gateway get signature method (#4174) * fix apollo gateway get signature method & add checks for config values --- index.d.ts | 21 +++++++++++- .../src/gateway/request.js | 4 +-- .../datadog-plugin-apollo/test/index.spec.js | 34 ++++++++----------- 3 files changed, 37 insertions(+), 22 deletions(-) diff --git a/index.d.ts b/index.d.ts index dc35e30e1f5..2a7541331a0 100644 --- a/index.d.ts +++ b/index.d.ts @@ -1124,7 +1124,26 @@ declare namespace tracer { * This module uses graphql operations to service requests & thus generates graphql spans. * We recommend disabling the graphql plugin if you only want to trace @apollo/gateway */ - interface apollo extends Instrumentation {} + interface apollo extends Instrumentation { + /** + * Whether to include the source of the operation within the query as a tag + * on every span. This may contain sensitive information and should only be + * enabled if sensitive data is always sent as variables and not in the + * query text. + * + * @default false + */ + source?: boolean; + + /** + * Whether to enable signature calculation for the resource name. This can + * be disabled if your apollo/gateway operations always have a name. Note that when + * disabled all queries will need to be named for this to work properly. + * + * @default true + */ + signature?: boolean; + } /** * This plugin automatically instruments the diff --git a/packages/datadog-plugin-apollo/src/gateway/request.js b/packages/datadog-plugin-apollo/src/gateway/request.js index 5594fc969ff..0044b57f760 100644 --- a/packages/datadog-plugin-apollo/src/gateway/request.js +++ b/packages/datadog-plugin-apollo/src/gateway/request.js @@ -42,7 +42,7 @@ class ApolloGatewayRequestPlugin extends ApolloBasePlugin { if (requestContext?.operationName) { spanData.meta['graphql.operation.name'] = requestContext.operationName } - if (gateway?.config?.telemetry?.includeDocument !== false && requestContext?.source) { + if ((this.config.source || gateway?.config?.telemetry?.includeDocument) && requestContext?.source) { spanData.meta['graphql.source'] = requestContext.source } @@ -121,7 +121,7 @@ function getSignature (document, operationName, operationType, calculate) { if (calculate !== false && tools !== false) { try { try { - tools = tools || require('./tools') + tools = tools || require('../../../datadog-plugin-graphql/src/tools') } catch (e) { tools = false throw e diff --git a/packages/datadog-plugin-apollo/test/index.spec.js b/packages/datadog-plugin-apollo/test/index.spec.js index 63ed1063797..91986782af5 100644 --- a/packages/datadog-plugin-apollo/test/index.spec.js +++ b/packages/datadog-plugin-apollo/test/index.spec.js @@ -134,19 +134,18 @@ describe('Plugin', () => { }) it('should instrument apollo/gateway', done => { const operationName = 'MyQuery' - const resource = `query ${operationName}` - const source = `${resource} { hello(name: "world") }` + const source = `query ${operationName} { hello(name: "world") }` const variableValues = { who: 'world' } agent .use((traces) => { // the spans are in order of execution expect(traces[0][0]).to.have.property('name', expectedSchema.server.opName) expect(traces[0][0]).to.have.property('service', expectedSchema.server.serviceName) - expect(traces[0][0]).to.have.property('resource', resource) + expect(traces[0][0]).to.have.property('resource', 'query MyQuery{hello(name:"")}') expect(traces[0][0]).to.have.property('type', 'web') expect(traces[0][0]).to.have.property('error', 0) expect(traces[0][0].meta).to.have.property('graphql.operation.name', operationName) - expect(traces[0][0].meta).to.have.property('graphql.source', source) + expect(traces[0][0].meta).to.not.have.property('graphql.source') expect(traces[0][0].meta).to.have.property('graphql.operation.type', 'query') expect(traces[0][0].meta).to.have.property('component', 'apollo.gateway') @@ -196,10 +195,10 @@ describe('Plugin', () => { .use((traces) => { expect(traces[0][0]).to.have.property('name', expectedSchema.server.opName) expect(traces[0][0]).to.have.property('service', expectedSchema.server.serviceName) - expect(traces[0][0]).to.have.property('resource', 'query') + expect(traces[0][0]).to.have.property('resource', '{hello(name:"")}') expect(traces[0][0]).to.have.property('type', 'web') expect(traces[0][0]).to.have.property('error', 0) - expect(traces[0][0].meta).to.have.property('graphql.source', source) + expect(traces[0][0].meta).to.not.have.property('graphql.source') expect(traces[0][0].meta).to.have.property('graphql.operation.type', 'query') expect(traces[0][0].meta).to.have.property('component', 'apollo.gateway') }) @@ -228,10 +227,10 @@ describe('Plugin', () => { .use((traces) => { expect(traces[0][0]).to.have.property('name', expectedSchema.server.opName) expect(traces[0][0]).to.have.property('service', expectedSchema.server.serviceName) - expect(traces[0][0]).to.have.property('resource', 'query') + expect(traces[0][0]).to.have.property('resource', '{human{address{civicNumber street}name}}') expect(traces[0][0]).to.have.property('type', 'web') expect(traces[0][0]).to.have.property('error', 0) - expect(traces[0][0].meta).to.have.property('graphql.source', source) + expect(traces[0][0].meta).to.not.have.property('graphql.source') expect(traces[0][0].meta).to.have.property('graphql.operation.type', 'query') expect(traces[0][0].meta).to.have.property('component', 'apollo.gateway') }) @@ -315,8 +314,7 @@ describe('Plugin', () => { it('should instrument plan failure', done => { let error const operationName = 'MyQuery' - const resource = `subscription ${operationName}` - const source = `${resource} { hello(name: "world") }` + const source = `subscription ${operationName} { hello(name: "world") }` const variableValues = { who: 'world' } agent .use((traces) => { @@ -350,8 +348,7 @@ describe('Plugin', () => { it('should instrument fetch failure', done => { let error const operationName = 'MyQuery' - const resource = `query ${operationName}` - const source = `${resource} { hello(name: "world") }` + const source = `query ${operationName} { hello(name: "world") }` const variableValues = { who: 'world' } agent .use((traces) => { @@ -410,8 +407,7 @@ describe('Plugin', () => { it('should run spans in the correct context', done => { const operationName = 'MyQuery' - const resource = `query ${operationName}` - const source = `${resource} { hello(name: "world") }` + const source = `query ${operationName} { hello(name: "world") }` const variableValues = { who: 'world' } agent @@ -446,8 +442,7 @@ describe('Plugin', () => { withNamingSchema( () => { const operationName = 'MyQuery' - const resource = `query ${operationName}` - const source = `${resource} { hello(name: "world") }` + const source = `query ${operationName} { hello(name: "world") }` const variableValues = { who: 'world' } gateway() .then(({ executor }) => { @@ -464,18 +459,19 @@ describe('Plugin', () => { describe('with configuration', () => { before(() => { - return agent.load('apollo', { service: 'custom' }) + return agent.load('apollo', { service: 'custom', source: true, signature: false }) }) it('should be configured with the correct values', done => { const operationName = 'MyQuery' - const resource = `query ${operationName}` - const source = `${resource} { hello(name: "world") }` + const source = `query ${operationName} { hello(name: "world") }` const variableValues = { who: 'world' } agent .use((traces) => { expect(traces[0][0]).to.have.property('name', expectedSchema.server.opName) expect(traces[0][0]).to.have.property('service', 'custom') + expect(traces[0][0]).to.have.property('resource', `query ${operationName}`) + expect(traces[0][0].meta).to.have.property('graphql.source', source) expect(traces[0][1]).to.have.property('name', 'apollo.gateway.validate') expect(traces[0][1]).to.have.property('service', 'custom')