[PM-6426] Attempting to re-work implementation to facilitate userId-spcific alarms
This commit is contained in:
parent
6cb2577f90
commit
6beab4fbd2
|
@ -2,7 +2,7 @@ import { TaskSchedulerService } from "@bitwarden/common/platform/abstractions/ta
|
||||||
import { ScheduledTaskName } from "@bitwarden/common/platform/enums/scheduled-task-name.enum";
|
import { ScheduledTaskName } from "@bitwarden/common/platform/enums/scheduled-task-name.enum";
|
||||||
|
|
||||||
export type ActiveAlarm = {
|
export type ActiveAlarm = {
|
||||||
name: ScheduledTaskName;
|
taskName: ScheduledTaskName;
|
||||||
startTime: number;
|
startTime: number;
|
||||||
createInfo: chrome.alarms.AlarmCreateInfo;
|
createInfo: chrome.alarms.AlarmCreateInfo;
|
||||||
};
|
};
|
||||||
|
|
|
@ -30,15 +30,15 @@ describe("BrowserTaskSchedulerService", () => {
|
||||||
jest.useFakeTimers();
|
jest.useFakeTimers();
|
||||||
activeAlarms = [
|
activeAlarms = [
|
||||||
mock<ActiveAlarm>({
|
mock<ActiveAlarm>({
|
||||||
name: ScheduledTaskNames.eventUploadsInterval,
|
taskName: ScheduledTaskNames.eventUploadsInterval,
|
||||||
createInfo: eventUploadsIntervalCreateInfo,
|
createInfo: eventUploadsIntervalCreateInfo,
|
||||||
}),
|
}),
|
||||||
mock<ActiveAlarm>({
|
mock<ActiveAlarm>({
|
||||||
name: ScheduledTaskNames.scheduleNextSyncInterval,
|
taskName: ScheduledTaskNames.scheduleNextSyncInterval,
|
||||||
createInfo: scheduleNextSyncIntervalCreateInfo,
|
createInfo: scheduleNextSyncIntervalCreateInfo,
|
||||||
}),
|
}),
|
||||||
mock<ActiveAlarm>({
|
mock<ActiveAlarm>({
|
||||||
name: ScheduledTaskNames.fido2ClientAbortTimeout,
|
taskName: ScheduledTaskNames.fido2ClientAbortTimeout,
|
||||||
startTime: Date.now() - 60001,
|
startTime: Date.now() - 60001,
|
||||||
createInfo: { delayInMinutes: 1, periodInMinutes: undefined },
|
createInfo: { delayInMinutes: 1, periodInMinutes: undefined },
|
||||||
}),
|
}),
|
||||||
|
@ -131,7 +131,9 @@ describe("BrowserTaskSchedulerService", () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it("triggers a recovered alarm immediately and skips creating the alarm", async () => {
|
it("triggers a recovered alarm immediately and skips creating the alarm", async () => {
|
||||||
activeAlarms = [mock<ActiveAlarm>({ name: ScheduledTaskNames.loginStrategySessionTimeout })];
|
activeAlarms = [
|
||||||
|
mock<ActiveAlarm>({ taskName: ScheduledTaskNames.loginStrategySessionTimeout }),
|
||||||
|
];
|
||||||
browserTaskSchedulerService["recoveredAlarms"].add(
|
browserTaskSchedulerService["recoveredAlarms"].add(
|
||||||
ScheduledTaskNames.loginStrategySessionTimeout,
|
ScheduledTaskNames.loginStrategySessionTimeout,
|
||||||
);
|
);
|
||||||
|
@ -234,7 +236,9 @@ describe("BrowserTaskSchedulerService", () => {
|
||||||
|
|
||||||
it("triggers a recovered alarm before creating the interval alarm", async () => {
|
it("triggers a recovered alarm before creating the interval alarm", async () => {
|
||||||
const periodInMinutes = 4;
|
const periodInMinutes = 4;
|
||||||
activeAlarms = [mock<ActiveAlarm>({ name: ScheduledTaskNames.loginStrategySessionTimeout })];
|
activeAlarms = [
|
||||||
|
mock<ActiveAlarm>({ taskName: ScheduledTaskNames.loginStrategySessionTimeout }),
|
||||||
|
];
|
||||||
browserTaskSchedulerService["recoveredAlarms"].add(
|
browserTaskSchedulerService["recoveredAlarms"].add(
|
||||||
ScheduledTaskNames.loginStrategySessionTimeout,
|
ScheduledTaskNames.loginStrategySessionTimeout,
|
||||||
);
|
);
|
||||||
|
|
|
@ -132,21 +132,26 @@ export class BrowserTaskSchedulerService
|
||||||
/**
|
/**
|
||||||
* Creates a browser extension alarm with the given name and create info.
|
* Creates a browser extension alarm with the given name and create info.
|
||||||
*
|
*
|
||||||
* @param name - The name of the alarm.
|
* @param taskName - The name of the alarm.
|
||||||
* @param createInfo - The alarm create info.
|
* @param createInfo - The alarm create info.
|
||||||
*/
|
*/
|
||||||
private async scheduleAlarm(
|
private async scheduleAlarm(
|
||||||
name: ScheduledTaskName,
|
taskName: ScheduledTaskName,
|
||||||
createInfo: chrome.alarms.AlarmCreateInfo,
|
createInfo: chrome.alarms.AlarmCreateInfo,
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
const existingAlarm = await this.getAlarm(name);
|
const existingAlarm = await this.getAlarm(taskName);
|
||||||
if (existingAlarm) {
|
if (existingAlarm) {
|
||||||
this.logService.warning(`Alarm ${name} already exists. Skipping creation.`);
|
this.logService.warning(`Alarm ${taskName} already exists. Skipping creation.`);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
await this.createAlarm(name, createInfo);
|
await this.createAlarm(taskName, createInfo);
|
||||||
await this.setActiveAlarm({ name, startTime: Date.now(), createInfo });
|
|
||||||
|
await this.setActiveAlarm({
|
||||||
|
taskName,
|
||||||
|
startTime: Date.now(),
|
||||||
|
createInfo,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -158,8 +163,8 @@ export class BrowserTaskSchedulerService
|
||||||
const activeAlarms = await firstValueFrom(this.activeAlarms$);
|
const activeAlarms = await firstValueFrom(this.activeAlarms$);
|
||||||
|
|
||||||
for (const alarm of activeAlarms) {
|
for (const alarm of activeAlarms) {
|
||||||
const { name, startTime, createInfo } = alarm;
|
const { taskName, startTime, createInfo } = alarm;
|
||||||
const existingAlarm = await this.getAlarm(name);
|
const existingAlarm = await this.getAlarm(taskName);
|
||||||
if (existingAlarm) {
|
if (existingAlarm) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
@ -170,11 +175,11 @@ export class BrowserTaskSchedulerService
|
||||||
createInfo.delayInMinutes &&
|
createInfo.delayInMinutes &&
|
||||||
startTime + createInfo.delayInMinutes * 60 * 1000 < currentTime;
|
startTime + createInfo.delayInMinutes * 60 * 1000 < currentTime;
|
||||||
if (shouldAlarmHaveBeenTriggered || hasSetTimeoutAlarmExceededDelay) {
|
if (shouldAlarmHaveBeenTriggered || hasSetTimeoutAlarmExceededDelay) {
|
||||||
this.recoveredAlarms.add(name);
|
this.recoveredAlarms.add(taskName);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
void this.scheduleAlarm(name, createInfo);
|
void this.scheduleAlarm(taskName, createInfo);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 10 seconds after verifying the alarm state, we should treat any newly created alarms as non-recovered alarms.
|
// 10 seconds after verifying the alarm state, we should treat any newly created alarms as non-recovered alarms.
|
||||||
|
@ -195,11 +200,11 @@ export class BrowserTaskSchedulerService
|
||||||
/**
|
/**
|
||||||
* Deletes an active alarm from state.
|
* Deletes an active alarm from state.
|
||||||
*
|
*
|
||||||
* @param name - The name of the active alarm to delete.
|
* @param taskName - The name of the active alarm to delete.
|
||||||
*/
|
*/
|
||||||
private async deleteActiveAlarm(name: ScheduledTaskName): Promise<void> {
|
private async deleteActiveAlarm(taskName: ScheduledTaskName): Promise<void> {
|
||||||
const activeAlarms = await firstValueFrom(this.activeAlarms$);
|
const activeAlarms = await firstValueFrom(this.activeAlarms$);
|
||||||
const filteredAlarms = activeAlarms?.filter((alarm) => alarm.name !== name);
|
const filteredAlarms = activeAlarms?.filter((alarm) => alarm.taskName !== taskName);
|
||||||
await this.updateActiveAlarms(filteredAlarms || []);
|
await this.updateActiveAlarms(filteredAlarms || []);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -247,13 +252,17 @@ export class BrowserTaskSchedulerService
|
||||||
* Triggers an alarm by calling its handler and
|
* Triggers an alarm by calling its handler and
|
||||||
* deleting it if it is a one-time alarm.
|
* deleting it if it is a one-time alarm.
|
||||||
*
|
*
|
||||||
* @param name - The name of the alarm to trigger.
|
* @param alarmName - The name of the alarm to trigger.
|
||||||
* @param periodInMinutes - The period in minutes of an interval alarm.
|
* @param periodInMinutes - The period in minutes of an interval alarm.
|
||||||
*/
|
*/
|
||||||
protected async triggerTask(name: ScheduledTaskName, periodInMinutes?: number): Promise<void> {
|
protected async triggerTask(
|
||||||
const handler = this.taskHandlers.get(name);
|
alarmName: ScheduledTaskName,
|
||||||
|
periodInMinutes?: number,
|
||||||
|
): Promise<void> {
|
||||||
|
const activeUserAlarmName = await this.getActiveUserAlarmName(alarmName);
|
||||||
|
const handler = this.taskHandlers.get(activeUserAlarmName);
|
||||||
if (!periodInMinutes) {
|
if (!periodInMinutes) {
|
||||||
await this.deleteActiveAlarm(name);
|
await this.deleteActiveAlarm(alarmName);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (handler) {
|
if (handler) {
|
||||||
|
@ -265,14 +274,15 @@ export class BrowserTaskSchedulerService
|
||||||
* Clears a new alarm with the given name and create info. Returns a promise
|
* Clears a new alarm with the given name and create info. Returns a promise
|
||||||
* that indicates when the alarm has been cleared successfully.
|
* that indicates when the alarm has been cleared successfully.
|
||||||
*
|
*
|
||||||
* @param alarmName - The name of the alarm to create.
|
* @param taskName - The name of the alarm to create.
|
||||||
*/
|
*/
|
||||||
clearAlarm(alarmName: string): Promise<boolean> {
|
async clearAlarm(taskName: string): Promise<boolean> {
|
||||||
|
const activeUserAlarmName = await this.getActiveUserAlarmName(taskName);
|
||||||
if (typeof browser !== "undefined" && browser.alarms) {
|
if (typeof browser !== "undefined" && browser.alarms) {
|
||||||
return browser.alarms.clear(alarmName);
|
return browser.alarms.clear(activeUserAlarmName);
|
||||||
}
|
}
|
||||||
|
|
||||||
return new Promise((resolve) => chrome.alarms.clear(alarmName, resolve));
|
return new Promise((resolve) => chrome.alarms.clear(activeUserAlarmName, resolve));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -290,28 +300,30 @@ export class BrowserTaskSchedulerService
|
||||||
/**
|
/**
|
||||||
* Creates a new alarm with the given name and create info.
|
* Creates a new alarm with the given name and create info.
|
||||||
*
|
*
|
||||||
* @param name - The name of the alarm to create.
|
* @param taskName - The name of the alarm to create.
|
||||||
* @param createInfo - The creation info for the alarm.
|
* @param createInfo - The creation info for the alarm.
|
||||||
*/
|
*/
|
||||||
async createAlarm(name: string, createInfo: chrome.alarms.AlarmCreateInfo): Promise<void> {
|
async createAlarm(taskName: string, createInfo: chrome.alarms.AlarmCreateInfo): Promise<void> {
|
||||||
|
const activeUserAlarmName = await this.getActiveUserAlarmName(taskName);
|
||||||
if (typeof browser !== "undefined" && browser.alarms) {
|
if (typeof browser !== "undefined" && browser.alarms) {
|
||||||
return browser.alarms.create(name, createInfo);
|
return browser.alarms.create(activeUserAlarmName, createInfo);
|
||||||
}
|
}
|
||||||
|
|
||||||
return new Promise((resolve) => chrome.alarms.create(name, createInfo, resolve));
|
return new Promise((resolve) => chrome.alarms.create(activeUserAlarmName, createInfo, resolve));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the alarm with the given name.
|
* Gets the alarm with the given name.
|
||||||
*
|
*
|
||||||
* @param alarmName - The name of the alarm to get.
|
* @param taskName - The name of the alarm to get.
|
||||||
*/
|
*/
|
||||||
getAlarm(alarmName: string): Promise<chrome.alarms.Alarm> {
|
async getAlarm(taskName: string): Promise<chrome.alarms.Alarm> {
|
||||||
|
const activeUserAlarmName = await this.getActiveUserAlarmName(taskName);
|
||||||
if (typeof browser !== "undefined" && browser.alarms) {
|
if (typeof browser !== "undefined" && browser.alarms) {
|
||||||
return browser.alarms.get(alarmName);
|
return browser.alarms.get(activeUserAlarmName);
|
||||||
}
|
}
|
||||||
|
|
||||||
return new Promise((resolve) => chrome.alarms.get(alarmName, resolve));
|
return new Promise((resolve) => chrome.alarms.get(activeUserAlarmName, resolve));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -324,4 +336,13 @@ export class BrowserTaskSchedulerService
|
||||||
|
|
||||||
return new Promise((resolve) => chrome.alarms.getAll(resolve));
|
return new Promise((resolve) => chrome.alarms.getAll(resolve));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected async getActiveUserAlarmName(taskName: string): Promise<string> {
|
||||||
|
const activeUserId = await firstValueFrom(this.stateProvider.activeUserId$);
|
||||||
|
if (!activeUserId) {
|
||||||
|
return taskName;
|
||||||
|
}
|
||||||
|
|
||||||
|
return `${activeUserId}_${taskName}`;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,9 +17,9 @@ export abstract class TaskSchedulerService {
|
||||||
protected stateProvider: StateProvider,
|
protected stateProvider: StateProvider,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
abstract registerTaskHandler(taskName: ScheduledTaskName, handler: () => void): Promise<void>;
|
abstract registerTaskHandler(taskName: ScheduledTaskName, handler: () => void): void;
|
||||||
|
|
||||||
abstract unregisterTaskHandler(taskName: ScheduledTaskName): Promise<void>;
|
abstract unregisterTaskHandler(taskName: ScheduledTaskName): void;
|
||||||
|
|
||||||
abstract setTimeout(
|
abstract setTimeout(
|
||||||
taskName: ScheduledTaskName,
|
taskName: ScheduledTaskName,
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
import { firstValueFrom } from "rxjs";
|
|
||||||
|
|
||||||
import { LogService } from "../abstractions/log.service";
|
import { LogService } from "../abstractions/log.service";
|
||||||
import { TaskIdentifier, TaskSchedulerService } from "../abstractions/task-scheduler.service";
|
import { TaskIdentifier, TaskSchedulerService } from "../abstractions/task-scheduler.service";
|
||||||
import { ScheduledTaskName } from "../enums/scheduled-task-name.enum";
|
import { ScheduledTaskName } from "../enums/scheduled-task-name.enum";
|
||||||
|
@ -12,23 +10,24 @@ export class DefaultTaskSchedulerService extends TaskSchedulerService {
|
||||||
this.taskHandlers = new Map();
|
this.taskHandlers = new Map();
|
||||||
}
|
}
|
||||||
|
|
||||||
async registerTaskHandler(taskName: ScheduledTaskName, handler: () => void): Promise<void> {
|
registerTaskHandler(taskName: ScheduledTaskName, handler: () => void) {
|
||||||
const activeUserTaskName = await this.getActiveUserTaskName(taskName);
|
const existingHandler = this.taskHandlers.get(taskName);
|
||||||
const existingHandler = this.taskHandlers.get(activeUserTaskName);
|
|
||||||
if (existingHandler) {
|
if (existingHandler) {
|
||||||
this.logService.warning(`Task handler for ${taskName} already exists. Overwriting.`);
|
this.logService.warning(`Task handler for ${taskName} already exists. Overwriting.`);
|
||||||
await this.unregisterTaskHandler(taskName);
|
this.unregisterTaskHandler(taskName);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.taskHandlers.set(activeUserTaskName, handler);
|
this.taskHandlers.set(taskName, handler);
|
||||||
}
|
}
|
||||||
|
|
||||||
async unregisterTaskHandler(taskName: ScheduledTaskName): Promise<void> {
|
unregisterTaskHandler(taskName: ScheduledTaskName) {
|
||||||
const activeUserTaskName = await this.getActiveUserTaskName(taskName);
|
this.taskHandlers.delete(taskName);
|
||||||
this.taskHandlers.delete(activeUserTaskName);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected triggerTask(taskName: ScheduledTaskName, _periodInMinutes?: number): void {
|
protected async triggerTask(
|
||||||
|
taskName: ScheduledTaskName,
|
||||||
|
_periodInMinutes?: number,
|
||||||
|
): Promise<void> {
|
||||||
const handler = this.taskHandlers.get(taskName);
|
const handler = this.taskHandlers.get(taskName);
|
||||||
if (handler) {
|
if (handler) {
|
||||||
handler();
|
handler();
|
||||||
|
@ -77,13 +76,4 @@ export class DefaultTaskSchedulerService extends TaskSchedulerService {
|
||||||
globalThis.clearInterval(taskIdentifier.intervalId);
|
globalThis.clearInterval(taskIdentifier.intervalId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async getActiveUserTaskName(taskName: ScheduledTaskName): Promise<string> {
|
|
||||||
const activeUserId = await firstValueFrom(this.stateProvider.activeUserId$);
|
|
||||||
if (!activeUserId) {
|
|
||||||
return taskName;
|
|
||||||
}
|
|
||||||
|
|
||||||
return `${activeUserId}_${taskName}`;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue