Merge branch '4-fix/mvp-ui' into 'main'

Resolve "Реализовать запуск команд через UI с обратной связью о результате исполнения"

Closes #4

See merge request robossembler/webservice!4
This commit is contained in:
Igor Brylyov 2023-12-19 18:17:59 +00:00
commit 1754c24d79
175 changed files with 4686 additions and 851 deletions

BIN
.DS_Store vendored

Binary file not shown.

10
.vscode/launch.json vendored
View file

@ -25,6 +25,16 @@
"run-script", "test:watch" "run-script", "test:watch"
], ],
"cwd": "${workspaceRoot}/server/", "cwd": "${workspaceRoot}/server/",
},
{
"type": "node",
"request": "launch",
"name": "ui-dev",
"runtimeExecutable": "npm",
"runtimeArgs": [
"run-script", "dev"
],
"cwd": "${workspaceRoot}/ui/",
} }
] ]
} }

40
.vscode/settings.json vendored
View file

@ -1,24 +1,20 @@
{ {
"files.exclude": { "files.exclude": {
"server/build/src/core/*controllers.*": true, "**/.git": false,
"server/build/src/core/*di": true, "**/.svn": false,
"server/build/src/core/*di.*": true, "**/.hg": false,
"server/build/src/core/*extensions": true, "**/CVS": false,
"server/build/src/core/*extensions.*": true, "**/__pycache__": false,
"server/build/src/core/*helper": true, "*.DS_Store": false,
"server/build/src/core/*helper.*": true, "*.DS_Store.*": false,
"server/build/src/core/*interfaces": true, "*.git": false,
"server/build/src/core/*interfaces.*": true, "*.git.*": false,
"server/build/src/core/*middlewares": true, "*.vscode": false,
"server/build/src/core/*middlewares.*": true, "*.vscode.*": false,
"server/build/src/core/*model": true, "*server.*": false,
"server/build/src/core/*model.*": true, "*ui": false,
"server/build/src/core/*services": true, "*ui.*": false
"server/build/src/core/*services.*": true, },
"server/build/src/core/*usecases": true, "cSpell.words": ["antd", "fileupload", "metadatas", "undici", "uuidv"],
"server/build/src/core/*usecases.*": true, "editor.rulers": [100]
"server/src/core/model/exec_error_model.js": true, }
"**/*.map": true,
"**/*.js": true
}
}

3
server/.gitignore vendored
View file

@ -7,4 +7,5 @@ coverage
package-lock.json package-lock.json
.*.swp .*.swp
build/ build/
model_create.ts model_create.ts
public

View file

@ -5,9 +5,9 @@
"main": "index.js", "main": "index.js",
"scripts": { "scripts": {
"pretest": "tsc", "pretest": "tsc",
"test": "ts-node ./build/test/test.js", "test:unit": "NODE_ENV=unit tsc-watch --onSuccess 'ts-node ./build/test/test.js'",
"test:watch": "tsc-watch --onSuccess 'ts-node ./build/test/test.js'", "test:e2e": "NODE_ENV=e2e tsc-watch --onSuccess 'ts-node ./build/test/test.js'",
"dev": "tsc-watch --onSuccess 'ts-node ./build/src/main.js'" "dev": "NODE_ENV=dev tsc-watch --onSuccess 'ts-node ./build/src/main.js'"
}, },
"author": "IDONTSUDO", "author": "IDONTSUDO",
"devDependencies": { "devDependencies": {
@ -31,12 +31,13 @@
}, },
"dependencies": { "dependencies": {
"@grpc/grpc-js": "^1.9.0", "@grpc/grpc-js": "^1.9.0",
"axios": "^1.6.2",
"babel-register": "^6.26.0", "babel-register": "^6.26.0",
"class-transformer": "^0.5.1", "class-transformer": "^0.5.1",
"class-validator": "^0.14.0", "class-validator": "^0.14.0",
"concurrently": "^8.2.0",
"cors": "^2.8.5", "cors": "^2.8.5",
"express": "^4.18.2", "express": "^4.18.2",
"express-fileupload": "^1.4.2",
"first-di": "^1.0.11", "first-di": "^1.0.11",
"md5": "^2.3.0", "md5": "^2.3.0",
"mongoose": "^7.6.2", "mongoose": "^7.6.2",
@ -49,6 +50,6 @@
"spark-md5": "^3.0.2", "spark-md5": "^3.0.2",
"ts-md5": "^1.3.1", "ts-md5": "^1.3.1",
"tsc-watch": "^6.0.4", "tsc-watch": "^6.0.4",
"typedi": "^0.10.0" "uuid": "^9.0.1"
} }
} }

View file

@ -1,32 +1,67 @@
import express from "express"; import express from "express";
import { Routes } from "../interfaces/router"; import { Routes } from "../interfaces/router";
import cors from "cors"; import cors from "cors";
import locator from "../di/register_di"; import { Server } from "socket.io";
import { DevEnv, UnitTestEnv } from "../di/env"; import { createServer } from "http";
import mongoose from "mongoose"; import { SocketSubscriber } from "./socket_controller";
import http from "http"; import { dirname } from "path";
// import { Server } from "socket.io"; import fileUpload from "express-fileupload";
import { SetLastActivePipelineToRealTimeServiceScenario } from "../scenarios/set_active_pipeline_to_realtime_service_scenario";
import { CheckAndCreateStaticFilesFolderUseCase } from "../usecases/check_and_create_static_files_folder_usecase";
import { DataBaseConnectUseCase } from "../usecases/database_connect_usecase";
import { TypedEvent } from "../helpers/typed_event";
export class App { export enum ServerStatus {
init = "init",
finished = "finshed",
error = "error",
}
export enum Environment {
DEV = "DEV",
E2E_TEST = "E2E_TEST",
}
export class App extends TypedEvent<ServerStatus> {
public app: express.Application; public app: express.Application;
public port: number; public port: number;
public env: string; public env: Environment;
public computedFolder: string; public socketSubscribers: SocketSubscriber<any>[];
// public io: public io: Server;
constructor(routes: Routes[], computedFolder: string) { status: ServerStatus;
this.port = 3000;
this.env = "dev"; constructor(routes: Routes[] = [], socketSubscribers: SocketSubscriber<any>[] = [], env = Environment.DEV) {
this.loadAppDependencies().then(() => { super();
this.app = express(); this.init(routes, socketSubscribers, env);
this.initializeMiddlewares();
this.initializeRoutes(routes);
this.computedFolder = computedFolder;
});
} }
public init(routes: Routes[], socketSubscribers: SocketSubscriber<any>[], env: Environment) {
this.port = 4001;
this.socketSubscribers = socketSubscribers;
this.env = env;
this.app = express();
this.setServerStatus(ServerStatus.init);
this.loadAppDependencies().then(() => {
this.initializeMiddlewares();
this.initializeRoutes(routes);
if (this.status !== ServerStatus.error) {
this.setServerStatus(ServerStatus.finished);
}
});
}
public listen() { public listen() {
const httpServer = new http.Server(this.app); const httpServer = createServer(this.app);
// const io = new Server(httpServer); const io = new Server(httpServer, {
cors: { origin: "*" },
});
io.on("connection", (socket) => {
this.socketSubscribers.map((el) => {
el.emitter.on((e) => {
socket.emit(el.event, e);
});
});
});
httpServer.listen(this.port, () => { httpServer.listen(this.port, () => {
console.info(`=================================`); console.info(`=================================`);
@ -35,18 +70,13 @@ export class App {
console.info(`🚀 WS ws://localhost:${this.port}`); console.info(`🚀 WS ws://localhost:${this.port}`);
console.info(`=================================`); console.info(`=================================`);
}); });
// io.on("connection", (socket) => {
// socket.on("disconnect", function (msg) {
// console.log("Disconnected");
// });
// });
// setInterval(function () { this.io = io;
// io.emit("goodbye"); }
// console.log(200); setServerStatus(status: ServerStatus) {
// }, 1000); this.emit(status);
this.status = status;
} }
public getServer() { public getServer() {
return this.app; return this.app;
} }
@ -55,6 +85,13 @@ export class App {
this.app.use(cors()); this.app.use(cors());
this.app.use(express.json()); this.app.use(express.json());
this.app.use(express.urlencoded({ extended: true })); this.app.use(express.urlencoded({ extended: true }));
this.app.use(express.static("public"));
this.app.use(
fileUpload({
createParentPath: true,
})
);
} }
private initializeRoutes(routes: Routes[]) { private initializeRoutes(routes: Routes[]) {
@ -62,21 +99,25 @@ export class App {
this.app.use("/", route.router); this.app.use("/", route.router);
}); });
} }
async loadAppDependencies() {
await locator( async loadAppDependencies(): Promise<void> {
this.env == "development" const dataBaseName = this.env === Environment.E2E_TEST ? "e2e_test" : "dev";
? new DevEnv(this.computedFolder) // TODO(IDONTSUDO):maybe convert it to a class and map it there
: new UnitTestEnv(this.computedFolder) const result = await new DataBaseConnectUseCase().call(dataBaseName);
await result.fold(
async (_s) => {
await new CheckAndCreateStaticFilesFolderUseCase().call();
await new SetLastActivePipelineToRealTimeServiceScenario().call();
},
async (_e) => {
this.setServerStatus(ServerStatus.error);
}
); );
mongoose
.connect("mongodb://127.0.0.1:27017/test")
.then(() => console.log("Connected!"))
.catch((e) => {
console.log("ERROR:", e);
});
} }
static staticFilesStoreDir = () => {
const dir = dirname(__filename);
const rootDir = dir.slice(0, dir.length - 20);
return rootDir + "public/";
};
} }

View file

@ -12,23 +12,23 @@ export class CrudController<V, D> extends CoreHttpController<V> {
constructor(routerModel: IRouteModel) { constructor(routerModel: IRouteModel) {
super(routerModel); super(routerModel);
this.url = "/" + routerModel.url; this.mainURL = "/" + routerModel.url;
this.validationModel = routerModel.validationModel; this.validationModel = routerModel.validationModel;
this.dataBaseModel = routerModel.databaseModel; this.dataBaseModel = routerModel.databaseModel;
this.init(); this.init();
} }
init() { init() {
this.routes["POST"] = new CreateDataBaseModelUseCase<D>( if (this.routes["POST"] === null) {
this.dataBaseModel this.routes["POST"] = new CreateDataBaseModelUseCase<D>(this.dataBaseModel).call;
).call; }
this.routes["GET"] = new PaginationDataBaseModelUseCase<D>( if (this.routes["GET"] === null) {
this.dataBaseModel this.routes["GET"] = new PaginationDataBaseModelUseCase<D>(this.dataBaseModel).call;
).call; }
this.routes["DELETE"] = new DeleteDataBaseModelUseCase<D>( if (this.routes["DELETE"] === null) {
this.dataBaseModel this.routes["DELETE"] = new DeleteDataBaseModelUseCase<D>(this.dataBaseModel).call;
).call; }
this.routes["PUT"] = new UpdateDataBaseModelUseCase<V, D>( if (this.routes["PUT"] === null) {
this.dataBaseModel this.routes["PUT"] = new UpdateDataBaseModelUseCase<V, D>(this.dataBaseModel).call;
).call; }
} }
} }

View file

