@grouparoo/core
Version:
The Grouparoo Core
430 lines (429 loc) • 16.7 kB
JavaScript
;
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;