1+ import { createEventEnvelope , getClient , withScope } from '@sentry/core' ;
2+ import type { FeedbackEvent , TransportMakeRequestResponse } from '@sentry/types' ;
13import { getLocationHref } from '@sentry/utils' ;
2-
3- import { FEEDBACK_API_SOURCE } from './constants' ;
4+ import { FEEDBACK_API_SOURCE , FEEDBACK_WIDGET_SOURCE } from './constants' ;
45import type { SendFeedbackOptions } from './types' ;
5- import { sendFeedbackRequest } from './util/sendFeedbackRequest ' ;
6+ import { prepareFeedbackEvent } from './util/prepareFeedbackEvent ' ;
67
78interface SendFeedbackParams {
89 message : string ;
@@ -15,24 +16,124 @@ interface SendFeedbackParams {
1516/**
1617 * Public API to send a Feedback item to Sentry
1718 */
18- export function sendFeedback (
19+ export async function sendFeedback (
1920 { name, email, message, source = FEEDBACK_API_SOURCE , url = getLocationHref ( ) } : SendFeedbackParams ,
20- options : SendFeedbackOptions = { } ,
21- ) : ReturnType < typeof sendFeedbackRequest > {
21+ { includeReplay = true } : SendFeedbackOptions = { } ,
22+ ) : Promise < TransportMakeRequestResponse > {
2223 if ( ! message ) {
2324 throw new Error ( 'Unable to submit feedback with empty message' ) ;
2425 }
2526
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 : {
2837 feedback : {
38+ contact_email : email ,
2939 name,
30- email,
3140 message,
3241 url,
3342 source,
3443 } ,
3544 } ,
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+ } ) ;
3897}
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