diff --git a/MIGRATION.md b/MIGRATION.md index c5bf86c56706..9a3b1560e696 100644 --- a/MIGRATION.md +++ b/MIGRATION.md @@ -356,7 +356,7 @@ Removed top-level exports: `tracingOrigins`, `MetricsAggregator`, `metricsAggreg `Sentry.configureScope`, `Span`, `spanStatusfromHttpCode`, `makeMain`, `lastEventId`, `pushScope`, `popScope`, `addGlobalEventProcessor`, `timestampWithMs`, `addExtensionMethods` -Remove util exports: `timestampWithMs` +Removed `@sentry/utils` exports: `timestampWithMs`, `addOrUpdateIntegration`, `tracingContextFromHeaders`, `walk` - [Deprecation of `Hub` and `getCurrentHub()`](./MIGRATION.md#deprecate-hub) - [Removal of class-based integrations](./MIGRATION.md#removal-of-class-based-integrations) diff --git a/packages/utils/src/index.ts b/packages/utils/src/index.ts index 5c714e8fbd28..a0649cef48ad 100644 --- a/packages/utils/src/index.ts +++ b/packages/utils/src/index.ts @@ -30,7 +30,6 @@ export * from './clientreport'; export * from './ratelimit'; export * from './baggage'; export * from './url'; -export * from './userIntegrations'; export * from './cache'; export * from './eventbuilder'; export * from './anr'; diff --git a/packages/utils/src/normalize.ts b/packages/utils/src/normalize.ts index 18820ecdc989..064ea0ee934e 100644 --- a/packages/utils/src/normalize.ts +++ b/packages/utils/src/normalize.ts @@ -169,11 +169,6 @@ function visit( return normalized; } -/** - * @deprecated This export will be removed in v8. - */ -export { visit as walk }; - /* eslint-disable complexity */ /** * Stringify the given value. Handles various known special values and types. diff --git a/packages/utils/src/tracing.ts b/packages/utils/src/tracing.ts index a687a68fa36b..def5194bbe6e 100644 --- a/packages/utils/src/tracing.ts +++ b/packages/utils/src/tracing.ts @@ -43,49 +43,6 @@ export function extractTraceparentData(traceparent?: string): TraceparentData | }; } -/** - * Create tracing context from incoming headers. - * - * @deprecated Use `propagationContextFromHeaders` instead. - */ -// TODO(v8): Remove this function -export function tracingContextFromHeaders( - sentryTrace: Parameters[0], - baggage: Parameters[0], -): { - traceparentData: ReturnType; - dynamicSamplingContext: ReturnType; - propagationContext: PropagationContext; -} { - const traceparentData = extractTraceparentData(sentryTrace); - const dynamicSamplingContext = baggageHeaderToDynamicSamplingContext(baggage); - - const { traceId, parentSpanId, parentSampled } = traceparentData || {}; - - if (!traceparentData) { - return { - traceparentData, - dynamicSamplingContext: undefined, - propagationContext: { - traceId: traceId || uuid4(), - spanId: uuid4().substring(16), - }, - }; - } else { - return { - traceparentData, - dynamicSamplingContext: dynamicSamplingContext || {}, // If we have traceparent data but no DSC it means we are not head of trace and we must freeze it - propagationContext: { - traceId: traceId || uuid4(), - parentSpanId: parentSpanId || uuid4().substring(16), - spanId: uuid4().substring(16), - sampled: parentSampled, - dsc: dynamicSamplingContext || {}, // If we have traceparent data but no DSC it means we are not head of trace and we must freeze it - }, - }; - } -} - /** * Create a propagation context from incoming headers. */ diff --git a/packages/utils/src/userIntegrations.ts b/packages/utils/src/userIntegrations.ts deleted file mode 100644 index 345c43ef04d7..000000000000 --- a/packages/utils/src/userIntegrations.ts +++ /dev/null @@ -1,115 +0,0 @@ -import type { Integration } from '@sentry/types'; - -export type UserIntegrationsFunction = (integrations: Integration[]) => Integration[]; -export type UserIntegrations = Integration[] | UserIntegrationsFunction; -export type IntegrationWithExclusionOption = Integration & { - /** - * Allow the user to exclude this integration by not returning it from a function provided as the `integrations` option - * in `Sentry.init()`. Meant to be used with default integrations, the idea being that if a user has actively filtered - * an integration out, we should be able to respect that choice if we wish. - */ - allowExclusionByUser?: boolean; -}; - -type ForcedIntegrationOptions = { - [keyPath: string]: unknown; -}; - -/** - * Recursively traverses an object to update an existing nested key. - * Note: The provided key path must include existing properties, - * the function will not create objects while traversing. - * - * @param obj An object to update - * @param value The value to update the nested key with - * @param keyPath The path to the key to update ex. fizz.buzz.foo - */ -// eslint-disable-next-line @typescript-eslint/no-explicit-any -function setNestedKey(obj: Record, keyPath: string, value: unknown): void { - // Ex. foo.bar.zoop will extract foo and bar.zoop - const match = keyPath.match(/([a-z_]+)\.(.*)/i); - // The match will be null when there's no more recursing to do, i.e., when we've reached the right level of the object - if (match === null) { - obj[keyPath] = value; - } else { - // `match[1]` is the initial segment of the path, and `match[2]` is the remainder of the path - const innerObj = obj[match[1]]; - setNestedKey(innerObj, match[2], value); - } -} - -/** - * Enforces inclusion of a given integration with specified options in an integration array originally determined by the - * user, by either including the given default instance or by patching an existing user instance with the given options. - * - * Ideally this would happen when integrations are set up, but there isn't currently a mechanism there for merging - * options from a default integration instance with those from a user-provided instance of the same integration, only - * for allowing the user to override a default instance entirely. (TODO: Fix that.) - * - * @param defaultIntegrationInstance An instance of the integration with the correct options already set - * @param userIntegrations Integrations defined by the user. - * @param forcedOptions Options with which to patch an existing user-derived instance on the integration. - * @returns A final integrations array. - * - * @deprecated This will be removed in v8. - */ -export function addOrUpdateIntegration( - defaultIntegrationInstance: Integration, - userIntegrations: T, - forcedOptions: ForcedIntegrationOptions = {}, -): T { - return ( - Array.isArray(userIntegrations) - ? addOrUpdateIntegrationInArray(defaultIntegrationInstance, userIntegrations, forcedOptions) - : addOrUpdateIntegrationInFunction( - defaultIntegrationInstance, - // Somehow TS can't figure out that not being an array makes this necessarily a function - userIntegrations as UserIntegrationsFunction, - forcedOptions, - ) - ) as T; -} - -function addOrUpdateIntegrationInArray( - defaultIntegrationInstance: Integration, - userIntegrations: Integration[], - forcedOptions: ForcedIntegrationOptions, -): Integration[] { - const userInstance = userIntegrations.find(integration => integration.name === defaultIntegrationInstance.name); - - if (userInstance) { - for (const [keyPath, value] of Object.entries(forcedOptions)) { - setNestedKey(userInstance, keyPath, value); - } - - return userIntegrations; - } - - return [...userIntegrations, defaultIntegrationInstance]; -} - -function addOrUpdateIntegrationInFunction( - defaultIntegrationInstance: IntegrationWithExclusionOption, - userIntegrationsFunc: UserIntegrationsFunction, - forcedOptions: ForcedIntegrationOptions, -): UserIntegrationsFunction { - const wrapper: UserIntegrationsFunction = defaultIntegrations => { - const userFinalIntegrations = userIntegrationsFunc(defaultIntegrations); - - // There are instances where we want the user to be able to prevent an integration from appearing at all, which they - // would do by providing a function which filters out the integration in question. If that's happened in one of - // those cases, don't add our default back in. - if (defaultIntegrationInstance.allowExclusionByUser) { - const userFinalInstance = userFinalIntegrations.find( - integration => integration.name === defaultIntegrationInstance.name, - ); - if (!userFinalInstance) { - return userFinalIntegrations; - } - } - - return addOrUpdateIntegrationInArray(defaultIntegrationInstance, userFinalIntegrations, forcedOptions); - }; - - return wrapper; -} diff --git a/packages/utils/test/tracing.test.ts b/packages/utils/test/tracing.test.ts index 070b7e8c058e..399c37104445 100644 --- a/packages/utils/test/tracing.test.ts +++ b/packages/utils/test/tracing.test.ts @@ -1,17 +1,8 @@ -import { extractTraceparentData, propagationContextFromHeaders, tracingContextFromHeaders } from '../src/tracing'; +import { extractTraceparentData, propagationContextFromHeaders } from '../src/tracing'; const EXAMPLE_SENTRY_TRACE = '12312012123120121231201212312012-1121201211212012-1'; const EXAMPLE_BAGGAGE = 'sentry-release=1.2.3,sentry-foo=bar,other=baz'; -describe('tracingContextFromHeaders()', () => { - it('should produce a frozen baggage (empty object) when there is an incoming trace but no baggage header', () => { - // eslint-disable-next-line deprecation/deprecation - const tracingContext = tracingContextFromHeaders('12312012123120121231201212312012-1121201211212012-1', undefined); - expect(tracingContext.dynamicSamplingContext).toEqual({}); - expect(tracingContext.propagationContext.dsc).toEqual({}); - }); -}); - describe('propagationContextFromHeaders()', () => { it('returns a completely new propagation context when no sentry-trace data is given but baggage data is given', () => { const result = propagationContextFromHeaders(undefined, undefined); diff --git a/packages/utils/test/userIntegrations.test.ts b/packages/utils/test/userIntegrations.test.ts deleted file mode 100644 index 96782fc414d6..000000000000 --- a/packages/utils/test/userIntegrations.test.ts +++ /dev/null @@ -1,217 +0,0 @@ -import type { IntegrationWithExclusionOption as Integration, UserIntegrations } from '../src/userIntegrations'; -import { addOrUpdateIntegration } from '../src/userIntegrations'; - -type MockIntegrationOptions = { - name: string; - descriptor: string; - age?: number; -}; - -class DogIntegration implements Integration { - public static id: string = 'Dog'; - public name: string = DogIntegration.id; - - public dogName: string; - public descriptor: string; - public age?: number; - - public allowExclusionByUser?: boolean; - - constructor(options: MockIntegrationOptions) { - this.dogName = options.name; - this.descriptor = options.descriptor; - this.age = options.age; - } - - setupOnce() { - return undefined; - } -} - -class CatIntegration implements Integration { - public static id: string = 'Cat'; - public name: string = CatIntegration.id; - - public catName: string; - public descriptor: string; - public age?: number; - - constructor(options: MockIntegrationOptions) { - this.catName = options.name; - this.descriptor = options.descriptor; - this.age = options.age; - } - - setupOnce() { - return undefined; - } -} - -const defaultDogIntegration = new DogIntegration({ name: 'Maisey', descriptor: 'silly' }); -const defaultCatIntegration = new CatIntegration({ name: 'Piper', descriptor: 'fluffy' }); -const forcedDogIntegration = new DogIntegration({ name: 'Charlie', descriptor: 'goofy' }); -const forcedDogIntegrationProperties = { dogName: 'Charlie', descriptor: 'goofy' }; - -// Note: This is essentially the implementation of a `test.each()` test. Making it a separate function called in -// individual tests instead allows the various `describe` clauses to be nested, which is helpful here given how many -// different combinations of factors come into play. -function runTest(testOptions: { - userIntegrations: UserIntegrations; - forcedDogIntegrationInstance: DogIntegration; - underlyingDefaultIntegrations?: Integration[]; - allowIntegrationExclusion?: boolean; - expectedDogIntegrationProperties: Partial | undefined; -}): void { - const { - userIntegrations, - forcedDogIntegrationInstance, - underlyingDefaultIntegrations = [], - allowIntegrationExclusion = false, - expectedDogIntegrationProperties, - } = testOptions; - - if (allowIntegrationExclusion) { - forcedDogIntegrationInstance.allowExclusionByUser = true; - } - - let integrations; - if (typeof userIntegrations === 'function') { - // eslint-disable-next-line deprecation/deprecation - const wrappedUserIntegrationsFunction = addOrUpdateIntegration(forcedDogIntegrationInstance, userIntegrations, { - dogName: 'Charlie', - descriptor: 'goofy', - }); - integrations = wrappedUserIntegrationsFunction(underlyingDefaultIntegrations); - } else { - // eslint-disable-next-line deprecation/deprecation - integrations = addOrUpdateIntegration( - forcedDogIntegrationInstance, - userIntegrations, - forcedDogIntegrationProperties, - ); - } - - const finalDogIntegrationInstance = integrations.find(integration => integration.name === 'Dog') as DogIntegration; - - if (expectedDogIntegrationProperties) { - expect(finalDogIntegrationInstance).toMatchObject(expectedDogIntegrationProperties); - } else { - expect(finalDogIntegrationInstance).toBeUndefined(); - } - - delete forcedDogIntegrationInstance.allowExclusionByUser; -} - -describe('addOrUpdateIntegration()', () => { - describe('user provides no `integrations` option', () => { - it('adds forced integration instance', () => { - expect.assertions(1); - - runTest({ - userIntegrations: [], // default if no option is provided - forcedDogIntegrationInstance: forcedDogIntegration, - expectedDogIntegrationProperties: forcedDogIntegrationProperties, - }); - }); - }); - - describe('user provides `integrations` array', () => { - describe('array contains forced integration type', () => { - it('updates user instance with forced options', () => { - expect.assertions(1); - - runTest({ - userIntegrations: [{ ...defaultDogIntegration, age: 9 } as unknown as Integration], - forcedDogIntegrationInstance: forcedDogIntegration, - expectedDogIntegrationProperties: { ...forcedDogIntegrationProperties, age: 9 }, - }); - }); - }); - - describe('array does not contain forced integration type', () => { - it('adds forced integration instance', () => { - expect.assertions(1); - - runTest({ - userIntegrations: [defaultCatIntegration], - forcedDogIntegrationInstance: forcedDogIntegration, - expectedDogIntegrationProperties: forcedDogIntegrationProperties, - }); - }); - }); - }); - - describe('user provides `integrations` function', () => { - describe('forced integration in `defaultIntegrations`', () => { - const underlyingDefaultIntegrations = [defaultDogIntegration, defaultCatIntegration]; - - describe('function filters out forced integration type', () => { - it('adds forced integration instance by default', () => { - expect.assertions(1); - - runTest({ - userIntegrations: _defaults => [defaultCatIntegration], - forcedDogIntegrationInstance: forcedDogIntegration, - underlyingDefaultIntegrations, - expectedDogIntegrationProperties: forcedDogIntegrationProperties, - }); - }); - - it('does not add forced integration instance if integration exclusion is allowed', () => { - expect.assertions(1); - - runTest({ - userIntegrations: _defaults => [defaultCatIntegration], - forcedDogIntegrationInstance: forcedDogIntegration, - underlyingDefaultIntegrations, - allowIntegrationExclusion: true, - expectedDogIntegrationProperties: undefined, // this means no instance was found - }); - }); - }); - - describe("function doesn't filter out forced integration type", () => { - it('updates user instance with forced options', () => { - expect.assertions(1); - - runTest({ - userIntegrations: _defaults => [{ ...defaultDogIntegration, age: 9 } as unknown as Integration], - forcedDogIntegrationInstance: forcedDogIntegration, - underlyingDefaultIntegrations, - expectedDogIntegrationProperties: { ...forcedDogIntegrationProperties, age: 9 }, - }); - }); - }); - }); - - describe('forced integration not in `defaultIntegrations`', () => { - const underlyingDefaultIntegrations = [defaultCatIntegration]; - - describe('function returns forced integration type', () => { - it('updates user instance with forced options', () => { - expect.assertions(1); - - runTest({ - userIntegrations: _defaults => [{ ...defaultDogIntegration, age: 9 } as unknown as Integration], - forcedDogIntegrationInstance: forcedDogIntegration, - underlyingDefaultIntegrations, - expectedDogIntegrationProperties: { ...forcedDogIntegrationProperties, age: 9 }, - }); - }); - }); - - describe("function doesn't return forced integration type", () => { - it('adds forced integration instance', () => { - expect.assertions(1); - - runTest({ - userIntegrations: _defaults => [{ ...defaultCatIntegration, age: 1 } as unknown as Integration], - forcedDogIntegrationInstance: forcedDogIntegration, - underlyingDefaultIntegrations, - expectedDogIntegrationProperties: forcedDogIntegrationProperties, - }); - }); - }); - }); - }); -});