Merge pull request #3854 from bluewave-labs/fix/infra-instant-gauge

fix: add recent checks for infra monitors
This commit is contained in:
Alexander Holliday authored and GitHub committed 2026-08-19 10:29:44 -07:00
commit 79957693fe
7 files changed
+115 -17

No files matched your search

@@ -106,7 +106,7 @@ export class MaintenanceWindowService implements IMaintenanceWindowService {
start: string;
end: string;
}) => {
const monitors = await this.monitorsRepository.findByIds(monitorIDs, { includeRecentChecks: false });
const monitors = await this.monitorsRepository.findByIds(monitorIDs, { recentChecks: "none" });
const unauthorizedMonitors = monitors.filter((monitor) => monitor.teamId !== teamId);
@@ -193,7 +193,7 @@ export class MaintenanceWindowService implements IMaintenanceWindowService {
const update: Partial<MaintenanceWindow> = rest;
if (monitors !== undefined) {
const monitorDocs = await this.monitorsRepository.findByIds(monitors, { includeRecentChecks: false });
const monitorDocs = await this.monitorsRepository.findByIds(monitors, { recentChecks: "none" });
const unauthorizedMonitors = monitorDocs.filter((monitor) => monitor.teamId !== teamId);
if (unauthorizedMonitors.length > 0) {
throw new AppError({
@@ -16,6 +16,8 @@ export interface SummaryConfig {
tags?: string | string[];
}
export type RecentChecksMode = "all" | "latestHardware" | "none";
export interface IMonitorsRepository {
// create
create(monitor: Monitor, teamId: string, userId: string): Promise<Monitor | null>;
@@ -29,7 +31,7 @@ export interface IMonitorsRepository {
findAllForScheduling(): Promise<MonitorScheduleFields[]>;
findByTeamId(teamId: string, config: TeamQueryConfig, options?: { includeRecentChecks?: boolean }): Promise<Monitor[]>;
findByTeamIdWithStats(teamId: string, config: TeamQueryConfig): Promise<Monitor[]>;
findByIds(monitorIds: string[], options?: { includeRecentChecks?: boolean }): Promise<Monitor[]>;
findByIds(monitorIds: string[], options?: { recentChecks?: RecentChecksMode }): Promise<Monitor[]>;
// update
updateById(monitorId: string, teamId: string, updates: Partial<Monitor>): Promise<Monitor>;
@@ -5,7 +5,7 @@ import type { Monitor, MonitorScheduleFields, MonitorStatus, MonitorsSummary } f
import mongoose, { type FilterQuery, type PipelineStage } from "mongoose";
import { MongoBulkWriteError } from "mongodb";
import { AppError } from "@/utils/AppError.js";
import { IMonitorsRepository, TeamQueryConfig, SummaryConfig } from "@/domain/monitors/monitor.repository.interface.js";
import { IMonitorsRepository, TeamQueryConfig, SummaryConfig, RecentChecksMode } from "@/domain/monitors/monitor.repository.interface.js";
import { toStringId, toDateString } from "@/utils/mongoMappers.js";
import { toCheckSnapshot } from "@/domain/checks/check.snapshot.js";
class MongoMonitorsRepository implements IMonitorsRepository {
@@ -131,7 +131,26 @@ class MongoMonitorsRepository implements IMonitorsRepository {
return documents.map((doc) => this.toEntity(doc));
};
findByIds = async (monitorIds: string[], options?: { includeRecentChecks?: boolean }): Promise<Monitor[]> => {
private recentChecksStages = (mode: RecentChecksMode = "all"): PipelineStage[] => {
switch (mode) {
case "none":
return [{ $project: { recentChecks: 0 } }];
case "latestHardware":
return [
{
$set: {
recentChecks: {
$cond: [{ $eq: ["$type", "hardware"] }, { $slice: [{ $ifNull: ["$recentChecks", []] }, -1] }, []],
},
},
},
];
case "all":
return [];
}
};
findByIds = async (monitorIds: string[], options?: { recentChecks?: "all" | "latestHardware" | "none" }): Promise<Monitor[]> => {
if (!monitorIds.length) {
return [];
}
@@ -140,7 +159,7 @@ class MongoMonitorsRepository implements IMonitorsRepository {
const pipeline: PipelineStage[] = [
{ $match: { _id: { $in: objectIds } } },
...(options?.includeRecentChecks === false ? [{ $project: { recentChecks: 0 } } as PipelineStage] : []),
...this.recentChecksStages(options?.recentChecks),
...this.uptimeStatsLookupStages,
];
const documents = await MonitorModel.aggregate(pipeline);
@@ -134,7 +134,7 @@ export class StatusPageService implements IStatusPageService {
const dbSettings = await this.settingsService.getDBSettings();
const showURL = dbSettings.showURL;
const monitors = await this.monitorsRepository.findByIds(statusPage.monitors, { includeRecentChecks: range === "latest" });
const monitors = await this.monitorsRepository.findByIds(statusPage.monitors, { recentChecks: range === "latest" ? "all" : "latestHardware" });
const order = new Map(statusPage.monitors.map((id, i) => [id, i]));
const sorted = [...monitors].sort((a, b) => (order.get(a.id) ?? Number.MAX_SAFE_INTEGER) - (order.get(b.id) ?? Number.MAX_SAFE_INTEGER));
@@ -162,7 +162,6 @@ export class StatusPageService implements IStatusPageService {
checkTTLDays: dbSettings.checkTTL,
monitors: sorted.map((monitor) => ({
...this.toPublicMonitor(monitor, showURL),
recentChecks: [],
dailyChecks: bucketsByMonitor.get(monitor.id) ?? [],
})),
};
@@ -31,6 +31,23 @@ const seedMonitor = (teamId: mongoose.Types.ObjectId, name: string, tags: mongoo
tags,
});
const makeSnapshot = (id: string, dayOfMonth: number) => ({
id,
status: true,
responseTime: 100,
createdAt: new Date(2026, 7, dayOfMonth),
});
const seedMonitorWithChecks = (teamId: mongoose.Types.ObjectId, name: string, type: string, snapshotIds: string[]) =>
MonitorModel.create({
userId: new mongoose.Types.ObjectId(),
teamId,
name,
type,
url: "https://example.com",
recentChecks: snapshotIds.map((id, i) => makeSnapshot(id, i + 1)),
});
describe("MongoMonitorsRepository", () => {
describe("tag filtering", () => {
it("matches ObjectId tag references when filters arrive as query strings", async () => {
@@ -51,4 +68,59 @@ describe("MongoMonitorsRepository", () => {
expect(summary.totalMonitors).toBe(1);
});
});
describe("findByIds recentChecks modes", () => {
const seedFixtures = async () => {
const teamId = new mongoose.Types.ObjectId();
const hardware = await seedMonitorWithChecks(teamId, "hw", "hardware", ["hw-1", "hw-2", "hw-3"]);
const http = await seedMonitorWithChecks(teamId, "web", "http", ["web-1", "web-2"]);
return { ids: [hardware._id.toString(), http._id.toString()] };
};
const checksByName = (monitors: Awaited<ReturnType<MongoMonitorsRepository["findByIds"]>>) =>
Object.fromEntries(monitors.map((monitor) => [monitor.name, monitor.recentChecks.map((check) => check.id)]));
it("returns full recentChecks by default and for mode 'all'", async () => {
const repo = new MongoMonitorsRepository();
const { ids } = await seedFixtures();
const byDefault = await repo.findByIds(ids);
const byAll = await repo.findByIds(ids, { recentChecks: "all" });
for (const monitors of [byDefault, byAll]) {
expect(checksByName(monitors)).toEqual({ hw: ["hw-1", "hw-2", "hw-3"], web: ["web-1", "web-2"] });
}
});
it("strips recentChecks for mode 'none'", async () => {
const repo = new MongoMonitorsRepository();
const { ids } = await seedFixtures();
const monitors = await repo.findByIds(ids, { recentChecks: "none" });
expect(checksByName(monitors)).toEqual({ hw: [], web: [] });
});
it("keeps only the newest snapshot for hardware monitors in 'latestHardware' mode", async () => {
const repo = new MongoMonitorsRepository();
const { ids } = await seedFixtures();
const monitors = await repo.findByIds(ids, { recentChecks: "latestHardware" });
expect(checksByName(monitors)).toEqual({ hw: ["hw-3"], web: [] });
});
it("returns an empty array in 'latestHardware' mode for a hardware monitor with no recentChecks field at all", async () => {
const repo = new MongoMonitorsRepository();
const teamId = new mongoose.Types.ObjectId();
const hardware = await seedMonitorWithChecks(teamId, "hw-fresh", "hardware", []);
// $slice on a missing operand is a hard aggregation error; the $ifNull guard must cover it
await MonitorModel.updateOne({ _id: hardware._id }, { $unset: { recentChecks: 1 } });
const monitors = await repo.findByIds([hardware._id.toString()], { recentChecks: "latestHardware" });
expect(monitors).toHaveLength(1);
expect(monitors[0].recentChecks).toEqual([]);
});
});
});
@@ -123,7 +123,7 @@ describe("MaintenanceWindowService", () => {
await service.createMaintenanceWindow(defaultCreateParams);
expect(monitorsRepository.findByIds).toHaveBeenCalledWith(["mon-1", "mon-2"], { includeRecentChecks: false });
expect(monitorsRepository.findByIds).toHaveBeenCalledWith(["mon-1", "mon-2"], { recentChecks: "none" });
});
it("throws 403 when a monitor belongs to a different team", async () => {
@@ -404,7 +404,7 @@ describe("MaintenanceWindowService", () => {
body: { monitors: ["mon-1", "mon-2"] },
});
expect(monitorsRepository.findByIds).toHaveBeenCalledWith(["mon-1", "mon-2"], { includeRecentChecks: false });
expect(monitorsRepository.findByIds).toHaveBeenCalledWith(["mon-1", "mon-2"], { recentChecks: "none" });
});
it("throws 403 when a new monitor belongs to a different team", async () => {
@@ -376,23 +376,29 @@ describe("StatusPageService", () => {
expect(payload.monitors[0]).not.toHaveProperty("dailyChecks");
}
expect(checksRepo.getDailyStatusBuckets).not.toHaveBeenCalled();
expect(monitorsRepo.findByIds).toHaveBeenCalledWith(["mon-1"], { includeRecentChecks: true });
expect(monitorsRepo.findByIds).toHaveBeenCalledWith(["mon-1"], { recentChecks: "all" });
});
it("attaches dailyChecks, empties recentChecks, and echoes range metadata for a day range", async () => {
it("attaches dailyChecks, passes the repo-trimmed recentChecks through, and echoes range metadata for a day range", async () => {
const { service, checksRepo, monitorsRepo } = createService();
const bucket = { monitorId: "mon-1", date: "2026-08-01", totalChecks: 10, upChecks: 9, downChecks: 1, avgResponseTime: 120 };
const hardwareSnapshot = { id: "chk-1" } as CheckSnapshot;
(checksRepo.getDailyStatusBuckets as jest.Mock).mockResolvedValue([bucket]);
(monitorsRepo.findByIds as jest.Mock).mockResolvedValue([makeMonitor({ recentChecks: [{ id: "chk-1" } as CheckSnapshot] })]);
const page = makeStatusPage({ isPublished: true, timezone: "America/Toronto" });
(monitorsRepo.findByIds as jest.Mock).mockResolvedValue([
makeMonitor({ id: "mon-1", type: "hardware", recentChecks: [hardwareSnapshot] }),
makeMonitor({ id: "mon-2", recentChecks: [] }),
]);
const page = makeStatusPage({ isPublished: true, timezone: "America/Toronto", monitors: ["mon-1", "mon-2"] });
const payload = await service.getPublicStatusPagePayload(page, undefined, "30d");
expect(checksRepo.getDailyStatusBuckets).toHaveBeenCalledWith(["mon-1"], 30, "America/Toronto");
expect(monitorsRepo.findByIds).toHaveBeenCalledWith(["mon-1"], { includeRecentChecks: false });
expect(checksRepo.getDailyStatusBuckets).toHaveBeenCalledWith(["mon-1", "mon-2"], 30, "America/Toronto");
expect(monitorsRepo.findByIds).toHaveBeenCalledWith(["mon-1", "mon-2"], { recentChecks: "latestHardware" });
expect(payload).toMatchObject({ range: "30d", bucketTimezone: "America/Toronto", checkTTLDays: 30 });
expect(payload.monitors[0].recentChecks).toEqual([]);
expect(payload.monitors[0].recentChecks).toEqual([hardwareSnapshot]);
expect(payload.monitors[0].dailyChecks).toEqual([bucket]);
expect(payload.monitors[1].recentChecks).toEqual([]);
expect(payload.monitors[1].dailyChecks).toEqual([]);
for (const field of SENSITIVE_FIELDS) {
expect(payload.monitors[0]).not.toHaveProperty(field);
}