Skip to content

Commit

Permalink
fix: Make redux integration be configurable via normalizeDepth (#7379)
Browse files Browse the repository at this point in the history
  • Loading branch information
lforst committed Mar 8, 2023
2 parents 17f31c6 + ec78a02 commit 2b44452
Show file tree
Hide file tree
Showing 3 changed files with 82 additions and 5 deletions.
18 changes: 16 additions & 2 deletions packages/react/src/redux.ts
@@ -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
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
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]',
});
});
});
});

0 comments on commit 2b44452

Please sign in to comment.