Skip to content

fix(nextjs): Add default option to OnUncaughtException integration #6142

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

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
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
11 changes: 5 additions & 6 deletions packages/nextjs/src/index.server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import * as path from 'path';
import { isBuild } from './utils/isBuild';
import { buildMetadata } from './utils/metadata';
import { NextjsOptions } from './utils/nextjsOptions';
import { addOrUpdateIntegration } from './utils/userIntegrations';
import { addOrUpdateIntegration, updateIntegration } from './utils/userIntegrations';

export * from '@sentry/node';
export { captureUnderscoreErrorException } from './utils/_error';
Expand Down Expand Up @@ -118,18 +118,17 @@ function addServerIntegrations(options: NextjsOptions): void {
});
integrations = addOrUpdateIntegration(defaultRewriteFramesIntegration, integrations);

const nativeBehaviourOnUncaughtException = new Integrations.OnUncaughtException();
integrations = addOrUpdateIntegration(nativeBehaviourOnUncaughtException, integrations, {
_options: { exitEvenIfOtherHandlersAreRegistered: false },
});

if (hasTracingEnabled(options)) {
const defaultHttpTracingIntegration = new Integrations.Http({ tracing: true });
integrations = addOrUpdateIntegration(defaultHttpTracingIntegration, integrations, {
_tracing: true,
});
}

updateIntegration('OnUncaughtException', integrations, {
_options: { exitEvenIfOtherHandlersAreRegistered: false },
});

options.integrations = integrations;
}

Expand Down
47 changes: 47 additions & 0 deletions packages/nextjs/src/utils/userIntegrations.ts
Original file line number Diff line number Diff line change
Expand Up @@ -82,3 +82,50 @@ function addOrUpdateIntegrationInFunction(
};
return wrapper;
}

/**
* Updates the internal fields of a given integration in an integration array or integration function.
*
* @param defaultIntegrationInstance The `name` field of the integration to patch.
* @param userIntegrations Integrations defined by the user.
* @param forcedOptions Options with which to patch an existing integration instance.
* @returns A final integrations array.
*/
export function updateIntegration(
integrationName: string,
userIntegrations: UserIntegrations,
forcedOptions: ForcedIntegrationOptions,
): UserIntegrations {
return Array.isArray(userIntegrations)
? updateIntegrationInArray(integrationName, userIntegrations, forcedOptions)
: updateIntegrationInFunction(integrationName, userIntegrations, forcedOptions);
}

function updateIntegrationInFunction(
integrationName: string,
userIntegrationsFunc: UserIntegrationsFunction,
forcedOptions: ForcedIntegrationOptions,
): UserIntegrationsFunction {
const wrapper: UserIntegrationsFunction = defaultIntegrations => {
const userFinalIntegrations = userIntegrationsFunc(defaultIntegrations);
updateIntegrationInArray(integrationName, userFinalIntegrations, forcedOptions);
return userFinalIntegrations;
};
return wrapper;
}

function updateIntegrationInArray(
integrationName: string,
userIntegrations: Integration[],
forcedOptions: ForcedIntegrationOptions,
): Integration[] {
const userInstance = userIntegrations.find(integration => integration.name === integrationName);

if (userInstance) {
for (const [keyPath, value] of Object.entries(forcedOptions)) {
setNestedKey(userInstance, keyPath, value);
}
}

return userIntegrations;
}
125 changes: 90 additions & 35 deletions packages/nextjs/test/utils/userIntegrations.test.ts
Original file line number Diff line number Diff line change
@@ -1,50 +1,105 @@
import { RewriteFrames } from '@sentry/integrations';
import { Integration } from '@sentry/types';

import { addOrUpdateIntegration, UserIntegrationsFunction } from '../../src/utils/userIntegrations';
import { addOrUpdateIntegration, updateIntegration, UserIntegrationsFunction } from '../../src/utils/userIntegrations';

const testIntegration = new RewriteFrames();

describe('user integrations without any integrations', () => {
test('as an array', () => {
const userIntegrations: Integration[] = [];
// Should get a single integration
let finalIntegrations = addOrUpdateIntegration(testIntegration, userIntegrations);
expect(finalIntegrations).toBeInstanceOf(Array);
finalIntegrations = finalIntegrations as Integration[];
expect(finalIntegrations).toHaveLength(1);
expect(finalIntegrations[0]).toMatchObject(testIntegration);
describe('addOrUpdateIntegration()', () => {
describe('user integrations without any integrations', () => {
test('as an array', () => {
const userIntegrations: Integration[] = [];
// Should get a single integration
let finalIntegrations = addOrUpdateIntegration(testIntegration, userIntegrations);
expect(finalIntegrations).toBeInstanceOf(Array);
finalIntegrations = finalIntegrations as Integration[];
expect(finalIntegrations).toHaveLength(1);
expect(finalIntegrations[0]).toMatchObject(testIntegration);
});

test('as a function', () => {
const userIntegrationFnc: UserIntegrationsFunction = (): Integration[] => [];
// Should get a single integration
const integrationWrapper = addOrUpdateIntegration(testIntegration, userIntegrationFnc);
expect(integrationWrapper).toBeInstanceOf(Function);
const finalIntegrations = (integrationWrapper as UserIntegrationsFunction)([]);
expect(finalIntegrations).toHaveLength(1);
expect(finalIntegrations[0]).toMatchObject(testIntegration);
});
});

test('as a function', () => {
const userIntegrationFnc: UserIntegrationsFunction = (): Integration[] => [];
// Should get a single integration
const integrationWrapper = addOrUpdateIntegration(testIntegration, userIntegrationFnc);
expect(integrationWrapper).toBeInstanceOf(Function);
const finalIntegrations = (integrationWrapper as UserIntegrationsFunction)([]);
expect(finalIntegrations).toHaveLength(1);
expect(finalIntegrations[0]).toMatchObject(testIntegration);
describe('user integrations with integrations', () => {
test('as an array', () => {
const userIntegrations = [new RewriteFrames()];
// Should get the same array (with no patches)
const finalIntegrations = addOrUpdateIntegration(testIntegration, userIntegrations);
expect(finalIntegrations).toMatchObject(userIntegrations);
});

test('as a function', () => {
const userIntegrations = [new RewriteFrames()];
const integrationsFnc: UserIntegrationsFunction = (_integrations: Integration[]): Integration[] => {
return userIntegrations;
};
// Should get a function that returns the test integration
let finalIntegrations = addOrUpdateIntegration(testIntegration, integrationsFnc);
expect(typeof finalIntegrations === 'function').toBe(true);
expect(finalIntegrations).toBeInstanceOf(Function);
finalIntegrations = finalIntegrations as UserIntegrationsFunction;
expect(finalIntegrations([])).toMatchObject(userIntegrations);
});
});
});

describe('user integrations with integrations', () => {
test('as an array', () => {
const userIntegrations = [new RewriteFrames()];
// Should get the same array (with no patches)
const finalIntegrations = addOrUpdateIntegration(testIntegration, userIntegrations);
expect(finalIntegrations).toMatchObject(userIntegrations);
describe('updateIntegration()', () => {
describe('integrations function', () => {
test('should update an integration if it exists', () => {
const intagration = new RewriteFrames() as any;
const userIntegrations = [intagration];
const integrationsFnc: UserIntegrationsFunction = (_integrations: Integration[]): Integration[] => {
return userIntegrations;
};
// Should get a function that returns the test integration
let finalIntegrations = updateIntegration('RewriteFrames', integrationsFnc, { _option: true });
expect(typeof finalIntegrations === 'function').toBe(true);
finalIntegrations = finalIntegrations as UserIntegrationsFunction;
const evaluatedIntegrations = finalIntegrations([]);
expect(evaluatedIntegrations).toMatchObject(userIntegrations);
expect(intagration._option).toBe(true);
});

test("should not update an integration if it doesn't exist", () => {
const intagration = new RewriteFrames() as any;
const userIntegrations = [intagration];
const integrationsFnc: UserIntegrationsFunction = (_integrations: Integration[]): Integration[] => {
return userIntegrations;
};
let finalIntegrations = updateIntegration('UncaughtException', integrationsFnc, { _option: true });
expect(typeof finalIntegrations === 'function').toBe(true);
finalIntegrations = finalIntegrations as UserIntegrationsFunction;
const evaluatedIntegrations = finalIntegrations([]);
expect(evaluatedIntegrations).toMatchObject(userIntegrations);
expect(intagration._option).not.toBeDefined();
});
});

test('as a function', () => {
const userIntegrations = [new RewriteFrames()];
const integrationsFnc: UserIntegrationsFunction = (_integrations: Integration[]): Integration[] => {
return userIntegrations;
};
// Should get a function that returns the test integration
let finalIntegrations = addOrUpdateIntegration(testIntegration, integrationsFnc);
expect(typeof finalIntegrations === 'function').toBe(true);
expect(finalIntegrations).toBeInstanceOf(Function);
finalIntegrations = finalIntegrations as UserIntegrationsFunction;
expect(finalIntegrations([])).toMatchObject(userIntegrations);
describe('integrations array', () => {
test('should update an integration if it exists', () => {
const intagration = new RewriteFrames() as any;
const userIntegrations = [intagration];
const finalIntegrations = updateIntegration('RewriteFrames', userIntegrations, { _option: true });
expect(finalIntegrations).toBeInstanceOf(Array);
expect(finalIntegrations).toMatchObject(userIntegrations);
expect(intagration._option).toBe(true);
});

test("should not update an integration if it doesn't exist", () => {
const intagration = new RewriteFrames() as any;
const userIntegrations = [intagration];
const finalIntegrations = updateIntegration('UncaughtException', userIntegrations, { _option: true });
expect(finalIntegrations).toBeInstanceOf(Array);
expect(finalIntegrations).toMatchObject(userIntegrations);
expect(intagration._option).not.toBeDefined();
});
});
});