dd-trace 4.16.0 → 4.18.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/index.d.ts +9 -1
- package/package.json +5 -4
- package/packages/datadog-instrumentations/src/body-parser.js +2 -1
- package/packages/datadog-instrumentations/src/cucumber.js +29 -4
- package/packages/datadog-instrumentations/src/express-mongo-sanitize.js +45 -0
- package/packages/datadog-instrumentations/src/express.js +2 -1
- package/packages/datadog-instrumentations/src/helpers/hooks.js +2 -1
- package/packages/datadog-instrumentations/src/jest.js +58 -20
- package/packages/datadog-instrumentations/src/knex.js +69 -1
- package/packages/datadog-instrumentations/src/mocha.js +34 -4
- package/packages/datadog-instrumentations/src/mongodb.js +63 -0
- package/packages/datadog-instrumentations/src/mongoose.js +140 -1
- package/packages/datadog-instrumentations/src/next.js +98 -23
- package/packages/datadog-instrumentations/src/playwright.js +22 -8
- package/packages/datadog-plugin-cucumber/src/index.js +17 -5
- package/packages/datadog-plugin-cypress/src/plugin.js +38 -8
- package/packages/datadog-plugin-http/src/client.js +2 -0
- package/packages/datadog-plugin-jest/src/index.js +29 -6
- package/packages/datadog-plugin-jest/src/util.js +45 -2
- package/packages/datadog-plugin-memcached/src/index.js +10 -5
- package/packages/datadog-plugin-mocha/src/index.js +25 -6
- package/packages/datadog-plugin-next/src/index.js +4 -3
- package/packages/datadog-plugin-playwright/src/index.js +4 -1
- package/packages/dd-trace/src/appsec/channels.js +3 -1
- package/packages/dd-trace/src/appsec/iast/analyzers/analyzers.js +2 -0
- package/packages/dd-trace/src/appsec/iast/analyzers/hardcoded-secret-analyzer.js +60 -0
- package/packages/dd-trace/src/appsec/iast/analyzers/hardcoded-secrets-rules.js +269 -0
- package/packages/dd-trace/src/appsec/iast/analyzers/hsts-header-missing-analyzer.js +5 -2
- package/packages/dd-trace/src/appsec/iast/analyzers/missing-header-analyzer.js +22 -4
- package/packages/dd-trace/src/appsec/iast/analyzers/nosql-injection-mongodb-analyzer.js +173 -0
- package/packages/dd-trace/src/appsec/iast/analyzers/sql-injection-analyzer.js +21 -1
- package/packages/dd-trace/src/appsec/iast/analyzers/unvalidated-redirect-analyzer.js +3 -3
- package/packages/dd-trace/src/appsec/iast/analyzers/vulnerability-analyzer.js +1 -2
- package/packages/dd-trace/src/appsec/iast/analyzers/xcontenttype-header-missing-analyzer.js +2 -2
- package/packages/dd-trace/src/appsec/iast/iast-log.js +9 -4
- package/packages/dd-trace/src/appsec/iast/iast-plugin.js +4 -0
- package/packages/dd-trace/src/appsec/iast/path-line.js +6 -1
- package/packages/dd-trace/src/appsec/iast/taint-tracking/operations.js +25 -12
- package/packages/dd-trace/src/appsec/iast/taint-tracking/plugin.js +4 -4
- package/packages/dd-trace/src/appsec/iast/taint-tracking/rewriter.js +13 -2
- package/packages/dd-trace/src/appsec/iast/taint-tracking/secure-marks-generator.js +13 -0
- package/packages/dd-trace/src/appsec/iast/taint-tracking/source-types.js +2 -1
- package/packages/dd-trace/src/appsec/iast/telemetry/index.js +1 -14
- package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-analyzers/json-sensitive-analyzer.js +16 -0
- package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-handler.js +22 -4
- package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-regex.js +9 -0
- package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/index.js +15 -2
- package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/utils.js +169 -0
- package/packages/dd-trace/src/appsec/iast/vulnerabilities.js +2 -0
- package/packages/dd-trace/src/appsec/iast/vulnerability-reporter.js +5 -1
- package/packages/dd-trace/src/appsec/index.js +31 -13
- package/packages/dd-trace/src/appsec/remote_config/manager.js +11 -3
- package/packages/dd-trace/src/appsec/waf/waf_context_wrapper.js +14 -1
- package/packages/dd-trace/src/ci-visibility/intelligent-test-runner/get-itr-configuration.js +4 -2
- package/packages/dd-trace/src/ci-visibility/intelligent-test-runner/get-skippable-suites.js +2 -0
- package/packages/dd-trace/src/config.js +37 -13
- package/packages/dd-trace/src/format.js +3 -0
- package/packages/dd-trace/src/git_properties.js +16 -15
- package/packages/dd-trace/src/plugin_manager.js +3 -1
- package/packages/dd-trace/src/plugins/util/ci.js +17 -0
- package/packages/dd-trace/src/plugins/util/git.js +26 -4
- package/packages/dd-trace/src/plugins/util/test.js +45 -2
- package/packages/dd-trace/src/profiling/config.js +20 -3
- package/packages/dd-trace/src/profiling/profilers/wall.js +51 -40
- package/packages/dd-trace/src/service-naming/extra-services.js +24 -0
- package/packages/dd-trace/src/telemetry/index.js +4 -0
- package/packages/dd-trace/src/telemetry/logs/index.js +65 -0
- package/packages/dd-trace/src/{appsec/iast/telemetry/log → telemetry/logs}/log-collector.js +9 -22
- package/packages/dd-trace/src/telemetry/metrics.js +0 -5
- package/packages/dd-trace/src/appsec/iast/telemetry/log/index.js +0 -87
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
|
-
const { addHook } = require('./helpers/instrument')
|
|
3
|
+
const { addHook, channel } = require('./helpers/instrument')
|
|
4
4
|
const { wrapThen } = require('./helpers/promise')
|
|
5
5
|
const { AsyncResource } = require('./helpers/instrument')
|
|
6
6
|
const shimmer = require('../../datadog-shimmer')
|
|
@@ -26,5 +26,144 @@ addHook({
|
|
|
26
26
|
}
|
|
27
27
|
|
|
28
28
|
shimmer.wrap(mongoose.Collection.prototype, 'addQueue', wrapAddQueue)
|
|
29
|
+
|
|
29
30
|
return mongoose
|
|
30
31
|
})
|
|
32
|
+
|
|
33
|
+
const startCh = channel('datadog:mongoose:model:filter:start')
|
|
34
|
+
const finishCh = channel('datadog:mongoose:model:filter:finish')
|
|
35
|
+
|
|
36
|
+
const collectionMethodsWithFilter = [
|
|
37
|
+
'count',
|
|
38
|
+
'countDocuments',
|
|
39
|
+
'deleteMany',
|
|
40
|
+
'deleteOne',
|
|
41
|
+
'find',
|
|
42
|
+
'findOne',
|
|
43
|
+
'findOneAndDelete',
|
|
44
|
+
'findOneAndReplace',
|
|
45
|
+
'replaceOne',
|
|
46
|
+
'remove'
|
|
47
|
+
]
|
|
48
|
+
|
|
49
|
+
const collectionMethodsWithTwoFilters = [
|
|
50
|
+
'findOneAndUpdate',
|
|
51
|
+
'updateMany',
|
|
52
|
+
'updateOne'
|
|
53
|
+
]
|
|
54
|
+
|
|
55
|
+
addHook({
|
|
56
|
+
name: 'mongoose',
|
|
57
|
+
versions: ['>=4.6.4 <5', '5', '6', '>=7'],
|
|
58
|
+
file: 'lib/model.js'
|
|
59
|
+
}, Model => {
|
|
60
|
+
[...collectionMethodsWithFilter, ...collectionMethodsWithTwoFilters].forEach(methodName => {
|
|
61
|
+
const useTwoArguments = collectionMethodsWithTwoFilters.includes(methodName)
|
|
62
|
+
if (!(methodName in Model)) return
|
|
63
|
+
|
|
64
|
+
shimmer.wrap(Model, methodName, method => {
|
|
65
|
+
return function wrappedModelMethod () {
|
|
66
|
+
if (!startCh.hasSubscribers) {
|
|
67
|
+
return method.apply(this, arguments)
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
const asyncResource = new AsyncResource('bound-anonymous-fn')
|
|
71
|
+
|
|
72
|
+
const filters = [arguments[0]]
|
|
73
|
+
if (useTwoArguments) {
|
|
74
|
+
filters.push(arguments[1])
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
const finish = asyncResource.bind(function () {
|
|
78
|
+
finishCh.publish()
|
|
79
|
+
})
|
|
80
|
+
|
|
81
|
+
let callbackWrapped = false
|
|
82
|
+
const lastArgumentIndex = arguments.length - 1
|
|
83
|
+
|
|
84
|
+
if (typeof arguments[lastArgumentIndex] === 'function') {
|
|
85
|
+
// is a callback, wrap it to execute finish()
|
|
86
|
+
shimmer.wrap(arguments, lastArgumentIndex, originalCb => {
|
|
87
|
+
return function () {
|
|
88
|
+
finish()
|
|
89
|
+
|
|
90
|
+
return originalCb.apply(this, arguments)
|
|
91
|
+
}
|
|
92
|
+
})
|
|
93
|
+
|
|
94
|
+
callbackWrapped = true
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
return asyncResource.runInAsyncScope(() => {
|
|
98
|
+
startCh.publish({
|
|
99
|
+
filters,
|
|
100
|
+
methodName
|
|
101
|
+
})
|
|
102
|
+
|
|
103
|
+
const res = method.apply(this, arguments)
|
|
104
|
+
|
|
105
|
+
// if it is not callback, wrap exec method and its then
|
|
106
|
+
if (!callbackWrapped) {
|
|
107
|
+
shimmer.wrap(res, 'exec', originalExec => {
|
|
108
|
+
return function wrappedExec () {
|
|
109
|
+
const execResult = originalExec.apply(this, arguments)
|
|
110
|
+
|
|
111
|
+
// wrap them method, wrap resolve and reject methods
|
|
112
|
+
shimmer.wrap(execResult, 'then', originalThen => {
|
|
113
|
+
return function wrappedThen () {
|
|
114
|
+
const resolve = arguments[0]
|
|
115
|
+
const reject = arguments[1]
|
|
116
|
+
|
|
117
|
+
// not using shimmer here because resolve/reject could be empty
|
|
118
|
+
arguments[0] = function wrappedResolve () {
|
|
119
|
+
finish()
|
|
120
|
+
|
|
121
|
+
if (resolve) {
|
|
122
|
+
return resolve.apply(this, arguments)
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
arguments[1] = function wrappedReject () {
|
|
127
|
+
finish()
|
|
128
|
+
|
|
129
|
+
if (reject) {
|
|
130
|
+
return reject.apply(this, arguments)
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
return originalThen.apply(this, arguments)
|
|
135
|
+
}
|
|
136
|
+
})
|
|
137
|
+
|
|
138
|
+
return execResult
|
|
139
|
+
}
|
|
140
|
+
})
|
|
141
|
+
}
|
|
142
|
+
return res
|
|
143
|
+
})
|
|
144
|
+
}
|
|
145
|
+
})
|
|
146
|
+
})
|
|
147
|
+
|
|
148
|
+
return Model
|
|
149
|
+
})
|
|
150
|
+
|
|
151
|
+
const sanitizeFilterFinishCh = channel('datadog:mongoose:sanitize-filter:finish')
|
|
152
|
+
|
|
153
|
+
addHook({
|
|
154
|
+
name: 'mongoose',
|
|
155
|
+
versions: ['6', '>=7'],
|
|
156
|
+
file: 'lib/helpers/query/sanitizeFilter.js'
|
|
157
|
+
}, sanitizeFilter => {
|
|
158
|
+
return shimmer.wrap(sanitizeFilter, function wrappedSanitizeFilter () {
|
|
159
|
+
const sanitizedObject = sanitizeFilter.apply(this, arguments)
|
|
160
|
+
|
|
161
|
+
if (sanitizeFilterFinishCh.hasSubscribers) {
|
|
162
|
+
sanitizeFilterFinishCh.publish({
|
|
163
|
+
sanitizedObject
|
|
164
|
+
})
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
return sanitizedObject
|
|
168
|
+
})
|
|
169
|
+
})
|
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
|
-
// TODO: either instrument all or none of the render functions
|
|
4
|
-
|
|
5
3
|
const { channel, addHook } = require('./helpers/instrument')
|
|
6
4
|
const shimmer = require('../../datadog-shimmer')
|
|
7
5
|
const { DD_MAJOR } = require('../../../version')
|
|
@@ -10,8 +8,13 @@ const startChannel = channel('apm:next:request:start')
|
|
|
10
8
|
const finishChannel = channel('apm:next:request:finish')
|
|
11
9
|
const errorChannel = channel('apm:next:request:error')
|
|
12
10
|
const pageLoadChannel = channel('apm:next:page:load')
|
|
11
|
+
const bodyParsedChannel = channel('apm:next:body-parsed')
|
|
12
|
+
const queryParsedChannel = channel('apm:next:query-parsed')
|
|
13
13
|
|
|
14
14
|
const requests = new WeakSet()
|
|
15
|
+
const nodeNextRequestsToNextRequests = new WeakMap()
|
|
16
|
+
|
|
17
|
+
const MIDDLEWARE_HEADER = 'x-middleware-invoke'
|
|
15
18
|
|
|
16
19
|
function wrapHandleRequest (handleRequest) {
|
|
17
20
|
return function (req, res, pathname, query) {
|
|
@@ -54,18 +57,6 @@ function wrapHandleApiRequestWithMatch (handleApiRequest) {
|
|
|
54
57
|
}
|
|
55
58
|
}
|
|
56
59
|
|
|
57
|
-
function wrapRenderToResponse (renderToResponse) {
|
|
58
|
-
return function (ctx) {
|
|
59
|
-
return instrument(ctx.req, ctx.res, () => renderToResponse.apply(this, arguments))
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
function wrapRenderErrorToResponse (renderErrorToResponse) {
|
|
64
|
-
return function (ctx) {
|
|
65
|
-
return instrument(ctx.req, ctx.res, () => renderErrorToResponse.apply(this, arguments))
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
|
|
69
60
|
function wrapRenderToHTML (renderToHTML) {
|
|
70
61
|
return function (req, res, pathname, query, parsedUrl) {
|
|
71
62
|
return instrument(req, res, () => renderToHTML.apply(this, arguments))
|
|
@@ -78,6 +69,18 @@ function wrapRenderErrorToHTML (renderErrorToHTML) {
|
|
|
78
69
|
}
|
|
79
70
|
}
|
|
80
71
|
|
|
72
|
+
function wrapRenderToResponse (renderToResponse) {
|
|
73
|
+
return function (ctx) {
|
|
74
|
+
return instrument(ctx.req, ctx.res, () => renderToResponse.apply(this, arguments))
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function wrapRenderErrorToResponse (renderErrorToResponse) {
|
|
79
|
+
return function (ctx) {
|
|
80
|
+
return instrument(ctx.req, ctx.res, () => renderErrorToResponse.apply(this, arguments))
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
81
84
|
function wrapFindPageComponents (findPageComponents) {
|
|
82
85
|
return function (pathname, query) {
|
|
83
86
|
const result = findPageComponents.apply(this, arguments)
|
|
@@ -112,7 +115,9 @@ function instrument (req, res, handler) {
|
|
|
112
115
|
req = req.originalRequest || req
|
|
113
116
|
res = res.originalResponse || res
|
|
114
117
|
|
|
115
|
-
|
|
118
|
+
// TODO support middleware properly in the future?
|
|
119
|
+
const isMiddleware = req.headers[MIDDLEWARE_HEADER]
|
|
120
|
+
if (isMiddleware || requests.has(req)) return handler()
|
|
116
121
|
|
|
117
122
|
requests.add(req)
|
|
118
123
|
|
|
@@ -152,6 +157,11 @@ function finish (ctx, result, err) {
|
|
|
152
157
|
errorChannel.publish(ctx)
|
|
153
158
|
}
|
|
154
159
|
|
|
160
|
+
const maybeNextRequest = nodeNextRequestsToNextRequests.get(ctx.req)
|
|
161
|
+
if (maybeNextRequest) {
|
|
162
|
+
ctx.nextRequest = maybeNextRequest
|
|
163
|
+
}
|
|
164
|
+
|
|
155
165
|
finishChannel.publish(ctx)
|
|
156
166
|
|
|
157
167
|
if (err) {
|
|
@@ -161,6 +171,24 @@ function finish (ctx, result, err) {
|
|
|
161
171
|
return result
|
|
162
172
|
}
|
|
163
173
|
|
|
174
|
+
// also wrapped in dist/server/future/route-handlers/app-route-route-handler.js
|
|
175
|
+
// in versions below 13.3.0 that support middleware,
|
|
176
|
+
// however, it is not provided as a class function or exported property
|
|
177
|
+
addHook({
|
|
178
|
+
name: 'next',
|
|
179
|
+
versions: ['>=13.3.0'],
|
|
180
|
+
file: 'dist/server/web/spec-extension/adapters/next-request.js'
|
|
181
|
+
}, NextRequestAdapter => {
|
|
182
|
+
shimmer.wrap(NextRequestAdapter.NextRequestAdapter, 'fromNodeNextRequest', fromNodeNextRequest => {
|
|
183
|
+
return function (nodeNextRequest) {
|
|
184
|
+
const nextRequest = fromNodeNextRequest.apply(this, arguments)
|
|
185
|
+
nodeNextRequestsToNextRequests.set(nodeNextRequest.originalRequest, nextRequest)
|
|
186
|
+
return nextRequest
|
|
187
|
+
}
|
|
188
|
+
})
|
|
189
|
+
return NextRequestAdapter
|
|
190
|
+
})
|
|
191
|
+
|
|
164
192
|
addHook({
|
|
165
193
|
name: 'next',
|
|
166
194
|
versions: ['>=11.1'],
|
|
@@ -173,27 +201,32 @@ addHook({
|
|
|
173
201
|
file: 'dist/next-server/server/serve-static.js'
|
|
174
202
|
}, serveStatic => shimmer.wrap(serveStatic, 'serveStatic', wrapServeStatic))
|
|
175
203
|
|
|
176
|
-
addHook({ name: 'next', versions: ['>=
|
|
204
|
+
addHook({ name: 'next', versions: ['>=11.1'], file: 'dist/server/next-server.js' }, nextServer => {
|
|
177
205
|
const Server = nextServer.default
|
|
178
206
|
|
|
179
207
|
shimmer.wrap(Server.prototype, 'handleRequest', wrapHandleRequest)
|
|
180
|
-
|
|
208
|
+
|
|
209
|
+
// Wrapping these makes sure any public API render methods called in a custom server
|
|
210
|
+
// are traced properly
|
|
211
|
+
// (instead of wrapping the top-level API methods, just wrapping these covers them all)
|
|
181
212
|
shimmer.wrap(Server.prototype, 'renderToResponse', wrapRenderToResponse)
|
|
182
213
|
shimmer.wrap(Server.prototype, 'renderErrorToResponse', wrapRenderErrorToResponse)
|
|
214
|
+
|
|
183
215
|
shimmer.wrap(Server.prototype, 'findPageComponents', wrapFindPageComponents)
|
|
184
216
|
|
|
185
217
|
return nextServer
|
|
186
218
|
})
|
|
187
219
|
|
|
188
|
-
|
|
220
|
+
// `handleApiRequest` changes parameters/implementation at 13.2.0
|
|
221
|
+
addHook({ name: 'next', versions: ['>=13.2'], file: 'dist/server/next-server.js' }, nextServer => {
|
|
189
222
|
const Server = nextServer.default
|
|
223
|
+
shimmer.wrap(Server.prototype, 'handleApiRequest', wrapHandleApiRequestWithMatch)
|
|
224
|
+
return nextServer
|
|
225
|
+
})
|
|
190
226
|
|
|
191
|
-
|
|
227
|
+
addHook({ name: 'next', versions: ['>=11.1 <13.2'], file: 'dist/server/next-server.js' }, nextServer => {
|
|
228
|
+
const Server = nextServer.default
|
|
192
229
|
shimmer.wrap(Server.prototype, 'handleApiRequest', wrapHandleApiRequest)
|
|
193
|
-
shimmer.wrap(Server.prototype, 'renderToResponse', wrapRenderToResponse)
|
|
194
|
-
shimmer.wrap(Server.prototype, 'renderErrorToResponse', wrapRenderErrorToResponse)
|
|
195
|
-
shimmer.wrap(Server.prototype, 'findPageComponents', wrapFindPageComponents)
|
|
196
|
-
|
|
197
230
|
return nextServer
|
|
198
231
|
})
|
|
199
232
|
|
|
@@ -206,9 +239,51 @@ addHook({
|
|
|
206
239
|
|
|
207
240
|
shimmer.wrap(Server.prototype, 'handleRequest', wrapHandleRequest)
|
|
208
241
|
shimmer.wrap(Server.prototype, 'handleApiRequest', wrapHandleApiRequest)
|
|
242
|
+
|
|
243
|
+
// Likewise with newer versions, these correlate to public API render methods for custom servers
|
|
244
|
+
// all public ones use these methods somewhere in their code path
|
|
209
245
|
shimmer.wrap(Server.prototype, 'renderToHTML', wrapRenderToHTML)
|
|
210
246
|
shimmer.wrap(Server.prototype, 'renderErrorToHTML', wrapRenderErrorToHTML)
|
|
247
|
+
|
|
211
248
|
shimmer.wrap(Server.prototype, 'findPageComponents', wrapFindPageComponents)
|
|
212
249
|
|
|
213
250
|
return nextServer
|
|
214
251
|
})
|
|
252
|
+
|
|
253
|
+
addHook({
|
|
254
|
+
name: 'next',
|
|
255
|
+
versions: ['>=13'],
|
|
256
|
+
file: 'dist/server/web/spec-extension/request.js'
|
|
257
|
+
}, request => {
|
|
258
|
+
const nextUrlDescriptor = Object.getOwnPropertyDescriptor(request.NextRequest.prototype, 'nextUrl')
|
|
259
|
+
shimmer.wrap(nextUrlDescriptor, 'get', function (originalGet) {
|
|
260
|
+
return function wrappedGet () {
|
|
261
|
+
const nextUrl = originalGet.apply(this, arguments)
|
|
262
|
+
if (queryParsedChannel.hasSubscribers) {
|
|
263
|
+
const query = {}
|
|
264
|
+
for (const key of nextUrl.searchParams.keys()) {
|
|
265
|
+
if (!query[key]) {
|
|
266
|
+
query[key] = nextUrl.searchParams.getAll(key)
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
queryParsedChannel.publish({ query })
|
|
271
|
+
}
|
|
272
|
+
return nextUrl
|
|
273
|
+
}
|
|
274
|
+
})
|
|
275
|
+
|
|
276
|
+
Object.defineProperty(request.NextRequest.prototype, 'nextUrl', nextUrlDescriptor)
|
|
277
|
+
|
|
278
|
+
shimmer.massWrap(request.NextRequest.prototype, ['text', 'json'], function (originalMethod) {
|
|
279
|
+
return async function wrappedJson () {
|
|
280
|
+
const body = await originalMethod.apply(this, arguments)
|
|
281
|
+
bodyParsedChannel.publish({
|
|
282
|
+
body
|
|
283
|
+
})
|
|
284
|
+
return body
|
|
285
|
+
}
|
|
286
|
+
})
|
|
287
|
+
|
|
288
|
+
return request
|
|
289
|
+
})
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
const { addHook, channel, AsyncResource } = require('./helpers/instrument')
|
|
2
2
|
const shimmer = require('../../datadog-shimmer')
|
|
3
|
+
const { parseAnnotations } = require('../../dd-trace/src/plugins/util/test')
|
|
3
4
|
|
|
4
5
|
const testStartCh = channel('ci:playwright:test:start')
|
|
5
6
|
const testFinishCh = channel('ci:playwright:test:finish')
|
|
@@ -103,7 +104,11 @@ function testBeginHandler (test) {
|
|
|
103
104
|
})
|
|
104
105
|
}
|
|
105
106
|
|
|
106
|
-
function testEndHandler (test, testStatus, error) {
|
|
107
|
+
function testEndHandler (test, annotations, testStatus, error) {
|
|
108
|
+
let annotationTags
|
|
109
|
+
if (annotations.length) {
|
|
110
|
+
annotationTags = parseAnnotations(annotations)
|
|
111
|
+
}
|
|
107
112
|
const { _requireFile: testSuiteAbsolutePath, results, _type } = test
|
|
108
113
|
|
|
109
114
|
if (_type === 'beforeAll' || _type === 'afterAll') {
|
|
@@ -113,7 +118,7 @@ function testEndHandler (test, testStatus, error) {
|
|
|
113
118
|
const testResult = results[results.length - 1]
|
|
114
119
|
const testAsyncResource = testToAr.get(test)
|
|
115
120
|
testAsyncResource.runInAsyncScope(() => {
|
|
116
|
-
testFinishCh.publish({ testStatus, steps: testResult.steps, error })
|
|
121
|
+
testFinishCh.publish({ testStatus, steps: testResult.steps, error, extraTags: annotationTags })
|
|
117
122
|
})
|
|
118
123
|
|
|
119
124
|
if (!testSuiteToTestStatuses.has(testSuiteAbsolutePath)) {
|
|
@@ -172,7 +177,7 @@ function dispatcherHook (dispatcherExport) {
|
|
|
172
177
|
const { results } = test
|
|
173
178
|
const testResult = results[results.length - 1]
|
|
174
179
|
|
|
175
|
-
testEndHandler(test, STATUS_TO_TEST_STATUS[testResult.status], testResult.error)
|
|
180
|
+
testEndHandler(test, params.annotations, STATUS_TO_TEST_STATUS[testResult.status], testResult.error)
|
|
176
181
|
}
|
|
177
182
|
})
|
|
178
183
|
|
|
@@ -181,6 +186,15 @@ function dispatcherHook (dispatcherExport) {
|
|
|
181
186
|
return dispatcherExport
|
|
182
187
|
}
|
|
183
188
|
|
|
189
|
+
function getTestByTestId (dispatcher, testId) {
|
|
190
|
+
if (dispatcher._testById) {
|
|
191
|
+
return dispatcher._testById.get(testId)?.test
|
|
192
|
+
}
|
|
193
|
+
if (dispatcher._allTests) {
|
|
194
|
+
return dispatcher._allTests.find(({ id }) => id === testId)
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
|
|
184
198
|
function dispatcherHookNew (dispatcherExport, runWrapper) {
|
|
185
199
|
shimmer.wrap(dispatcherExport.Dispatcher.prototype, 'run', runWrapper)
|
|
186
200
|
shimmer.wrap(dispatcherExport.Dispatcher.prototype, '_createWorker', createWorker => function () {
|
|
@@ -188,13 +202,13 @@ function dispatcherHookNew (dispatcherExport, runWrapper) {
|
|
|
188
202
|
const worker = createWorker.apply(this, arguments)
|
|
189
203
|
|
|
190
204
|
worker.on('testBegin', ({ testId }) => {
|
|
191
|
-
const
|
|
205
|
+
const test = getTestByTestId(dispatcher, testId)
|
|
192
206
|
testBeginHandler(test)
|
|
193
207
|
})
|
|
194
|
-
worker.on('testEnd', ({ testId, status, errors }) => {
|
|
195
|
-
const
|
|
208
|
+
worker.on('testEnd', ({ testId, status, errors, annotations }) => {
|
|
209
|
+
const test = getTestByTestId(dispatcher, testId)
|
|
196
210
|
|
|
197
|
-
testEndHandler(test, STATUS_TO_TEST_STATUS[status], errors && errors[0])
|
|
211
|
+
testEndHandler(test, annotations, STATUS_TO_TEST_STATUS[status], errors && errors[0])
|
|
198
212
|
})
|
|
199
213
|
|
|
200
214
|
return worker
|
|
@@ -221,7 +235,7 @@ function runnerHook (runnerExport, playwrightVersion) {
|
|
|
221
235
|
// because they were skipped
|
|
222
236
|
tests.forEach(test => {
|
|
223
237
|
testBeginHandler(test)
|
|
224
|
-
testEndHandler(test, 'skip')
|
|
238
|
+
testEndHandler(test, [], 'skip')
|
|
225
239
|
})
|
|
226
240
|
})
|
|
227
241
|
|
|
@@ -10,7 +10,9 @@ const {
|
|
|
10
10
|
finishAllTraceSpans,
|
|
11
11
|
getTestSuitePath,
|
|
12
12
|
getTestSuiteCommonTags,
|
|
13
|
-
addIntelligentTestRunnerSpanTags
|
|
13
|
+
addIntelligentTestRunnerSpanTags,
|
|
14
|
+
TEST_ITR_UNSKIPPABLE,
|
|
15
|
+
TEST_ITR_FORCED_RUN
|
|
14
16
|
} = require('../../dd-trace/src/plugins/util/test')
|
|
15
17
|
const { RESOURCE_NAME } = require('../../../ext/tags')
|
|
16
18
|
const { COMPONENT, ERROR_MESSAGE } = require('../../dd-trace/src/constants')
|
|
@@ -29,7 +31,9 @@ class CucumberPlugin extends CiPlugin {
|
|
|
29
31
|
status,
|
|
30
32
|
isSuitesSkipped,
|
|
31
33
|
numSkippedSuites,
|
|
32
|
-
testCodeCoverageLinesTotal
|
|
34
|
+
testCodeCoverageLinesTotal,
|
|
35
|
+
hasUnskippableSuites,
|
|
36
|
+
hasForcedToRunSuites
|
|
33
37
|
}) => {
|
|
34
38
|
const { isSuitesSkippingEnabled, isCodeCoverageEnabled } = this.itrConfig || {}
|
|
35
39
|
addIntelligentTestRunnerSpanTags(
|
|
@@ -41,7 +45,9 @@ class CucumberPlugin extends CiPlugin {
|
|
|
41
45
|
isCodeCoverageEnabled,
|
|
42
46
|
testCodeCoverageLinesTotal,
|
|
43
47
|
skippingCount: numSkippedSuites,
|
|
44
|
-
skippingType: 'suite'
|
|
48
|
+
skippingType: 'suite',
|
|
49
|
+
hasUnskippableSuites,
|
|
50
|
+
hasForcedToRunSuites
|
|
45
51
|
}
|
|
46
52
|
)
|
|
47
53
|
|
|
@@ -55,13 +61,19 @@ class CucumberPlugin extends CiPlugin {
|
|
|
55
61
|
this.tracer._exporter.flush()
|
|
56
62
|
})
|
|
57
63
|
|
|
58
|
-
this.addSub('ci:cucumber:test-suite:start', (
|
|
64
|
+
this.addSub('ci:cucumber:test-suite:start', ({ testSuitePath, isUnskippable, isForcedToRun }) => {
|
|
59
65
|
const testSuiteMetadata = getTestSuiteCommonTags(
|
|
60
66
|
this.command,
|
|
61
67
|
this.frameworkVersion,
|
|
62
|
-
|
|
68
|
+
testSuitePath,
|
|
63
69
|
'cucumber'
|
|
64
70
|
)
|
|
71
|
+
if (isUnskippable) {
|
|
72
|
+
testSuiteMetadata[TEST_ITR_UNSKIPPABLE] = 'true'
|
|
73
|
+
}
|
|
74
|
+
if (isForcedToRun) {
|
|
75
|
+
testSuiteMetadata[TEST_ITR_FORCED_RUN] = 'true'
|
|
76
|
+
}
|
|
65
77
|
this.testSuiteSpan = this.tracer.startSpan('cucumber.test_suite', {
|
|
66
78
|
childOf: this.testModuleSpan,
|
|
67
79
|
tags: {
|
|
@@ -21,11 +21,14 @@ const {
|
|
|
21
21
|
getCoveredFilenamesFromCoverage,
|
|
22
22
|
getTestSuitePath,
|
|
23
23
|
addIntelligentTestRunnerSpanTags,
|
|
24
|
-
TEST_SKIPPED_BY_ITR
|
|
24
|
+
TEST_SKIPPED_BY_ITR,
|
|
25
|
+
TEST_ITR_UNSKIPPABLE,
|
|
26
|
+
TEST_ITR_FORCED_RUN
|
|
25
27
|
} = require('../../dd-trace/src/plugins/util/test')
|
|
26
28
|
const { ORIGIN_KEY, COMPONENT } = require('../../dd-trace/src/constants')
|
|
27
29
|
const log = require('../../dd-trace/src/log')
|
|
28
30
|
const NoopTracer = require('../../dd-trace/src/noop/tracer')
|
|
31
|
+
const { isMarkedAsUnskippable } = require('../../datadog-plugin-jest/src/util')
|
|
29
32
|
|
|
30
33
|
const TEST_FRAMEWORK_NAME = 'cypress'
|
|
31
34
|
|
|
@@ -185,8 +188,11 @@ module.exports = (on, config) => {
|
|
|
185
188
|
let isSuitesSkippingEnabled = false
|
|
186
189
|
let isCodeCoverageEnabled = false
|
|
187
190
|
let testsToSkip = []
|
|
191
|
+
const unskippableSuites = []
|
|
192
|
+
let hasForcedToRunSuites = false
|
|
193
|
+
let hasUnskippableSuites = false
|
|
188
194
|
|
|
189
|
-
function getTestSpan (testName, testSuite) {
|
|
195
|
+
function getTestSpan (testName, testSuite, isUnskippable, isForcedToRun) {
|
|
190
196
|
const testSuiteTags = {
|
|
191
197
|
[TEST_COMMAND]: command,
|
|
192
198
|
[TEST_COMMAND]: command,
|
|
@@ -212,6 +218,16 @@ module.exports = (on, config) => {
|
|
|
212
218
|
testSpanMetadata[TEST_CODE_OWNERS] = codeOwners
|
|
213
219
|
}
|
|
214
220
|
|
|
221
|
+
if (isUnskippable) {
|
|
222
|
+
hasUnskippableSuites = true
|
|
223
|
+
testSpanMetadata[TEST_ITR_UNSKIPPABLE] = 'true'
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
if (isForcedToRun) {
|
|
227
|
+
hasForcedToRunSuites = true
|
|
228
|
+
testSpanMetadata[TEST_ITR_FORCED_RUN] = 'true'
|
|
229
|
+
}
|
|
230
|
+
|
|
215
231
|
return tracer.startSpan(`${TEST_FRAMEWORK_NAME}.test`, {
|
|
216
232
|
childOf,
|
|
217
233
|
tags: {
|
|
@@ -233,13 +249,21 @@ module.exports = (on, config) => {
|
|
|
233
249
|
isCodeCoverageEnabled = itrConfig.isCodeCoverageEnabled
|
|
234
250
|
}
|
|
235
251
|
|
|
236
|
-
getSkippableTests(isSuitesSkippingEnabled, tracer, testConfiguration).then(({ err, skippableTests }) => {
|
|
252
|
+
return getSkippableTests(isSuitesSkippingEnabled, tracer, testConfiguration).then(({ err, skippableTests }) => {
|
|
237
253
|
if (err) {
|
|
238
254
|
log.error(err)
|
|
239
255
|
} else {
|
|
240
256
|
testsToSkip = skippableTests || []
|
|
241
257
|
}
|
|
242
258
|
|
|
259
|
+
// `details.specs` are test files
|
|
260
|
+
details.specs.forEach(({ absolute, relative }) => {
|
|
261
|
+
const isUnskippableSuite = isMarkedAsUnskippable({ path: absolute })
|
|
262
|
+
if (isUnskippableSuite) {
|
|
263
|
+
unskippableSuites.push(relative)
|
|
264
|
+
}
|
|
265
|
+
})
|
|
266
|
+
|
|
243
267
|
const childOf = getTestParentSpan(tracer)
|
|
244
268
|
rootDir = getRootDir(details)
|
|
245
269
|
|
|
@@ -340,7 +364,9 @@ module.exports = (on, config) => {
|
|
|
340
364
|
isSuitesSkippingEnabled,
|
|
341
365
|
isCodeCoverageEnabled,
|
|
342
366
|
skippingType: 'test',
|
|
343
|
-
skippingCount: skippedTests.length
|
|
367
|
+
skippingCount: skippedTests.length,
|
|
368
|
+
hasForcedToRunSuites,
|
|
369
|
+
hasUnskippableSuites
|
|
344
370
|
}
|
|
345
371
|
)
|
|
346
372
|
|
|
@@ -384,17 +410,21 @@ module.exports = (on, config) => {
|
|
|
384
410
|
},
|
|
385
411
|
'dd:beforeEach': (test) => {
|
|
386
412
|
const { testName, testSuite } = test
|
|
387
|
-
|
|
388
|
-
if (testsToSkip.find(test => {
|
|
413
|
+
const shouldSkip = !!testsToSkip.find(test => {
|
|
389
414
|
return testName === test.name && testSuite === test.suite
|
|
390
|
-
})
|
|
415
|
+
})
|
|
416
|
+
const isUnskippable = unskippableSuites.includes(testSuite)
|
|
417
|
+
const isForcedToRun = shouldSkip && isUnskippable
|
|
418
|
+
|
|
419
|
+
// skip test
|
|
420
|
+
if (shouldSkip && !isUnskippable) {
|
|
391
421
|
skippedTests.push(test)
|
|
392
422
|
isTestsSkipped = true
|
|
393
423
|
return { shouldSkip: true }
|
|
394
424
|
}
|
|
395
425
|
|
|
396
426
|
if (!activeSpan) {
|
|
397
|
-
activeSpan = getTestSpan(testName, testSuite)
|
|
427
|
+
activeSpan = getTestSpan(testName, testSuite, isUnskippable, isForcedToRun)
|
|
398
428
|
}
|
|
399
429
|
|
|
400
430
|
return activeSpan ? { traceId: activeSpan.context().toTraceId() } : {}
|
|
@@ -76,6 +76,7 @@ class HttpClientPlugin extends ClientPlugin {
|
|
|
76
76
|
}
|
|
77
77
|
|
|
78
78
|
finish ({ req, res, span }) {
|
|
79
|
+
if (!span) return
|
|
79
80
|
if (res) {
|
|
80
81
|
const status = res.status || res.statusCode
|
|
81
82
|
|
|
@@ -98,6 +99,7 @@ class HttpClientPlugin extends ClientPlugin {
|
|
|
98
99
|
}
|
|
99
100
|
|
|
100
101
|
error ({ span, error }) {
|
|
102
|
+
if (!span) return
|
|
101
103
|
if (error) {
|
|
102
104
|
span.addTags({
|
|
103
105
|
[ERROR_TYPE]: error.name,
|
|
@@ -10,7 +10,9 @@ const {
|
|
|
10
10
|
TEST_PARAMETERS,
|
|
11
11
|
TEST_COMMAND,
|
|
12
12
|
TEST_FRAMEWORK_VERSION,
|
|
13
|
-
TEST_SOURCE_START
|
|
13
|
+
TEST_SOURCE_START,
|
|
14
|
+
TEST_ITR_UNSKIPPABLE,
|
|
15
|
+
TEST_ITR_FORCED_RUN
|
|
14
16
|
} = require('../../dd-trace/src/plugins/util/test')
|
|
15
17
|
const { COMPONENT } = require('../../dd-trace/src/constants')
|
|
16
18
|
const id = require('../../dd-trace/src/id')
|
|
@@ -50,11 +52,19 @@ class JestPlugin extends CiPlugin {
|
|
|
50
52
|
isSuitesSkippingEnabled,
|
|
51
53
|
isCodeCoverageEnabled,
|
|
52
54
|
testCodeCoverageLinesTotal,
|
|
53
|
-
numSkippedSuites
|
|
55
|
+
numSkippedSuites,
|
|
56
|
+
hasUnskippableSuites,
|
|
57
|
+
hasForcedToRunSuites,
|
|
58
|
+
error
|
|
54
59
|
}) => {
|
|
55
60
|
this.testSessionSpan.setTag(TEST_STATUS, status)
|
|
56
61
|
this.testModuleSpan.setTag(TEST_STATUS, status)
|
|
57
62
|
|
|
63
|
+
if (error) {
|
|
64
|
+
this.testSessionSpan.setTag('error', error)
|
|
65
|
+
this.testModuleSpan.setTag('error', error)
|
|
66
|
+
}
|
|
67
|
+
|
|
58
68
|
addIntelligentTestRunnerSpanTags(
|
|
59
69
|
this.testSessionSpan,
|
|
60
70
|
this.testModuleSpan,
|
|
@@ -64,7 +74,9 @@ class JestPlugin extends CiPlugin {
|
|
|
64
74
|
isCodeCoverageEnabled,
|
|
65
75
|
testCodeCoverageLinesTotal,
|
|
66
76
|
skippingType: 'suite',
|
|
67
|
-
skippingCount: numSkippedSuites
|
|
77
|
+
skippingCount: numSkippedSuites,
|
|
78
|
+
hasUnskippableSuites,
|
|
79
|
+
hasForcedToRunSuites
|
|
68
80
|
}
|
|
69
81
|
)
|
|
70
82
|
|
|
@@ -89,7 +101,9 @@ class JestPlugin extends CiPlugin {
|
|
|
89
101
|
const {
|
|
90
102
|
_ddTestSessionId: testSessionId,
|
|
91
103
|
_ddTestCommand: testCommand,
|
|
92
|
-
_ddTestModuleId: testModuleId
|
|
104
|
+
_ddTestModuleId: testModuleId,
|
|
105
|
+
_ddForcedToRun,
|
|
106
|
+
_ddUnskippable
|
|
93
107
|
} = testEnvironmentOptions
|
|
94
108
|
|
|
95
109
|
const testSessionSpanContext = this.tracer.extract('text_map', {
|
|
@@ -99,6 +113,13 @@ class JestPlugin extends CiPlugin {
|
|
|
99
113
|
|
|
100
114
|
const testSuiteMetadata = getTestSuiteCommonTags(testCommand, frameworkVersion, testSuite, 'jest')
|
|
101
115
|
|
|
116
|
+
if (_ddUnskippable) {
|
|
117
|
+
testSuiteMetadata[TEST_ITR_UNSKIPPABLE] = 'true'
|
|
118
|
+
if (_ddForcedToRun) {
|
|
119
|
+
testSuiteMetadata[TEST_ITR_FORCED_RUN] = 'true'
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
102
123
|
this.testSuiteSpan = this.tracer.startSpan('jest.test_suite', {
|
|
103
124
|
childOf: testSessionSpanContext,
|
|
104
125
|
tags: {
|
|
@@ -135,9 +156,11 @@ class JestPlugin extends CiPlugin {
|
|
|
135
156
|
})
|
|
136
157
|
})
|
|
137
158
|
|
|
138
|
-
this.addSub('ci:jest:test-suite:finish', ({ status, errorMessage }) => {
|
|
159
|
+
this.addSub('ci:jest:test-suite:finish', ({ status, errorMessage, error }) => {
|
|
139
160
|
this.testSuiteSpan.setTag(TEST_STATUS, status)
|
|
140
|
-
if (
|
|
161
|
+
if (error) {
|
|
162
|
+
this.testSuiteSpan.setTag('error', error)
|
|
163
|
+
} else if (errorMessage) {
|
|
141
164
|
this.testSuiteSpan.setTag('error', new Error(errorMessage))
|
|
142
165
|
}
|
|
143
166
|
this.testSuiteSpan.finish()
|