1
+ import { createEventEnvelope , getClient , withScope } from '@sentry/core' ;
2
+ import type { FeedbackEvent , TransportMakeRequestResponse } from '@sentry/types' ;
1
3
import { getLocationHref } from '@sentry/utils' ;
2
-
3
- import { FEEDBACK_API_SOURCE } from './constants' ;
4
+ import { FEEDBACK_API_SOURCE , FEEDBACK_WIDGET_SOURCE } from './constants' ;
4
5
import type { SendFeedbackOptions } from './types' ;
5
- import { sendFeedbackRequest } from './util/sendFeedbackRequest ' ;
6
+ import { prepareFeedbackEvent } from './util/prepareFeedbackEvent ' ;
6
7
7
8
interface SendFeedbackParams {
8
9
message : string ;
@@ -15,24 +16,124 @@ interface SendFeedbackParams {
15
16
/**
16
17
* Public API to send a Feedback item to Sentry
17
18
*/
18
- export function sendFeedback (
19
+ export async function sendFeedback (
19
20
{ name, email, message, source = FEEDBACK_API_SOURCE , url = getLocationHref ( ) } : SendFeedbackParams ,
20
- options : SendFeedbackOptions = { } ,
21
- ) : ReturnType < typeof sendFeedbackRequest > {
21
+ { includeReplay = true } : SendFeedbackOptions = { } ,
22
+ ) : Promise < TransportMakeRequestResponse > {
22
23
if ( ! message ) {
23
24
throw new Error ( 'Unable to submit feedback with empty message' ) ;
24
25
}
25
26
26
- return sendFeedbackRequest (
27
- {
27
+ const client = getClient ( ) ;
28
+ const transport = client && client . getTransport ( ) ;
29
+ const dsn = client && client . getDsn ( ) ;
30
+
31
+ if ( ! client || ! transport || ! dsn ) {
32
+ throw new Error ( 'Invalid Sentry client' ) ;
33
+ }
34
+
35
+ const baseEvent : FeedbackEvent = {
36
+ contexts : {
28
37
feedback : {
38
+ contact_email : email ,
29
39
name,
30
- email,
31
40
message,
32
41
url,
33
42
source,
34
43
} ,
35
44
} ,
36
- options ,
37
- ) ;
45
+ type : 'feedback' ,
46
+ } ;
47
+
48
+ return withScope ( async scope => {
49
+ // No use for breadcrumbs in feedback
50
+ scope . clearBreadcrumbs ( ) ;
51
+
52
+ if ( [ FEEDBACK_API_SOURCE , FEEDBACK_WIDGET_SOURCE ] . includes ( String ( source ) ) ) {
53
+ scope . setLevel ( 'info' ) ;
54
+ }
55
+
56
+ const feedbackEvent = await prepareFeedbackEvent ( {
57
+ scope,
58
+ client,
59
+ event : baseEvent ,
60
+ } ) ;
61
+
62
+ if ( client . emit ) {
63
+ client . emit ( 'beforeSendFeedback' , feedbackEvent , { includeReplay : Boolean ( includeReplay ) } ) ;
64
+ }
65
+
66
+ const envelope = createEventEnvelope ( feedbackEvent , dsn , client . getOptions ( ) . _metadata , client . getOptions ( ) . tunnel ) ;
67
+
68
+ let response : void | TransportMakeRequestResponse ;
69
+
70
+ try {
71
+ response = await transport . send ( envelope ) ;
72
+ } catch ( err ) {
73
+ const error = new Error ( 'Unable to send Feedback' ) ;
74
+
75
+ try {
76
+ // In case browsers don't allow this property to be writable
77
+ // @ts -expect-error This needs lib es2022 and newer
78
+ error . cause = err ;
79
+ } catch {
80
+ // nothing to do
81
+ }
82
+ throw error ;
83
+ }
84
+
85
+ // TODO (v8): we can remove this guard once transport.send's type signature doesn't include void anymore
86
+ if ( ! response ) {
87
+ throw new Error ( 'Unable to send Feedback' ) ;
88
+ }
89
+
90
+ // Require valid status codes, otherwise can assume feedback was not sent successfully
91
+ if ( typeof response . statusCode === 'number' && ( response . statusCode < 200 || response . statusCode >= 300 ) ) {
92
+ throw new Error ( 'Unable to send Feedback. Invalid response from server.' ) ;
93
+ }
94
+
95
+ return response ;
96
+ } ) ;
38
97
}
98
+
99
+ /*
100
+ * For reference, the fully built event looks something like this:
101
+ * {
102
+ * "type": "feedback",
103
+ * "event_id": "d2132d31b39445f1938d7e21b6bf0ec4",
104
+ * "timestamp": 1597977777.6189718,
105
+ * "dist": "1.12",
106
+ * "platform": "javascript",
107
+ * "environment": "production",
108
+ * "release": 42,
109
+ * "tags": {"transaction": "/organizations/:orgId/performance/:eventSlug/"},
110
+ * "sdk": {"name": "name", "version": "version"},
111
+ * "user": {
112
+ * "id": "123",
113
+ * "username": "user",
114
+ * "email": "user@site .com",
115
+ * "ip_address": "192.168.11.12",
116
+ * },
117
+ * "request": {
118
+ * "url": None,
119
+ * "headers": {
120
+ * "user-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/15.5 Safari/605.1.15"
121
+ * },
122
+ * },
123
+ * "contexts": {
124
+ * "feedback": {
125
+ * "message": "test message",
126
+ * "contact_email": "test@example .com",
127
+ * "type": "feedback",
128
+ * },
129
+ * "trace": {
130
+ * "trace_id": "4C79F60C11214EB38604F4AE0781BFB2",
131
+ * "span_id": "FA90FDEAD5F74052",
132
+ * "type": "trace",
133
+ * },
134
+ * "replay": {
135
+ * "replay_id": "e2d42047b1c5431c8cba85ee2a8ab25d",
136
+ * },
137
+ * },
138
+ * }
139
+ */
0 commit comments