Skip to content

Commit 6921f06

Browse files
authored
feat(feedback): Bootstrap new Feedback integration (#9139)
Note this is mostly a placeholder for our new Feedback integration. This is not intended to be published (yet) with the rest of the monorepo packages. It currently has limited functionality. There will be follow-ups to allow creation of the UI widget, and eventually will be added to the Sentry namespace.
1 parent 4ee2f03 commit 6921f06

23 files changed

+632
-1
lines changed

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@
5050
"packages/ember",
5151
"packages/eslint-config-sdk",
5252
"packages/eslint-plugin-sdk",
53+
"packages/feedback",
5354
"packages/gatsby",
5455
"packages/hub",
5556
"packages/integrations",

packages/feedback/.eslintignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
node_modules/
2+
build/

packages/feedback/.eslintrc.js

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
// Note: All paths are relative to the directory in which eslint is being run, rather than the directory where this file
2+
// lives
3+
4+
// ESLint config docs: https://eslint.org/docs/user-guide/configuring/
5+
6+
module.exports = {
7+
extends: ['../../.eslintrc.js'],
8+
overrides: [
9+
{
10+
files: ['jest.setup.ts', 'jest.config.ts'],
11+
parserOptions: {
12+
project: ['tsconfig.test.json'],
13+
},
14+
rules: {
15+
'no-console': 'off',
16+
},
17+
},
18+
{
19+
files: ['test/**/*.ts'],
20+
21+
rules: {
22+
// most of these errors come from `new Promise(process.nextTick)`
23+
'@typescript-eslint/unbound-method': 'off',
24+
// TODO: decide if we want to enable this again after the migration
25+
// We can take the freedom to be a bit more lenient with tests
26+
'@typescript-eslint/no-floating-promises': 'off',
27+
},
28+
},
29+
{
30+
files: ['src/types/deprecated.ts'],
31+
rules: {
32+
'@typescript-eslint/naming-convention': 'off',
33+
},
34+
},
35+
],
36+
};

packages/feedback/.gitignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
node_modules
2+
/*.tgz
3+
.eslintcache
4+
build

packages/feedback/LICENSE

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
Copyright (c) 2023 Sentry (https://sentry.io) and individual contributors. All rights reserved.
2+
3+
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
4+
documentation files (the "Software"), to deal in the Software without restriction, including without limitation the
5+
rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit
6+
persons to whom the Software is furnished to do so, subject to the following conditions:
7+
8+
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the
9+
Software.
10+
11+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
12+
WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
13+
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
14+
OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

packages/feedback/README.md

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
<p align="center">
2+
<a href="https://sentry.io/?utm_source=github&utm_medium=logo" target="_blank">
3+
<img src="https://sentry-brand.storage.googleapis.com/sentry-wordmark-dark-280x84.png" alt="Sentry" width="280" height="84">
4+
</a>
5+
</p>
6+
7+
# Sentry Integration for Feedback
8+
9+
This SDK is **considered experimental and in an alpha state**. It may experience breaking changes, and may be discontinued at any time. Please reach out on
10+
[GitHub](https://github.com/getsentry/sentry-javascript/issues/new/choose) if you have any feedback/concerns.
11+
12+
## Pre-requisites
13+
14+
`@sentry/feedback` currently can only be used by browsers with [Shadow DOM](https://developer.mozilla.org/en-US/docs/Web/API/Web_components/Using_shadow_DOM) support.
15+
16+
## Installation
17+
18+
Feedback can be imported from `@sentry/browser`, or a respective SDK package like `@sentry/react` or `@sentry/vue`.
19+
You don't need to install anything in order to use Feedback. The minimum version that includes Feedback is <<CHANGEME>>.
20+
21+
For details on using Feedback when using Sentry via the CDN bundles, see [CDN bundle](#loading-feedback-as-a-cdn-bundle).
22+
23+
## Setup
24+
25+
To set up the integration, add the following to your Sentry initialization. Several options are supported and passable via the integration constructor.
26+
See the [configuration section](#configuration) below for more details.
27+
28+
```javascript
29+
import * as Sentry from '@sentry/browser';
30+
// or e.g. import * as Sentry from '@sentry/react';
31+
32+
Sentry.init({
33+
dsn: '__DSN__',
34+
integrations: [
35+
new Sentry.Feedback({
36+
// Additional SDK configuration goes in here, for example:
37+
// See below for all available options
38+
})
39+
],
40+
// ...
41+
});
42+
```
43+
44+
### Lazy loading Feedback
45+
46+
Feedback will start automatically when you add the integration.
47+
If you do not want to start Feedback immediately (e.g. if you want to lazy-load it),
48+
you can also use `addIntegration` to load it later:
49+
50+
```js
51+
import * as Sentry from "@sentry/react";
52+
import { BrowserClient } from "@sentry/browser";
53+
54+
Sentry.init({
55+
// Do not load it initially
56+
integrations: []
57+
});
58+
59+
// Sometime later
60+
const { Feedback } = await import('@sentry/browser');
61+
const client = Sentry.getCurrentHub().getClient<BrowserClient>();
62+
63+
// Client can be undefined
64+
client?.addIntegration(new Feedback());
65+
```
66+
67+
### Identifying Users
68+
69+
If you have only followed the above instructions to setup session feedbacks, you will only see IP addresses in Sentry's UI. In order to associate a user identity to a session feedback, use [`setUser`](https://docs.sentry.io/platforms/javascript/enriching-events/identify-user/).
70+
71+
```javascript
72+
import * as Sentry from "@sentry/browser";
73+
74+
Sentry.setUser({ email: "[email protected]" });
75+
```
76+
77+
## Loading Feedback as a CDN Bundle
78+
79+
As an alternative to the NPM package, you can use Feedback as a CDN bundle.
80+
Please refer to the [Feedback installation guide](https://docs.sentry.io/platforms/javascript/session-feedback/#install) for CDN bundle instructions.
81+
82+
83+
## Configuration
84+
85+
### General Integration Configuration
86+
87+
The following options can be configured as options to the integration, in `new Feedback({})`:
88+
89+
| key | type | default | description |
90+
| --------- | ------- | ------- | ----------- |
91+
| tbd | boolean | `true` | tbd |
92+
93+
94+
95+
## Manually Sending Feedback Data
96+
97+
Connect your own feedback UI to Sentry's You can use `feedback.flush()` to immediately send all currently captured feedback data.
98+
When Feedback is currently in buffering mode, this will send up to the last 60 seconds of feedback data,
99+
and also continue sending afterwards, similar to when an error happens & is recorded.

packages/feedback/jest.config.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
module.exports = require('../../jest/jest.config.js');

packages/feedback/package.json

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
{
2+
"name": "@sentry-internal/feedback",
3+
"version": "7.70.0",
4+
"description": "Sentry SDK integration for user feedback",
5+
"repository": "git://github.com/getsentry/sentry-javascript.git",
6+
"homepage": "https://github.com/getsentry/sentry-javascript/tree/master/packages/feedback",
7+
"author": "Sentry",
8+
"license": "MIT",
9+
"engines": {
10+
"node": ">=12"
11+
},
12+
"main": "build/npm/cjs/index.js",
13+
"module": "build/npm/esm/index.js",
14+
"types": "build/npm/types/index.d.ts",
15+
"typesVersions": {
16+
"<4.9": {
17+
"build/npm/types/index.d.ts": [
18+
"build/npm/types-ts3.8/index.d.ts"
19+
]
20+
}
21+
},
22+
"publishConfig": {
23+
"access": "public"
24+
},
25+
"dependencies": {
26+
"@sentry/core": "7.70.0",
27+
"@sentry/types": "7.70.0",
28+
"@sentry/utils": "7.70.0",
29+
"tslib": "^2.4.1 || ^1.9.3"
30+
},
31+
"scripts": {
32+
"build": "run-p build:transpile build:types build:bundle",
33+
"build:transpile": "rollup -c rollup.npm.config.js",
34+
"build:bundle": "rollup -c rollup.bundle.config.js",
35+
"build:dev": "run-p build:transpile build:types",
36+
"build:types": "run-s build:types:core build:types:downlevel",
37+
"build:types:core": "tsc -p tsconfig.types.json",
38+
"build:types:downlevel": "yarn downlevel-dts build/npm/types build/npm/types-ts3.8 --to ts3.8",
39+
"build:watch": "run-p build:transpile:watch build:bundle:watch build:types:watch",
40+
"build:dev:watch": "run-p build:transpile:watch build:types:watch",
41+
"build:transpile:watch": "yarn build:transpile --watch",
42+
"build:bundle:watch": "yarn build:bundle --watch",
43+
"build:types:watch": "tsc -p tsconfig.types.json --watch",
44+
"build:tarball": "ts-node ../../scripts/prepack.ts --bundles && npm pack ./build/npm",
45+
"circularDepCheck": "madge --circular src/index.ts",
46+
"clean": "rimraf build sentry-replay-*.tgz",
47+
"fix": "run-s fix:eslint fix:prettier",
48+
"fix:eslint": "eslint . --format stylish --fix",
49+
"fix:prettier": "prettier --write \"{src,test,scripts}/**/*.ts\"",
50+
"lint": "run-s lint:prettier lint:eslint",
51+
"lint:eslint": "eslint . --format stylish",
52+
"lint:prettier": "prettier --check \"{src,test,scripts}/**/*.ts\"",
53+
"test": "jest",
54+
"test:watch": "jest --watch",
55+
"yalc:publish": "ts-node ../../scripts/prepack.ts --bundles && yalc publish ./build/npm --push"
56+
},
57+
"volta": {
58+
"extends": "../../package.json"
59+
},
60+
"sideEffects": false
61+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import { makeBaseBundleConfig, makeBundleConfigVariants } from '../../rollup/index.js';
2+
3+
const baseBundleConfig = makeBaseBundleConfig({
4+
bundleType: 'addon',
5+
entrypoints: ['src/index.ts'],
6+
jsVersion: 'es6',
7+
licenseTitle: '@sentry-internal/feedback',
8+
outputFileBase: () => 'bundles/feedback',
9+
});
10+
11+
const builds = makeBundleConfigVariants(baseBundleConfig);
12+
13+
export default builds;
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import { makeBaseNPMConfig, makeNPMConfigVariants } from '../../rollup/index';
2+
3+
export default makeNPMConfigVariants(
4+
makeBaseNPMConfig({
5+
hasBundles: true,
6+
packageSpecificConfig: {
7+
output: {
8+
// set exports to 'named' or 'auto' so that rollup doesn't warn
9+
exports: 'named',
10+
// set preserveModules to false because for feedback we actually want
11+
// to bundle everything into one file.
12+
preserveModules: false,
13+
},
14+
},
15+
}),
16+
);

packages/feedback/src/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export { sendFeedbackRequest } from './util/sendFeedbackRequest';

packages/feedback/src/types/index.ts

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
import type { Event, Primitive } from '@sentry/types';
2+
3+
export type SentryTags = { [key: string]: Primitive } | undefined;
4+
5+
/**
6+
* NOTE: These types are still considered Beta and subject to change.
7+
* @hidden
8+
*/
9+
export interface FeedbackEvent extends Event {
10+
feedback: {
11+
message: string;
12+
url: string;
13+
contact_email?: string;
14+
name?: string;
15+
replay_id?: string;
16+
};
17+
// TODO: Add this event type to Event
18+
// type: 'feedback_event';
19+
}
20+
21+
export interface SendFeedbackData {
22+
feedback: {
23+
message: string;
24+
url: string;
25+
email?: string;
26+
replay_id?: string;
27+
name?: string;
28+
};
29+
}
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
import type { Scope } from '@sentry/core';
2+
import { prepareEvent } from '@sentry/core';
3+
import type { Client } from '@sentry/types';
4+
5+
import type { FeedbackEvent } from '../types';
6+
7+
interface PrepareFeedbackEventParams {
8+
client: Client;
9+
event: FeedbackEvent;
10+
scope: Scope;
11+
}
12+
/**
13+
* Prepare a feedback event & enrich it with the SDK metadata.
14+
*/
15+
export async function prepareFeedbackEvent({
16+
client,
17+
scope,
18+
event,
19+
}: PrepareFeedbackEventParams): Promise<FeedbackEvent | null> {
20+
const eventHint = { integrations: undefined };
21+
if (client.emit) {
22+
client.emit('preprocessEvent', event, eventHint);
23+
}
24+
25+
const preparedEvent = (await prepareEvent(
26+
client.getOptions(),
27+
event,
28+
{ integrations: undefined },
29+
scope,
30+
)) as FeedbackEvent | null;
31+
32+
// If e.g. a global event processor returned null
33+
if (!preparedEvent) {
34+
return null;
35+
}
36+
37+
// This normally happens in browser client "_prepareEvent"
38+
// but since we do not use this private method from the client, but rather the plain import
39+
// we need to do this manually.
40+
preparedEvent.platform = preparedEvent.platform || 'javascript';
41+
42+
// extract the SDK name because `client._prepareEvent` doesn't add it to the event
43+
const metadata = client.getSdkMetadata && client.getSdkMetadata();
44+
const { name, version } = (metadata && metadata.sdk) || {};
45+
46+
preparedEvent.sdk = {
47+
...preparedEvent.sdk,
48+
name: name || 'sentry.javascript.unknown',
49+
version: version || '0.0.0',
50+
};
51+
return preparedEvent;
52+
}

0 commit comments

Comments
 (0)