progress
This commit is contained in:
parent
6f86377685
commit
8ecb036b1d
36 changed files with 498 additions and 212 deletions
|
@ -49,6 +49,6 @@
|
|||
"spark-md5": "^3.0.2",
|
||||
"ts-md5": "^1.3.1",
|
||||
"tsc-watch": "^6.0.4",
|
||||
"typedi": "^0.10.0"
|
||||
"uuid": "^9.0.1"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,13 +1,13 @@
|
|||
import express from "express";
|
||||
import { Routes } from "../interfaces/router";
|
||||
import cors from "cors";
|
||||
import locator from "../di/register_di";
|
||||
import { DevEnv, UnitTestEnv } from "../di/env";
|
||||
import mongoose from "mongoose";
|
||||
import { Server } from "socket.io";
|
||||
import { createServer } from "http";
|
||||
import { TypedEvent } from "../helper/typed_event";
|
||||
import { SocketSubscriber } from "./socket_controller";
|
||||
import { dirname } from "path";
|
||||
import { CreateFolderUseCase } from "../usecases/crete_folder_usecase";
|
||||
import { dirIsExists } from "../repository/fs";
|
||||
|
||||
export class App {
|
||||
public app: express.Application;
|
||||
|
@ -59,6 +59,7 @@ export class App {
|
|||
this.app.use(cors());
|
||||
this.app.use(express.json());
|
||||
this.app.use(express.urlencoded({ extended: true }));
|
||||
this.app.use(express.static("public"));
|
||||
}
|
||||
|
||||
private initializeRoutes(routes: Routes[]) {
|
||||
|
@ -72,6 +73,7 @@ export class App {
|
|||
// ? new DevEnv(this.computedFolder)
|
||||
// : new UnitTestEnv(this.computedFolder)
|
||||
// );
|
||||
await this.appStartController();
|
||||
|
||||
mongoose
|
||||
.connect("mongodb://127.0.0.1:27017/test")
|
||||
|
@ -80,4 +82,27 @@ export class App {
|
|||
console.log("ERROR:", e);
|
||||
});
|
||||
}
|
||||
|
||||
async appStartController() {
|
||||
console.log(App.staticFilesStoreDir());
|
||||
if (await dirIsExists(App.staticFilesStoreDir())) {
|
||||
return;
|
||||
}
|
||||
const createFolderUseCase = await new CreateFolderUseCase().call(
|
||||
App.staticFilesStoreDir()
|
||||
);
|
||||
|
||||
createFolderUseCase.fold(
|
||||
(_s) => {},
|
||||
(e) => {
|
||||
// TODO:(IDONTSUDO) need logger
|
||||
console.log(e);
|
||||
}
|
||||
);
|
||||
}
|
||||
static staticFilesStoreDir = () => {
|
||||
const dir = dirname(__filename);
|
||||
const rootDir = dir.slice(0, dir.length - 20);
|
||||
return rootDir + "public/";
|
||||
};
|
||||
}
|
||||
|
|
|
@ -18,17 +18,25 @@ export class CrudController<V, D> extends CoreHttpController<V> {
|
|||
this.init();
|
||||
}
|
||||
init() {
|
||||
this.routes["POST"] = new CreateDataBaseModelUseCase<D>(
|
||||
this.dataBaseModel
|
||||
).call;
|
||||
this.routes["GET"] = new PaginationDataBaseModelUseCase<D>(
|
||||
this.dataBaseModel
|
||||
).call;
|
||||
this.routes["DELETE"] = new DeleteDataBaseModelUseCase<D>(
|
||||
this.dataBaseModel
|
||||
).call;
|
||||
this.routes["PUT"] = new UpdateDataBaseModelUseCase<V, D>(
|
||||
this.dataBaseModel
|
||||
).call;
|
||||
if (this.routes["POST"] === null) {
|
||||
this.routes["POST"] = new CreateDataBaseModelUseCase<D>(
|
||||
this.dataBaseModel
|
||||
).call;
|
||||
}
|
||||
if (this.routes["GET"] === null) {
|
||||
this.routes["GET"] = new PaginationDataBaseModelUseCase<D>(
|
||||
this.dataBaseModel
|
||||
).call;
|
||||
}
|
||||
if (this.routes["DELETE"] === null) {
|
||||
this.routes["DELETE"] = new DeleteDataBaseModelUseCase<D>(
|
||||
this.dataBaseModel
|
||||
).call;
|
||||
}
|
||||
if (this.routes["PUT"] === null) {
|
||||
this.routes["PUT"] = new UpdateDataBaseModelUseCase<V, D>(
|
||||
this.dataBaseModel
|
||||
).call;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -68,7 +68,7 @@ export class CoreHttpController<V> implements ICoreHttpController {
|
|||
public delete(usecase: CallBackFunction<V>) {
|
||||
this.routes["DELETE"] = usecase;
|
||||
}
|
||||
private async requestResponseController<T>(
|
||||
public async requestResponseController<T>(
|
||||
req: Request,
|
||||
res: Response,
|
||||
usecase: CallBackFunction<T>
|
||||
|
|
29
server/src/core/repository/fs.ts
Normal file
29
server/src/core/repository/fs.ts
Normal file
|
@ -0,0 +1,29 @@
|
|||
import * as fs from "fs";
|
||||
import { promisify } from "node:util";
|
||||
|
||||
export const readFileAsync = promisify(fs.readFile);
|
||||
export const readdir = promisify(fs.readdir);
|
||||
export const stat = promisify(fs.stat);
|
||||
export const lsStat = promisify(fs.lstat);
|
||||
export const createDir = promisify(fs.mkdir);
|
||||
export const dirIsExists = promisify(fs.exists);
|
||||
|
||||
export function readDirRecursive(path: string, filesToDir: string[] = []) {
|
||||
const files = fs.readdirSync(path);
|
||||
files.forEach((file) => {
|
||||
let filePath = "";
|
||||
if (path[path.length - 1] !== "/") {
|
||||
filePath = `${path}/${file}`;
|
||||
} else {
|
||||
filePath = `${path}${file}`;
|
||||
}
|
||||
|
||||
const stats = fs.statSync(filePath);
|
||||
if (stats.isDirectory()) {
|
||||
readDirRecursive(filePath, filesToDir);
|
||||
} else {
|
||||
filesToDir.push(file);
|
||||
}
|
||||
});
|
||||
return filesToDir;
|
||||
}
|
|
@ -1,6 +1,6 @@
|
|||
import * as fs from "fs";
|
||||
import { resolve } from "node:path";
|
||||
import { promisify } from "node:util";
|
||||
|
||||
import { createHash } from "node:crypto";
|
||||
import "reflect-metadata";
|
||||
import { BinaryLike } from "crypto";
|
||||
|
@ -10,11 +10,9 @@ import {
|
|||
} from "../model/meta_data_file_manager_model";
|
||||
import { Result } from "../helper/result";
|
||||
import { TypedEvent } from "../helper/typed_event";
|
||||
import { lsStat, readFileAsync, readdir, stat } from "../repository/fs";
|
||||
|
||||
const readFileAsync = promisify(fs.readFile);
|
||||
const readdir = promisify(fs.readdir);
|
||||
const stat = promisify(fs.stat);
|
||||
const lsStat = promisify(fs.lstat);
|
||||
|
||||
|
||||
function joinBuffers(buffers: Array<Buffer>, delimiter = " ") {
|
||||
const d = Buffer.from(delimiter);
|
||||
|
@ -34,6 +32,7 @@ async function readFileAtBuffer(path: string): Promise<Buffer> {
|
|||
}
|
||||
return await readFileAsync(path);
|
||||
}
|
||||
|
||||
function md5(content: Buffer | BinaryLike): Promise<string> {
|
||||
return new Promise((resolve, _reject) => {
|
||||
return resolve(createHash("md5").update(content).digest("hex"));
|
||||
|
@ -47,6 +46,8 @@ export interface IHashesCache {
|
|||
export abstract class IFilesChangeNotifierService {
|
||||
abstract directory: string;
|
||||
}
|
||||
|
||||
|
||||
export class FilesChangeNotifierService
|
||||
extends TypedEvent<Result<Error, IHashesCache>>
|
||||
implements IFilesChangeNotifierService
|
||||
|
|
|
@ -1,11 +1,10 @@
|
|||
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 { IPipelineMeta } from "../model/pipeline_meta";
|
||||
import { IPipeline } from "../model/process_model";
|
||||
import { Iteration, StackService } from "./stack_service";
|
||||
|
||||
|
||||
export class PipelineRealTimeService extends TypedEvent<IPipelineMeta> {
|
||||
status: IPipelineMeta;
|
||||
pipelineModels?: IPipeline[];
|
||||
|
@ -13,7 +12,7 @@ export class PipelineRealTimeService extends TypedEvent<IPipelineMeta> {
|
|||
super();
|
||||
this.init();
|
||||
}
|
||||
init() {
|
||||
private init(): void {
|
||||
this.status = {
|
||||
pipelineIsRunning: false,
|
||||
projectUUID: null,
|
||||
|
|
17
server/src/core/usecases/crete_folder_usecase.ts
Normal file
17
server/src/core/usecases/crete_folder_usecase.ts
Normal file
|
@ -0,0 +1,17 @@
|
|||
import { Result } from "../helper/result";
|
||||
import { dirIsExists, createDir } from "../repository/fs";
|
||||
|
||||
export class CreateFolderUseCase {
|
||||
call = async (path: string): Promise<Result<Error, string>> => {
|
||||
try {
|
||||
if (await dirIsExists(path)) {
|
||||
return Result.error(new Error("createFolderUseCase create dir "));
|
||||
}
|
||||
await createDir(path);
|
||||
|
||||
return Result.ok("ok");
|
||||
} catch (error) {
|
||||
return Result.error(error as Error);
|
||||
}
|
||||
};
|
||||
}
|
33
server/src/features/projects/create_new_project_scenario.ts
Normal file
33
server/src/features/projects/create_new_project_scenario.ts
Normal file
|
@ -0,0 +1,33 @@
|
|||
import { App } from "../../core/controllers/app";
|
||||
import { Result } from "../../core/helper/result";
|
||||
import { CreateDataBaseModelUseCase } from "../../core/usecases/create_database_model_usecase";
|
||||
import { CreateFolderUseCase } from "../../core/usecases/crete_folder_usecase";
|
||||
import { ProjectDBModel, ProjectValidationModel } from "./projects_model";
|
||||
import { v4 as uuidv4 } from "uuid";
|
||||
|
||||
export class CreateNewProjectScenario {
|
||||
call = async (model: ProjectValidationModel): Promise<Result<Error, any>> => {
|
||||
try {
|
||||
const folderName = uuidv4() + "/";
|
||||
const createFolderUseCase = await new CreateFolderUseCase().call(
|
||||
App.staticFilesStoreDir() + folderName
|
||||
);
|
||||
if (createFolderUseCase.isFailure()) {
|
||||
return createFolderUseCase.forward();
|
||||
}
|
||||
|
||||
model.rootDir = folderName;
|
||||
const createDataBaseModelUseCase = await new CreateDataBaseModelUseCase(
|
||||
ProjectDBModel
|
||||
).call(model);
|
||||
|
||||
if (createDataBaseModelUseCase.isFailure()) {
|
||||
return createDataBaseModelUseCase.forward();
|
||||
}
|
||||
|
||||
return Result.ok({ status: "ok" });
|
||||
} catch (error) {
|
||||
return Result.error(error as Error);
|
||||
}
|
||||
};
|
||||
}
|
|
@ -1,8 +1,9 @@
|
|||
import { Schema, model } from "mongoose";
|
||||
import { PipelineModel, schemaPipeline } from "../pipelines/pipeline_model";
|
||||
import { IsMongoId, IsString } from "class-validator";
|
||||
import { IsArray, IsOptional, IsString } from "class-validator";
|
||||
|
||||
export interface IProjectModel {
|
||||
_id: string;
|
||||
pipelines: [PipelineModel];
|
||||
rootDir: string;
|
||||
description: string;
|
||||
|
@ -27,13 +28,11 @@ const schema = "Projects";
|
|||
|
||||
export const ProjectDBModel = model<IProjectModel>(schema, ProjectSchema);
|
||||
|
||||
export class ProjectModel implements IProjectModel {
|
||||
@IsMongoId()
|
||||
public pipelines: [PipelineModel];
|
||||
|
||||
@IsString()
|
||||
public rootDir: string;
|
||||
|
||||
export class ProjectValidationModel {
|
||||
@IsArray()
|
||||
public pipelines: [string];
|
||||
@IsString()
|
||||
public description: string;
|
||||
@IsOptional()
|
||||
public rootDir: string;
|
||||
}
|
||||
|
|
|
@ -1,16 +1,17 @@
|
|||
// import { TriggerDBModel, TriggerModel } from "./trigger_model";
|
||||
import { CrudController } from "../../core/controllers/crud_controller";
|
||||
import { ProjectDBModel, ProjectModel } from "./projects_model";
|
||||
import { CreateNewProjectScenario } from "./create_new_project_scenario";
|
||||
import { ProjectDBModel, ProjectValidationModel } from "./projects_model";
|
||||
|
||||
export class ProjectsPresentation extends CrudController<
|
||||
ProjectModel,
|
||||
ProjectValidationModel,
|
||||
typeof ProjectDBModel
|
||||
> {
|
||||
constructor() {
|
||||
super({
|
||||
url: "project",
|
||||
validationModel: ProjectModel,
|
||||
validationModel: ProjectValidationModel,
|
||||
databaseModel: ProjectDBModel,
|
||||
});
|
||||
super.post(new CreateNewProjectScenario().call);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,29 +1,24 @@
|
|||
import { IsString } from "class-validator";
|
||||
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";
|
||||
import { PipelineRealTimeService } from "../../core/services/pipeline_real_time_service";
|
||||
import { RunInstancePipelineUseCase } from "./usecases/run_instance_pipeline_usecase";
|
||||
import { PipelineStatusUseCase } from "./usecases/pipeline_status_usecase";
|
||||
|
||||
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 RealTimeValidationModel {
|
||||
@IsString()
|
||||
public id: string;
|
||||
}
|
||||
|
||||
export class RealTimePresentation extends CoreHttpController<void> {
|
||||
export class RealTimePresentation extends CoreHttpController<RealTimeValidationModel> {
|
||||
constructor() {
|
||||
super({
|
||||
validationModel: null,
|
||||
validationModel: RealTimeValidationModel,
|
||||
url: "realtime",
|
||||
databaseModel: null,
|
||||
});
|
||||
super.post(new RunInstancePipelineUseCase().call);
|
||||
super.get(new PipelineStatusUseCase().call);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,13 @@
|
|||
import { Result } from "../../../core/helper/result";
|
||||
import { IPipelineMeta } from "../../../core/model/pipeline_meta";
|
||||
import { pipelineRealTimeService } from "../realtime_presentation";
|
||||
|
||||
export class PipelineStatusUseCase {
|
||||
async call(): Promise<Result<Error, IPipelineMeta>> {
|
||||
try {
|
||||
return Result.ok(pipelineRealTimeService.status);
|
||||
} catch (error) {
|
||||
return Result.error(error as Error);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
import { Result } from "../../../core/helper/result";
|
||||
import { ReadByIdDataBaseModelUseCase } from "../../../core/usecases/read_by_id_database_model_usecase";
|
||||
import { IProjectModel, ProjectDBModel } from "../../projects/projects_model";
|
||||
import {
|
||||
RealTimeValidationModel,
|
||||
pipelineRealTimeService,
|
||||
} from "../realtime_presentation";
|
||||
|
||||
export class RunInstancePipelineUseCase {
|
||||
async call(model: RealTimeValidationModel): Promise<Result<Error, any>> {
|
||||
const id = model.id;
|
||||
const readByIdDataBaseModelUseCase =
|
||||
await new ReadByIdDataBaseModelUseCase<IProjectModel>(
|
||||
ProjectDBModel
|
||||
).call(id);
|
||||
if (readByIdDataBaseModelUseCase.isFailure()) {
|
||||
return readByIdDataBaseModelUseCase.forward();
|
||||
}
|
||||
|
||||
const projectModel = readByIdDataBaseModelUseCase.value;
|
||||
|
||||
pipelineRealTimeService.runPipeline(
|
||||
projectModel.pipelines,
|
||||
projectModel.rootDir,
|
||||
projectModel._id
|
||||
);
|
||||
|
||||
return Result.ok({ status: "ok" });
|
||||
}
|
||||
}
|
|
@ -1,16 +1,16 @@
|
|||
import "reflect-metadata";
|
||||
import { App } from "./core/controllers/app";
|
||||
import { SocketSubscriber } from "./core/controllers/socket_controller";
|
||||
import { Routes } from "./core/interfaces/router";
|
||||
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(),
|
||||
|
@ -24,4 +24,4 @@ const socketSubscribers = [
|
|||
];
|
||||
|
||||
new App(httpRoutes, socketSubscribers).listen();
|
||||
|
||||
|
|
@ -5,7 +5,6 @@ import { dirname__ } from "../test";
|
|||
export class PipelineRealTimeServiceTest extends PipelineRealTimeService {
|
||||
constructor() {
|
||||
super();
|
||||
this.init();
|
||||
}
|
||||
async test() {
|
||||
this.runPipeline(mockSimplePipeline, dirname__, "");
|
||||
|
|
|
@ -1,34 +1,15 @@
|
|||
import { rmSync } from "fs";
|
||||
import * as fs from "fs";
|
||||
import { StackService } from "../../src/core/services/stack_service";
|
||||
import { delay } from "../../src/core/helper/delay";
|
||||
import { assert, dirname__ } from "../test";
|
||||
import { mockSimplePipeline } from "../model/mock_pipelines";
|
||||
import { readDirRecursive } from "../../src/core/repository/fs";
|
||||
|
||||
abstract class IStackServiceTest {
|
||||
abstract test(): Promise<boolean>;
|
||||
}
|
||||
|
||||
export function readDirRecursive(path: string, filesToDir: string[] = []) {
|
||||
const files = fs.readdirSync(path);
|
||||
files.forEach((file) => {
|
||||
let filePath = "";
|
||||
if (path[path.length - 1] !== "/") {
|
||||
filePath = `${path}/${file}`;
|
||||
} else {
|
||||
filePath = `${path}${file}`;
|
||||
}
|
||||
|
||||
const stats = fs.statSync(filePath);
|
||||
if (stats.isDirectory()) {
|
||||
readDirRecursive(filePath, filesToDir);
|
||||
} else {
|
||||
filesToDir.push(file);
|
||||
}
|
||||
});
|
||||
return filesToDir;
|
||||
}
|
||||
|
||||
|
||||
class SimpleTestStackServiceTest
|
||||
extends StackService
|
||||
implements IStackServiceTest
|
||||
|
|
|
@ -14,9 +14,8 @@ import { UpdateDataBaseModelUseCaseTest } from "./usecases/update_database_model
|
|||
import { PaginationDataBaseModelUseCaseTest } from "./usecases/pagination_database_model_usecase_test";
|
||||
// import { PipelineRealTimeServiceTest } from "./services/pipeline_real_time_service_test";
|
||||
|
||||
|
||||
const testCore = TestCore.instance;
|
||||
|
||||
|
||||
export const dirname__: string = dirname(__filename);
|
||||
export const assert = testCore.assert;
|
||||
export const resultTest = testCore.resultTest;
|
||||
|
@ -24,30 +23,36 @@ const env = new UnitTestEnv(dirname__);
|
|||
|
||||
locator(env);
|
||||
|
||||
const tests = [CreateDataBaseModelUseCaseTest, DeleteDataBaseModelUseCaseTest,ReadDataBaseModelUseCaseTest,UpdateDataBaseModelUseCaseTest, PaginationDataBaseModelUseCaseTest]
|
||||
const init = async () =>{
|
||||
await mongoose.connect('mongodb://127.0.0.1:27017/test')
|
||||
}
|
||||
|
||||
const test = async () =>{
|
||||
// await new ExecutorProgramServiceTest(dirname__).test();
|
||||
// await new FilesChangerTest(dirname__).test();
|
||||
await new StackServiceTest(dirname__ + "/context/").test();
|
||||
// await new TriggerServiceTest().test();
|
||||
// await new CreateDataBaseModelUseCaseTest().test()
|
||||
const tests = [
|
||||
CreateDataBaseModelUseCaseTest,
|
||||
DeleteDataBaseModelUseCaseTest,
|
||||
ReadDataBaseModelUseCaseTest,
|
||||
UpdateDataBaseModelUseCaseTest,
|
||||
PaginationDataBaseModelUseCaseTest,
|
||||
];
|
||||
const init = async () => {
|
||||
await mongoose.connect("mongodb://127.0.0.1:27017/test");
|
||||
};
|
||||
|
||||
// await new CreateDataBaseModelUseCaseTest().test()
|
||||
// await new DeleteDataBaseModelUseCaseTest().test()
|
||||
// await new ReadDataBaseModelUseCaseTest().test()
|
||||
// await new UpdateDataBaseModelUseCaseTest().test()
|
||||
const test = async () => {
|
||||
await new ExecutorProgramServiceTest(dirname__).test();
|
||||
await new FilesChangerTest(dirname__).test();
|
||||
await new StackServiceTest(dirname__ + "/context/").test();
|
||||
await new TriggerServiceTest().test();
|
||||
await new CreateDataBaseModelUseCaseTest().test();
|
||||
|
||||
await new CreateDataBaseModelUseCaseTest().test();
|
||||
await new DeleteDataBaseModelUseCaseTest().test();
|
||||
await new ReadDataBaseModelUseCaseTest().test();
|
||||
await new UpdateDataBaseModelUseCaseTest().test();
|
||||
// await new PipelineRealTimeServiceTest().test()
|
||||
// for await (const usecase of tests) {
|
||||
// testCore.assert(await new usecase().test(), usecase.name)
|
||||
// }
|
||||
}
|
||||
for await (const usecase of tests) {
|
||||
testCore.assert(await new usecase().test(), usecase.name);
|
||||
}
|
||||
};
|
||||
const main = async () => {
|
||||
await init()
|
||||
await test()
|
||||
await init();
|
||||
await test();
|
||||
await testCore.testResult();
|
||||
};
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { CreateDataBaseModelUseCase } from "../../src/core/usecases/create_database_model_usecase";
|
||||
import { ReadByIdDataBaseModelUseCase } from "../../src/core/usecases/read_database_model_usecase";
|
||||
import { ReadByIdDataBaseModelUseCase } from "../../src/core/usecases/read_by_id_database_model_usecase";
|
||||
import { ITestModel, TestDBModel } from "../model/test_db_mongo_model";
|
||||
|
||||
export class ReadDataBaseModelUseCaseTest {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue