UNPKG

@fdm-monster/server

Version:

FDM Monster is a bulk OctoPrint manager to set up, configure and monitor 3D printers. Our aim is to provide extremely optimized websocket performance and reliability.

434 lines (433 loc) 19.6 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); Object.defineProperty(exports, "YamlService", { enumerable: true, get: function() { return YamlService; } }); const _validators = require("../../handlers/validators"); const _yamlservicevalidation = require("../validators/yaml-service.validation"); const _jsyaml = require("js-yaml"); const _printerapiinterface = require("../printer-api.interface"); class YamlService { printerGroupService; printerService; printerCache; floorStore; floorService; isTypeormMode; logger; constructor(loggerFactory, printerGroupService, printerService, printerCache, floorStore, floorService, isTypeormMode){ this.printerGroupService = printerGroupService; this.printerService = printerService; this.printerCache = printerCache; this.floorStore = floorStore; this.floorService = floorService; this.isTypeormMode = isTypeormMode; this.logger = loggerFactory(YamlService.name); } async importPrintersAndFloors(yamlBuffer) { const importSpec = await (0, _jsyaml.load)(yamlBuffer); const databaseTypeSqlite = importSpec.databaseType === "sqlite"; const { exportPrinters, exportFloorGrid } = importSpec.config; for (const printer of importSpec.printers){ if (!printer.name && printer.printerName) { printer.name = printer.printerName; delete printer.printerName; } if (printer.settingsAppearance?.name) { printer.name = printer.settingsAppearance?.name; delete printer.settingsAppearance?.name; } if (databaseTypeSqlite && typeof printer.id === "string") { printer.id = parseInt(printer.id); } if (![ _printerapiinterface.OctoprintType, _printerapiinterface.MoonrakerType ].includes(printer.printerType)) { printer.printerType = _printerapiinterface.OctoprintType; } } if (databaseTypeSqlite) { for (const floor of importSpec.floors){ if (typeof floor.id === "string") { floor.id = parseInt(floor.id); } for (const printer of floor.printers){ if (typeof printer.printerId === "string") { printer.printerId = parseInt(printer.printerId); } } } } const importData = await (0, _validators.validateInput)(importSpec, _yamlservicevalidation.importPrintersFloorsYamlSchema); if (exportFloorGrid && importData.floors?.length) { for (const floor of importData.floors){ await (0, _validators.validateInput)(floor, _yamlservicevalidation.importPrinterPositionsSchema); } } this.logger.log("Analysing printers for import"); const { updateByPropertyPrinters, insertPrinters } = await this.analysePrintersUpsert(importData.printers ?? [], importData.config.printerComparisonStrategiesByPriority); this.logger.log("Analysing floors for import"); const { updateByPropertyFloors, insertFloors } = await this.analyseFloorsUpsert(importData.floors ?? [], importData.config.floorComparisonStrategiesByPriority); this.logger.log("Analysing groups for import"); const { updateByNameGroups, insertGroups } = await this.analyseUpsertGroups(importData.groups ?? []); this.logger.log(`Performing pure insert printers (${insertPrinters.length} printers)`); const printerIdMap = {}; for (const newPrinter of insertPrinters){ const state = await this.printerService.create({ ...newPrinter }); if (!newPrinter.id) { throw new Error(`Saved ID was empty ${JSON.stringify(newPrinter)}`); } printerIdMap[newPrinter.id] = state.id; } this.logger.log(`Performing update import printers (${updateByPropertyPrinters.length} printers)`); for (const updatePrinterSpec of updateByPropertyPrinters){ const updateId = updatePrinterSpec.printerId; const updatedPrinter = updatePrinterSpec.value; if (typeof updateId === "string" && this.isTypeormMode) { throw new Error("Cannot update a printer by string id in SQLite mode"); } else if (typeof updateId === "number" && !this.isTypeormMode) { throw new Error("Cannot update a printer by number id in MongoDB mode"); } const originalPrinterId = updatedPrinter.id; delete updatePrinterSpec.value.id; updatedPrinter.id = updateId; const state = await this.printerService.update(updateId, updatedPrinter); if (!updatePrinterSpec.printerId) { throw new Error("Saved ID was empty"); } printerIdMap[originalPrinterId] = state.id; } this.logger.log(`Performing pure create floors (${insertFloors.length} floors)`); const floorIdMap = {}; for (const newFloor of insertFloors){ const originalFloorId = newFloor.id; delete newFloor.id; const knownPrinterPositions = []; if (exportFloorGrid && exportPrinters) { for (const floorPosition of newFloor.printers){ const knownPrinterId = printerIdMap[floorPosition.printerId]; if (!knownPrinterId) { continue; } delete floorPosition.id; delete floorPosition.floorId; floorPosition.printerId = knownPrinterId; knownPrinterPositions.push(floorPosition); } newFloor.printers = knownPrinterPositions; } const createdFloor = await this.floorStore.create({ ...newFloor }); floorIdMap[originalFloorId] = createdFloor.id; } this.logger.log(`Performing update of floors (${updateByPropertyFloors.length} floors)`); for (const updateFloorSpec of updateByPropertyFloors){ const updateId = updateFloorSpec.floorId; if (typeof updateId === "string" && this.isTypeormMode) { throw new Error("Cannot update a floor by string id in SQLite mode"); } else if (typeof updateId === "number" && !this.isTypeormMode) { throw new Error("Cannot update a floor by number id in MongoDB mode"); } const updatedFloor = updateFloorSpec.value; const originalFloorId = updatedFloor.id; delete updatedFloor.id; const knownPrinters = []; if (exportFloorGrid && exportPrinters) { for (const floorPosition of updatedFloor?.printers){ const knownPrinterId = printerIdMap[floorPosition.printerId]; if (!knownPrinterId) { continue; } delete floorPosition.id; delete floorPosition.floorId; floorPosition.printerId = knownPrinterId; floorPosition.floorId = updateId; knownPrinters.push(floorPosition); } updatedFloor.id = updateId; updatedFloor.printers = knownPrinters; } const newFloor = await this.floorStore.update(updateId, updatedFloor); floorIdMap[originalFloorId] = newFloor.id; } await this.floorStore.loadStore(); this.logger.log(`Performing pure create groups (${insertGroups.length} groups)`); for (const group of insertGroups){ const createdGroup = await this.printerGroupService.createGroup({ name: group.name }); for (const printer of group.printers){ const knownPrinterId = printerIdMap[printer.printerId]; if (!knownPrinterId) continue; await this.printerGroupService.addPrinterToGroup(createdGroup.id, knownPrinterId); } } this.logger.log(`Performing update of grouped printer links (${updateByNameGroups.length} groups)`); for (const updateGroupSpec of updateByNameGroups){ const existingGroup = await this.printerGroupService.getGroupWithPrinters(updateGroupSpec.groupId); const existingPrinterIds = existingGroup.printers.map((p)=>p.printerId); const wantedTargetPrinterIds = updateGroupSpec.value.printers.filter((p)=>!!printerIdMap[p.printerId]).map((p)=>printerIdMap[p.printerId]); for (const unwantedId of existingPrinterIds.filter((eid)=>!wantedTargetPrinterIds.includes(eid))){ await this.printerGroupService.removePrinterFromGroup(existingGroup.id, unwantedId); } for (const nonExistingNewId of wantedTargetPrinterIds.filter((eid)=>!existingPrinterIds.includes(eid))){ await this.printerGroupService.addPrinterToGroup(existingGroup.id, nonExistingNewId); } } return { updateByPropertyPrinters, updateByPropertyFloors, insertPrinters, insertFloors, printerIdMap, floorIdMap }; } async analysePrintersUpsert(upsertPrinters, comparisonStrategies) { const existingPrinters = await this.printerService.list(); const names = existingPrinters.map((p)=>p.name.toLowerCase()); const urls = existingPrinters.map((p)=>p.printerURL); const ids = existingPrinters.map((p)=>p.id.toString()); const insertPrinters = []; const updateByPropertyPrinters = []; for (const printer of upsertPrinters){ for (const strategy of [ ...comparisonStrategies, "new" ]){ if (strategy === "name") { const comparedName = printer.name.toLowerCase(); const foundIndex = names.findIndex((n)=>n === comparedName); if (foundIndex !== -1) { if (!ids[foundIndex]) { throw new Error("Update ID is undefined"); } updateByPropertyPrinters.push({ strategy: "name", printerId: this.isTypeormMode ? parseInt(ids[foundIndex]) : ids[foundIndex], value: printer }); break; } } else if (strategy === "url") { const comparedName = printer.printerURL.toLowerCase(); const foundIndex = urls.findIndex((n)=>n === comparedName); if (foundIndex !== -1) { if (!ids[foundIndex]) { throw new Error("Update ID is undefined"); } updateByPropertyPrinters.push({ strategy: "url", printerId: this.isTypeormMode ? parseInt(ids[foundIndex]) : ids[foundIndex], value: printer }); break; } } else if (strategy === "id") { const comparedName = printer.id.toLowerCase(); const foundIndex = ids.findIndex((n)=>n === comparedName); if (foundIndex !== -1) { if (!ids[foundIndex]) { throw new Error("Update ID is undefined"); } updateByPropertyPrinters.push({ strategy: "id", printerId: this.isTypeormMode ? parseInt(ids[foundIndex]) : ids[foundIndex], value: printer }); break; } } else if (strategy === "new") { if (!printer.id) { throw new Error(JSON.stringify(printer)); } insertPrinters.push(printer); break; } } } return { updateByPropertyPrinters, insertPrinters }; } async analyseFloorsUpsert(upsertFloors, comparisonStrategy) { const existingFloors = await this.floorService.list(); const names = existingFloors.map((p)=>p.name.toLowerCase()); const floorLevels = existingFloors.map((p)=>p.floor); const ids = existingFloors.map((p)=>p.id.toString()); const insertFloors = []; const updateByPropertyFloors = []; for (const floor of upsertFloors){ for (const strategy of [ comparisonStrategy, "new" ]){ if (strategy === "name") { const comparedProperty = floor.name.toLowerCase(); const foundIndex = names.findIndex((n)=>n === comparedProperty); if (foundIndex !== -1) { if (!ids[foundIndex]) { throw new Error("IDS not found, floor name"); } updateByPropertyFloors.push({ strategy: "name", floorId: this.isTypeormMode ? parseInt(ids[foundIndex]) : ids[foundIndex], value: floor }); break; } } else if (strategy === "floor") { const comparedProperty = floor.floor; const foundIndex = floorLevels.findIndex((n)=>n === comparedProperty); if (foundIndex !== -1) { if (!ids[foundIndex]) { throw new Error("IDS not found, floor level"); } updateByPropertyFloors.push({ strategy: "floor", floorId: this.isTypeormMode ? parseInt(ids[foundIndex]) : ids[foundIndex], value: floor }); break; } } else if (strategy === "id") { const comparedProperty = floor.id.toLowerCase(); const foundIndex = ids.findIndex((n)=>n === comparedProperty); if (foundIndex !== -1) { if (!ids[foundIndex]) { throw new Error("IDS not found, floor id"); } updateByPropertyFloors.push({ strategy: "id", floorId: this.isTypeormMode ? parseInt(ids[foundIndex]) : ids[foundIndex], value: floor }); break; } } else if (strategy === "new") { insertFloors.push(floor); break; } } } return { updateByPropertyFloors, insertFloors }; } async analyseUpsertGroups(upsertGroups) { if (!this.isTypeormMode || !upsertGroups?.length) { return { updateByNameGroups: [], insertGroups: [] }; } const existingGroups = await this.printerGroupService.listGroups(); const names = existingGroups.map((p)=>p.name.toLowerCase()); const ids = existingGroups.map((p)=>p.id.toString()); const insertGroups = []; const updateByNameGroups = []; for (const group of upsertGroups){ const comparedProperty = group.name.toLowerCase(); const foundIndex = names.findIndex((n)=>n === comparedProperty); if (foundIndex !== -1) { if (!ids[foundIndex]) { throw new Error("IDS not found, group name"); } updateByNameGroups.push({ strategy: "name", groupId: parseInt(ids[foundIndex]), value: group }); break; } else { insertGroups.push(group); } } return { insertGroups, updateByNameGroups }; } async exportPrintersAndFloors(options) { const input = await (0, _validators.validateInput)(options, _yamlservicevalidation.exportPrintersFloorsYamlSchema); if (!this.isTypeormMode) { input.exportGroups = false; } const { exportFloors, exportPrinters, exportFloorGrid, exportGroups } = input; const dumpedObject = { version: process.env.npm_package_version, exportedAt: new Date(), databaseType: this.isTypeormMode ? "sqlite" : "mongo", config: input, printers: undefined, floors: undefined, groups: undefined }; if (exportPrinters) { const printers = await this.printerService.list(); dumpedObject.printers = printers.map((p)=>{ const printerId = p.id; const { apiKey } = this.printerCache.getLoginDto(printerId); return { id: printerId, disabledReason: p.disabledReason, enabled: p.enabled, printerType: p.printerType, dateAdded: p.dateAdded, name: p.name, printerURL: p.printerURL, apiKey }; }); } if (exportFloors) { const floors = await this.floorStore.listCache(); dumpedObject.floors = floors.map((f)=>{ const dumpedFloor = { id: f.id, floor: f.floor, name: f.name, printers: undefined }; if (exportFloorGrid) { dumpedFloor.printers = f.printers.map((p)=>{ const fPrinterId = p.printerId; return { printerId: fPrinterId, x: p.x, y: p.y }; }); } return dumpedFloor; }); } if (exportGroups && this.isTypeormMode) { const groups = await this.printerGroupService.listGroups(); dumpedObject.groups = groups.map((g)=>{ return { name: g.name, id: g.id, printers: g.printers.map((p)=>{ return { printerId: p.printerId }; }) }; }); } return (0, _jsyaml.dump)(dumpedObject, {}); } } //# sourceMappingURL=yaml.service.js.map