Skip to content
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

fix: Make redux integration be configurable via normalizeDepth #7379

Merged
merged 2 commits into from
Mar 8, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
18 changes: 16 additions & 2 deletions packages/react/src/redux.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
import { configureScope } from '@sentry/browser';
import { configureScope, getCurrentHub } from '@sentry/browser';
import type { Scope } from '@sentry/types';
import { addNonEnumerableProperty } from '@sentry/utils';

interface Action<T = any> {
type: T;
Expand Down Expand Up @@ -105,7 +106,20 @@ function createReduxEnhancer(enhancerOptions?: Partial<SentryEnhancerOptions>):
/* Set latest state to scope */
const transformedState = options.stateTransformer(newState);
if (typeof transformedState !== 'undefined' && transformedState !== null) {
scope.setContext('state', { state: { type: 'redux', value: transformedState } });
const client = getCurrentHub().getClient();
const options = client && client.getOptions();
const normalizationDepth = (options && options.normalizeDepth) || 3; // default state normalization depth to 3

// Set the normalization depth of the redux state to the configured `normalizeDepth` option or a sane number as a fallback
const newStateContext = { state: { type: 'redux', value: transformedState } };
addNonEnumerableProperty(
newStateContext,
'__sentry_override_normalization_depth__',
3 + // 3 layers for `state.value.transformedState`
normalizationDepth, // rest for the actual state
);

scope.setContext('state', newStateContext);
} else {
scope.setContext('state', null);
}
Expand Down
15 changes: 12 additions & 3 deletions packages/utils/src/normalize.ts
Original file line number Diff line number Diff line change
Expand Up @@ -100,8 +100,17 @@ function visit(
return value as ObjOrArray<unknown>;
}

// Do not normalize objects that we know have already been normalized. As a general rule, the
// "__sentry_skip_normalization__" property should only be used sparingly and only should only be set on objects that
// have already been normalized.
let overriddenDepth = depth;

if (typeof (value as ObjOrArray<unknown>)['__sentry_override_normalization_depth__'] === 'number') {
overriddenDepth = (value as ObjOrArray<unknown>)['__sentry_override_normalization_depth__'] as number;
}

// We're also done if we've reached the max depth
if (depth === 0) {
if (overriddenDepth === 0) {
// At this point we know `serialized` is a string of the form `"[object XXXX]"`. Clean it up so it's just `"[XXXX]"`.
return stringified.replace('object ', '');
}
Expand All @@ -117,7 +126,7 @@ function visit(
try {
const jsonValue = valueWithToJSON.toJSON();
// We need to normalize the return value of `.toJSON()` in case it has circular references
return visit('', jsonValue, depth - 1, maxProperties, memo);
return visit('', jsonValue, overriddenDepth - 1, maxProperties, memo);
} catch (err) {
// pass (The built-in `toJSON` failed, but we can still try to do it ourselves)
}
Expand Down Expand Up @@ -146,7 +155,7 @@ function visit(

// Recursively visit all the child nodes
const visitValue = visitable[visitKey];
normalized[visitKey] = visit(visitKey, visitValue, depth - 1, maxProperties, memo);
normalized[visitKey] = visit(visitKey, visitValue, overriddenDepth - 1, maxProperties, memo);

numAdded++;
}
Expand Down
54 changes: 54 additions & 0 deletions packages/utils/test/normalize.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -582,4 +582,58 @@ describe('normalize()', () => {
expect(result?.foo?.bar?.boo?.bam?.pow).not.toBe('poof');
});
});

describe('overrides normalization depth with a non-enumerable property __sentry_override_normalization_depth__', () => {
test('by increasing depth if it is higher', () => {
const normalizationTarget = {
foo: 'bar',
baz: 42,
obj: {
obj: {
obj: {
bestSmashCharacter: 'Cpt. Falcon',
},
},
},
};

addNonEnumerableProperty(normalizationTarget, '__sentry_override_normalization_depth__', 3);

const result = normalize(normalizationTarget, 1);

expect(result).toEqual({
baz: 42,
foo: 'bar',
obj: {
obj: {
obj: '[Object]',
},
},
});
});

test('by decreasing depth if it is lower', () => {
const normalizationTarget = {
foo: 'bar',
baz: 42,
obj: {
obj: {
obj: {
bestSmashCharacter: 'Cpt. Falcon',
},
},
},
};

addNonEnumerableProperty(normalizationTarget, '__sentry_override_normalization_depth__', 1);

const result = normalize(normalizationTarget, 3);

expect(result).toEqual({
baz: 42,
foo: 'bar',
obj: '[Object]',
});
});
});
});