Skip to content

Commit 2b44452

Browse files
authored
fix: Make redux integration be configurable via normalizeDepth (#7379)
2 parents 17f31c6 + ec78a02 commit 2b44452

File tree

3 files changed

+82
-5
lines changed

3 files changed

+82
-5
lines changed

packages/react/src/redux.ts

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
/* eslint-disable @typescript-eslint/no-explicit-any */
2-
import { configureScope } from '@sentry/browser';
2+
import { configureScope, getCurrentHub } from '@sentry/browser';
33
import type { Scope } from '@sentry/types';
4+
import { addNonEnumerableProperty } from '@sentry/utils';
45

56
interface Action<T = any> {
67
type: T;
@@ -105,7 +106,20 @@ function createReduxEnhancer(enhancerOptions?: Partial<SentryEnhancerOptions>):
105106
/* Set latest state to scope */
106107
const transformedState = options.stateTransformer(newState);
107108
if (typeof transformedState !== 'undefined' && transformedState !== null) {
108-
scope.setContext('state', { state: { type: 'redux', value: transformedState } });
109+
const client = getCurrentHub().getClient();
110+
const options = client && client.getOptions();
111+
const normalizationDepth = (options && options.normalizeDepth) || 3; // default state normalization depth to 3
112+
113+
// Set the normalization depth of the redux state to the configured `normalizeDepth` option or a sane number as a fallback
114+
const newStateContext = { state: { type: 'redux', value: transformedState } };
115+
addNonEnumerableProperty(
116+
newStateContext,
117+
'__sentry_override_normalization_depth__',
118+
3 + // 3 layers for `state.value.transformedState`
119+
normalizationDepth, // rest for the actual state
120+
);
121+
122+
scope.setContext('state', newStateContext);
109123
} else {
110124
scope.setContext('state', null);
111125
}

packages/utils/src/normalize.ts

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -100,8 +100,17 @@ function visit(
100100
return value as ObjOrArray<unknown>;
101101
}
102102

103+
// Do not normalize objects that we know have already been normalized. As a general rule, the
104+
// "__sentry_skip_normalization__" property should only be used sparingly and only should only be set on objects that
105+
// have already been normalized.
106+
let overriddenDepth = depth;
107+
108+
if (typeof (value as ObjOrArray<unknown>)['__sentry_override_normalization_depth__'] === 'number') {
109+
overriddenDepth = (value as ObjOrArray<unknown>)['__sentry_override_normalization_depth__'] as number;
110+
}
111+
103112
// We're also done if we've reached the max depth
104-
if (depth === 0) {
113+
if (overriddenDepth === 0) {
105114
// At this point we know `serialized` is a string of the form `"[object XXXX]"`. Clean it up so it's just `"[XXXX]"`.
106115
return stringified.replace('object ', '');
107116
}
@@ -117,7 +126,7 @@ function visit(
117126
try {
118127
const jsonValue = valueWithToJSON.toJSON();
119128
// We need to normalize the return value of `.toJSON()` in case it has circular references
120-
return visit('', jsonValue, depth - 1, maxProperties, memo);
129+
return visit('', jsonValue, overriddenDepth - 1, maxProperties, memo);
121130
} catch (err) {
122131
// pass (The built-in `toJSON` failed, but we can still try to do it ourselves)
123132
}
@@ -146,7 +155,7 @@ function visit(
146155

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

151160
numAdded++;
152161
}

packages/utils/test/normalize.test.ts

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -582,4 +582,58 @@ describe('normalize()', () => {
582582
expect(result?.foo?.bar?.boo?.bam?.pow).not.toBe('poof');
583583
});
584584
});
585+
586+
describe('overrides normalization depth with a non-enumerable property __sentry_override_normalization_depth__', () => {
587+
test('by increasing depth if it is higher', () => {
588+
const normalizationTarget = {
589+
foo: 'bar',
590+
baz: 42,
591+
obj: {
592+
obj: {
593+
obj: {
594+
bestSmashCharacter: 'Cpt. Falcon',
595+
},
596+
},
597+
},
598+
};
599+
600+
addNonEnumerableProperty(normalizationTarget, '__sentry_override_normalization_depth__', 3);
601+
602+
const result = normalize(normalizationTarget, 1);
603+
604+
expect(result).toEqual({
605+
baz: 42,
606+
foo: 'bar',
607+
obj: {
608+
obj: {
609+
obj: '[Object]',
610+
},
611+
},
612+
});
613+
});
614+
615+
test('by decreasing depth if it is lower', () => {
616+
const normalizationTarget = {
617+
foo: 'bar',
618+
baz: 42,
619+
obj: {
620+
obj: {
621+
obj: {
622+
bestSmashCharacter: 'Cpt. Falcon',
623+
},
624+
},
625+
},
626+
};
627+
628+
addNonEnumerableProperty(normalizationTarget, '__sentry_override_normalization_depth__', 1);
629+
630+
const result = normalize(normalizationTarget, 3);
631+
632+
expect(result).toEqual({
633+
baz: 42,
634+
foo: 'bar',
635+
obj: '[Object]',
636+
});
637+
});
638+
});
585639
});

0 commit comments

Comments
 (0)