handle paused status

This commit is contained in:
Alex Holliday committed 2026-05-21 11:02:59 -07:00
1 parent fbfb861447
commit 3abacb046b
4 files changed
+21 -17

No files matched your search

@@ -1,4 +1,4 @@
import { type MonitorType, type Monitor, type MonitorsSummary, CheckSnapshot } from "@/types/index.js";
import { type MonitorType, type Monitor, type MonitorStatus, type MonitorsSummary, CheckSnapshot } from "@/types/index.js";
export interface TeamQueryConfig {
limit?: number;
@@ -31,7 +31,7 @@ export interface IMonitorsRepository {
// update
updateById(monitorId: string, teamId: string, updates: Partial<Monitor>): Promise<Monitor>;
updateByIds(monitorIds: string[], teamId: string, updates: Partial<Monitor>): Promise<number>;
updateByIds(monitorIds: string[], teamId: string, updates: Partial<Monitor>, excludeStatuses?: MonitorStatus[]): Promise<number>;
updateStatusWindowAndChecks(
monitorId: string,
teamId: string,
@@ -1,6 +1,6 @@
import { MonitorModel } from "@/db/models/index.js";
import type { MonitorDocument, CheckSnapshotDocument } from "@/db/models/index.js";
import type { Monitor, MonitorsSummary, CheckSnapshot } from "@/types/index.js";
import type { Monitor, MonitorStatus, MonitorsSummary, CheckSnapshot } from "@/types/index.js";
import mongoose, { type FilterQuery, type PipelineStage } from "mongoose";
import type { IMonitorsRepository, TeamQueryConfig, SummaryConfig } from "./IMonitorsRepository.js";
import { MongoBulkWriteError } from "mongodb";
@@ -190,9 +190,13 @@ class MongoMonitorsRepository implements IMonitorsRepository {
return this.toEntity(updatedMonitor);
};
updateByIds = async (monitorIds: string[], teamId: string, updates: Partial<Monitor>) => {
updateByIds = async (monitorIds: string[], teamId: string, updates: Partial<Monitor>, excludeStatuses?: MonitorStatus[]) => {
const objectIds = monitorIds.map((id) => new mongoose.Types.ObjectId(id));
const updated = await MonitorModel.updateMany({ _id: { $in: objectIds }, teamId }, { $set: updates }, { runValidators: true });
const filter: Record<string, unknown> = { _id: { $in: objectIds }, teamId };
if (excludeStatuses && excludeStatuses.length > 0) {
filter.status = { $nin: excludeStatuses };
}
const updated = await MonitorModel.updateMany(filter, { $set: updates }, { runValidators: true });
return updated.modifiedCount;
};
@@ -69,7 +69,7 @@ export class MaintenanceWindowService implements IMaintenanceWindowService {
const toInitializing = monitorIds.filter((monitorId) => !stillCovered.has(monitorId));
if (toInitializing.length > 0) {
await this.monitorsRepository.updateByIds(toInitializing, teamId, { status: "initializing" });
await this.monitorsRepository.updateByIds(toInitializing, teamId, { status: "initializing" }, ["paused"]);
}
};
@@ -120,7 +120,7 @@ export class MaintenanceWindowService implements IMaintenanceWindowService {
});
if (isWindowActive(created)) {
await this.monitorsRepository.updateByIds(created.monitorIds, teamId, { status: "maintenance" });
await this.monitorsRepository.updateByIds(created.monitorIds, teamId, { status: "maintenance" }, ["paused"]);
}
};
@@ -205,7 +205,7 @@ export class MaintenanceWindowService implements IMaintenanceWindowService {
const leavingCandidates = wasActive ? existing.monitorIds.filter((monitorId) => !isActive || !updated.monitorIds.includes(monitorId)) : [];
if (enteringMaintenance.length > 0) {
await this.monitorsRepository.updateByIds(enteringMaintenance, teamId, { status: "maintenance" });
await this.monitorsRepository.updateByIds(enteringMaintenance, teamId, { status: "maintenance" }, ["paused"]);
}
if (leavingCandidates.length > 0) {
@@ -158,7 +158,7 @@ describe("MaintenanceWindowService", () => {
await service.createMaintenanceWindow(defaultCreateParams);
expect(monitorsRepository.updateByIds).toHaveBeenCalledTimes(1);
expect(monitorsRepository.updateByIds).toHaveBeenCalledWith(["mon-1", "mon-2"], "team-1", { status: "maintenance" });
expect(monitorsRepository.updateByIds).toHaveBeenCalledWith(["mon-1", "mon-2"], "team-1", { status: "maintenance" }, ["paused"]);
});
it("does not touch monitor status when the created window is inactive", async () => {
@@ -272,7 +272,7 @@ describe("MaintenanceWindowService", () => {
await service.deleteMaintenanceWindow({ id: "mw-1", teamId: "team-1" });
expect(monitorsRepository.updateByIds).toHaveBeenCalledTimes(1);
expect(monitorsRepository.updateByIds).toHaveBeenCalledWith(["mon-1", "mon-2"], "team-1", { status: "initializing" });
expect(monitorsRepository.updateByIds).toHaveBeenCalledWith(["mon-1", "mon-2"], "team-1", { status: "initializing" }, ["paused"]);
});
it("excludes the deleted window from the overlap check", async () => {
@@ -294,7 +294,7 @@ describe("MaintenanceWindowService", () => {
await service.deleteMaintenanceWindow({ id: "mw-1", teamId: "team-1" });
expect(monitorsRepository.updateByIds).toHaveBeenCalledTimes(1);
expect(monitorsRepository.updateByIds).toHaveBeenCalledWith(["mon-1"], "team-1", { status: "initializing" });
expect(monitorsRepository.updateByIds).toHaveBeenCalledWith(["mon-1"], "team-1", { status: "initializing" }, ["paused"]);
});
});
@@ -406,7 +406,7 @@ describe("MaintenanceWindowService", () => {
});
expect(monitorsRepository.updateByIds).toHaveBeenCalledTimes(1);
expect(monitorsRepository.updateByIds).toHaveBeenCalledWith(["mon-1", "mon-2"], "team-1", { status: "maintenance" });
expect(monitorsRepository.updateByIds).toHaveBeenCalledWith(["mon-1", "mon-2"], "team-1", { status: "maintenance" }, ["paused"]);
});
it("sets all monitors to initializing when window transitions active → inactive", async () => {
@@ -422,7 +422,7 @@ describe("MaintenanceWindowService", () => {
});
expect(monitorsRepository.updateByIds).toHaveBeenCalledTimes(1);
expect(monitorsRepository.updateByIds).toHaveBeenCalledWith(["mon-1", "mon-2"], "team-1", { status: "initializing" });
expect(monitorsRepository.updateByIds).toHaveBeenCalledWith(["mon-1", "mon-2"], "team-1", { status: "initializing" }, ["paused"]);
});
it("flips only newly added monitors to maintenance when window stays active", async () => {
@@ -438,7 +438,7 @@ describe("MaintenanceWindowService", () => {
});
expect(monitorsRepository.updateByIds).toHaveBeenCalledTimes(1);
expect(monitorsRepository.updateByIds).toHaveBeenCalledWith(["mon-2"], "team-1", { status: "maintenance" });
expect(monitorsRepository.updateByIds).toHaveBeenCalledWith(["mon-2"], "team-1", { status: "maintenance" }, ["paused"]);
});
it("flips only removed monitors to initializing when window stays active", async () => {
@@ -454,7 +454,7 @@ describe("MaintenanceWindowService", () => {
});
expect(monitorsRepository.updateByIds).toHaveBeenCalledTimes(1);
expect(monitorsRepository.updateByIds).toHaveBeenCalledWith(["mon-2"], "team-1", { status: "initializing" });
expect(monitorsRepository.updateByIds).toHaveBeenCalledWith(["mon-2"], "team-1", { status: "initializing" }, ["paused"]);
});
it("makes no monitor updates when window stays active and monitor set is unchanged", async () => {
@@ -486,7 +486,7 @@ describe("MaintenanceWindowService", () => {
});
expect(monitorsRepository.updateByIds).toHaveBeenCalledTimes(1);
expect(monitorsRepository.updateByIds).toHaveBeenCalledWith(["mon-1"], "team-1", { status: "initializing" });
expect(monitorsRepository.updateByIds).toHaveBeenCalledWith(["mon-1"], "team-1", { status: "initializing" }, ["paused"]);
});
it("excludes the current window from the overlap check", async () => {
@@ -519,7 +519,7 @@ describe("MaintenanceWindowService", () => {
body: { active: false },
});
expect(monitorsRepository.updateByIds).toHaveBeenCalledWith(["mon-1"], "team-1", { status: "initializing" });
expect(monitorsRepository.updateByIds).toHaveBeenCalledWith(["mon-1"], "team-1", { status: "initializing" }, ["paused"]);
});
});
});