Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

meta(changelog): Update changelog for 7.81.0 #9596

Merged
merged 18 commits into from
Nov 20, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
010eb69
Merge pull request #9552 from getsentry/master
github-actions[bot] Nov 14, 2023
f1ed0e9
ref: Deprecate `extractTraceParentData` from `@sentry/core` & downstr…
mydea Nov 14, 2023
11e0c2d
feat(nextjs): Add instrumentation utility for server actions (#9553)
lforst Nov 14, 2023
db127a3
chore(feedback): Readme note beta and ea org (#9558)
bruno-garcia Nov 14, 2023
04a41ee
codeql: use extended query pack
mdtro Nov 14, 2023
6d42457
docs(feedback): Example docs on `sendFeedback` (#9560)
billyvg Nov 14, 2023
ad4f2d1
Merge pull request #9559 from getsentry/mdtro/codeql-extended
mdtro Nov 14, 2023
ade4c1d
test(e2e): Assert that there are no funky build time warnings when bu…
lforst Nov 16, 2023
d68ab17
test: Add test for manual client usage (#9567)
mydea Nov 16, 2023
e4cd09c
test: Add loader test for unhandled promise rejection (#9581)
mydea Nov 16, 2023
5698094
ref(replay): Add further logging to network body parsing (#9566)
mydea Nov 16, 2023
ed2b201
fix(nextjs): Download CLI binary if it can't be found (#9584)
lforst Nov 16, 2023
13aaf3c
feat(feedback): Add `level` and remove breadcrumbs from feedback even…
billyvg Nov 16, 2023
f48b697
dev: Add missing `--sig` arg to `yalc:publish` task (#9511)
billyvg Nov 16, 2023
1e2bf6e
ref(tracing-internal): Export fetch instrumentation (#9473)
lforst Nov 17, 2023
ff416ae
feat(vercel-edge): Add fetch instrumentation (#9504)
lforst Nov 17, 2023
6f4edf8
feat(vue): Support Vue 3 lifecycle hooks in mixin options (#9578)
snoozbuster Nov 20, 2023
4ff96ec
meta(changelog): Update changelog for 7.81.0
lforst Nov 20, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .github/workflows/codeql-analysis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ concurrency:
jobs:
analyze:
name: Analyze
runs-on: ubuntu-20.04
runs-on: ubuntu-latest

strategy:
fail-fast: false
Expand All @@ -51,6 +51,7 @@ jobs:
uses: github/codeql-action/init@v2
with:
config-file: ./.github/codeql/codeql-config.yml
queries: security-extended
languages: ${{ matrix.language }}
# If you wish to specify custom queries, you can do so here or in a config file.
# By default, queries listed here will override any specified in a config file.
Expand Down
53 changes: 53 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,59 @@

- "You miss 100 percent of the chances you don't take. — Wayne Gretzky" — Michael Scott

## 7.81.0

### Important Changes

**- feat(nextjs): Add instrumentation utility for server actions (#9553)**

This release adds a utility function `withServerActionInstrumentation` to the `@sentry/nextjs` SDK for instrumenting your Next.js server actions with error and performance monitoring.

You can optionally pass form data and headers to record them, and configure the wrapper to record the Server Action responses:

```tsx
import * as Sentry from "@sentry/nextjs";
import { headers } from "next/headers";

export default function ServerComponent() {
async function myServerAction(formData: FormData) {
"use server";
return await Sentry.withServerActionInstrumentation(
"myServerAction", // The name you want to associate this Server Action with in Sentry
{
formData, // Optionally pass in the form data
headers: headers(), // Optionally pass in headers
recordResponse: true, // Optionally record the server action response
},
async () => {
// ... Your Server Action code

return { name: "John Doe" };
}
);
}

return (
<form action={myServerAction}>
<input type="text" name="some-input-value" />
<button type="submit">Run Action</button>
</form>
);
}
```

### Other Changes

- docs(feedback): Example docs on `sendFeedback` (#9560)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
- docs(feedback): Example docs on `sendFeedback` (#9560)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thought I'd leave it in because maybe people become inspired and look at the examples 🤷

- feat(feedback): Add `level` and remove breadcrumbs from feedback event (#9533)
- feat(vercel-edge): Add fetch instrumentation (#9504)
- feat(vue): Support Vue 3 lifecycle hooks in mixin options (#9578)
- fix(nextjs): Download CLI binary if it can't be found (#9584)
- ref: Deprecate `extractTraceParentData` from `@sentry/core` & downstream packages (#9158)
- ref(replay): Add further logging to network body parsing (#9566)

Work in this release contributed by @snoozbuster. Thank you for your contribution!

## 7.80.1

- fix(astro): Adjust Vite plugin config to upload server source maps (#9541)
Expand Down
6 changes: 6 additions & 0 deletions MIGRATION.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,12 @@ npx @sentry/migr8@latest

This will let you select which updates to run, and automatically update your code. Make sure to still review all code changes!

## Deprecate `extractTraceParentData` export from `@sentry/core` & downstream packages

Instead, import this directly from `@sentry/utils`.

Generally, in most cases you should probably use `continueTrace` instead, which abstracts this away from you and handles scope propagation for you.

## Deprecate `timestampWithMs` export - #7878

The `timestampWithMs` util is deprecated in favor of using `timestampInSeconds`.
Expand Down
1 change: 1 addition & 0 deletions packages/astro/src/index.server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ export {
withMonitor,
configureScope,
createTransport,
// eslint-disable-next-line deprecation/deprecation
extractTraceparentData,
getActiveTransaction,
getHubFromCarrier,
Expand Down
2 changes: 1 addition & 1 deletion packages/browser-integration-tests/fixtures/loader.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
new Promise(function (resolve, reject) {
reject('this is unhandled');
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { expect } from '@playwright/test';

import { sentryTest } from '../../../../utils/fixtures';
import { envelopeRequestParser, waitForErrorRequestOnUrl } from '../../../../utils/helpers';

sentryTest('unhandled promise rejection handler works', async ({ getLocalTestUrl, page }) => {
const url = await getLocalTestUrl({ testDir: __dirname });
const req = await waitForErrorRequestOnUrl(page, url);

const eventData = envelopeRequestParser(req);
expect(eventData.exception?.values?.length).toBe(1);
expect(eventData.exception?.values?.[0]?.value).toBe(
'Non-Error promise rejection captured with value: this is unhandled',
);
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import * as Sentry from '@sentry/browser';
import { Feedback } from '@sentry-internal/feedback';

window.Sentry = Sentry;

Sentry.init({
dsn: 'https://public@dsn.ingest.sentry.io/1337',
integrations: [new Feedback()],
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
</head>
<body></body>
</html>
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
import { expect } from '@playwright/test';

import { sentryTest } from '../../../utils/fixtures';
import { envelopeRequestParser, getEnvelopeType } from '../../../utils/helpers';

sentryTest('should capture feedback (@sentry-internal/feedback import)', async ({ getLocalTestPath, page }) => {
if (process.env.PW_BUNDLE) {
sentryTest.skip();
}

const feedbackRequestPromise = page.waitForResponse(res => {
const req = res.request();

const postData = req.postData();
if (!postData) {
return false;
}

try {
return getEnvelopeType(req) === 'feedback';
} catch (err) {
return false;
}
});

await page.route('https://dsn.ingest.sentry.io/**/*', route => {
return route.fulfill({
status: 200,
contentType: 'application/json',
body: JSON.stringify({ id: 'test-id' }),
});
});

const url = await getLocalTestPath({ testDir: __dirname });

await page.goto(url);
await page.getByText('Report a Bug').click();
expect(await page.locator(':visible:text-is("Report a Bug")').count()).toEqual(1);
await page.locator('[name="name"]').fill('Jane Doe');
await page.locator('[name="email"]').fill('janedoe@example.org');
await page.locator('[name="message"]').fill('my example feedback');
await page.getByLabel('Send Bug Report').click();

const feedbackEvent = envelopeRequestParser((await feedbackRequestPromise).request());
expect(feedbackEvent).toEqual({
type: 'feedback',
contexts: {
feedback: {
contact_email: 'janedoe@example.org',
message: 'my example feedback',
name: 'Jane Doe',
source: 'widget',
url: expect.stringContaining('/dist/index.html'),
},
},
level: 'info',
timestamp: expect.any(Number),
event_id: expect.stringMatching(/\w{32}/),
environment: 'production',
sdk: {
integrations: expect.arrayContaining(['Feedback']),
version: expect.any(String),
name: 'sentry.javascript.browser',
packages: expect.anything(),
},
request: {
url: expect.stringContaining('/dist/index.html'),
headers: {
'User-Agent': expect.stringContaining(''),
},
},
platform: 'javascript',
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import * as Sentry from '@sentry/browser';
import { Feedback } from '@sentry-internal/feedback';

window.Sentry = Sentry;

Sentry.init({
dsn: 'https://public@dsn.ingest.sentry.io/1337',
replaysOnErrorSampleRate: 1.0,
replaysSessionSampleRate: 0,
integrations: [
new Sentry.Replay({
flushMinDelay: 200,
flushMaxDelay: 200,
minReplayDuration: 0,
}),
new Feedback(),
],
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
</head>
<body></body>
</html>
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
import { expect } from '@playwright/test';

import { sentryTest } from '../../../utils/fixtures';
import { envelopeRequestParser, getEnvelopeType } from '../../../utils/helpers';
import { getCustomRecordingEvents, getReplayEvent, waitForReplayRequest } from '../../../utils/replayHelpers';

sentryTest('should capture feedback (@sentry-internal/feedback import)', async ({ getLocalTestPath, page }) => {
if (process.env.PW_BUNDLE) {
sentryTest.skip();
}

const reqPromise0 = waitForReplayRequest(page, 0);
const feedbackRequestPromise = page.waitForResponse(res => {
const req = res.request();

const postData = req.postData();
if (!postData) {
return false;
}

try {
return getEnvelopeType(req) === 'feedback';
} catch (err) {
return false;
}
});

await page.route('https://dsn.ingest.sentry.io/**/*', route => {
return route.fulfill({
status: 200,
contentType: 'application/json',
body: JSON.stringify({ id: 'test-id' }),
});
});

const url = await getLocalTestPath({ testDir: __dirname });

await page.goto(url);
await page.getByText('Report a Bug').click();
await page.locator('[name="name"]').fill('Jane Doe');
await page.locator('[name="email"]').fill('janedoe@example.org');
await page.locator('[name="message"]').fill('my example feedback');
await page.getByLabel('Send Bug Report').click();

const [feedbackResp, replayReq] = await Promise.all([feedbackRequestPromise, reqPromise0]);

const feedbackEvent = envelopeRequestParser(feedbackResp.request());
const replayEvent = getReplayEvent(replayReq);
const { breadcrumbs } = getCustomRecordingEvents(replayReq);

expect(breadcrumbs).toEqual(
expect.arrayContaining([
{
category: 'sentry.feedback',
data: { feedbackId: expect.any(String) },
},
]),
);

expect(feedbackEvent).toEqual({
type: 'feedback',
contexts: {
feedback: {
contact_email: 'janedoe@example.org',
message: 'my example feedback',
name: 'Jane Doe',
replay_id: replayEvent.event_id,
source: 'widget',
url: expect.stringContaining('/dist/index.html'),
},
},
level: 'info',
timestamp: expect.any(Number),
event_id: expect.stringMatching(/\w{32}/),
environment: 'production',
sdk: {
integrations: expect.arrayContaining(['Feedback']),
version: expect.any(String),
name: 'sentry.javascript.browser',
packages: expect.anything(),
},
request: {
url: expect.stringContaining('/dist/index.html'),
headers: {
'User-Agent': expect.stringContaining(''),
},
},
platform: 'javascript',
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import {
BrowserClient,
Breadcrumbs,
Dedupe,
FunctionToString,
HttpContext,
InboundFilters,
LinkedErrors,
defaultStackParser,
makeFetchTransport,
Hub,
} from '@sentry/browser';

const integrations = [
new Breadcrumbs(),
new FunctionToString(),
new Dedupe(),
new HttpContext(),
new InboundFilters(),
new LinkedErrors(),
];

const client = new BrowserClient({
dsn: 'https://public@dsn.ingest.sentry.io/1337',
release: '0.0.1',
environment: 'local',
sampleRate: 1.0,
tracesSampleRate: 0.0,
transport: makeFetchTransport,
stackParser: defaultStackParser,
integrations,
debug: true,
});

const hub = new Hub(client);

hub.captureException(new Error('test client'));
Loading
Loading