Skip to content

Commit

Permalink
Merge 71b28e8 into 8ab11b6
Browse files Browse the repository at this point in the history
  • Loading branch information
antonis authored Nov 28, 2024
2 parents 8ab11b6 + 71b28e8 commit beb77ad
Show file tree
Hide file tree
Showing 8 changed files with 68 additions and 75 deletions.
18 changes: 18 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,24 @@
## Unreleased

### Features

- Adds new `captureFeedback` and deprecates the `captureUserFeedback` API ([#4320](https://github.com/getsentry/sentry-react-native/pull/4320))


```jsx
import * as Sentry from "@sentry/react-native";

const eventId = Sentry.lastEventId();

Sentry.captureFeedback({
name: "John Doe",
email: "[email protected]",
message: "Hello World!",
associatedEventId: eventId, // optional
});
```

### Fixes

- Return `lastEventId` export from `@sentry/core` ([#4315](https://github.com/getsentry/sentry-react-native/pull/4315))
Expand Down
16 changes: 1 addition & 15 deletions packages/core/src/js/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ import type {
Outcome,
SeverityLevel,
TransportMakeRequestResponse,
UserFeedback,
} from '@sentry/types';
import { dateTimestampInSeconds, logger, SentryError } from '@sentry/utils';
import { Alert } from 'react-native';
Expand All @@ -20,7 +19,7 @@ import { getDefaultSidecarUrl } from './integrations/spotlight';
import type { ReactNativeClientOptions } from './options';
import type { mobileReplayIntegration } from './replay/mobilereplay';
import { MOBILE_REPLAY_INTEGRATION_NAME } from './replay/mobilereplay';
import { createUserFeedbackEnvelope, items } from './utils/envelope';
import { items } from './utils/envelope';
import { ignoreRequireCycleLogs } from './utils/ignorerequirecyclelogs';
import { mergeOutcomes } from './utils/outcome';
import { ReactNativeLibraries } from './utils/rnlibraries';
Expand Down Expand Up @@ -83,19 +82,6 @@ export class ReactNativeClient extends BaseClient<ReactNativeClientOptions> {
});
}

/**
* Sends user feedback to Sentry.
*/
public captureUserFeedback(feedback: UserFeedback): void {
const envelope = createUserFeedbackEnvelope(feedback, {
metadata: this._options._metadata,
dsn: this.getDsn(),
tunnel: undefined,
});
// eslint-disable-next-line @typescript-eslint/no-floating-promises
this.sendEnvelope(envelope);
}

/**
* @inheritdoc
*/
Expand Down
2 changes: 2 additions & 0 deletions packages/core/src/js/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ export type {
SdkInfo,
Event,
Exception,
SendFeedbackParams,
SeverityLevel,
StackFrame,
Stacktrace,
Expand All @@ -16,6 +17,7 @@ export {
addBreadcrumb,
captureException,
captureEvent,
captureFeedback,
captureMessage,
Scope,
setContext,
Expand Down
13 changes: 10 additions & 3 deletions packages/core/src/js/sdk.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
/* eslint-disable complexity */
import { getClient, getGlobalScope,getIntegrationsToSetup, getIsolationScope,initAndBind, withScope as coreWithScope } from '@sentry/core';
import { captureFeedback, getClient, getGlobalScope,getIntegrationsToSetup, getIsolationScope,initAndBind, withScope as coreWithScope } from '@sentry/core';
import {
defaultStackParser,
makeFetchTransport,
} from '@sentry/react';
import type { Breadcrumb, BreadcrumbHint, Integration, Scope, UserFeedback } from '@sentry/types';
import type { Breadcrumb, BreadcrumbHint, Integration, Scope, SendFeedbackParams, UserFeedback } from '@sentry/types';
import { logger, stackParserFromStackParserOptions } from '@sentry/utils';
import * as React from 'react';

Expand Down Expand Up @@ -219,9 +219,16 @@ export async function close(): Promise<void> {

/**
* Captures user feedback and sends it to Sentry.
* @deprecated Use `Sentry.captureFeedback` instead.
*/
export function captureUserFeedback(feedback: UserFeedback): void {
getClient<ReactNativeClient>()?.captureUserFeedback(feedback);
const feedbackParams: SendFeedbackParams = {
name: feedback.name,
email: feedback.email,
message: feedback.comments,
associatedEventId: feedback.event_id,
};
captureFeedback(feedbackParams);
}

/**
Expand Down
47 changes: 0 additions & 47 deletions packages/core/test/client.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ import {
envelopeItems,
firstArg,
getMockSession,
getMockUserFeedback,
getSyncPromiseRejectOnFirstCall,
} from './testutils';

Expand Down Expand Up @@ -187,15 +186,6 @@ describe('Tests ReactNativeClient', () => {
expect(mockTransport.send).not.toBeCalled();
});

test('captureUserFeedback does not call transport when enabled false', () => {
const mockTransport = createMockTransport();
const client = createDisabledClientWith(mockTransport);

client.captureUserFeedback(getMockUserFeedback());

expect(mockTransport.send).not.toBeCalled();
});

function createDisabledClientWith(transport: Transport) {
return new ReactNativeClient({
...DEFAULT_OPTIONS,
Expand Down Expand Up @@ -289,38 +279,6 @@ describe('Tests ReactNativeClient', () => {
});
});

describe('UserFeedback', () => {
test('sends UserFeedback to native Layer', () => {
const mockTransportSend: jest.Mock = jest.fn(() => Promise.resolve());
const client = new ReactNativeClient({
...DEFAULT_OPTIONS,
dsn: EXAMPLE_DSN,
transport: () => ({
send: mockTransportSend,
flush: jest.fn(),
}),
});

client.captureUserFeedback({
comments: 'Test Comments',
email: '[email protected]',
name: 'Test User',
event_id: 'testEvent123',
});

expect(mockTransportSend.mock.calls[0][firstArg][envelopeHeader].event_id).toEqual('testEvent123');
expect(mockTransportSend.mock.calls[0][firstArg][envelopeItems][0][envelopeItemHeader].type).toEqual(
'user_report',
);
expect(mockTransportSend.mock.calls[0][firstArg][envelopeItems][0][envelopeItemPayload]).toEqual({
comments: 'Test Comments',
email: '[email protected]',
name: 'Test User',
event_id: 'testEvent123',
});
});
});

describe('attachStacktrace', () => {
let mockTransportSend: jest.Mock;
let client: ReactNativeClient;
Expand Down Expand Up @@ -417,11 +375,6 @@ describe('Tests ReactNativeClient', () => {
client.captureSession(getMockSession());
expect(getSdkInfoFrom(mockTransportSend)).toStrictEqual(expectedSdkInfo);
});

test('send SdkInfo in the user feedback envelope header', () => {
client.captureUserFeedback(getMockUserFeedback());
expect(getSdkInfoFrom(mockTransportSend)).toStrictEqual(expectedSdkInfo);
});
});

describe('event data enhancement', () => {
Expand Down
9 changes: 1 addition & 8 deletions packages/core/test/testutils.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { Session, Transport, UserFeedback } from '@sentry/types';
import type { Session, Transport } from '@sentry/types';
import { rejectedSyncPromise } from '@sentry/utils';

export type MockInterface<T> = {
Expand Down Expand Up @@ -36,13 +36,6 @@ export const getMockSession = (): Session => ({
}),
});

export const getMockUserFeedback = (): UserFeedback => ({
comments: 'comments_test_value',
email: 'email_test_value',
name: 'name_test_value',
event_id: 'event_id_test_value',
});

export const getSyncPromiseRejectOnFirstCall = <Y extends any[]>(reason: unknown): jest.Mock => {
let shouldSyncReject = true;
return jest.fn((..._args: Y) => {
Expand Down
19 changes: 18 additions & 1 deletion samples/react-native-macos/src/components/UserFeedbackModal.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import React from 'react';
import { View, StyleSheet, Text, TextInput, Image, Button } from 'react-native';
import * as Sentry from '@sentry/react-native';
import { UserFeedback } from '@sentry/react-native';
import { SendFeedbackParams, UserFeedback } from '@sentry/react-native';

export const DEFAULT_COMMENTS = "It's broken again! Please fix it.";

Expand Down Expand Up @@ -48,6 +48,23 @@ export function UserFeedbackModal(props: { onDismiss: () => void }) {
}}
/>
<View style={styles.buttonSpacer} />
<Button
title="Send feedback without event"
color="#6C5FC7"
onPress={async () => {
onDismiss();

const userFeedback: SendFeedbackParams = {
message: comments,
name: 'John Doe',
email: '[email protected]',
};

Sentry.captureFeedback(userFeedback);
clearComments();
}}
/>
<View style={styles.buttonSpacer} />
<Button
title="Close"
color="#6C5FC7"
Expand Down
19 changes: 18 additions & 1 deletion samples/react-native/src/components/UserFeedbackModal.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import React from 'react';
import { View, StyleSheet, Text, TextInput, Image, Button } from 'react-native';
import * as Sentry from '@sentry/react-native';
import { UserFeedback } from '@sentry/react-native';
import { SendFeedbackParams, UserFeedback } from '@sentry/react-native';

export const DEFAULT_COMMENTS = "It's broken again! Please fix it.";

Expand Down Expand Up @@ -48,6 +48,23 @@ export function UserFeedbackModal(props: { onDismiss: () => void }) {
}}
/>
<View style={styles.buttonSpacer} />
<Button
title="Send feedback without event"
color="#6C5FC7"
onPress={async () => {
onDismiss();

const userFeedback: SendFeedbackParams = {
message: comments,
name: 'John Doe',
email: '[email protected]',
};

Sentry.captureFeedback(userFeedback);
clearComments();
}}
/>
<View style={styles.buttonSpacer} />
<Button
title="Close"
color="#6C5FC7"
Expand Down

0 comments on commit beb77ad

Please sign in to comment.