Skip to content

Commit 72844ef

Browse files
authored
fix(datastreams): preserve extracted schema field descriptions (#7838)
Populate extracted schema property descriptions from Avro field docs and protobufjs field comments so shipped schema metadata matches the source definitions. Add regression tests and clean up related test/helper issues surfaced by stricter linting.
1 parent df17db8 commit 72844ef

17 files changed

Lines changed: 158 additions & 80 deletions

File tree

benchmark/e2e-test-optimization/benchmark-run.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,8 @@ const parseGitHubJsonResponse = ({ body, endpoint, res }) => {
3636
return JSON.parse(body)
3737
} catch (e) {
3838
throw new Error(
39-
`GitHub API ${endpoint} returned invalid JSON. Body preview: ${getResponsePreview(body)}`
39+
`GitHub API ${endpoint} returned invalid JSON. Body preview: ${getResponsePreview(body)}`,
40+
{ cause: e }
4041
)
4142
}
4243
}

integration-tests/ci-visibility-intake.js

Lines changed: 4 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -351,7 +351,7 @@ class FakeCiVisIntake extends FakeAgent {
351351
this.server = http.createServer(app)
352352
this.server.on('error', reject)
353353
this.server.listen(this.port, () => {
354-
this.port = this.server.address().port
354+
this.port = (/** @type {import('net').AddressInfo} */ (this.server.address())).port
355355
clearTimeout(timeoutObj)
356356
resolve(this)
357357
})
@@ -473,7 +473,7 @@ class FakeCiVisIntake extends FakeAgent {
473473
// always be faster or as fast as gatherPayloads
474474
gatherPayloadsMaxTimeout (payloadMatch, onPayload, maxGatheringTime = 15000) {
475475
const payloads = []
476-
return new Promise((resolve, reject) => {
476+
return /** @type {Promise<void>} */ (new Promise((resolve, reject) => {
477477
const timeoutId = setTimeout(() => {
478478
try {
479479
onPayload(payloads)
@@ -499,7 +499,7 @@ class FakeCiVisIntake extends FakeAgent {
499499
}
500500
}
501501
this.on('message', messageHandler)
502-
})
502+
}))
503503
}
504504

505505
gatherPayloads (payloadMatch, gatheringTime = 15000) {
@@ -540,37 +540,7 @@ class FakeCiVisIntake extends FakeAgent {
540540
}
541541

542542
assertPayloadReceived (fn, messageMatch, timeout) {
543-
let resultResolve
544-
let resultReject
545-
let error
546-
547-
const timeoutObj = setTimeout(() => {
548-
resultReject([error, new Error('timeout')])
549-
}, timeout || 15000)
550-
551-
const messageHandler = (message) => {
552-
if (!messageMatch || messageMatch(message)) {
553-
try {
554-
fn(message)
555-
resultResolve()
556-
} catch (e) {
557-
resultReject(e)
558-
}
559-
this.off('message', messageHandler)
560-
}
561-
}
562-
this.on('message', messageHandler)
563-
564-
return new Promise((resolve, reject) => {
565-
resultResolve = () => {
566-
clearTimeout(timeoutObj)
567-
resolve()
568-
}
569-
resultReject = (e) => {
570-
clearTimeout(timeoutObj)
571-
reject(e)
572-
}
573-
})
543+
return this.payloadReceived(messageMatch, timeout).then(fn)
574544
}
575545
}
576546

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
'use strict'
2+
3+
const assert = require('node:assert/strict')
4+
5+
const { describe, it } = require('mocha')
6+
7+
const { FakeCiVisIntake } = require('../ci-visibility-intake')
8+
9+
describe('FakeCiVisIntake', () => {
10+
it('should reject with a timeout error and remove the listener', async () => {
11+
const intake = new FakeCiVisIntake()
12+
13+
await assert.rejects(
14+
intake.assertPayloadReceived(() => {}, undefined, 5),
15+
{
16+
message: 'Timeout',
17+
}
18+
)
19+
assert.strictEqual(intake.listenerCount('message'), 0)
20+
})
21+
22+
it('should resolve when a matching payload satisfies the assertion', async () => {
23+
const intake = new FakeCiVisIntake()
24+
const payloadPromise = intake.assertPayloadReceived(({ payload }) => {
25+
assert.deepStrictEqual(payload, { ok: true })
26+
}, ({ url }) => url === '/expected')
27+
28+
intake.emit('message', {
29+
payload: { ok: true },
30+
url: '/expected',
31+
})
32+
33+
await payloadPromise
34+
assert.strictEqual(intake.listenerCount('message'), 0)
35+
})
36+
})

packages/datadog-plugin-avsc/src/schema_iterator.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,9 +43,9 @@ class SchemaExtractor {
4343
let type
4444
let format
4545
let enumValues
46-
let description
4746
let ref
4847

48+
const description = field.doc
4949
const fieldType = field.type?.types ?? field.type?.typeName ?? field.type
5050

5151
if (Array.isArray(fieldType)) {
@@ -129,7 +129,7 @@ class SchemaExtractor {
129129
}
130130

131131
iterateOverSchema (builder) {
132-
this.constructor.extractSchema(this.schema, builder, 0)
132+
SchemaExtractor.extractSchema(this.schema, builder, 0)
133133
}
134134

135135
static attachSchemaOnSpan (args, span, operation, tracer) {

packages/datadog-plugin-avsc/test/index.spec.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ const ADVANCED_USER_SCHEMA_DEF = JSON.parse(
2727
fs.readFileSync(path.join(__dirname, 'schemas/expected_advanced_user_schema.json'), 'utf8')
2828
)
2929

30-
const BASIC_USER_SCHEMA_ID = '1605040621379664412'
30+
const BASIC_USER_SCHEMA_ID = '15683462889181473629'
3131
const ADVANCED_USER_SCHEMA_ID = '919692610494986520'
3232
function compareJson (expected, span) {
3333
const actual = JSON.parse(span.context().getTag(SCHEMA_DEFINITION))
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
'use strict'
2+
3+
const assert = require('node:assert/strict')
4+
5+
const { describe, it } = require('mocha')
6+
7+
const { SchemaBuilder } = require('../../dd-trace/src/datastreams/schemas/schema_builder')
8+
const SchemaExtractor = require('../src/schema_iterator')
9+
10+
describe('SchemaExtractor', () => {
11+
describe('avsc', () => {
12+
it('should include field docs in the extracted schema', () => {
13+
const schema = {
14+
name: 'UserWithDocs',
15+
fields: [
16+
{
17+
name: 'name',
18+
type: 'string',
19+
doc: 'The user name',
20+
},
21+
],
22+
}
23+
24+
const schemaData = SchemaBuilder.getSchemaDefinition(
25+
new SchemaBuilder(new SchemaExtractor(schema)).build()
26+
)
27+
const property = JSON.parse(schemaData.definition).components.schemas.UserWithDocs.properties.name
28+
29+
assert.deepStrictEqual(property, {
30+
description: 'The user name',
31+
type: 'string',
32+
})
33+
})
34+
})
35+
})

packages/datadog-plugin-avsc/test/schemas/expected_user_schema.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,8 @@
66
"type": "object",
77
"properties": {
88
"name": {
9-
"type": "string"
9+
"type": "string",
10+
"description": "The user name"
1011
},
1112
"favorite_number": {
1213
"type": "union[integer,null]"

packages/datadog-plugin-avsc/test/schemas/user.avsc

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,8 @@
55
"fields": [
66
{
77
"name": "name",
8-
"type": "string"
8+
"type": "string",
9+
"doc": "The user name"
910
},
1011
{
1112
"name": "favorite_number",

packages/datadog-plugin-confluentinc-kafka-javascript/test/index.spec.js

Lines changed: 20 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -173,27 +173,30 @@ describe('Plugin', () => {
173173
])
174174
})
175175

176-
it('should run the consumer in the context of the consumer span', done => {
176+
it('should run the consumer in the context of the consumer span', async () => {
177177
const firstSpan = tracer.scope().active()
178-
let consumerReceiveMessagePromise
179-
let eachMessage = async ({ topic, partition, message }) => {
180-
const currentSpan = tracer.scope().active()
178+
let hasReceivedMessage = false
179+
const consumerReceiveMessagePromise = /** @type {Promise<void>} */(new Promise((resolve, reject) => {
180+
const eachMessage = async () => {
181+
if (hasReceivedMessage) return
181182

182-
try {
183-
assert.notStrictEqual(currentSpan, firstSpan)
184-
assert.strictEqual(currentSpan.context()._name, expectedSchema.receive.opName)
185-
eachMessage = async () => {} // avoid being called for each message
186-
done()
187-
} catch (e) {
188-
eachMessage = async () => {}
189-
done(e)
183+
hasReceivedMessage = true
184+
const currentSpan = tracer.scope().active()
185+
186+
try {
187+
assert.notStrictEqual(currentSpan, firstSpan)
188+
assert.strictEqual(currentSpan.context()._name, expectedSchema.receive.opName)
189+
resolve()
190+
} catch (e) {
191+
reject(e)
192+
}
190193
}
191-
}
192194

193-
consumer.run({ eachMessage: (...args) => eachMessage(...args) })
194-
.then(() => sendMessages(kafka, testTopic, messages))
195-
.then(() => consumerReceiveMessagePromise)
196-
.catch(done)
195+
consumer.run({ eachMessage }).catch(reject)
196+
}))
197+
198+
await sendMessages(kafka, testTopic, messages)
199+
await consumerReceiveMessagePromise
197200
})
198201

199202
it('should propagate context', async () => {

packages/datadog-plugin-express-mongo-sanitize/test/integration-test/client.spec.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ withVersions('express-mongo-sanitize', 'express-mongo-sanitize', version => {
3434

3535
for (const variant of varySandbox.VARIANTS) {
3636
it(`is instrumented loaded with ${variant}`, async () => {
37-
const proc = await spawnPluginIntegrationTestProc(sandboxCwd(), variants[variant], agent.port)
37+
proc = await spawnPluginIntegrationTestProc(sandboxCwd(), variants[variant], agent.port)
3838
const response = await axios.get(`${proc.url}/?param=paramvalue`)
3939
assert.equal(response.headers['x-counter'], '1')
4040
})

0 commit comments

Comments
 (0)