Skip to content

Commit 5230e7c

Browse files
committed
feat(core): Update & deprecate undefined option handling
1 parent d8d9324 commit 5230e7c

File tree

5 files changed

+227
-12
lines changed

5 files changed

+227
-12
lines changed

packages/core/src/baseclient.ts

+13-1
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ import { dsnToString, makeDsn } from './utils-hoist/dsn';
4646
import { addItemToEnvelope, createAttachmentEnvelopeItem } from './utils-hoist/envelope';
4747
import { SentryError } from './utils-hoist/error';
4848
import { isParameterizedString, isPlainObject, isPrimitive, isThenable } from './utils-hoist/is';
49-
import { logger } from './utils-hoist/logger';
49+
import { consoleSandbox, logger } from './utils-hoist/logger';
5050
import { checkOrSetAlreadyCaught, uuid4 } from './utils-hoist/misc';
5151
import { dropUndefinedKeys } from './utils-hoist/object';
5252
import { SyncPromise, rejectedSyncPromise, resolvedSyncPromise } from './utils-hoist/syncpromise';
@@ -142,6 +142,18 @@ export abstract class BaseClient<O extends ClientOptions> implements Client<O> {
142142
url,
143143
});
144144
}
145+
146+
// TODO(v9): Remove this deprecation warning
147+
const tracingOptions = ['enableTracing', 'tracesSampleRate', 'tracesSampler'] as const;
148+
const undefinedOption = tracingOptions.find(option => option in options && options[option] == undefined);
149+
if (undefinedOption) {
150+
consoleSandbox(() => {
151+
// eslint-disable-next-line no-console
152+
console.warn(
153+
`[Sentry] Deprecation warning: ${undefinedOption} is set to undefined, which leads to tracing being enabled. In v9, you need to set this to 0 to disable tracing.`,
154+
);
155+
});
156+
}
145157
}
146158