@ -1,19 +1,54 @@
import { validationModelMiddleware } from "../middlewares/validation_model"; import { validationModelMiddleware } from "../middlewares/validation_model";
import { Result } from "../helper/result"; import { Result } from "../helpers/result";
import { Router, Request, Response } from "express"; import { Router, Request, Response } from "express";
import { IRouteModel, Routes } from "../interfaces/router"; import { IRouteModel, Routes } from "../interfaces/router";
export type CallBackFunction<T> = (a: T) => Promise<Result<any, any>>; export type HttpMethodType = "GET" | "POST" | "PUT" | "DELETE" | "PATCH" | "PATCH" | "HEAD";
export type ResponseBase = Promise<Result<any, any>>;
export abstract class CallbackStrategyWithEmpty {
abstract call(): ResponseBase;
}
export abstract class CallbackStrategyWithValidationModel<V> {
abstract validationModel: V;
abstract call(a: V): ResponseBase;
}
export abstract class CallbackStrategyWithIdQuery {
abstract idValidationExpression: RegExp | null;
abstract call(id: string): ResponseBase;
}
export abstract class CallBackStrategyWithQueryPage {
abstract validationPageExpression: RegExp | null;
abstract call(page: string): ResponseBase;
}
export abstract class CallbackStrategyWithFileUpload {
abstract checkingFileExpression: RegExp;
abstract call(file: File): ResponseBase;
}
interface ISubSetFeatureRouter<T> {
method: HttpMethodType;
subUrl: string;
fn:
| CallbackStrategyWithValidationModel<T>
| CallbackStrategyWithEmpty
| CallbackStrategyWithIdQuery
| CallBackStrategyWithQueryPage
| CallbackStrategyWithFileUpload;
}
abstract class ICoreHttpController { abstract class ICoreHttpController {
abstract url: string; abstract mainURL: string;
public router = Router(); public router = Router();
abstract call(): Routes; abstract call(): Routes;
} }
export class CoreHttpController<V> implements ICoreHttpController { export class CoreHttpController<V> implements ICoreHttpController {
url: string; mainURL: string;
validationModel: any; validationModel: any;
subRoutes: ISubSetFeatureRouter<V>[] = [];
routes = { routes = {
POST: null, POST: null,
@ -25,55 +60,97 @@ export class CoreHttpController<V> implements ICoreHttpController {
public router = Router(); public router = Router();
constructor(routerModel: IRouteModel) { constructor(routerModel: IRouteModel) {
this.url = "/" + routerModel.url; this.mainURL = "/" + routerModel.url;
this.validationModel = routerModel.validationModel; this.validationModel = routerModel.validationModel;
} }
async responseHelper(res: Response, fn: ResponseBase) {
(await fn).fold(
(ok) => {
res.json(ok);
return;
},
(err) => {
res.status(400).json({ error: String(err) });
return;
}
);
}
call(): Routes { call(): Routes {
if (this.subRoutes.isNotEmpty()) {
this.subRoutes.map((el) => {
this.router[el.method.toLowerCase()](this.mainURL + "/" + el.subUrl, async (req, res) => {
if (el.fn instanceof CallbackStrategyWithValidationModel) {
// TODO(IDONTSUDO):
throw Error("needs to be implimed");
}
if (el.fn instanceof CallbackStrategyWithIdQuery) {
throw Error("needs to be implimed");
}
if (el.fn instanceof CallBackStrategyWithQueryPage) {
throw Error("needs to be implimed");
}
if (el.fn instanceof CallbackStrategyWithEmpty) {
await this.responseHelper(res, el.fn.call());
return;
}
if (el.fn instanceof CallbackStrategyWithFileUpload) {
if (req["files"] === undefined) {
res.status(400).json("need files to form-data request");
return;
}
if (req["files"]["file"] === undefined) {
res.status(400).json("need file to form data request");
return;
}
if (el.fn instanceof CallbackStrategyWithFileUpload) {
if (!el.fn.checkingFileExpression.test(req["files"]["file"]["name"])) {
res.status(400).json("a file with this extension is expected: " + String(el.fn.checkingFileExpression));
return;
}
}
await this.responseHelper(res, el.fn.call(req["files"]["file"]));
}
});
});
}
if (this.routes["POST"] != null) { if (this.routes["POST"] != null) {
this.router.post( this.router.post(this.mainURL, validationModelMiddleware(this.validationModel), (req, res) =>
this.url, this.requestResponseController<V>(req, res, this.routes["POST"])
validationModelMiddleware(this.validationModel),
(req, res) =>
this.requestResponseController<V>(req, res, this.routes["POST"])
); );
} }
if (this.routes["DELETE"] != null) { if (this.routes["DELETE"] != null) {
this.router.delete(this.url, (req, res) => this.router.delete(this.mainURL, (req, res) =>
this.requestResponseController<V>(req, res, this.routes["DELETE"]) this.requestResponseController<V>(req, res, this.routes["DELETE"])
); );
} }
if (this.routes["PUT"] != null) { if (this.routes["PUT"] != null) {
this.router.put( this.router.put(this.mainURL, validationModelMiddleware(this.validationModel), (req, res) =>
this.url, this.requestResponseController<V>(req, res, this.routes["PUT"])
validationModelMiddleware(this.validationModel),
(req, res) =>
this.requestResponseController<V>(req, res, this.routes["PUT"])
); );
} }
if (this.routes["GET"] != null) { if (this.routes["GET"] != null) {
this.router.get(this.url, (req, res) => this.router.get(this.mainURL, (req, res) => this.requestResponseController<V>(req, res, this.routes["GET"]));
this.requestResponseController<V>(req, res, this.routes["GET"])
);
} }
return { return {
router: this.router, router: this.router,
}; };
} }
public put(usecase: CallBackFunction<V>) { public put(usecase: CallbackStrategyWithValidationModel<V>) {
this.routes["PUT"] = usecase; this.routes["PUT"] = usecase;
} }
public delete(usecase: CallBackFunction<V>) { public delete(usecase: CallbackStrategyWithValidationModel<V>) {
this.routes["DELETE"] = usecase; this.routes["DELETE"] = usecase;
} }
private async requestResponseController<T>( public async requestResponseController<T>(
req: Request, req: Request,
res: Response, res: Response,
usecase: CallBackFunction<T> usecase: CallbackStrategyWithValidationModel<T>
) { ) {
let payload = null; let payload = null;
const useCase = usecase as any;
if (req["model"] != undefined) { if (req["model"] != undefined) {
payload = req.body as T; payload = req.body as T;
} }
@ -85,23 +162,23 @@ export class CoreHttpController<V> implements ICoreHttpController {
if (req.query.id !== undefined) { if (req.query.id !== undefined) {
payload = String(req.query.id); payload = String(req.query.id);
} }
(await useCase(payload)).fold(
(await usecase(payload)).fold(
(ok) => { (ok) => {
res.json(ok); res.json(ok);
return; return;
}, },
(err) => { (err) => {
res.status(400).json(err); res.status(400).json({ error: String(err) });
return; return;
} }
); );
} }
public post(usecase: CallBackFunction<V>) { // TODO(IDONTSUDO):need fix to CallbackStrategyWithValidationModel<V>
public post(usecase: any) {
this.routes["POST"] = usecase; this.routes["POST"] = usecase;
} }
public get(usecase: CallBackFunction<V>) { public get(usecase: any) {
this.routes["GET"] = usecase; this.routes["GET"] = usecase;
} }
} }

View file

@ -0,0 +1,26 @@
import { NixStoreManagerPresentation } from "../../features/nix_store_manager/nix_store_manager";
import { PipelinePresentation } from "../../features/pipelines/pipeline_presentation";
import { ProcessPresentation } from "../../features/process/process_presentation";
import { ProjectInstancePresentation } from "../../features/project_instance/project_instance_presentation";
import { ProjectsPresentation } from "../../features/projects/projects_presentation";
import { RealTimePresentation } from "../../features/realtime/realtime_presentation";
import { TriggerPresentation } from "../../features/triggers/triggers_presentation";
import { extensions } from "../extensions/extensions";
import { Routes } from "../interfaces/router";
extensions();
export const routersImplementPureCrud = [
new TriggerPresentation(),
new ProjectsPresentation(),
new ProcessPresentation(),
new PipelinePresentation(),
];
export const httpRoutes: Routes[] = [
new RealTimePresentation(),
new ProjectInstancePresentation(),
new NixStoreManagerPresentation(),
]
.concat(routersImplementPureCrud)
.map((el) => el.call());

View file

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

View file

@ -1,44 +1,44 @@
import { Service } from "typedi"; // import { Service } from "typedi";
@Service() // @Service()
export class IEnv{ // export class IEnv{
rootFolder!: string; // rootFolder!: string;
constructor(){ // constructor(){
} // }
toStringEnv(){ // toStringEnv(){
return '' // return ''
} // }
static env(){ // static env(){
return '' // return ''
} // }
} // }
@Service() // @Service()
export class DevEnv implements IEnv { // export class DevEnv implements IEnv {
rootFolder:string; // rootFolder:string;
constructor(rootFolder:string){ // constructor(rootFolder:string){
this.rootFolder = rootFolder // this.rootFolder = rootFolder
} // }
toStringEnv(): string { // toStringEnv(): string {
return DevEnv.env() // return DevEnv.env()
} // }
static env(){ // static env(){
return 'DevEnv' // return 'DevEnv'
} // }
} // }
@Service() // @Service()
export class UnitTestEnv implements IEnv{ // export class UnitTestEnv implements IEnv{
rootFolder:string; // rootFolder:string;
constructor(rootFolder:string){ // constructor(rootFolder:string){
this.rootFolder = rootFolder // this.rootFolder = rootFolder
} // }
toStringEnv(): string { // toStringEnv(): string {
return UnitTestEnv.env() // return UnitTestEnv.env()
} // }
static env(){ // static env(){
return 'UnitTestEnv' // return 'UnitTestEnv'
} // }
} // }

View file

@ -1,53 +1,53 @@
import { DevEnv, IEnv, UnitTestEnv } from "./env"; // import { DevEnv, IEnv, UnitTestEnv } from "./env";
import { extensions } from "../extensions/extensions"; // import { extensions } from "../extensions/extensions";
// import { Container, Service } from 'typedi'; // // import { Container, Service } from 'typedi';
export default function locator(env: IEnv) { // export default function locator(env: IEnv) {
extensions(); // extensions();
envRegister(env); // envRegister(env);
registerRepository(env); // registerRepository(env);
registerController(env); // registerController(env);
registerService(env); // registerService(env);
// override(MetaDataFileManagerModel, MetaDataFileManagerModel); // // override(MetaDataFileManagerModel, MetaDataFileManagerModel);
} // }
const envRegister = (env: IEnv) => { // const envRegister = (env: IEnv) => {
switch (env.toStringEnv()) { // switch (env.toStringEnv()) {
case UnitTestEnv.env(): // case UnitTestEnv.env():
// override(IEnv, UnitTestEnv); // // override(IEnv, UnitTestEnv);
return; // return;
case "DevEnv": // case "DevEnv":
// override(IEnv, DevEnv); // // override(IEnv, DevEnv);
return; // return;
} // }
}; // };
const registerRepository = (env: IEnv) => { // const registerRepository = (env: IEnv) => {
switch (env.toStringEnv()) { // switch (env.toStringEnv()) {
case UnitTestEnv.env(): // case UnitTestEnv.env():
// override(IEnv, UnitTestEnv); // // override(IEnv, UnitTestEnv);
return; // return;
case DevEnv.env(): // case DevEnv.env():
// override(IEnv, DevEnv); // // override(IEnv, DevEnv);
return; // return;
} // }
}; // };
const registerController = (env: IEnv) => { // const registerController = (env: IEnv) => {
switch (env.toStringEnv()) { // switch (env.toStringEnv()) {
case UnitTestEnv.env(): // case UnitTestEnv.env():
return; // return;
case DevEnv.env(): // case DevEnv.env():
return; // return;
} // }
}; // };
const registerService = (env: IEnv) => { // const registerService = (env: IEnv) => {
switch (env.toStringEnv()) { // switch (env.toStringEnv()) {
case UnitTestEnv.env(): // case UnitTestEnv.env():
return; // return;
case DevEnv.env(): // case DevEnv.env():
return; // return;
} // }
}; // };

View file

@ -1,25 +1,23 @@
export {}; /* eslint-disable @typescript-eslint/no-this-alias */
export const ArrayExtensions = () => {
declare global { if ([].firstElement === undefined) {
interface Array<T> { Array.prototype.firstElement = function () {
// @strict: The parameter is determined whether the arrays must be exactly the same in content and order of this relationship or simply follow the same requirements. return this[0];
equals(array: Array<T>, strict: boolean): boolean; };
} }
} if ([].equals === undefined) {
// eslint-disable-next-line no-extend-native
export const ArrayEquals = () => {
if ([].equals == undefined) {
Array.prototype.equals = function (array, strict = true) { Array.prototype.equals = function (array, strict = true) {
if (!array) return false; if (!array) return false;
if (arguments.length == 1) strict = true; if (arguments.length === 1) strict = true;
if (this.length != array.length) return false; if (this.length !== array.length) return false;
for (let i = 0; i < this.length; i++) { for (let i = 0; i < this.length; i++) {
if (this[i] instanceof Array && array[i] instanceof Array) { if (this[i] instanceof Array && array[i] instanceof Array) {
if (!this[i].equals(array[i], strict)) return false; if (!this[i].equals(array[i], strict)) return false;
} else if (strict && this[i] != array[i]) { } else if (strict && this[i] !== array[i]) {
return false; return false;
} else if (!strict) { } else if (!strict) {
return this.sort().equals(array.sort(), true); return this.sort().equals(array.sort(), true);
@ -28,4 +26,27 @@ export const ArrayEquals = () => {
return true; return true;
}; };
} }
if ([].lastElement === undefined) {
// eslint-disable-next-line no-extend-native
Array.prototype.lastElement = function () {
const instanceCheck = this;
if (instanceCheck === undefined) {
return undefined;
} else {
const instance = instanceCheck as [];
return instance[instance.length - 1];
}
};
}
if ([].isEmpty === undefined) {
// eslint-disable-next-line no-extend-native
Array.prototype.isEmpty = function () {
return this.length === 0;
};
}
if ([].isNotEmpty === undefined) {
Array.prototype.isNotEmpty = function () {
return this.length !== 0;
};
}
}; };

View file

@ -1,6 +1,24 @@
import { ArrayEquals } from "./array"; import { ArrayExtensions } from "./array";
import { StringExtensions } from "./string";
export const extensions = () =>{ declare global {
ArrayEquals() interface Array<T> {
// @strict: The parameter is determined whether the arrays must be exactly the same in content and order of this relationship or simply follow the same requirements.
equals(array: Array<T>, strict: boolean): boolean;
lastElement(): T | undefined;
firstElement(): T | undefined;
isEmpty(): boolean;
isNotEmpty(): boolean;
}
interface String {
isEmpty(): boolean;
isNotEmpty(): boolean;
lastElement(): string;
hasPattern(pattern: string): boolean;
hasNoPattern(pattern: string): boolean;
}
} }
export const extensions = () => {
ArrayExtensions();
StringExtensions();
};

View file

@ -0,0 +1,29 @@
export const StringExtensions = () => {
if ("".isEmpty === undefined) {
// eslint-disable-next-line no-extend-native
String.prototype.isEmpty = function () {
return this.length === 0;
};
}
if ("".isNotEmpty === undefined) {
// eslint-disable-next-line no-extend-native
String.prototype.isNotEmpty = function () {
return this.length !== 0;
};
}
if ("".lastElement === undefined) {
String.prototype.lastElement = function () {
return this[this.length - 1];
};
}
if ("".hasPattern === undefined) {
String.prototype.hasPattern = function (pattern) {
return new RegExp(pattern).test(this);
};
}
if ("".hasNoPattern === undefined) {
String.prototype.hasNoPattern = function (pattern) {
return !this.hasPattern(pattern);
};
}
};

View file

@ -1,3 +0,0 @@
export function delay(ms: number) {
return new Promise((resolve) => setTimeout(resolve, ms));
}

View file

@ -1,49 +0,0 @@
export interface Listener<T> {
(event: T): any;
}
export interface Disposable {
dispose():void;
}
export class TypedEvent<T> {
private listeners: Listener<T>[] = [];
public listenersOnces: Listener<T>[] = [];
on = (listener: Listener<T>): Disposable => {
this.listeners.push(listener);
return {
dispose: () => this.off(listener),
};
};
once = (listener: Listener<T>): void => {
this.listenersOnces.push(listener);
};
off = (listener: Listener<T>) => {
const callbackIndex = this.listeners.indexOf(
listener
);
if (callbackIndex > -1)
this.listeners.splice(callbackIndex, 1);
};
emit = (event: T) => {
this.listeners.forEach((listener) =>
listener(event)
);
if (this.listenersOnces.length > 0) {
const toCall = this.listenersOnces;
this.listenersOnces = [];
toCall.forEach((listener) => listener(event));
}
};
pipe = (te: TypedEvent<T>): Disposable => {
return this.on((e) => te.emit(e));
};
}

View file

@ -0,0 +1,85 @@
import { randomBytes, randomInt, randomUUID } from "crypto";
import { getMetadataStorage, IS_ARRAY, IS_BOOLEAN, IS_MONGO_ID, IS_NUMBER, IS_STRING, IS_UUID } from "class-validator";
import { ValidationMetadata } from "class-validator/types/metadata/ValidationMetadata";
type AvailableTypes = string | number | boolean | undefined | [];
export class ClassValidatorMocker {
// eslint-disable-next-line @typescript-eslint/ban-types
public static create<T>(constructor: Function, partial: Partial<T> = {}): T {
return new ClassValidatorMocker().create(constructor, partial);
}
// eslint-disable-next-line @typescript-eslint/ban-types
public create<T>(constructor: Function, partial: Partial<T> = {}): T {
const metadataStorage = getMetadataStorage();
const targetMetadatas = metadataStorage.getTargetValidationMetadatas(constructor, "", false, false);
const groupedMetadatas = metadataStorage.groupByPropertyName(targetMetadatas);
// nestedValidation
let randomFixture = {} as T;
for (const propertyName of Object.keys(groupedMetadatas)) {
const metadatas = groupedMetadatas[propertyName];
const value = this.generatePropertyValueFromMetadatas(metadatas);
if (value !== undefined) {
randomFixture = {
...randomFixture,
[propertyName]: value,
};
}
}
return { ...randomFixture, ...partial };
}
private generatePropertyValueFromMetadatas(metadatas: ValidationMetadata[]): AvailableTypes {
for (const metadata of metadatas) {
const constraints = getMetadataStorage().getTargetValidatorConstraints(metadata.constraintCls);
for (const constraint of constraints) {
switch (constraint.name) {
case IS_ARRAY:
return [];
case "isEnum":
return Object.keys(metadata.constraints.firstElement()).firstElement();
case IS_MONGO_ID:
return this.randomUUID();
case IS_STRING:
return this.randomString();
case IS_NUMBER:
return this.randomNumber();
case IS_BOOLEAN:
return this.randomBoolean();
case IS_UUID:
return this.randomUUID();
default:
break;
}
}
}
return undefined;
}
private randomString(): string {
return randomBytes(randomInt(1, 10)).toString("hex");
}
private randomNumber(): number {
return randomInt(0, 99_999);
}
private randomBoolean(): boolean {
return randomInt(0, 1) === 1;
}
private randomUUID(): string {
if (randomUUID != null) {
return randomUUID();
}
return randomBytes(16).toString("hex");
}
}

View file

@ -0,0 +1,3 @@
export function delay(ms: number) {
return new Promise((resolve) => setTimeout(resolve, ms));
}

View file

@ -0,0 +1,444 @@
/* eslint-disable @typescript-eslint/ban-types */
/* eslint-disable @typescript-eslint/no-unnecessary-type-constraint */
/* eslint-disable @typescript-eslint/no-namespace */
function isAsyncFn(fn: Function) {
return fn.constructor.name === "AsyncFunction";
}
function isResult(value: unknown): value is Result<any, any, any> {
return value instanceof Ok || value instanceof Err;
}
interface SyncThenable {
isSync: true;
then<Fn extends () => Promise<any>>(cb: Fn): ReturnType<Fn>;
then<Fn extends () => any>(cb: Fn): SyncThenable;
}
function syncThenable(): SyncThenable {
function then<Fn extends () => Promise<any>>(cb: Fn): ReturnType<Fn>;
function then<Fn extends () => any>(cb: Fn): SyncThenable;
function then(cb: any) {
const result = cb();
if (result instanceof Promise) {
return result;
}
return syncThenable();
}
return {
isSync: true,
then,
};
}
function forEachValueThunkOrPromise<T>(items: unknown[], execFn: (value: T) => boolean, foldFn: () => unknown) {
let shouldBreak = false;
const result: any = items.reduce((prev: { then: Function }, valueOrThunk) => {
return prev.then(() => {
if (shouldBreak) {
return null;
}
function run(value: T) {
const isSuccess = execFn(value);
if (!isSuccess) {
shouldBreak = true;
}
}
const valueOrPromise = typeof valueOrThunk === "function" ? valueOrThunk() : valueOrThunk;
if (valueOrPromise instanceof Promise) {
return valueOrPromise.then(run);
}
return run(valueOrPromise);
});
}, syncThenable());
if ((result as SyncThenable).isSync) {
return foldFn();
}
return result.then(() => {
return foldFn();
});
}
export type Result<ErrorType, OkType, RollbackFn extends RollbackFunction = any> =
| Ok<ErrorType, OkType, RollbackFn>
| Err<ErrorType, OkType, RollbackFn>;
interface IResult<ErrorType, OkType> {
isSuccess(): this is Ok<ErrorType, OkType, any>;
isFailure(): this is Err<ErrorType, OkType, any>;
getOrNull(): OkType | null;
toString(): string;
inspect(): string;
fold<R>(onSuccess: (value: OkType) => R, onFailure: (error: ErrorType) => R): R;
fold<R>(onSuccess: (value: OkType) => Promise<R>, onFailure: (error: ErrorType) => Promise<R>): Promise<R>;
getOrDefault(defaultValue: OkType): OkType;
getOrElse(onFailure: (error: ErrorType) => OkType): OkType;
getOrElse(onFailure: (error: ErrorType) => Promise<OkType>): Promise<OkType>;
getOrThrow(): OkType;
map<T>(
fn: (value: OkType) => Promise<T>
): Promise<JoinErrorTypes<ErrorType, T extends Result<any, any, any> ? T : Result<Error, T, any>>>;
map<T>(
fn: (value: OkType) => T
): JoinErrorTypes<ErrorType, T extends Result<any, any, any> ? T : Result<Error, T, any>>;
rollback(): Result<Error, void> | Promise<Result<Error, void>>;
}
type InferErrorType<T extends Result<any, any, any>> = T extends Result<infer Errortype, any, any> ? Errortype : never;
type InferOkType<T extends Result<any, any, any>> = T extends Result<any, infer OkType, any> ? OkType : never;
type JoinErrorTypes<ErrorType, B extends Result<any, any, any>> = Result<
ErrorType | InferErrorType<B>,
InferOkType<B>,
any
>;
type ExtractErrorTypes<Tuple extends any[]> = {
[Index in keyof Tuple]: Tuple[Index] extends Result<any, any, any> ? InferErrorType<Tuple[Index]> : never;
}[number];
type MapResultTupleToOkTypeTuple<Tuple extends any[]> = {
[Index in keyof Tuple]: Tuple[Index] extends Result<any, any, any> ? InferOkType<Tuple[Index]> : never;
};
type RollbackFunction = (() => void) | (() => Promise<void>);
type HasAsyncRollbackFunction<T extends any[]> = {
[Index in keyof T]: T[Index] extends () => Promise<infer U> | infer U
? U extends Result<any, any, () => Promise<void>>
? true
: false
: false;
}[number] extends false
? false
: true;
type UnwrapThunks<T extends any[]> = {
[Index in keyof T]: T[Index] extends () => Promise<infer U> ? U : T[Index] extends () => infer U ? U : T[Index];
};
type HasAsyncThunk<T extends any[]> = {
[Index in keyof T]: T[Index] extends () => Promise<any> ? true : false;
}[number] extends false
? false
: true;
type PromiseReturnType<T extends (...args: any) => any> = T extends (...args: any) => Promise<infer U> ? U : never;
export namespace Result {
export function ok<ErrorType extends unknown, OkType, RollbackFn extends RollbackFunction = any>(
value?: OkType,
rollbackFn?: RollbackFn
): Result<ErrorType, OkType, RollbackFn> {
return new Ok<ErrorType, OkType, RollbackFn>(value || null!, rollbackFn) as any;
}
export function error<ErrorType extends unknown, OkType extends unknown, RollbackFn extends RollbackFunction = any>(
error: ErrorType,
rollbackFn?: RollbackFn
): Result<ErrorType, OkType, RollbackFn> {
return new Err<ErrorType, OkType, RollbackFn>(error, rollbackFn);
}
type SafeReturnType<E, T> = T extends Result<any, any, any>
? Result<E | InferErrorType<T>, InferOkType<T>, never>
: Result<E, T, never>;
export function safe<T>(fn: () => Promise<T>): Promise<SafeReturnType<Error, T>>;
export function safe<T>(fn: () => T): SafeReturnType<Error, T>;
export function safe<ErrorType, T>(
err: ErrorType | (new (...args: any[]) => ErrorType),
fn: () => Promise<T>
): Promise<SafeReturnType<ErrorType, T>>;
export function safe<ErrorType, T>(
err: ErrorType | (new (...args: any[]) => ErrorType),
fn: () => T
): SafeReturnType<ErrorType, T>;
export function safe(errOrFn: any, fn?: any) {
const hasCustomError = fn !== undefined;
const execute = hasCustomError ? fn : errOrFn;
function getError(caughtError: Error) {
if (!hasCustomError) {
return caughtError;
}
if (typeof errOrFn === "function") {
return new errOrFn(caughtError);
}
return errOrFn;
}
try {
const resultOrPromise = execute();
if (resultOrPromise instanceof Promise) {
return resultOrPromise
.then((okValue) => {
return isResult(okValue) ? okValue : Result.ok(okValue);
})
.catch((caughtError) => error(getError(caughtError)));
}
return isResult(resultOrPromise) ? resultOrPromise : Result.ok(resultOrPromise);
} catch (caughtError) {
return error(getError(caughtError as Error));
}
}
type CombineResult<T extends (unknown | (() => unknown) | (() => Promise<unknown>))[]> = Result<
ExtractErrorTypes<UnwrapThunks<T>>,
MapResultTupleToOkTypeTuple<UnwrapThunks<T>>,
HasAsyncRollbackFunction<T> extends true ? () => Promise<void> : () => void
>;
export function combine<T extends (unknown | (() => unknown) | (() => Promise<unknown>))[]>(
...items: T
): HasAsyncThunk<T> extends true ? Promise<CombineResult<T>> : CombineResult<T> {
if (!items.length) {
throw new Error("Expected at least 1 argument");
}
const values: unknown[] = [];
const rollbacks: RollbackFunction[] = [];
let error: Err<unknown, unknown, any> | null = null;
function rollback() {
const reversedRollbacks = rollbacks.reverse();
const wrappedRollbackFns = reversedRollbacks.map((fn) => Result.wrap(fn));
let error: Err<unknown, unknown, any> | null = null;
return forEachValueThunkOrPromise(
wrappedRollbackFns,
(result: Result<unknown, unknown>) => {
if (result.isFailure()) {
error = Result.error<unknown, unknown, any>(result.error) as any;
return false;
}
return true;
},
() => error || ok()
);
}
return forEachValueThunkOrPromise(
items,
(result: Result<unknown, unknown>) => {
if (result.isFailure()) {
error = Result.error<unknown, unknown>(result.error, rollback) as any;
return false;
}
values.push(result.value);
rollbacks.push(() => result.rollback());
return true;
},
() => error || ok(values, rollback)
);
}
export function wrap<Fn extends (...args: any) => Promise<any>>(
fn: Fn
): (...args: Parameters<Fn>) => Promise<Result<Error, PromiseReturnType<Fn>, never>>;
export function wrap<Fn extends (...args: any) => any>(
fn: Fn
): (...args: Parameters<Fn>) => Result<Error, ReturnType<Fn>, never>;
export function wrap(fn: any) {
return function wrapped(...args: any) {
try {
const resultOrPromise = fn(...args);
if (resultOrPromise instanceof Promise) {
return resultOrPromise.then((okValue) => Result.ok(okValue)).catch((err) => error(err));
}
return ok(resultOrPromise);
} catch (err) {
return error(err);
}
};
}
}
abstract class Base<ErrorType extends unknown, OkType extends unknown, RollbackFn extends RollbackFunction>
implements IResult<ErrorType, OkType>
{
constructor(protected readonly rollbackFn?: RollbackFn) {}
errorOrNull(): ErrorType | null {
if (this.isSuccess()) {
return null;
}
return (this as any).error as ErrorType;
}
getOrNull(): OkType | null {
if (this.isFailure()) {
return null;
}
return (this as any).value as OkType;
}
toString(): string {
throw new Error("Method not implemented.");
}
inspect(): string {
return this.toString();
}
fold<R>(onSuccess: (value: OkType) => R, onFailure: (error: ErrorType) => R): R;
fold<R>(onSuccess: (value: OkType) => Promise<R>, onFailure: (error: ErrorType) => Promise<R>): Promise<R>;
fold(onSuccess: any, onFailure: any) {
if (this.isFailure()) {
return onFailure(this.error);
}
return onSuccess((this as any).value as OkType);
}
getOrDefault(defaultValue: OkType): OkType {
if (this.isSuccess()) {
return this.value;
}
return defaultValue;
}
getOrElse(onFailure: (error: ErrorType) => OkType): OkType;
getOrElse(onFailure: (error: ErrorType) => Promise<OkType>): Promise<OkType>;
getOrElse(onFailure: any) {
if (this.isSuccess()) {
return isAsyncFn(onFailure) ? Promise.resolve(this.value) : this.value;
}
return onFailure((this as any).error as ErrorType);
}
getOrThrow(): OkType {
if (this.isFailure()) {
throw this.error;
}
return (this as any).value as OkType;
}
isSuccess(): this is Ok<ErrorType, OkType, RollbackFn> {
throw new Error("Method not implemented.");
}
isFailure(): this is Err<ErrorType, OkType, RollbackFn> {
throw new Error("Method not implemented.");
}
map<T>(
fn: (value: OkType) => Promise<T>
): Promise<JoinErrorTypes<ErrorType, T extends Result<any, any, any> ? T : Result<Error, T, any>>>;
map<T>(
fn: (value: OkType) => T
): JoinErrorTypes<ErrorType, T extends Result<any, any, any> ? T : Result<Error, T, any>>;
map(fn: any) {
if (this.isFailure()) {
return isAsyncFn(fn) ? Promise.resolve(this) : this;
}
const result = Result.safe(() => fn((this as any).value) as any);
return result as any;
}
rollback(): RollbackFn extends RollbackFunction
? RollbackFn extends () => Promise<void>
? Promise<Result<Error, void>>
: Result<Error, void>
: void {
if (this.rollbackFn) {
return this.rollbackFn() as any;
}
return null as any;
}
}
class Ok<ErrorType extends unknown, OkType extends unknown, RollbackFn extends RollbackFunction> extends Base<
ErrorType,
OkType,
RollbackFn
> {
public readonly value: OkType;
constructor(val: OkType, rollbackFn?: RollbackFn) {
super(rollbackFn);
this.value = val;
}
isSuccess(): this is Ok<ErrorType, OkType, RollbackFn> {
return true;
}
isFailure(): this is Err<ErrorType, OkType, RollbackFn> {
return false;
}
toString(): string {
return `Result.Ok(${this.value})`;
}
forward(): Result<any, OkType, RollbackFn> {
return Result.ok(this.value);
}
}
class Err<ErrorType extends unknown, OkType extends unknown, RollbackFn extends RollbackFunction> extends Base<
ErrorType,
OkType,
RollbackFn
> {
public readonly error: ErrorType;
constructor(err: ErrorType, rollbackFn?: RollbackFn) {
super(rollbackFn);
this.error = err;
}
isSuccess(): this is Ok<ErrorType, OkType, RollbackFn> {
return false;
}
isFailure(): this is Err<ErrorType, OkType, RollbackFn> {
return true;
}
toString(): string {
return `Result.Error(${this.error})`;
}
forward(): Result<ErrorType, any, RollbackFn> {
return Result.error(this.error);
}
}

View file

@ -0,0 +1,42 @@
export interface Listener<T> {
(event: T): any;
}
export interface Disposable {
dispose(): void;
}
export class TypedEvent<T> {
private listeners: Listener<T>[] = [];
public listenersOnces: Listener<T>[] = [];
on = (listener: Listener<T>): Disposable => {
this.listeners.push(listener);
return {
dispose: () => this.off(listener),
};
};
once = (listener: Listener<T>): void => {
this.listenersOnces.push(listener);
};
off = (listener: Listener<T>) => {
const callbackIndex = this.listeners.indexOf(listener);
if (callbackIndex > -1) this.listeners.splice(callbackIndex, 1);
};
emit = (event: T) => {
this.listeners.forEach((listener) => listener(event));
if (this.listenersOnces.length > 0) {
const toCall = this.listenersOnces;
this.listenersOnces = [];
toCall.forEach((listener) => listener(event));
}
};
pipe = (te: TypedEvent<T>): Disposable => {
return this.on((e) => te.emit(e));
};
}

View file

@ -1,6 +1,6 @@
import { EXEC_EVENT, EXEC_TYPE, ExecError } from "../model/exec_error_model"; import { EXEC_EVENT, EXEC_TYPE, ExecError } from "../models/exec_error_model";
import * as cp from "child_process"; import * as cp from "child_process";
import { ExecutorResult } from "../model/executor_result"; import { ExecutorResult } from "../models/executor_result";
export enum WorkerType { export enum WorkerType {
EXEC = "EXEC", EXEC = "EXEC",
@ -11,7 +11,7 @@ export interface WorkerDataExec {
command: string; command: string;
execPath: string; execPath: string;
type: WorkerType; type: WorkerType;
cliArgs:Array<string> | undefined cliArgs: Array<string> | undefined;
} }
process.on("message", async (message) => { process.on("message", async (message) => {
@ -30,15 +30,15 @@ process.on("message", async (message) => {
}); });
} }
}); });
subprocess.on('close', (_code) =>{ subprocess.on("close", (_code) => {
if (process.send) { if (process.send) {
process.send({ process.send({
type: EXEC_TYPE.SPAWN.toString(), type: EXEC_TYPE.SPAWN.toString(),
event: EXEC_EVENT.END.toString(), event: EXEC_EVENT.END.toString(),
data:null data: null,
}); });
} }
}) });
process.on("uncaughtException", (error) => { process.on("uncaughtException", (error) => {
if (process.send) { if (process.send) {
process.send({ process.send({
@ -50,26 +50,21 @@ process.on("message", async (message) => {
}); });
} else if (workerData.type == WorkerType.EXEC) { } else if (workerData.type == WorkerType.EXEC) {
try { try {
const result = await exec(workerData.command, { const result = await exec(workerData.command, {
cwd: workerData.execPath, cwd: workerData.execPath,
}); });
if (process.send) { if (process.send) {
process.send( process.send(new ExecutorResult(EXEC_TYPE.EXEC, EXEC_EVENT.END, result));
new ExecutorResult(EXEC_TYPE.EXEC, EXEC_EVENT.END, result)
);
} }
} catch (error) { } catch (error) {
if (process.send) { if (process.send) {
process.send(new ExecError(workerData.command, error )); process.send(new ExecError(workerData.command, error));
} }
} }
} }
}); });
async function exec( async function exec(cmd: string, opts: cp.ExecOptions & { trim?: boolean } = {}): Promise<string> {
cmd: string,
opts: cp.ExecOptions & { trim?: boolean } = {}
): Promise<string> {
return new Promise((c, e) => { return new Promise((c, e) => {
cp.exec(cmd, { env: process.env, ...opts }, (err, stdout) => { cp.exec(cmd, { env: process.env, ...opts }, (err, stdout) => {
return err ? e(err) : c(opts.trim ? stdout.trim() : stdout); return err ? e(err) : c(opts.trim ? stdout.trim() : stdout);

View file

@ -0,0 +1,3 @@
export interface IFile extends File {
data: Buffer;
}

View file

@ -1,4 +1,3 @@
// export class Payload<T>{ // export class Payload<T>{
// model: T | undefined // model: T | undefined
// query:string | undefined // query:string | undefined
@ -11,4 +10,4 @@
// isEmpty(){ // isEmpty(){
// return this.model != undefined || this.query != undefined // return this.model != undefined || this.query != undefined
// } // }
// } // }

View file

@ -5,10 +5,7 @@ export interface Routes {
} }
export interface IRouteModel { export interface IRouteModel {
validationModel: any; validationModel?: any;
url: string; url: string;
databaseModel: any; databaseModel?: any;
} }

View file

@ -1,12 +1,13 @@
// import { RequestHandler } from "express"; import { RequestHandler } from "express";
// export const validationMiddleware = ( export const validationMiddleware = (
// type: any, type: any,
// value = 'body', value = "body",
// skipMissingProperties = false, skipMissingProperties = false,
// whitelist = true, whitelist = true,
// forbidNonWhitelisted = true, forbidNonWhitelisted = true
// ): RequestHandler => { ): RequestHandler => {
// TODO:(IDONTSUDO) need TOKEN
// return nextTick
// } return (req, res, next) => {};
};

View file

@ -1,30 +1,28 @@
import { plainToInstance } from 'class-transformer'; import { plainToInstance } from "class-transformer";
import { validate, ValidationError } from 'class-validator'; import { validate, ValidationError } from "class-validator";
import { RequestHandler } from 'express'; import { RequestHandler } from "express";
export const validationModelMiddleware = ( export const validationModelMiddleware = (
type: any, type: any,
value = 'body', value = "body",
skipMissingProperties = false, skipMissingProperties = false,
whitelist = true, whitelist = true,
forbidNonWhitelisted = true, forbidNonWhitelisted = true
): RequestHandler => { ): RequestHandler => {
return (req, res, next) => { return (req, res, next) => {
if(type === null && type == undefined){ if (type === null && type == undefined) {
next() next();
return return;
} }
const model = plainToInstance(type, req[value]); const model = plainToInstance(type, req[value]);
validate(model, { skipMissingProperties, whitelist, forbidNonWhitelisted }).then((errors: ValidationError[]) => { validate(model, { skipMissingProperties, whitelist, forbidNonWhitelisted }).then((errors: ValidationError[]) => {
console.log(errors)
if (errors.length > 0) { if (errors.length > 0) {
const message = errors.map((error: ValidationError) => Object.values(error.constraints)).join(', '); const message = errors.map((error: ValidationError) => Object.values(error.constraints)).join(", ");
return res.status(400).json(message) return res.status(400).json(message);
} else { } else {
req['model'] = model req["model"] = model;
next(); next();
} }
}); });
}; };
}; };

View file

@ -0,0 +1,24 @@
export class ActivePipeline {
pipelineIsRunning: boolean;
projectUUID?: string | null;
lastProcessCompleteCount: number | null;
error: any;
path: string;
constructor(
pipelineIsRunning: boolean,
lastProcessCompleteCount: number | null,
error: any,
path: string | null,
projectUUID?: string | null
) {
this.pipelineIsRunning = pipelineIsRunning;
this.projectUUID = projectUUID;
this.lastProcessCompleteCount = lastProcessCompleteCount;
this.error = error;
this.path = path;
}
static empty() {
return new ActivePipeline(false, null, null, null, null);
}
}

View file

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

View file

@ -1,5 +1,4 @@
export enum EventsFileChanger { export enum EventsFileChanger {
remove = "remove",
update = "update", update = "update",
delete = "delete", delete = "delete",
create = "create", create = "create",
@ -18,6 +17,7 @@ export class MetaDataFileManagerModel {
this.event = event; this.event = event;
this.unixTime = Date.now(); this.unixTime = Date.now();
} }
public get timeString(): string { public get timeString(): string {
const date = new Date(this.unixTime * 1000); const date = new Date(this.unixTime * 1000);
const hours = date.getHours(); const hours = date.getHours();

View file

@ -1,9 +1,9 @@
import { Trigger } from "../../features/triggers/trigger_model"; import { Trigger } from "../../features/triggers/models/trigger_database_model";
import { EXEC_TYPE } from "./exec_error_model"; import { EXEC_TYPE } from "./exec_error_model";
export interface IPipeline { export interface IPipeline {
process: IProcess; process: IProcess;
trigger: Trigger; trigger?: Trigger;
env: Env | null; env: Env | null;
stackGenerateType: StackGenerateType; stackGenerateType: StackGenerateType;
} }

View file

@ -0,0 +1,31 @@
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 const writeFileAsync = promisify(fs.writeFile);
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;
}

View file

@ -0,0 +1,24 @@
import axios from "axios";
import { Result } from "../helpers/result";
import { HttpMethodType } from "../controllers/http_controller";
export class HttpRepository {
serverUrl = "http://localhost:4001";
constructor(serverURL: string) {
this.serverUrl = serverURL;
}
async jsonRequest<T>(url: string, method: HttpMethodType, reqBody?: any): Promise<Result<Error, T>> {
try {
const result = await axios(this.serverUrl + url, { method: method, data: reqBody });
if (result.status !== 200) {
return Result.error(Error("status code" + String(result.status)));
}
return Result.ok(result.data);
} catch (error) {
console.log(error);
return Result.error(error);
}
}
}

View file

@ -0,0 +1,23 @@
import {
IProjectInstanceModel,
ProjectInstanceDbModel,
} from "../../features/project_instance/models/project_instance_database_model";
import { pipelineRealTimeService } from "../../features/realtime/realtime_presentation";
import { App } from "../controllers/app";
import { CreateFolderUseCase } from "../usecases/crete_folder_usecase";
import { SearchDataBaseModelUseCase } from "../usecases/search_database_model_usecase";
export class SetLastActivePipelineToRealTimeServiceScenario {
call = async (): Promise<void> => {
const result = await new SearchDataBaseModelUseCase<IProjectInstanceModel>(ProjectInstanceDbModel).call({
isActive: true,
});
if (result.isSuccess()) {
const projectModel = result.value;
const projectPath = App.staticFilesStoreDir() + result.value.rootDir + "/";
await new CreateFolderUseCase().call(projectPath);
pipelineRealTimeService.setPipelineDependency(projectModel.project.pipelines, projectPath, projectModel._id);
}
};
}

View file

@ -1,10 +1,10 @@
import cluster, { Worker } from "node:cluster"; import cluster, { Worker } from "node:cluster";
import { TypedEvent } from "../helper/typed_event"; import { TypedEvent } from "../helpers/typed_event";
import { Result } from "../helper/result"; import { Result } from "../helpers/result";
import { WorkerDataExec, WorkerType } from "../helper/worker_computed"; import { WorkerDataExec, WorkerType } from "../helpers/worker_computed";
import { delay } from "../helper/delay"; import { delay } from "../helpers/delay";
import { ExecutorResult } from "../model/executor_result"; import { ExecutorResult } from "../models/executor_result";
import { EXEC_TYPE, ExecError, SpawnError } from "../model/exec_error_model"; import { EXEC_TYPE, ExecError, SpawnError } from "../models/exec_error_model";
abstract class IExecutorProgramService { abstract class IExecutorProgramService {
abstract execPath: string; abstract execPath: string;
@ -25,13 +25,9 @@ export class ExecutorProgramService
this.maxTime = maxTime; this.maxTime = maxTime;
} }
private async workerExecuted( private async workerExecuted(command: string, workerType: WorkerType, args: Array<string> | undefined = undefined) {
command: string,
workerType: WorkerType,
args: Array<string> | undefined = undefined
) {
cluster.setupPrimary({ cluster.setupPrimary({
exec: "./src/core/helper/worker_computed", exec: "/Users/idontsudo/Desktop/testdeck-mocha-seed/server/build/src/core/helpers/worker_computed.js",
}); });
const worker = cluster.fork(); const worker = cluster.fork();
@ -69,21 +65,13 @@ export class ExecutorProgramService
setTimeout(() => { setTimeout(() => {
this.worker.kill(); this.worker.kill();
this.emit( this.emit(
Result.error( Result.error(WorkerType.EXEC ? new ExecError(command, "timeout err") : new SpawnError(command, "timeout err"))
WorkerType.EXEC
? new ExecError(command, "timeout err")
: new SpawnError(command, "timeout err")
)
); );
}, this.maxTime!); }, this.maxTime!);
} }
} }
public async call( public async call(type: EXEC_TYPE, command: string, args: Array<string> | undefined = undefined): Promise<void> {
type: EXEC_TYPE,
command: string,
args: Array<string> | undefined = undefined
): Promise<void> {
if (type == EXEC_TYPE.EXEC) { if (type == EXEC_TYPE.EXEC) {
this.workerExecuted(command, WorkerType.EXEC); this.workerExecuted(command, WorkerType.EXEC);

View file

@ -1,20 +1,11 @@
import * as fs from "fs"; import * as fs from "fs";
import { resolve } from "node:path"; import { resolve } from "node:path";
import { promisify } from "node:util";
import { createHash } from "node:crypto"; import { createHash } from "node:crypto";
import "reflect-metadata";
import { BinaryLike } from "crypto"; import { BinaryLike } from "crypto";
import { import { EventsFileChanger, MetaDataFileManagerModel } from "../models/meta_data_file_manager_model";
EventsFileChanger, import { Result } from "../helpers/result";
MetaDataFileManagerModel, import { TypedEvent } from "../helpers/typed_event";
} from "../model/meta_data_file_manager_model"; import { lsStat, readFileAsync, readdir, stat } from "../repository/fs";
import { Result } from "../helper/result";
import { TypedEvent } from "../helper/typed_event";
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 = " ") { function joinBuffers(buffers: Array<Buffer>, delimiter = " ") {
const d = Buffer.from(delimiter); const d = Buffer.from(delimiter);
@ -27,13 +18,11 @@ async function readFileAtBuffer(path: string): Promise<Buffer> {
await readdir(path, { await readdir(path, {
encoding: "buffer", encoding: "buffer",
}) })
).reduce( ).reduce((accumulator, currentValue) => joinBuffers([accumulator, currentValue]), Buffer.from(""));
(accumulator, currentValue) => joinBuffers([accumulator, currentValue]),
Buffer.from("")
);
} }
return await readFileAsync(path); return await readFileAsync(path);
} }
function md5(content: Buffer | BinaryLike): Promise<string> { function md5(content: Buffer | BinaryLike): Promise<string> {
return new Promise((resolve, _reject) => { return new Promise((resolve, _reject) => {
return resolve(createHash("md5").update(content).digest("hex")); return resolve(createHash("md5").update(content).digest("hex"));
@ -47,6 +36,7 @@ export interface IHashesCache {
export abstract class IFilesChangeNotifierService { export abstract class IFilesChangeNotifierService {
abstract directory: string; abstract directory: string;
} }
export class FilesChangeNotifierService export class FilesChangeNotifierService
extends TypedEvent<Result<Error, IHashesCache>> extends TypedEvent<Result<Error, IHashesCache>>
implements IFilesChangeNotifierService implements IFilesChangeNotifierService
@ -69,11 +59,7 @@ export class FilesChangeNotifierService
const data = await readFileAsync(file); const data = await readFileAsync(file);
const md5Current = await md5(data); const md5Current = await md5(data);
this.hashes[file] = new MetaDataFileManagerModel( this.hashes[file] = new MetaDataFileManagerModel(file, md5Current, EventsFileChanger.static);
file,
md5Current,
EventsFileChanger.static
);
this.emit(Result.ok(this.hashes)); this.emit(Result.ok(this.hashes));
} }
async getFiles(dir: string): Promise<string | any[]> { async getFiles(dir: string): Promise<string | any[]> {
@ -91,46 +77,31 @@ export class FilesChangeNotifierService
try { try {
let md5Previous: string | null = null; let md5Previous: string | null = null;
let fsWait: NodeJS.Timeout | boolean = false; let fsWait: NodeJS.Timeout | boolean = false;
const watcher = fs.watch( const watcher = fs.watch(this.directory, { recursive: true }, async (_e, filename) => {
this.directory, const filePath = this.directory + filename;
{ recursive: true }, if (filename) {
async (_e, filename) => { if (fsWait) return;
const filePath = this.directory + filename; fsWait = setTimeout(() => {
if (filename) { fsWait = false;
if (fsWait) return; }, 100);
fsWait = setTimeout(() => { try {
fsWait = false; const file = await readFileAtBuffer(filePath);
}, 100); const md5Current = await md5(file);
try { if (md5Current === md5Previous) {
const file = await readFileAtBuffer(filePath); return;
const md5Current = await md5(file);
if (md5Current === md5Previous) {
return;
}
const status =
this.hashes[filePath] === undefined
? EventsFileChanger.create
: EventsFileChanger.update;
const model = new MetaDataFileManagerModel(
filePath,
md5Current,
status
);
this.hashes[filePath] = model;
md5Previous = md5Current;
this.emit(Result.ok(this.hashes));
} catch (error) {
this.emit(Result.ok(this.hashes));
this.hashes[filePath] = new MetaDataFileManagerModel(
filePath,
null,
EventsFileChanger.delete
);
} }
const status = this.hashes[filePath] === undefined ? EventsFileChanger.create : EventsFileChanger.update;
const model = new MetaDataFileManagerModel(filePath, md5Current, status);
this.hashes[filePath] = model;
md5Previous = md5Current;
this.emit(Result.ok(this.hashes));
} catch (error) {
this.emit(Result.ok(this.hashes));
this.hashes[filePath] = new MetaDataFileManagerModel(filePath, null, EventsFileChanger.delete);
} }
} }
); });
this.watcher = watcher; this.watcher = watcher;
return Result.ok(true); return Result.ok(true);
} catch (error) { } catch (error) {

View file

@ -0,0 +1,73 @@
import { TypedEvent } from "../helpers/typed_event";
import { ExecError } from "../models/exec_error_model";
import { ExecutorResult } from "../models/executor_result";
import { ActivePipeline } from "../models/active_pipeline_model";
import { IPipeline } from "../models/process_model";
import { Iteration } from "./stack_service";
export class PipelineRealTimeService extends TypedEvent<ActivePipeline> {
status: ActivePipeline;
pipelineModels?: IPipeline[];
constructor() {
super();
this.init();
}
private init(): void {
this.status = ActivePipeline.empty();
}
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(iteration);
}
iterationLogSaver(iteration: Iteration): void {
// throw new Error("Method not implemented.");
// citeration.result.data
}
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;
}
}
setPipelineDependency(pipelineModels: IPipeline[], path: string, projectUUID: string) {
this.pipelineModels = pipelineModels;
this.status["projectUUID"] = projectUUID;
this.status["path"] = path;
}
runPipeline(): void {
// const stack = new StackService(this.pipelineModels, this.path);
// this.status["pipelineIsRunning"] = true;
// stack.on(this.pipelineSubscriber);
// stack.call();
}
}

View file

@ -1,16 +1,13 @@
import { import { FilesChangeNotifierService, IHashesCache } from "./files_change_notifier_service";
FilesChangeNotifierService, import { IPipeline } from "../models/process_model";
IHashesCache,
} from "./files_change_notifier_service";
import { IPipeline } from "../model/process_model";
import { ExecutorProgramService } from "./executor_program_service"; import { ExecutorProgramService } from "./executor_program_service";
import { EXEC_EVENT, ExecError, SpawnError } from "../model/exec_error_model"; import { EXEC_EVENT, ExecError, SpawnError } from "../models/exec_error_model";
import { TypedEvent } from "../helper/typed_event"; import { TypedEvent } from "../helpers/typed_event";
import { Result } from "../helper/result"; import { Result } from "../helpers/result";
import { ExecutorResult } from "../model/executor_result"; import { ExecutorResult } from "../models/executor_result";
import { delay } from "../helper/delay"; import { delay } from "../helpers/delay";
import { TriggerService } from "./trigger_service"; import { TriggerService } from "./trigger_service";
import { Trigger } from "../../features/triggers/trigger_model"; import { Trigger } from "../../features/triggers/models/trigger_database_model";
export interface Iteration { export interface Iteration {
hashes: IHashesCache | null; hashes: IHashesCache | null;
@ -24,7 +21,7 @@ export abstract class IStackService {
abstract init(processed: IPipeline[], path: string): void; 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[]; callStack: Iteration[];
path: string; path: string;
constructor(processed: IPipeline[], path: string) { constructor(processed: IPipeline[], path: string) {
@ -44,10 +41,7 @@ export class StackService extends TypedEvent<string> implements IStackService {
} }
} }
private commandHandler(processMetaData: IPipeline) { private commandHandler(processMetaData: IPipeline) {
processMetaData.process.command = processMetaData.process.command.replace( processMetaData.process.command = processMetaData.process.command.replace("$PATH", this.path);
"$PATH",
this.path
);
return processMetaData; return processMetaData;
} }
public async call() { public async call() {
@ -56,36 +50,23 @@ export class StackService extends TypedEvent<string> implements IStackService {
for await (const el of this.callStack!) { for await (const el of this.callStack!) {
await this.execStack(inc, el); await this.execStack(inc, el);
inc += 1; inc += 1;
this.emit(this.callStack);
} }
} }
async execStack( async execStack(stackNumber: number, stackLayer: Iteration): Promise<void | boolean> {
stackNumber: number,
stackLayer: Iteration
): Promise<void | boolean> {
const executorService = new ExecutorProgramService(this.path); const executorService = new ExecutorProgramService(this.path);
executorService.call(stackLayer.process.process.type, stackLayer.process.process.command);
executorService.call( const filesChangeNotifierService = new FilesChangeNotifierService(this.path);
stackLayer.process.process.type,
stackLayer.process.process.command
);
const filesChangeNotifierService = new FilesChangeNotifierService(
this.path
);
filesChangeNotifierService.call(); filesChangeNotifierService.call();
const result = await this.waitEvent< const result = await this.waitEvent<Result<ExecError | SpawnError, ExecutorResult>>(executorService);
Result<ExecError | SpawnError, ExecutorResult>
>(executorService);
await delay(100); await delay(100);
if (result.isSuccess()) { if (result.isSuccess()) {
this.callStack[stackNumber].result = result.value; this.callStack[stackNumber].result = result.value;
this.callStack[stackNumber].hashes = filesChangeNotifierService.hashes; this.callStack[stackNumber].hashes = filesChangeNotifierService.hashes;
const triggerResult = await this.triggerExec( const triggerResult = await this.triggerExec(stackLayer.process.trigger, stackNumber);
stackLayer.process.trigger,
stackNumber
);
triggerResult.fold( triggerResult.fold(
(s) => { (s) => {
s; s;
@ -120,15 +101,15 @@ export class StackService extends TypedEvent<string> implements IStackService {
}); });
return promise; return promise;
} }
private async triggerExec( private async triggerExec(trigger: Trigger | null, stackNumber: number): Promise<Result<boolean, boolean>> {
trigger: Trigger, if (trigger !== null) {
stackNumber: number const hashes = this.callStack[stackNumber].hashes;
): Promise<Result<boolean, boolean>> {
const hashes = this.callStack[stackNumber].hashes;
if (hashes != null) { if (hashes != null) {
return await new TriggerService(trigger, hashes, this.path).call(); 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

@ -1,9 +1,9 @@
import * as vm from "node:vm"; import * as vm from "node:vm";
import { IHashesCache } from "./files_change_notifier_service"; import { IHashesCache } from "./files_change_notifier_service";
import { EventsFileChanger } from "../model/meta_data_file_manager_model"; import { EventsFileChanger } from "../models/meta_data_file_manager_model";
import { Result } from "../helper/result"; import { Result } from "../helpers/result";
import { TypedEvent } from "../helper/typed_event"; import { TypedEvent } from "../helpers/typed_event";
import { Trigger, TriggerType } from "../../features/triggers/trigger_model"; import { Trigger, TriggerType } from "../../features/triggers/models/trigger_database_model";
export class TriggerCallResult { export class TriggerCallResult {
results: Array<TriggerSuccessResult | TriggerErrorReport>; results: Array<TriggerSuccessResult | TriggerErrorReport>;
@ -35,11 +35,7 @@ export class TriggerErrorReport extends Error {
hashes: IHashesCache; hashes: IHashesCache;
trigger: string | Trigger; trigger: string | Trigger;
processOutput: any; processOutput: any;
constructor( constructor(hashes: IHashesCache, trigger: string | Trigger, processOutput?: any) {
hashes: IHashesCache,
trigger: string | Trigger,
processOutput?: any
) {
super(); super();
this.hashes = hashes; this.hashes = hashes;
this.trigger = trigger; this.trigger = trigger;
@ -123,9 +119,7 @@ export class TriggerService extends TypedEvent<TriggerCallResult> {
}) })
); );
} }
private reportTriggerTypeProcess( private reportTriggerTypeProcess(triggerResult: Array<TriggerSuccessResult>): TriggerCallResult {
triggerResult: Array<TriggerSuccessResult>
): TriggerCallResult {
return new TriggerCallResult( return new TriggerCallResult(
triggerResult.map((el) => { triggerResult.map((el) => {
if (el.status) { if (el.status) {

View file

@ -0,0 +1,19 @@
import { App } from "../controllers/app";
import { dirIsExists } from "../repository/fs";
import { CreateFolderUseCase } from "./crete_folder_usecase";
export class CheckAndCreateStaticFilesFolderUseCase {
call = async (): Promise<void> => {
if (await dirIsExists(App.staticFilesStoreDir())) {
return;
}
const createFolderUseCase = await new CreateFolderUseCase().call(App.staticFilesStoreDir());
createFolderUseCase.fold(
(_s) => {},
(e) => {
console.log(e);
}
);
};
}

View file

@ -1,4 +1,4 @@
import { Result } from "../helper/result"; import { Result } from "../helpers/result";
import { ICreateObjectDataBase } from "../interfaces/response"; import { ICreateObjectDataBase } from "../interfaces/response";
export class CreateDataBaseModelUseCase<V> { export class CreateDataBaseModelUseCase<V> {
@ -8,9 +8,7 @@ export class CreateDataBaseModelUseCase<V> {
this.databaseModel = model; this.databaseModel = model;
} }
call = async ( call = async (validationModel: V): Promise<Result<Error, ICreateObjectDataBase>> => {
validationModel: V
): Promise<Result<Error, ICreateObjectDataBase>> => {
try { try {
const result = new this.databaseModel(validationModel); const result = new this.databaseModel(validationModel);

View file

@ -0,0 +1,13 @@
import { Result } from "../helpers/result";
import { writeFileAsync } from "../repository/fs";
export class CreateFileUseCase {
async call(path: string, buffer: Buffer): Promise<Result<Error, void>> {
try {
await writeFileAsync(path, buffer);
return Result.ok();
} catch (err) {
return Result.error(err as Error);
}
}
}

View file

@ -0,0 +1,17 @@
import { Result } from "../helpers/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);
}
};
}

View file

@ -0,0 +1,15 @@
import mongoose from "mongoose";
import { Result } from "../helpers/result";
export class DataBaseConnectUseCase {
call = async (dataBaseName: string = "test"): Promise<Result<Error, void>> => {
try {
await mongoose.connect(`mongodb://127.0.0.1:27017/${dataBaseName}`);
return Result.ok();
} catch (error) {
console.log(error);
console.log("database connect error");
return Result.error(error as Error);
}
};
}

View file

@ -1,16 +1,21 @@
import { Result } from "../helper/result"; import { Result } from "../helpers/result";
export class DeleteDataBaseModelUseCase<D> { export class DeleteDataBaseModelUseCase<D> {
databaseModel: D | any; databaseModel: D | any;
constructor(model) { constructor(model) {
this.databaseModel = model; this.databaseModel = model;
} }
call = async (id: string): Promise<Result<Error, boolean>> => { call = async (id: string): Promise<Result<Error, any>> => {
try { try {
const model = new this.databaseModel({ _id: id }); const model = this.databaseModel as any;
await model.deleteOne();
return Result.ok(true); const result = await model.deleteOne({ _id: id });
if (result.deletedCount === 0) {
return Result.error(Error(`the database does not have a collection with this ID:${id}`));
}
return Result.ok({ ok: "model delete" });
} catch (error) { } catch (error) {
return Result.error(error); return Result.error(error);
} }

View file

@ -0,0 +1,7 @@
import mongoose from "mongoose";
export class DropDataBaseUseCase {
call = async () => {
await mongoose.connection.dropDatabase();
};
}

View file

@ -0,0 +1,5 @@
export class ExitAppUseCase {
call = () => {
process.exit();
};
}

View file

@ -1,4 +1,4 @@
import { Result } from "../helper/result"; import { Result } from "../helpers/result";
export class PaginationDataBaseModelUseCase<D> { export class PaginationDataBaseModelUseCase<D> {
databaseModel: D; databaseModel: D;
@ -9,9 +9,7 @@ export class PaginationDataBaseModelUseCase<D> {
this.perPage = perPage; this.perPage = perPage;
} }
call = async ( call = async (pageNumber: number): Promise<Result<Error, [D]>> => {
pageNumber: number
): Promise<Result<Error, [D]>> => {
try { try {
const page = Math.max(0, pageNumber); const page = Math.max(0, pageNumber);
const model = this.databaseModel as any; const model = this.databaseModel as any;

View file

@ -0,0 +1,17 @@
import { Result } from "../helpers/result";
export class ReadByIdDataBaseModelUseCase<D> {
databaseModel: D;
constructor(model) {
this.databaseModel = model;
}
call = async (id: string): Promise<Result<Error, D>> => {
try {
const dbModel = this.databaseModel as any;
return Result.ok(await dbModel.findById(id));
} catch (error) {
return Result.error(error);
}
};
}

View file

@ -1,5 +1,4 @@
import { Result } from "../helpers/result";
import { Result } from "../helper/result";
export class ReadByIdDataBaseModelUseCase<D> { export class ReadByIdDataBaseModelUseCase<D> {
databaseModel: D; databaseModel: D;

View file

@ -0,0 +1,18 @@
import { Result } from "../helpers/result";
export class SearchDataBaseModelUseCase<T> {
model: any;
constructor(model: any) {
this.model = model;
}
call = async (findFilter: Partial<T>): Promise<Result<null, T>> => {
const result = await this.model.findOne(findFilter);
if (result === null) {
return Result.error(null);
} else {
return Result.ok(result);
}
};
}

View file

@ -1,4 +1,4 @@
import { Result } from "../helper/result"; import { Result } from "../helpers/result";
interface uuid { interface uuid {
_id?: string; _id?: string;
@ -17,7 +17,14 @@ export class UpdateDataBaseModelUseCase<D, T extends uuid> {
} }
const databaseModel = this.databaseModel as any; const databaseModel = this.databaseModel as any;
const model = await databaseModel.findById(updateModel._id); const model = await databaseModel.findById(updateModel._id);
if (model === null) {
throw new Error(
`we cant find the model in the database with ID:${updateModel._id} collection: ${databaseModel.modelName}`
);
}
Object.assign(model, updateModel); Object.assign(model, updateModel);
await model.save(); await model.save();
return Result.ok(model as T); return Result.ok(model as T);
} catch (error) { } catch (error) {

View file

@ -0,0 +1,58 @@
import { CallbackStrategyWithEmpty, CoreHttpController } from "../../core/controllers/http_controller";
import { Result } from "../../core/helpers/result";
import { EXEC_TYPE } from "../../core/models/exec_error_model";
import { ExecutorResult } from "../../core/models/executor_result";
import { IPipeline, IssueType, StackGenerateType } from "../../core/models/process_model";
import { StackService } from "../../core/services/stack_service";
import { TriggerType } from "../triggers/models/trigger_database_model";
class NixStoreModel {}
const getNixStoreFolderCommand: IPipeline[] = [
{
process: {
type: EXEC_TYPE.EXEC,
command: `ls /nix/store`,
isGenerating: true,
isLocaleCode: false,
issueType: IssueType.WARNING,
},
trigger: {
type: TriggerType.FILE,
value: ["context"],
},
env: null,
stackGenerateType: StackGenerateType.SINGLETON,
},
];
class GetNixStorePackagesUseCase extends CallbackStrategyWithEmpty {
call = async () => {
const stackService = new StackService(
getNixStoreFolderCommand,
"/Users/idontsudo/Desktop/testdeck-mocha-seed/server/build/test/"
);
stackService.call();
const promise = new Promise((resolve, _reject) => {
stackService.on((e) => {
const iteration = e.firstElement();
if (iteration.result instanceof ExecutorResult) {
const nixPackage = iteration.result.data;
resolve(nixPackage.split("\n").filter((e) => e.hasNoPattern(".drv")));
} else {
return "GetNixStorePackagesUseCase unknown Error";
}
});
});
return Result.ok(await promise);
};
}
export class NixStoreManagerPresentation extends CoreHttpController<NixStoreModel> {
constructor() {
super({
url: "nix_store_api",
});
super.get(new GetNixStorePackagesUseCase().call);
}
}

View file

@ -0,0 +1,27 @@
import { Schema, model } from "mongoose";
import { IPipeline } from "../../../core/models/process_model";
import { schemaProcess } from "../../process/models/process_database_model";
import { triggerSchema } from "../../triggers/models/trigger_database_model";
export const PipelineSchema = new Schema({
process: {
type: Schema.Types.ObjectId,
ref: schemaProcess,
autopopulate: true,
default: null,
},
trigger: {
type: Schema.Types.ObjectId,
ref: triggerSchema,
autopopulate: true,
default: null,
},
stackGenerateType: {
type: String,
default: null,
},
}).plugin(require("mongoose-autopopulate"));
export const schemaPipeline = "Pipeline";
export const PipelineDBModel = model<IPipeline>(schemaPipeline, PipelineSchema);

View file

@ -0,0 +1,21 @@
import { IsOptional, ValidateNested } from "class-validator";
import { IPipeline, IProcess, StackGenerateType } from "../../../core/models/process_model";
import { Type } from "class-transformer";
import { ProcessModel } from "../../process/models/process_validation_model";
import { TriggerModelValidationModel } from "../../triggers/models/trigger_validation_model";
export class PipelineModel implements IPipeline {
@ValidateNested()
@Type(() => ProcessModel)
public process: IProcess;
@ValidateNested()
@Type(() => TriggerModelValidationModel)
public trigger: TriggerModelValidationModel;
@IsOptional()
public env = null;
@IsOptional()
public stackGenerateType: StackGenerateType;
}

View file

@ -0,0 +1,17 @@
import { IsMongoId, IsOptional } from "class-validator";
import { IProcess, StackGenerateType } from "../../../core/models/process_model";
import { TriggerModelValidationModel } from "../../triggers/models/trigger_validation_model";
export class PipelineValidationModel {
@IsMongoId()
public process: IProcess;
@IsMongoId()
public trigger: TriggerModelValidationModel;
@IsOptional()
public env = null;
@IsOptional()
public stackGenerateType: StackGenerateType;
}

View file

@ -1,11 +1,8 @@
import { IsMongoId, IsEnum } from "class-validator"; import { IsMongoId, IsEnum } from "class-validator";
import { Schema, model } from "mongoose"; import { Schema, model } from "mongoose";
import { StackGenerateType } from "../../core/model/process_model"; import { TriggerModel, triggerSchema } from "../triggers/trigger_model";
import {
TriggerModel,
triggerSchema,
} from "../triggers/trigger_model";
import { schemaProcess } from "../process/process_model"; import { schemaProcess } from "../process/process_model";
import { StackGenerateType } from "../../core/models/process_model";
export const PipelineSchema = new Schema({ export const PipelineSchema = new Schema({
process: { process: {
@ -27,10 +24,7 @@ export const PipelineSchema = new Schema({
export const schemaPipeline = "Pipeline"; export const schemaPipeline = "Pipeline";
export const PipelineDBModel = model<PipelineModel>( export const PipelineDBModel = model<PipelineModel>(schemaPipeline, PipelineSchema);
schemaPipeline,
PipelineSchema
);
export class PipelineModel { export class PipelineModel {
@IsMongoId() @IsMongoId()

View file

@ -1,16 +1,13 @@
import { CrudController } from "../../core/controllers/crud_controller"; import { CrudController } from "../../core/controllers/crud_controller";
import { PipelineDBModel, PipelineModel } from "./pipeline_model"; import { PipelineDBModel } from "./models/pipeline_database_model";
import { PipelineValidationModel } from "./models/pipeline_validation_model";
export class PipelinePresentation extends CrudController< export class PipelinePresentation extends CrudController<PipelineValidationModel, typeof PipelineDBModel> {
PipelineModel,
typeof PipelineDBModel
> {
constructor() { constructor() {
super({ super({
url: "pipeline", url: "pipeline",
validationModel: PipelineModel, validationModel: PipelineValidationModel,
databaseModel: PipelineDBModel, databaseModel: PipelineDBModel,
}); });
} }
} }

View file

@ -0,0 +1,35 @@
import { Schema, model } from "mongoose";
import { IProcess } from "../../../core/models/process_model";
export const ProcessSchema = new Schema({
type: {
type: String,
},
description: {
type: String,
},
command: {
type: String,
},
isGenerating: {
type: String,
},
isLocaleCode: {
type: String,
},
issueType: {
type: String,
},
timeout: {
type: Number,
default: null,
},
commit: {
type: String,
default: null,
},
});
export const schemaProcess = "Process";
export const ProcessDBModel = model<IProcess>(schemaProcess, ProcessSchema);

View file

@ -0,0 +1,31 @@
import { IsBoolean, IsEnum, IsNumber, IsOptional, IsString } from "class-validator";
import { EXEC_TYPE } from "../../../core/models/exec_error_model";
import { IProcess, IssueType } from "../../../core/models/process_model";
export class ProcessModel implements IProcess {
@IsEnum(EXEC_TYPE)
public type: EXEC_TYPE;
@IsString()
public description: string;
@IsString()
public command: string;
@IsBoolean()
public isGenerating: boolean;
@IsBoolean()
public isLocaleCode: boolean;
@IsEnum(IssueType)
public issueType: IssueType;
@IsOptional()
@IsNumber()
public timeout?: number;
@IsOptional()
@IsString()
public commit?: string;
}

View file

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

View file

@ -1,10 +1,8 @@
import { CrudController } from "../../core/controllers/crud_controller"; import { CrudController } from "../../core/controllers/crud_controller";
import { ProcessDBModel, ProcessModel } from "./process_model"; import { ProcessDBModel } from "./models/process_database_model";
import { ProcessModel } from "./models/process_validation_model";
export class ProcessPresentation extends CrudController< export class ProcessPresentation extends CrudController<ProcessModel, typeof ProcessDBModel> {
ProcessModel,
typeof ProcessDBModel
> {
constructor() { constructor() {
super({ super({
url: "process", url: "process",

View file

@ -0,0 +1,30 @@
import { App } from "../../../core/controllers/app";
import { Result } from "../../../core/helpers/result";
import { CreateDataBaseModelUseCase } from "../../../core/usecases/create_database_model_usecase";
import { CreateFolderUseCase } from "../../../core/usecases/crete_folder_usecase";
import { ProjectInstanceDbModel } from "../models/project_instance_database_model";
import { ProjectInstanceValidationModel } from "../models/project_instance_validation_model";
import { v4 as uuidv4 } from "uuid";
export class CreateNewProjectInstanceScenario {
call = async (model: ProjectInstanceValidationModel): 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(ProjectInstanceDbModel).call(model);
if (createDataBaseModelUseCase.isFailure()) {
return createDataBaseModelUseCase.forward();
}
return Result.ok({ status: "ok" });
} catch (error) {
return Result.error(error as Error);
}
};
}

View file

@ -0,0 +1,23 @@
import { CallbackStrategyWithFileUpload, ResponseBase } from "../../../core/controllers/http_controller";
import { Result } from "../../../core/helpers/result";
import { IFile } from "../../../core/interfaces/file";
import { CreateFileUseCase } from "../../../core/usecases/create_file_usecase";
import { PipelineStatusUseCase } from "../../realtime/domain/pipeline_status_usecase";
export class UploadCadFileToProjectScenario extends CallbackStrategyWithFileUpload {
checkingFileExpression: RegExp = RegExp(".FCStd");
async call(file: IFile): ResponseBase {
const pipelineStatusUseCase = await new PipelineStatusUseCase().call();
if (pipelineStatusUseCase.isFailure()) {
return pipelineStatusUseCase.forward();
}
const projectFolder = pipelineStatusUseCase.value.path;
const createFileUseCase = await new CreateFileUseCase().call(projectFolder + file.name, file.data);
if (createFileUseCase.isFailure()) {
return createFileUseCase.forward();
}
return Result.ok("ok");
}
}

View file

@ -0,0 +1,33 @@
import { Schema, model } from "mongoose";
import { IProjectModel, projectSchema } from "../../projects/models/project_database_model";
export interface IProjectInstanceModel {
_id: string;
project: IProjectModel;
description: string;
rootDir: string;
isActive: boolean;
}
export const ProjectInstanceSchema = new Schema({
project: {
type: Schema.Types.ObjectId,
ref: projectSchema,
autopopulate: true,
default: null,
},
description: {
type: String,
},
rootDir: {
type: String,
},
isActive: {
type: Boolean,
default: false,
},
}).plugin(require("mongoose-autopopulate"));
export const schemaProjectInstance = "instance_project";
export const ProjectInstanceDbModel = model<IProjectInstanceModel>(schemaProjectInstance, ProjectInstanceSchema);

View file

@ -0,0 +1,12 @@
import { IsMongoId, IsOptional, IsString } from "class-validator";
export class ProjectInstanceValidationModel {
@IsMongoId()
public project: string;
@IsString()
public description: string;
@IsOptional()
public rootDir: string;
}

View file

@ -0,0 +1,27 @@
import { CrudController } from "../../core/controllers/crud_controller";
import { CreateNewProjectInstanceScenario } from "./domain/create_new_project_scenario";
import { UploadCadFileToProjectScenario } from "./domain/upload_file_to_to_project_scenario";
import { ProjectInstanceDbModel } from "./models/project_instance_database_model";
import { ProjectInstanceValidationModel } from "./models/project_instance_validation_model";
export class ProjectInstancePresentation extends CrudController<
ProjectInstanceValidationModel,
typeof ProjectInstanceDbModel
> {
constructor() {
super({
validationModel: ProjectInstanceValidationModel,
url: "project_instance",
databaseModel: ProjectInstanceDbModel,
});
super.post(new CreateNewProjectInstanceScenario().call);
super.subRoutes = [
{
method: "POST",
subUrl: "upload",
fn: new UploadCadFileToProjectScenario(),
},
];
}
}

View file

@ -0,0 +1,31 @@
import { Schema, model } from "mongoose";
import { schemaPipeline } from "../../pipelines/models/pipeline_database_model";
import { PipelineValidationModel } from "../../pipelines/models/pipeline_validation_model";
export interface IProjectModel {
_id?: string;
pipelines: [PipelineValidationModel];
rootDir: string;
description: string;
isActive: boolean;
}
export const ProjectSchema = new Schema({
pipelines: {
type: Array<Schema.Types.ObjectId>,
ref: schemaPipeline,
autopopulate: true,
default: null,
},
description: {
type: String,
},
isActive: {
type: Boolean,
default: false,
},
}).plugin(require("mongoose-autopopulate"));
export const projectSchema = "Projects";
export const ProjectDBModel = model<IProjectModel>(projectSchema, ProjectSchema);

View file

@ -0,0 +1,9 @@
import { IsArray, IsString } from "class-validator";
export class ProjectValidationModel {
@IsArray()
public pipelines: [string];
@IsString()
public description: string;
}

View file

@ -1,15 +1,12 @@
// import { TriggerDBModel, TriggerModel } from "./trigger_model";
import { CrudController } from "../../core/controllers/crud_controller"; import { CrudController } from "../../core/controllers/crud_controller";
import { ProjectDBModel, ProjectModel } from "./projects_model"; import { ProjectDBModel } from "./models/project_database_model";
import { ProjectValidationModel } from "./models/project_validation_model";
export class ProjectsPresentation extends CrudController< export class ProjectsPresentation extends CrudController<ProjectValidationModel, typeof ProjectDBModel> {
ProjectModel,
typeof ProjectDBModel
> {
constructor() { constructor() {
super({ super({
url: "project", url: "project",
validationModel: ProjectModel, validationModel: ProjectValidationModel,
databaseModel: ProjectDBModel, databaseModel: ProjectDBModel,
}); });
} }

View file

@ -0,0 +1,19 @@
import { Result } from "../../../core/helpers/result";
import { ActivePipeline } from "../../../core/models/active_pipeline_model";
import { pipelineRealTimeService } from "../realtime_presentation";
export class PipelineStatusUseCase {
async call(): Promise<Result<Error, ActivePipeline>> {
try {
const status = pipelineRealTimeService.status;
if (status.projectUUID !== null) {
return Result.ok(status);
}
if (status.projectUUID === null) {
return Result.error(new Error("pipelineRealTimeService does not have an active project instance"));
}
} catch (error) {
return Result.error(error as Error);
}
}
}

View file

@ -0,0 +1,41 @@
import { App } from "../../../core/controllers/app";
import { Result } from "../../../core/helpers/result";
import { ReadByIdDataBaseModelUseCase } from "../../../core/usecases/read_by_id_database_model_usecase";
import { UpdateDataBaseModelUseCase } from "../../../core/usecases/update_database_model_usecase";
import {
IProjectInstanceModel,
ProjectInstanceDbModel,
} from "../../project_instance/models/project_instance_database_model";
import { RealTimeValidationModel, pipelineRealTimeService } from "../realtime_presentation";
export class RunInstancePipelineUseCase {
async call(model: RealTimeValidationModel): Promise<Result<Error, any>> {
const { id } = model;
const readByIdDataBaseModelUseCase = await new ReadByIdDataBaseModelUseCase<IProjectInstanceModel>(
ProjectInstanceDbModel
).call(id);
if (readByIdDataBaseModelUseCase.isFailure()) {
return readByIdDataBaseModelUseCase.forward();
}
const projectModel = readByIdDataBaseModelUseCase.value;
projectModel.isActive = true;
const updateDataBaseModelUseCase = await new UpdateDataBaseModelUseCase<IProjectInstanceModel, any>(
ProjectInstanceDbModel
).call(projectModel);
if (updateDataBaseModelUseCase.isFailure()) {
return updateDataBaseModelUseCase.forward();
}
pipelineRealTimeService.setPipelineDependency(
projectModel.project.pipelines,
App.staticFilesStoreDir() + projectModel.rootDir + "/",
projectModel._id
);
pipelineRealTimeService.runPipeline();
return Result.ok({ status: "ok" });
}
}

View file

@ -0,0 +1,24 @@
import { IsString } from "class-validator";
import { CoreHttpController } from "../../core/controllers/http_controller";
import { PipelineRealTimeService } from "../../core/services/pipeline_real_time_service";
import { RunInstancePipelineUseCase } from "./domain/run_instance_pipeline_usecase";
import { PipelineStatusUseCase } from "./domain/pipeline_status_usecase";
export const pipelineRealTimeService = new PipelineRealTimeService();
export class RealTimeValidationModel {
@IsString()
public id: string;
}
export class RealTimePresentation extends CoreHttpController<RealTimeValidationModel> {
constructor() {
super({
validationModel: RealTimeValidationModel,
url: "realtime",
databaseModel: null,
});
super.post(new RunInstancePipelineUseCase().call);
super.get(new PipelineStatusUseCase().call);
}
}

View file

@ -0,0 +1,36 @@
import { Schema, model } from "mongoose";
export interface ITriggerModel {
_id?: string;
type: string;
description: string;
value: string[];
}
export const TriggerSchema = new Schema({
type: {
type: String,
require: true,
},
description: {
type: String,
},
value: {
type: Array,
require: true,
},
});
export const triggerSchema = "Trigger";
export const TriggerDBModel = model<ITriggerModel>(triggerSchema, TriggerSchema);
export enum TriggerType {
PROCESS = "PROCESS",
FILE = "FILE",
}
export interface Trigger {
type: TriggerType;
value: string[];
}

View file

@ -0,0 +1,16 @@
import { IsArray, IsOptional, IsEnum, IsString } from "class-validator";
import { ITriggerModel, TriggerType } from "./trigger_database_model";
export class TriggerModelValidationModel implements ITriggerModel {
@IsOptional()
public _id: string;
@IsString()
public description;
@IsEnum(TriggerType)
public type: TriggerType;
@IsArray()
public value: string[];
}

View file

@ -1,14 +1,12 @@
import { TriggerDBModel, TriggerModel } from "./trigger_model";
import { CrudController } from "../../core/controllers/crud_controller"; import { CrudController } from "../../core/controllers/crud_controller";
import { TriggerDBModel } from "./models/trigger_database_model";
import { TriggerModelValidationModel as TriggerValidationMode } from "./models/trigger_validation_model";
export class TriggerPresentation extends CrudController< export class TriggerPresentation extends CrudController<TriggerValidationMode, typeof TriggerDBModel> {
TriggerModel,
typeof TriggerDBModel
> {
constructor() { constructor() {
super({ super({
url: "trigger", url: "trigger",
validationModel: TriggerModel, validationModel: TriggerValidationMode,
databaseModel: TriggerDBModel, databaseModel: TriggerDBModel,
}); });
} }

View file

@ -1,21 +1,12 @@
import "reflect-metadata"; import "reflect-metadata";
import { App } from "./core/controllers/app"; import { App } from "./core/controllers/app";
import { Routes } from "./core/interfaces/router"; import { SocketSubscriber } from "./core/controllers/socket_controller";
import { TriggerPresentation } from "./features/triggers/triggers_presentation"; import { extensions } from "./core/extensions/extensions";
import { ProjectsPresentation } from "./features/projects/projects_presentation"; import { httpRoutes } from "./core/controllers/routes";
import { PipelinePresentation } from "./features/pipelines/pipeline_presentation"; import { pipelineRealTimeService } from "./features/realtime/realtime_presentation";
import { ProcessPresentation } from "./features/process/process_presentation";
extensions();
const httpRoutes: Routes[] = [ const socketSubscribers = [new SocketSubscriber(pipelineRealTimeService, "realtime")];
new TriggerPresentation(),
new ProjectsPresentation(),
new ProcessPresentation(),
new PipelinePresentation(),
].map((el) => el.call());
const computedFolder = ""; new App(httpRoutes, socketSubscribers).listen();
new App(httpRoutes, computedFolder).listen();

View file

@ -0,0 +1,49 @@
import { CrudController } from "../../src/core/controllers/crud_controller";
import { ClassValidatorMocker } from "../../src/core/helpers/class_validator_mocker";
import { HttpRepository } from "../../src/core/repository/http_repository";
function instanceOfObjectAndHaveId(s: any): string {
if (s instanceof Object && "id" in s) {
return s.id;
}
if (s instanceof Object && "_id" in s) {
return s._id;
}
throw Error(`${s} is not instance object or not have property _id`);
}
export class CrudControllerTest {
controllerTest: CrudController<any, any>;
httpRepository: HttpRepository;
constructor(port: number, controller: CrudController<any, any>) {
this.controllerTest = controller;
this.httpRepository = new HttpRepository(`http://localhost:${port}`);
}
async call() {
let result = false;
const mockModel = ClassValidatorMocker.create<any>(this.controllerTest.validationModel);
const postRequestBody = await this.httpRepository.jsonRequest(this.controllerTest.mainURL, "POST", mockModel);
await postRequestBody.map(async (s) => {
const id = instanceOfObjectAndHaveId(s);
const getRequestBody = await this.httpRepository.jsonRequest(this.controllerTest.mainURL, "GET");
await getRequestBody.map(async (el) => {
if (el instanceof Array) {
const firstElement = el.firstElement();
const mockModelUpdate = ClassValidatorMocker.create<any>(this.controllerTest.validationModel);
Object.assign(firstElement, mockModelUpdate);
delete firstElement.__v;
const putReqBody = await this.httpRepository.jsonRequest(this.controllerTest.mainURL, "PUT", firstElement);
await putReqBody.map(async () => {
(await this.httpRepository.jsonRequest(this.controllerTest.mainURL + "?id=" + id, "DELETE")).map(() => {
result = true;
});
});
}
});
});
return result;
}
}

View file

@ -1,12 +1,14 @@
import mongoose from "mongoose"; import mongoose from "mongoose";
import { delay } from "../../src/core/helper/delay"; import { delay } from "../../src/core/helpers/delay";
import { Result } from "../../src/core/helper/result"; import { Result } from "../../src/core/helpers/result";
import { TypedEvent } from "../../src/core/helper/typed_event"; import { TypedEvent } from "../../src/core/helpers/typed_event";
import { DropDataBaseUseCase } from "../../src/core/usecases/drop_database_usecase";
import { ExitAppUseCase } from "../../src/core/usecases/exit_app_usecase";
export const before = async () => {
export const before = async () =>{ new DropDataBaseUseCase().call();
await mongoose.connection.dropDatabase() new ExitAppUseCase().call();
} };
export class TestCore { export class TestCore {
allTests = 0; allTests = 0;
@ -33,17 +35,14 @@ export class TestCore {
console.log("\x1b[32m", "============="); console.log("\x1b[32m", "=============");
if (this.allTests - this.testOk === 0) { if (this.allTests - this.testOk === 0) {
console.log( console.log("\x1b[32m", `✅ All test success! ${this.allTests}/${this.testOk}`);
"\x1b[32m",
`✅ All test success! ${this.allTests}/${this.testOk}`
);
return; return;
} }
if (this.testErr !== 0) { if (this.testErr !== 0) {
console.log("\x1b[31m", "❌ test error:" + String(this.testErr)); console.log("\x1b[31m", "❌ test error:" + String(this.testErr));
console.log("\x1b[32m", `✅ test success! ${this.testOk}`); console.log("\x1b[32m", `✅ test success! ${this.testOk}`);
} }
await before() await before();
}; };
resultTest = async ( resultTest = async (
eventClass: TypedEvent<Result<any, any>> | any, eventClass: TypedEvent<Result<any, any>> | any,
@ -54,24 +53,20 @@ export class TestCore {
) => { ) => {
let testIsOk = false; let testIsOk = false;
eventClass.call(...args); eventClass.call(...args);
const listener = eventClass.on( const listener = eventClass.on((e: { fold: (arg0: (_s: any) => void, arg1: (_e: any) => void) => void }) => {
(e: { e.fold(
fold: (arg0: (_s: any) => void, arg1: (_e: any) => void) => void; () => {
}) => { if (isOk) {
e.fold( testIsOk = true;
() => {
if (isOk) {
testIsOk = true;
}
},
() => {
if (!isOk) {
testIsOk = true;
}
} }
); },
} () => {
); if (!isOk) {
testIsOk = true;
}
}
);
});
await delay(delayTime); await delay(delayTime);
this.assert(testIsOk, testName); this.assert(testIsOk, testName);
listener.dispose(); listener.dispose();

View file

@ -1,17 +1,15 @@
setTimeout(() => {
console.log("log");
setTimeout(() =>{ console.log("log");
console.log('log') console.log("log");
console.log('log') console.log("log");
console.log('log') console.log("log");
console.log('log') console.log("log");
console.log('log') console.log("log");
console.log('log') console.log("log");
console.log('log') console.log("log");
console.log('log') console.log("log");
console.log('log') }, 2000);
console.log('log') setTimeout(() => {
},2000) console.log("log end");
setTimeout(() =>{ }, 5000);
console.log('log end')
}, 5000)

View file

@ -1,4 +1,4 @@
const seconds = 1000 * 10 const seconds = 1000 * 10;
setTimeout(()=>{ setTimeout(() => {
console.log(200) console.log("long code");
}, seconds) }, seconds);

View file

@ -0,0 +1,43 @@
import { EXEC_TYPE } from "../../src/core/models/exec_error_model";
import { IPipeline, IssueType, StackGenerateType } from "../../src/core/models/process_model";
import { TriggerType } from "../../src/features/triggers/models/trigger_database_model";
export const mockSimplePipeline: IPipeline[] = [
{
process: {
type: EXEC_TYPE.EXEC,
command: `nix run gitlab:robossembler/nix-robossembler-overlay#test-script '{
"filesMeta":[
{"type":"folder","name":"example", "path": null,"rewrite":true}
],
"path":"$PATH"
}'`,
isGenerating: true,
isLocaleCode: false,
issueType: IssueType.WARNING,
},
trigger: {
type: TriggerType.PROCESS,
value: [""],
},
env: null,
stackGenerateType: StackGenerateType.SINGLETON,
},
{
process: {
type: EXEC_TYPE.EXEC,
command: `nix run gitlab:robossembler/nix-robossembler-overlay#test-script '{
"filesMeta":[
{"type":"file","name":"1.txt", "path":"example","rewrite":true}
],
"path":"$PATH"
}'`,
isGenerating: true,
isLocaleCode: false,
issueType: IssueType.WARNING,
},
trigger: null,
env: null,
stackGenerateType: StackGenerateType.SINGLETON,
},
];

View file

@ -1,6 +1,6 @@
import { Schema, model } from "mongoose"; import { Schema, model } from "mongoose";
export interface ITestModel{ export interface ITestModel {
_id?: string; _id?: string;
result: string; result: string;
} }
@ -12,4 +12,3 @@ export const TestSchema = new Schema({
const schema = "Test"; const schema = "Test";
export const TestDBModel = model<ITestModel>(schema, TestSchema); export const TestDBModel = model<ITestModel>(schema, TestSchema);

View file

@ -1,6 +1,6 @@
import { delay } from "../../src/core/helper/delay"; import { delay } from "../../src/core/helpers/delay";
import { EXEC_TYPE } from "../../src/core/model/exec_error_model"; import { EXEC_TYPE } from "../../src/core/models/exec_error_model";
import { ExecutorResult } from "../../src/core/model/executor_result"; import { ExecutorResult } from "../../src/core/models/executor_result";
import { ExecutorProgramService } from "../../src/core/services/executor_program_service"; import { ExecutorProgramService } from "../../src/core/services/executor_program_service";
import { TestCore } from "../core/test_core"; import { TestCore } from "../core/test_core";
import { resultTest as resultTest, dirname__ } from "../test"; import { resultTest as resultTest, dirname__ } from "../test";
@ -15,12 +15,8 @@ export class ExecutorProgramServiceTest extends ExecutorProgramService {
await this.logWriteAndEventEndTypeSpawn(); await this.logWriteAndEventEndTypeSpawn();
}; };
private async logWriteAndEventEndTypeSpawn() { private async logWriteAndEventEndTypeSpawn() {
const executorProgramService = await new ExecutorProgramService( const executorProgramService = await new ExecutorProgramService(dirname__ + "/");
dirname__ + "/" executorProgramService.call(EXEC_TYPE.SPAWN, "node", ["./mocks/log_code"]);
);
executorProgramService.call(EXEC_TYPE.SPAWN, "node", [
"./mocks/log_code",
]);
const test = TestCore.instance; const test = TestCore.instance;
let testIsOk = false; let testIsOk = false;
let logEvent = false; let logEvent = false;
@ -29,24 +25,17 @@ export class ExecutorProgramServiceTest extends ExecutorProgramService {
if (e.isSuccess()) { if (e.isSuccess()) {
const executorResult = e.value as ExecutorResult; const executorResult = e.value as ExecutorResult;
if (logEvent == false) { if (logEvent == false) {
logEvent = logEvent = executorResult.data != null && executorResult.data != undefined;
executorResult.data != null && executorResult.data != undefined;
} }
testIsOk = executorResult.event == "END" && logEvent; testIsOk = executorResult.event == "END" && logEvent;
} }
}); });
await delay(8000); await delay(8000);
test.assert( test.assert(testIsOk, "ExecutorProgramService EXEC_TYPE.SPAWN end event and log write");
testIsOk,
"ExecutorProgramService EXEC_TYPE.SPAWN end event and log write"
);
} }
private async logWriteAndEventEndTestTypeExec() { private async logWriteAndEventEndTestTypeExec() {
const executorProgramService = await new ExecutorProgramService(dirname__); const executorProgramService = await new ExecutorProgramService(dirname__);
executorProgramService.call( executorProgramService.call(EXEC_TYPE.EXEC, "node ./test/mocks/log_code");
EXEC_TYPE.EXEC,
"node ./test/mocks/log_code"
);
const test = TestCore.instance; const test = TestCore.instance;
executorProgramService.on((e) => { executorProgramService.on((e) => {
if (e.isSuccess()) { if (e.isSuccess()) {
@ -61,10 +50,7 @@ export class ExecutorProgramServiceTest extends ExecutorProgramService {
} }
private async longTimeCancelTest() { private async longTimeCancelTest() {
const executorProgramService = await new ExecutorProgramService("", 1000); const executorProgramService = await new ExecutorProgramService("", 1000);
executorProgramService.call( executorProgramService.call(EXEC_TYPE.EXEC, "node ./test/mocks/long_code");
EXEC_TYPE.EXEC,
"node ./test/mocks/long_code"
);
await delay(1500); await delay(1500);
const worker = executorProgramService.worker as Worker; const worker = executorProgramService.worker as Worker;
const test = TestCore.instance; const test = TestCore.instance;

View file

@ -1,8 +1,8 @@
import * as fs from "fs"; import * as fs from "fs";
import { FilesChangeNotifierService } from "../../src/core/services/files_change_notifier_service"; import { FilesChangeNotifierService } from "../../src/core/services/files_change_notifier_service";
import { EventsFileChanger } from "../../src/core/model/meta_data_file_manager_model"; import { EventsFileChanger } from "../../src/core/models/meta_data_file_manager_model";
import { assert, dirname__ } from "../test"; import { assert, dirname__ } from "../test";
import { delay } from "../../src/core/helper/delay"; import { delay } from "../../src/core/helpers/delay";
export class FilesChangerTest extends FilesChangeNotifierService { export class FilesChangerTest extends FilesChangeNotifierService {
directory = dirname__ + "/context/"; directory = dirname__ + "/context/";
@ -26,10 +26,7 @@ export class FilesChangerTest extends FilesChangeNotifierService {
await delay(2000); await delay(2000);
fs.writeFileSync(this.filePath, this.data()); fs.writeFileSync(this.filePath, this.data());
await delay(1000); await delay(1000);
this.hashUnitEqualTo( this.hashUnitEqualTo(EventsFileChanger.create, "FilesChangeNotifierService create file");
EventsFileChanger.create,
"FilesChangeNotifierService create file"
);
this.cancel(); this.cancel();
} }
@ -39,28 +36,19 @@ export class FilesChangerTest extends FilesChangeNotifierService {
await delay(1000); await delay(1000);
fs.writeFileSync(this.filePath, this.data() + "132"); fs.writeFileSync(this.filePath, this.data() + "132");
await delay(500); await delay(500);
this.hashUnitEqualTo( this.hashUnitEqualTo(EventsFileChanger.update, "FilesChangeNotifierService update file");
EventsFileChanger.update,
"FilesChangeNotifierService update file"
);
this.cancel(); this.cancel();
} }
public async initFile() { public async initFile() {
this.init(); this.init();
await delay(500); await delay(500);
this.hashUnitEqualTo( this.hashUnitEqualTo(EventsFileChanger.static, "FilesChangeNotifierService init file");
EventsFileChanger.static,
"FilesChangeNotifierService init file"
);
} }
public async deleteFile() { public async deleteFile() {
this.call(); this.call();
fs.unlinkSync(this.filePath); fs.unlinkSync(this.filePath);
await delay(1000); await delay(1000);
this.hashUnitEqualTo( this.hashUnitEqualTo(EventsFileChanger.delete, "FilesChangeNotifierService delete file");
EventsFileChanger.delete,
"FilesChangeNotifierService delete file"
);
this.cancel(); this.cancel();
} }
public async notExistsDirectory() { public async notExistsDirectory() {

View file

@ -0,0 +1,12 @@
import { PipelineRealTimeService } from "../../src/core/services/pipeline_real_time_service";
import { mockSimplePipeline } from "../model/mock_pipelines";
import { dirname__ } from "../test";
export class PipelineRealTimeServiceTest extends PipelineRealTimeService {
constructor() {
super();
}
async test() {
// this.runPipeline(mockSimplePipeline, dirname__, "");
}
}

View file

@ -1,97 +1,21 @@
import { rmSync } from "fs"; import { rmSync } from "fs";
import * as fs from "fs";
import {
IssueType,
StackGenerateType,
} from "../../src/core/model/process_model";
import { EXEC_TYPE } from "../../src/core/model/exec_error_model";
import { StackService } from "../../src/core/services/stack_service"; import { StackService } from "../../src/core/services/stack_service";
import { delay } from "../../src/core/helper/delay"; import { delay } from "../../src/core/helpers/delay";
import { assert, dirname__ } from "../test"; import { assert, dirname__ } from "../test";
import { TriggerType } from "../../src/features/triggers/trigger_model"; import { mockSimplePipeline } from "../model/mock_pipelines";
import { readDirRecursive } from "../../src/core/repository/fs";
abstract class IStackServiceTest { abstract class IStackServiceTest {
abstract test(): Promise<boolean>; abstract test(): Promise<boolean>;
} }
export function readDirRecursive(path: string, filesToDir: string[] = []) { class SimpleTestStackServiceTest extends StackService implements IStackServiceTest {
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
{
constructor() { constructor() {
super( super(mockSimplePipeline, dirname__ + "/context/");
[
{
process: {
type: EXEC_TYPE.EXEC,
command: `nix run gitlab:robossembler/nix-robossembler-overlay#test-script '{
"filesMeta":[
{"type":"folder","name":"example", "path": null,"rewrite":true}
],
"path":"$PATH"
}'`,
isGenerating: true,
isLocaleCode: false,
issueType: IssueType.WARNING,
},
trigger: {
type: TriggerType.FILE,
value: ["context"],
},
env: null,
stackGenerateType: StackGenerateType.SINGLETON,
},
{
process: {
type: EXEC_TYPE.EXEC,
command: `nix run gitlab:robossembler/nix-robossembler-overlay#test-script '{
"filesMeta":[
{"type":"file","name":"1.txt", "path":"example","rewrite":true}
],
"path":"$PATH"
}'`,
isGenerating: true,
isLocaleCode: false,
issueType: IssueType.WARNING,
},
trigger: {
type: TriggerType.FILE,
value: ["1.txt"],
},
env: null,
stackGenerateType: StackGenerateType.SINGLETON,
},
],
dirname__ + "/context/"
);
} }
async test(): Promise<boolean> { async test(): Promise<boolean> {
await this.call(); await this.call();
const testResult = readDirRecursive(this.path).equals( const testResult = readDirRecursive(this.path).equals(["1.txt", "test.txt"], true);
["1.txt", "test.txt"],
true
);
await delay(100); await delay(100);
rmSync(this.path + "example/", { recursive: true }); rmSync(this.path + "example/", { recursive: true });
return testResult; return testResult;

View file

@ -1,10 +1,7 @@
import { import { EventsFileChanger, MetaDataFileManagerModel } from "../../src/core/models/meta_data_file_manager_model";
EventsFileChanger,
MetaDataFileManagerModel,
} from "../../src/core/model/meta_data_file_manager_model";
import { TriggerService } from "../../src/core/services/trigger_service"; import { TriggerService } from "../../src/core/services/trigger_service";
import { TriggerType } from "../../src/features/triggers/trigger_model"; import { TriggerType } from "../../src/features/triggers/models/trigger_database_model";
import { assert } from "../test"; import { assert } from "../test";
abstract class TriggerTest { abstract class TriggerTest {
abstract test(): Promise<boolean>; abstract test(): Promise<boolean>;
@ -17,11 +14,7 @@ class TriggerServiceFileOkTest extends TriggerService implements TriggerTest {
value: ["context"], value: ["context"],
}, },
{ {
"/context/": new MetaDataFileManagerModel( "/context/": new MetaDataFileManagerModel("", "", EventsFileChanger.create),
"",
"",
EventsFileChanger.create
),
}, },
"" ""
); );
@ -32,10 +25,7 @@ class TriggerServiceFileOkTest extends TriggerService implements TriggerTest {
return r.isSuccess(); return r.isSuccess();
} }
} }
class TriggerServiceFileErrorTest class TriggerServiceFileErrorTest extends TriggerService implements TriggerTest {
extends TriggerService
implements TriggerTest
{
constructor() { constructor() {
super( super(
{ {
@ -44,11 +34,7 @@ class TriggerServiceFileErrorTest
}, },
{ {
"/ctx/": new MetaDataFileManagerModel("", "", EventsFileChanger.create), "/ctx/": new MetaDataFileManagerModel("", "", EventsFileChanger.create),
"/context/": new MetaDataFileManagerModel( "/context/": new MetaDataFileManagerModel("", "", EventsFileChanger.create),
"",
"",
EventsFileChanger.create
),
}, },
"" ""
@ -60,10 +46,7 @@ class TriggerServiceFileErrorTest
return r.isFailure(); return r.isFailure();
} }
} }
class TriggerServiceProcessOkTest class TriggerServiceProcessOkTest extends TriggerService implements TriggerTest {
extends TriggerService
implements TriggerTest
{
constructor() { constructor() {
super( super(
{ {
@ -77,11 +60,7 @@ class TriggerServiceProcessOkTest
], ],
}, },
{ {
"/context/": new MetaDataFileManagerModel( "/context/": new MetaDataFileManagerModel("", "", EventsFileChanger.create),
"",
"",
EventsFileChanger.create
),
}, },
"" ""
); );
@ -92,10 +71,7 @@ class TriggerServiceProcessOkTest
} }
} }
class TriggerServiceProcessErrorTest class TriggerServiceProcessErrorTest extends TriggerService implements TriggerTest {
extends TriggerService
implements TriggerTest
{
constructor() { constructor() {
super( super(
{ {
@ -109,11 +85,7 @@ class TriggerServiceProcessErrorTest
], ],
}, },
{ {
"/context/": new MetaDataFileManagerModel( "/context/": new MetaDataFileManagerModel("", "", EventsFileChanger.create),
"",
"",
EventsFileChanger.create
),
}, },
"" ""
); );

View file

@ -1,51 +1,84 @@
import "reflect-metadata";
import { TestCore } from "./core/test_core"; import { TestCore } from "./core/test_core";
import { UnitTestEnv } from "../src/core/di/env"; // import { UnitTestEnv } from "../src/core/di/env";
import { dirname } from "path"; import { dirname } from "path";
import locator from "../src/core/di/register_di"; // import locator from "../src/core/di/register_di";
import { ExecutorProgramServiceTest } from "./services/executor_program_service_test"; import { ExecutorProgramServiceTest } from "./services/executor_program_service_test";
import { FilesChangerTest } from "./services/files_change_notifier_service_test"; import { FilesChangerTest } from "./services/files_change_notifier_service_test";
import { TriggerServiceTest } from "./services/trigger_service_test"; import { TriggerServiceTest } from "./services/trigger_service_test";
import { StackServiceTest } from "./services/stack_service_test"; import { StackServiceTest } from "./services/stack_service_test";
import mongoose from "mongoose";
import { CreateDataBaseModelUseCaseTest } from "./usecases/create_database_model_usecase_test"; import { CreateDataBaseModelUseCaseTest } from "./usecases/create_database_model_usecase_test";
import { DeleteDataBaseModelUseCaseTest } from "./usecases/delete_database_model_usecase_test"; import { DeleteDataBaseModelUseCaseTest } from "./usecases/delete_database_model_usecase_test";
import { ReadDataBaseModelUseCaseTest } from "./usecases/read_database_model_usecase_test"; import { ReadDataBaseModelUseCaseTest } from "./usecases/read_database_model_usecase_test";
import { UpdateDataBaseModelUseCaseTest } from "./usecases/update_database_model_usecase"; import { UpdateDataBaseModelUseCaseTest } from "./usecases/update_database_model_usecase";
import { PaginationDataBaseModelUseCaseTest } from "./usecases/pagination_database_model_usecase_test"; import { PaginationDataBaseModelUseCaseTest } from "./usecases/pagination_database_model_usecase_test";
import { extensions } from "../src/core/extensions/extensions";
import { CrudControllerTest } from "./controllers/crud_controller_test";
import { TriggerPresentation } from "../src/features/triggers/triggers_presentation";
import { App, Environment, ServerStatus } from "../src/core/controllers/app";
import { httpRoutes } from "../src/core/controllers/routes";
import { DataBaseConnectUseCase } from "../src/core/usecases/database_connect_usecase";
const testCore = TestCore.instance; const testCore = TestCore.instance;
export const dirname__: string = dirname(__filename); export const dirname__: string = dirname(__filename);
export const assert = testCore.assert; export const assert = testCore.assert;
export const resultTest = testCore.resultTest; export const resultTest = testCore.resultTest;
const env = new UnitTestEnv(dirname__);
locator(env); const tests = [
CreateDataBaseModelUseCaseTest,
DeleteDataBaseModelUseCaseTest,
ReadDataBaseModelUseCaseTest,
UpdateDataBaseModelUseCaseTest,
PaginationDataBaseModelUseCaseTest,
];
const init = async () => {
await new DataBaseConnectUseCase().call();
};
const tests = [CreateDataBaseModelUseCaseTest, DeleteDataBaseModelUseCaseTest,ReadDataBaseModelUseCaseTest,UpdateDataBaseModelUseCaseTest, PaginationDataBaseModelUseCaseTest] const unitTest = async () => {
const init = async () =>{ await init();
await mongoose.connect('mongodb://127.0.0.1:27017/test')
}
const test = async () =>{
await new ExecutorProgramServiceTest(dirname__).test(); await new ExecutorProgramServiceTest(dirname__).test();
await new FilesChangerTest(dirname__).test(); await new FilesChangerTest(dirname__).test();
await new StackServiceTest(dirname__ + "/context/").test(); await new StackServiceTest(dirname__ + "/context/").test();
await new TriggerServiceTest().test(); await new TriggerServiceTest().test();
await new CreateDataBaseModelUseCaseTest().test() await new CreateDataBaseModelUseCaseTest().test();
await new CreateDataBaseModelUseCaseTest().test();
await new CreateDataBaseModelUseCaseTest().test() await new DeleteDataBaseModelUseCaseTest().test();
await new DeleteDataBaseModelUseCaseTest().test() await new ReadDataBaseModelUseCaseTest().test();
await new ReadDataBaseModelUseCaseTest().test() await new UpdateDataBaseModelUseCaseTest().test();
await new UpdateDataBaseModelUseCaseTest().test() // await new PipelineRealTimeServiceTest().test()
for await (const usecase of tests) { for await (const usecase of tests) {
testCore.assert(await new usecase().test(), usecase.name) testCore.assert(await new usecase().test(), usecase.name);
} }
} };
const presentationCrudControllers = [new TriggerPresentation()];
const e2eTest = async () => {
const app = new App(httpRoutes, [], Environment.E2E_TEST);
app.listen();
await new Promise((resolve, reject) => {
app.on(async (e) => {
if (e === ServerStatus.finished) {
for await (const el of presentationCrudControllers) {
testCore.assert(await new CrudControllerTest(app.port, el).call(), el.constructor.name);
}
resolve(e);
}
if (e === ServerStatus.error) {
console.log(e);
reject(e);
}
});
});
};
const main = async () => { const main = async () => {
await init() extensions();
await test() if (process.env.NODE_ENV === "unit") {
await unitTest();
}
if (process.env.NODE_ENV === "e2e") {
await e2eTest();
}
await testCore.testResult(); await testCore.testResult();
}; };

View file

@ -5,9 +5,7 @@ export class PaginationDataBaseModelUseCaseTest {
async test() { async test() {
let testIsSuccess = false; let testIsSuccess = false;
await ( await (
await new PaginationDataBaseModelUseCase<ITestModel>(TestDBModel, 1).call( await new PaginationDataBaseModelUseCase<ITestModel>(TestDBModel, 1).call(1)
1
)
).fold( ).fold(
(s) => { (s) => {
testIsSuccess = s.length === 1; testIsSuccess = s.length === 1;

View file

@ -1,25 +1,20 @@
import { CreateDataBaseModelUseCase } from "../../src/core/usecases/create_database_model_usecase"; 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"; import { ITestModel, TestDBModel } from "../model/test_db_mongo_model";
export class ReadDataBaseModelUseCaseTest { export class ReadDataBaseModelUseCaseTest {
async test() { async test() {
let testIsSuccess = false; let testIsSuccess = false;
const result = await new CreateDataBaseModelUseCase<ITestModel>( const result = await new CreateDataBaseModelUseCase<ITestModel>(TestDBModel).call({
TestDBModel
).call({
result: "test", result: "test",
}); });
await result.fold( await result.fold(
async (s) => { async (s) => {
const r = await new ReadByIdDataBaseModelUseCase<ITestModel>( const r = await new ReadByIdDataBaseModelUseCase<ITestModel>(TestDBModel).call(s.id);
TestDBModel
).call(s.id);
await r.fold( await r.fold(
(_s1) => { (_s1) => {
testIsSuccess = true; testIsSuccess = true;
}, },
(_e) => {} (_e) => {}
); );

View file

@ -6,18 +6,14 @@ export class UpdateDataBaseModelUseCaseTest {
async test() { async test() {
let testIsSuccess = false; let testIsSuccess = false;
const model = await new CreateDataBaseModelUseCase<ITestModel>( const model = await new CreateDataBaseModelUseCase<ITestModel>(TestDBModel).call({
TestDBModel
).call({
result: "test", result: "test",
}); });
await model.fold( await model.fold(
async (s) => { async (s) => {
( (
await new UpdateDataBaseModelUseCase<any, ITestModel>( await new UpdateDataBaseModelUseCase<any, ITestModel>(TestDBModel).call({
TestDBModel _id: s.id,
).call({
_id:s.id,
result: "complete", result: "complete",
}) })
).fold( ).fold(

View file

@ -1,19 +1,19 @@
{ {
"compileOnSave": false, "compileOnSave": false,
"compilerOptions": { "compilerOptions": {
"target": "es2017", "target": "es2017",
"allowSyntheticDefaultImports": true, "allowSyntheticDefaultImports": true,
"experimentalDecorators": true, "experimentalDecorators": true,
"emitDecoratorMetadata": true, "emitDecoratorMetadata": true,
"forceConsistentCasingInFileNames": true, "forceConsistentCasingInFileNames": true,
"moduleResolution": "node", "moduleResolution": "node",
"pretty": true, "pretty": true,
"declaration": true, "declaration": true,
"outDir": "./build", "outDir": "./build",
"allowJs": true, "allowJs": true,
"noEmit": false, "noEmit": false,
"esModuleInterop": true, "esModuleInterop": true,
"resolveJsonModule": true, "resolveJsonModule": true,
"importHelpers": true, "importHelpers": true
} }
} }

25
ui/.gitignore vendored Normal file
View file

@ -0,0 +1,25 @@
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
# dependencies
/node_modules
/.pnp
.pnp.js
# testing
/coverage
# production
/build
# misc
.DS_Store
.env.local
.env.development.local
.env.test.local
.env.production.local
npm-debug.log*
yarn-debug.log*
yarn-error.log*
package-lock.json
todo.md

62
ui/package.json Normal file
View file

@ -0,0 +1,62 @@
{
"name": "ui-robossembler",
"version": "0.1.0",
"private": true,
"dependencies": {
"@monaco-editor/react": "^4.6.0",
"@testing-library/jest-dom": "^5.17.0",
"@testing-library/react": "^13.4.0",
"@testing-library/user-event": "^13.5.0",
"@types/jest": "^27.5.2",
"@types/node": "^16.18.46",
"@types/react": "^18.2.21",
"@types/react-dom": "^18.2.7",
"@types/socket.io-client": "^3.0.0",
"@types/uuid": "^9.0.2",
"formik-antd": "^2.0.4",
"i18next": "^23.6.0",
"mobx": "^6.10.0",
"mobx-react-lite": "^4.0.4",
"mobx-store-inheritance": "^1.0.6",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-i18next": "^13.3.1",
"react-infinite-scroll-component": "^6.1.0",
"react-router-dom": "^6.18.0",
"react-scripts": "5.0.1",
"sass": "^1.66.1",
"socket.io-client": "^4.7.2",
"three": "^0.159.0",
"typescript": "^4.9.5",
"urdf-loader": "^0.12.1",
"uuid": "^9.0.1",
"web-vitals": "^2.1.4"
},
"scripts": {
"dev": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test",
"eject": "react-scripts eject"
},
"eslintConfig": {
"extends": [
"react-app",
"react-app/jest"
]
},
"browserslist": {
"production": [
">0.2%",
"not dead",
"not op_mini all"
],
"development": [
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
]
},
"devDependencies": {
"@types/three": "^0.158.3"
}
}

BIN
ui/public/favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

21
ui/public/index.html Normal file
View file

@ -0,0 +1,21 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="theme-color" content="#000000" />
<meta name="description" content="Web site created using create-react-app" />
<link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
<title>robossembler: pipeline </title>
</head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
<div id="root"></div>
</body>
</html>

BIN
ui/public/logo192.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.2 KiB

Some files were not shown because too many files have changed in this diff Show more