Skip to content

Commit 9dfc939

Browse files
committed
preserve hook context reference across hooks
Signed-off-by: Adam Wootton <[email protected]>
1 parent 24b477a commit 9dfc939

File tree

3 files changed

+29
-25
lines changed

3 files changed

+29
-25
lines changed

libs/providers/multi-provider/src/lib/hook-executor.ts

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,18 +7,17 @@ export class HookExecutor {
77
constructor(private logger: Logger) {}
88

99
async beforeHooks(hooks: Hook[] | undefined, hookContext: HookContext, hints: HookHints) {
10-
const newContext = { ...hookContext.context };
1110
for (const hook of hooks ?? []) {
1211
// freeze the hookContext
1312
Object.freeze(hookContext);
1413

15-
Object.assign(newContext, {
14+
Object.assign(hookContext.context, {
1615
...(await hook?.before?.(hookContext, Object.freeze(hints))),
1716
});
1817
}
1918

2019
// after before hooks, freeze the EvaluationContext.
21-
return Object.freeze(newContext);
20+
return Object.freeze(hookContext.context);
2221
}
2322

2423
async afterHooks(

libs/providers/multi-provider/src/lib/multi-provider.spec.ts

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -267,11 +267,14 @@ describe('MultiProvider', () => {
267267
logger: logger,
268268
};
269269

270+
let weakMap = new WeakMap();
271+
270272
provider1.hooks = [
271273
{
272274
before: async (context) => {
273275
hook1Called = true;
274-
expect(context).toBe(hookContext);
276+
expect(context).toEqual(hookContext);
277+
weakMap.set(context, 'test');
275278
return { ...context.context, hook1: true };
276279
},
277280
after: async (context) => {
@@ -280,13 +283,18 @@ describe('MultiProvider', () => {
280283
hook1: true,
281284
hook2: true,
282285
});
286+
expect(weakMap.get(context)).toEqual('test');
283287
after1Called = true;
284288
},
285289
},
286290
{
287291
before: async (context) => {
288292
hook2Called = true;
289-
expect(context).toBe(hookContext);
293+
expect(weakMap.get(context)).toEqual('test');
294+
expect(context.context).toEqual({
295+
test: true,
296+
hook1: true,
297+
});
290298
return { ...context.context, hook2: true };
291299
},
292300
},
@@ -295,6 +303,7 @@ describe('MultiProvider', () => {
295303
provider2.hooks = [
296304
{
297305
after: async (context) => {
306+
expect(weakMap.get(context)).toBeFalsy();
298307
expect(context.context).toEqual({
299308
test: true,
300309
});
@@ -339,7 +348,7 @@ describe('MultiProvider', () => {
339348
expect(after2Called).toBe(true);
340349
});
341350

342-
it('runs error hook and finally hook with modified context', async () => {
351+
it('runs error hook and finally hook with modified context using same object reference', async () => {
343352
const provider1 = new TestProvider();
344353
let hook1Called = false;
345354
let error1Called = false;
@@ -359,18 +368,22 @@ describe('MultiProvider', () => {
359368
logger: logger,
360369
};
361370

371+
let weakMap = new WeakMap();
372+
362373
provider1.hooks = [
363374
{
364375
before: async (context) => {
365376
hook1Called = true;
366-
expect(context).toBe(hookContext);
377+
weakMap.set(context, 'exists');
378+
expect(context).toEqual(hookContext);
367379
return { ...context.context, hook1: true };
368380
},
369381
error: async (context) => {
370382
expect(context.context).toEqual({
371383
test: true,
372384
hook1: true,
373385
});
386+
expect(weakMap.get(context)).toEqual('exists');
374387
error1Called = true;
375388
throw new Error('error hook error');
376389
},
@@ -379,6 +392,7 @@ describe('MultiProvider', () => {
379392
test: true,
380393
hook1: true,
381394
});
395+
expect(weakMap.get(context)).toEqual('exists');
382396
finally1Called = true;
383397
},
384398
},
@@ -392,6 +406,7 @@ describe('MultiProvider', () => {
392406

393407
provider1.resolveBooleanEvaluation.mockRejectedValue(new Error('test error'));
394408

409+
// call the multiprovider before hook to set up the hookcontext
395410
await multiProvider.hooks[0].before!(hookContext);
396411
await expect(() => multiProvider.resolveBooleanEvaluation('flag', false, context)).rejects.toThrow();
397412
expect(hook1Called).toBe(true);

libs/providers/multi-provider/src/lib/multi-provider.ts

Lines changed: 8 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -241,8 +241,12 @@ export class MultiProvider implements Provider {
241241
let providerContext: EvaluationContext | undefined = undefined;
242242
let evaluationResult: ResolutionDetails<T>;
243243

244+
// create a copy of the shared hook context because we're going to mutate the evaluation context
245+
const hookContextCopy = { ...hookContext, context: { ...hookContext.context } };
246+
244247
try {
245-
providerContext = await this.hookExecutor.beforeHooks(provider.hooks, hookContext, hookHints);
248+
// return the modified provider context and mutate the hook context to contain it
249+
providerContext = await this.hookExecutor.beforeHooks(provider.hooks, hookContextCopy, hookHints);
246250

247251
evaluationResult = (await this.callProviderResolve(
248252
provider,
@@ -257,27 +261,13 @@ export class MultiProvider implements Provider {
257261
flagKey,
258262
};
259263

260-
await this.hookExecutor.afterHooks(
261-
provider.hooks,
262-
{ ...hookContext, context: providerContext },
263-
afterHookEvalDetails,
264-
hookHints,
265-
);
264+
await this.hookExecutor.afterHooks(provider.hooks, hookContextCopy, afterHookEvalDetails, hookHints);
266265
return evaluationResult;
267266
} catch (error: unknown) {
268-
await this.hookExecutor.errorHooks(
269-
provider.hooks,
270-
{ ...hookContext, context: providerContext ?? hookContext.context },
271-
error,
272-
hookHints,
273-
);
267+
await this.hookExecutor.errorHooks(provider.hooks, hookContextCopy, error, hookHints);
274268
throw error;
275269
} finally {
276-
await this.hookExecutor.finallyHooks(
277-
provider.hooks,
278-
{ ...hookContext, context: providerContext ?? hookContext.context },
279-
hookHints,
280-
);
270+
await this.hookExecutor.finallyHooks(provider.hooks, hookContextCopy, hookHints);
281271
}
282272
}
283273

0 commit comments

Comments
 (0)