webstudio/server/src/core/services/trigger_service.ts
2023-10-31 09:03:41 +03:00

143 lines
4 KiB
TypeScript

import * as vm from "node:vm";
import { IHashesCache } from "./files_change_notifier_service";
import { EventsFileChanger } from "../model/meta_data_file_manager_model";
import { Result } from "../helper/result";
import { TypedEvent } from "../helper/typed_event";
import { Trigger, TriggerType } from "../../features/triggers/trigger_model";
export class TriggerCallResult {
results: Array<TriggerSuccessResult | TriggerErrorReport>;
constructor(results: Array<TriggerSuccessResult | TriggerErrorReport>) {
this.results = results;
}
get isErrorComputed(): Result<boolean, boolean> {
for (const el of this.results) {
if (el instanceof TriggerErrorReport) {
return Result.error(true);
}
}
return Result.ok(false);
}
}
export class TriggerSuccessResult {
status: boolean;
processOutput: any;
trigger: string;
constructor(status: boolean, trigger: string, processOutput?: any) {
this.status = status;
this.processOutput = processOutput;
this.trigger = trigger;
}
}
export class TriggerErrorReport extends Error {
hashes: IHashesCache;
trigger: string | Trigger;
processOutput: any;
constructor(
hashes: IHashesCache,
trigger: string | Trigger,
processOutput?: any
) {
super();
this.hashes = hashes;
this.trigger = trigger;
this.processOutput = processOutput;
}
}
export class TriggerService extends TypedEvent<TriggerCallResult> {
context: any = {};
constructor(trigger: Trigger, hashes: IHashesCache, path: string) {
super();
this.trigger = trigger;
this.hashes = hashes;
this.path = path;
this.triggerResult = null;
this.init();
}
triggerResult: null | TriggerCallResult;
path: string;
hashes: IHashesCache;
trigger: Trigger;
private init(): void {
if (this.context["hashes"] != undefined) {
this.context["hashes"] = this.hashes;
}
}
private getAllHashesDeleteWithouts(): string[] {
return Object.entries(this.hashes).map(([k, v]) => {
if (v.event !== EventsFileChanger.delete) {
return k.replace(new RegExp(`${this.path}`), "");
}
return "";
});
}
public async call(): Promise<Result<boolean, boolean>> {
if (this.trigger.type === TriggerType.PROCESS) {
const triggerResult = await this.triggerTypeProcess();
this.emit(triggerResult);
return triggerResult.isErrorComputed;
}
if (this.trigger.type === TriggerType.FILE) {
const triggerResult = await this.triggerTypeFile();
this.emit(triggerResult);
return triggerResult.isErrorComputed;
}
return Result.error(false);
}
private triggerTypeProcess(): TriggerCallResult {
const triggerResult: TriggerSuccessResult[] = [];
for (const el of this.trigger.value) {
const processOutput = this.processCall(el);
triggerResult.push({
status: processOutput ? processOutput : false,
processOutput: processOutput,
trigger: el,
});
}
return this.reportTriggerTypeProcess(triggerResult);
}
private triggerTypeFile(): TriggerCallResult {
const files = this.getAllHashesDeleteWithouts();
return new TriggerCallResult(
this.trigger.value.map((el) => {
let result = false;
for (const file of files) {
if (result != true) {
result = new RegExp(`${el}`).test(file);
}
}
if (result === false) {
return new TriggerErrorReport(this.hashes, el);
}
return new TriggerSuccessResult(result, el);
})
);
}
private reportTriggerTypeProcess(
triggerResult: Array<TriggerSuccessResult>
): TriggerCallResult {
return new TriggerCallResult(
triggerResult.map((el) => {
if (el.status) {
return el;
} else {
return new TriggerErrorReport(this.hashes, el.trigger);
}
})
);
}
private processCall(code: string): undefined | boolean | any {
const ctx = vm.createContext(this.context);
return vm.runInContext(code, ctx);
}
}