From 8383f8d3e54e828c9879ff63e76484cd5ffad2ef Mon Sep 17 00:00:00 2001 From: Ryan Albrecht Date: Mon, 17 Jun 2024 13:35:16 -0700 Subject: [PATCH] feat(feedback): Allow passing `tags` field to any feedback config param (#12197) We were missing the ability to set tags within feedback before, this corrects it. See https://github.com/getsentry/sentry-docs/pull/10137 for the docs update. Now a developer can set `tags: {...}` anytime an options param is passed into feedback. This includes: - when we init the integration `feedbackIntegration({tags: {hello: 'world'}})` - when attachTo is called: `feedback.attachTo(element, {tags: {hello: 'world'}})` - when createWidget is called: `feedback.createWidget({tags: {hello: 'world'}})` - when createForm is called: `feedback.createForm({tags: {hello: 'world'}})` Users can also pass tags to `sendFeedback()` which is slightly nicer than having to set them on scope first. - `Sentry.sendFeedback({tags: {hello: 'world'}})` - `captureFeedback()` is unaffected, keeping it closer in style to the other `capture*()` methods. I also took the chance to re-use related types in more places. Checkout the first 2 commits on the branch to see those changes individually. Fixes https://github.com/getsentry/sentry/issues/71254 --- .../suites/feedback/captureFeedback/init.js | 6 +++++- .../suites/feedback/captureFeedback/test.ts | 3 +++ .../captureFeedbackAndReplay/hasSampling/test.ts | 1 + packages/core/src/feedback.ts | 5 +++-- packages/feedback/src/core/integration.ts | 8 +++++--- packages/feedback/src/core/sendFeedback.ts | 13 ++++++++----- packages/feedback/src/modal/components/Form.tsx | 2 ++ packages/feedback/src/util/mergeOptions.ts | 4 ++++ packages/types/src/feedback/config.ts | 6 ++++++ packages/types/src/feedback/sendFeedback.ts | 12 +++++++----- 10 files changed, 44 insertions(+), 16 deletions(-) diff --git a/dev-packages/browser-integration-tests/suites/feedback/captureFeedback/init.js b/dev-packages/browser-integration-tests/suites/feedback/captureFeedback/init.js index 6455e8d8851a..7d36dc233847 100644 --- a/dev-packages/browser-integration-tests/suites/feedback/captureFeedback/init.js +++ b/dev-packages/browser-integration-tests/suites/feedback/captureFeedback/init.js @@ -4,5 +4,9 @@ window.Sentry = Sentry; Sentry.init({ dsn: 'https://public@dsn.ingest.sentry.io/1337', - integrations: [Sentry.feedbackIntegration()], + integrations: [ + Sentry.feedbackIntegration({ + tags: { from: 'integration init' }, + }), + ], }); diff --git a/dev-packages/browser-integration-tests/suites/feedback/captureFeedback/test.ts b/dev-packages/browser-integration-tests/suites/feedback/captureFeedback/test.ts index fede6aa2c0e2..eb16cf1e1848 100644 --- a/dev-packages/browser-integration-tests/suites/feedback/captureFeedback/test.ts +++ b/dev-packages/browser-integration-tests/suites/feedback/captureFeedback/test.ts @@ -59,6 +59,9 @@ sentryTest('should capture feedback', async ({ getLocalTestUrl, page }) => { }, }, level: 'info', + tags: { + from: 'integration init', + }, timestamp: expect.any(Number), event_id: expect.stringMatching(/\w{32}/), environment: 'production', diff --git a/dev-packages/browser-integration-tests/suites/feedback/captureFeedbackAndReplay/hasSampling/test.ts b/dev-packages/browser-integration-tests/suites/feedback/captureFeedbackAndReplay/hasSampling/test.ts index 4457d64ccd9e..5a88a429e53c 100644 --- a/dev-packages/browser-integration-tests/suites/feedback/captureFeedbackAndReplay/hasSampling/test.ts +++ b/dev-packages/browser-integration-tests/suites/feedback/captureFeedbackAndReplay/hasSampling/test.ts @@ -95,6 +95,7 @@ sentryTest('should capture feedback', async ({ forceFlushReplay, getLocalTestUrl }, }, level: 'info', + tags: {}, timestamp: expect.any(Number), event_id: expect.stringMatching(/\w{32}/), environment: 'production', diff --git a/packages/core/src/feedback.ts b/packages/core/src/feedback.ts index 9bd583a288bb..4920842e2114 100644 --- a/packages/core/src/feedback.ts +++ b/packages/core/src/feedback.ts @@ -6,11 +6,11 @@ import { getClient, getCurrentScope } from './currentScopes'; * Send user feedback to Sentry. */ export function captureFeedback( - feedbackParams: SendFeedbackParams, + params: SendFeedbackParams, hint: EventHint & { includeReplay?: boolean } = {}, scope = getCurrentScope(), ): string { - const { message, name, email, url, source, associatedEventId } = feedbackParams; + const { message, name, email, url, source, associatedEventId, tags } = params; const feedbackEvent: FeedbackEvent = { contexts: { @@ -25,6 +25,7 @@ export function captureFeedback( }, type: 'feedback', level: 'info', + tags, }; const client = (scope && scope.getClient()) || getClient(); diff --git a/packages/feedback/src/core/integration.ts b/packages/feedback/src/core/integration.ts index 11609cd35f89..0cfd84061e67 100644 --- a/packages/feedback/src/core/integration.ts +++ b/packages/feedback/src/core/integration.ts @@ -64,8 +64,10 @@ export const buildFeedbackIntegration = ({ const feedbackIntegration = (({ // FeedbackGeneralConfiguration id = 'sentry-feedback', - showBranding = true, autoInject = true, + showBranding = true, + isEmailRequired = false, + isNameRequired = false, showEmail = true, showName = true, enableScreenshot = true, @@ -73,8 +75,7 @@ export const buildFeedbackIntegration = ({ email: 'email', name: 'username', }, - isNameRequired = false, - isEmailRequired = false, + tags, // FeedbackThemeConfiguration colorScheme = 'system', @@ -115,6 +116,7 @@ export const buildFeedbackIntegration = ({ showName, enableScreenshot, useSentryUser, + tags, colorScheme, themeDark, diff --git a/packages/feedback/src/core/sendFeedback.ts b/packages/feedback/src/core/sendFeedback.ts index 3848eb9cbc33..2fa136b06897 100644 --- a/packages/feedback/src/core/sendFeedback.ts +++ b/packages/feedback/src/core/sendFeedback.ts @@ -1,7 +1,7 @@ import { captureFeedback } from '@sentry/core'; import { getClient } from '@sentry/core'; -import type { EventHint, SendFeedback, SendFeedbackParams, TransportMakeRequestResponse } from '@sentry/types'; -import type { Event } from '@sentry/types'; +import { getCurrentScope } from '@sentry/core'; +import type { Event, EventHint, SendFeedback, SendFeedbackParams, TransportMakeRequestResponse } from '@sentry/types'; import { getLocationHref } from '@sentry/utils'; import { FEEDBACK_API_SOURCE } from '../constants'; @@ -9,10 +9,10 @@ import { FEEDBACK_API_SOURCE } from '../constants'; * Public API to send a Feedback item to Sentry */ export const sendFeedback: SendFeedback = ( - options: SendFeedbackParams, + params: SendFeedbackParams, hint: EventHint & { includeReplay?: boolean } = { includeReplay: true }, ): Promise => { - if (!options.message) { + if (!params.message) { throw new Error('Unable to submit feedback with empty message'); } @@ -23,11 +23,14 @@ export const sendFeedback: SendFeedback = ( throw new Error('No client setup, cannot send feedback.'); } + if (params.tags && Object.keys(params.tags).length) { + getCurrentScope().setTags(params.tags); + } const eventId = captureFeedback( { source: FEEDBACK_API_SOURCE, url: getLocationHref(), - ...options, + ...params, }, hint, ); diff --git a/packages/feedback/src/modal/components/Form.tsx b/packages/feedback/src/modal/components/Form.tsx index 788512cb192b..c8815a7ef97d 100644 --- a/packages/feedback/src/modal/components/Form.tsx +++ b/packages/feedback/src/modal/components/Form.tsx @@ -46,6 +46,7 @@ export function Form({ screenshotInput, }: Props): VNode { const { + tags, addScreenshotButtonLabel, removeScreenshotButtonLabel, cancelButtonLabel, @@ -122,6 +123,7 @@ export function Form({ email: data.email, message: data.message, source: FEEDBACK_WIDGET_SOURCE, + tags, }, { attachments: data.attachments }, ); diff --git a/packages/feedback/src/util/mergeOptions.ts b/packages/feedback/src/util/mergeOptions.ts index 8e7961fc9a21..0a30a893c777 100644 --- a/packages/feedback/src/util/mergeOptions.ts +++ b/packages/feedback/src/util/mergeOptions.ts @@ -11,6 +11,10 @@ export function mergeOptions( return { ...defaultOptions, ...optionOverrides, + tags: { + ...defaultOptions.tags, + ...optionOverrides.tags, + }, onFormOpen: () => { optionOverrides.onFormOpen && optionOverrides.onFormOpen(); defaultOptions.onFormOpen && defaultOptions.onFormOpen(); diff --git a/packages/types/src/feedback/config.ts b/packages/types/src/feedback/config.ts index e6f92e65d52e..2350545941be 100644 --- a/packages/types/src/feedback/config.ts +++ b/packages/types/src/feedback/config.ts @@ -1,3 +1,4 @@ +import type { Primitive } from '../misc'; import type { FeedbackFormData } from './form'; import type { FeedbackTheme } from './theme'; @@ -55,6 +56,11 @@ export interface FeedbackGeneralConfiguration { email: string; name: string; }; + + /** + * Set an object that will be merged sent as tags data with the event. + */ + tags?: { [key: string]: Primitive }; } /** diff --git a/packages/types/src/feedback/sendFeedback.ts b/packages/types/src/feedback/sendFeedback.ts index a284e82f107b..8f865b57038d 100644 --- a/packages/types/src/feedback/sendFeedback.ts +++ b/packages/types/src/feedback/sendFeedback.ts @@ -1,4 +1,5 @@ import type { Event, EventHint } from '../event'; +import type { Primitive } from '../misc'; import type { User } from '../user'; /** @@ -38,13 +39,14 @@ export interface SendFeedbackParams { url?: string; source?: string; associatedEventId?: string; -} -interface SendFeedbackOptions extends EventHint { /** - * Should include replay with the feedback? + * Set an object that will be merged sent as tags data with the event. */ - includeReplay?: boolean; + tags?: { [key: string]: Primitive }; } -export type SendFeedback = (params: SendFeedbackParams, options?: SendFeedbackOptions) => Promise; +export type SendFeedback = ( + params: SendFeedbackParams, + hint?: EventHint & { includeReplay?: boolean }, +) => Promise;