Skip to content

Commit

Permalink
feat(gatsby): Support user integrations as a function (#4050)
Browse files Browse the repository at this point in the history
The goal is to make the Gatsby SDK support non-serializable options, which means supporting the definition of integrations as a function.
  • Loading branch information
iker-barriocanal committed Oct 11, 2021
1 parent 1d8c500 commit 223d330
Show file tree
Hide file tree
Showing 3 changed files with 100 additions and 16 deletions.
1 change: 1 addition & 0 deletions packages/gatsby/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
export * from '@sentry/react';
export { Integrations } from '@sentry/tracing';

export { init } from './sdk';
47 changes: 41 additions & 6 deletions packages/gatsby/src/utils/integrations.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,54 @@ import { Integration } from '@sentry/types';

import { GatsbyOptions } from './types';

type UserFnIntegrations = (integrations: Integration[]) => Integration[];
export type UserIntegrations = Integration[] | UserFnIntegrations;

/**
* Returns the integrations to add to the SDK.
* If tracing is enabled, `BrowserTracing` is always present.
*
* @param options The options users have defined.
*/
export function getIntegrationsFromOptions(options: GatsbyOptions): Integration[] {
const integrations = [...(options.integrations || [])];
export function getIntegrationsFromOptions(options: GatsbyOptions): UserIntegrations {
const isTracingEnabled = Tracing.hasTracingEnabled(options);
if (options.integrations === undefined) {
return getIntegrationsFromArray([], isTracingEnabled);
} else if (Array.isArray(options.integrations)) {
return getIntegrationsFromArray(options.integrations, isTracingEnabled);
} else {
return getIntegrationsFromFunction(options.integrations, isTracingEnabled);
}
}

/**
* Returns the integrations to add to the SDK, from the given integrations array.
*
* @param userIntegrations Array of user's integrations.
* @param isTracingEnabled Whether the user has enabled tracing.
*/
function getIntegrationsFromArray(userIntegrations: Integration[], isTracingEnabled: boolean): Integration[] {
if (
Tracing.hasTracingEnabled(options) &&
!integrations.some(integration => integration.name === Tracing.Integrations.BrowserTracing.name)
isTracingEnabled &&
!userIntegrations.some(integration => integration.name === Tracing.Integrations.BrowserTracing.name)
) {
integrations.push(new Tracing.Integrations.BrowserTracing());
userIntegrations.push(new Tracing.Integrations.BrowserTracing());
}
return integrations;
return userIntegrations;
}

/**
* Returns the integrations to add to the SDK, from the given function.
*
* @param userIntegrations Function returning the user's integrations.
* @param isTracingEnabled Whether the user has enabled tracing.
*/
function getIntegrationsFromFunction(
userIntegrations: UserFnIntegrations,
isTracingEnabled: boolean,
): UserFnIntegrations {
const wrapper: UserFnIntegrations = (defaultIntegrations: Integration[]) => {
return getIntegrationsFromArray(userIntegrations(defaultIntegrations), isTracingEnabled);
};
return wrapper;
}
68 changes: 58 additions & 10 deletions packages/gatsby/test/sdk.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { Integrations } from '@sentry/tracing';
import { Integration, Package } from '@sentry/types';

import { defaultOptions, init as gatsbyInit } from '../src/sdk';
import { UserIntegrations } from '../src/utils/integrations';
import { GatsbyOptions } from '../src/utils/types';

const reactInit = reactInitRaw as jest.Mock;
Expand Down Expand Up @@ -65,28 +66,75 @@ describe('Integrations from options', () => {
afterEach(() => reactInit.mockClear());

test.each([
['tracing disabled, no integrations', {}, []],
['tracing enabled, no integrations', { tracesSampleRate: 1 }, ['BrowserTracing']],
['tracing disabled, no integrations', [], {}, []],
['tracing enabled, no integrations', [], { tracesSampleRate: 1 }, ['BrowserTracing']],
[
'tracing disabled, with Integrations.BrowserTracing',
'tracing disabled, with Integrations.BrowserTracing as an array',
[],
{ integrations: [new Integrations.BrowserTracing()] },
['BrowserTracing'],
],
[
'tracing enabled, with Integrations.BrowserTracing',
'tracing disabled, with Integrations.BrowserTracing as a function',
[],
{
integrations: () => [new Integrations.BrowserTracing()],
},
['BrowserTracing'],
],
[
'tracing enabled, with Integrations.BrowserTracing as an array',
[],
{ tracesSampleRate: 1, integrations: [new Integrations.BrowserTracing()] },
['BrowserTracing'],
],
[
'tracing enabled, with another integration',
'tracing enabled, with Integrations.BrowserTracing as a function',
[],
{ tracesSampleRate: 1, integrations: () => [new Integrations.BrowserTracing()] },
['BrowserTracing'],
],
[
'tracing enabled, with another integration as an array',
[],
{ tracesSampleRate: 1, integrations: [new Integrations.Express()] },
['Express', 'BrowserTracing'],
],
['tracing disabled, with another integration', { integrations: [new Integrations.Express()] }, ['Express']],
])('%s', (_testName, options: GatsbyOptions, expectedIntNames: string[]) => {
[
'tracing enabled, with another integration as a function',
[],
{ tracesSampleRate: 1, integrations: () => [new Integrations.Express()] },
['Express', 'BrowserTracing'],
],
[
'tracing disabled, with another integration as an array',
[],
{ integrations: [new Integrations.Express()] },
['Express'],
],
[
'tracing disabled, with another integration as a function',
[],
{ integrations: () => [new Integrations.Express()] },
['Express'],
],
[
'merges integrations with user integrations as a function',
[new Integrations.Mongo()],
{
tracesSampleRate: 1,
integrations: (defaultIntegrations: Integration[]): Integration[] => [
...defaultIntegrations,
new Integrations.Express(),
],
},
['Mongo', 'Express', 'BrowserTracing'],
],
])('%s', (_testName, defaultIntegrations: Integration[], options: GatsbyOptions, expectedIntNames: string[]) => {
gatsbyInit(options);
const integrations: Integration[] = reactInit.mock.calls[0][0].integrations;
expect(integrations).toHaveLength(expectedIntNames.length);
integrations.map((integration, idx) => expect(integration.name).toStrictEqual(expectedIntNames[idx]));
const integrations: UserIntegrations = reactInit.mock.calls[0][0].integrations;
const arrIntegrations = Array.isArray(integrations) ? integrations : integrations(defaultIntegrations);
expect(arrIntegrations).toHaveLength(expectedIntNames.length);
arrIntegrations.map((integration, idx) => expect(integration.name).toStrictEqual(expectedIntNames[idx]));
});
});

0 comments on commit 223d330

Please sign in to comment.