From 64e6c7a674b225422f42ef1f005538b10580cb78 Mon Sep 17 00:00:00 2001 From: Francesco Novy Date: Fri, 15 Nov 2024 09:09:14 +0100 Subject: [PATCH 01/10] fix(core): Ensure `normalizedRequest` on `sdkProcessingMetadata` is merged --- packages/core/src/scope.ts | 4 +- .../core/src/utils/applyScopeDataToEvent.ts | 32 +++- packages/core/test/lib/scope.test.ts | 45 +++++- .../lib/utils/applyScopeDataToEvent.test.ts | 149 +++++++++++++++++- packages/types/src/scope.ts | 4 +- 5 files changed, 223 insertions(+), 11 deletions(-) diff --git a/packages/core/src/scope.ts b/packages/core/src/scope.ts index ff89c0d593a9..f0c940d01971 100644 --- a/packages/core/src/scope.ts +++ b/packages/core/src/scope.ts @@ -24,6 +24,7 @@ import type { import { dateTimestampInSeconds, generatePropagationContext, isPlainObject, logger, uuid4 } from '@sentry/utils'; import { updateSession } from './session'; +import { mergeSdkProcessingMetadata } from './utils/applyScopeDataToEvent'; import { _getSpanForScope, _setSpanForScope } from './utils/spanOnScope'; /** @@ -479,8 +480,7 @@ class ScopeClass implements ScopeInterface { * @inheritDoc */ public setSDKProcessingMetadata(newData: { [key: string]: unknown }): this { - this._sdkProcessingMetadata = { ...this._sdkProcessingMetadata, ...newData }; - + this._sdkProcessingMetadata = mergeSdkProcessingMetadata(this._sdkProcessingMetadata, newData); return this; } diff --git a/packages/core/src/utils/applyScopeDataToEvent.ts b/packages/core/src/utils/applyScopeDataToEvent.ts index f3b1ac0d0be7..772065f191e0 100644 --- a/packages/core/src/utils/applyScopeDataToEvent.ts +++ b/packages/core/src/utils/applyScopeDataToEvent.ts @@ -46,7 +46,8 @@ export function mergeScopeData(data: ScopeData, mergeData: ScopeData): void { mergeAndOverwriteScopeData(data, 'tags', tags); mergeAndOverwriteScopeData(data, 'user', user); mergeAndOverwriteScopeData(data, 'contexts', contexts); - mergeAndOverwriteScopeData(data, 'sdkProcessingMetadata', sdkProcessingMetadata); + + data.sdkProcessingMetadata = mergeSdkProcessingMetadata(data.sdkProcessingMetadata, sdkProcessingMetadata); if (level) { data.level = level; @@ -115,6 +116,35 @@ export function mergeArray( event[prop] = merged.length ? merged : undefined; } +/** + * Merge new SDK processing metadata into existing data. + * New data will overwrite existing data. + * `normalizedRequest` is special handled and will also be merged. + */ +export function mergeSdkProcessingMetadata( + sdkProcessingMetadata: ScopeData['sdkProcessingMetadata'], + newSdkProcessingMetadata: ScopeData['sdkProcessingMetadata'], +): ScopeData['sdkProcessingMetadata'] { + // We want to merge `normalizedRequest` to avoid some partial entry on the scope + // overwriting potentially more complete data on the isolation scope + const normalizedRequestBefore = sdkProcessingMetadata['normalizedRequest']; + const normalizedRequest = newSdkProcessingMetadata['normalizedRequest']; + + const newData = { + ...sdkProcessingMetadata, + ...newSdkProcessingMetadata, + }; + + if (normalizedRequestBefore || normalizedRequest) { + newData['normalizedRequest'] = { + ...(normalizedRequestBefore || {}), + ...(normalizedRequest || {}), + }; + } + + return newData; +} + function applyDataToEvent(event: Event, data: ScopeData): void { const { extra, tags, user, contexts, level, transactionName } = data; diff --git a/packages/core/test/lib/scope.test.ts b/packages/core/test/lib/scope.test.ts index 33c89a2e9eb5..5711e90c3da8 100644 --- a/packages/core/test/lib/scope.test.ts +++ b/packages/core/test/lib/scope.test.ts @@ -204,10 +204,47 @@ describe('Scope', () => { expect(scope['_user']).toEqual({}); }); - test('setProcessingMetadata', () => { - const scope = new Scope(); - scope.setSDKProcessingMetadata({ dogs: 'are great!' }); - expect(scope['_sdkProcessingMetadata'].dogs).toEqual('are great!'); + describe('setProcessingMetadata', () => { + test('it works with no initial data', () => { + const scope = new Scope(); + scope.setSDKProcessingMetadata({ dogs: 'are great!' }); + expect(scope['_sdkProcessingMetadata'].dogs).toEqual('are great!'); + }); + + test('it overwrites arbitrary data', () => { + const scope = new Scope(); + scope.setSDKProcessingMetadata({ dogs: 'are great!' }); + scope.setSDKProcessingMetadata({ dogs: 'are really great!' }); + scope.setSDKProcessingMetadata({ cats: 'are also great!' }); + scope.setSDKProcessingMetadata({ obj: { nested: 'value1' } }); + scope.setSDKProcessingMetadata({ obj: { nested2: 'value2' } }); + + expect(scope['_sdkProcessingMetadata']).toEqual({ + dogs: 'are really great!', + cats: 'are also great!', + obj: { nested2: 'value2' }, + }); + }); + + test('it merges normalizedRequest data', () => { + const scope = new Scope(); + scope.setSDKProcessingMetadata({ + normalizedRequest: { + url: 'value1', + method: 'value1', + }, + }); + scope.setSDKProcessingMetadata({ + normalizedRequest: { + url: 'value2', + headers: {}, + }, + }); + + expect(scope['_sdkProcessingMetadata']).toEqual({ + normalizedRequest: { url: 'value2', method: 'value1', headers: {} }, + }); + }); }); test('set and get propagation context', () => { diff --git a/packages/core/test/lib/utils/applyScopeDataToEvent.test.ts b/packages/core/test/lib/utils/applyScopeDataToEvent.test.ts index e6370931f4cf..a0a52ccffa76 100644 --- a/packages/core/test/lib/utils/applyScopeDataToEvent.test.ts +++ b/packages/core/test/lib/utils/applyScopeDataToEvent.test.ts @@ -5,6 +5,7 @@ import { mergeAndOverwriteScopeData, mergeArray, mergeScopeData, + mergeSdkProcessingMetadata, } from '../../../src/utils/applyScopeDataToEvent'; describe('mergeArray', () => { @@ -134,7 +135,15 @@ describe('mergeScopeData', () => { contexts: { os: { name: 'os1' }, culture: { display_name: 'name1' } }, attachments: [attachment1], propagationContext: { spanId: '1', traceId: '1' }, - sdkProcessingMetadata: { aa: 'aa', bb: 'aa' }, + sdkProcessingMetadata: { + aa: 'aa', + bb: 'aa', + obj: { key: 'value' }, + normalizedRequest: { + url: 'oldUrl', + method: 'oldMethod', + }, + }, fingerprint: ['aa', 'bb'], }; const data2: ScopeData = { @@ -146,7 +155,17 @@ describe('mergeScopeData', () => { contexts: { os: { name: 'os2' } }, attachments: [attachment2, attachment3], propagationContext: { spanId: '2', traceId: '2' }, - sdkProcessingMetadata: { bb: 'bb', cc: 'bb' }, + sdkProcessingMetadata: { + bb: 'bb', + cc: 'bb', + // Regular objects are not deep merged + obj: { key2: 'value2' }, + // Only normalizedRequest is deep merged + normalizedRequest: { + url: 'newUrl', + headers: {}, + }, + }, fingerprint: ['cc'], }; mergeScopeData(data1, data2); @@ -159,12 +178,136 @@ describe('mergeScopeData', () => { contexts: { os: { name: 'os2' }, culture: { display_name: 'name1' } }, attachments: [attachment1, attachment2, attachment3], propagationContext: { spanId: '2', traceId: '2' }, - sdkProcessingMetadata: { aa: 'aa', bb: 'bb', cc: 'bb' }, + sdkProcessingMetadata: { + aa: 'aa', + bb: 'bb', + cc: 'bb', + obj: { key2: 'value2' }, + normalizedRequest: { + url: 'newUrl', + method: 'oldMethod', + headers: {}, + }, + }, fingerprint: ['aa', 'bb', 'cc'], }); }); }); +describe('mergeSdkProcessingMetadata', () => { + it('works with empty objects', () => { + const oldData = {}; + const newData = {}; + + const actual = mergeSdkProcessingMetadata(oldData, newData); + + expect(actual).toEqual({}); + expect(actual).not.toBe(oldData); + expect(actual).not.toBe(newData); + }); + + it('works with arbitrary data', () => { + const oldData = { + old1: 'old1', + old2: 'old2', + obj: { key: 'value' }, + }; + const newData = { + new1: 'new1', + old2: 'new2', + obj: { key2: 'value2' }, + }; + + const actual = mergeSdkProcessingMetadata(oldData, newData); + + expect(actual).toEqual({ + old1: 'old1', + old2: 'new2', + new1: 'new1', + obj: { key2: 'value2' }, + }); + expect(actual).not.toBe(oldData); + expect(actual).not.toBe(newData); + }); + + it('merges normalizedRequest', () => { + const oldData = { + old1: 'old1', + normalizedRequest: { + url: 'oldUrl', + method: 'oldMethod', + }, + }; + const newData = { + new1: 'new1', + normalizedRequest: { + url: 'newUrl', + headers: {}, + }, + }; + + const actual = mergeSdkProcessingMetadata(oldData, newData); + + expect(actual).toEqual({ + old1: 'old1', + new1: 'new1', + normalizedRequest: { + url: 'newUrl', + method: 'oldMethod', + headers: {}, + }, + }); + }); + + it('keeps existing normalizedRequest', () => { + const oldData = { + old1: 'old1', + normalizedRequest: { + url: 'oldUrl', + method: 'oldMethod', + }, + }; + const newData = { + new1: 'new1', + }; + + const actual = mergeSdkProcessingMetadata(oldData, newData); + + expect(actual).toEqual({ + old1: 'old1', + new1: 'new1', + normalizedRequest: { + url: 'oldUrl', + method: 'oldMethod', + }, + }); + }); + + it('adds new normalizedRequest', () => { + const oldData = { + old1: 'old1', + }; + const newData = { + new1: 'new1', + normalizedRequest: { + url: 'newUrl', + method: 'newMethod', + }, + }; + + const actual = mergeSdkProcessingMetadata(oldData, newData); + + expect(actual).toEqual({ + old1: 'old1', + new1: 'new1', + normalizedRequest: { + url: 'newUrl', + method: 'newMethod', + }, + }); + }); +}); + describe('applyScopeDataToEvent', () => { it("doesn't apply scope.transactionName to transaction events", () => { const data: ScopeData = { diff --git a/packages/types/src/scope.ts b/packages/types/src/scope.ts index 2d5c72230aef..d2c33bf0b720 100644 --- a/packages/types/src/scope.ts +++ b/packages/types/src/scope.ts @@ -217,7 +217,9 @@ export interface Scope { clearAttachments(): this; /** - * Add data which will be accessible during event processing but won't get sent to Sentry + * Add data which will be accessible during event processing but won't get sent to Sentry. + * + * TODO(v9): We should type this stricter, so that e.g. `normalizedRequest` is strictly typed. */ setSDKProcessingMetadata(newData: { [key: string]: unknown }): this; From fc21920222209fa44690b4c25039d9b44a22239e Mon Sep 17 00:00:00 2001 From: Francesco Novy Date: Fri, 15 Nov 2024 09:55:06 +0100 Subject: [PATCH 02/10] fix circular dependency --- packages/core/src/scope.ts | 2 +- .../core/src/utils/applyScopeDataToEvent.ts | 30 +---- .../src/utils/mergeSdkProcessingMetadata.ts | 30 +++++ .../lib/utils/applyScopeDataToEvent.test.ts | 115 ------------------ .../utils/mergeSdkProcessingMetadata.test.ts | 115 ++++++++++++++++++ 5 files changed, 147 insertions(+), 145 deletions(-) create mode 100644 packages/core/src/utils/mergeSdkProcessingMetadata.ts create mode 100644 packages/core/test/lib/utils/mergeSdkProcessingMetadata.test.ts diff --git a/packages/core/src/scope.ts b/packages/core/src/scope.ts index f0c940d01971..b9b76d417a6c 100644 --- a/packages/core/src/scope.ts +++ b/packages/core/src/scope.ts @@ -24,7 +24,7 @@ import type { import { dateTimestampInSeconds, generatePropagationContext, isPlainObject, logger, uuid4 } from '@sentry/utils'; import { updateSession } from './session'; -import { mergeSdkProcessingMetadata } from './utils/applyScopeDataToEvent'; +import { mergeSdkProcessingMetadata } from './utils/mergeSdkProcessingMetadata'; import { _getSpanForScope, _setSpanForScope } from './utils/spanOnScope'; /** diff --git a/packages/core/src/utils/applyScopeDataToEvent.ts b/packages/core/src/utils/applyScopeDataToEvent.ts index 772065f191e0..156a06cc0ff0 100644 --- a/packages/core/src/utils/applyScopeDataToEvent.ts +++ b/packages/core/src/utils/applyScopeDataToEvent.ts @@ -1,6 +1,7 @@ import type { Breadcrumb, Event, ScopeData, Span } from '@sentry/types'; import { arrayify, dropUndefinedKeys } from '@sentry/utils'; import { getDynamicSamplingContextFromSpan } from '../tracing/dynamicSamplingContext'; +import { mergeSdkProcessingMetadata } from './mergeSdkProcessingMetadata'; import { getRootSpan, spanToJSON, spanToTraceContext } from './spanUtils'; /** @@ -116,35 +117,6 @@ export function mergeArray( event[prop] = merged.length ? merged : undefined; } -/** - * Merge new SDK processing metadata into existing data. - * New data will overwrite existing data. - * `normalizedRequest` is special handled and will also be merged. - */ -export function mergeSdkProcessingMetadata( - sdkProcessingMetadata: ScopeData['sdkProcessingMetadata'], - newSdkProcessingMetadata: ScopeData['sdkProcessingMetadata'], -): ScopeData['sdkProcessingMetadata'] { - // We want to merge `normalizedRequest` to avoid some partial entry on the scope - // overwriting potentially more complete data on the isolation scope - const normalizedRequestBefore = sdkProcessingMetadata['normalizedRequest']; - const normalizedRequest = newSdkProcessingMetadata['normalizedRequest']; - - const newData = { - ...sdkProcessingMetadata, - ...newSdkProcessingMetadata, - }; - - if (normalizedRequestBefore || normalizedRequest) { - newData['normalizedRequest'] = { - ...(normalizedRequestBefore || {}), - ...(normalizedRequest || {}), - }; - } - - return newData; -} - function applyDataToEvent(event: Event, data: ScopeData): void { const { extra, tags, user, contexts, level, transactionName } = data; diff --git a/packages/core/src/utils/mergeSdkProcessingMetadata.ts b/packages/core/src/utils/mergeSdkProcessingMetadata.ts new file mode 100644 index 000000000000..77495485f0e6 --- /dev/null +++ b/packages/core/src/utils/mergeSdkProcessingMetadata.ts @@ -0,0 +1,30 @@ +import type { ScopeData } from '@sentry/types'; + +/** + * Merge new SDK processing metadata into existing data. + * New data will overwrite existing data. + * `normalizedRequest` is special handled and will also be merged. + */ +export function mergeSdkProcessingMetadata( + sdkProcessingMetadata: ScopeData['sdkProcessingMetadata'], + newSdkProcessingMetadata: ScopeData['sdkProcessingMetadata'], +): ScopeData['sdkProcessingMetadata'] { + // We want to merge `normalizedRequest` to avoid some partial entry on the scope + // overwriting potentially more complete data on the isolation scope + const normalizedRequestBefore = sdkProcessingMetadata['normalizedRequest']; + const normalizedRequest = newSdkProcessingMetadata['normalizedRequest']; + + const newData = { + ...sdkProcessingMetadata, + ...newSdkProcessingMetadata, + }; + + if (normalizedRequestBefore || normalizedRequest) { + newData['normalizedRequest'] = { + ...(normalizedRequestBefore || {}), + ...(normalizedRequest || {}), + }; + } + + return newData; +} diff --git a/packages/core/test/lib/utils/applyScopeDataToEvent.test.ts b/packages/core/test/lib/utils/applyScopeDataToEvent.test.ts index a0a52ccffa76..0603dfaabf45 100644 --- a/packages/core/test/lib/utils/applyScopeDataToEvent.test.ts +++ b/packages/core/test/lib/utils/applyScopeDataToEvent.test.ts @@ -5,7 +5,6 @@ import { mergeAndOverwriteScopeData, mergeArray, mergeScopeData, - mergeSdkProcessingMetadata, } from '../../../src/utils/applyScopeDataToEvent'; describe('mergeArray', () => { @@ -194,120 +193,6 @@ describe('mergeScopeData', () => { }); }); -describe('mergeSdkProcessingMetadata', () => { - it('works with empty objects', () => { - const oldData = {}; - const newData = {}; - - const actual = mergeSdkProcessingMetadata(oldData, newData); - - expect(actual).toEqual({}); - expect(actual).not.toBe(oldData); - expect(actual).not.toBe(newData); - }); - - it('works with arbitrary data', () => { - const oldData = { - old1: 'old1', - old2: 'old2', - obj: { key: 'value' }, - }; - const newData = { - new1: 'new1', - old2: 'new2', - obj: { key2: 'value2' }, - }; - - const actual = mergeSdkProcessingMetadata(oldData, newData); - - expect(actual).toEqual({ - old1: 'old1', - old2: 'new2', - new1: 'new1', - obj: { key2: 'value2' }, - }); - expect(actual).not.toBe(oldData); - expect(actual).not.toBe(newData); - }); - - it('merges normalizedRequest', () => { - const oldData = { - old1: 'old1', - normalizedRequest: { - url: 'oldUrl', - method: 'oldMethod', - }, - }; - const newData = { - new1: 'new1', - normalizedRequest: { - url: 'newUrl', - headers: {}, - }, - }; - - const actual = mergeSdkProcessingMetadata(oldData, newData); - - expect(actual).toEqual({ - old1: 'old1', - new1: 'new1', - normalizedRequest: { - url: 'newUrl', - method: 'oldMethod', - headers: {}, - }, - }); - }); - - it('keeps existing normalizedRequest', () => { - const oldData = { - old1: 'old1', - normalizedRequest: { - url: 'oldUrl', - method: 'oldMethod', - }, - }; - const newData = { - new1: 'new1', - }; - - const actual = mergeSdkProcessingMetadata(oldData, newData); - - expect(actual).toEqual({ - old1: 'old1', - new1: 'new1', - normalizedRequest: { - url: 'oldUrl', - method: 'oldMethod', - }, - }); - }); - - it('adds new normalizedRequest', () => { - const oldData = { - old1: 'old1', - }; - const newData = { - new1: 'new1', - normalizedRequest: { - url: 'newUrl', - method: 'newMethod', - }, - }; - - const actual = mergeSdkProcessingMetadata(oldData, newData); - - expect(actual).toEqual({ - old1: 'old1', - new1: 'new1', - normalizedRequest: { - url: 'newUrl', - method: 'newMethod', - }, - }); - }); -}); - describe('applyScopeDataToEvent', () => { it("doesn't apply scope.transactionName to transaction events", () => { const data: ScopeData = { diff --git a/packages/core/test/lib/utils/mergeSdkProcessingMetadata.test.ts b/packages/core/test/lib/utils/mergeSdkProcessingMetadata.test.ts new file mode 100644 index 000000000000..de32c05b3004 --- /dev/null +++ b/packages/core/test/lib/utils/mergeSdkProcessingMetadata.test.ts @@ -0,0 +1,115 @@ +import { mergeSdkProcessingMetadata } from '../../../src/utils/mergeSdkProcessingMetadata'; + +describe('mergeSdkProcessingMetadata', () => { + it('works with empty objects', () => { + const oldData = {}; + const newData = {}; + + const actual = mergeSdkProcessingMetadata(oldData, newData); + + expect(actual).toEqual({}); + expect(actual).not.toBe(oldData); + expect(actual).not.toBe(newData); + }); + + it('works with arbitrary data', () => { + const oldData = { + old1: 'old1', + old2: 'old2', + obj: { key: 'value' }, + }; + const newData = { + new1: 'new1', + old2: 'new2', + obj: { key2: 'value2' }, + }; + + const actual = mergeSdkProcessingMetadata(oldData, newData); + + expect(actual).toEqual({ + old1: 'old1', + old2: 'new2', + new1: 'new1', + obj: { key2: 'value2' }, + }); + expect(actual).not.toBe(oldData); + expect(actual).not.toBe(newData); + }); + + it('merges normalizedRequest', () => { + const oldData = { + old1: 'old1', + normalizedRequest: { + url: 'oldUrl', + method: 'oldMethod', + }, + }; + const newData = { + new1: 'new1', + normalizedRequest: { + url: 'newUrl', + headers: {}, + }, + }; + + const actual = mergeSdkProcessingMetadata(oldData, newData); + + expect(actual).toEqual({ + old1: 'old1', + new1: 'new1', + normalizedRequest: { + url: 'newUrl', + method: 'oldMethod', + headers: {}, + }, + }); + }); + + it('keeps existing normalizedRequest', () => { + const oldData = { + old1: 'old1', + normalizedRequest: { + url: 'oldUrl', + method: 'oldMethod', + }, + }; + const newData = { + new1: 'new1', + }; + + const actual = mergeSdkProcessingMetadata(oldData, newData); + + expect(actual).toEqual({ + old1: 'old1', + new1: 'new1', + normalizedRequest: { + url: 'oldUrl', + method: 'oldMethod', + }, + }); + }); + + it('adds new normalizedRequest', () => { + const oldData = { + old1: 'old1', + }; + const newData = { + new1: 'new1', + normalizedRequest: { + url: 'newUrl', + method: 'newMethod', + }, + }; + + const actual = mergeSdkProcessingMetadata(oldData, newData); + + expect(actual).toEqual({ + old1: 'old1', + new1: 'new1', + normalizedRequest: { + url: 'newUrl', + method: 'newMethod', + }, + }); + }); +}); From 99ee12feaf6c84c4b51735566b6a92dbe671954a Mon Sep 17 00:00:00 2001 From: Francesco Novy Date: Fri, 15 Nov 2024 10:05:43 +0100 Subject: [PATCH 03/10] expose proper util to set normalized request --- packages/core/src/index.ts | 1 + packages/core/src/scope.ts | 3 +-- .../core/src/utils/applyScopeDataToEvent.ts | 2 +- ...ngMetadata.ts => sdkProcessingMetadata.ts} | 19 +++++++++++++++- packages/core/test/lib/scope.test.ts | 22 +------------------ ....test.ts => sdkProcessingMetadata.test.ts} | 2 +- .../http/SentryHttpInstrumentation.ts | 6 +++-- 7 files changed, 27 insertions(+), 28 deletions(-) rename packages/core/src/utils/{mergeSdkProcessingMetadata.ts => sdkProcessingMetadata.ts} (56%) rename packages/core/test/lib/utils/{mergeSdkProcessingMetadata.test.ts => sdkProcessingMetadata.test.ts} (96%) diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index 598b0168f3ef..0d2a07c516ea 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -68,6 +68,7 @@ export { hasTracingEnabled } from './utils/hasTracingEnabled'; export { isSentryRequestUrl } from './utils/isSentryRequestUrl'; export { handleCallbackErrors } from './utils/handleCallbackErrors'; export { parameterize } from './utils/parameterize'; +export { setNormalizedRequest } from './utils/sdkProcessingMetadata'; export { spanToTraceHeader, spanToJSON, diff --git a/packages/core/src/scope.ts b/packages/core/src/scope.ts index b9b76d417a6c..008b094ad3d4 100644 --- a/packages/core/src/scope.ts +++ b/packages/core/src/scope.ts @@ -24,7 +24,6 @@ import type { import { dateTimestampInSeconds, generatePropagationContext, isPlainObject, logger, uuid4 } from '@sentry/utils'; import { updateSession } from './session'; -import { mergeSdkProcessingMetadata } from './utils/mergeSdkProcessingMetadata'; import { _getSpanForScope, _setSpanForScope } from './utils/spanOnScope'; /** @@ -480,7 +479,7 @@ class ScopeClass implements ScopeInterface { * @inheritDoc */ public setSDKProcessingMetadata(newData: { [key: string]: unknown }): this { - this._sdkProcessingMetadata = mergeSdkProcessingMetadata(this._sdkProcessingMetadata, newData); + this._sdkProcessingMetadata = { ...this._sdkProcessingMetadata, ...newData }; return this; } diff --git a/packages/core/src/utils/applyScopeDataToEvent.ts b/packages/core/src/utils/applyScopeDataToEvent.ts index 156a06cc0ff0..0ea8d7de1465 100644 --- a/packages/core/src/utils/applyScopeDataToEvent.ts +++ b/packages/core/src/utils/applyScopeDataToEvent.ts @@ -1,7 +1,7 @@ import type { Breadcrumb, Event, ScopeData, Span } from '@sentry/types'; import { arrayify, dropUndefinedKeys } from '@sentry/utils'; import { getDynamicSamplingContextFromSpan } from '../tracing/dynamicSamplingContext'; -import { mergeSdkProcessingMetadata } from './mergeSdkProcessingMetadata'; +import { mergeSdkProcessingMetadata } from './sdkProcessingMetadata'; import { getRootSpan, spanToJSON, spanToTraceContext } from './spanUtils'; /** diff --git a/packages/core/src/utils/mergeSdkProcessingMetadata.ts b/packages/core/src/utils/sdkProcessingMetadata.ts similarity index 56% rename from packages/core/src/utils/mergeSdkProcessingMetadata.ts rename to packages/core/src/utils/sdkProcessingMetadata.ts index 77495485f0e6..af425bfdfcdc 100644 --- a/packages/core/src/utils/mergeSdkProcessingMetadata.ts +++ b/packages/core/src/utils/sdkProcessingMetadata.ts @@ -1,4 +1,5 @@ -import type { ScopeData } from '@sentry/types'; +import type { Request, ScopeData } from '@sentry/types'; +import { getIsolationScope } from '../currentScopes'; /** * Merge new SDK processing metadata into existing data. @@ -28,3 +29,19 @@ export function mergeSdkProcessingMetadata( return newData; } + +/** + * Set a normalizedRequest on the scope, ensuring it merges with potentially existing request data. + * By default, this will put this data on the isolation scope, + * but you can also pass a different scope to set the data on. + */ +export function setNormalizedRequest(normalizedRequest: Request, scope = getIsolationScope()): void { + const normalizedRequestBefore = scope.getScopeData().sdkProcessingMetadata['normalizedRequest']; + + const newNormalizedRequest = { + ...(normalizedRequestBefore || {}), + ...normalizedRequest, + } satisfies Request; + + scope.setSDKProcessingMetadata({ normalizedRequest: newNormalizedRequest }); +} diff --git a/packages/core/test/lib/scope.test.ts b/packages/core/test/lib/scope.test.ts index 5711e90c3da8..6e934864a83c 100644 --- a/packages/core/test/lib/scope.test.ts +++ b/packages/core/test/lib/scope.test.ts @@ -211,7 +211,7 @@ describe('Scope', () => { expect(scope['_sdkProcessingMetadata'].dogs).toEqual('are great!'); }); - test('it overwrites arbitrary data', () => { + test('it overwrites data', () => { const scope = new Scope(); scope.setSDKProcessingMetadata({ dogs: 'are great!' }); scope.setSDKProcessingMetadata({ dogs: 'are really great!' }); @@ -225,26 +225,6 @@ describe('Scope', () => { obj: { nested2: 'value2' }, }); }); - - test('it merges normalizedRequest data', () => { - const scope = new Scope(); - scope.setSDKProcessingMetadata({ - normalizedRequest: { - url: 'value1', - method: 'value1', - }, - }); - scope.setSDKProcessingMetadata({ - normalizedRequest: { - url: 'value2', - headers: {}, - }, - }); - - expect(scope['_sdkProcessingMetadata']).toEqual({ - normalizedRequest: { url: 'value2', method: 'value1', headers: {} }, - }); - }); }); test('set and get propagation context', () => { diff --git a/packages/core/test/lib/utils/mergeSdkProcessingMetadata.test.ts b/packages/core/test/lib/utils/sdkProcessingMetadata.test.ts similarity index 96% rename from packages/core/test/lib/utils/mergeSdkProcessingMetadata.test.ts rename to packages/core/test/lib/utils/sdkProcessingMetadata.test.ts index de32c05b3004..859201c3f36b 100644 --- a/packages/core/test/lib/utils/mergeSdkProcessingMetadata.test.ts +++ b/packages/core/test/lib/utils/sdkProcessingMetadata.test.ts @@ -1,4 +1,4 @@ -import { mergeSdkProcessingMetadata } from '../../../src/utils/mergeSdkProcessingMetadata'; +import { mergeSdkProcessingMetadata } from '../../../src/utils/sdkProcessingMetadata'; describe('mergeSdkProcessingMetadata', () => { it('works with empty objects', () => { diff --git a/packages/node/src/integrations/http/SentryHttpInstrumentation.ts b/packages/node/src/integrations/http/SentryHttpInstrumentation.ts index b17810adb601..74309922052e 100644 --- a/packages/node/src/integrations/http/SentryHttpInstrumentation.ts +++ b/packages/node/src/integrations/http/SentryHttpInstrumentation.ts @@ -5,7 +5,7 @@ import { VERSION } from '@opentelemetry/core'; import type { InstrumentationConfig } from '@opentelemetry/instrumentation'; import { InstrumentationBase, InstrumentationNodeModuleDefinition } from '@opentelemetry/instrumentation'; import { getRequestInfo } from '@opentelemetry/instrumentation-http'; -import { addBreadcrumb, getClient, getIsolationScope, withIsolationScope } from '@sentry/core'; +import { addBreadcrumb, getClient, getIsolationScope, setNormalizedRequest, withIsolationScope } from '@sentry/core'; import type { PolymorphicRequest, RequestEventData, SanitizedRequestData } from '@sentry/types'; import { getBreadcrumbLogLevelFromHttpStatusCode, @@ -153,7 +153,9 @@ export class SentryHttpInstrumentation extends InstrumentationBase(); if (client && client.getOptions().autoSessionTracking) { From 196460f2ec10ee3979aa8121b7391455c69f8c6e Mon Sep 17 00:00:00 2001 From: Francesco Novy Date: Fri, 15 Nov 2024 10:32:49 +0100 Subject: [PATCH 04/10] bump size limit :( --- .size-limit.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.size-limit.js b/.size-limit.js index 3f1be5b9c140..0a785854b3f3 100644 --- a/.size-limit.js +++ b/.size-limit.js @@ -107,7 +107,7 @@ module.exports = [ path: 'packages/browser/build/npm/esm/index.js', import: createImport('init', 'feedbackAsyncIntegration'), gzip: true, - limit: '33 KB', + limit: '34 KB', }, // React SDK (ESM) { From c3f785ecfbc4bfd8de59e528f3e4ab25532a00bc Mon Sep 17 00:00:00 2001 From: Francesco Novy Date: Fri, 15 Nov 2024 11:15:17 +0100 Subject: [PATCH 05/10] fix body setting --- .../src/integrations/http/SentryHttpInstrumentation.ts | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/packages/node/src/integrations/http/SentryHttpInstrumentation.ts b/packages/node/src/integrations/http/SentryHttpInstrumentation.ts index 74309922052e..6eac2b1c2657 100644 --- a/packages/node/src/integrations/http/SentryHttpInstrumentation.ts +++ b/packages/node/src/integrations/http/SentryHttpInstrumentation.ts @@ -6,7 +6,7 @@ import type { InstrumentationConfig } from '@opentelemetry/instrumentation'; import { InstrumentationBase, InstrumentationNodeModuleDefinition } from '@opentelemetry/instrumentation'; import { getRequestInfo } from '@opentelemetry/instrumentation-http'; import { addBreadcrumb, getClient, getIsolationScope, setNormalizedRequest, withIsolationScope } from '@sentry/core'; -import type { PolymorphicRequest, RequestEventData, SanitizedRequestData } from '@sentry/types'; +import type { PolymorphicRequest, RequestEventData, SanitizedRequestData, Scope } from '@sentry/types'; import { getBreadcrumbLogLevelFromHttpStatusCode, getSanitizedUrlString, @@ -150,7 +150,7 @@ export class SentryHttpInstrumentation extends InstrumentationBase Date: Fri, 15 Nov 2024 11:16:25 +0100 Subject: [PATCH 06/10] rename to `setRequestEventData` --- packages/core/src/index.ts | 2 +- packages/core/src/utils/sdkProcessingMetadata.ts | 2 +- .../node/src/integrations/http/SentryHttpInstrumentation.ts | 6 +++--- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index 0d2a07c516ea..3be668fbbdfb 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -68,7 +68,7 @@ export { hasTracingEnabled } from './utils/hasTracingEnabled'; export { isSentryRequestUrl } from './utils/isSentryRequestUrl'; export { handleCallbackErrors } from './utils/handleCallbackErrors'; export { parameterize } from './utils/parameterize'; -export { setNormalizedRequest } from './utils/sdkProcessingMetadata'; +export { setRequestEventData } from './utils/sdkProcessingMetadata'; export { spanToTraceHeader, spanToJSON, diff --git a/packages/core/src/utils/sdkProcessingMetadata.ts b/packages/core/src/utils/sdkProcessingMetadata.ts index af425bfdfcdc..de47c290b403 100644 --- a/packages/core/src/utils/sdkProcessingMetadata.ts +++ b/packages/core/src/utils/sdkProcessingMetadata.ts @@ -35,7 +35,7 @@ export function mergeSdkProcessingMetadata( * By default, this will put this data on the isolation scope, * but you can also pass a different scope to set the data on. */ -export function setNormalizedRequest(normalizedRequest: Request, scope = getIsolationScope()): void { +export function setRequestEventData(normalizedRequest: Request, scope = getIsolationScope()): void { const normalizedRequestBefore = scope.getScopeData().sdkProcessingMetadata['normalizedRequest']; const newNormalizedRequest = { diff --git a/packages/node/src/integrations/http/SentryHttpInstrumentation.ts b/packages/node/src/integrations/http/SentryHttpInstrumentation.ts index 6eac2b1c2657..96e53f28f38e 100644 --- a/packages/node/src/integrations/http/SentryHttpInstrumentation.ts +++ b/packages/node/src/integrations/http/SentryHttpInstrumentation.ts @@ -5,7 +5,7 @@ import { VERSION } from '@opentelemetry/core'; import type { InstrumentationConfig } from '@opentelemetry/instrumentation'; import { InstrumentationBase, InstrumentationNodeModuleDefinition } from '@opentelemetry/instrumentation'; import { getRequestInfo } from '@opentelemetry/instrumentation-http'; -import { addBreadcrumb, getClient, getIsolationScope, setNormalizedRequest, withIsolationScope } from '@sentry/core'; +import { addBreadcrumb, getClient, getIsolationScope, setRequestEventData, withIsolationScope } from '@sentry/core'; import type { PolymorphicRequest, RequestEventData, SanitizedRequestData, Scope } from '@sentry/types'; import { getBreadcrumbLogLevelFromHttpStatusCode, @@ -155,7 +155,7 @@ export class SentryHttpInstrumentation extends InstrumentationBase(); if (client && client.getOptions().autoSessionTracking) { @@ -399,7 +399,7 @@ function patchRequestToCaptureBody(req: IncomingMessage, isolationScope: Scope): const body = Buffer.concat(chunks).toString('utf-8'); if (body) { - setNormalizedRequest({ data: body }, isolationScope); + setRequestEventData({ data: body }, isolationScope); } } catch { // ignore errors here From 15961cd732dff02e70eedc930f3d14a18fa3b9b9 Mon Sep 17 00:00:00 2001 From: Francesco Novy Date: Mon, 18 Nov 2024 10:11:46 +0100 Subject: [PATCH 07/10] fix linting --- packages/core/src/utils/sdkProcessingMetadata.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/core/src/utils/sdkProcessingMetadata.ts b/packages/core/src/utils/sdkProcessingMetadata.ts index de47c290b403..e0837856fcbe 100644 --- a/packages/core/src/utils/sdkProcessingMetadata.ts +++ b/packages/core/src/utils/sdkProcessingMetadata.ts @@ -1,4 +1,4 @@ -import type { Request, ScopeData } from '@sentry/types'; +import type { RequestEventData, ScopeData } from '@sentry/types'; import { getIsolationScope } from '../currentScopes'; /** @@ -35,13 +35,13 @@ export function mergeSdkProcessingMetadata( * By default, this will put this data on the isolation scope, * but you can also pass a different scope to set the data on. */ -export function setRequestEventData(normalizedRequest: Request, scope = getIsolationScope()): void { +export function setRequestEventData(normalizedRequest: RequestEventData, scope = getIsolationScope()): void { const normalizedRequestBefore = scope.getScopeData().sdkProcessingMetadata['normalizedRequest']; const newNormalizedRequest = { ...(normalizedRequestBefore || {}), ...normalizedRequest, - } satisfies Request; + } satisfies RequestEventData; scope.setSDKProcessingMetadata({ normalizedRequest: newNormalizedRequest }); } From cd577f32d8eb9c9232360fcb185f9eeb8410ebdc Mon Sep 17 00:00:00 2001 From: Francesco Novy Date: Mon, 18 Nov 2024 14:57:37 +0100 Subject: [PATCH 08/10] use generic merge instead --- packages/core/src/index.ts | 1 - packages/core/src/scope.ts | 3 +- .../core/src/utils/applyScopeDataToEvent.ts | 14 +-- packages/core/src/utils/merge.ts | 29 +++++ .../core/src/utils/sdkProcessingMetadata.ts | 47 ------- packages/core/test/lib/scope.test.ts | 6 +- .../lib/utils/applyScopeDataToEvent.test.ts | 4 +- packages/core/test/lib/utils/merge.test.ts | 54 ++++++++ .../lib/utils/sdkProcessingMetadata.test.ts | 115 ------------------ .../http/SentryHttpInstrumentation.ts | 11 +- 10 files changed, 99 insertions(+), 185 deletions(-) create mode 100644 packages/core/src/utils/merge.ts delete mode 100644 packages/core/src/utils/sdkProcessingMetadata.ts create mode 100644 packages/core/test/lib/utils/merge.test.ts delete mode 100644 packages/core/test/lib/utils/sdkProcessingMetadata.test.ts diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index 3be668fbbdfb..598b0168f3ef 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -68,7 +68,6 @@ export { hasTracingEnabled } from './utils/hasTracingEnabled'; export { isSentryRequestUrl } from './utils/isSentryRequestUrl'; export { handleCallbackErrors } from './utils/handleCallbackErrors'; export { parameterize } from './utils/parameterize'; -export { setRequestEventData } from './utils/sdkProcessingMetadata'; export { spanToTraceHeader, spanToJSON, diff --git a/packages/core/src/scope.ts b/packages/core/src/scope.ts index 008b094ad3d4..dbd6c90a395d 100644 --- a/packages/core/src/scope.ts +++ b/packages/core/src/scope.ts @@ -24,6 +24,7 @@ import type { import { dateTimestampInSeconds, generatePropagationContext, isPlainObject, logger, uuid4 } from '@sentry/utils'; import { updateSession } from './session'; +import { merge } from './utils/merge'; import { _getSpanForScope, _setSpanForScope } from './utils/spanOnScope'; /** @@ -479,7 +480,7 @@ class ScopeClass implements ScopeInterface { * @inheritDoc */ public setSDKProcessingMetadata(newData: { [key: string]: unknown }): this { - this._sdkProcessingMetadata = { ...this._sdkProcessingMetadata, ...newData }; + this._sdkProcessingMetadata = merge(this._sdkProcessingMetadata, newData, 2); return this; } diff --git a/packages/core/src/utils/applyScopeDataToEvent.ts b/packages/core/src/utils/applyScopeDataToEvent.ts index 0ea8d7de1465..91d129b52444 100644 --- a/packages/core/src/utils/applyScopeDataToEvent.ts +++ b/packages/core/src/utils/applyScopeDataToEvent.ts @@ -1,7 +1,7 @@ import type { Breadcrumb, Event, ScopeData, Span } from '@sentry/types'; import { arrayify, dropUndefinedKeys } from '@sentry/utils'; import { getDynamicSamplingContextFromSpan } from '../tracing/dynamicSamplingContext'; -import { mergeSdkProcessingMetadata } from './sdkProcessingMetadata'; +import { merge } from './merge'; import { getRootSpan, spanToJSON, spanToTraceContext } from './spanUtils'; /** @@ -48,7 +48,7 @@ export function mergeScopeData(data: ScopeData, mergeData: ScopeData): void { mergeAndOverwriteScopeData(data, 'user', user); mergeAndOverwriteScopeData(data, 'contexts', contexts); - data.sdkProcessingMetadata = mergeSdkProcessingMetadata(data.sdkProcessingMetadata, sdkProcessingMetadata); + data.sdkProcessingMetadata = merge(data.sdkProcessingMetadata, sdkProcessingMetadata, 2); if (level) { data.level = level; @@ -89,15 +89,7 @@ export function mergeAndOverwriteScopeData< Prop extends 'extra' | 'tags' | 'user' | 'contexts' | 'sdkProcessingMetadata', Data extends ScopeData, >(data: Data, prop: Prop, mergeVal: Data[Prop]): void { - if (mergeVal && Object.keys(mergeVal).length) { - // Clone object - data[prop] = { ...data[prop] }; - for (const key in mergeVal) { - if (Object.prototype.hasOwnProperty.call(mergeVal, key)) { - data[prop][key] = mergeVal[key]; - } - } - } + data[prop] = merge(data[prop], mergeVal, 1); } /** Exported only for tests */ diff --git a/packages/core/src/utils/merge.ts b/packages/core/src/utils/merge.ts new file mode 100644 index 000000000000..6870bfa53c4e --- /dev/null +++ b/packages/core/src/utils/merge.ts @@ -0,0 +1,29 @@ +/** + * Shallow merge two objects. + * Does not mutate the passed in objects. + * By default, this merges 2 levels deep. + */ +export function merge(initialObj: T, mergeObj: T, levels = 2): T { + // If the merge value is not an object, or we have no merge levels left, + // we just set the value to the merge value + if (typeof mergeObj !== 'object' || levels <= 0) { + return mergeObj; + } + + // If the merge object is an empty object, and the initial object is not undefined, we return the initial object + if (initialObj && mergeObj && Object.keys(mergeObj).length === 0) { + return initialObj; + } + + // Clone object + const output = { ...initialObj }; + + // Merge values into output, resursively + for (const key in mergeObj) { + if (Object.prototype.hasOwnProperty.call(mergeObj, key)) { + output[key] = merge(output[key], mergeObj[key], levels - 1); + } + } + + return output; +} diff --git a/packages/core/src/utils/sdkProcessingMetadata.ts b/packages/core/src/utils/sdkProcessingMetadata.ts deleted file mode 100644 index e0837856fcbe..000000000000 --- a/packages/core/src/utils/sdkProcessingMetadata.ts +++ /dev/null @@ -1,47 +0,0 @@ -import type { RequestEventData, ScopeData } from '@sentry/types'; -import { getIsolationScope } from '../currentScopes'; - -/** - * Merge new SDK processing metadata into existing data. - * New data will overwrite existing data. - * `normalizedRequest` is special handled and will also be merged. - */ -export function mergeSdkProcessingMetadata( - sdkProcessingMetadata: ScopeData['sdkProcessingMetadata'], - newSdkProcessingMetadata: ScopeData['sdkProcessingMetadata'], -): ScopeData['sdkProcessingMetadata'] { - // We want to merge `normalizedRequest` to avoid some partial entry on the scope - // overwriting potentially more complete data on the isolation scope - const normalizedRequestBefore = sdkProcessingMetadata['normalizedRequest']; - const normalizedRequest = newSdkProcessingMetadata['normalizedRequest']; - - const newData = { - ...sdkProcessingMetadata, - ...newSdkProcessingMetadata, - }; - - if (normalizedRequestBefore || normalizedRequest) { - newData['normalizedRequest'] = { - ...(normalizedRequestBefore || {}), - ...(normalizedRequest || {}), - }; - } - - return newData; -} - -/** - * Set a normalizedRequest on the scope, ensuring it merges with potentially existing request data. - * By default, this will put this data on the isolation scope, - * but you can also pass a different scope to set the data on. - */ -export function setRequestEventData(normalizedRequest: RequestEventData, scope = getIsolationScope()): void { - const normalizedRequestBefore = scope.getScopeData().sdkProcessingMetadata['normalizedRequest']; - - const newNormalizedRequest = { - ...(normalizedRequestBefore || {}), - ...normalizedRequest, - } satisfies RequestEventData; - - scope.setSDKProcessingMetadata({ normalizedRequest: newNormalizedRequest }); -} diff --git a/packages/core/test/lib/scope.test.ts b/packages/core/test/lib/scope.test.ts index 6e934864a83c..c6a081948453 100644 --- a/packages/core/test/lib/scope.test.ts +++ b/packages/core/test/lib/scope.test.ts @@ -216,13 +216,13 @@ describe('Scope', () => { scope.setSDKProcessingMetadata({ dogs: 'are great!' }); scope.setSDKProcessingMetadata({ dogs: 'are really great!' }); scope.setSDKProcessingMetadata({ cats: 'are also great!' }); - scope.setSDKProcessingMetadata({ obj: { nested: 'value1' } }); - scope.setSDKProcessingMetadata({ obj: { nested2: 'value2' } }); + scope.setSDKProcessingMetadata({ obj: { nested1: 'value1', nested: 'value1' } }); + scope.setSDKProcessingMetadata({ obj: { nested2: 'value2', nested: 'value2' } }); expect(scope['_sdkProcessingMetadata']).toEqual({ dogs: 'are really great!', cats: 'are also great!', - obj: { nested2: 'value2' }, + obj: { nested2: 'value2', nested: 'value2', nested1: 'value1' }, }); }); }); diff --git a/packages/core/test/lib/utils/applyScopeDataToEvent.test.ts b/packages/core/test/lib/utils/applyScopeDataToEvent.test.ts index 0603dfaabf45..7fb7befcd2a7 100644 --- a/packages/core/test/lib/utils/applyScopeDataToEvent.test.ts +++ b/packages/core/test/lib/utils/applyScopeDataToEvent.test.ts @@ -157,9 +157,7 @@ describe('mergeScopeData', () => { sdkProcessingMetadata: { bb: 'bb', cc: 'bb', - // Regular objects are not deep merged obj: { key2: 'value2' }, - // Only normalizedRequest is deep merged normalizedRequest: { url: 'newUrl', headers: {}, @@ -181,7 +179,7 @@ describe('mergeScopeData', () => { aa: 'aa', bb: 'bb', cc: 'bb', - obj: { key2: 'value2' }, + obj: { key: 'value', key2: 'value2' }, normalizedRequest: { url: 'newUrl', method: 'oldMethod', diff --git a/packages/core/test/lib/utils/merge.test.ts b/packages/core/test/lib/utils/merge.test.ts new file mode 100644 index 000000000000..394917e71ffc --- /dev/null +++ b/packages/core/test/lib/utils/merge.test.ts @@ -0,0 +1,54 @@ +import { merge } from '../../../src/utils/merge'; + +describe('merge', () => { + it('works with empty objects', () => { + const oldData = {}; + const newData = {}; + + const actual = merge(oldData, newData); + + expect(actual).toEqual({}); + expect(actual).toBe(oldData); + expect(actual).not.toBe(newData); + }); + + it('works with empty merge object', () => { + const oldData = { aa: 'aha' }; + const newData = {}; + + const actual = merge(oldData, newData); + + expect(actual).toEqual({ aa: 'aha' }); + expect(actual).toBe(oldData); + expect(actual).not.toBe(newData); + }); + + it('works with arbitrary data', () => { + const oldData = { + old1: 'old1', + old2: 'old2', + obj: { key: 'value1', key1: 'value1', deep: { key: 'value' } }, + } as any; + const newData = { + new1: 'new1', + old2: 'new2', + obj: { key2: 'value2', key: 'value2', deep: { key2: 'value2' } }, + } as any; + + const actual = merge(oldData, newData); + + expect(actual).toEqual({ + old1: 'old1', + old2: 'new2', + new1: 'new1', + obj: { + key2: 'value2', + key: 'value2', + key1: 'value1', + deep: { key2: 'value2' }, + }, + }); + expect(actual).not.toBe(oldData); + expect(actual).not.toBe(newData); + }); +}); diff --git a/packages/core/test/lib/utils/sdkProcessingMetadata.test.ts b/packages/core/test/lib/utils/sdkProcessingMetadata.test.ts deleted file mode 100644 index 859201c3f36b..000000000000 --- a/packages/core/test/lib/utils/sdkProcessingMetadata.test.ts +++ /dev/null @@ -1,115 +0,0 @@ -import { mergeSdkProcessingMetadata } from '../../../src/utils/sdkProcessingMetadata'; - -describe('mergeSdkProcessingMetadata', () => { - it('works with empty objects', () => { - const oldData = {}; - const newData = {}; - - const actual = mergeSdkProcessingMetadata(oldData, newData); - - expect(actual).toEqual({}); - expect(actual).not.toBe(oldData); - expect(actual).not.toBe(newData); - }); - - it('works with arbitrary data', () => { - const oldData = { - old1: 'old1', - old2: 'old2', - obj: { key: 'value' }, - }; - const newData = { - new1: 'new1', - old2: 'new2', - obj: { key2: 'value2' }, - }; - - const actual = mergeSdkProcessingMetadata(oldData, newData); - - expect(actual).toEqual({ - old1: 'old1', - old2: 'new2', - new1: 'new1', - obj: { key2: 'value2' }, - }); - expect(actual).not.toBe(oldData); - expect(actual).not.toBe(newData); - }); - - it('merges normalizedRequest', () => { - const oldData = { - old1: 'old1', - normalizedRequest: { - url: 'oldUrl', - method: 'oldMethod', - }, - }; - const newData = { - new1: 'new1', - normalizedRequest: { - url: 'newUrl', - headers: {}, - }, - }; - - const actual = mergeSdkProcessingMetadata(oldData, newData); - - expect(actual).toEqual({ - old1: 'old1', - new1: 'new1', - normalizedRequest: { - url: 'newUrl', - method: 'oldMethod', - headers: {}, - }, - }); - }); - - it('keeps existing normalizedRequest', () => { - const oldData = { - old1: 'old1', - normalizedRequest: { - url: 'oldUrl', - method: 'oldMethod', - }, - }; - const newData = { - new1: 'new1', - }; - - const actual = mergeSdkProcessingMetadata(oldData, newData); - - expect(actual).toEqual({ - old1: 'old1', - new1: 'new1', - normalizedRequest: { - url: 'oldUrl', - method: 'oldMethod', - }, - }); - }); - - it('adds new normalizedRequest', () => { - const oldData = { - old1: 'old1', - }; - const newData = { - new1: 'new1', - normalizedRequest: { - url: 'newUrl', - method: 'newMethod', - }, - }; - - const actual = mergeSdkProcessingMetadata(oldData, newData); - - expect(actual).toEqual({ - old1: 'old1', - new1: 'new1', - normalizedRequest: { - url: 'newUrl', - method: 'newMethod', - }, - }); - }); -}); diff --git a/packages/node/src/integrations/http/SentryHttpInstrumentation.ts b/packages/node/src/integrations/http/SentryHttpInstrumentation.ts index 96e53f28f38e..060d30f2d5a3 100644 --- a/packages/node/src/integrations/http/SentryHttpInstrumentation.ts +++ b/packages/node/src/integrations/http/SentryHttpInstrumentation.ts @@ -5,7 +5,7 @@ import { VERSION } from '@opentelemetry/core'; import type { InstrumentationConfig } from '@opentelemetry/instrumentation'; import { InstrumentationBase, InstrumentationNodeModuleDefinition } from '@opentelemetry/instrumentation'; import { getRequestInfo } from '@opentelemetry/instrumentation-http'; -import { addBreadcrumb, getClient, getIsolationScope, setRequestEventData, withIsolationScope } from '@sentry/core'; +import { addBreadcrumb, getClient, getIsolationScope, withIsolationScope } from '@sentry/core'; import type { PolymorphicRequest, RequestEventData, SanitizedRequestData, Scope } from '@sentry/types'; import { getBreadcrumbLogLevelFromHttpStatusCode, @@ -154,8 +154,10 @@ export class SentryHttpInstrumentation extends InstrumentationBase(); if (client && client.getOptions().autoSessionTracking) { @@ -399,7 +401,8 @@ function patchRequestToCaptureBody(req: IncomingMessage, isolationScope: Scope): const body = Buffer.concat(chunks).toString('utf-8'); if (body) { - setRequestEventData({ data: body }, isolationScope); + const normalizedRequest = { data: body } satisfies RequestEventData; + isolationScope.setSDKProcessingMetadata({ normalizedRequest }); } } catch { // ignore errors here From 2880f72d03f309deca439b7dc72d8c1e97060b89 Mon Sep 17 00:00:00 2001 From: Francesco Novy Date: Tue, 19 Nov 2024 08:47:26 +0100 Subject: [PATCH 09/10] small fixes and tests --- .size-limit.js | 2 +- packages/core/src/utils/merge.ts | 4 +++- packages/core/test/lib/utils/merge.test.ts | 21 +++++++++++++++++++++ 3 files changed, 25 insertions(+), 2 deletions(-) diff --git a/.size-limit.js b/.size-limit.js index 0a785854b3f3..09b6f82b230c 100644 --- a/.size-limit.js +++ b/.size-limit.js @@ -160,7 +160,7 @@ module.exports = [ name: 'CDN Bundle (incl. Tracing)', path: createCDNPath('bundle.tracing.min.js'), gzip: true, - limit: '38 KB', + limit: '39 KB', }, { name: 'CDN Bundle (incl. Tracing, Replay)', diff --git a/packages/core/src/utils/merge.ts b/packages/core/src/utils/merge.ts index 6870bfa53c4e..d80520b45cf6 100644 --- a/packages/core/src/utils/merge.ts +++ b/packages/core/src/utils/merge.ts @@ -1,12 +1,14 @@ /** * Shallow merge two objects. * Does not mutate the passed in objects. + * Undefined/empty values in the merge object will overwrite existing values. + * * By default, this merges 2 levels deep. */ export function merge(initialObj: T, mergeObj: T, levels = 2): T { // If the merge value is not an object, or we have no merge levels left, // we just set the value to the merge value - if (typeof mergeObj !== 'object' || levels <= 0) { + if (!mergeObj || typeof mergeObj !== 'object' || levels <= 0) { return mergeObj; } diff --git a/packages/core/test/lib/utils/merge.test.ts b/packages/core/test/lib/utils/merge.test.ts index 394917e71ffc..66146b3477d4 100644 --- a/packages/core/test/lib/utils/merge.test.ts +++ b/packages/core/test/lib/utils/merge.test.ts @@ -51,4 +51,25 @@ describe('merge', () => { expect(actual).not.toBe(oldData); expect(actual).not.toBe(newData); }); + + it.each([ + [undefined, { a: 'aa' }, { a: 'aa' }], + [{ a: 'aa' }, undefined, undefined], + [{ a: 'aa' }, null, null], + [{ a: 'aa' }, { a: undefined }, { a: undefined }], + [{ a: 'aa' }, { a: null }, { a: null }], + [{ a: 'aa' }, { a: '' }, { a: '' }], + [ + { a0: { a1: { a2: { a3: { a4: 'a4a' }, a3a: 'a3a' }, a2a: 'a2a' }, a1a: 'a1a' }, a0a: 'a0a' }, + { a0: { a1: { a2: { a3: { a4: 'a4b' }, a3b: 'a3b' }, a2b: 'a2b' }, a1b: 'a1b' }, a0b: 'a0b' }, + { + a0: { a1: { a2: { a3: { a4: 'a4a' }, a3b: 'a3a' }, a2b: 'a2b' }, a1b: 'a1b', a1a: 'a1a' }, + a0b: 'a0b', + a0a: 'a0a', + }, + ], + ])('works with %p and %p', (oldData, newData, expected) => { + const actual = merge(oldData, newData as any); + expect(actual).toEqual(expected); + }); }); From 003f261aeeed72615ae5e408abbf8e9634d72157 Mon Sep 17 00:00:00 2001 From: Francesco Novy Date: Tue, 19 Nov 2024 09:17:57 +0100 Subject: [PATCH 10/10] fix test --- packages/core/test/lib/utils/merge.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/core/test/lib/utils/merge.test.ts b/packages/core/test/lib/utils/merge.test.ts index 66146b3477d4..b0a215231bfb 100644 --- a/packages/core/test/lib/utils/merge.test.ts +++ b/packages/core/test/lib/utils/merge.test.ts @@ -63,7 +63,7 @@ describe('merge', () => { { a0: { a1: { a2: { a3: { a4: 'a4a' }, a3a: 'a3a' }, a2a: 'a2a' }, a1a: 'a1a' }, a0a: 'a0a' }, { a0: { a1: { a2: { a3: { a4: 'a4b' }, a3b: 'a3b' }, a2b: 'a2b' }, a1b: 'a1b' }, a0b: 'a0b' }, { - a0: { a1: { a2: { a3: { a4: 'a4a' }, a3b: 'a3a' }, a2b: 'a2b' }, a1b: 'a1b', a1a: 'a1a' }, + a0: { a1: { a2: { a3: { a4: 'a4b' }, a3b: 'a3b' }, a2b: 'a2b' }, a1b: 'a1b', a1a: 'a1a' }, a0b: 'a0b', a0a: 'a0a', },