Skip to content

Commit 97db3e4

Browse files
committed
feat(core): Extract scope application to util
1 parent 55ecb4f commit 97db3e4

File tree

9 files changed

+196
-117
lines changed

9 files changed

+196
-117
lines changed

packages/core/src/scope.ts

Lines changed: 48 additions & 85 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import type {
1515
RequestSession,
1616
Scope as ScopeInterface,
1717
ScopeContext,
18+
ScopeData,
1819
Session,
1920
Severity,
2021
SeverityLevel,
@@ -26,6 +27,7 @@ import { arrayify, dateTimestampInSeconds, isPlainObject, uuid4 } from '@sentry/
2627

2728
import { getGlobalEventProcessors, notifyEventProcessors } from './eventProcessors';
2829
import { updateSession } from './session';
30+
import { applyScopeDataToEvent } from './utils/applyScopeDataToEvent';
2931

3032
/**
3133
* Default value for maximum number of breadcrumbs added to an event.
@@ -466,78 +468,65 @@ export class Scope implements ScopeInterface {
466468
return this;
467469
}
468470

471+
/** @inheritDoc */
472+
public getScopeData(): ScopeData {
473+
const {
474+
_breadcrumbs,
475+
_attachments,
476+
_contexts,
477+
_tags,
478+
_extra,
479+
_user,
480+
_level,
481+
_fingerprint,
482+
_eventProcessors,
483+
_propagationContext,
484+
_sdkProcessingMetadata,
485+
_transactionName,
486+
_span,
487+
} = this;
488+
489+
return {
490+
breadcrumbs: _breadcrumbs,
491+
attachments: _attachments,
492+
contexts: _contexts,
493+
tags: _tags,
494+
extra: _extra,
495+
user: _user,
496+
level: _level,
497+
fingerprint: _fingerprint || [],
498+
eventProcessors: _eventProcessors,
499+
propagationContext: _propagationContext,
500+
sdkProcessingMetadata: _sdkProcessingMetadata,
501+
transactionName: _transactionName,
502+
span: _span,
503+
};
504+
}
505+
469506
/**
470507
* Applies data from the scope to the event and runs all event processors on it.
471508
*
472509
* @param event Event
473510
* @param hint Object containing additional information about the original exception, for use by the event processors.
474511
* @hidden
512+
* @deprecated Use `applyScopeDataToEvent()` directly
475513
*/
476514
public applyToEvent(
477515
event: Event,
478516
hint: EventHint = {},
479-
additionalEventProcessors?: EventProcessor[],
517+
additionalEventProcessors: EventProcessor[] = [],
480518
): PromiseLike<Event | null> {
481-
if (this._extra && Object.keys(this._extra).length) {
482-
event.extra = { ...this._extra, ...event.extra };
483-
}
484-
if (this._tags && Object.keys(this._tags).length) {
485-
event.tags = { ...this._tags, ...event.tags };
486-
}
487-
if (this._user && Object.keys(this._user).length) {
488-
event.user = { ...this._user, ...event.user };
489-
}
490-
if (this._contexts && Object.keys(this._contexts).length) {
491-
event.contexts = { ...this._contexts, ...event.contexts };
492-
}
493-
if (this._level) {
494-
event.level = this._level;
495-
}
496-
if (this._transactionName) {
497-
event.transaction = this._transactionName;
498-
}
499-
500-
// We want to set the trace context for normal events only if there isn't already
501-
// a trace context on the event. There is a product feature in place where we link
502-
// errors with transaction and it relies on that.
503-
if (this._span) {
504-
event.contexts = { trace: this._span.getTraceContext(), ...event.contexts };
505-
const transaction = this._span.transaction;
506-
if (transaction) {
507-
event.sdkProcessingMetadata = {
508-
dynamicSamplingContext: transaction.getDynamicSamplingContext(),
509-
...event.sdkProcessingMetadata,
510-
};
511-
const transactionName = transaction.name;
512-
if (transactionName) {
513-
event.tags = { transaction: transactionName, ...event.tags };
514-
}
515-
}
516-
}
517-
518-
this._applyFingerprint(event);
519-
520-
const scopeBreadcrumbs = this._getBreadcrumbs();
521-
const breadcrumbs = [...(event.breadcrumbs || []), ...scopeBreadcrumbs];
522-
event.breadcrumbs = breadcrumbs.length > 0 ? breadcrumbs : undefined;
523-
524-
event.sdkProcessingMetadata = {
525-
...event.sdkProcessingMetadata,
526-
...this._sdkProcessingMetadata,
527-
propagationContext: this._propagationContext,
528-
};
519+
applyScopeDataToEvent(event, this.getScopeData());
529520

530521
// TODO (v8): Update this order to be: Global > Client > Scope
531-
return notifyEventProcessors(
532-
[
533-
...(additionalEventProcessors || []),
534-
// eslint-disable-next-line deprecation/deprecation
535-
...getGlobalEventProcessors(),
536-
...this._eventProcessors,
537-
],
538-
event,
539-
hint,
540-
);
522+
const eventProcessors: EventProcessor[] = [
523+
...additionalEventProcessors,
524+
// eslint-disable-next-line deprecation/deprecation
525+
...getGlobalEventProcessors(),
526+
...this._eventProcessors,
527+
];
528+
529+
return notifyEventProcessors(eventProcessors, event, hint);
541530
}
542531

543532
/**
@@ -564,13 +553,6 @@ export class Scope implements ScopeInterface {
564553
return this._propagationContext;
565554
}
566555

567-
/**
568-
* Get the breadcrumbs for this scope.
569-
*/
570-
protected _getBreadcrumbs(): Breadcrumb[] {
571-
return this._breadcrumbs;
572-
}
573-
574556
/**
575557
* This will be called on every set call.
576558
*/
@@ -586,25 +568,6 @@ export class Scope implements ScopeInterface {
586568
this._notifyingListeners = false;
587569
}
588570
}
589-
590-
/**
591-
* Applies fingerprint from the scope to the event if there's one,
592-
* uses message if there's one instead or get rid of empty fingerprint
593-
*/
594-
private _applyFingerprint(event: Event): void {
595-
// Make sure it's an array first and we actually have something in place
596-
event.fingerprint = event.fingerprint ? arrayify(event.fingerprint) : [];
597-
598-
// If we have something on the scope, then merge it with event
599-
if (this._fingerprint) {
600-
event.fingerprint = event.fingerprint.concat(this._fingerprint);
601-
}
602-
603-
// If we have no data at all, remove empty array default
604-
if (event.fingerprint && !event.fingerprint.length) {
605-
delete event.fingerprint;
606-
}
607-
}
608571
}
609572

