Merge pull request #3406 from bluewave-labs/fix/incident-controller-typing

fix: Incident Controller/Service typing
This commit is contained in:
Alexander Holliday authored and GitHub committed 2026-03-11 11:18:05 -07:00
commit cda0ec3a4c
4 files changed
+65 -52

No files matched your search

+15 -21
View File
@@ -1,35 +1,30 @@
import { AppError } from "@/utils/AppError.js";
import { Request, Response, NextFunction } from "express";
import { requireTeamId, requireUserId, requireUserEmail } from "./controllerUtils.js";
import { IIncidentService } from "@/service/index.js";
import { getIncidentsByTeamQueryValidation, getIncidentSummaryQueryValidation } from "@/validation/incidentValidation.js";
const SERVICE_NAME = "incidentController";
const SERVICE_NAME = "IncidentController";
class IncidentController {
static SERVICE_NAME = SERVICE_NAME;
private incidentService: any;
constructor(incidentService: any) {
private incidentService: IIncidentService;
constructor(incidentService: IIncidentService) {
this.incidentService = incidentService;
}
get serviceName() {
return IncidentController.SERVICE_NAME;
}
getIncidentsByTeam = async (req: Request, res: Response, next: NextFunction) => {
try {
const { sortOrder, dateRange, page, rowsPerPage, status, monitorId, resolutionType } = req.query || {};
const validatedQuery = getIncidentsByTeamQueryValidation.parse(req.query);
const teamId = requireTeamId(req.user?.teamId);
const result = await this.incidentService.getIncidentsByTeam(
teamId,
sortOrder,
dateRange,
page,
rowsPerPage,
status,
monitorId,
resolutionType
validatedQuery.sortOrder,
validatedQuery.dateRange,
validatedQuery.page,
validatedQuery.rowsPerPage,
validatedQuery.status,
validatedQuery.monitorId,
validatedQuery.resolutionType
);
return res.status(200).json({
@@ -45,10 +40,9 @@ class IncidentController {
getIncidentSummary = async (req: Request, res: Response, next: NextFunction) => {
try {
const teamId = requireTeamId(req.user?.teamId);
const validatedQuery = getIncidentSummaryQueryValidation.parse(req.query);
const limit = req.query.limit ? parseInt(req.query.limit as string, 10) : 10;
const summary = await this.incidentService.getIncidentSummary(teamId, limit);
const summary = await this.incidentService.getIncidentSummary(teamId, validatedQuery.limit);
return res.status(200).json({
success: true,
msg: "Incident summary retrieved successfully",
+29 -31
View File
@@ -2,7 +2,6 @@ const SERVICE_NAME = "incidentService";
import type { Monitor } from "@/types/monitor.js";
import type { MonitorStatusResponse } from "@/types/network.js";
import { AppError } from "@/utils/AppError.js";
import { ParseBoolean } from "@/utils/utils.js";
import { getDateForRange } from "@/utils/dataUtils.js";
import type { IIncidentsRepository, IMonitorsRepository, IUsersRepository } from "@/repositories/index.js";
import type { Incident, IncidentSummary, User } from "@/types/index.js";
@@ -22,13 +21,13 @@ export interface IIncidentService {
teamId: string,
sortOrder: string,
dateRange: string,
page: string,
rowsPerPage: string,
status: string,
monitorId: string,
resolutionType: string
page: number,
rowsPerPage: number,
status: boolean | undefined,
monitorId: string | undefined,
resolutionType: string | undefined
): Promise<{ incidents: Incident[]; count: number }>;
getIncidentSummary(teamId: string, limit?: string): Promise<IncidentSummary>;
getIncidentSummary(teamId: string, limit?: number): Promise<IncidentSummary>;
getIncidentById(incidentId: string, teamId: string): Promise<{ incident: Incident; monitor: Monitor; user: User | null }>;
}
@@ -164,13 +163,13 @@ export class IncidentService implements IIncidentService {
});
return resolvedIncident;
} catch (error: any) {
} catch (error: unknown) {
this.logger.error({
service: SERVICE_NAME,
method: "resolveIncident",
message: error.message,
message: error instanceof Error ? error.message : "Unknown error",
details: { id: incidentId },
stack: error.stack,
stack: error instanceof Error ? error.stack : undefined,
});
throw error;
}
@@ -180,11 +179,11 @@ export class IncidentService implements IIncidentService {
teamId: string,
sortOrder: string,
dateRange: string,
page: string,
rowsPerPage: string,
status: string,
monitorId: string,
resolutionType: string
page: number,
rowsPerPage: number,
status: boolean | undefined,
monitorId: string | undefined,
resolutionType: string | undefined
) => {
try {
if (!teamId) {
@@ -193,9 +192,8 @@ export class IncidentService implements IIncidentService {
const startDate = getDateForRange(dateRange);
const parsedPage = Number.isFinite(parseInt(page)) ? parseInt(page) : 0;
const parsedRowsPerPage = Number.isFinite(parseInt(rowsPerPage)) ? parseInt(rowsPerPage) : 20;
const parsedStatus = typeof status === "undefined" ? undefined : ParseBoolean(status);
const parsedPage = page ?? 0;
const parsedRowsPerPage = rowsPerPage ?? 20;
const incidents = await this.incidentsRepository.findByTeamId(
teamId,
@@ -203,43 +201,43 @@ export class IncidentService implements IIncidentService {
parsedPage,
parsedRowsPerPage,
sortOrder,
parsedStatus,
status,
monitorId,
resolutionType
);
const count = await this.incidentsRepository.countByTeamId(teamId, startDate, parsedStatus, monitorId, resolutionType);
const count = await this.incidentsRepository.countByTeamId(teamId, startDate, status, monitorId, resolutionType);
return { incidents, count };
} catch (error: any) {
} catch (error: unknown) {
this.logger.error({
service: SERVICE_NAME,
method: "getIncidentsByTeam",
message: error.message,
message: error instanceof Error ? error.message : "Unknown error",
details: { teamId },
stack: error.stack,
stack: error instanceof Error ? error.stack : undefined,
});
throw error;
}
};
getIncidentSummary = async (teamId: string, limit?: string) => {
getIncidentSummary = async (teamId: string, limit?: number) => {
try {
if (!teamId) {
throw new AppError({ message: "No team ID in request", service: SERVICE_NAME, method: "getIncidentSummary", status: 400 });
}
const parsedLimit = limit && Number.isFinite(parseInt(limit, 10)) ? parseInt(limit, 10) : 10;
const parsedLimit = limit ?? 10;
const summary = await this.incidentsRepository.findSummaryByTeamId(teamId, parsedLimit);
return summary;
} catch (error: any) {
} catch (error: unknown) {
this.logger.error({
service: SERVICE_NAME,
method: "getIncidentSummary",
message: error.message,
message: error instanceof Error ? error.message : "Unknown error",
details: { teamId },
stack: error.stack,
stack: error instanceof Error ? error.stack : undefined,
});
throw error;
}
@@ -254,13 +252,13 @@ export class IncidentService implements IIncidentService {
user = await this.usersRepository.findById(incident.resolvedBy);
}
return { incident, monitor, user };
} catch (error: any) {
} catch (error: unknown) {
this.logger.error({
service: SERVICE_NAME,
method: "getIncidentById",
message: error.message,
message: error instanceof Error ? error.message : "Unknown error",
details: { incidentId },
stack: error.stack,
stack: error instanceof Error ? error.stack : undefined,
});
throw error;
}
@@ -0,0 +1,20 @@
import { z } from "zod";
import { booleanCoercion } from "./shared.js";
//****************************************
// Incident Validations
//****************************************
export const getIncidentsByTeamQueryValidation = z.object({
sortOrder: z.enum(["asc", "desc"]),
dateRange: z.enum(["recent", "hour", "day", "week", "month", "all"]),
page: z.coerce.number().int().min(0),
rowsPerPage: z.coerce.number().int().min(1),
status: booleanCoercion.optional(),
monitorId: z.string().optional(),
resolutionType: z.enum(["manual", "automatic"]).optional(),
});
export const getIncidentSummaryQueryValidation = z.object({
limit: z.coerce.number().int().min(1).optional(),
});
+1
View File
@@ -20,3 +20,4 @@ export * from "./settingsValidation.js";
export * from "./statusPageValidation.js";
export * from "./notificationValidation.js";
export * from "./userValidation.js";
export * from "./incidentValidation.js";