Skip to content

Commit c9e2cc1

Browse files
docs(onboarding): add cloudflare onboarding (#83701)
Adding onboarding flows for both Cloudflare Pages and Workers. <img width="983" alt="Bildschirmfoto 2025-01-22 um 17 58 29" src="https://github.com/user-attachments/assets/5e0c6dea-69f1-4813-a63b-38e17182b3b5" /> <img width="493" alt="Bildschirmfoto 2025-01-22 um 17 58 41" src="https://github.com/user-attachments/assets/a547d19a-2f09-48c3-85b5-e383a26aa76b" /> <img width="992" alt="Bildschirmfoto 2025-01-22 um 17 58 56" src="https://github.com/user-attachments/assets/79445202-b074-4add-8110-d00105c90e19" /> <img width="988" alt="Bildschirmfoto 2025-01-22 um 17 58 11" src="https://github.com/user-attachments/assets/f0310361-5a32-4ac0-8e69-2b4f4fcf7049" /> Closes #56721
1 parent 3dc0233 commit c9e2cc1

12 files changed

+452
-10
lines changed

package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -138,7 +138,7 @@
138138
"moment-timezone": "0.5.44",
139139
"papaparse": "^5.3.2",
140140
"peggy": "^4.1.1",
141-
"platformicons": "^7.0.1",
141+
"platformicons": "^7.0.4",
142142
"po-catalog-loader": "2.1.0",
143143
"prettier": "3.3.2",
144144
"prismjs": "^1.29.0",

src/sentry/models/project.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -64,8 +64,6 @@
6464
"apple-macos",
6565
"bun",
6666
"capacitor",
67-
"cloudflare-pages",
68-
"cloudflare-workers",
6967
"cordova",
7068
"dart",
7169
"deno",
@@ -119,6 +117,8 @@
119117
"node",
120118
"node-awslambda",
121119
"node-azurefunctions",
120+
"node-cloudflare-pages",
121+
"node-cloudflare-workers",
122122
"node-connect",
123123
"node-express",
124124
"node-fastify",

src/sentry/utils/platform_categories.py

+4-3
Original file line numberDiff line numberDiff line change
@@ -51,8 +51,6 @@
5151
# When changing this file, make sure to keep sentry/static/app/data/platformCategories.tsx in sync.
5252
BACKEND = {
5353
"bun",
54-
"cloudflare-pages",
55-
"cloudflare-workers",
5654
"deno",
5755
"dotnet",
5856
"dotnet-aspnet",
@@ -78,6 +76,8 @@
7876
"kotlin",
7977
"native",
8078
"node",
79+
"node-cloudflare-pages",
80+
"node-cloudflare-workers",
8181
"node-connect",
8282
"node-express",
8383
"node-fastify",
@@ -121,10 +121,11 @@
121121
SERVERLESS = {
122122
"dotnet-awslambda",
123123
"dotnet-gcpfunctions",
124-
"cloudflare-workers",
125124
"node-awslambda",
126125
"node-azurefunctions",
127126
"node-gcpfunctions",
127+
"node-cloudflare-pages",
128+
"node-cloudflare-workers",
128129
"python-awslambda",
129130
"python-azurefunctions",
130131
"python-gcpfunctions",

static/app/data/platformCategories.tsx

+4
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,8 @@ export const backend: PlatformKey[] = [
8787
'node-express',
8888
'node-koa',
8989
'node-connect',
90+
'node-cloudflare-pages',
91+
'node-cloudflare-workers',
9092
'perl',
9193
'php',
9294
'php-laravel',
@@ -127,6 +129,8 @@ export const serverless: PlatformKey[] = [
127129
'node-awslambda',
128130
'node-azurefunctions',
129131
'node-gcpfunctions',
132+
'node-cloudflare-pages',
133+
'node-cloudflare-workers',
130134
'python-awslambda',
131135
'python-azurefunctions',
132136
'python-gcpfunctions',

static/app/data/platformPickerCategories.tsx

+4
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,8 @@ const server: Set<PlatformKey> = new Set([
7373
'kotlin',
7474
'native',
7575
'node',
76+
'node-cloudflare-pages',
77+
'node-cloudflare-workers',
7678
'node-connect',
7779
'node-express',
7880
'node-fastify',
@@ -144,6 +146,8 @@ const serverless: Set<PlatformKey> = new Set([
144146
'node-awslambda',
145147
'node-azurefunctions',
146148
'node-gcpfunctions',
149+
'node-cloudflare-pages',
150+
'node-cloudflare-workers',
147151
'python-awslambda',
148152
'python-gcpfunctions',
149153
'python-serverless',

static/app/data/platforms.tsx

+14
Original file line numberDiff line numberDiff line change
@@ -417,6 +417,20 @@ export const platforms: PlatformIntegration[] = [
417417
language: 'node',
418418
link: 'https://docs.sentry.io/platforms/javascript/guides/azure-functions/',
419419
},
420+
{
421+
id: 'node-cloudflare-pages',
422+
name: 'Cloudflare Pages',
423+
type: 'framework',
424+
language: 'node',
425+
link: 'https://docs.sentry.io/platforms/javascript/guides/cloudflare/',
426+
},
427+
{
428+
id: 'node-cloudflare-workers',
429+
name: 'Cloudflare Workers',
430+
type: 'framework',
431+
language: 'node',
432+
link: 'https://docs.sentry.io/platforms/javascript/guides/cloudflare/',
433+
},
420434
{
421435
id: 'node-connect',
422436
name: 'Connect',
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
import {renderWithOnboardingLayout} from 'sentry-test/onboarding/renderWithOnboardingLayout';
2+
import {screen} from 'sentry-test/reactTestingLibrary';
3+
import {textWithMarkupMatcher} from 'sentry-test/utils';
4+
5+
import {ProductSolution} from 'sentry/components/onboarding/gettingStartedDoc/types';
6+
7+
import docs from './cloudflare-pages';
8+
9+
describe('express onboarding docs', function () {
10+
it('renders onboarding docs correctly', () => {
11+
renderWithOnboardingLayout(docs);
12+
13+
// Renders main headings
14+
expect(screen.getByRole('heading', {name: 'Install'})).toBeInTheDocument();
15+
expect(screen.getByRole('heading', {name: 'Configure SDK'})).toBeInTheDocument();
16+
expect(screen.getByRole('heading', {name: 'Upload Source Maps'})).toBeInTheDocument();
17+
18+
// Includes import statement
19+
const allMatches = screen.getAllByText(
20+
textWithMarkupMatcher(/import \* as Sentry from "@sentry\/cloudflare"/)
21+
);
22+
allMatches.forEach(match => {
23+
expect(match).toBeInTheDocument();
24+
});
25+
});
26+
27+
it('displays sample rates by default', () => {
28+
renderWithOnboardingLayout(docs, {
29+
selectedProducts: [
30+
ProductSolution.ERROR_MONITORING,
31+
ProductSolution.PERFORMANCE_MONITORING,
32+
ProductSolution.PROFILING,
33+
],
34+
});
35+
36+
expect(
37+
screen.getByText(textWithMarkupMatcher(/tracesSampleRate/))
38+
).toBeInTheDocument();
39+
});
40+
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,161 @@
1+
import ExternalLink from 'sentry/components/links/externalLink';
2+
import {StepType} from 'sentry/components/onboarding/gettingStartedDoc/step';
3+
import type {
4+
Docs,
5+
DocsParams,
6+
OnboardingConfig,
7+
} from 'sentry/components/onboarding/gettingStartedDoc/types';
8+
import {getUploadSourceMapsStep} from 'sentry/components/onboarding/gettingStartedDoc/utils';
9+
import {
10+
getCrashReportJavaScriptInstallStep,
11+
getCrashReportModalConfigDescription,
12+
getCrashReportModalIntroduction,
13+
} from 'sentry/components/onboarding/gettingStartedDoc/utils/feedbackOnboarding';
14+
import {t, tct} from 'sentry/locale';
15+
import {getInstallConfig} from 'sentry/utils/gettingStartedDocs/node';
16+
17+
type Params = DocsParams;
18+
19+
const getSdkConfigureSnippetToml = () => `
20+
compatibility_flags = ["nodejs_compat"]
21+
# compatibility_flags = ["nodejs_als"]
22+
`;
23+
24+
const getSdkConfigureSnippetJson = () => `
25+
{
26+
"compatibility_flags": [
27+
"nodejs_compat"
28+
],
29+
"compatibility_date": "2024-09-23"
30+
}`;
31+
32+
const getSdkSetupSnippet = (params: Params) => `
33+
import * as Sentry from "@sentry/cloudflare";
34+
35+
export const onRequest = [
36+
// Make sure Sentry is the first middleware
37+
Sentry.sentryPagesPlugin((context) => ({
38+
dsn: "${params.dsn.public}",
39+
// Set tracesSampleRate to 1.0 to capture 100% of spans for tracing.
40+
// Learn more at
41+
// https://docs.sentry.io/platforms/javascript/configuration/options/#traces-sample-rate
42+
tracesSampleRate: 1.0,
43+
})),
44+
// Add more middlewares here
45+
];`;
46+
47+
const getVerifySnippet = () => `
48+
setTimeout(() => {
49+
throw new Error();
50+
});`;
51+
52+
const onboarding: OnboardingConfig = {
53+
introduction: () =>
54+
t(
55+
'In this quick guide you’ll set up and configure the Sentry Cloudflare SDK for the use in your Cloudflare Pages application.'
56+
),
57+
install: params => [
58+
{
59+
type: StepType.INSTALL,
60+
description: t('Add the Sentry Cloudflare SDK as a dependency:'),
61+
configurations: getInstallConfig(params, {
62+
basePackage: '@sentry/cloudflare',
63+
}),
64+
},
65+
],
66+
configure: params => [
67+
{
68+
type: StepType.CONFIGURE,
69+
description: t(
70+
"Configuration should happen as early as possible in your application's lifecycle."
71+
),
72+
configurations: [
73+
{
74+
description: tct(
75+
"To use the SDK, you'll need to set either the [code:nodejs_compat] or [code:nodejs_als] compatibility flags in your [code:wrangler.toml]. This is because the SDK needs access to the [code:AsyncLocalStorage] API to work correctly.",
76+
{
77+
code: <code />,
78+
}
79+
),
80+
code: [
81+
{
82+
label: 'JSON',
83+
value: 'json',
84+
language: 'json',
85+
filename: 'wrangler.json',
86+
code: getSdkConfigureSnippetJson(),
87+
},
88+
{
89+
label: 'Toml',
90+
value: 'toml',
91+
language: 'toml',
92+
filename: 'wrangler.toml',
93+
code: getSdkConfigureSnippetToml(),
94+
},
95+
],
96+
},
97+
{
98+
description: tct(
99+
'Add the [code:sentryPagesPlugin] as [guideLink:middleware to your Cloudflare Pages application]. We recommend adding a [code:functions/_middleware.js] for the middleware setup so that Sentry is initialized for your entire app.',
100+
{
101+
code: <code />,
102+
guideLink: (
103+
<ExternalLink href="https://developers.cloudflare.com/pages/functions/middleware/" />
104+
),
105+
}
106+
),
107+
code: [
108+
{
109+
label: 'JavaScript',
110+
value: 'javascript',
111+
language: 'javascript',
112+
filename: 'functions/_middleware.js',
113+
code: getSdkSetupSnippet(params),
114+
},
115+
],
116+
},
117+
],
118+
},
119+
getUploadSourceMapsStep({
120+
guideLink:
121+
'https://docs.sentry.io/platforms/javascript/guides/cloudflare/sourcemaps/',
122+
...params,
123+
}),
124+
],
125+
verify: () => [
126+
{
127+
type: StepType.VERIFY,
128+
description: t(
129+
"This snippet contains an intentional error and can be used as a test to make sure that everything's working as expected."
130+
),
131+
configurations: [
132+
{
133+
language: 'javascript',
134+
code: getVerifySnippet(),
135+
},
136+
],
137+
},
138+
],
139+
};
140+
141+
const crashReportOnboarding: OnboardingConfig = {
142+
introduction: () => getCrashReportModalIntroduction(),
143+
install: (params: Params) => getCrashReportJavaScriptInstallStep(params),
144+
configure: () => [
145+
{
146+
type: StepType.CONFIGURE,
147+
description: getCrashReportModalConfigDescription({
148+
link: 'https://docs.sentry.io/platforms/javascript/guides/cloudflare/user-feedback/configuration/#crash-report-modal',
149+
}),
150+
},
151+
],
152+
verify: () => [],
153+
nextSteps: () => [],
154+
};
155+
156+
const docs: Docs = {
157+
onboarding,
158+
crashReportOnboarding,
159+
};
160+
161+
export default docs;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
import {renderWithOnboardingLayout} from 'sentry-test/onboarding/renderWithOnboardingLayout';
2+
import {screen} from 'sentry-test/reactTestingLibrary';
3+
import {textWithMarkupMatcher} from 'sentry-test/utils';
4+
5+
import {ProductSolution} from 'sentry/components/onboarding/gettingStartedDoc/types';
6+
7+
import docs from './cloudflare-workers';
8+
9+
describe('express onboarding docs', function () {
10+
it('renders onboarding docs correctly', () => {
11+
renderWithOnboardingLayout(docs);
12+
13+
// Renders main headings
14+
expect(screen.getByRole('heading', {name: 'Install'})).toBeInTheDocument();
15+
expect(screen.getByRole('heading', {name: 'Configure SDK'})).toBeInTheDocument();
16+
expect(screen.getByRole('heading', {name: 'Upload Source Maps'})).toBeInTheDocument();
17+
18+
// Includes import statement
19+
const allMatches = screen.getAllByText(
20+
textWithMarkupMatcher(/import \* as Sentry from "@sentry\/cloudflare"/)
21+
);
22+
allMatches.forEach(match => {
23+
expect(match).toBeInTheDocument();
24+
});
25+
});
26+
27+
it('displays sample rates by default', () => {
28+
renderWithOnboardingLayout(docs, {
29+
selectedProducts: [
30+
ProductSolution.ERROR_MONITORING,
31+
ProductSolution.PERFORMANCE_MONITORING,
32+
ProductSolution.PROFILING,
33+
],
34+
});
35+
36+
expect(
37+
screen.getByText(textWithMarkupMatcher(/tracesSampleRate/))
38+
).toBeInTheDocument();
39+
});
40+
41+
it('enables performance setting the tracesSampleRate to 1', () => {
42+
renderWithOnboardingLayout(docs, {
43+
selectedProducts: [
44+
ProductSolution.ERROR_MONITORING,
45+
ProductSolution.PERFORMANCE_MONITORING,
46+
],
47+
});
48+
49+
expect(
50+
screen.getByText(textWithMarkupMatcher(/tracesSampleRate: 1\.0/))
51+
).toBeInTheDocument();
52+
});
53+
});

0 commit comments

Comments
 (0)