Skip to content

Commit f9c71ff

Browse files
committed
feat: withScope returns from wrapped function
This is part of a larger work towards better scope management. This change makes Sentry.withScope and Hub.withScope easier to use with existing functions that return a non-void type. The motivation is that withScope could be used to wrap existing functions and run them in a new scope without requiring a closure or mutating state outside of the wrapped function (since it had to be void). While this does change the withScope signature, we're considering it relatively low risk of breaking downstream code as we're making it more general.
1 parent 715a886 commit f9c71ff

File tree

5 files changed

+39
-12
lines changed

5 files changed

+39
-12
lines changed

packages/hub/src/hub.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -148,10 +148,10 @@ export class Hub implements HubInterface {
148148
/**
149149
* @inheritDoc
150150
*/
151-
public withScope(callback: (scope: Scope) => void): void {
151+
public withScope<T>(fn: (scope: Scope) => T): T {
152152
const scope = this.pushScope();
153153
try {
154-
callback(scope);
154+
return fn(scope);
155155
} finally {
156156
this.popScope();
157157
}

packages/hub/test/hub.test.ts

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -121,16 +121,20 @@ describe('Hub', () => {
121121
});
122122

123123
describe('withScope', () => {
124+
let hub: Hub;
125+
126+
beforeEach(() => {
127+
hub = new Hub();
128+
});
129+
124130
test('simple', () => {
125-
const hub = new Hub();
126131
hub.withScope(() => {
127132
expect(hub.getStack()).toHaveLength(2);
128133
});
129134
expect(hub.getStack()).toHaveLength(1);
130135
});
131136

132137
test('bindClient', () => {
133-
const hub = new Hub();
134138
const testClient: any = { bla: 'a' };
135139
hub.withScope(() => {
136140
hub.bindClient(testClient);
@@ -139,6 +143,27 @@ describe('Hub', () => {
139143
});
140144
expect(hub.getStack()).toHaveLength(1);
141145
});
146+
147+
test('should bubble up exceptions', () => {
148+
const error = new Error('test');
149+
expect(() => {
150+
hub.withScope(() => {
151+
throw error;
152+
});
153+
}).toThrow(error);
154+
});
155+
156+
test('should return return value from wrapped function', () => {
157+
// someFn represents an existing function
158+
const someFn = () => {
159+
const hub = getCurrentHub();
160+
hub.setTag('key', 'value');
161+
hub.captureMessage('test');
162+
return 'ok';
163+
};
164+
const value = hub.withScope(someFn); // runs someFn in a new scope
165+
expect(value).toBe('ok');
166+
});
142167
});
143168

144169
test('getCurrentClient', () => {

packages/minimal/src/index.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -170,13 +170,13 @@ export function setUser(user: User | null): void {
170170
* This is essentially a convenience function for:
171171
*
172172
* pushScope();
173-
* callback();
173+
* fn();
174174
* popScope();
175175
*
176-
* @param callback that will be enclosed into push/popScope.
176+
* @param fn wrapped function.
177177
*/
178-
export function withScope(callback: (scope: Scope) => void): void {
179-
callOnHub<void>('withScope', callback);
178+
export function withScope<T>(fn: (scope: Scope) => T): T {
179+
return callOnHub<T>('withScope', fn);
180180
}
181181

182182
/**

packages/minimal/test/lib/minimal.test.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -244,7 +244,7 @@ describe('Minimal', () => {
244244
});
245245

246246
test('withScope', () => {
247-
withScope(scope => {
247+
const value = withScope(scope => {
248248
scope.setLevel(Severity.Warning);
249249
scope.setFingerprint(['1']);
250250
withScope(scope2 => {
@@ -261,8 +261,10 @@ describe('Minimal', () => {
261261
expect(global.__SENTRY__.hub._stack).toHaveLength(3);
262262
});
263263
expect(global.__SENTRY__.hub._stack).toHaveLength(2);
264+
return 'ok';
264265
});
265266
expect(global.__SENTRY__.hub._stack).toHaveLength(1);
267+
expect(value).toBe('ok');
266268
});
267269

268270
test('setExtras', () => {

packages/types/src/hub.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -61,12 +61,12 @@ export interface Hub {
6161
* This is essentially a convenience function for:
6262
*
6363
* pushScope();
64-
* callback();
64+
* fn();
6565
* popScope();
6666
*
67-
* @param callback that will be enclosed into push/popScope.
67+
* @param fn wrapped function.
6868
*/
69-
withScope(callback: (scope: Scope) => void): void;
69+
withScope<T>(fn: (scope: Scope) => T): T;
7070

7171
/** Returns the client of the top stack. */
7272
getClient(): Client | undefined;

0 commit comments

Comments
 (0)