Skip to content

Commit f78d501

Browse files
committed
Add requestAccepted and requestRejected events
1 parent ecb36ec commit f78d501

File tree

2 files changed

+128
-23
lines changed

2 files changed

+128
-23
lines changed

src/approval/ApprovalController.test.ts

Lines changed: 77 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -509,12 +509,37 @@ describe('approval controller', () => {
509509
});
510510

511511
describe('accept and reject', () => {
512-
it('accepts and rejects multiple approval promises out of order', async () => {
512+
it('accepts and rejects multiple approval promises out of order, and emits events', async () => {
513+
const messenger = new ControllerMessenger<
514+
ApprovalControllerActions,
515+
ApprovalControllerEvents
516+
>();
517+
518+
const restrictedMessenger = messenger.getRestricted<
519+
typeof controllerName,
520+
never,
521+
never
522+
>({
523+
name: 'ApprovalController',
524+
});
525+
513526
const approvalController = new ApprovalController({
514-
messenger: getRestrictedMessenger(),
527+
messenger: restrictedMessenger,
515528
showApprovalRequest: sinon.spy(),
516529
});
517530

531+
const acceptListener = jest.fn();
532+
const rejectListener = jest.fn();
533+
534+
messenger.subscribe(
535+
`${controllerName}:requestAccepted` as const,
536+
acceptListener,
537+
);
538+
messenger.subscribe(
539+
`${controllerName}:requestRejected` as const,
540+
rejectListener,
541+
);
542+
518543
const promise1 = approvalController.add({
519544
id: 'foo1',
520545
origin: 'bar.baz',
@@ -560,6 +585,56 @@ describe('approval controller', () => {
560585
expect(approvalController.has({ origin: 'bar.baz' })).toStrictEqual(
561586
false,
562587
);
588+
589+
expect(acceptListener).toHaveBeenCalledTimes(2);
590+
expect(acceptListener).toHaveBeenNthCalledWith(
591+
1,
592+
{
593+
id: expect.any(String),
594+
time: expect.any(Number),
595+
origin: 'bar.baz',
596+
type: 'myType2',
597+
requestData: null,
598+
},
599+
'success2',
600+
);
601+
602+
expect(acceptListener).toHaveBeenNthCalledWith(
603+
2,
604+
{
605+
id: expect.any(String),
606+
time: expect.any(Number),
607+
origin: 'bar.baz',
608+
type: TYPE,
609+
requestData: null,
610+
},
611+
'success1',
612+
);
613+
614+
expect(rejectListener).toHaveBeenCalledTimes(2);
615+
expect(rejectListener).toHaveBeenNthCalledWith(
616+
1,
617+
{
618+
id: expect.any(String),
619+
time: expect.any(Number),
620+
origin: 'bar.baz',
621+
type: 'myType4',
622+
requestData: null,
623+
},
624+
new Error('failure4'),
625+
);
626+
627+
expect(rejectListener).toHaveBeenNthCalledWith(
628+
2,
629+
{
630+
id: expect.any(String),
631+
time: expect.any(Number),
632+
origin: 'fizz.buzz',
633+
type: TYPE,
634+
requestData: null,
635+
},
636+
new Error('failure3'),
637+
);
563638
});
564639
});
565640

src/approval/ApprovalController.ts

Lines changed: 51 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,20 @@ export type ApprovalStateChange = {
120120
payload: [ApprovalControllerState, Patch[]];
121121
};
122122

123-
export type ApprovalControllerEvents = ApprovalStateChange;
123+
export type ApprovalRequestAccepted = {
124+
type: `${typeof controllerName}:requestAccepted`;
125+
payload: [ApprovalRequest<Record<string, Json>>, unknown];
126+
};
127+
128+
export type ApprovalRequestRejected = {
129+
type: `${typeof controllerName}:requestRejected`;
130+
payload: [ApprovalRequest<Record<string, Json>>, Error];
131+
};
132+
133+
export type ApprovalControllerEvents =
134+
| ApprovalStateChange
135+
| ApprovalRequestAccepted
136+
| ApprovalRequestRejected;
124137

125138
export type ApprovalControllerMessenger = RestrictedControllerMessenger<
126139
typeof controllerName,
@@ -421,7 +434,13 @@ export class ApprovalController extends BaseController<
421434
* @param value - The value to resolve the approval promise with.
422435
*/
423436
accept(id: string, value?: unknown): void {
424-
this._deleteApprovalAndGetCallbacks(id).resolve(value);
437+
const [approvalRequest, callbacks] = this._deleteApprovalAndGetData(id);
438+
callbacks.resolve(value);
439+
this.messagingSystem.publish(
440+
`${controllerName}:requestAccepted` as const,
441+
approvalRequest,
442+
value,
443+
);
425444
}
426445

427446
/**
@@ -432,7 +451,13 @@ export class ApprovalController extends BaseController<
432451
* @param error - The error to reject the approval promise with.
433452
*/
434453
reject(id: string, error: Error): void {
435-
this._deleteApprovalAndGetCallbacks(id).reject(error);
454+
const [approvalRequest, callbacks] = this._deleteApprovalAndGetData(id);
455+
callbacks.reject(error);
456+
this.messagingSystem.publish(
457+
`${controllerName}:requestRejected` as const,
458+
approvalRequest,
459+
error,
460+
);
436461
}
437462

438463
/**
@@ -564,6 +589,29 @@ export class ApprovalController extends BaseController<
564589
});
565590
}
566591

592+
/**
593+
* Gets the approval callbacks for the given id, deletes the entry, and then
594+
* returns the callbacks for promise resolution.
595+
* Throws an error if no approval is found for the given id.
596+
*
597+
* @param id - The id of the approval request.
598+
* @returns The promise callbacks associated with the approval request.
599+
*/
600+
private _deleteApprovalAndGetData(
601+
id: string,
602+
): [ApprovalRequest<Record<string, Json>>, ApprovalCallbacks] {
603+
const callbacks = this._approvals.get(id);
604+
if (!callbacks) {
605+
throw new Error(`Approval request with id '${id}' not found.`);
606+
}
607+
608+
// We've already verified that the approval exists.
609+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
610+
const approvalRequest = this.state.pendingApprovals[id]!;
611+
this._delete(id);
612+
return [approvalRequest, callbacks];
613+
}
614+
567615
/**
568616
* Deletes the approval with the given id. The approval promise must be
569617
* resolved or reject before this method is called.
@@ -593,24 +641,6 @@ export class ApprovalController extends BaseController<
593641
});
594642
}
595643

596-
/**
597-
* Gets the approval callbacks for the given id, deletes the entry, and then
598-
* returns the callbacks for promise resolution.
599-
* Throws an error if no approval is found for the given id.
600-
*
601-
* @param id - The id of the approval request.
602-
* @returns The promise callbacks associated with the approval request.
603-
*/
604-
private _deleteApprovalAndGetCallbacks(id: string): ApprovalCallbacks {
605-
const callbacks = this._approvals.get(id);
606-
if (!callbacks) {
607-
throw new Error(`Approval request with id '${id}' not found.`);
608-
}
609-
610-
this._delete(id);
611-
return callbacks;
612-
}
613-
614644
/**
615645
* Checks whether there are any approvals associated with the given
616646
* origin.

0 commit comments

Comments
 (0)