1
1
import { defineIntegration } from '../integration' ;
2
- import type { IntegrationFn } from '../types-hoist' ;
3
- import { type AddRequestDataToEventOptions , addNormalizedRequestDataToEvent } from '../utils-hoist/requestdata' ;
2
+ import type { Event , IntegrationFn , RequestEventData } from '../types-hoist' ;
3
+ import { parseCookie } from '../utils-hoist/cookie' ;
4
+ import { getClientIPAddress , ipHeaderNames } from '../utils-hoist/vendor/getIpAddress' ;
4
5
5
- export type RequestDataIntegrationOptions = {
6
- /**
7
- * Controls what data is pulled from the request and added to the event
8
- */
9
- include ?: {
10
- cookies ?: boolean ;
11
- data ?: boolean ;
12
- headers ?: boolean ;
13
- ip ?: boolean ;
14
- query_string ?: boolean ;
15
- url ?: boolean ;
16
- user ?:
17
- | boolean
18
- | {
19
- id ?: boolean ;
20
- username ?: boolean ;
21
- email ?: boolean ;
22
- } ;
23
- } ;
6
+ interface RequestDataIncludeOptions {
7
+ cookies ?: boolean ;
8
+ data ?: boolean ;
9
+ headers ?: boolean ;
10
+ ip ?: boolean ;
11
+ query_string ?: boolean ;
12
+ url ?: boolean ;
13
+ }
24
14
15
+ type RequestDataIntegrationOptions = {
25
16
/**
26
- * Whether to identify transactions by parameterized path, parameterized path with method, or handler name.
27
- * @deprecated This option does not do anything anymore, and will be removed in v9.
17
+ * Controls what data is pulled from the request and added to the event.
28
18
*/
29
- transactionNamingScheme ?: 'path' | 'methodPath' | 'handler' ;
19
+ include ?: RequestDataIncludeOptions ;
30
20
} ;
31
21
32
- const DEFAULT_OPTIONS = {
33
- include : {
34
- cookies : true ,
35
- data : true ,
36
- headers : true ,
37
- ip : false ,
38
- query_string : true ,
39
- url : true ,
40
- user : {
41
- id : true ,
42
- username : true ,
43
- email : true ,
44
- } ,
45
- } ,
46
- transactionNamingScheme : 'methodPath' as const ,
47
- } ;
22
+ const DEFAULT_INCLUDE = {
23
+ cookies : true ,
24
+ data : true ,
25
+ headers : true ,
26
+ ip : false ,
27
+ query_string : true ,
28
+ url : true ,
29
+ } satisfies RequestDataIncludeOptions ;
48
30
49
31
const INTEGRATION_NAME = 'RequestData' ;
50
32
51
33
const _requestDataIntegration = ( ( options : RequestDataIntegrationOptions = { } ) => {
52
- const _options : Required < RequestDataIntegrationOptions > = {
53
- ...DEFAULT_OPTIONS ,
54
- ...options ,
55
- include : {
56
- ...DEFAULT_OPTIONS . include ,
57
- ...options . include ,
58
- user :
59
- options . include && typeof options . include . user === 'boolean'
60
- ? options . include . user
61
- : {
62
- ...DEFAULT_OPTIONS . include . user ,
63
- // Unclear why TS still thinks `options.include.user` could be a boolean at this point
64
- ...( ( options . include || { } ) . user as Record < string , boolean > ) ,
65
- } ,
66
- } ,
34
+ const include = {
35
+ ...DEFAULT_INCLUDE ,
36
+ ...options . include ,
67
37
} ;
68
38
69
39
return {
70
40
name : INTEGRATION_NAME ,
71
41
processEvent ( event ) {
72
- // Note: In the long run, most of the logic here should probably move into the request data utility functions. For
73
- // the moment it lives here, though, until https://github.com/getsentry/sentry-javascript/issues/5718 is addressed.
74
- // (TL;DR: Those functions touch many parts of the repo in many different ways, and need to be cleaned up. Once
75
- // that's happened, it will be easier to add this logic in without worrying about unexpected side effects.)
76
-
77
42
const { sdkProcessingMetadata = { } } = event ;
78
43
const { normalizedRequest, ipAddress } = sdkProcessingMetadata ;
79
44
80
- const addRequestDataOptions = convertReqDataIntegrationOptsToAddReqDataOpts ( _options ) ;
81
-
82
- // If this is set, it takes precedence over the plain request object
83
45
if ( normalizedRequest ) {
84
- // TODO: user???
85
- addNormalizedRequestDataToEvent ( event , normalizedRequest , { ipAddress } , addRequestDataOptions ) ;
46
+ addNormalizedRequestDataToEvent ( event , normalizedRequest , { ipAddress } , include ) ;
86
47
return event ;
87
48
}
88
49
@@ -97,42 +58,75 @@ const _requestDataIntegration = ((options: RequestDataIntegrationOptions = {}) =
97
58
*/
98
59
export const requestDataIntegration = defineIntegration ( _requestDataIntegration ) ;
99
60
100
- /** Convert this integration's options to match what `addRequestDataToEvent` expects */
101
- /** TODO: Can possibly be deleted once https://github.com/getsentry/sentry-javascript/issues/5718 is fixed */
102
- function convertReqDataIntegrationOptsToAddReqDataOpts (
103
- integrationOptions : Required < RequestDataIntegrationOptions > ,
104
- ) : AddRequestDataToEventOptions {
105
- const {
106
- include : { ip, user, ...requestOptions } ,
107
- } = integrationOptions ;
108
-
109
- const requestIncludeKeys : string [ ] = [ 'method' ] ;
110
- for ( const [ key , value ] of Object . entries ( requestOptions ) ) {
111
- if ( value ) {
112
- requestIncludeKeys . push ( key ) ;
61
+ /**
62
+ * Add already normalized request data to an event.
63
+ * This mutates the passed in event.
64
+ */
65
+ function addNormalizedRequestDataToEvent (
66
+ event : Event ,
67
+ req : RequestEventData ,
68
+ // Data that should not go into `event.request` but is somehow related to requests
69
+ additionalData : { ipAddress ?: string } ,
70
+ include : RequestDataIncludeOptions ,
71
+ ) : void {
72
+ event . request = {
73
+ ...event . request ,
74
+ ...extractNormalizedRequestData ( req , include ) ,
75
+ } ;
76
+
77
+ if ( include . ip ) {
78
+ const ip = ( req . headers && getClientIPAddress ( req . headers ) ) || additionalData . ipAddress ;
79
+ if ( ip ) {
80
+ event . user = {
81
+ ...event . user ,
82
+ ip_address : ip ,
83
+ } ;
113
84
}
114
85
}
86
+ }
115
87
116
- let addReqDataUserOpt ;
117
- if ( user === undefined ) {
118
- addReqDataUserOpt = true ;
119
- } else if ( typeof user === 'boolean' ) {
120
- addReqDataUserOpt = user ;
121
- } else {
122
- const userIncludeKeys : string [ ] = [ ] ;
123
- for ( const [ key , value ] of Object . entries ( user ) ) {
124
- if ( value ) {
125
- userIncludeKeys . push ( key ) ;
126
- }
88
+ function extractNormalizedRequestData (
89
+ normalizedRequest : RequestEventData ,
90
+ include : RequestDataIncludeOptions ,
91
+ ) : RequestEventData {
92
+ const requestData : RequestEventData = { } ;
93
+ const headers = { ...normalizedRequest . headers } ;
94
+
95
+ if ( include . headers ) {
96
+ requestData . headers = headers ;
97
+
98
+ // Remove the Cookie header in case cookie data should not be included in the event
99
+ if ( ! include . cookies ) {
100
+ delete ( headers as { cookie ?: string } ) . cookie ;
101
+ }
102
+
103
+ // Remove IP headers in case IP data should not be included in the event
104
+ if ( ! include . ip ) {
105
+ ipHeaderNames . forEach ( ipHeaderName => {
106
+ // eslint-disable-next-line @typescript-eslint/no-dynamic-delete
107
+ delete ( headers as Record < string , unknown > ) [ ipHeaderName ] ;
108
+ } ) ;
127
109
}
128
- addReqDataUserOpt = userIncludeKeys ;
129
110
}
130
111
131
- return {
132
- include : {
133
- ip,
134
- user : addReqDataUserOpt ,
135
- request : requestIncludeKeys . length !== 0 ? requestIncludeKeys : undefined ,
136
- } ,
137
- } ;
112
+ requestData . method = normalizedRequest . method ;
113
+
114
+ if ( include . url ) {
115
+ requestData . url = normalizedRequest . url ;
116
+ }
117
+
118
+ if ( include . cookies ) {
119
+ const cookies = normalizedRequest . cookies || ( headers && headers . cookie ? parseCookie ( headers . cookie ) : undefined ) ;
120
+ requestData . cookies = cookies || { } ;
121
+ }
122
+
123
+ if ( include . query_string ) {
124
+ requestData . query_string = normalizedRequest . query_string ;
125
+ }
126
+
127
+ if ( include . data ) {
128
+ requestData . data = normalizedRequest . data ;
129
+ }
130
+
131
+ return requestData ;
138
132
}
0 commit comments