mvp progress

This commit is contained in:
IDONTSUDO 2023-11-10 12:06:40 +03:00
parent 9b16b25187
commit 6446da7e76
75 changed files with 1865 additions and 244 deletions

View file

@ -4,20 +4,23 @@ import cors from "cors";
import locator from "../di/register_di";
import { DevEnv, UnitTestEnv } from "../di/env";
import mongoose from "mongoose";
// import http from "http";
// import { Server } from "socket.io";
import { Server } from "socket.io";
import { createServer } from "http";
import { TypedEvent } from "../helper/typed_event";
import { SocketSubscriber } from "./socket_controller";
export class App {
public app: express.Application;
public port: number;
public env: string;
public computedFolder: string;
// public io:
constructor(routes: Routes[], computedFolder: string) {
this.port = 3000;
public socketSubscribers: SocketSubscriber<any>[];
public io: Server;
constructor(routes: Routes[], socketSubscribers: SocketSubscriber<any>[]) {
this.port = 4001;
this.socketSubscribers = socketSubscribers;
this.env = "dev";
this.app = express();
this.computedFolder = computedFolder;
this.loadAppDependencies().then(() => {
this.initializeMiddlewares();
this.initializeRoutes(routes);
@ -25,26 +28,27 @@ export class App {
}
public listen() {
// const httpServer = new http.Server();
// const io = new Server(httpServer);
const httpServer = createServer(this.app);
const io = new Server(httpServer, {
cors: { origin: "*" },
});
this.app.listen(this.port, () => {
io.on("connection", (socket) => {
this.socketSubscribers.map((el) => {
el.emitter.on((e) => {
socket.emit(el.event, e);
});
});
});
httpServer.listen(this.port, () => {
console.info(`=================================`);
console.info(`======= ENV: ${this.env} =======`);
console.info(`🚀 HTTP http://localhost:${this.port}`);
console.info(`🚀 WS ws://localhost:${this.port}`);
console.info(`=================================`);
});
// io.on("connection", (socket) => {
// socket.on("disconnect", function (msg) {
// console.log("Disconnected");
// });
// });
// setInterval(function () {
// io.emit("goodbye");
// console.log(200);
// }, 1000);
this.io = io;
}
public getServer() {
@ -63,20 +67,17 @@ export class App {
});
}
async loadAppDependencies() {
await locator(
this.env == "development"
? new DevEnv(this.computedFolder)
: new UnitTestEnv(this.computedFolder)
);
// await locator(
// this.env == "development"
// ? new DevEnv(this.computedFolder)
// : new UnitTestEnv(this.computedFolder)
// );
mongoose
.connect("mongodb://127.0.0.1:27017/test")
.then(() => console.log("Connected!"))
.then(() => {})
.catch((e) => {
console.log("ERROR:", e);
});
}
}

View file

@ -1,15 +1,11 @@
// import path from "path";
// import { TypedEvent } from "../helper/typed_event";
// import { StackService } from "../services/stack_service";
// // TODO(IDONTSUDO): up to do
import { TypedEvent } from "../helper/typed_event";
// class SocketController<T>{
// emitter:TypedEvent<T>;
// constructor(emitter:TypedEvent<T>, ){
// this.emitter = emitter
// }
// call = () =>{
// }
// }
export class SocketSubscriber<T> {
emitter: TypedEvent<T>;
event: string;
constructor(emitter: TypedEvent<T>, event: string) {
this.emitter = emitter;
this.event = event;
}
}

View file

@ -17,7 +17,6 @@ export const validationModelMiddleware = (
}
const model = plainToInstance(type, req[value]);
validate(model, { skipMissingProperties, whitelist, forbidNonWhitelisted }).then((errors: ValidationError[]) => {
console.log(errors)
if (errors.length > 0) {
const message = errors.map((error: ValidationError) => Object.values(error.constraints)).join(', ');
return res.status(400).json(message)

View file

@ -1,22 +1,19 @@
export class ExecError extends Error {
static isExecError(e: any) {
if ("type" in e && "script" in e && "unixTime" in e && 'error' in e) {
static isExecError(e: any): ExecError | void {
if ("type" in e && "script" in e && "unixTime" in e && "error" in e) {
return new ExecError(e.type, e.event, e.data);
}
return;
return;
}
script: string;
unixTime: number;
type = EXEC_TYPE.EXEC;
error:any;
constructor(
script: string,
...args: any
) {
error: any;
constructor(script: string, ...args: any) {
super(...args);
this.script = script;
this.unixTime = Date.now();
this.error = args[0]
this.error = args[0];
}
}
@ -26,17 +23,13 @@ export class SpawnError extends Error {
unixTime: number;
type = EXEC_TYPE.SPAWN;
constructor(
environment: string,
script: string,
...args: any
) {
constructor(environment: string, script: string, ...args: any) {
super(...args);
this.environment = environment;
this.script = script;
this.unixTime = Date.now();
}
static isError(errorType: any): void | SpawnError {
static isError(errorType: any): SpawnError | void {
if (
"command" in errorType &&
"error" in errorType &&

View file

@ -0,0 +1,7 @@
export interface IPipelineMeta {
pipelineIsRunning: boolean;
projectUUID?: string | null;
lastProcessCompleteCount: number | null;
error: any;
}

View file

@ -3,7 +3,7 @@ import { EXEC_TYPE } from "./exec_error_model";
export interface IPipeline {
process: IProcess;
trigger: Trigger;
trigger?: Trigger;
env: Env | null;
stackGenerateType: StackGenerateType;
}

View file

@ -9,9 +9,11 @@ import { EXEC_TYPE, ExecError, SpawnError } from "../model/exec_error_model";
abstract class IExecutorProgramService {
abstract execPath: string;
}
class P{
}
export class ExecutorProgramService
extends TypedEvent<Result<ExecError | SpawnError, ExecutorResult>>
extends TypedEvent<Result<ExecError | SpawnError, ExecutorResult>>
implements IExecutorProgramService
{
static event = "ExecutorProgramService";

View file

@ -0,0 +1,81 @@
import { TypedEvent } from "../helper/typed_event";
import { ExecError } from "../model/exec_error_model";
import { ExecutorResult } from "../model/executor_result";
import { IPipelineMeta } from "../model/pipiline_meta";
import { IPipeline } from "../model/process_model";
import { Iteration, StackService } from "./stack_service";
export class PipelineRealTimeService extends TypedEvent<IPipelineMeta> {
status: IPipelineMeta;
pipelineModels?: IPipeline[];
constructor() {
super();
this.init();
}
init() {
this.status = {
pipelineIsRunning: false,
projectUUID: null,
lastProcessCompleteCount: null,
error: null,
};
}
pipelineSubscriber = (iterations: Iteration[]): void => {
if (this.status["lastProcessCompleteCount"] === 0) {
this.status["lastProcessCompleteCount"] = 0;
}
this.status.lastProcessCompleteCount += 1;
this.pipelineCompleted();
this.iterationHelper(iterations[iterations.length - 1]);
this.emit(this.status);
};
iterationHelper(iteration: Iteration): void {
this.iterationErrorObserver(iteration);
// TODO(IDONTSUDO): implements
// this.iterationLogSaver()
}
iterationLogSaver() {
throw new Error("Method not implemented.");
}
iterationErrorObserver(iteration: Iteration): void {
if (this.status.lastProcessCompleteCount === 1) {
return;
}
const result = iteration?.result;
const executorResult = ExecutorResult.isExecutorResult(result);
const execError = ExecError.isExecError(result);
if (executorResult instanceof ExecutorResult) {
this.status.error = executorResult;
}
if (execError instanceof ExecError) {
this.status.error = execError;
}
}
pipelineCompleted() {
const pipelineIsCompleted =
this.status.lastProcessCompleteCount === this.pipelineModels?.length;
if (pipelineIsCompleted) {
this.status.pipelineIsRunning = false;
}
}
runPipeline(
pipelineModels: IPipeline[],
path: string,
projectUUID: string
): void {
const testPath = path + "/context";
const stack = new StackService(pipelineModels, testPath);
this.status["projectUUID"] = projectUUID;
this.status["pipelineIsRunning"] = true;
stack.on(this.pipelineSubscriber);
stack.call();
}
}

View file

@ -24,7 +24,10 @@ export abstract class IStackService {
abstract init(processed: IPipeline[], path: string): void;
}
export class StackService extends TypedEvent<string> implements IStackService {
export class StackService
extends TypedEvent<Iteration[]>
implements IStackService
{
callStack: Iteration[];
path: string;
constructor(processed: IPipeline[], path: string) {
@ -56,6 +59,7 @@ export class StackService extends TypedEvent<string> implements IStackService {
for await (const el of this.callStack!) {
await this.execStack(inc, el);
inc += 1;
this.emit(this.callStack);
}
}
async execStack(
@ -121,14 +125,17 @@ export class StackService extends TypedEvent<string> implements IStackService {
return promise;
}
private async triggerExec(
trigger: Trigger,
trigger: Trigger | null,
stackNumber: number
): Promise<Result<boolean, boolean>> {
const hashes = this.callStack[stackNumber].hashes;
if (trigger !== null) {
const hashes = this.callStack[stackNumber].hashes;
if (hashes != null) {
return await new TriggerService(trigger, hashes, this.path).call();
if (hashes != null) {
return await new TriggerService(trigger, hashes, this.path).call();
}
throw new Error("Hashes is null");
}
throw new Error("Hashes is null");
return Result.ok();
}
}

View file

@ -0,0 +1,3 @@
export class RegExpSearchDataBaseModelUseCase {
call = () => {};
}

View file

@ -1,6 +1,6 @@
import { IsMongoId, IsEnum } from "class-validator";
import { Schema, model } from "mongoose";
import { StackGenerateType } from "../../core/model/process_model";
import { IProcess, StackGenerateType } from "../../core/model/process_model";
import {
TriggerModel,
triggerSchema,
@ -19,10 +19,7 @@ export const PipelineSchema = new Schema({
ref: triggerSchema,
autopopulate: true,
default: null,
},
command: {
type: String,
},
}
}).plugin(require("mongoose-autopopulate"));
export const schemaPipeline = "Pipeline";
@ -34,7 +31,7 @@ export const PipelineDBModel = model<PipelineModel>(
export class PipelineModel {
@IsMongoId()
public process: PipelineModel;
public process: IProcess;
@IsMongoId()
//TODO(IDONTSUDO):NEED OPTION DECORATOR??

View file

@ -6,16 +6,16 @@ import {
IsBoolean,
} from "class-validator";
import { Schema, model } from "mongoose";
import {
IProcess,
IssueType,
} from "../../core/model/process_model";
import { IProcess, IssueType } from "../../core/model/process_model";
import { EXEC_TYPE } from "../../core/model/exec_error_model";
export const ProcessSchema = new Schema({
type: {
type: String,
},
description: {
type: String,
},
command: {
type: String,
},
@ -46,6 +46,9 @@ export class ProcessModel implements IProcess {
@IsEnum(EXEC_TYPE)
public type: EXEC_TYPE;
@IsString()
public description: string;
@IsString()
public command: string;
@ -61,9 +64,8 @@ export class ProcessModel implements IProcess {
@IsOptional()
@IsNumber()
public timeout?: number;
@IsOptional()
@IsString()
public commit?: string;
}

View file

@ -5,6 +5,7 @@ import { IsMongoId, IsString } from "class-validator";
export interface IProjectModel {
pipelines: [PipelineModel];
rootDir: string;
description: string;
}
export const ProjectSchema = new Schema({
@ -17,6 +18,9 @@ export const ProjectSchema = new Schema({
rootDir: {
type: String,
},
description: {
type: String,
},
}).plugin(require("mongoose-autopopulate"));
const schema = "Projects";
@ -25,7 +29,11 @@ export const ProjectDBModel = model<IProjectModel>(schema, ProjectSchema);
export class ProjectModel implements IProjectModel {
@IsMongoId()
pipelines: [PipelineModel];
public pipelines: [PipelineModel];
@IsString()
rootDir: string;
public rootDir: string;
@IsString()
public description: string;
}

View file

@ -0,0 +1,29 @@
import { CoreHttpController } from "../../core/controllers/http_controller";
import { Result } from "../../core/helper/result";
import { IPipelineMeta } from "../../core/model/pipiline_meta";
import {
PipelineRealTimeService,
} from "../../core/services/pipeline_real_time_service";
export const pipelineRealTimeService = new PipelineRealTimeService();
class PipelineStatusUseCase {
async call(): Promise<Result<Error, IPipelineMeta>> {
try {
return Result.ok(pipelineRealTimeService.status);
} catch (error) {
return Result.error(error as Error);
}
}
}
export class RealTimePresentation extends CoreHttpController<void> {
constructor() {
super({
validationModel: null,
url: "realtime",
databaseModel: null,
});
super.get(new PipelineStatusUseCase().call);
}
}

View file

@ -1,9 +1,10 @@
import { IsArray, IsOptional, IsEnum} from "class-validator";
import { IsArray, IsOptional, IsEnum, IsString } from "class-validator";
import { Schema, model } from "mongoose";
export interface ITriggerModel {
_id?: string;
type: string;
description: string;
value: string[];
}
@ -12,6 +13,9 @@ export const TriggerSchema = new Schema({
type: String,
require: true,
},
description: {
type: String,
},
value: {
type: Array,
require: true,
@ -20,7 +24,10 @@ export const TriggerSchema = new Schema({
export const triggerSchema = "Trigger";
export const TriggerDBModel = model<ITriggerModel>(triggerSchema, TriggerSchema);
export const TriggerDBModel = model<ITriggerModel>(
triggerSchema,
TriggerSchema
);
export enum TriggerType {
PROCESS = "PROCESS",
@ -30,6 +37,8 @@ export enum TriggerType {
export class TriggerModel implements ITriggerModel {
@IsOptional()
public _id: string;
@IsString()
public description;
@IsEnum(TriggerType)
public type: TriggerType;
@IsArray()
@ -40,4 +49,3 @@ export interface Trigger {
type: TriggerType;
value: string[];
}

View file

@ -5,17 +5,22 @@ import { TriggerPresentation } from "./features/triggers/triggers_presentation";
import { ProjectsPresentation } from "./features/projects/projects_presentation";
import { PipelinePresentation } from "./features/pipelines/pipeline_presentation";
import { ProcessPresentation } from "./features/process/process_presentation";
import { SocketSubscriber } from "./core/controllers/socket_controller";
import {
RealTimePresentation,
pipelineRealTimeService,
} from "./features/realtime/realtime_presentation";
const httpRoutes: Routes[] = [
new TriggerPresentation(),
new ProjectsPresentation(),
new ProcessPresentation(),
new PipelinePresentation(),
new RealTimePresentation(),
].map((el) => el.call());
const computedFolder = "";
const socketSubscribers = [
new SocketSubscriber(pipelineRealTimeService, "realtime"),
];
new App(httpRoutes, computedFolder).listen();
new App(httpRoutes, socketSubscribers).listen();