Skip to content

Commit

Permalink
fix(nextjs): Only update OnUncaughtException integration when it's …
Browse files Browse the repository at this point in the history
…already configured
  • Loading branch information
lforst committed Nov 7, 2022
1 parent eb04258 commit 5b901f2
Show file tree
Hide file tree
Showing 3 changed files with 142 additions and 41 deletions.
11 changes: 5 additions & 6 deletions packages/nextjs/src/index.server.ts
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
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
@@ -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();
});
});
});

0 comments on commit 5b901f2

Please sign in to comment.