dd-trace 5.10.0 → 5.12.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/index.d.ts +15 -0
- package/package.json +2 -1
- package/packages/datadog-instrumentations/src/fetch.js +6 -45
- package/packages/datadog-instrumentations/src/helpers/fetch.js +22 -0
- package/packages/datadog-instrumentations/src/helpers/hooks.js +3 -1
- package/packages/datadog-instrumentations/src/jest.js +77 -10
- package/packages/datadog-instrumentations/src/mongoose.js +2 -1
- package/packages/datadog-instrumentations/src/openai.js +149 -0
- package/packages/datadog-instrumentations/src/otel-sdk-trace.js +6 -1
- package/packages/datadog-instrumentations/src/selenium.js +69 -0
- package/packages/datadog-plugin-cucumber/src/index.js +2 -2
- package/packages/datadog-plugin-cypress/src/cypress-plugin.js +2 -2
- package/packages/datadog-plugin-cypress/src/support.js +19 -3
- package/packages/datadog-plugin-fetch/src/index.js +20 -11
- package/packages/datadog-plugin-jest/src/index.js +7 -2
- package/packages/datadog-plugin-mocha/src/index.js +4 -5
- package/packages/datadog-plugin-openai/src/index.js +159 -32
- package/packages/datadog-plugin-openai/src/services.js +2 -1
- package/packages/datadog-plugin-playwright/src/index.js +2 -2
- package/packages/datadog-plugin-selenium/src/index.js +71 -0
- package/packages/dd-trace/src/appsec/iast/analyzers/analyzers.js +1 -0
- package/packages/dd-trace/src/appsec/iast/analyzers/hardcoded-base-analyzer.js +70 -0
- package/packages/dd-trace/src/appsec/iast/analyzers/hardcoded-password-analyzer.js +14 -0
- package/packages/dd-trace/src/appsec/iast/analyzers/hardcoded-password-rules.js +12 -0
- package/packages/dd-trace/src/appsec/iast/analyzers/hardcoded-rule-type.js +6 -0
- package/packages/dd-trace/src/appsec/iast/analyzers/hardcoded-secret-analyzer.js +5 -50
- package/packages/dd-trace/src/appsec/iast/analyzers/hardcoded-secret-rules.js +742 -0
- package/packages/dd-trace/src/appsec/iast/analyzers/hardcoded-secrets-rules.js +539 -66
- package/packages/dd-trace/src/appsec/iast/taint-tracking/operations-taint-object.js +1 -9
- package/packages/dd-trace/src/appsec/iast/taint-tracking/plugin.js +4 -2
- package/packages/dd-trace/src/appsec/iast/vulnerabilities.js +1 -0
- package/packages/dd-trace/src/appsec/remote_config/index.js +5 -5
- package/packages/dd-trace/src/appsec/reporter.js +11 -10
- package/packages/dd-trace/src/appsec/telemetry.js +36 -7
- package/packages/dd-trace/src/ci-visibility/exporters/ci-visibility-exporter.js +4 -2
- package/packages/dd-trace/src/ci-visibility/requests/get-library-configuration.js +4 -1
- package/packages/dd-trace/src/config.js +94 -9
- package/packages/dd-trace/src/dogstatsd.js +13 -11
- package/packages/dd-trace/src/index.js +5 -1
- package/packages/dd-trace/src/noop/dogstatsd.js +11 -0
- package/packages/dd-trace/src/noop/proxy.js +3 -0
- package/packages/dd-trace/src/opentracing/propagation/text_map.js +10 -4
- package/packages/dd-trace/src/opentracing/span.js +2 -0
- package/packages/dd-trace/src/plugins/index.js +2 -0
- package/packages/dd-trace/src/plugins/util/test.js +34 -3
- package/packages/dd-trace/src/profiling/config.js +8 -4
- package/packages/dd-trace/src/profiling/exporters/agent.js +5 -3
- package/packages/dd-trace/src/profiling/profiler.js +4 -0
- package/packages/dd-trace/src/profiling/ssi-telemetry-mock-profiler.js +33 -0
- package/packages/dd-trace/src/profiling/ssi-telemetry.js +167 -0
- package/packages/dd-trace/src/proxy.js +33 -7
- package/packages/dd-trace/src/tagger.js +13 -3
- package/packages/dd-trace/src/telemetry/index.js +5 -4
- package/packages/dd-trace/src/telemetry/metrics.js +2 -2
package/index.d.ts
CHANGED
|
@@ -187,6 +187,7 @@ interface Plugins {
|
|
|
187
187
|
"restify": tracer.plugins.restify;
|
|
188
188
|
"rhea": tracer.plugins.rhea;
|
|
189
189
|
"router": tracer.plugins.router;
|
|
190
|
+
"selenium": tracer.plugins.selenium;
|
|
190
191
|
"sharedb": tracer.plugins.sharedb;
|
|
191
192
|
"tedious": tracer.plugins.tedious;
|
|
192
193
|
"winston": tracer.plugins.winston;
|
|
@@ -789,6 +790,14 @@ declare namespace tracer {
|
|
|
789
790
|
*/
|
|
790
791
|
gauge(stat: string, value?: number, tags?: { [tag: string]: string|number }): void
|
|
791
792
|
|
|
793
|
+
/**
|
|
794
|
+
* Sets a histogram value, optionally specifying tags.
|
|
795
|
+
* @param {string} stat The dot-separated metric name.
|
|
796
|
+
* @param {number} value The amount to increment the stat by.
|
|
797
|
+
* @param {[tag:string]:string|number} tags Tags to pass along, such as `{ foo: 'bar' }`. Values are combined with config.tags.
|
|
798
|
+
*/
|
|
799
|
+
histogram(stat: string, value?: number, tags?: { [tag: string]: string|number }): void
|
|
800
|
+
|
|
792
801
|
/**
|
|
793
802
|
* Forces any unsent metrics to be sent
|
|
794
803
|
*
|
|
@@ -1728,6 +1737,12 @@ declare namespace tracer {
|
|
|
1728
1737
|
*/
|
|
1729
1738
|
interface router extends Integration {}
|
|
1730
1739
|
|
|
1740
|
+
/**
|
|
1741
|
+
* This plugin automatically instruments the
|
|
1742
|
+
* [selenium-webdriver](https://www.npmjs.com/package/selenium-webdriver) module.
|
|
1743
|
+
*/
|
|
1744
|
+
interface selenium extends Integration {}
|
|
1745
|
+
|
|
1731
1746
|
/**
|
|
1732
1747
|
* This plugin automatically instruments the
|
|
1733
1748
|
* [sharedb](https://github.com/share/sharedb) module.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dd-trace",
|
|
3
|
-
"version": "5.
|
|
3
|
+
"version": "5.12.0",
|
|
4
4
|
"description": "Datadog APM tracing client for JavaScript",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"typings": "index.d.ts",
|
|
@@ -36,6 +36,7 @@
|
|
|
36
36
|
"test:integration:cucumber": "mocha --colors --timeout 30000 \"integration-tests/cucumber/*.spec.js\"",
|
|
37
37
|
"test:integration:cypress": "mocha --colors --timeout 30000 \"integration-tests/cypress/*.spec.js\"",
|
|
38
38
|
"test:integration:playwright": "mocha --colors --timeout 30000 \"integration-tests/playwright/*.spec.js\"",
|
|
39
|
+
"test:integration:selenium": "mocha --colors --timeout 30000 \"integration-tests/selenium/*.spec.js\"",
|
|
39
40
|
"test:integration:profiler": "mocha --colors --timeout 90000 \"integration-tests/profiler/*.spec.js\"",
|
|
40
41
|
"test:integration:serverless": "mocha --colors --timeout 30000 \"integration-tests/serverless/*.spec.js\"",
|
|
41
42
|
"test:integration:plugins": "mocha --colors --exit -r \"packages/dd-trace/test/setup/mocha.js\" \"packages/datadog-plugin-@($(echo $PLUGINS))/test/integration-test/**/*.spec.js\"",
|
|
@@ -1,51 +1,12 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
3
|
const shimmer = require('../../datadog-shimmer')
|
|
4
|
-
const {
|
|
5
|
-
|
|
6
|
-
const startChannel = channel('apm:fetch:request:start')
|
|
7
|
-
const finishChannel = channel('apm:fetch:request:finish')
|
|
8
|
-
const errorChannel = channel('apm:fetch:request:error')
|
|
9
|
-
|
|
10
|
-
function wrapFetch (fetch, Request) {
|
|
11
|
-
if (typeof fetch !== 'function') return fetch
|
|
12
|
-
|
|
13
|
-
return function (input, init) {
|
|
14
|
-
if (!startChannel.hasSubscribers) return fetch.apply(this, arguments)
|
|
15
|
-
|
|
16
|
-
const req = new Request(input, init)
|
|
17
|
-
const headers = req.headers
|
|
18
|
-
const message = { req, headers }
|
|
19
|
-
|
|
20
|
-
return startChannel.runStores(message, () => {
|
|
21
|
-
// Request object is read-only so we need new objects to change headers.
|
|
22
|
-
arguments[0] = message.req
|
|
23
|
-
arguments[1] = { headers: message.headers }
|
|
24
|
-
|
|
25
|
-
return fetch.apply(this, arguments)
|
|
26
|
-
.then(
|
|
27
|
-
res => {
|
|
28
|
-
message.res = res
|
|
29
|
-
|
|
30
|
-
finishChannel.publish(message)
|
|
31
|
-
|
|
32
|
-
return res
|
|
33
|
-
},
|
|
34
|
-
err => {
|
|
35
|
-
if (err.name !== 'AbortError') {
|
|
36
|
-
message.error = err
|
|
37
|
-
errorChannel.publish(message)
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
finishChannel.publish(message)
|
|
41
|
-
|
|
42
|
-
throw err
|
|
43
|
-
}
|
|
44
|
-
)
|
|
45
|
-
})
|
|
46
|
-
}
|
|
47
|
-
}
|
|
4
|
+
const { tracingChannel } = require('dc-polyfill')
|
|
5
|
+
const { createWrapFetch } = require('./helpers/fetch')
|
|
48
6
|
|
|
49
7
|
if (globalThis.fetch) {
|
|
50
|
-
|
|
8
|
+
const ch = tracingChannel('apm:fetch:request')
|
|
9
|
+
const wrapFetch = createWrapFetch(globalThis.Request, ch)
|
|
10
|
+
|
|
11
|
+
globalThis.fetch = shimmer.wrap(fetch, wrapFetch(fetch))
|
|
51
12
|
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
exports.createWrapFetch = function createWrapFetch (Request, ch) {
|
|
4
|
+
return function wrapFetch (fetch) {
|
|
5
|
+
if (typeof fetch !== 'function') return fetch
|
|
6
|
+
|
|
7
|
+
return function (input, init) {
|
|
8
|
+
if (!ch.start.hasSubscribers) return fetch.apply(this, arguments)
|
|
9
|
+
|
|
10
|
+
if (input instanceof Request) {
|
|
11
|
+
const ctx = { req: input }
|
|
12
|
+
|
|
13
|
+
return ch.tracePromise(() => fetch.call(this, input, init), ctx)
|
|
14
|
+
} else {
|
|
15
|
+
const req = new Request(input, init)
|
|
16
|
+
const ctx = { req }
|
|
17
|
+
|
|
18
|
+
return ch.tracePromise(() => fetch.call(this, req), ctx)
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
}
|
|
@@ -58,6 +58,7 @@ module.exports = {
|
|
|
58
58
|
'jest-environment-node': () => require('../jest'),
|
|
59
59
|
'jest-environment-jsdom': () => require('../jest'),
|
|
60
60
|
'jest-jasmine2': () => require('../jest'),
|
|
61
|
+
'jest-runtime': () => require('../jest'),
|
|
61
62
|
'jest-worker': () => require('../jest'),
|
|
62
63
|
knex: () => require('../knex'),
|
|
63
64
|
koa: () => require('../koa'),
|
|
@@ -103,8 +104,9 @@ module.exports = {
|
|
|
103
104
|
restify: () => require('../restify'),
|
|
104
105
|
rhea: () => require('../rhea'),
|
|
105
106
|
router: () => require('../router'),
|
|
106
|
-
|
|
107
|
+
'selenium-webdriver': () => require('../selenium'),
|
|
107
108
|
sequelize: () => require('../sequelize'),
|
|
109
|
+
sharedb: () => require('../sharedb'),
|
|
108
110
|
tedious: () => require('../tedious'),
|
|
109
111
|
when: () => require('../when'),
|
|
110
112
|
winston: () => require('../winston')
|
|
@@ -11,7 +11,8 @@ const {
|
|
|
11
11
|
getTestSuitePath,
|
|
12
12
|
getTestParametersString,
|
|
13
13
|
addEfdStringToTestName,
|
|
14
|
-
removeEfdStringFromTestName
|
|
14
|
+
removeEfdStringFromTestName,
|
|
15
|
+
getIsFaultyEarlyFlakeDetection
|
|
15
16
|
} = require('../../dd-trace/src/plugins/util/test')
|
|
16
17
|
const {
|
|
17
18
|
getFormattedJestTestParameters,
|
|
@@ -61,6 +62,8 @@ let hasUnskippableSuites = false
|
|
|
61
62
|
let hasForcedToRunSuites = false
|
|
62
63
|
let isEarlyFlakeDetectionEnabled = false
|
|
63
64
|
let earlyFlakeDetectionNumRetries = 0
|
|
65
|
+
let earlyFlakeDetectionFaultyThreshold = 30
|
|
66
|
+
let isEarlyFlakeDetectionFaulty = false
|
|
64
67
|
let hasFilteredSkippableSuites = false
|
|
65
68
|
|
|
66
69
|
const sessionAsyncResource = new AsyncResource('bound-anonymous-fn')
|
|
@@ -121,6 +124,7 @@ function getWrappedEnvironment (BaseEnvironment, jestVersion) {
|
|
|
121
124
|
this.testSuite = getTestSuitePath(context.testPath, rootDir)
|
|
122
125
|
this.nameToParams = {}
|
|
123
126
|
this.global._ddtrace = global._ddtrace
|
|
127
|
+
this.hasSnapshotTests = undefined
|
|
124
128
|
|
|
125
129
|
this.displayName = config.projectConfig?.displayName?.name
|
|
126
130
|
this.testEnvironmentOptions = getTestEnvironmentOptions(config)
|
|
@@ -147,6 +151,21 @@ function getWrappedEnvironment (BaseEnvironment, jestVersion) {
|
|
|
147
151
|
}
|
|
148
152
|
}
|
|
149
153
|
|
|
154
|
+
getHasSnapshotTests () {
|
|
155
|
+
if (this.hasSnapshotTests !== undefined) {
|
|
156
|
+
return this.hasSnapshotTests
|
|
157
|
+
}
|
|
158
|
+
let hasSnapshotTests = true
|
|
159
|
+
try {
|
|
160
|
+
const { _snapshotData } = this.context.expect.getState().snapshotState
|
|
161
|
+
hasSnapshotTests = Object.keys(_snapshotData).length > 0
|
|
162
|
+
} catch (e) {
|
|
163
|
+
// if we can't be sure, we'll err on the side of caution and assume it has snapshots
|
|
164
|
+
}
|
|
165
|
+
this.hasSnapshotTests = hasSnapshotTests
|
|
166
|
+
return hasSnapshotTests
|
|
167
|
+
}
|
|
168
|
+
|
|
150
169
|
// Function that receives a list of known tests for a test service and
|
|
151
170
|
// returns the ones that belong to the current suite
|
|
152
171
|
getKnownTestsForSuite (knownTests) {
|
|
@@ -231,6 +250,13 @@ function getWrappedEnvironment (BaseEnvironment, jestVersion) {
|
|
|
231
250
|
const isSkipped = event.mode === 'todo' || event.mode === 'skip'
|
|
232
251
|
if (isNew && !isSkipped && !retriedTestsToNumAttempts.has(testName)) {
|
|
233
252
|
retriedTestsToNumAttempts.set(testName, 0)
|
|
253
|
+
// Retrying snapshots has proven to be problematic, so we'll skip them for now
|
|
254
|
+
// We'll still detect new tests, but we won't retry them.
|
|
255
|
+
// TODO: do not bail out of EFD with the whole test suite
|
|
256
|
+
if (this.getHasSnapshotTests()) {
|
|
257
|
+
log.warn('Early flake detection is disabled for suites with snapshots')
|
|
258
|
+
return
|
|
259
|
+
}
|
|
234
260
|
for (let retryIndex = 0; retryIndex < earlyFlakeDetectionNumRetries; retryIndex++) {
|
|
235
261
|
if (this.global.test) {
|
|
236
262
|
this.global.test(addEfdStringToTestName(event.testName, retryIndex), event.fn, event.timeout)
|
|
@@ -312,7 +338,7 @@ function applySuiteSkipping (originalTests, rootDir, frameworkVersion) {
|
|
|
312
338
|
numSkippedSuites = jestSuitesToRun.skippedSuites.length
|
|
313
339
|
|
|
314
340
|
itrSkippedSuitesCh.publish({ skippedSuites: jestSuitesToRun.skippedSuites, frameworkVersion })
|
|
315
|
-
|
|
341
|
+
|
|
316
342
|
return jestSuitesToRun.suitesToRun
|
|
317
343
|
}
|
|
318
344
|
|
|
@@ -410,6 +436,7 @@ function cliWrapper (cli, jestVersion) {
|
|
|
410
436
|
isSuitesSkippingEnabled = libraryConfig.isSuitesSkippingEnabled
|
|
411
437
|
isEarlyFlakeDetectionEnabled = libraryConfig.isEarlyFlakeDetectionEnabled
|
|
412
438
|
earlyFlakeDetectionNumRetries = libraryConfig.earlyFlakeDetectionNumRetries
|
|
439
|
+
earlyFlakeDetectionFaultyThreshold = libraryConfig.earlyFlakeDetectionFaultyThreshold
|
|
413
440
|
}
|
|
414
441
|
} catch (err) {
|
|
415
442
|
log.error(err)
|
|
@@ -524,6 +551,7 @@ function cliWrapper (cli, jestVersion) {
|
|
|
524
551
|
hasForcedToRunSuites,
|
|
525
552
|
error,
|
|
526
553
|
isEarlyFlakeDetectionEnabled,
|
|
554
|
+
isEarlyFlakeDetectionFaulty,
|
|
527
555
|
onDone
|
|
528
556
|
})
|
|
529
557
|
})
|
|
@@ -760,13 +788,26 @@ addHook({
|
|
|
760
788
|
const SearchSource = searchSourcePackage.default ? searchSourcePackage.default : searchSourcePackage
|
|
761
789
|
|
|
762
790
|
shimmer.wrap(SearchSource.prototype, 'getTestPaths', getTestPaths => async function () {
|
|
763
|
-
|
|
764
|
-
return getTestPaths.apply(this, arguments)
|
|
765
|
-
}
|
|
766
|
-
|
|
791
|
+
const testPaths = await getTestPaths.apply(this, arguments)
|
|
767
792
|
const [{ rootDir, shard }] = arguments
|
|
768
793
|
|
|
769
|
-
if (
|
|
794
|
+
if (isEarlyFlakeDetectionEnabled) {
|
|
795
|
+
const projectSuites = testPaths.tests.map(test => getTestSuitePath(test.path, test.context.config.rootDir))
|
|
796
|
+
const isFaulty =
|
|
797
|
+
getIsFaultyEarlyFlakeDetection(projectSuites, knownTests.jest || {}, earlyFlakeDetectionFaultyThreshold)
|
|
798
|
+
if (isFaulty) {
|
|
799
|
+
log.error('Early flake detection is disabled because the number of new suites is too high.')
|
|
800
|
+
isEarlyFlakeDetectionEnabled = false
|
|
801
|
+
const testEnvironmentOptions = testPaths.tests[0]?.context?.config?.testEnvironmentOptions
|
|
802
|
+
// Project config is shared among all tests, so we can modify it here
|
|
803
|
+
if (testEnvironmentOptions) {
|
|
804
|
+
testEnvironmentOptions._ddIsEarlyFlakeDetectionEnabled = false
|
|
805
|
+
}
|
|
806
|
+
isEarlyFlakeDetectionFaulty = true
|
|
807
|
+
}
|
|
808
|
+
}
|
|
809
|
+
|
|
810
|
+
if (shard?.shardCount > 1 || !isSuitesSkippingEnabled || !skippableSuites.length) {
|
|
770
811
|
// If the user is using jest sharding, we want to apply the filtering of tests in the shard process.
|
|
771
812
|
// The reason for this is the following:
|
|
772
813
|
// The tests for different shards are likely being run in different CI jobs so
|
|
@@ -774,10 +815,8 @@ addHook({
|
|
|
774
815
|
// If the skippable endpoint is returning different suites and we filter the list of tests here,
|
|
775
816
|
// the base list of tests that is used for sharding might be different,
|
|
776
817
|
// causing the shards to potentially run the same suite.
|
|
777
|
-
return
|
|
818
|
+
return testPaths
|
|
778
819
|
}
|
|
779
|
-
|
|
780
|
-
const testPaths = await getTestPaths.apply(this, arguments)
|
|
781
820
|
const { tests } = testPaths
|
|
782
821
|
|
|
783
822
|
const suitesToRun = applySuiteSkipping(tests, rootDir, frameworkVersion)
|
|
@@ -837,6 +876,34 @@ if (DD_MAJOR < 4) {
|
|
|
837
876
|
}, jasmineAsyncInstallWraper)
|
|
838
877
|
}
|
|
839
878
|
|
|
879
|
+
const LIBRARIES_BYPASSING_JEST_REQUIRE_ENGINE = [
|
|
880
|
+
'selenium-webdriver'
|
|
881
|
+
]
|
|
882
|
+
|
|
883
|
+
function shouldBypassJestRequireEngine (moduleName) {
|
|
884
|
+
return (
|
|
885
|
+
LIBRARIES_BYPASSING_JEST_REQUIRE_ENGINE.some(library => moduleName.includes(library))
|
|
886
|
+
)
|
|
887
|
+
}
|
|
888
|
+
|
|
889
|
+
addHook({
|
|
890
|
+
name: 'jest-runtime',
|
|
891
|
+
versions: ['>=24.8.0']
|
|
892
|
+
}, (runtimePackage) => {
|
|
893
|
+
const Runtime = runtimePackage.default ? runtimePackage.default : runtimePackage
|
|
894
|
+
|
|
895
|
+
shimmer.wrap(Runtime.prototype, 'requireModuleOrMock', requireModuleOrMock => function (from, moduleName) {
|
|
896
|
+
// TODO: do this for every library that we instrument
|
|
897
|
+
if (shouldBypassJestRequireEngine(moduleName)) {
|
|
898
|
+
// To bypass jest's own require engine
|
|
899
|
+
return this._requireCoreModule(moduleName)
|
|
900
|
+
}
|
|
901
|
+
return requireModuleOrMock.apply(this, arguments)
|
|
902
|
+
})
|
|
903
|
+
|
|
904
|
+
return runtimePackage
|
|
905
|
+
})
|
|
906
|
+
|
|
840
907
|
addHook({
|
|
841
908
|
name: 'jest-worker',
|
|
842
909
|
versions: ['>=24.9.0'],
|
|
@@ -21,7 +21,8 @@ addHook({
|
|
|
21
21
|
name: 'mongoose',
|
|
22
22
|
versions: ['>=4.6.4 <5', '5', '6', '>=7']
|
|
23
23
|
}, mongoose => {
|
|
24
|
-
|
|
24
|
+
// As of Mongoose 7, custom promise libraries are no longer supported and mongoose.Promise may be undefined
|
|
25
|
+
if (mongoose.Promise && mongoose.Promise !== global.Promise) {
|
|
25
26
|
shimmer.wrap(mongoose.Promise.prototype, 'then', wrapThen)
|
|
26
27
|
}
|
|
27
28
|
|
|
@@ -10,6 +10,98 @@ const startCh = channel('apm:openai:request:start')
|
|
|
10
10
|
const finishCh = channel('apm:openai:request:finish')
|
|
11
11
|
const errorCh = channel('apm:openai:request:error')
|
|
12
12
|
|
|
13
|
+
const V4_PACKAGE_SHIMS = [
|
|
14
|
+
{
|
|
15
|
+
file: 'resources/chat/completions.js',
|
|
16
|
+
targetClass: 'Completions',
|
|
17
|
+
baseResource: 'chat.completions',
|
|
18
|
+
methods: ['create']
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
file: 'resources/completions.js',
|
|
22
|
+
targetClass: 'Completions',
|
|
23
|
+
baseResource: 'completions',
|
|
24
|
+
methods: ['create']
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
file: 'resources/embeddings.js',
|
|
28
|
+
targetClass: 'Embeddings',
|
|
29
|
+
baseResource: 'embeddings',
|
|
30
|
+
methods: ['create']
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
file: 'resources/files.js',
|
|
34
|
+
targetClass: 'Files',
|
|
35
|
+
baseResource: 'files',
|
|
36
|
+
methods: ['create', 'del', 'list', 'retrieve']
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
file: 'resources/files.js',
|
|
40
|
+
targetClass: 'Files',
|
|
41
|
+
baseResource: 'files',
|
|
42
|
+
methods: ['retrieveContent'],
|
|
43
|
+
versions: ['>=4.0.0 <4.17.1']
|
|
44
|
+
},
|
|
45
|
+
{
|
|
46
|
+
file: 'resources/files.js',
|
|
47
|
+
targetClass: 'Files',
|
|
48
|
+
baseResource: 'files',
|
|
49
|
+
methods: ['content'], // replaced `retrieveContent` in v4.17.1
|
|
50
|
+
versions: ['>=4.17.1']
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
file: 'resources/images.js',
|
|
54
|
+
targetClass: 'Images',
|
|
55
|
+
baseResource: 'images',
|
|
56
|
+
methods: ['createVariation', 'edit', 'generate']
|
|
57
|
+
},
|
|
58
|
+
{
|
|
59
|
+
file: 'resources/fine-tuning/jobs/jobs.js',
|
|
60
|
+
targetClass: 'Jobs',
|
|
61
|
+
baseResource: 'fine_tuning.jobs',
|
|
62
|
+
methods: ['cancel', 'create', 'list', 'listEvents', 'retrieve'],
|
|
63
|
+
versions: ['>=4.34.0'] // file location changed in 4.34.0
|
|
64
|
+
},
|
|
65
|
+
{
|
|
66
|
+
file: 'resources/fine-tuning/jobs.js',
|
|
67
|
+
targetClass: 'Jobs',
|
|
68
|
+
baseResource: 'fine_tuning.jobs',
|
|
69
|
+
methods: ['cancel', 'create', 'list', 'listEvents', 'retrieve'],
|
|
70
|
+
versions: ['>=4.1.0 <4.34.0']
|
|
71
|
+
},
|
|
72
|
+
{
|
|
73
|
+
file: 'resources/fine-tunes.js', // deprecated after 4.1.0
|
|
74
|
+
targetClass: 'FineTunes',
|
|
75
|
+
baseResource: 'fine-tune',
|
|
76
|
+
methods: ['cancel', 'create', 'list', 'listEvents', 'retrieve'],
|
|
77
|
+
versions: ['>=4.0.0 <4.1.0']
|
|
78
|
+
},
|
|
79
|
+
{
|
|
80
|
+
file: 'resources/models.js',
|
|
81
|
+
targetClass: 'Models',
|
|
82
|
+
baseResource: 'models',
|
|
83
|
+
methods: ['del', 'list', 'retrieve']
|
|
84
|
+
},
|
|
85
|
+
{
|
|
86
|
+
file: 'resources/moderations.js',
|
|
87
|
+
targetClass: 'Moderations',
|
|
88
|
+
baseResource: 'moderations',
|
|
89
|
+
methods: ['create']
|
|
90
|
+
},
|
|
91
|
+
{
|
|
92
|
+
file: 'resources/audio/transcriptions.js',
|
|
93
|
+
targetClass: 'Transcriptions',
|
|
94
|
+
baseResource: 'audio.transcriptions',
|
|
95
|
+
methods: ['create']
|
|
96
|
+
},
|
|
97
|
+
{
|
|
98
|
+
file: 'resources/audio/translations.js',
|
|
99
|
+
targetClass: 'Translations',
|
|
100
|
+
baseResource: 'audio.translations',
|
|
101
|
+
methods: ['create']
|
|
102
|
+
}
|
|
103
|
+
]
|
|
104
|
+
|
|
13
105
|
addHook({ name: 'openai', file: 'dist/api.js', versions: ['>=3.0.0 <4'] }, exports => {
|
|
14
106
|
const methodNames = Object.getOwnPropertyNames(exports.OpenAIApi.prototype)
|
|
15
107
|
methodNames.shift() // remove leading 'constructor' method
|
|
@@ -48,3 +140,60 @@ addHook({ name: 'openai', file: 'dist/api.js', versions: ['>=3.0.0 <4'] }, expor
|
|
|
48
140
|
|
|
49
141
|
return exports
|
|
50
142
|
})
|
|
143
|
+
|
|
144
|
+
for (const shim of V4_PACKAGE_SHIMS) {
|
|
145
|
+
const { file, targetClass, baseResource, methods } = shim
|
|
146
|
+
addHook({ name: 'openai', file, versions: shim.versions || ['>=4'] }, exports => {
|
|
147
|
+
const targetPrototype = exports[targetClass].prototype
|
|
148
|
+
|
|
149
|
+
for (const methodName of methods) {
|
|
150
|
+
shimmer.wrap(targetPrototype, methodName, methodFn => function () {
|
|
151
|
+
if (!startCh.hasSubscribers) {
|
|
152
|
+
return methodFn.apply(this, arguments)
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
const client = this._client || this.client
|
|
156
|
+
|
|
157
|
+
startCh.publish({
|
|
158
|
+
methodName: `${baseResource}.${methodName}`,
|
|
159
|
+
args: arguments,
|
|
160
|
+
basePath: client.baseURL,
|
|
161
|
+
apiKey: client.apiKey
|
|
162
|
+
})
|
|
163
|
+
|
|
164
|
+
const apiProm = methodFn.apply(this, arguments)
|
|
165
|
+
|
|
166
|
+
// wrapping `parse` avoids problematic wrapping of `then` when trying to call
|
|
167
|
+
// `withResponse` in userland code after. This way, we can return the whole `APIPromise`
|
|
168
|
+
shimmer.wrap(apiProm, 'parse', origApiPromParse => function () {
|
|
169
|
+
return origApiPromParse.apply(this, arguments)
|
|
170
|
+
// the original response is wrapped in a promise, so we need to unwrap it
|
|
171
|
+
.then(body => Promise.all([this.responsePromise, body]))
|
|
172
|
+
.then(([{ response, options }, body]) => {
|
|
173
|
+
finishCh.publish({
|
|
174
|
+
headers: response.headers,
|
|
175
|
+
body,
|
|
176
|
+
path: response.url,
|
|
177
|
+
method: options.method
|
|
178
|
+
})
|
|
179
|
+
|
|
180
|
+
return body
|
|
181
|
+
})
|
|
182
|
+
.catch(err => {
|
|
183
|
+
errorCh.publish({ err })
|
|
184
|
+
|
|
185
|
+
throw err
|
|
186
|
+
})
|
|
187
|
+
.finally(() => {
|
|
188
|
+
// maybe we don't want to unwrap here in case the promise is re-used?
|
|
189
|
+
// other hand: we want to avoid resource leakage
|
|
190
|
+
shimmer.unwrap(apiProm, 'parse')
|
|
191
|
+
})
|
|
192
|
+
})
|
|
193
|
+
|
|
194
|
+
return apiProm
|
|
195
|
+
})
|
|
196
|
+
}
|
|
197
|
+
return exports
|
|
198
|
+
})
|
|
199
|
+
}
|
|
@@ -4,7 +4,12 @@ const { addHook } = require('./helpers/instrument')
|
|
|
4
4
|
const shimmer = require('../../datadog-shimmer')
|
|
5
5
|
const tracer = require('../../dd-trace')
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
const otelSdkEnabled = process.env.DD_TRACE_OTEL_ENABLED ||
|
|
8
|
+
process.env.OTEL_SDK_DISABLED
|
|
9
|
+
? !process.env.OTEL_SDK_DISABLED
|
|
10
|
+
: undefined
|
|
11
|
+
|
|
12
|
+
if (otelSdkEnabled) {
|
|
8
13
|
addHook({
|
|
9
14
|
name: '@opentelemetry/sdk-trace-node',
|
|
10
15
|
file: 'build/src/NodeTracerProvider.js',
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
const { addHook, channel } = require('./helpers/instrument')
|
|
2
|
+
const shimmer = require('../../datadog-shimmer')
|
|
3
|
+
|
|
4
|
+
const ciSeleniumDriverGetStartCh = channel('ci:selenium:driver:get')
|
|
5
|
+
|
|
6
|
+
const RUM_STOP_SESSION_SCRIPT = `
|
|
7
|
+
if (window.DD_RUM && window.DD_RUM.stopSession) {
|
|
8
|
+
window.DD_RUM.stopSession();
|
|
9
|
+
return true;
|
|
10
|
+
} else {
|
|
11
|
+
return false;
|
|
12
|
+
}
|
|
13
|
+
`
|
|
14
|
+
const IS_RUM_ACTIVE_SCRIPT = 'return !!window.DD_RUM'
|
|
15
|
+
|
|
16
|
+
const DD_CIVISIBILITY_RUM_FLUSH_WAIT_MILLIS = 500
|
|
17
|
+
const DD_CIVISIBILITY_TEST_EXECUTION_ID_COOKIE_NAME = 'datadog-ci-visibility-test-execution-id'
|
|
18
|
+
|
|
19
|
+
// TODO: can we increase the supported version range?
|
|
20
|
+
addHook({
|
|
21
|
+
name: 'selenium-webdriver',
|
|
22
|
+
versions: ['>=4.11.0']
|
|
23
|
+
}, (seleniumPackage, seleniumVersion) => {
|
|
24
|
+
// TODO: do not turn this into async. Use promises
|
|
25
|
+
shimmer.wrap(seleniumPackage.WebDriver.prototype, 'get', get => async function () {
|
|
26
|
+
let traceId
|
|
27
|
+
const setTraceId = (inputTraceId) => {
|
|
28
|
+
traceId = inputTraceId
|
|
29
|
+
}
|
|
30
|
+
const getResult = await get.apply(this, arguments)
|
|
31
|
+
|
|
32
|
+
const isRumActive = await this.executeScript(IS_RUM_ACTIVE_SCRIPT)
|
|
33
|
+
const capabilities = await this.getCapabilities()
|
|
34
|
+
|
|
35
|
+
ciSeleniumDriverGetStartCh.publish({
|
|
36
|
+
setTraceId,
|
|
37
|
+
seleniumVersion,
|
|
38
|
+
browserName: capabilities.getBrowserName(),
|
|
39
|
+
browserVersion: capabilities.getBrowserVersion(),
|
|
40
|
+
isRumActive
|
|
41
|
+
})
|
|
42
|
+
|
|
43
|
+
await this.manage().addCookie({
|
|
44
|
+
name: DD_CIVISIBILITY_TEST_EXECUTION_ID_COOKIE_NAME,
|
|
45
|
+
value: traceId
|
|
46
|
+
})
|
|
47
|
+
|
|
48
|
+
return getResult
|
|
49
|
+
})
|
|
50
|
+
|
|
51
|
+
shimmer.wrap(seleniumPackage.WebDriver.prototype, 'quit', quit => async function () {
|
|
52
|
+
const isRumActive = await this.executeScript(RUM_STOP_SESSION_SCRIPT)
|
|
53
|
+
|
|
54
|
+
if (isRumActive) {
|
|
55
|
+
// We'll have time for RUM to flush the events (there's no callback to know when it's done)
|
|
56
|
+
await new Promise(resolve => {
|
|
57
|
+
setTimeout(() => {
|
|
58
|
+
resolve()
|
|
59
|
+
}, DD_CIVISIBILITY_RUM_FLUSH_WAIT_MILLIS)
|
|
60
|
+
})
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
await this.manage().deleteCookie(DD_CIVISIBILITY_TEST_EXECUTION_ID_COOKIE_NAME)
|
|
64
|
+
|
|
65
|
+
return quit.apply(this, arguments)
|
|
66
|
+
})
|
|
67
|
+
|
|
68
|
+
return seleniumPackage
|
|
69
|
+
})
|
|
@@ -16,7 +16,7 @@ const {
|
|
|
16
16
|
TEST_CODE_OWNERS,
|
|
17
17
|
ITR_CORRELATION_ID,
|
|
18
18
|
TEST_SOURCE_FILE,
|
|
19
|
-
|
|
19
|
+
TEST_EARLY_FLAKE_ENABLED,
|
|
20
20
|
TEST_IS_NEW,
|
|
21
21
|
TEST_IS_RETRY
|
|
22
22
|
} = require('../../dd-trace/src/plugins/util/test')
|
|
@@ -68,7 +68,7 @@ class CucumberPlugin extends CiPlugin {
|
|
|
68
68
|
}
|
|
69
69
|
)
|
|
70
70
|
if (isEarlyFlakeDetectionEnabled) {
|
|
71
|
-
this.testSessionSpan.setTag(
|
|
71
|
+
this.testSessionSpan.setTag(TEST_EARLY_FLAKE_ENABLED, 'true')
|
|
72
72
|
}
|
|
73
73
|
|
|
74
74
|
this.testSessionSpan.setTag(TEST_STATUS, status)
|
|
@@ -28,7 +28,7 @@ const {
|
|
|
28
28
|
TEST_SOURCE_FILE,
|
|
29
29
|
TEST_IS_NEW,
|
|
30
30
|
TEST_IS_RETRY,
|
|
31
|
-
|
|
31
|
+
TEST_EARLY_FLAKE_ENABLED
|
|
32
32
|
} = require('../../dd-trace/src/plugins/util/test')
|
|
33
33
|
const { isMarkedAsUnskippable } = require('../../datadog-plugin-jest/src/util')
|
|
34
34
|
const { ORIGIN_KEY, COMPONENT } = require('../../dd-trace/src/constants')
|
|
@@ -364,7 +364,7 @@ class CypressPlugin {
|
|
|
364
364
|
getTestModuleCommonTags(this.command, this.frameworkVersion, TEST_FRAMEWORK_NAME)
|
|
365
365
|
|
|
366
366
|
if (this.isEarlyFlakeDetectionEnabled) {
|
|
367
|
-
testSessionSpanMetadata[
|
|
367
|
+
testSessionSpanMetadata[TEST_EARLY_FLAKE_ENABLED] = 'true'
|
|
368
368
|
}
|
|
369
369
|
|
|
370
370
|
this.testSessionSpan = this.tracer.startSpan(`${TEST_FRAMEWORK_NAME}.test_session`, {
|
|
@@ -4,6 +4,16 @@ let knownTestsForSuite = []
|
|
|
4
4
|
let suiteTests = []
|
|
5
5
|
let earlyFlakeDetectionNumRetries = 0
|
|
6
6
|
|
|
7
|
+
// If the test is using multi domain with cy.origin, trying to access
|
|
8
|
+
// window properties will result in a cross origin error.
|
|
9
|
+
function safeGetRum (window) {
|
|
10
|
+
try {
|
|
11
|
+
return window.DD_RUM
|
|
12
|
+
} catch (e) {
|
|
13
|
+
return null
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
|
|
7
17
|
function isNewTest (test) {
|
|
8
18
|
return !knownTestsForSuite.includes(test.fullTitle())
|
|
9
19
|
}
|
|
@@ -62,7 +72,7 @@ before(function () {
|
|
|
62
72
|
|
|
63
73
|
after(() => {
|
|
64
74
|
cy.window().then(win => {
|
|
65
|
-
if (win
|
|
75
|
+
if (safeGetRum(win)) {
|
|
66
76
|
win.dispatchEvent(new Event('beforeunload'))
|
|
67
77
|
}
|
|
68
78
|
})
|
|
@@ -84,9 +94,15 @@ afterEach(function () {
|
|
|
84
94
|
testInfo.testSourceLine = Cypress.mocha.getRunner().currentRunnable.invocationDetails.line
|
|
85
95
|
} catch (e) {}
|
|
86
96
|
|
|
87
|
-
if (win
|
|
97
|
+
if (safeGetRum(win)) {
|
|
88
98
|
testInfo.isRUMActive = true
|
|
89
99
|
}
|
|
90
|
-
|
|
100
|
+
let coverage
|
|
101
|
+
try {
|
|
102
|
+
coverage = win.__coverage__
|
|
103
|
+
} catch (e) {
|
|
104
|
+
// ignore error and continue
|
|
105
|
+
}
|
|
106
|
+
cy.task('dd:afterEach', { test: testInfo, coverage })
|
|
91
107
|
})
|
|
92
108
|
})
|