Merge pull request #3324 from bluewave-labs/fix/import

fix: import
This commit is contained in:
Alexander Holliday authored and GitHub committed 2026-02-21 08:20:57 -08:00
commit 06be65ec52
7 files changed
+134 -27

No files matched your search

+12 -10
View File
@@ -23,6 +23,7 @@ import {
parseMonitorTypeFilter,
parseSortOrder,
requireTeamId,
requireUserId,
} from "./controllerUtils.js";
import { AppError } from "@/utils/AppError.js";
import { IMonitorService } from "@/service/index.js";
@@ -172,22 +173,23 @@ class MonitorController {
}
};
createBulkMonitors = async (req: Request, res: Response, next: NextFunction) => {
importMonitorsFromJSON = async (req: Request, res: Response, next: NextFunction) => {
try {
const userId = req.user?.id;
if (!userId) {
throw new AppError({ message: "User ID is required", status: 400 });
const teamId = requireTeamId(req?.user?.teamId);
const userId = requireUserId(req?.user?.id);
const { monitors } = req.body;
if (!monitors || !Array.isArray(monitors)) {
throw new AppError({ message: "Invalid request: monitors array is required", status: 400 });
}
const teamId = requireTeamId(req?.user?.teamId);
const data = req.body;
const monitors = await this.monitorService.createBulkMonitors(data, userId, teamId);
const result = await this.monitorService.importMonitorsFromJSON({ teamId, userId, monitors });
return res.status(200).json({
success: true,
msg: "Bulk monitors created successfully",
data: monitors,
msg: `Successfully imported ${result.imported} monitor(s)`,
data: result,
});
} catch (error) {
next(error);
@@ -17,7 +17,7 @@ export interface SummaryConfig {
export interface IMonitorsRepository {
// create
create(monitor: Monitor, teamId: string, userId: string): Promise<Monitor | null>;
createBulkMonitors(monitors: Monitor[]): Promise<Monitor[]>;
createMonitors(monitors: Monitor[]): Promise<Monitor[]>;
// single fetch
findById(monitorId: string, teamId: string): Promise<Monitor>;
@@ -12,13 +12,23 @@ class MongoMonitorsRepository implements IMonitorsRepository {
return this.toEntity(saved);
};
createBulkMonitors = async (monitors: Monitor[]): Promise<Monitor[]> => {
createMonitors = async (monitors: Monitor[]): Promise<Monitor[]> => {
if (!monitors.length) {
return [];
}
const payload = monitors.map((monitor) => ({ ...monitor, notifications: undefined }));
const inserted = await MonitorModel.insertMany(payload, { ordered: false });
return this.mapDocuments(inserted);
try {
const inserted = await MonitorModel.insertMany(payload, { ordered: false });
return this.mapDocuments(inserted);
} catch (error: any) {
if (error.name === "MongoBulkWriteError" || error.name === "BulkWriteError") {
const insertedDocs = error.insertedDocs || [];
if (insertedDocs.length > 0) {
return this.mapDocuments(insertedDocs);
}
}
throw error;
}
};
findById = async (monitorId: string, teamId: string): Promise<Monitor> => {
+3 -1
View File
@@ -26,6 +26,7 @@ class MonitorRoutes {
// Hardware routes
this.router.get("/hardware/details/:monitorId", this.monitorController.getHardwareDetailsById);
// PageSpeed routes
this.router.get("/pagespeed/details/:monitorId", this.monitorController.getPageSpeedDetailsById);
@@ -44,7 +45,8 @@ class MonitorRoutes {
// Other static routes
this.router.post("/demo", isAllowed(["admin", "superadmin"]), this.monitorController.addDemoMonitors);
this.router.get("/export/json", isAllowed(["admin", "superadmin"]), this.monitorController.exportMonitorsToJSON);
this.router.post("/bulk", isAllowed(["admin", "superadmin"]), upload.single("csvFile"), this.monitorController.createBulkMonitors);
this.router.post("/import/json", isAllowed(["admin", "superadmin"]), this.monitorController.importMonitorsFromJSON);
this.router.post("/test-email", isAllowed(["admin", "superadmin"]), this.monitorController.sendTestEmail);
this.router.get("/games", this.monitorController.getAllGames);
+43 -5
View File
@@ -29,7 +29,7 @@ export interface IMonitorService {
// create
createMonitor(teamId: string, userId: string, body: Monitor): Promise<void>;
createBulkMonitors(monitors: Array<Monitor>, userId: string, teamId: string): Promise<Monitor[] | null>;
createMonitors(monitors: Array<Monitor>, userId: string, teamId: string): Promise<Monitor[] | null>;
addDemoMonitors(args: { userId: string; teamId: string }): Promise<Monitor[]>;
// read
@@ -72,6 +72,7 @@ export interface IMonitorService {
// other
sendTestEmail(args: { to: string }): Promise<string>;
exportMonitorsToJSON(args: { teamId: string }): Promise<Monitor[]>;
importMonitorsFromJSON(args: { teamId: string; userId: string; monitors: any[] }): Promise<{ imported: number; errors: string[] }>;
}
export class MonitorService implements IMonitorService {
@@ -157,10 +158,10 @@ export class MonitorService implements IMonitorService {
this.jobQueue.addJob(monitor.id, monitor);
};
createBulkMonitors = async (monitors: Array<Monitor>, userId: string, teamId: string): Promise<Monitor[] | null> => {
const createdMonitors = await this.monitorsRepository.createBulkMonitors(monitors);
createMonitors = async (monitors: Array<Monitor>, userId: string, teamId: string): Promise<Monitor[] | null> => {
const createdMonitors = await this.monitorsRepository.createMonitors(monitors);
if (!monitors || monitors.length === 0) {
throw new AppError({ message: "Failed to create monitors", status: 500, service: SERVICE_NAME, method: "createBulkMonitors" });
throw new AppError({ message: "Failed to create monitors", status: 500, service: SERVICE_NAME, method: "createMonitors" });
}
await Promise.all(createdMonitors.map((monitor) => this.jobQueue.addJob(monitor.id, monitor)));
@@ -184,7 +185,7 @@ export class MonitorService implements IMonitorService {
interval: 60000,
};
});
const demoMonitors = await this.monitorsRepository.createBulkMonitors(monitors);
const demoMonitors = await this.monitorsRepository.createMonitors(monitors);
await Promise.all(demoMonitors.map((monitor) => this.jobQueue.addJob(monitor.id, monitor)));
return demoMonitors;
@@ -486,4 +487,41 @@ export class MonitorService implements IMonitorService {
return monitors;
};
importMonitorsFromJSON = async ({
teamId,
userId,
monitors,
}: {
teamId: string;
userId: string;
monitors: any[];
}): Promise<{ imported: number; errors: string[] }> => {
const errors: string[] = [];
const cleanedMonitors = monitors.map((monitor) => {
const cleanData = { ...monitor };
delete cleanData.id;
delete cleanData._id;
delete cleanData.createdAt;
delete cleanData.updatedAt;
delete cleanData.recentChecks;
// Monitors must belong to current team
cleanData.teamId = teamId;
return cleanData;
});
const createdMonitors = await this.createMonitors(cleanedMonitors, userId, teamId);
if (!createdMonitors || createdMonitors.length === 0) {
throw new AppError({
message: "Failed to import any monitors. Please check the file format and try again.",
service: SERVICE_NAME,
method: "importMonitorsFromJSON",
status: 400,
});
}
return { imported: createdMonitors.length, errors };
};
}