Skip to content

Commit cff6f9a

Browse files
ref(gatsby): Move SDK initialization logic to SDK (#4040)
This PR refactors how the Gatsby plugin initializes the SDK. The initialization was done in the `gatsby-browser` script, and the Gatsby SDK was initializing the React SDK. This approach forces to use that script to initialize it, without providing any alternative.
1 parent 5d6f996 commit cff6f9a

File tree

10 files changed

+237
-78
lines changed

10 files changed

+237
-78
lines changed

packages/gatsby/gatsby-browser.js

+1-25
Original file line numberDiff line numberDiff line change
@@ -1,40 +1,16 @@
1-
const Sentry = require('@sentry/react');
2-
const Tracing = require('@sentry/tracing');
1+
const Sentry = require('@sentry/gatsby');
32

43
exports.onClientEntry = function(_, pluginParams) {
54
if (pluginParams === undefined) {
65
return;
76
}
87

9-
pluginParams._metadata = pluginParams._metadata || {};
10-
pluginParams._metadata.sdk = {
11-
name: 'sentry.javascript.gatsby',
12-
packages: [
13-
{
14-
name: 'npm:@sentry/gatsby',
15-
version: Sentry.SDK_VERSION,
16-
},
17-
],
18-
version: Sentry.SDK_VERSION,
19-
};
20-
21-
const integrations = [...(pluginParams.integrations || [])];
22-
23-
if (Tracing.hasTracingEnabled(pluginParams) && !integrations.some(ele => ele.name === 'BrowserTracing')) {
24-
integrations.push(new Tracing.Integrations.BrowserTracing(pluginParams.browserTracingOptions));
25-
}
26-
27-
Tracing.addExtensionMethods();
28-
298
Sentry.init({
30-
autoSessionTracking: true,
31-
environment: process.env.NODE_ENV || 'development',
329
// eslint-disable-next-line no-undef
3310
release: __SENTRY_RELEASE__,
3411
// eslint-disable-next-line no-undef
3512
dsn: __SENTRY_DSN__,
3613
...pluginParams,
37-
integrations,
3814
});
3915

4016
window.Sentry = Sentry;

packages/gatsby/package.json

+3-1
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,10 @@
3333
"gatsby": "^2.0.0 || ^3.0.0"
3434
},
3535
"devDependencies": {
36-
"@sentry-internal/eslint-config-sdk": "6.13.3",
36+
"@sentry-internal/eslint-config-sdk": "6.13.2",
37+
"@sentry/types": "6.13.2",
3738
"@testing-library/react": "^10.4.9",
39+
"@types/node": "^16.10.3",
3840
"jest": "^24.7.1",
3941
"npm-run-all": "^4.1.2",
4042
"prettier": "1.19.0",

packages/gatsby/src/index.ts

+2
Original file line numberDiff line numberDiff line change
@@ -1 +1,3 @@
11
export * from '@sentry/react';
2+
3+
export { init } from './sdk';

packages/gatsby/src/sdk.ts

+33
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
import { init as reactInit, SDK_VERSION } from '@sentry/react';
2+
3+
import { getIntegrationsFromOptions } from './utils/integrations';
4+
import { GatsbyOptions } from './utils/types';
5+
6+
export const defaultOptions = {
7+
autoSessionTracking: true,
8+
environment: process.env.NODE_ENV || 'development',
9+
};
10+
11+
/**
12+
* Inits the Sentry Gatsby SDK.
13+
*/
14+
export function init(options: GatsbyOptions): void {
15+
options._metadata = options._metadata || {};
16+
options._metadata.sdk = options._metadata.sdk || {
17+
name: 'sentry.javascript.gatsby',
18+
packages: [
19+
{
20+
name: 'npm:@sentry/gatsby',
21+
version: SDK_VERSION,
22+
},
23+
],
24+
version: SDK_VERSION,
25+
};
26+
27+
const integrations = getIntegrationsFromOptions(options);
28+
reactInit({
29+
...defaultOptions,
30+
...options,
31+
integrations,
32+
});
33+
}
+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import * as Tracing from '@sentry/tracing';
2+
import { Integration } from '@sentry/types';
3+
4+
import { GatsbyOptions } from './types';
5+
6+
/**
7+
* Returns the integrations to add to the SDK.
8+
* If tracing is enabled, `BrowserTracing` is always present.
9+
*
10+
* @param options The options users have defined.
11+
*/
12+
export function getIntegrationsFromOptions(options: GatsbyOptions): Integration[] {
13+
const integrations = [...(options.integrations || [])];
14+
if (
15+
Tracing.hasTracingEnabled(options) &&
16+
!integrations.some(integration => integration.name === Tracing.Integrations.BrowserTracing.name)
17+
) {
18+
integrations.push(new Tracing.Integrations.BrowserTracing());
19+
}
20+
return integrations;
21+
}

packages/gatsby/src/utils/types.ts

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
import { BrowserOptions } from '@sentry/react';
2+
3+
export type GatsbyOptions = BrowserOptions;

packages/gatsby/test/gatsby-browser.test.ts

+11-49
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,8 @@ const { onClientEntry } = require('../gatsby-browser');
77
(global as any).__SENTRY_DSN__ = 'https://[email protected]/0';
88

99
let sentryInit = jest.fn();
10-
jest.mock('@sentry/react', () => {
11-
const original = jest.requireActual('@sentry/react');
10+
jest.mock('@sentry/gatsby', () => {
11+
const original = jest.requireActual('@sentry/gatsby');
1212
return {
1313
...original,
1414
init: (...args: any[]) => {
@@ -38,42 +38,23 @@ describe('onClientEntry', () => {
3838
(window as any).Sentry = undefined;
3939
});
4040

41-
it('inits Sentry by default', () => {
42-
onClientEntry(undefined, {});
41+
it.each([
42+
[{}, ['dsn', 'release']],
43+
[{ key: 'value' }, ['dsn', 'release', 'key']],
44+
])('inits Sentry by default', (pluginParams, expectedKeys) => {
45+
onClientEntry(undefined, pluginParams);
4346
expect(sentryInit).toHaveBeenCalledTimes(1);
44-
expect(sentryInit).toHaveBeenLastCalledWith({
45-
dsn: (global as any).__SENTRY_DSN__,
46-
environment: process.env.NODE_ENV,
47-
integrations: [],
48-
release: (global as any).__SENTRY_RELEASE__,
49-
autoSessionTracking: true,
50-
_metadata: {
51-
sdk: {
52-
name: 'sentry.javascript.gatsby',
53-
packages: [
54-
{
55-
name: 'npm:@sentry/gatsby',
56-
version: expect.any(String),
57-
},
58-
],
59-
version: expect.any(String),
60-
},
61-
},
62-
});
47+
const calledWith = sentryInit.mock.calls[0][0];
48+
for (const key of expectedKeys) {
49+
expect(calledWith[key]).toBeDefined();
50+
}
6351
});
6452

6553
it('sets window.Sentry', () => {
6654
onClientEntry(undefined, {});
6755
expect((window as any).Sentry).not.toBeUndefined();
6856
});
6957

70-
it('adds Tracing extension methods', () => {
71-
onClientEntry(undefined, {});
72-
73-
expect(tracingAddExtensionMethods).toHaveBeenCalledTimes(1);
74-
expect(tracingAddExtensionMethods).toHaveBeenLastCalledWith();
75-
});
76-
7758
it('sets a tracesSampleRate if defined as option', () => {
7859
onClientEntry(undefined, { tracesSampleRate: 0.5 });
7960
expect(sentryInit).toHaveBeenLastCalledWith(
@@ -93,25 +74,6 @@ describe('onClientEntry', () => {
9374
);
9475
});
9576

96-
it('adds `BrowserTracing` integration if tracesSampleRate is defined', () => {
97-
onClientEntry(undefined, { tracesSampleRate: 0.5 });
98-
expect(sentryInit).toHaveBeenLastCalledWith(
99-
expect.objectContaining({
100-
integrations: [expect.objectContaining({ name: 'BrowserTracing' })],
101-
}),
102-
);
103-
});
104-
105-
it('adds `BrowserTracing` integration if tracesSampler is defined', () => {
106-
const tracesSampler = jest.fn();
107-
onClientEntry(undefined, { tracesSampler });
108-
expect(sentryInit).toHaveBeenLastCalledWith(
109-
expect.objectContaining({
110-
integrations: [expect.objectContaining({ name: 'BrowserTracing' })],
111-
}),
112-
);
113-
});
114-
11577
it('only defines a single `BrowserTracing` integration', () => {
11678
const Tracing = jest.requireActual('@sentry/tracing');
11779
const integrations = [new Tracing.Integrations.BrowserTracing()];

packages/gatsby/test/index.test.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import * as GatsbyIntegration from '../src';
1+
import * as GatsbyIntegration from '../src/index';
22

33
describe('package', () => {
44
it('exports init', () => {

packages/gatsby/test/sdk.test.ts

+92
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
import { init as reactInitRaw, SDK_VERSION } from '@sentry/react';
2+
import { Integrations } from '@sentry/tracing';
3+
import { Integration, Package } from '@sentry/types';
4+
5+
import { defaultOptions, init as gatsbyInit } from '../src/sdk';
6+
import { GatsbyOptions } from '../src/utils/types';
7+
8+
const reactInit = reactInitRaw as jest.Mock;
9+
jest.mock('@sentry/react');
10+
11+
describe('Initialize React SDK', () => {
12+
afterEach(() => reactInit.mockReset());
13+
14+
test('Default init props', () => {
15+
gatsbyInit({});
16+
expect(reactInit).toHaveBeenCalledTimes(1);
17+
const calledWith = reactInit.mock.calls[0][0];
18+
expect(calledWith).toMatchObject(defaultOptions);
19+
});
20+
21+
test('Has correct SDK metadata', () => {
22+
gatsbyInit({});
23+
const calledWith = reactInit.mock.calls[0][0];
24+
const sdkMetadata = calledWith._metadata.sdk;
25+
expect(sdkMetadata.name).toStrictEqual('sentry.javascript.gatsby');
26+
expect(sdkMetadata.version).toBe(SDK_VERSION);
27+
expect(sdkMetadata.packages).toMatchInlineSnapshot(`
28+
Array [
29+
Object {
30+
"name": "npm:@sentry/gatsby",
31+
"version": "6.13.3",
32+
},
33+
]
34+
`);
35+
});
36+
37+
describe('Environment', () => {
38+
test('process.env', () => {
39+
gatsbyInit({});
40+
expect(reactInit).toHaveBeenCalledTimes(1);
41+
const callingObject = reactInit.mock.calls[0][0];
42+
expect(callingObject.environment).toStrictEqual('test');
43+
});
44+
45+
test('defined in the options', () => {
46+
gatsbyInit({
47+
environment: 'custom env!',
48+
});
49+
expect(reactInit).toHaveBeenCalledTimes(1);
50+
const callingObject = reactInit.mock.calls[0][0];
51+
expect(callingObject.environment).toStrictEqual('custom env!');
52+
});
53+
});
54+
55+
test('Has BrowserTracing if tracing enabled', () => {
56+
gatsbyInit({ tracesSampleRate: 1 });
57+
expect(reactInit).toHaveBeenCalledTimes(1);
58+
const calledWith = reactInit.mock.calls[0][0];
59+
const integrationNames: string[] = calledWith.integrations.map((integration: Integration) => integration.name);
60+
expect(integrationNames.some(name => name === 'BrowserTracing')).toBe(true);
61+
});
62+
});
63+
64+
describe('Integrations from options', () => {
65+
afterEach(() => reactInit.mockClear());
66+
67+
test.each([
68+
['tracing disabled, no integrations', {}, []],
69+
['tracing enabled, no integrations', { tracesSampleRate: 1 }, ['BrowserTracing']],
70+
[
71+
'tracing disabled, with Integrations.BrowserTracing',
72+
{ integrations: [new Integrations.BrowserTracing()] },
73+
['BrowserTracing'],
74+
],
75+
[
76+
'tracing enabled, with Integrations.BrowserTracing',
77+
{ tracesSampleRate: 1, integrations: [new Integrations.BrowserTracing()] },
78+
['BrowserTracing'],
79+
],
80+
[
81+
'tracing enabled, with another integration',
82+
{ tracesSampleRate: 1, integrations: [new Integrations.Express()] },
83+
['Express', 'BrowserTracing'],
84+
],
85+
['tracing disabled, with another integration', { integrations: [new Integrations.Express()] }, ['Express']],
86+
])('%s', (_testName, options: GatsbyOptions, expectedIntNames: string[]) => {
87+
gatsbyInit(options);
88+
const integrations: Integration[] = reactInit.mock.calls[0][0].integrations;
89+
expect(integrations).toHaveLength(expectedIntNames.length);
90+
integrations.map((integration, idx) => expect(integration.name).toStrictEqual(expectedIntNames[idx]));
91+
});
92+
});

0 commit comments

Comments
 (0)