147159
/**

packages/core/src/utils/prepareEvent.ts

+12-9
Original file line numberDiff line numberDiff line change
@@ -129,23 +129,26 @@ export function prepareEvent(
129129
}
130130

131131
/**
132-
* Enhances event using the client configuration.
133-
* It takes care of all "static" values like environment, release and `dist`,
134-
* as well as truncating overly long values.
132+
* Enhances event using the client configuration.
133+
* It takes care of all "static" values like environment, release and `dist`,
134+
* as well as truncating overly long values.
135+
*
136+
* Only exported for tests.
137+
*
135138
* @param event event instance to be enhanced
136139
*/
137-
function applyClientOptions(event: Event, options: ClientOptions): void {
140+
export function applyClientOptions(event: Event, options: ClientOptions): void {
138141
const { environment, release, dist, maxValueLength = 250 } = options;
139142

140-
if (!('environment' in event)) {
141-
event.environment = 'environment' in options ? environment : DEFAULT_ENVIRONMENT;
142-
}
143+
// empty strings do not make sense for environment, release, and dist
144+
// so we handle them the same as if they were not provided
145+
event.environment = event.environment || environment || DEFAULT_ENVIRONMENT;
143146

144-
if (event.release === undefined && release !== undefined) {
147+
if (!event.release && release) {
145148
event.release = release;
146149
}
147150

148-
if (event.dist === undefined && dist !== undefined) {
151+
if (!event.dist && dist) {
149152
event.dist = dist;
150153
}
151154

packages/core/test/lib/base.test.ts renamed to packages/core/test/lib/baseclient.test.ts

+60-2
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,56 @@ describe('BaseClient', () => {
7777
});
7878
});
7979

80+
describe('constructor() / warnings', () => {
81+
test('does not warn for defaults', () => {
82+
const consoleWarnSpy = jest.spyOn(console, 'warn').mockImplementation(() => undefined);
83+
84+
const options = getDefaultTestClientOptions({ dsn: PUBLIC_DSN });
85+
new TestClient(options);
86+
87+
expect(consoleWarnSpy).toHaveBeenCalledTimes(0);
88+
consoleWarnSpy.mockRestore();
89+
});
90+
91+
describe.each(['tracesSampleRate', 'tracesSampler', 'enableTracing'])('%s', key => {
92+
it('warns when set to undefined', () => {
93+
const consoleWarnSpy = jest.spyOn(console, 'warn').mockImplementation(() => undefined);
94+
95+
const options = getDefaultTestClientOptions({ dsn: PUBLIC_DSN, [key]: undefined });
96+
new TestClient(options);
97+
98+
expect(consoleWarnSpy).toHaveBeenCalledTimes(1);
99+
expect(consoleWarnSpy).toBeCalledWith(
100+
`[Sentry] Deprecation warning: ${key} is set to undefined, which leads to tracing being enabled. In v9, you need to set this to 0 to disable tracing.`,
101+
);
102+
consoleWarnSpy.mockRestore();
103+
});
104+
105+
it('warns when set to null', () => {
106+
const consoleWarnSpy = jest.spyOn(console, 'warn').mockImplementation(() => undefined);
107+
108+
const options = getDefaultTestClientOptions({ dsn: PUBLIC_DSN, [key]: null });
109+
new TestClient(options);
110+
111+
expect(consoleWarnSpy).toHaveBeenCalledTimes(1);
112+
expect(consoleWarnSpy).toBeCalledWith(
113+
`[Sentry] Deprecation warning: ${key} is set to undefined, which leads to tracing being enabled. In v9, you need to set this to 0 to disable tracing.`,
114+
);
115+
consoleWarnSpy.mockRestore();
116+
});
117+
118+
it('does not warn when set to 0', () => {
119+
const consoleWarnSpy = jest.spyOn(console, 'warn').mockImplementation(() => undefined);
120+
121+
const options = getDefaultTestClientOptions({ dsn: PUBLIC_DSN, [key]: 0 });
122+
new TestClient(options);
123+
124+
expect(consoleWarnSpy).toHaveBeenCalledTimes(0);
125+
consoleWarnSpy.mockRestore();
126+
});
127+
});
128+
});
129+
80130
describe('getOptions()', () => {
81131
test('returns the options', () => {
82132
expect.assertions(1);
@@ -552,7 +602,7 @@ describe('BaseClient', () => {
552602
);
553603
});
554604

555-
test('allows for environment to be explicitly set to falsy value', () => {
605+
test('uses default environment when set to falsy value', () => {
556606
expect.assertions(1);
557607

558608
const options = getDefaultTestClientOptions({ dsn: PUBLIC_DSN, environment: undefined });
@@ -563,7 +613,7 @@ describe('BaseClient', () => {
563613

564614
expect(TestClient.instance!.event!).toEqual(
565615
expect.objectContaining({
566-
environment: undefined,
616+
environment: 'production',
567617
event_id: '42',
568618
message: 'message',
569619
timestamp: 2020,
@@ -1122,6 +1172,8 @@ describe('BaseClient', () => {
11221172
});
11231173

11241174
test('calls `beforeSendSpan` and discards the span', () => {
1175+
const consoleWarnSpy = jest.spyOn(console, 'warn').mockImplementation(() => undefined);
1176+
11251177
const beforeSendSpan = jest.fn(() => null);
11261178
const options = getDefaultTestClientOptions({ dsn: PUBLIC_DSN, beforeSendSpan });
11271179
const client = new TestClient(options);
@@ -1150,6 +1202,12 @@ describe('BaseClient', () => {
11501202
const capturedEvent = TestClient.instance!.event!;
11511203
expect(capturedEvent.spans).toHaveLength(0);
11521204
expect(client['_outcomes']).toEqual({ 'before_send:span': 2 });
1205+
1206+
expect(consoleWarnSpy).toHaveBeenCalledTimes(1);
1207+
expect(consoleWarnSpy).toBeCalledWith(
1208+
'[Sentry] Deprecation warning: Returning null from `beforeSendSpan` will be disallowed from SDK version 9.0.0 onwards. The callback will only support mutating spans. To drop certain spans, configure the respective integrations directly.',
1209+
);
1210+
consoleWarnSpy.mockRestore();
11531211
});
11541212

11551213
test('calls `beforeSend` and logs info about invalid return value', () => {

packages/core/test/lib/prepareEvent.test.ts

+134
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import { GLOBAL_OBJ, createStackParser, getGlobalScope, getIsolationScope } from
1212

1313
import { Scope } from '../../src/scope';
1414
import {
15+
applyClientOptions,
1516
applyDebugIds,
1617
applyDebugMeta,
1718
parseEventHintOrCaptureContext,
@@ -518,3 +519,136 @@ describe('prepareEvent', () => {
518519
});
519520
});
520521
});
522+
523+
describe('applyClientOptions', () => {
524+
it('works with defaults', () => {
525+
const event: Event = {};
526+
const options = {} as ClientOptions;
527+
528+
applyClientOptions(event, options);
529+
530+
expect(event).toEqual({
531+
environment: 'production',
532+
});
533+
534+
// These should not be set at all on the event
535+
expect('release' in event).toBe(false);
536+
expect('dist' in event).toBe(false);
537+
});
538+
539+
it('works with event data and no options', () => {
540+
const event: Event = {
541+
environment: 'blub',
542+
release: 'blab',
543+
dist: 'blib',
544+
};
545+
const options = {} as ClientOptions;
546+
547+
applyClientOptions(event, options);
548+
549+
expect(event).toEqual({
550+
environment: 'blub',
551+
release: 'blab',
552+
dist: 'blib',
553+
});
554+
});
555+
556+
it('event data has precedence over options', () => {
557+
const event: Event = {
558+
environment: 'blub',
559+
release: 'blab',
560+
dist: 'blib',
561+
};
562+
const options = {
563+
environment: 'blub2',
564+
release: 'blab2',
565+
dist: 'blib2',
566+
} as ClientOptions;
567+
568+
applyClientOptions(event, options);
569+
570+
expect(event).toEqual({
571+
environment: 'blub',
572+
release: 'blab',
573+
dist: 'blib',
574+
});
575+
});
576+
577+
it('option data is used if no event data exists', () => {
578+
const event: Event = {};
579+
const options = {
580+
environment: 'blub2',
581+
release: 'blab2',
582+
dist: 'blib2',
583+
} as ClientOptions;
584+
585+
applyClientOptions(event, options);
586+
587+
expect(event).toEqual({
588+
environment: 'blub2',
589+
release: 'blab2',
590+
dist: 'blib2',
591+
});
592+
});
593+
594+
it('option data is ignored if empty string', () => {
595+
const event: Event = {};
596+
const options = {
597+
environment: '',
598+
release: '',
599+
dist: '',
600+
} as ClientOptions;
601+
602+
applyClientOptions(event, options);
603+
604+
expect(event).toEqual({
605+
environment: 'production',
606+
});
607+
608+
// These should not be set at all on the event
609+
expect('release' in event).toBe(false);
610+
expect('dist' in event).toBe(false);
611+
});
612+
613+
it('option data is used if event data is undefined', () => {
614+
const event: Event = {
615+
environment: undefined,
616+
release: undefined,
617+
dist: undefined,
618+
};
619+
const options = {
620+
environment: 'blub2',
621+
release: 'blab2',
622+
dist: 'blib2',
623+
} as ClientOptions;
624+
625+
applyClientOptions(event, options);
626+
627+
expect(event).toEqual({
628+
environment: 'blub2',
629+
release: 'blab2',
630+
dist: 'blib2',
631+
});
632+
});
633+
634+
it('option data is used if event data is empty string', () => {
635+
const event: Event = {
636+
environment: '',
637+
release: '',
638+
dist: '',
639+
};
640+
const options = {
641+
environment: 'blub2',
642+
release: 'blab2',
643+
dist: 'blib2',
644+
} as ClientOptions;
645+
646+
applyClientOptions(event, options);
647+
648+
expect(event).toEqual({
649+
environment: 'blub2',
650+
release: 'blab2',
651+
dist: 'blib2',
652+
});
653+
});
654+
});

packages/core/test/lib/tracing/sentrySpan.test.ts

+8
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,8 @@ describe('SentrySpan', () => {
161161
});
162162

163163
test('does not send the span if `beforeSendSpan` drops the span', () => {
164+
const consoleWarnSpy = jest.spyOn(console, 'warn').mockImplementation(() => undefined);
165+
164166
const beforeSendSpan = jest.fn(() => null);
165167
const client = new TestClient(
166168
getDefaultTestClientOptions({
@@ -185,6 +187,12 @@ describe('SentrySpan', () => {
185187

186188
expect(mockSend).not.toHaveBeenCalled();
187189
expect(recordDroppedEventSpy).toHaveBeenCalledWith('before_send', 'span');
190+
191+
expect(consoleWarnSpy).toHaveBeenCalledTimes(1);
192+
expect(consoleWarnSpy).toBeCalledWith(
193+
'[Sentry] Deprecation warning: Returning null from `beforeSendSpan` will be disallowed from SDK version 9.0.0 onwards. The callback will only support mutating spans. To drop certain spans, configure the respective integrations directly.',
194+
);
195+
consoleWarnSpy.mockRestore();
188196
});
189197
});
190198

0 commit comments

Comments
 (0)