UNPKG

@grouparoo/core

Version:
430 lines (429 loc) 16.7 kB
"use strict"; 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 __metadata = (this && this.__metadata) || function (k, v) { if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); }; var Schedule_1; Object.defineProperty(exports, "__esModule", { value: true }); exports.Schedule = void 0; const actionhero_1 = require("actionhero"); const sequelize_typescript_1 = require("sequelize-typescript"); const sequelize_1 = require("sequelize"); const Source_1 = require("./Source"); const Property_1 = require("./Property"); const Run_1 = require("./Run"); const Option_1 = require("./Option"); const optionHelper_1 = require("./../modules/optionHelper"); const stateMachine_1 = require("./../modules/stateMachine"); const schedule_1 = require("../modules/ops/schedule"); const lockableHelper_1 = require("../modules/lockableHelper"); const configWriter_1 = require("../modules/configWriter"); const cls_1 = require("../modules/cls"); const apiData_1 = require("../modules/apiData"); const filterHelper_1 = require("../modules/filterHelper"); const Filter_1 = require("./Filter"); const commonModel_1 = require("../classes/commonModel"); const STATES = ["draft", "ready"]; const STATE_TRANSITIONS = [ { from: "draft", to: "ready", checks: [(instance) => instance.validateOptions()], }, ]; let Schedule = Schedule_1 = class Schedule extends commonModel_1.CommonModel { idPrefix() { return "sch"; } async getOptions(sourceFromEnvironment = true) { return optionHelper_1.OptionHelper.getOptions(this, sourceFromEnvironment); } async setOptions(options, externallyValidate = true) { return optionHelper_1.OptionHelper.setOptions(this, options, externallyValidate); } async afterSetOptions(hasChanges) { if (!hasChanges) return; if (this.state !== "ready") return; await this.resetHighWatermarks(); await this.enqueueRun(); } async validateOptions(options, externallyValidate = true) { if (!options) options = await this.getOptions(true); if (!externallyValidate) return; const pluginOptions = await this.pluginOptions(); const optionsSpec = pluginOptions.map((opt) => { var _a; return ({ ...opt, options: (_a = opt.options) === null || _a === void 0 ? void 0 : _a.map((o) => o.key), }); }); await optionHelper_1.OptionHelper.validateOptions(this, options, optionsSpec); } async getPlugin() { return optionHelper_1.OptionHelper.getPlugin(this); } async pluginOptions() { return schedule_1.ScheduleOps.pluginOptions(this); } async getFilters() { return filterHelper_1.FilterHelper.getFilters(this); } async setFilters(filters, externallyValidate = true) { return filterHelper_1.FilterHelper.setFilters(this, filters, externallyValidate); } async afterSetFilters(hasChanges) { if (!hasChanges) return; if (this.state !== "ready") return; await this.resetHighWatermarks(); await this.enqueueRun(); } async shouldRun(options = {}) { return schedule_1.ScheduleOps.shouldRun(this, options); } async runPercentComplete(run) { return schedule_1.ScheduleOps.runPercentComplete(this, run); } async apiData() { const options = await this.getOptions(null); const filters = await this.getFilters(); const { pluginConnection } = await this.getPlugin(); return { id: this.id, name: this.name, state: this.state, sourceId: this.sourceId, incremental: this.incremental, recurring: this.recurring, refreshEnabled: this.refreshEnabled, locked: this.locked, confirmRecords: this.confirmRecords, supportIncrementalSchedule: pluginConnection.supportIncrementalSchedule, options, filters, recurringFrequency: this.recurringFrequency, createdAt: apiData_1.APIData.formatDate(this.createdAt), updatedAt: apiData_1.APIData.formatDate(this.updatedAt), }; } async enqueueRun() { const run = await Run_1.Run.create({ creatorId: this.id, creatorType: "schedule", state: "running", }); await cls_1.CLS.enqueueTask("schedule:run", { scheduleId: this.id, runId: run.id }, "schedules"); return run; } async resetHighWatermarks() { let runs = await this.$get("runs", { where: { highWaterMark: { [sequelize_1.Op.ne]: null } }, scope: null, limit: 1, }); while (runs.length > 0) { runs = await this.$get("runs", { where: { highWaterMark: { [sequelize_1.Op.ne]: null } }, scope: null, limit: 100, }); for (const run of runs) if (run.state === "running") await run.stop(); // sequelize doesn't seem to be able to set a value back to null if (runs.length > 0) { await actionhero_1.api.sequelize.query(`UPDATE "runs" SET "highWaterMark" = NULL WHERE "id" IN (${runs .map((r) => `'${r.id}'`) .join(", ")});`); } } } async run(run) { return schedule_1.ScheduleOps.run(this, run); } getConfigId() { return this.idIsDefault() ? configWriter_1.ConfigWriter.generateId(this.name) : this.id; } async getConfigObject() { var _a; const { name, recurring, incremental, recurringFrequency, confirmRecords, refreshEnabled, } = this; this.source = await this.$get("source"); const sourceId = (_a = this.source) === null || _a === void 0 ? void 0 : _a.getConfigId(); if (!sourceId || !name) return; const options = await this.getOptions(false); const filters = await this.getFilters(); return { class: "Schedule", id: this.getConfigId(), name, sourceId, incremental, recurring, recurringFrequency, confirmRecords, refreshEnabled, options, filters, }; } // --- Class Methods --- // static async ensureSourceOptions(instance) { const source = await Source_1.Source.findById(instance.sourceId); const sourceOptions = await source.getOptions(true); await source.validateOptions(sourceOptions); } static async noUpdateIfLocked(instance) { await lockableHelper_1.LockableHelper.beforeSave(instance, ["state"]); } static async checkRecurringFrequency(instance) { // we cannot use the @Min validator as null is also allowed if (instance.recurring) { if (!instance.recurringFrequency || instance.recurringFrequency < 1000 * 60) { throw new Error("recurring frequency is required to be one minute or greater"); } } } static async ensureName(instance) { if (!instance.name) { const source = await Source_1.Source.findById(instance.sourceId); instance.name = `${source.name} schedule`; } } static async ensureSetIncremental(instance) { if (instance.isNewRecord && typeof instance.incremental === "undefined") { const { pluginConnection } = await instance.getPlugin(); instance.incremental = Boolean(pluginConnection.supportIncrementalSchedule); } } static async ensureSourceCanUseSchedule(instance) { const source = await Source_1.Source.findById(instance.sourceId); if (source.state !== "ready") throw new Error("source is not ready"); const scheduleAvailable = await source.scheduleAvailable(); if (!scheduleAvailable) { throw new Error(`source ${source.name} (${instance.sourceId}) cannot have a schedule`); } } static async ensureOnePerSource(instance) { const existingCount = await Schedule_1.scope(null).count({ where: { sourceId: instance.sourceId, }, }); if (existingCount > 0) { throw new Error(`source ${instance.sourceId} already has a schedule`); } } static async checkIncremental(instance) { const { pluginConnection } = await instance.getPlugin(); if (instance.incremental && !pluginConnection.supportIncrementalSchedule) { throw new Error(`${pluginConnection.name} does not support incremental schedules`); } } static async updateState(instance) { await stateMachine_1.StateMachine.transition(instance, STATE_TRANSITIONS); } static async runAfterSave(instance) { const shouldRun = await instance.shouldRun(); if (shouldRun) await instance.enqueueRun(); } static async noDestroyIfLocked(instance) { await lockableHelper_1.LockableHelper.beforeDestroy(instance); } static async destroyAppOptions(instance) { return Option_1.Option.destroy({ where: { ownerId: instance.id, ownerType: "schedule" }, }); } static async destroyFilters(instance) { await Filter_1.Filter.destroy({ where: { ownerId: instance.id, ownerType: "schedule" }, }); } static async destroyRuns(instance) { const runs = await Run_1.Run.findAll({ where: { creatorId: instance.id }, }); for (const i in runs) await runs[i].destroy(); } }; __decorate([ (0, sequelize_typescript_1.AllowNull)(false), sequelize_typescript_1.Column, (0, sequelize_typescript_1.ForeignKey)(() => Source_1.Source), __metadata("design:type", String) ], Schedule.prototype, "sourceId", void 0); __decorate([ (0, sequelize_typescript_1.Length)({ min: 0, max: 191 }), (0, sequelize_typescript_1.Default)(""), sequelize_typescript_1.Column, __metadata("design:type", String) ], Schedule.prototype, "name", void 0); __decorate([ (0, sequelize_typescript_1.AllowNull)(false), (0, sequelize_typescript_1.Default)("draft"), (0, sequelize_typescript_1.Column)(sequelize_typescript_1.DataType.ENUM(...STATES)), __metadata("design:type", Object) ], Schedule.prototype, "state", void 0); __decorate([ sequelize_typescript_1.Column, __metadata("design:type", String) ], Schedule.prototype, "locked", void 0); __decorate([ (0, sequelize_typescript_1.AllowNull)(false), sequelize_typescript_1.Column, __metadata("design:type", Boolean) ], Schedule.prototype, "incremental", void 0); __decorate([ (0, sequelize_typescript_1.AllowNull)(false), (0, sequelize_typescript_1.Default)(false), sequelize_typescript_1.Column, __metadata("design:type", Boolean) ], Schedule.prototype, "recurring", void 0); __decorate([ sequelize_typescript_1.Column, __metadata("design:type", Number) ], Schedule.prototype, "recurringFrequency", void 0); __decorate([ (0, sequelize_typescript_1.AllowNull)(false), (0, sequelize_typescript_1.Default)(false), sequelize_typescript_1.Column, __metadata("design:type", Boolean) ], Schedule.prototype, "confirmRecords", void 0); __decorate([ (0, sequelize_typescript_1.AllowNull)(false), (0, sequelize_typescript_1.Default)(true), sequelize_typescript_1.Column, __metadata("design:type", Boolean) ], Schedule.prototype, "refreshEnabled", void 0); __decorate([ (0, sequelize_typescript_1.HasMany)(() => Option_1.Option, { foreignKey: "ownerId", scope: { ownerType: "schedule" }, }), __metadata("design:type", Array) ], Schedule.prototype, "__options", void 0); __decorate([ (0, sequelize_typescript_1.HasMany)(() => Filter_1.Filter, { foreignKey: "ownerId", scope: { ownerType: "schedule" }, }), __metadata("design:type", Array) ], Schedule.prototype, "filters", void 0); __decorate([ (0, sequelize_typescript_1.HasMany)(() => Run_1.Run, { foreignKey: "creatorId", scope: { creatorType: "schedule" }, }), __metadata("design:type", Array) ], Schedule.prototype, "runs", void 0); __decorate([ (0, sequelize_typescript_1.BelongsTo)(() => Source_1.Source), __metadata("design:type", Source_1.Source) ], Schedule.prototype, "source", void 0); __decorate([ sequelize_typescript_1.BeforeSave, __metadata("design:type", Function), __metadata("design:paramtypes", [Schedule]), __metadata("design:returntype", Promise) ], Schedule, "ensureSourceOptions", null); __decorate([ sequelize_typescript_1.BeforeSave, __metadata("design:type", Function), __metadata("design:paramtypes", [Schedule]), __metadata("design:returntype", Promise) ], Schedule, "noUpdateIfLocked", null); __decorate([ sequelize_typescript_1.BeforeUpdate, __metadata("design:type", Function), __metadata("design:paramtypes", [Schedule]), __metadata("design:returntype", Promise) ], Schedule, "checkRecurringFrequency", null); __decorate([ sequelize_typescript_1.BeforeValidate, __metadata("design:type", Function), __metadata("design:paramtypes", [Schedule]), __metadata("design:returntype", Promise) ], Schedule, "ensureName", null); __decorate([ sequelize_typescript_1.BeforeValidate, __metadata("design:type", Function), __metadata("design:paramtypes", [Schedule]), __metadata("design:returntype", Promise) ], Schedule, "ensureSetIncremental", null); __decorate([ sequelize_typescript_1.BeforeCreate, __metadata("design:type", Function), __metadata("design:paramtypes", [Schedule]), __metadata("design:returntype", Promise) ], Schedule, "ensureSourceCanUseSchedule", null); __decorate([ sequelize_typescript_1.BeforeCreate, __metadata("design:type", Function), __metadata("design:paramtypes", [Schedule]), __metadata("design:returntype", Promise) ], Schedule, "ensureOnePerSource", null); __decorate([ sequelize_typescript_1.BeforeSave, __metadata("design:type", Function), __metadata("design:paramtypes", [Schedule]), __metadata("design:returntype", Promise) ], Schedule, "checkIncremental", null); __decorate([ sequelize_typescript_1.BeforeSave, __metadata("design:type", Function), __metadata("design:paramtypes", [Schedule]), __metadata("design:returntype", Promise) ], Schedule, "updateState", null); __decorate([ sequelize_typescript_1.AfterSave, __metadata("design:type", Function), __metadata("design:paramtypes", [Schedule]), __metadata("design:returntype", Promise) ], Schedule, "runAfterSave", null); __decorate([ sequelize_typescript_1.BeforeDestroy, __metadata("design:type", Function), __metadata("design:paramtypes", [Schedule]), __metadata("design:returntype", Promise) ], Schedule, "noDestroyIfLocked", null); __decorate([ sequelize_typescript_1.AfterDestroy, __metadata("design:type", Function), __metadata("design:paramtypes", [Schedule]), __metadata("design:returntype", Promise) ], Schedule, "destroyAppOptions", null); __decorate([ sequelize_typescript_1.AfterDestroy, __metadata("design:type", Function), __metadata("design:paramtypes", [Property_1.Property]), __metadata("design:returntype", Promise) ], Schedule, "destroyFilters", null); __decorate([ sequelize_typescript_1.AfterDestroy, __metadata("design:type", Function), __metadata("design:paramtypes", [Schedule]), __metadata("design:returntype", Promise) ], Schedule, "destroyRuns", null); Schedule = Schedule_1 = __decorate([ (0, sequelize_typescript_1.DefaultScope)(() => ({ where: { state: "ready" }, })), (0, sequelize_typescript_1.Table)({ tableName: "schedules", paranoid: false }) ], Schedule); exports.Schedule = Schedule;