610573
function generatePropagationContext(): PropagationContext {
Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
import type { Breadcrumb, Event, PropagationContext, ScopeData, Span } from '@sentry/types';
2+
import { arrayify } from '@sentry/utils';
3+
4+
/**
5+
* Applies data from the scope to the event and runs all event processors on it.
6+
*/
7+
export function applyScopeDataToEvent(event: Event, data: ScopeData): void {
8+
const { fingerprint, span, breadcrumbs, sdkProcessingMetadata, propagationContext } = data;
9+
10+
// Apply general data
11+
applyDataToEvent(event, data);
12+
13+
// We want to set the trace context for normal events only if there isn't already
14+
// a trace context on the event. There is a product feature in place where we link
15+
// errors with transaction and it relies on that.
16+
if (span) {
17+
applySpanToEvent(event, span);
18+
}
19+
20+
applyFingerprintToEvent(event, fingerprint);
21+
applyBreadcrumbsToEvent(event, breadcrumbs);
22+
applySdkMetadataToEvent(event, sdkProcessingMetadata, propagationContext);
23+
}
24+
25+
function applyDataToEvent(event: Event, data: ScopeData): void {
26+
const { extra, tags, user, contexts, level, transactionName } = data;
27+
28+
if (extra && Object.keys(extra).length) {
29+
event.extra = { ...extra, ...event.extra };
30+
}
31+
if (tags && Object.keys(tags).length) {
32+
event.tags = { ...tags, ...event.tags };
33+
}
34+
if (user && Object.keys(user).length) {
35+
event.user = { ...user, ...event.user };
36+
}
37+
if (contexts && Object.keys(contexts).length) {
38+
event.contexts = { ...contexts, ...event.contexts };
39+
}
40+
if (level) {
41+
event.level = level;
42+
}
43+
if (transactionName) {
44+
event.transaction = transactionName;
45+
}
46+
}
47+
48+
function applyBreadcrumbsToEvent(event: Event, breadcrumbs: Breadcrumb[]): void {
49+
const mergedBreadcrumbs = [...(event.breadcrumbs || []), ...breadcrumbs];
50+
event.breadcrumbs = mergedBreadcrumbs.length ? mergedBreadcrumbs : undefined;
51+
}
52+
53+
function applySdkMetadataToEvent(
54+
event: Event,
55+
sdkProcessingMetadata: ScopeData['sdkProcessingMetadata'],
56+
propagationContext: PropagationContext,
57+
): void {
58+
event.sdkProcessingMetadata = {
59+
...event.sdkProcessingMetadata,
60+
...sdkProcessingMetadata,
61+
propagationContext: propagationContext,
62+
};
63+
}
64+
65+
function applySpanToEvent(event: Event, span: Span): void {
66+
event.contexts = { trace: span.getTraceContext(), ...event.contexts };
67+
const transaction = span.transaction;
68+
if (transaction) {
69+
event.sdkProcessingMetadata = {
70+
dynamicSamplingContext: transaction.getDynamicSamplingContext(),
71+
...event.sdkProcessingMetadata,
72+
};
73+
const transactionName = transaction.name;
74+
if (transactionName) {
75+
event.tags = { transaction: transactionName, ...event.tags };
76+
}
77+
}
78+
}
79+
80+
/**
81+
* Applies fingerprint from the scope to the event if there's one,
82+
* uses message if there's one instead or get rid of empty fingerprint
83+
*/
84+
function applyFingerprintToEvent(event: Event, fingerprint: ScopeData['fingerprint'] | undefined): void {
85+
// Make sure it's an array first and we actually have something in place
86+
event.fingerprint = event.fingerprint ? arrayify(event.fingerprint) : [];
87+
88+
// If we have something on the scope, then merge it with event
89+
if (fingerprint) {
90+
event.fingerprint = event.fingerprint.concat(fingerprint);
91+
}
92+
93+
// If we have no data at all, remove empty array default
94+
if (event.fingerprint && !event.fingerprint.length) {
95+
delete event.fingerprint;
96+
}
97+
}

packages/core/src/utils/prepareEvent.ts

Lines changed: 15 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -9,19 +9,12 @@ import type {
99
StackFrame,
1010
StackParser,
1111
} from '@sentry/types';
12-
import {
13-
GLOBAL_OBJ,
14-
addExceptionMechanism,
15-
dateTimestampInSeconds,
16-
normalize,
17-
resolvedSyncPromise,
18-
truncate,
19-
uuid4,
20-
} from '@sentry/utils';
12+
import { GLOBAL_OBJ, addExceptionMechanism, dateTimestampInSeconds, normalize, truncate, uuid4 } from '@sentry/utils';
2113

2214
import { DEFAULT_ENVIRONMENT } from '../constants';
2315
import { getGlobalEventProcessors, notifyEventProcessors } from '../eventProcessors';
2416
import { Scope } from '../scope';
17+
import { applyScopeDataToEvent } from './applyScopeDataToEvent';
2518

2619
/**
2720
* This type makes sure that we get either a CaptureContext, OR an EventHint.
@@ -80,10 +73,13 @@ export function prepareEvent(
8073
addExceptionMechanism(prepared, hint.mechanism);
8174
}
8275

83-
// We prepare the result here with a resolved Event.
84-
let result = resolvedSyncPromise<Event | null>(prepared);
85-
8676
const clientEventProcessors = client && client.getEventProcessors ? client.getEventProcessors() : [];
77+
// TODO (v8): Update this order to be: Global > Client > Scope
78+
const eventProcessors = [
79+
...clientEventProcessors,
80+
// eslint-disable-next-line deprecation/deprecation
81+
...getGlobalEventProcessors(),
82+
];
8783

8884
// This should be the last thing called, since we want that
8985
// {@link Hub.addEventProcessor} gets the finished prepared event.
@@ -102,22 +98,15 @@ export function prepareEvent(
10298
}
10399
}
104100

105-
// In case we have a hub we reassign it.
106-
result = finalScope.applyToEvent(prepared, hint, clientEventProcessors);
107-
} else {
108-
// Apply client & global event processors even if there is no scope
109-
// TODO (v8): Update the order to be Global > Client
110-
result = notifyEventProcessors(
111-
[
112-
...clientEventProcessors,
113-
// eslint-disable-next-line deprecation/deprecation
114-
...getGlobalEventProcessors(),
115-
],
116-
prepared,
117-
hint,
118-
);
101+
const scopeData = finalScope.getScopeData();
102+
applyScopeDataToEvent(prepared, scopeData);
103+
104+
// Run scope event processors _after_ all other processors
105+
eventProcessors.push(...scopeData.eventProcessors);
119106
}
120107

108+
const result = notifyEventProcessors(eventProcessors, prepared, hint);
109+
121110
return result.then(evt => {
122111
if (evt) {
123112
// We apply the debug_meta field only after all event processors have ran, so that if any event processors modified

packages/feedback/test/unit/util/prepareFeedbackEvent.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ describe('Unit | util | prepareFeedbackEvent', () => {
1616
hub.bindClient(client);
1717

1818
client = hub.getClient()!;
19-
scope = hub.getScope()!;
19+
scope = hub.getScope();
2020
});
2121

2222
afterEach(() => {

packages/node/test/integrations/requestdata.test.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,8 @@ describe('`RequestData` integration', () => {
6868

6969
sentryRequestMiddleware(req, res, next);
7070

71-
await getCurrentHub().getScope()!.applyToEvent(event, {});
71+
// eslint-disable-next-line deprecation/deprecation
72+
await getCurrentHub().getScope().applyToEvent(event, {});
7273
requestDataEventProcessor(event);
7374

7475
const passedOptions = addRequestDataToEventSpy.mock.calls[0][2];
@@ -97,7 +98,8 @@ describe('`RequestData` integration', () => {
9798

9899
wrappedGCPFunction(req, res);
99100

100-
await getCurrentHub().getScope()!.applyToEvent(event, {});
101+
// eslint-disable-next-line deprecation/deprecation
102+
await getCurrentHub().getScope().applyToEvent(event, {});
101103
requestDataEventProcessor(event);
102104

103105
const passedOptions = addRequestDataToEventSpy.mock.calls[0][2];

0 commit comments

Comments
 (0)