UNPKG

@grouparoo/core

Version:
377 lines (376 loc) 15 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; return c > 3 && r && Object.defineProperty(target, key, r), r; }; var __importStar = (this && this.__importStar) || function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); __setModuleDefault(result, mod); return result; }; var __metadata = (this && this.__metadata) || function (k, v) { if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); }; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; var Run_1; Object.defineProperty(exports, "__esModule", { value: true }); exports.Run = void 0; const sequelize_1 = __importStar(require("sequelize")); const GrouparooRecord_1 = require("./GrouparooRecord"); const sequelize_typescript_1 = require("sequelize-typescript"); const actionhero_1 = require("actionhero"); const Schedule_1 = require("./Schedule"); const Import_1 = require("./Import"); const Group_1 = require("./Group"); const stateMachine_1 = require("./../modules/stateMachine"); const Property_1 = require("./Property"); const TeamMember_1 = require("./TeamMember"); const runs_1 = require("../modules/ops/runs"); const plugin_1 = require("../modules/plugin"); const moment_1 = __importDefault(require("moment")); const apiData_1 = require("../modules/apiData"); const commonModel_1 = require("../classes/commonModel"); const GrouparooModel_1 = require("./GrouparooModel"); const RUN_CREATORS = [ "schedule", "property", "group", "grouparooModel", "task", "teamMember", ]; const STATES = ["draft", "running", "complete", "stopped"]; // we have no checks, as those are managed by the lifecycle methods below (and tasks) const STATE_TRANSITIONS = [ { from: "draft", to: "running", checks: [] }, { from: "draft", to: "complete", checks: [] }, { from: "draft", to: "stopped", checks: [] }, { from: "running", to: "complete", checks: [] }, { from: "running", to: "stopped", checks: [] }, ]; let Run = Run_1 = class Run extends commonModel_1.CommonModel { idPrefix() { return "run"; } get highWaterMark() { // @ts-ignore return JSON.parse(this.getDataValue("highWaterMark") || "{}"); } set highWaterMark(value) { // @ts-ignore this.setDataValue("highWaterMark", JSON.stringify(value)); } async determinePercentComplete(logPercentMessage = true, write = true) { const percentComplete = await runs_1.RunOps.determinePercentComplete(this); if (write) await this.update({ percentComplete }); if (logPercentMessage) { (0, actionhero_1.log)(`run ${this.id} is ${this.percentComplete}% complete`); } return percentComplete; } async afterBatch(newSate) { await this.reload(); if (newSate && this.state !== newSate && !["complete", "stopped"].includes(this.state)) { const percentComplete = await this.determinePercentComplete(true, false); await this.update({ state: newSate, percentComplete }); } if (this.state === "complete" && !this.completedAt) { await this.update({ completedAt: new Date(), percentComplete: 100 }); await this.buildErrorMessage(); } if (this.percentComplete >= 100) { await this.update({ percentComplete: 100 }); } } async updateTotals() { return runs_1.RunOps.updateTotals(this); } async buildErrorMessage() { const importErrorCounts = await Import_1.Import.findAll({ attributes: ["errorMessage", [sequelize_1.default.fn("COUNT", "id"), "errorCount"]], where: { creatorId: this.id, creatorType: "run", errorMessage: { [sequelize_1.Op.not]: null }, }, group: ["errorMessage"], }); const errorMessage = importErrorCounts .map((emc) => `${emc.errorMessage} (x${emc.get("errorCount")})`) .sort() .join("\r\n"); if (importErrorCounts.length > 0) this.error = errorMessage; return this.save(); } async stop() { await this.update({ state: "stopped", completedAt: new Date() }); await this.buildErrorMessage(); } /** * Used to find the previous run for this connection. Useful in Connection#run to get the timestamp of the last time the connection was run. * Will ensure that the run returned did not error and read at least one record */ async previousRun() { const setting = await plugin_1.plugin.readSetting("core", "runs-previous-can-include-errors"); const where = { creatorId: this.creatorId, importsCreated: { [sequelize_1.Op.gt]: 0 }, id: { [sequelize_1.Op.not]: this.id }, state: "complete", }; if (setting.value === "false") { where["error"] = { [sequelize_1.Op.eq]: null }; } const previousRun = await Run_1.findOne({ where, order: [["createdAt", "desc"]], limit: 1, }); return previousRun; } /** * This method tries to import a random record to check if the Properties are valid */ async test() { const record = await GrouparooRecord_1.GrouparooRecord.findOne({ order: [["id", "asc"]], }); if (record) { try { await record.import(false, null); } catch (error) { this.error = error.toString(); this.state = "stopped"; await this.save(); throw error; } } } async quantizedTimeline(steps = 25) { return runs_1.RunOps.quantizedTimeline(this, steps); } async apiData() { const creatorName = await this.getCreatorName(); return { id: this.id, creatorId: this.creatorId, creatorName, creatorType: this.creatorType, state: this.state, percentComplete: this.percentComplete, importsCreated: this.importsCreated, recordsCreated: this.recordsCreated, recordsImported: this.recordsImported, error: this.error, highWaterMark: this.highWaterMark, sourceOffset: this.sourceOffset, memberLimit: this.memberLimit, memberOffset: this.memberOffset, method: this.method, destinationId: this.destinationId, createdAt: apiData_1.APIData.formatDate(this.createdAt), updatedAt: apiData_1.APIData.formatDate(this.updatedAt), completedAt: apiData_1.APIData.formatDate(this.completedAt), }; } async getCreatorName() { let name = "unknown"; try { if (this.creatorType === "group") { const group = await Group_1.Group.findById(this.creatorId); name = group.name; } else if (this.creatorType === "property") { const property = await Property_1.Property.findById(this.creatorId); name = property.key; } else if (this.creatorType === "grouparooModel") { const model = await GrouparooModel_1.GrouparooModel.findById(this.creatorId); name = model.name; } else if (this.creatorType === "schedule") { const schedule = await Schedule_1.Schedule.findById(this.creatorId); const source = await schedule.$get("source", { scope: null }); name = source.name; } else if (this.creatorType === "teamMember") { const teamMember = await TeamMember_1.TeamMember.findById(this.creatorId); name = `${teamMember.firstName} ${teamMember.lastName}`; } else if (this.creatorType === "task") { name = this.creatorId; } } catch (error) { // likely the creator has been deleted } return name; } // --- Class Methods --- // static async ensureCreatorReady(instance) { let ready = true; // properties are ok to enqueue if they are in draft at the time. Options update before state if (instance.creatorType === "group") { let creator = await Group_1.Group.findById(instance.creatorId); if (creator.state === "draft") ready = false; } if (instance.creatorType === "schedule") { let creator = await Schedule_1.Schedule.findById(instance.creatorId); if (creator.state === "draft") ready = false; } if (!ready) throw new Error(`creator ${instance.creatorType} is not ready`); } static async updateState(instance) { await stateMachine_1.StateMachine.transition(instance, STATE_TRANSITIONS); } static async testRun(instance) { return instance.test(); } static async sweep(limit) { const days = parseInt((await plugin_1.plugin.readSetting("core", "sweeper-delete-old-runs-days")).value); const count = await Run_1.destroy({ where: { state: { [sequelize_1.Op.in]: ["stopped", "complete"] }, createdAt: { [sequelize_1.Op.lt]: (0, moment_1.default)().subtract(days, "days").toDate(), }, }, force: true, limit, }); return { count, days }; } }; __decorate([ (0, sequelize_typescript_1.AllowNull)(false), (0, sequelize_typescript_1.ForeignKey)(() => Schedule_1.Schedule), sequelize_typescript_1.Column, __metadata("design:type", String) ], Run.prototype, "creatorId", void 0); __decorate([ (0, sequelize_typescript_1.Column)(sequelize_typescript_1.DataType.ENUM(...RUN_CREATORS)), __metadata("design:type", Object) ], Run.prototype, "creatorType", void 0); __decorate([ (0, sequelize_typescript_1.AllowNull)(false), (0, sequelize_typescript_1.Column)(sequelize_typescript_1.DataType.ENUM(...STATES)), __metadata("design:type", Object) ], Run.prototype, "state", void 0); __decorate([ sequelize_typescript_1.Column, __metadata("design:type", Date) ], Run.prototype, "completedAt", void 0); __decorate([ (0, sequelize_typescript_1.AllowNull)(false), (0, sequelize_typescript_1.Default)(0), sequelize_typescript_1.Column, __metadata("design:type", Number) ], Run.prototype, "importsCreated", void 0); __decorate([ (0, sequelize_typescript_1.AllowNull)(false), (0, sequelize_typescript_1.Default)(0), sequelize_typescript_1.Column, __metadata("design:type", Number) ], Run.prototype, "recordsCreated", void 0); __decorate([ (0, sequelize_typescript_1.AllowNull)(false), (0, sequelize_typescript_1.Default)(0), sequelize_typescript_1.Column, __metadata("design:type", Number) ], Run.prototype, "recordsImported", void 0); __decorate([ sequelize_typescript_1.Column, __metadata("design:type", String) ], Run.prototype, "error", void 0); __decorate([ (0, sequelize_typescript_1.Column)(sequelize_typescript_1.DataType.STRING), __metadata("design:type", Object), __metadata("design:paramtypes", [Object]) ], Run.prototype, "highWaterMark", null); __decorate([ (0, sequelize_typescript_1.Column)(sequelize_typescript_1.DataType.STRING), __metadata("design:type", Object) ], Run.prototype, "sourceOffset", void 0); __decorate([ (0, sequelize_typescript_1.Default)(0), sequelize_typescript_1.Column, __metadata("design:type", Number) ], Run.prototype, "memberLimit", void 0); __decorate([ (0, sequelize_typescript_1.Default)(0), sequelize_typescript_1.Column, __metadata("design:type", Number) ], Run.prototype, "memberOffset", void 0); __decorate([ sequelize_typescript_1.Column, __metadata("design:type", String) ], Run.prototype, "method", void 0); __decorate([ (0, sequelize_typescript_1.Default)(0), sequelize_typescript_1.Column, __metadata("design:type", Number) ], Run.prototype, "percentComplete", void 0); __decorate([ sequelize_typescript_1.Column, __metadata("design:type", String) ], Run.prototype, "destinationId", void 0); __decorate([ (0, sequelize_typescript_1.BelongsTo)(() => Schedule_1.Schedule), __metadata("design:type", Schedule_1.Schedule) ], Run.prototype, "schedule", void 0); __decorate([ (0, sequelize_typescript_1.HasMany)(() => Import_1.Import, "creatorId"), __metadata("design:type", Array) ], Run.prototype, "imports", void 0); __decorate([ sequelize_typescript_1.BeforeCreate, __metadata("design:type", Function), __metadata("design:paramtypes", [Run]), __metadata("design:returntype", Promise) ], Run, "ensureCreatorReady", null); __decorate([ sequelize_typescript_1.BeforeSave, __metadata("design:type", Function), __metadata("design:paramtypes", [Run]), __metadata("design:returntype", Promise) ], Run, "updateState", null); __decorate([ sequelize_typescript_1.AfterCreate, __metadata("design:type", Function), __metadata("design:paramtypes", [Run]), __metadata("design:returntype", Promise) ], Run, "testRun", null); Run = Run_1 = __decorate([ (0, sequelize_typescript_1.Table)({ tableName: "runs", paranoid: false }) ], Run); exports.Run = Run;