Skip to content

Commit

Permalink
feat(bundles): Add pluggable integrations on CDN to Sentry namespace (
Browse files Browse the repository at this point in the history
#10452)

Previously, they were only put on `Sentry.Integrations.XXX`, now you can
do e.g. `Sentry.httpClientIntegration()`.

While at it, I also added a browser integration test for this. I also
made the way we do this more future proof, as in v8 this will not be
imported anymore from `@sentry/integrations` (which we rely on right
now), plus the heuristic used to rely on integration name === filename.
Now, there is a manual map of imported method names to a CDN bundle file
name.
  • Loading branch information
mydea authored and onurtemizkan committed Feb 4, 2024
1 parent 4c6b638 commit 9317f85
Show file tree
Hide file tree
Showing 5 changed files with 112 additions and 4 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import * as Sentry from '@sentry/browser';
import { httpClientIntegration } from '@sentry/integrations';

window.Sentry = Sentry;

Sentry.init({
dsn: 'https://public@dsn.ingest.sentry.io/1337',
integrations: [httpClientIntegration()],
tracesSampleRate: 1,
sendDefaultPii: true,
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
const xhr = new XMLHttpRequest();

xhr.open('GET', 'http://localhost:7654/foo', true);
xhr.withCredentials = true;
xhr.setRequestHeader('Accept', 'application/json');
xhr.setRequestHeader('Content-Type', 'application/json');
xhr.setRequestHeader('Cache', 'no-cache');
xhr.send();
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import { expect } from '@playwright/test';
import type { Event } from '@sentry/types';

import { sentryTest } from '../../../../utils/fixtures';
import { getFirstSentryEnvelopeRequest } from '../../../../utils/helpers';

sentryTest('works with httpClientIntegration', async ({ getLocalTestPath, page }) => {
const url = await getLocalTestPath({ testDir: __dirname });

await page.route('**/foo', route => {
return route.fulfill({
status: 500,
body: JSON.stringify({
error: {
message: 'Internal Server Error',
},
}),
headers: {
'Content-Type': 'text/html',
},
});
});

const eventData = await getFirstSentryEnvelopeRequest<Event>(page, url);

expect(eventData.exception?.values).toHaveLength(1);

// Not able to get the cookies from the request/response because of Playwright bug
// https://github.com/microsoft/playwright/issues/11035
expect(eventData).toMatchObject({
message: 'HTTP Client Error with status code: 500',
exception: {
values: [
{
type: 'Error',
value: 'HTTP Client Error with status code: 500',
mechanism: {
type: 'http.client',
handled: false,
},
},
],
},
request: {
url: 'http://localhost:7654/foo',
method: 'GET',
headers: {
accept: 'application/json',
cache: 'no-cache',
'content-type': 'application/json',
},
},
contexts: {
response: {
status_code: 500,
body_size: 45,
headers: {
'content-type': 'text/html',
'content-length': '45',
},
},
},
});
});
32 changes: 28 additions & 4 deletions dev-packages/browser-integration-tests/utils/generatePlugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,27 @@ const useCompiledModule = bundleKey === 'esm' || bundleKey === 'cjs';
const useBundleOrLoader = bundleKey && !useCompiledModule;
const useLoader = bundleKey.startsWith('loader');

// These are imports that, when using CDN bundles, are not included in the main CDN bundle.
// In this case, if we encounter this import, we want to add this CDN bundle file instead
const IMPORTED_INTEGRATION_CDN_BUNDLE_PATHS: Record<string, string> = {
httpClientIntegration: 'httpclient',
HttpClient: 'httpclient',
captureConsoleIntegration: 'captureconsole',
CaptureConsole: 'captureconsole',
debugIntegration: 'debug',
Debug: 'debug',
rewriteFramesIntegration: 'rewriteframes',
RewriteFrames: 'rewriteframes',
contextLinesIntegration: 'contextlines',
ContextLines: 'contextlines',
extraErrorDataIntegration: 'extraerrordata',
ExtraErrorData: 'extraerrordata',
reportingObserverIntegration: 'reportingobserver',
ReportingObserver: 'reportingobserver',
sessionTimingIntegration: 'sessiontiming',
SessionTiming: 'sessiontiming',
};

const BUNDLE_PATHS: Record<string, Record<string, string>> = {
browser: {
cjs: 'build/npm/cjs/index.js',
Expand Down Expand Up @@ -149,8 +170,8 @@ class SentryScenarioGenerationPlugin {
'@sentry/browser': 'Sentry',
'@sentry/tracing': 'Sentry',
'@sentry/replay': 'Sentry',
'@sentry/integrations': 'Sentry.Integrations',
'@sentry/wasm': 'Sentry.Integrations',
'@sentry/integrations': 'Sentry',
'@sentry/wasm': 'Sentry',
}
: {};

Expand All @@ -161,8 +182,11 @@ class SentryScenarioGenerationPlugin {
parser.hooks.import.tap(
this._name,
(statement: { specifiers: [{ imported: { name: string } }] }, source: string) => {
if (source === '@sentry/integrations') {
this.requiredIntegrations.push(statement.specifiers[0].imported.name.toLowerCase());
const imported = statement.specifiers?.[0]?.imported?.name;

if (imported && IMPORTED_INTEGRATION_CDN_BUNDLE_PATHS[imported]) {
const bundleName = IMPORTED_INTEGRATION_CDN_BUNDLE_PATHS[imported];
this.requiredIntegrations.push(bundleName);
} else if (source === '@sentry/wasm') {
this.requiresWASMIntegration = true;
}
Expand Down
1 change: 1 addition & 0 deletions dev-packages/rollup-utils/bundleHelpers.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ export function makeBaseBundleConfig(options) {
' for (var key in exports) {',
' if (Object.prototype.hasOwnProperty.call(exports, key)) {',
' __window.Sentry.Integrations[key] = exports[key];',
' __window.Sentry[key] = exports[key];',
' }',
' }',
].join('\n'),
Expand Down

0 comments on commit 9317f85

Please sign in to comment.