dd-trace 3.0.0 → 3.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE-3rdparty.csv +1 -0
- package/MIGRATING.md +6 -6
- package/package.json +2 -1
- package/packages/datadog-core/src/storage/async_resource.js +19 -1
- package/packages/datadog-instrumentations/src/cucumber.js +15 -0
- package/packages/datadog-instrumentations/src/helpers/instrumentations.js +5 -1
- package/packages/datadog-instrumentations/src/jest.js +33 -11
- package/packages/datadog-plugin-cucumber/src/index.js +4 -0
- package/packages/datadog-plugin-jest/src/index.js +25 -4
- package/packages/datadog-plugin-mongodb-core/src/index.js +21 -6
- package/packages/datadog-plugin-oracledb/src/index.js +12 -4
- package/packages/dd-trace/index.js +1 -1
- package/packages/dd-trace/src/ci-visibility/exporters/agentless/coverage-writer.js +50 -0
- package/packages/dd-trace/src/ci-visibility/exporters/agentless/index.js +53 -8
- package/packages/dd-trace/src/ci-visibility/exporters/agentless/writer.js +23 -24
- package/packages/dd-trace/src/config.js +7 -0
- package/packages/dd-trace/src/encode/0.4.js +51 -58
- package/packages/dd-trace/src/encode/agentless-ci-visibility.js +13 -34
- package/packages/dd-trace/src/encode/coverage-ci-visibility.js +84 -0
- package/packages/dd-trace/src/exporters/agent/index.js +13 -7
- package/packages/dd-trace/src/exporters/agent/writer.js +1 -1
- package/packages/dd-trace/src/exporters/common/request.js +20 -9
- package/packages/dd-trace/src/exporters/common/writer.js +9 -6
- package/packages/dd-trace/src/index.js +10 -0
- package/packages/dd-trace/src/noop/proxy.js +77 -0
- package/packages/dd-trace/src/plugin_manager.js +6 -4
- package/packages/dd-trace/src/proxy.js +5 -62
- package/packages/dd-trace/src/telemetry.js +1 -1
package/LICENSE-3rdparty.csv
CHANGED
|
@@ -7,6 +7,7 @@ require,crypto-randomuuid,MIT,Copyright 2021 Node.js Foundation and contributors
|
|
|
7
7
|
require,diagnostics_channel,MIT,Copyright 2021 Simon D.
|
|
8
8
|
require,ignore,MIT,Copyright 2013 Kael Zhang and contributors
|
|
9
9
|
require,import-in-the-middle,Apache license 2.0,Copyright 2021 Datadog Inc.
|
|
10
|
+
require,istanbul-lib-coverage,BSD-3-Clause,Copyright 2012-2015 Yahoo! Inc.
|
|
10
11
|
require,koalas,MIT,Copyright 2013-2017 Brian Woodward
|
|
11
12
|
require,limiter,MIT,Copyright 2011 John Hurliman
|
|
12
13
|
require,lodash.kebabcase,MIT,Copyright JS Foundation and other contributors
|
package/MIGRATING.md
CHANGED
|
@@ -108,20 +108,20 @@ respectively.
|
|
|
108
108
|
|
|
109
109
|
The use of `'dd-trace/ci/jest/env'` in [`testEnvironment`](https://jestjs.io/docs/configuration#testenvironment-string)
|
|
110
110
|
is no longer supported.
|
|
111
|
-
|
|
112
|
-
|
|
111
|
+
To instrument `jest` tests now, add `'-r dd-trace/ci/init'` to the `NODE_OPTIONS` environment
|
|
112
|
+
variable passed to the process running the tests, for example, `NODE_OPTIONS='-r dd-trace/ci/init' yarn test`.
|
|
113
113
|
|
|
114
114
|
#### Mocha
|
|
115
115
|
|
|
116
116
|
The use of `--require dd-trace/ci/init` as a `mocha` flag is no longer supported.
|
|
117
|
-
|
|
118
|
-
|
|
117
|
+
To instrument `mocha` tests now, add `'-r dd-trace/ci/init'` to the `NODE_OPTIONS` environment
|
|
118
|
+
variable passed to the process running the tests, for example, `NODE_OPTIONS='-r dd-trace/ci/init' yarn test`.
|
|
119
119
|
|
|
120
120
|
#### Cucumber
|
|
121
121
|
|
|
122
122
|
The use of `--require-module dd-trace/ci/init` as a `cucumber-js` flag is no longer supported.
|
|
123
|
-
|
|
124
|
-
|
|
123
|
+
To instrument `cucumber-js` tests now, add `'-r dd-trace/ci/init'` to the `NODE_OPTIONS` environment
|
|
124
|
+
variable passed to the process running the tests, for example, `NODE_OPTIONS='-r dd-trace/ci/init' yarn test`.
|
|
125
125
|
|
|
126
126
|
## 1.0 to 2.0
|
|
127
127
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dd-trace",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.1.0",
|
|
4
4
|
"description": "Datadog APM tracing client for JavaScript",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"typings": "index.d.ts",
|
|
@@ -66,6 +66,7 @@
|
|
|
66
66
|
"diagnostics_channel": "^1.1.0",
|
|
67
67
|
"ignore": "^5.2.0",
|
|
68
68
|
"import-in-the-middle": "^1.3.0",
|
|
69
|
+
"istanbul-lib-coverage": "3.2.0",
|
|
69
70
|
"koalas": "^1.0.2",
|
|
70
71
|
"limiter": "^1.1.4",
|
|
71
72
|
"lodash.kebabcase": "^4.1.1",
|
|
@@ -6,9 +6,27 @@ const { channel } = require('diagnostics_channel')
|
|
|
6
6
|
const beforeCh = channel('dd-trace:storage:before')
|
|
7
7
|
const afterCh = channel('dd-trace:storage:after')
|
|
8
8
|
|
|
9
|
+
let PrivateSymbol = Symbol
|
|
10
|
+
function makePrivateSymbol () {
|
|
11
|
+
// eslint-disable-next-line no-new-func
|
|
12
|
+
PrivateSymbol = new Function('name', 'return %CreatePrivateSymbol(name)')
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
try {
|
|
16
|
+
makePrivateSymbol()
|
|
17
|
+
} catch (e) {
|
|
18
|
+
try {
|
|
19
|
+
const v8 = require('v8')
|
|
20
|
+
v8.setFlagsFromString('--allow-natives-syntax')
|
|
21
|
+
makePrivateSymbol()
|
|
22
|
+
v8.setFlagsFromString('--no-allow-natives-syntax')
|
|
23
|
+
// eslint-disable-next-line no-empty
|
|
24
|
+
} catch (e) {}
|
|
25
|
+
}
|
|
26
|
+
|
|
9
27
|
class AsyncResourceStorage {
|
|
10
28
|
constructor () {
|
|
11
|
-
this._ddResourceStore =
|
|
29
|
+
this._ddResourceStore = PrivateSymbol('ddResourceStore')
|
|
12
30
|
this._enabled = false
|
|
13
31
|
this._hook = createHook(this._createHook())
|
|
14
32
|
}
|
|
@@ -7,6 +7,7 @@ const runStartCh = channel('ci:cucumber:run:start')
|
|
|
7
7
|
const runFinishCh = channel('ci:cucumber:run:finish')
|
|
8
8
|
const runStepStartCh = channel('ci:cucumber:run-step:start')
|
|
9
9
|
const errorCh = channel('ci:cucumber:error')
|
|
10
|
+
const sessionFinishCh = channel('ci:cucumber:session:finish')
|
|
10
11
|
|
|
11
12
|
// TODO: remove in a later major version
|
|
12
13
|
const patched = new WeakSet()
|
|
@@ -128,4 +129,18 @@ addHook({
|
|
|
128
129
|
file: 'lib/runtime/test_case_runner.js'
|
|
129
130
|
}, testCaseHook)
|
|
130
131
|
|
|
132
|
+
addHook({
|
|
133
|
+
name: '@cucumber/cucumber',
|
|
134
|
+
versions: ['>=7.0.0'],
|
|
135
|
+
file: 'lib/runtime/index.js'
|
|
136
|
+
}, (Runtime) => {
|
|
137
|
+
shimmer.wrap(Runtime.default.prototype, 'start', start => async function () {
|
|
138
|
+
const result = await start.apply(this, arguments)
|
|
139
|
+
sessionFinishCh.publish(undefined)
|
|
140
|
+
return result
|
|
141
|
+
})
|
|
142
|
+
|
|
143
|
+
return Runtime
|
|
144
|
+
})
|
|
145
|
+
|
|
131
146
|
module.exports = { pickleHook, testCaseHook }
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
'use strict'
|
|
2
|
-
|
|
2
|
+
const istanbul = require('istanbul-lib-coverage')
|
|
3
3
|
const { addHook, channel, AsyncResource } = require('./helpers/instrument')
|
|
4
4
|
const shimmer = require('../../datadog-shimmer')
|
|
5
5
|
|
|
@@ -7,7 +7,8 @@ const testStartCh = channel('ci:jest:test:start')
|
|
|
7
7
|
const testSkippedCh = channel('ci:jest:test:skip')
|
|
8
8
|
const testRunFinishCh = channel('ci:jest:test:finish')
|
|
9
9
|
const testErrCh = channel('ci:jest:test:err')
|
|
10
|
-
|
|
10
|
+
|
|
11
|
+
const testCodeCoverageCh = channel('ci:jest:test:code-coverage')
|
|
11
12
|
|
|
12
13
|
const {
|
|
13
14
|
getTestSuitePath,
|
|
@@ -16,6 +17,24 @@ const {
|
|
|
16
17
|
|
|
17
18
|
const { getFormattedJestTestParameters, getJestTestName } = require('../../datadog-plugin-jest/src/util')
|
|
18
19
|
|
|
20
|
+
// This function also resets the coverage counters
|
|
21
|
+
function extractCoverageInformation (coverage, rootDir) {
|
|
22
|
+
const coverageMap = istanbul.createCoverageMap(coverage)
|
|
23
|
+
|
|
24
|
+
return coverageMap
|
|
25
|
+
.files()
|
|
26
|
+
.filter(filename => {
|
|
27
|
+
const fileCoverage = coverageMap.fileCoverageFor(filename)
|
|
28
|
+
const lineCoverage = fileCoverage.getLineCoverage()
|
|
29
|
+
const isAnyLineExecuted = Object.entries(lineCoverage).some(([, numExecutions]) => !!numExecutions)
|
|
30
|
+
|
|
31
|
+
fileCoverage.resetHits()
|
|
32
|
+
|
|
33
|
+
return isAnyLineExecuted
|
|
34
|
+
})
|
|
35
|
+
.map(filename => filename.replace(`${rootDir}/`, ''))
|
|
36
|
+
}
|
|
37
|
+
|
|
19
38
|
const specStatusToTestStatus = {
|
|
20
39
|
'pending': 'skip',
|
|
21
40
|
'disabled': 'skip',
|
|
@@ -49,15 +68,11 @@ function getWrappedEnvironment (BaseEnvironment) {
|
|
|
49
68
|
constructor (config, context) {
|
|
50
69
|
super(config, context)
|
|
51
70
|
const rootDir = config.globalConfig ? config.globalConfig.rootDir : config.rootDir
|
|
71
|
+
this.rootDir = rootDir
|
|
52
72
|
this.testSuite = getTestSuitePath(context.testPath, rootDir)
|
|
53
73
|
this.nameToParams = {}
|
|
54
74
|
this.global._ddtrace = global._ddtrace
|
|
55
75
|
}
|
|
56
|
-
async teardown () {
|
|
57
|
-
super.teardown().finally(() => {
|
|
58
|
-
testSuiteFinish.publish()
|
|
59
|
-
})
|
|
60
|
-
}
|
|
61
76
|
|
|
62
77
|
async handleTestEvent (event, state) {
|
|
63
78
|
if (super.handleTestEvent) {
|
|
@@ -99,6 +114,10 @@ function getWrappedEnvironment (BaseEnvironment) {
|
|
|
99
114
|
if (event.name === 'test_done') {
|
|
100
115
|
const asyncResource = asyncResources.get(event.test)
|
|
101
116
|
asyncResource.runInAsyncScope(() => {
|
|
117
|
+
if (this.global.__coverage__) {
|
|
118
|
+
const coverageFiles = extractCoverageInformation(this.global.__coverage__, this.rootDir)
|
|
119
|
+
testCodeCoverageCh.publish(coverageFiles)
|
|
120
|
+
}
|
|
102
121
|
let status = 'pass'
|
|
103
122
|
if (event.test.errors && event.test.errors.length) {
|
|
104
123
|
status = 'fail'
|
|
@@ -111,10 +130,13 @@ function getWrappedEnvironment (BaseEnvironment) {
|
|
|
111
130
|
})
|
|
112
131
|
}
|
|
113
132
|
if (event.name === 'test_skip' || event.name === 'test_todo') {
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
133
|
+
const asyncResource = new AsyncResource('bound-anonymous-fn')
|
|
134
|
+
asyncResource.runInAsyncScope(() => {
|
|
135
|
+
testSkippedCh.publish({
|
|
136
|
+
name: getJestTestName(event.test),
|
|
137
|
+
suite: this.testSuite,
|
|
138
|
+
runner: 'jest-circus'
|
|
139
|
+
})
|
|
118
140
|
})
|
|
119
141
|
}
|
|
120
142
|
}
|
|
@@ -30,6 +30,10 @@ class CucumberPlugin extends Plugin {
|
|
|
30
30
|
const sourceRoot = process.cwd()
|
|
31
31
|
const codeOwnersEntries = getCodeOwnersFileEntries(sourceRoot)
|
|
32
32
|
|
|
33
|
+
this.addSub('ci:cucumber:session:finish', () => {
|
|
34
|
+
this.tracer._exporter._writer.flush()
|
|
35
|
+
})
|
|
36
|
+
|
|
33
37
|
this.addSub('ci:cucumber:run:start', ({ pickleName, pickleUri }) => {
|
|
34
38
|
const store = storage.getStore()
|
|
35
39
|
const childOf = store ? store.span : store
|
|
@@ -15,6 +15,9 @@ const {
|
|
|
15
15
|
TEST_CODE_OWNERS
|
|
16
16
|
} = require('../../dd-trace/src/plugins/util/test')
|
|
17
17
|
|
|
18
|
+
// https://github.com/facebook/jest/blob/d6ad15b0f88a05816c2fe034dd6900d28315d570/packages/jest-worker/src/types.ts#L38
|
|
19
|
+
const CHILD_MESSAGE_END = 2
|
|
20
|
+
|
|
18
21
|
function getTestSpanMetadata (tracer, test) {
|
|
19
22
|
const childOf = getTestParentSpan(tracer)
|
|
20
23
|
|
|
@@ -38,9 +41,31 @@ class JestPlugin extends Plugin {
|
|
|
38
41
|
constructor (...args) {
|
|
39
42
|
super(...args)
|
|
40
43
|
|
|
44
|
+
// Used to handle the end of a jest worker to be able to flush
|
|
45
|
+
const handler = ([message]) => {
|
|
46
|
+
if (message === CHILD_MESSAGE_END) {
|
|
47
|
+
this.tracer._exporter._writer.flush(() => {
|
|
48
|
+
// eslint-disable-next-line
|
|
49
|
+
// https://github.com/facebook/jest/blob/24ed3b5ecb419c023ee6fdbc838f07cc028fc007/packages/jest-worker/src/workers/processChild.ts#L118-L133
|
|
50
|
+
// Only after the flush is done we clean up open handles
|
|
51
|
+
// so the worker process can hopefully exit gracefully
|
|
52
|
+
process.removeListener('message', handler)
|
|
53
|
+
})
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
process.on('message', handler)
|
|
57
|
+
|
|
41
58
|
this.testEnvironmentMetadata = getTestEnvironmentMetadata('jest', this.config)
|
|
42
59
|
this.codeOwnersEntries = getCodeOwnersFileEntries()
|
|
43
60
|
|
|
61
|
+
this.addSub('ci:jest:test:code-coverage', (coverageFiles) => {
|
|
62
|
+
if (!this.config.isAgentlessEnabled || !this.config.isIntelligentTestRunnerEnabled) {
|
|
63
|
+
return
|
|
64
|
+
}
|
|
65
|
+
const testSpan = storage.getStore().span
|
|
66
|
+
this.tracer._exporter.exportCoverage({ testSpan, coverageFiles })
|
|
67
|
+
})
|
|
68
|
+
|
|
44
69
|
this.addSub('ci:jest:test:start', (test) => {
|
|
45
70
|
const store = storage.getStore()
|
|
46
71
|
const span = this.startTestSpan(test)
|
|
@@ -55,10 +80,6 @@ class JestPlugin extends Plugin {
|
|
|
55
80
|
finishAllTraceSpans(span)
|
|
56
81
|
})
|
|
57
82
|
|
|
58
|
-
this.addSub('ci:jest:test-suite:finish', () => {
|
|
59
|
-
this.tracer._exporter._writer.flush()
|
|
60
|
-
})
|
|
61
|
-
|
|
62
83
|
this.addSub('ci:jest:test:err', (error) => {
|
|
63
84
|
if (error) {
|
|
64
85
|
const span = storage.getStore().span
|
|
@@ -14,7 +14,7 @@ class MongodbCorePlugin extends Plugin {
|
|
|
14
14
|
|
|
15
15
|
this.addSub(`apm:mongodb:query:start`, ({ ns, ops, options, name }) => {
|
|
16
16
|
const query = getQuery(ops)
|
|
17
|
-
const resource = getResource(ns, query, name)
|
|
17
|
+
const resource = truncate(getResource(ns, query, name))
|
|
18
18
|
const store = storage.getStore()
|
|
19
19
|
const childOf = store ? store.span : store
|
|
20
20
|
const span = this.tracer.startSpan('mongodb.query', {
|
|
@@ -55,8 +55,8 @@ class MongodbCorePlugin extends Plugin {
|
|
|
55
55
|
|
|
56
56
|
function getQuery (cmd) {
|
|
57
57
|
if (!cmd || typeof cmd !== 'object' || Array.isArray(cmd)) return
|
|
58
|
-
if (cmd.query) return JSON.stringify(
|
|
59
|
-
if (cmd.filter) return JSON.stringify(
|
|
58
|
+
if (cmd.query) return JSON.stringify(limitDepth(cmd.query))
|
|
59
|
+
if (cmd.filter) return JSON.stringify(limitDepth(cmd.filter))
|
|
60
60
|
}
|
|
61
61
|
|
|
62
62
|
function getResource (ns, query, operationName) {
|
|
@@ -69,12 +69,25 @@ function getResource (ns, query, operationName) {
|
|
|
69
69
|
return parts.join(' ')
|
|
70
70
|
}
|
|
71
71
|
|
|
72
|
+
function truncate (input) {
|
|
73
|
+
return input.slice(0, Math.min(input.length, 10000))
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function simplify (input) {
|
|
77
|
+
return isBSON(input) ? input.toHexString() : input
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
function shouldSimplify (input) {
|
|
81
|
+
return !isObject(input) || isBSON(input)
|
|
82
|
+
}
|
|
83
|
+
|
|
72
84
|
function shouldHide (input) {
|
|
73
|
-
return
|
|
85
|
+
return Buffer.isBuffer(input) || typeof input === 'function'
|
|
74
86
|
}
|
|
75
87
|
|
|
76
|
-
function
|
|
88
|
+
function limitDepth (input) {
|
|
77
89
|
if (shouldHide(input)) return '?'
|
|
90
|
+
if (shouldSimplify(input)) return simplify(input)
|
|
78
91
|
|
|
79
92
|
const output = {}
|
|
80
93
|
const queue = [{
|
|
@@ -92,8 +105,10 @@ function sanitize (input) {
|
|
|
92
105
|
if (typeof input[key] === 'function') continue
|
|
93
106
|
|
|
94
107
|
const child = input[key]
|
|
95
|
-
if (depth >=
|
|
108
|
+
if (depth >= 10 || shouldHide(child)) {
|
|
96
109
|
output[key] = '?'
|
|
110
|
+
} else if (shouldSimplify(child)) {
|
|
111
|
+
output[key] = simplify(child)
|
|
97
112
|
} else {
|
|
98
113
|
queue.push({
|
|
99
114
|
input: child,
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
const Plugin = require('../../dd-trace/src/plugins/plugin')
|
|
4
4
|
const { storage } = require('../../datadog-core')
|
|
5
5
|
const analyticsSampler = require('../../dd-trace/src/analytics_sampler')
|
|
6
|
+
const log = require('../../dd-trace/src/log')
|
|
6
7
|
|
|
7
8
|
class OracledbPlugin extends Plugin {
|
|
8
9
|
static get name () {
|
|
@@ -14,18 +15,25 @@ class OracledbPlugin extends Plugin {
|
|
|
14
15
|
|
|
15
16
|
this.addSub('apm:oracledb:execute:start', ({ query, connAttrs }) => {
|
|
16
17
|
const service = getServiceName(this.tracer, this.config, connAttrs)
|
|
17
|
-
|
|
18
|
+
let connectStringObj
|
|
19
|
+
try {
|
|
20
|
+
connectStringObj = new URL(`http://${connAttrs.connectString}`)
|
|
21
|
+
} catch (e) {
|
|
22
|
+
log.error(e)
|
|
23
|
+
}
|
|
18
24
|
const tags = {
|
|
19
25
|
'span.kind': 'client',
|
|
20
26
|
'span.type': 'sql',
|
|
21
27
|
'sql.query': query,
|
|
22
|
-
'db.instance': connectStringObj.pathname.substring(1),
|
|
23
|
-
'db.hostname': connectStringObj.hostname,
|
|
24
28
|
'db.user': this.config.user,
|
|
25
|
-
'db.port': connectStringObj.port,
|
|
26
29
|
'resource.name': query,
|
|
27
30
|
'service.name': service
|
|
28
31
|
}
|
|
32
|
+
if (typeof connectStringObj !== 'undefined') {
|
|
33
|
+
tags['db.instance'] = connectStringObj.pathname.substring(1)
|
|
34
|
+
tags['db.hostname'] = connectStringObj.hostname
|
|
35
|
+
tags['db.port'] = connectStringObj.port
|
|
36
|
+
}
|
|
29
37
|
const store = storage.getStore()
|
|
30
38
|
const childOf = store ? store.span : store
|
|
31
39
|
const span = this.tracer.startSpan('oracle.query', {
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
const request = require('../../../exporters/common/request')
|
|
3
|
+
const log = require('../../../log')
|
|
4
|
+
|
|
5
|
+
const { CoverageCIVisibilityEncoder } = require('../../../encode/coverage-ci-visibility')
|
|
6
|
+
const BaseWriter = require('../../../exporters/common/writer')
|
|
7
|
+
|
|
8
|
+
function safeJSONStringify (value) {
|
|
9
|
+
return JSON.stringify(value, (key, value) =>
|
|
10
|
+
key !== 'dd-api-key' ? value : undefined
|
|
11
|
+
)
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
class Writer extends BaseWriter {
|
|
15
|
+
constructor ({ url }) {
|
|
16
|
+
super(...arguments)
|
|
17
|
+
this._url = url
|
|
18
|
+
this._encoder = new CoverageCIVisibilityEncoder()
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
_sendPayload (form, _, done) {
|
|
22
|
+
const options = {
|
|
23
|
+
path: '/api/v2/citestcov',
|
|
24
|
+
method: 'POST',
|
|
25
|
+
headers: {
|
|
26
|
+
'dd-api-key': process.env.DATADOG_API_KEY || process.env.DD_API_KEY,
|
|
27
|
+
...form.getHeaders()
|
|
28
|
+
},
|
|
29
|
+
timeout: 15000
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
options.protocol = this._url.protocol
|
|
33
|
+
options.hostname = this._url.hostname
|
|
34
|
+
options.port = this._url.port
|
|
35
|
+
|
|
36
|
+
log.debug(() => `Request to the intake: ${safeJSONStringify(options)}`)
|
|
37
|
+
|
|
38
|
+
request(form, options, (err, res) => {
|
|
39
|
+
if (err) {
|
|
40
|
+
log.error(err)
|
|
41
|
+
done()
|
|
42
|
+
return
|
|
43
|
+
}
|
|
44
|
+
log.debug(`Response from the intake: ${res}`)
|
|
45
|
+
done()
|
|
46
|
+
})
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
module.exports = Writer
|
|
@@ -2,30 +2,75 @@
|
|
|
2
2
|
|
|
3
3
|
const URL = require('url').URL
|
|
4
4
|
const Writer = require('./writer')
|
|
5
|
-
const
|
|
5
|
+
const CoverageWriter = require('./coverage-writer')
|
|
6
|
+
|
|
7
|
+
const log = require('../../../log')
|
|
6
8
|
|
|
7
9
|
class AgentlessCiVisibilityExporter {
|
|
8
10
|
constructor (config) {
|
|
9
|
-
|
|
11
|
+
this._config = config
|
|
12
|
+
const { tags, site, url, isIntelligentTestRunnerEnabled } = config
|
|
13
|
+
this._isIntelligentTestRunnerEnabled = isIntelligentTestRunnerEnabled
|
|
10
14
|
this._url = url || new URL(`https://citestcycle-intake.${site}`)
|
|
11
15
|
this._writer = new Writer({ url: this._url, tags })
|
|
16
|
+
this._timer = undefined
|
|
17
|
+
this._coverageTimer = undefined
|
|
18
|
+
|
|
19
|
+
this._coverageUrl = url || new URL(`https://event-platform-intake.${site}`)
|
|
20
|
+
this._coverageWriter = new CoverageWriter({ url: this._coverageUrl })
|
|
21
|
+
|
|
22
|
+
process.once('beforeExit', () => {
|
|
23
|
+
this._writer.flush()
|
|
24
|
+
this._coverageWriter.flush()
|
|
25
|
+
})
|
|
26
|
+
}
|
|
12
27
|
|
|
13
|
-
|
|
14
|
-
|
|
28
|
+
exportCoverage ({ testSpan, coverageFiles }) {
|
|
29
|
+
const formattedCoverage = {
|
|
30
|
+
traceId: testSpan.context()._traceId,
|
|
31
|
+
spanId: testSpan.context()._spanId,
|
|
32
|
+
files: coverageFiles
|
|
33
|
+
}
|
|
34
|
+
this._coverageWriter.append(formattedCoverage)
|
|
35
|
+
|
|
36
|
+
const { flushInterval } = this._config
|
|
37
|
+
|
|
38
|
+
if (flushInterval === 0) {
|
|
39
|
+
this._coverageWriter.flush()
|
|
40
|
+
} else if (flushInterval > 0 && !this._coverageTimer) {
|
|
41
|
+
this._coverageTimer = setTimeout(() => {
|
|
42
|
+
this._coverageWriter.flush()
|
|
43
|
+
this._coverageTimer = clearTimeout(this._coverageTimer)
|
|
44
|
+
}, flushInterval).unref()
|
|
15
45
|
}
|
|
16
|
-
this._scheduler && this._scheduler.start()
|
|
17
46
|
}
|
|
18
47
|
|
|
19
48
|
export (trace) {
|
|
20
49
|
this._writer.append(trace)
|
|
21
50
|
|
|
22
|
-
|
|
51
|
+
const { flushInterval } = this._config
|
|
52
|
+
|
|
53
|
+
if (flushInterval === 0) {
|
|
23
54
|
this._writer.flush()
|
|
55
|
+
} else if (flushInterval > 0 && !this._timer) {
|
|
56
|
+
this._timer = setTimeout(() => {
|
|
57
|
+
this._writer.flush()
|
|
58
|
+
this._timer = clearTimeout(this._timer)
|
|
59
|
+
}, flushInterval).unref()
|
|
24
60
|
}
|
|
25
61
|
}
|
|
26
62
|
|
|
27
|
-
|
|
28
|
-
|
|
63
|
+
setUrl (url, coverageUrl = url) {
|
|
64
|
+
try {
|
|
65
|
+
url = new URL(url)
|
|
66
|
+
coverageUrl = new URL(coverageUrl)
|
|
67
|
+
this._url = url
|
|
68
|
+
this._coverageUrl = coverageUrl
|
|
69
|
+
this._writer.setUrl(url)
|
|
70
|
+
this._coverageWriter.setUrl(coverageUrl)
|
|
71
|
+
} catch (e) {
|
|
72
|
+
log.error(e)
|
|
73
|
+
}
|
|
29
74
|
}
|
|
30
75
|
}
|
|
31
76
|
|
|
@@ -5,16 +5,37 @@ const log = require('../../../log')
|
|
|
5
5
|
const { AgentlessCiVisibilityEncoder } = require('../../../encode/agentless-ci-visibility')
|
|
6
6
|
const BaseWriter = require('../../../exporters/common/writer')
|
|
7
7
|
|
|
8
|
+
function safeJSONStringify (value) {
|
|
9
|
+
return JSON.stringify(value, (key, value) =>
|
|
10
|
+
key !== 'dd-api-key' ? value : undefined
|
|
11
|
+
)
|
|
12
|
+
}
|
|
13
|
+
|
|
8
14
|
class Writer extends BaseWriter {
|
|
9
15
|
constructor ({ url, tags }) {
|
|
10
16
|
super(...arguments)
|
|
11
17
|
const { 'runtime-id': runtimeId, env, service } = tags
|
|
12
18
|
this._url = url
|
|
13
|
-
this._encoder = new AgentlessCiVisibilityEncoder({ runtimeId, env, service })
|
|
19
|
+
this._encoder = new AgentlessCiVisibilityEncoder(this, { runtimeId, env, service })
|
|
14
20
|
}
|
|
15
21
|
|
|
16
22
|
_sendPayload (data, _, done) {
|
|
17
|
-
|
|
23
|
+
const options = {
|
|
24
|
+
path: '/api/v2/citestcycle',
|
|
25
|
+
method: 'POST',
|
|
26
|
+
headers: {
|
|
27
|
+
'dd-api-key': process.env.DATADOG_API_KEY || process.env.DD_API_KEY,
|
|
28
|
+
'Content-Type': 'application/msgpack'
|
|
29
|
+
},
|
|
30
|
+
timeout: 15000
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
options.protocol = this._url.protocol
|
|
34
|
+
options.hostname = this._url.hostname
|
|
35
|
+
options.port = this._url.port
|
|
36
|
+
|
|
37
|
+
log.debug(() => `Request to the intake: ${safeJSONStringify(options)}`)
|
|
38
|
+
request(data, options, (err, res) => {
|
|
18
39
|
if (err) {
|
|
19
40
|
log.error(err)
|
|
20
41
|
done()
|
|
@@ -26,26 +47,4 @@ class Writer extends BaseWriter {
|
|
|
26
47
|
}
|
|
27
48
|
}
|
|
28
49
|
|
|
29
|
-
function makeRequest (data, url, cb) {
|
|
30
|
-
const options = {
|
|
31
|
-
path: '/api/v2/citestcycle',
|
|
32
|
-
method: 'POST',
|
|
33
|
-
headers: {
|
|
34
|
-
'Content-Type': 'application/msgpack',
|
|
35
|
-
'dd-api-key': process.env.DATADOG_API_KEY || process.env.DD_API_KEY
|
|
36
|
-
},
|
|
37
|
-
timeout: 15000
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
options.protocol = url.protocol
|
|
41
|
-
options.hostname = url.hostname
|
|
42
|
-
options.port = url.port
|
|
43
|
-
|
|
44
|
-
log.debug(() => `Request to the intake: ${JSON.stringify(options)}`)
|
|
45
|
-
|
|
46
|
-
request(data, options, false, (err, res) => {
|
|
47
|
-
cb(err, res)
|
|
48
|
-
})
|
|
49
|
-
}
|
|
50
|
-
|
|
51
50
|
module.exports = Writer
|
|
@@ -70,6 +70,12 @@ class Config {
|
|
|
70
70
|
null
|
|
71
71
|
)
|
|
72
72
|
const DD_CIVISIBILITY_AGENTLESS_URL = process.env.DD_CIVISIBILITY_AGENTLESS_URL
|
|
73
|
+
|
|
74
|
+
const DD_CIVISIBILITY_ITR_ENABLED = coalesce(
|
|
75
|
+
process.env.DD_CIVISIBILITY_ITR_ENABLED,
|
|
76
|
+
false
|
|
77
|
+
)
|
|
78
|
+
|
|
73
79
|
const DD_SERVICE = options.service ||
|
|
74
80
|
process.env.DD_SERVICE ||
|
|
75
81
|
process.env.DD_SERVICE_NAME ||
|
|
@@ -262,6 +268,7 @@ ken|consumer_?(?:id|key|secret)|sign(?:ed|ature)?|auth(?:entication|orization)?)
|
|
|
262
268
|
obfuscatorValueRegex: DD_APPSEC_OBFUSCATION_PARAMETER_VALUE_REGEXP
|
|
263
269
|
}
|
|
264
270
|
this.isGitUploadEnabled = isTrue(DD_CIVISIBILITY_GIT_UPLOAD_ENABLED)
|
|
271
|
+
this.isIntelligentTestRunnerEnabled = isTrue(DD_CIVISIBILITY_ITR_ENABLED)
|
|
265
272
|
|
|
266
273
|
tagger.add(this.tags, {
|
|
267
274
|
service: this.service,
|