|
1 | | -import { createAttachmentEnvelope, createEventEnvelope, getClient, withScope } from '@sentry/core'; |
2 | | -import type { FeedbackEvent, SendFeedback, SendFeedbackParams } from '@sentry/types'; |
| 1 | +import { captureFeedback } from '@sentry/core'; |
| 2 | +import { getClient } from '@sentry/core'; |
| 3 | +import type { SendFeedback, SendFeedbackParams, TransportMakeRequestResponse } from '@sentry/types'; |
| 4 | +import type { Event } from '@sentry/types'; |
3 | 5 | import { getLocationHref } from '@sentry/utils'; |
4 | | -import { FEEDBACK_API_SOURCE, FEEDBACK_WIDGET_SOURCE } from '../constants'; |
5 | | -import { prepareFeedbackEvent } from '../util/prepareFeedbackEvent'; |
| 6 | +import { FEEDBACK_API_SOURCE } from '../constants'; |
6 | 7 |
|
7 | 8 | /** |
8 | 9 | * Public API to send a Feedback item to Sentry |
9 | 10 | */ |
10 | 11 | export const sendFeedback: SendFeedback = ( |
11 | 12 | { name, email, message, attachments, source = FEEDBACK_API_SOURCE, url = getLocationHref() }: SendFeedbackParams, |
12 | 13 | { includeReplay = true } = {}, |
13 | | -) => { |
| 14 | +): Promise<void> => { |
14 | 15 | if (!message) { |
15 | 16 | throw new Error('Unable to submit feedback with empty message'); |
16 | 17 | } |
17 | 18 |
|
| 19 | + // We want to wait for the feedback to be sent (or not) |
18 | 20 | const client = getClient(); |
19 | | - const transport = client && client.getTransport(); |
20 | | - const dsn = client && client.getDsn(); |
21 | 21 |
|
22 | | - if (!client || !transport || !dsn) { |
23 | | - throw new Error('Invalid Sentry client'); |
| 22 | + if (!client) { |
| 23 | + throw new Error('No client setup, cannot send feedback.'); |
24 | 24 | } |
25 | 25 |
|
26 | | - const baseEvent: FeedbackEvent = { |
27 | | - contexts: { |
28 | | - feedback: { |
29 | | - contact_email: email, |
30 | | - name, |
31 | | - message, |
32 | | - url, |
33 | | - source, |
34 | | - }, |
35 | | - }, |
36 | | - type: 'feedback', |
37 | | - }; |
| 26 | + const eventId = captureFeedback({ name, email, message, attachments, source, url }, { includeReplay }); |
38 | 27 |
|
39 | | - return withScope(async scope => { |
40 | | - // No use for breadcrumbs in feedback |
41 | | - scope.clearBreadcrumbs(); |
| 28 | + // We want to wait for the feedback to be sent (or not) |
| 29 | + return new Promise<void>((resolve, reject) => { |
| 30 | + // After 5s, we want to clear anyhow |
| 31 | + const timeout = setTimeout(() => reject('timeout'), 5_000); |
42 | 32 |
|
43 | | - if ([FEEDBACK_API_SOURCE, FEEDBACK_WIDGET_SOURCE].includes(String(source))) { |
44 | | - scope.setLevel('info'); |
45 | | - } |
46 | | - |
47 | | - const feedbackEvent = await prepareFeedbackEvent({ |
48 | | - scope, |
49 | | - client, |
50 | | - event: baseEvent, |
51 | | - }); |
52 | | - |
53 | | - if (client.emit) { |
54 | | - client.emit('beforeSendFeedback', feedbackEvent, { includeReplay: Boolean(includeReplay) }); |
55 | | - } |
56 | | - |
57 | | - try { |
58 | | - const response = await transport.send( |
59 | | - createEventEnvelope(feedbackEvent, dsn, client.getOptions()._metadata, client.getOptions().tunnel), |
60 | | - ); |
61 | | - |
62 | | - if (attachments && attachments.length) { |
63 | | - // TODO: https://docs.sentry.io/platforms/javascript/enriching-events/attachments/ |
64 | | - await transport.send( |
65 | | - createAttachmentEnvelope( |
66 | | - feedbackEvent, |
67 | | - attachments, |
68 | | - dsn, |
69 | | - client.getOptions()._metadata, |
70 | | - client.getOptions().tunnel, |
71 | | - ), |
72 | | - ); |
| 33 | + client.on('afterSendEvent', (event: Event, response: TransportMakeRequestResponse) => { |
| 34 | + if (event.event_id !== eventId) { |
| 35 | + return; |
73 | 36 | } |
74 | 37 |
|
| 38 | + clearTimeout(timeout); |
| 39 | + |
75 | 40 | // Require valid status codes, otherwise can assume feedback was not sent successfully |
76 | 41 | if (typeof response.statusCode === 'number' && (response.statusCode < 200 || response.statusCode >= 300)) { |
77 | 42 | if (response.statusCode === 0) { |
78 | | - throw new Error( |
| 43 | + return reject( |
79 | 44 | 'Unable to send Feedback. This is because of network issues, or because you are using an ad-blocker.', |
80 | 45 | ); |
81 | 46 | } |
82 | | - throw new Error('Unable to send Feedback. Invalid response from server.'); |
| 47 | + return reject('Unable to send Feedback. Invalid response from server.'); |
83 | 48 | } |
84 | 49 |
|
85 | | - return response; |
86 | | - } catch (err) { |
87 | | - const error = new Error('Unable to send Feedback'); |
88 | | - |
89 | | - try { |
90 | | - // In case browsers don't allow this property to be writable |
91 | | - // @ts-expect-error This needs lib es2022 and newer |
92 | | - error.cause = err; |
93 | | - } catch { |
94 | | - // nothing to do |
95 | | - } |
96 | | - throw error; |
97 | | - } |
| 50 | + resolve(); |
| 51 | + }); |
98 | 52 | }); |
99 | 53 | }; |
100 | 54 |
|
|
0 commit comments