From a92e87a4fd3cce5a2c6d4119f415185f6e687a7e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kry=C5=A1tof=20Wold=C5=99ich?= Date: Tue, 20 Sep 2022 17:58:22 +0200 Subject: [PATCH 1/8] Add UserFeedback draft --- src/js/client.ts | 17 ++++++++++++++++- src/js/envelope.ts | 31 +++++++++++++++++++++++++++++++ src/js/index.ts | 2 +- src/js/user.ts | 13 +++++++++++++ 4 files changed, 61 insertions(+), 2 deletions(-) create mode 100644 src/js/envelope.ts create mode 100644 src/js/user.ts diff --git a/src/js/client.ts b/src/js/client.ts index f3c322bdc2..a0c3788d74 100644 --- a/src/js/client.ts +++ b/src/js/client.ts @@ -2,10 +2,17 @@ import { BrowserClient, defaultStackParser, makeFetchTransport } from '@sentry/b import { BrowserTransportOptions } from '@sentry/browser/types/transports/types'; import { FetchImpl } from '@sentry/browser/types/transports/utils'; import { BaseClient } from '@sentry/core'; -import { Event, EventHint, SeverityLevel, Transport } from '@sentry/types'; +import { + Event, + EventHint, + SeverityLevel, + Transport, + UserFeedback, +} from '@sentry/types'; // @ts-ignore LogBox introduced in RN 0.63 import { Alert, LogBox, YellowBox } from 'react-native'; +import { createUserFeedbackEnvelope } from './envelope'; import { ReactNativeClientOptions } from './options'; import { NativeTransport } from './transports/native'; import { NATIVE } from './wrapper'; @@ -89,6 +96,14 @@ export class ReactNativeClient extends BaseClient { }); } + /** + * Sends user feedback to Sentry. + */ + public captureUserFeedback(feedback: UserFeedback): void { + const envelope = createUserFeedbackEnvelope(feedback); + this._sendEnvelope(envelope); + } + /** * Starts native client with dsn and options */ diff --git a/src/js/envelope.ts b/src/js/envelope.ts new file mode 100644 index 0000000000..5a2e6307c1 --- /dev/null +++ b/src/js/envelope.ts @@ -0,0 +1,31 @@ +import { + EventEnvelope, + EventEnvelopeHeaders, + UserFeedback, + UserFeedbackItem, +} from '@sentry/types'; +import { createEnvelope } from '@sentry/utils'; + +/** + * Creates an envelope from a user feedback. + */ +export function createUserFeedbackEnvelope( + feedback: UserFeedback +): EventEnvelope { + const headers: EventEnvelopeHeaders = { + event_id: feedback.event_id, // not sure feedback.event_id is correct + sent_at: new Date().toISOString(), + }; + const item = createUserFeedbackEnvelopeItem(feedback); + + return createEnvelope(headers, [item]); +} + +function createUserFeedbackEnvelopeItem( + feedback: UserFeedback +): UserFeedbackItem { + const feedbackHeaders: UserFeedbackItem[0] = { + type: 'user_report', + }; + return [feedbackHeaders, feedback]; +} diff --git a/src/js/index.ts b/src/js/index.ts index ffe92f6369..e5e1b012ea 100644 --- a/src/js/index.ts +++ b/src/js/index.ts @@ -7,7 +7,6 @@ export { StackFrame, Stacktrace, Thread, - User, } from '@sentry/types'; export { @@ -49,6 +48,7 @@ export { withProfiler, } from '@sentry/react'; +export { User, UserFeedback, captureUserFeedback } from './user'; import * as Integrations from './integrations'; import { SDK_NAME, SDK_VERSION } from './version'; export { ReactNativeOptions } from './options'; diff --git a/src/js/user.ts b/src/js/user.ts new file mode 100644 index 0000000000..735a246889 --- /dev/null +++ b/src/js/user.ts @@ -0,0 +1,13 @@ +import { getCurrentHub } from '@sentry/hub'; +import { UserFeedback } from '@sentry/types'; + +import { ReactNativeClient } from './client'; + +/** + * Captures a user feedback and sends it to Sentry. + */ +export function captureUserFeedback(feedback: UserFeedback): void { + getCurrentHub().getClient()?.captureUserFeedback(feedback); +} + +export { User, UserFeedback } from '@sentry/types'; From d9b0aab22a601a3d55a209f85e8f35cf9ae46535 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kry=C5=A1tof=20Wold=C5=99ich?= Date: Wed, 21 Sep 2022 14:39:42 +0200 Subject: [PATCH 2/8] Add user feedback to the sample --- sample/src/assets/sentry-announcement.png | Bin 0 -> 20616 bytes sample/src/components/UserFeedbackModal.tsx | 116 ++++++++++++++++++++ sample/src/screens/HomeScreen.tsx | 5 +- 3 files changed, 120 insertions(+), 1 deletion(-) create mode 100644 sample/src/assets/sentry-announcement.png create mode 100644 sample/src/components/UserFeedbackModal.tsx diff --git a/sample/src/assets/sentry-announcement.png b/sample/src/assets/sentry-announcement.png new file mode 100644 index 0000000000000000000000000000000000000000..b29992aba695bb45094f4ef4490b62be38db6c2c GIT binary patch literal 20616 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 iV~jDz7-Nhv#{54{mZR(saLn8Q0000Ry0 literal 0 HcmV?d00001 diff --git a/sample/src/components/UserFeedbackModal.tsx b/sample/src/components/UserFeedbackModal.tsx new file mode 100644 index 0000000000..63b8f7be8e --- /dev/null +++ b/sample/src/components/UserFeedbackModal.tsx @@ -0,0 +1,116 @@ +import React, { useState } from 'react'; +import { View, Modal, StyleSheet, Text, TouchableOpacity, TextInput, Image } from 'react-native'; +import * as Sentry from '@sentry/react-native'; +import { UserFeedback } from '@sentry/react-native'; +import { styles as homeScreenStyles } from '../screens/HomeScreen'; + +export const DEFAULT_COMMENTS = `It's broken again! Please fix it.`; + +export function UserFeedbackModal() { + const [comments, onChangeComments] = React.useState(DEFAULT_COMMENTS); + const [modalVisible, setModalVisible] = useState(false); + const clearComments = () => onChangeComments(DEFAULT_COMMENTS); + + return ( + + { + setModalVisible(!modalVisible); + }} + > + + + + Ups, what happened? + + { + setModalVisible(!modalVisible); + + const sentryId = Sentry.captureMessage('Message that needs user feedback'); + + const userFeedback: UserFeedback = { + event_id: sentryId, + name: 'John Doe', + email: 'john@doe.com', + comments, + }; + + Sentry.captureUserFeedback(userFeedback); + clearComments(); + }}> + Send feedback + + { + setModalVisible(!modalVisible); + }}> + Close + + + + + { + setModalVisible(true); + }}> + Send user feedback + + + ); +} + +const styles = StyleSheet.create({ + centeredView: { + flex: 1, + justifyContent: "center", + alignItems: "center", + }, + modalView: { + margin: 5, + backgroundColor: "white", + borderRadius: 6, + padding: 25, + alignItems: "center", + shadowColor: "#000", + shadowOffset: { + width: 0, + height: 2 + }, + shadowOpacity: 0.25, + shadowRadius: 4, + elevation: 5 + }, + input: { + margin: 12, + marginBottom: 20, + borderWidth: 0.5, + borderColor: '#c6becf', + padding: 15, + borderRadius: 6, + height: 100, + width: 250, + }, + modalText: { + marginBottom: 15, + textAlign: "center", + fontSize: 18, + }, + modalImage: { + marginBottom: 20, + width: 80, + height: 80, + } +}); diff --git a/sample/src/screens/HomeScreen.tsx b/sample/src/screens/HomeScreen.tsx index d42c2dd312..f720c45437 100644 --- a/sample/src/screens/HomeScreen.tsx +++ b/sample/src/screens/HomeScreen.tsx @@ -18,6 +18,7 @@ import { SENTRY_INTERNAL_DSN } from '../dsn'; import { SeverityLevel } from '@sentry/types'; import { Scope } from '@sentry/react-native'; import { NativeModules } from 'react-native'; +import { UserFeedbackModal } from '../components/UserFeedbackModal'; const {AssetsModule} = NativeModules; @@ -256,6 +257,8 @@ const HomeScreen = (props: Props) => { }}> Get attachment + + { ); }; -const styles = StyleSheet.create({ +export const styles = StyleSheet.create({ scrollView: { backgroundColor: '#fff', flex: 1, From cdd4f010a9d0e59b04e61e18967599fba3cc8157 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kry=C5=A1tof=20Wold=C5=99ich?= Date: Wed, 21 Sep 2022 14:45:25 +0200 Subject: [PATCH 3/8] Fix user feedback sample ui --- sample/src/components/UserFeedbackModal.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/sample/src/components/UserFeedbackModal.tsx b/sample/src/components/UserFeedbackModal.tsx index 63b8f7be8e..ddd707cf67 100644 --- a/sample/src/components/UserFeedbackModal.tsx +++ b/sample/src/components/UserFeedbackModal.tsx @@ -27,7 +27,7 @@ export function UserFeedbackModal() { source={require('../assets/sentry-announcement.png')} style={styles.modalImage} /> - Ups, what happened? + Whoops, what happened? Date: Wed, 21 Sep 2022 16:17:01 +0200 Subject: [PATCH 4/8] Add headers to user feedback --- src/js/client.ts | 11 +++++++++-- src/js/{ => utils}/envelope.ts | 23 +++++++++++++++++++---- 2 files changed, 28 insertions(+), 6 deletions(-) rename src/js/{ => utils}/envelope.ts (50%) diff --git a/src/js/client.ts b/src/js/client.ts index a0c3788d74..ad87f7eae0 100644 --- a/src/js/client.ts +++ b/src/js/client.ts @@ -12,7 +12,7 @@ import { // @ts-ignore LogBox introduced in RN 0.63 import { Alert, LogBox, YellowBox } from 'react-native'; -import { createUserFeedbackEnvelope } from './envelope'; +import { createUserFeedbackEnvelope } from './utils/envelope'; import { ReactNativeClientOptions } from './options'; import { NativeTransport } from './transports/native'; import { NATIVE } from './wrapper'; @@ -100,7 +100,14 @@ export class ReactNativeClient extends BaseClient { * Sends user feedback to Sentry. */ public captureUserFeedback(feedback: UserFeedback): void { - const envelope = createUserFeedbackEnvelope(feedback); + const envelope = createUserFeedbackEnvelope( + feedback, + { + metadata: this._options._metadata, + dsn: this.getDsn(), + tunnel: this._options.tunnel, + }, + ); this._sendEnvelope(envelope); } diff --git a/src/js/envelope.ts b/src/js/utils/envelope.ts similarity index 50% rename from src/js/envelope.ts rename to src/js/utils/envelope.ts index 5a2e6307c1..0f4891a089 100644 --- a/src/js/envelope.ts +++ b/src/js/utils/envelope.ts @@ -1,20 +1,35 @@ import { + BaseEnvelopeHeaders, + DsnComponents, EventEnvelope, EventEnvelopeHeaders, + SdkMetadata, UserFeedback, UserFeedbackItem, } from '@sentry/types'; -import { createEnvelope } from '@sentry/utils'; +import { createEnvelope, dsnToString } from '@sentry/utils'; /** * Creates an envelope from a user feedback. */ export function createUserFeedbackEnvelope( - feedback: UserFeedback + feedback: UserFeedback, + { + metadata, + tunnel, + dsn, + }: { + metadata: SdkMetadata | undefined, + tunnel: string | undefined, + dsn: DsnComponents | undefined, + }, ): EventEnvelope { - const headers: EventEnvelopeHeaders = { - event_id: feedback.event_id, // not sure feedback.event_id is correct + // TODO: Use EventEnvelope[0] when JS sdk fix is released + const headers: EventEnvelopeHeaders & BaseEnvelopeHeaders = { + event_id: feedback.event_id, sent_at: new Date().toISOString(), + ...(metadata && metadata.sdk && { sdk: metadata.sdk }), + ...(!!tunnel && !!dsn && { dsn: dsnToString(dsn) }), }; const item = createUserFeedbackEnvelopeItem(feedback); From dac2a92fefdc0672d577ea1fe9da2d9a735f6963 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kry=C5=A1tof=20Wold=C5=99ich?= Date: Thu, 22 Sep 2022 14:21:44 +0200 Subject: [PATCH 5/8] Add user feedback integration test --- test/client.test.ts | 71 +++++++++++++++++++++++++++++++++++++++++---- test/testutils.ts | 6 ++++ 2 files changed, 71 insertions(+), 6 deletions(-) diff --git a/test/client.test.ts b/test/client.test.ts index eabb455ce0..a50fe6c492 100644 --- a/test/client.test.ts +++ b/test/client.test.ts @@ -5,17 +5,44 @@ import { ReactNativeClient } from '../src/js/client'; import { ReactNativeClientOptions, ReactNativeOptions } from '../src/js/options'; import { NativeTransport } from '../src/js/transports/native'; import { NATIVE } from '../src/js/wrapper'; +import { + firstArg, + envelopeHeader, + envelopeItems, + envelopeItemHeader, + envelopeItemPayload, +} from './testutils'; const EXAMPLE_DSN = 'https://6890c2f6677340daa4804f8194804ea2@o19635.ingest.sentry.io/148053'; +interface MockedReactNative { + NativeModules: { + RNSentry: { + initNativeSdk: jest.Mock; + crash: jest.Mock; + captureEnvelope: jest.Mock; + }; + }; + Platform: { + OS: 'mock'; + }; + LogBox: { + ignoreLogs: jest.Mock; + }; + YellowBox: { + ignoreWarnings: jest.Mock; + }; +} + jest.mock( 'react-native', - () => ({ + (): MockedReactNative => ({ NativeModules: { RNSentry: { initNativeSdk: jest.fn(() => Promise.resolve(true)), crash: jest.fn(), + captureEnvelope: jest.fn(), }, }, Platform: { @@ -100,7 +127,7 @@ describe('Tests ReactNativeClient', () => { // eslint-disable-next-line deprecation/deprecation await expect(RN.YellowBox.ignoreWarnings).toBeCalled(); }); - + test('use custom transport function', async () => { // eslint-disable-next-line @typescript-eslint/no-unused-vars const mySend = (request: Envelope) => Promise.resolve(); @@ -149,11 +176,11 @@ describe('Tests ReactNativeClient', () => { }); test('calls onReady callback with false if Native SDK failed to initialize', (done) => { - const RN = require('react-native'); + const RN: MockedReactNative = require('react-native'); - RN.NativeModules.RNSentry.initNativeSdk = async () => { + RN.NativeModules.RNSentry.initNativeSdk = jest.fn(() => { throw new Error(); - }; + }); new ReactNativeClient({ dsn: EXAMPLE_DSN, @@ -170,7 +197,7 @@ describe('Tests ReactNativeClient', () => { describe('nativeCrash', () => { test('calls NativeModules crash', () => { - const RN = require('react-native'); + const RN: MockedReactNative = require('react-native'); const client = new ReactNativeClient({ ...DEFAULT_OPTIONS, @@ -183,4 +210,36 @@ describe('Tests ReactNativeClient', () => { expect(RN.NativeModules.RNSentry.crash).toBeCalled(); }); }); + + 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(), + }), + } as ReactNativeClientOptions); + + client.captureUserFeedback({ + comments: 'Test Comments', + email: 'test@email.com', + 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: 'test@email.com', + name: 'Test User', + event_id: 'testEvent123', + }); + }); + }); }); diff --git a/test/testutils.ts b/test/testutils.ts index 80f04e9838..5afdff6c7b 100644 --- a/test/testutils.ts +++ b/test/testutils.ts @@ -17,3 +17,9 @@ export const getMockTransaction = (name: string): Transaction => { return transaction; }; + +export const firstArg = 0; +export const envelopeHeader = 0; +export const envelopeItems = 1; +export const envelopeItemHeader = 0; +export const envelopeItemPayload = 1; From 5ed2138e66392f31fdb56c2205c0505b6885d533 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kry=C5=A1tof=20Wold=C5=99ich?= Date: Thu, 22 Sep 2022 14:25:15 +0200 Subject: [PATCH 6/8] Update changelog --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index ed8bcf13aa..a3fd5ffab0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,10 @@ ## Unreleased +### Features + +- Add user feedback ([#2486](https://github.com/getsentry/sentry-react-native/pull/2486)) + ### Fixes - Add typings for app hang functionality ([#2479](https://github.com/getsentry/sentry-react-native/pull/2479)) From d8eca88720563444ed1a824af7f3d91a50dd7279 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kry=C5=A1tof=20Wold=C5=99ich?= Date: Thu, 22 Sep 2022 14:27:01 +0200 Subject: [PATCH 7/8] Fix lint --- src/js/client.ts | 2 +- test/client.test.ts | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/js/client.ts b/src/js/client.ts index ad87f7eae0..f4bd90e617 100644 --- a/src/js/client.ts +++ b/src/js/client.ts @@ -12,9 +12,9 @@ import { // @ts-ignore LogBox introduced in RN 0.63 import { Alert, LogBox, YellowBox } from 'react-native'; -import { createUserFeedbackEnvelope } from './utils/envelope'; import { ReactNativeClientOptions } from './options'; import { NativeTransport } from './transports/native'; +import { createUserFeedbackEnvelope } from './utils/envelope'; import { NATIVE } from './wrapper'; /** diff --git a/test/client.test.ts b/test/client.test.ts index a50fe6c492..6d11289eb8 100644 --- a/test/client.test.ts +++ b/test/client.test.ts @@ -6,11 +6,11 @@ import { ReactNativeClientOptions, ReactNativeOptions } from '../src/js/options' import { NativeTransport } from '../src/js/transports/native'; import { NATIVE } from '../src/js/wrapper'; import { - firstArg, envelopeHeader, - envelopeItems, envelopeItemHeader, envelopeItemPayload, + envelopeItems, + firstArg, } from './testutils'; const EXAMPLE_DSN = From f15f49a787e4aeb70adff8aba5dd83e9d5334a03 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kry=C5=A1tof=20Wold=C5=99ich?= Date: Thu, 22 Sep 2022 15:32:58 +0200 Subject: [PATCH 8/8] Move user feedback to sdk.tsx --- src/js/index.ts | 4 +++- src/js/sdk.tsx | 9 ++++++++- src/js/user.ts | 13 ------------- 3 files changed, 11 insertions(+), 15 deletions(-) delete mode 100644 src/js/user.ts diff --git a/src/js/index.ts b/src/js/index.ts index e5e1b012ea..b1848707b1 100644 --- a/src/js/index.ts +++ b/src/js/index.ts @@ -7,6 +7,8 @@ export { StackFrame, Stacktrace, Thread, + User, + UserFeedback, } from '@sentry/types'; export { @@ -48,7 +50,6 @@ export { withProfiler, } from '@sentry/react'; -export { User, UserFeedback, captureUserFeedback } from './user'; import * as Integrations from './integrations'; import { SDK_NAME, SDK_VERSION } from './version'; export { ReactNativeOptions } from './options'; @@ -64,6 +65,7 @@ export { nativeCrash, flush, close, + captureUserFeedback, } from './sdk'; export { TouchEventBoundary, withTouchEventBoundary } from './touchevents'; diff --git a/src/js/sdk.tsx b/src/js/sdk.tsx index 4c4ef00a51..d37748119e 100644 --- a/src/js/sdk.tsx +++ b/src/js/sdk.tsx @@ -2,7 +2,7 @@ import { getIntegrationsToSetup, initAndBind, setExtra } from '@sentry/core'; import { Hub, makeMain } from '@sentry/hub'; import { RewriteFrames } from '@sentry/integrations'; import { defaultIntegrations, defaultStackParser, getCurrentHub } from '@sentry/react'; -import { Integration, StackFrame } from '@sentry/types'; +import { Integration, StackFrame, UserFeedback } from '@sentry/types'; import { getGlobalObject, logger, stackParserFromStackParserOptions } from '@sentry/utils'; import * as React from 'react'; @@ -222,3 +222,10 @@ export async function close(): Promise { logger.error('Failed to close the SDK'); } } + +/** + * Captures user feedback and sends it to Sentry. + */ + export function captureUserFeedback(feedback: UserFeedback): void { + getCurrentHub().getClient()?.captureUserFeedback(feedback); +} diff --git a/src/js/user.ts b/src/js/user.ts deleted file mode 100644 index 735a246889..0000000000 --- a/src/js/user.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { getCurrentHub } from '@sentry/hub'; -import { UserFeedback } from '@sentry/types'; - -import { ReactNativeClient } from './client'; - -/** - * Captures a user feedback and sends it to Sentry. - */ -export function captureUserFeedback(feedback: UserFeedback): void { - getCurrentHub().getClient()?.captureUserFeedback(feedback); -} - -export { User, UserFeedback } from '@sentry/types';