This commit is contained in:
IDONTSUDO 2023-11-14 20:44:06 +03:00
parent 6f86377685
commit 8ecb036b1d
36 changed files with 498 additions and 212 deletions

View file

@ -16,6 +16,7 @@
"*ui.*": false "*ui.*": false
}, },
"cSpell.words": [ "cSpell.words": [
"antd",
"uuidv" "uuidv"
] ]
} }

View file

@ -49,6 +49,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,13 +1,13 @@
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 { DevEnv, UnitTestEnv } from "../di/env";
import mongoose from "mongoose"; import mongoose from "mongoose";
import { Server } from "socket.io"; import { Server } from "socket.io";
import { createServer } from "http"; import { createServer } from "http";
import { TypedEvent } from "../helper/typed_event";
import { SocketSubscriber } from "./socket_controller"; import { SocketSubscriber } from "./socket_controller";
import { dirname } from "path";
import { CreateFolderUseCase } from "../usecases/crete_folder_usecase";
import { dirIsExists } from "../repository/fs";
export class App { export class App {
public app: express.Application; public app: express.Application;
@ -59,6 +59,7 @@ 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"));
} }
private initializeRoutes(routes: Routes[]) { private initializeRoutes(routes: Routes[]) {
@ -72,6 +73,7 @@ export class App {
// ? new DevEnv(this.computedFolder) // ? new DevEnv(this.computedFolder)
// : new UnitTestEnv(this.computedFolder) // : new UnitTestEnv(this.computedFolder)
// ); // );
await this.appStartController();
mongoose mongoose
.connect("mongodb://127.0.0.1:27017/test") .connect("mongodb://127.0.0.1:27017/test")
@ -80,4 +82,27 @@ export class App {
console.log("ERROR:", e); console.log("ERROR:", e);
}); });
} }
async appStartController() {
console.log(App.staticFilesStoreDir());
if (await dirIsExists(App.staticFilesStoreDir())) {
return;
}
const createFolderUseCase = await new CreateFolderUseCase().call(
App.staticFilesStoreDir()
);
createFolderUseCase.fold(
(_s) => {},
(e) => {
// TODO:(IDONTSUDO) need logger
console.log(e);
}
);
}
static staticFilesStoreDir = () => {
const dir = dirname(__filename);
const rootDir = dir.slice(0, dir.length - 20);
return rootDir + "public/";
};
} }

View file

@ -18,17 +18,25 @@ export class CrudController<V, D> extends CoreHttpController<V> {
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>(
).call; this.dataBaseModel
this.routes["GET"] = new PaginationDataBaseModelUseCase<D>( ).call;
this.dataBaseModel }
).call; if (this.routes["GET"] === null) {
this.routes["DELETE"] = new DeleteDataBaseModelUseCase<D>( this.routes["GET"] = new PaginationDataBaseModelUseCase<D>(
this.dataBaseModel this.dataBaseModel
).call; ).call;
this.routes["PUT"] = new UpdateDataBaseModelUseCase<V, D>( }
this.dataBaseModel if (this.routes["DELETE"] === null) {
).call; this.routes["DELETE"] = new DeleteDataBaseModelUseCase<D>(
this.dataBaseModel
).call;
}
if (this.routes["PUT"] === null) {
this.routes["PUT"] = new UpdateDataBaseModelUseCase<V, D>(
this.dataBaseModel
).call;
}
} }
} }

View file

@ -68,7 +68,7 @@ export class CoreHttpController<V> implements ICoreHttpController {
public delete(usecase: CallBackFunction<V>) { public delete(usecase: CallBackFunction<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: CallBackFunction<T>

View file

@ -0,0 +1,29 @@
import * as fs from "fs";
import { promisify } from "node:util";
export const readFileAsync = promisify(fs.readFile);
export const readdir = promisify(fs.readdir);
export const stat = promisify(fs.stat);
export const lsStat = promisify(fs.lstat);
export const createDir = promisify(fs.mkdir);
export const dirIsExists = promisify(fs.exists);
export function readDirRecursive(path: string, filesToDir: string[] = []) {
const files = fs.readdirSync(path);
files.forEach((file) => {
let filePath = "";
if (path[path.length - 1] !== "/") {
filePath = `${path}/${file}`;
} else {
filePath = `${path}${file}`;
}
const stats = fs.statSync(filePath);
if (stats.isDirectory()) {
readDirRecursive(filePath, filesToDir);
} else {
filesToDir.push(file);
}
});
return filesToDir;
}

View file

@ -1,6 +1,6 @@
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 "reflect-metadata";
import { BinaryLike } from "crypto"; import { BinaryLike } from "crypto";
@ -10,11 +10,9 @@ import {
} from "../model/meta_data_file_manager_model"; } from "../model/meta_data_file_manager_model";
import { Result } from "../helper/result"; import { Result } from "../helper/result";
import { TypedEvent } from "../helper/typed_event"; import { TypedEvent } from "../helper/typed_event";
import { lsStat, readFileAsync, readdir, stat } from "../repository/fs";
const readFileAsync = promisify(fs.readFile);
const readdir = promisify(fs.readdir);
const stat = promisify(fs.stat);
const lsStat = promisify(fs.lstat);
function joinBuffers(buffers: Array<Buffer>, delimiter = " ") { function joinBuffers(buffers: Array<Buffer>, delimiter = " ") {
const d = Buffer.from(delimiter); const d = Buffer.from(delimiter);
@ -34,6 +32,7 @@ async function readFileAtBuffer(path: string): Promise<Buffer> {
} }
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 +46,8 @@ 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

View file

@ -1,11 +1,10 @@
import { TypedEvent } from "../helper/typed_event"; import { TypedEvent } from "../helper/typed_event";
import { ExecError } from "../model/exec_error_model"; import { ExecError } from "../model/exec_error_model";
import { ExecutorResult } from "../model/executor_result"; import { ExecutorResult } from "../model/executor_result";
import { IPipelineMeta } from "../model/pipiline_meta"; import { IPipelineMeta } from "../model/pipeline_meta";
import { IPipeline } from "../model/process_model"; import { IPipeline } from "../model/process_model";
import { Iteration, StackService } from "./stack_service"; import { Iteration, StackService } from "./stack_service";
export class PipelineRealTimeService extends TypedEvent<IPipelineMeta> { export class PipelineRealTimeService extends TypedEvent<IPipelineMeta> {
status: IPipelineMeta; status: IPipelineMeta;
pipelineModels?: IPipeline[]; pipelineModels?: IPipeline[];
@ -13,7 +12,7 @@ export class PipelineRealTimeService extends TypedEvent<IPipelineMeta> {
super(); super();
this.init(); this.init();
} }
init() { private init(): void {
this.status = { this.status = {
pipelineIsRunning: false, pipelineIsRunning: false,
projectUUID: null, projectUUID: null,

View file

@ -0,0 +1,17 @@
import { Result } from "../helper/result";
import { dirIsExists, createDir } from "../repository/fs";
export class CreateFolderUseCase {
call = async (path: string): Promise<Result<Error, string>> => {
try {
if (await dirIsExists(path)) {
return Result.error(new Error("createFolderUseCase create dir "));
}
await createDir(path);
return Result.ok("ok");
} catch (error) {
return Result.error(error as Error);
}
};
}

View file

@ -0,0 +1,33 @@
import { App } from "../../core/controllers/app";
import { Result } from "../../core/helper/result";
import { CreateDataBaseModelUseCase } from "../../core/usecases/create_database_model_usecase";
import { CreateFolderUseCase } from "../../core/usecases/crete_folder_usecase";
import { ProjectDBModel, ProjectValidationModel } from "./projects_model";
import { v4 as uuidv4 } from "uuid";
export class CreateNewProjectScenario {
call = async (model: ProjectValidationModel): Promise<Result<Error, any>> => {
try {
const folderName = uuidv4() + "/";
const createFolderUseCase = await new CreateFolderUseCase().call(
App.staticFilesStoreDir() + folderName
);
if (createFolderUseCase.isFailure()) {
return createFolderUseCase.forward();
}
model.rootDir = folderName;
const createDataBaseModelUseCase = await new CreateDataBaseModelUseCase(
ProjectDBModel
).call(model);
if (createDataBaseModelUseCase.isFailure()) {
return createDataBaseModelUseCase.forward();
}
return Result.ok({ status: "ok" });
} catch (error) {
return Result.error(error as Error);
}
};
}

View file

@ -1,8 +1,9 @@
import { Schema, model } from "mongoose"; import { Schema, model } from "mongoose";
import { PipelineModel, schemaPipeline } from "../pipelines/pipeline_model"; import { PipelineModel, schemaPipeline } from "../pipelines/pipeline_model";
import { IsMongoId, IsString } from "class-validator"; import { IsArray, IsOptional, IsString } from "class-validator";
export interface IProjectModel { export interface IProjectModel {
_id: string;
pipelines: [PipelineModel]; pipelines: [PipelineModel];
rootDir: string; rootDir: string;
description: string; description: string;
@ -27,13 +28,11 @@ const schema = "Projects";
export const ProjectDBModel = model<IProjectModel>(schema, ProjectSchema); export const ProjectDBModel = model<IProjectModel>(schema, ProjectSchema);
export class ProjectModel implements IProjectModel { export class ProjectValidationModel {
@IsMongoId() @IsArray()
public pipelines: [PipelineModel]; public pipelines: [string];
@IsString()
public rootDir: string;
@IsString() @IsString()
public description: string; public description: string;
@IsOptional()
public rootDir: string;
} }

View file

@ -1,16 +1,17 @@
// 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 { CreateNewProjectScenario } from "./create_new_project_scenario";
import { ProjectDBModel, ProjectValidationModel } from "./projects_model";
export class ProjectsPresentation extends CrudController< export class ProjectsPresentation extends CrudController<
ProjectModel, ProjectValidationModel,
typeof ProjectDBModel typeof ProjectDBModel
> { > {
constructor() { constructor() {
super({ super({
url: "project", url: "project",
validationModel: ProjectModel, validationModel: ProjectValidationModel,
databaseModel: ProjectDBModel, databaseModel: ProjectDBModel,
}); });
super.post(new CreateNewProjectScenario().call);
} }
} }

View file

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

View file

@ -0,0 +1,13 @@
import { Result } from "../../../core/helper/result";
import { IPipelineMeta } from "../../../core/model/pipeline_meta";
import { pipelineRealTimeService } from "../realtime_presentation";
export class PipelineStatusUseCase {
async call(): Promise<Result<Error, IPipelineMeta>> {
try {
return Result.ok(pipelineRealTimeService.status);
} catch (error) {
return Result.error(error as Error);
}
}
}

View file

@ -0,0 +1,30 @@
import { Result } from "../../../core/helper/result";
import { ReadByIdDataBaseModelUseCase } from "../../../core/usecases/read_by_id_database_model_usecase";
import { IProjectModel, ProjectDBModel } from "../../projects/projects_model";
import {
RealTimeValidationModel,
pipelineRealTimeService,
} from "../realtime_presentation";
export class RunInstancePipelineUseCase {
async call(model: RealTimeValidationModel): Promise<Result<Error, any>> {
const id = model.id;
const readByIdDataBaseModelUseCase =
await new ReadByIdDataBaseModelUseCase<IProjectModel>(
ProjectDBModel
).call(id);
if (readByIdDataBaseModelUseCase.isFailure()) {
return readByIdDataBaseModelUseCase.forward();
}
const projectModel = readByIdDataBaseModelUseCase.value;
pipelineRealTimeService.runPipeline(
projectModel.pipelines,
projectModel.rootDir,
projectModel._id
);
return Result.ok({ status: "ok" });
}
}

View file

@ -1,11 +1,11 @@
import "reflect-metadata"; import "reflect-metadata";
import { App } from "./core/controllers/app"; import { App } from "./core/controllers/app";
import { SocketSubscriber } from "./core/controllers/socket_controller";
import { Routes } from "./core/interfaces/router"; import { Routes } from "./core/interfaces/router";
import { TriggerPresentation } from "./features/triggers/triggers_presentation"; import { TriggerPresentation } from "./features/triggers/triggers_presentation";
import { ProjectsPresentation } from "./features/projects/projects_presentation"; import { ProjectsPresentation } from "./features/projects/projects_presentation";
import { PipelinePresentation } from "./features/pipelines/pipeline_presentation"; import { PipelinePresentation } from "./features/pipelines/pipeline_presentation";
import { ProcessPresentation } from "./features/process/process_presentation"; import { ProcessPresentation } from "./features/process/process_presentation";
import { SocketSubscriber } from "./core/controllers/socket_controller";
import { import {
RealTimePresentation, RealTimePresentation,
pipelineRealTimeService, pipelineRealTimeService,

View file

@ -5,7 +5,6 @@ import { dirname__ } from "../test";
export class PipelineRealTimeServiceTest extends PipelineRealTimeService { export class PipelineRealTimeServiceTest extends PipelineRealTimeService {
constructor() { constructor() {
super(); super();
this.init();
} }
async test() { async test() {
this.runPipeline(mockSimplePipeline, dirname__, ""); this.runPipeline(mockSimplePipeline, dirname__, "");

View file

@ -1,33 +1,14 @@
import { rmSync } from "fs"; import { rmSync } from "fs";
import * as fs from "fs";
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/helper/delay";
import { assert, dirname__ } from "../test"; import { assert, dirname__ } from "../test";
import { mockSimplePipeline } from "../model/mock_pipelines"; 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[] = []) {
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 class SimpleTestStackServiceTest
extends StackService extends StackService

View file

@ -14,7 +14,6 @@ import { UpdateDataBaseModelUseCaseTest } from "./usecases/update_database_model
import { PaginationDataBaseModelUseCaseTest } from "./usecases/pagination_database_model_usecase_test"; import { PaginationDataBaseModelUseCaseTest } from "./usecases/pagination_database_model_usecase_test";
// import { PipelineRealTimeServiceTest } from "./services/pipeline_real_time_service_test"; // import { PipelineRealTimeServiceTest } from "./services/pipeline_real_time_service_test";
const testCore = TestCore.instance; const testCore = TestCore.instance;
export const dirname__: string = dirname(__filename); export const dirname__: string = dirname(__filename);
@ -24,30 +23,36 @@ const env = new UnitTestEnv(dirname__);
locator(env); locator(env);
const tests = [CreateDataBaseModelUseCaseTest, DeleteDataBaseModelUseCaseTest,ReadDataBaseModelUseCaseTest,UpdateDataBaseModelUseCaseTest, PaginationDataBaseModelUseCaseTest] const tests = [
const init = async () =>{ CreateDataBaseModelUseCaseTest,
await mongoose.connect('mongodb://127.0.0.1:27017/test') DeleteDataBaseModelUseCaseTest,
} ReadDataBaseModelUseCaseTest,
UpdateDataBaseModelUseCaseTest,
PaginationDataBaseModelUseCaseTest,
];
const init = async () => {
await mongoose.connect("mongodb://127.0.0.1:27017/test");
};
const test = async () =>{ 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() // 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 main = async () => { const main = async () => {
await init() await init();
await test() await test();
await testCore.testResult(); await testCore.testResult();
}; };

View file

@ -1,5 +1,5 @@
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 {

View file

@ -1,32 +1,64 @@
import { Result } from "../helper/result";
export enum HttpMethod { export enum HttpMethod {
GET = 'GET', GET = "GET",
POST = 'POST' POST = "POST",
}
export class HttpError extends Error {
status: number;
error: any;
constructor(error: any, status: number) {
super(error);
this.error = error;
this.status = status;
}
} }
export class HttpRepository { export class HttpRepository {
private server = "http://localhost:4001";
private server = 'http://localhost:4001' public async jsonRequest<T>(
method: HttpMethod,
url: string,
data?: any
): Promise<Result<HttpError, T>> {
try {
const reqInit = {
body: data,
method: method,
headers: { "Content-Type": "application/json" },
};
if (data !== undefined) {
reqInit["body"] = JSON.stringify(data);
}
const response = await fetch(this.server + url, reqInit);
public async jsonRequest<T>(method: HttpMethod, url: string, data?: any): Promise<T> { if (response.status !== 200) {
const reqInit = { return Result.error(new HttpError(this.server + url, response.status));
'body': data, }
'method': method,
'headers': { 'Content-Type': 'application/json' }, return Result.ok(await response.json());
} } catch (error) {
if (data !== undefined) { return Result.error(new HttpError(error, 0));
reqInit['body'] = JSON.stringify(data)
}
return (await fetch(this.server + url, reqInit)).json()
} }
}
public async request<T>(method: HttpMethod, url: string, data?: any): Promise<T> { public async request<T>(
const reqInit = { method: HttpMethod,
'body': data, url: string,
'method': method, data?: any
} ): Promise<T> {
if (data !== undefined) { const reqInit = {
reqInit['body'] = data body: data,
} method: method,
return (await fetch(this.server + url, reqInit)).json() };
if (data !== undefined) {
reqInit["body"] = data;
} }
const response = await fetch(this.server + url, reqInit);
if (response.status !== 200) {
throw new Error(await response.json());
}
return response.json();
}
} }

View file

@ -7,7 +7,7 @@ export class SocketRepository {
const socket = io(this.serverURL); const socket = io(this.serverURL);
this.socket = socket; this.socket = socket;
socket.connect(); socket.connect();
socket.on('mock', (d) =>{ socket.on('realtime', (d) =>{
console.log(d) console.log(d)
}) })
} }

View file

@ -15,6 +15,7 @@ import {
CreatePipelineScreen, CreatePipelineScreen,
CreatePipelineScreenPath, CreatePipelineScreenPath,
} from "../../features/create_pipeline/presentation/create_pipeline_screen"; } from "../../features/create_pipeline/presentation/create_pipeline_screen";
import { CreateProjectScreen, CreateProjectScreenPath } from "../../features/create_project/create_project_screen";
export const router = createBrowserRouter([ export const router = createBrowserRouter([
{ {
@ -33,4 +34,8 @@ export const router = createBrowserRouter([
path: CreatePipelineScreenPath, path: CreatePipelineScreenPath,
element: <CreatePipelineScreen />, element: <CreatePipelineScreen />,
}, },
{
path: CreateProjectScreenPath,
element: <CreateProjectScreen />,
},
]); ]);

View file

@ -12,38 +12,39 @@ interface ILoadPage extends IHeader {
children?: JSX.Element | JSX.Element[]; children?: JSX.Element | JSX.Element[];
} }
export const LoadPage: React.FunctionComponent<ILoadPage> = observer(( export const LoadPage: React.FunctionComponent<ILoadPage> = observer(
props: ILoadPage (props: ILoadPage) => {
) => { return (
return ( <>
<> <Header
<Header path={props.path}
path={props.path} largeText={props.largeText}
largeText={props.largeText} minText={props.minText}
minText={props.minText} />
/> {props.isError ? (
{props.isError ? ( <>
<> <ErrorIcon
<ErrorIcon style={{
style={{ height: "100px",
height: "100px", width: "-webkit-fill-available",
width: "-webkit-fill-available", }}
}} />
/> <Title style={{ textAlign: "center" }} level={3} type="danger">
<Title style={{ textAlign: "center" }} level={3} type="danger"> not expected error
not expected error </Title>
</Title> </>
</> ) : (
) : ( <>
<></> {props.isLoading ? (
)} <div style={{ marginTop: "50px" }}>
{props.isLoading ? ( <Loader />
<div style={{'marginTop':'50px'}}> </div>
<Loader /> ) : (
</div> <>{props.children}</>
) : ( )}
<>{props.children}</> </>
)} )}
</> </>
); );
}); }
);

View file

@ -9,8 +9,8 @@ export const AllProjectScreen: React.FunctionComponent = () => {
<> <>
<Header <Header
path={SelectProjectScreenPath} path={SelectProjectScreenPath}
largeText={"All Projects"} largeText={"Projects"}
minText={"create new pipiline?"} minText={"select instance project?"}
needBackButton={true} needBackButton={true}
/> />
</> </>

View file

@ -5,35 +5,22 @@ import {
import { ITriggerModel } from "../../../core/model/trigger_model"; import { ITriggerModel } from "../../../core/model/trigger_model";
import { Result } from "../../../core/helper/result"; import { Result } from "../../../core/helper/result";
import { IProcess } from "../../create_process/model/process_model"; import { IProcess } from "../../create_process/model/process_model";
import { PipelineModelDataBase } from "../model/pipiline_model"; import { PipelineModelDataBase } from "../model/pipeline_model";
export class CreatePipelineRepository extends HttpRepository { export class CreatePipelineRepository extends HttpRepository {
async savePipeline(model: PipelineModelDataBase): Promise<Result<Error, any>> { async savePipeline(
try { model: PipelineModelDataBase
return await Result.ok( ): Promise<Result<Error, any>> {
this.jsonRequest(HttpMethod.POST, `/pipeline`, model) return await this.jsonRequest(HttpMethod.POST, `/pipeline`, model);
);
} catch (error) {
return Result.error(error as Error);
}
} }
async getTriggers(page = 1): Promise<Result<Error, ITriggerModel[]>> { async getTriggers(page = 1): Promise<Result<Error, ITriggerModel[]>> {
try { return await this.jsonRequest(HttpMethod.GET, `/trigger?${page}`);
return Result.ok(
await this.jsonRequest(HttpMethod.GET, `/trigger?${page}`)
);
} catch (error) {
return Result.error(error as Error);
}
} }
async getProcessed(page = 1): Promise<Result<Error, IProcess[]>> { async getProcessed(page = 1): Promise<Result<Error, IProcess[]>> {
try { return await this.jsonRequest<IProcess[]>(
return Result.ok( HttpMethod.GET,
await this.jsonRequest<IProcess[]>(HttpMethod.GET, `/process?${page}`) `/process?${page}`
); );
} catch (error) {
return Result.error(error as Error);
}
} }
} }

View file

@ -6,6 +6,7 @@ import {
HttpRepository, HttpRepository,
} from "../../core/repository/http_repository"; } from "../../core/repository/http_repository";
import { IProcess } from "../create_process/model/process_model"; import { IProcess } from "../create_process/model/process_model";
import { ICreateProjectViewModel } from "./project_model";
export interface PipelineModel extends DatabaseModel { export interface PipelineModel extends DatabaseModel {
process: IProcess; process: IProcess;
@ -14,12 +15,11 @@ export interface PipelineModel extends DatabaseModel {
export class CreateProjectRepository extends HttpRepository { export class CreateProjectRepository extends HttpRepository {
async getAllPipelines(page = 1): Promise<Result<Error, PipelineModel[]>> { async getAllPipelines(page = 1): Promise<Result<Error, PipelineModel[]>> {
try { return await this.jsonRequest<PipelineModel[]>(HttpMethod.GET, "/pipeline");
return Result.ok( }
await this.jsonRequest<PipelineModel[]>(HttpMethod.GET, "/pipeline") async saveProject(
); model: ICreateProjectViewModel
} catch (error) { ): Promise<Result<Error, void>> {
return Result.error(error as Error); return await this.jsonRequest<void>(HttpMethod.POST, "/project", model);
}
} }
} }

View file

@ -1,6 +1,89 @@
import * as React from "react"; import * as React from "react";
export const createProjectScreenPath = "/create_project"; import { LoadPage } from "../../core/ui/pages/load_page";
import { createProjectStore } from "./create_project_store";
import { observer } from "mobx-react-lite";
import { Col, Row, Input, Button } from "antd";
import { ReactComponent as AddIcon } from "../../core/assets/icons/add.svg";
import { CreatePipelineScreenPath } from "../create_pipeline/presentation/create_pipeline_screen";
export const CreateProjectScreen: React.FunctionComponent = () => { export const CreateProjectScreenPath = "/create_project";
return <></>;
}; export const CreateProjectScreen: React.FunctionComponent = observer(() => {
return (
<>
<LoadPage
path={CreatePipelineScreenPath}
largeText={"Create project"}
minText={"add new pipelines?"}
isLoading={createProjectStore.isLoading}
isError={createProjectStore.isError}
children={
<Row>
<Col>
<>Pipelines</>
{createProjectStore.pipelineModels?.map((el) => {
return (
<div
style={{
backgroundColor: "ActiveBorder",
margin: "10px",
width: "400px",
}}
>
<div>{el.process.description}</div>
<div>{el.trigger.description}</div>
<AddIcon
style={{
width: "50px",
cursor: "pointer",
height: "50px",
marginRight: "40px",
}}
onClick={() => {
createProjectStore.addPipeline(el);
}}
/>
</div>
);
})}
</Col>
<Col>
<Row>
<Input
style={{ width: "250px" }}
onChange={(e) =>
createProjectStore.setDescriptionToNewProject(
e.target.value
)
}
placeholder="project description"
/>
<Button onClick={() => createProjectStore.saveProject()}>
save
</Button>
</Row>
{createProjectStore.newProjectViews.map((el, index) => {
return (
<div
style={{
backgroundColor: "ActiveBorder",
margin: "10px",
width: "400px",
}}
>
<div>{el.process.description}</div>
<div>{el.trigger.description}</div>
<div>{index + 1}</div>
</div>
);
})}
</Col>
</Row>
}
/>
</>
);
});

View file

@ -3,12 +3,15 @@ import {
CreateProjectRepository, CreateProjectRepository,
PipelineModel, PipelineModel,
} from "./create_project_repository"; } from "./create_project_repository";
import { message } from "antd";
class ProcessStore { class CreateProjectStore {
repository: CreateProjectRepository; repository: CreateProjectRepository;
isLoading = false; isLoading = false;
isError = false; isError = false;
pipelineModels?: PipelineModel[]; pipelineModels?: PipelineModel[];
newProjectDescription: string = "";
newProjectViews: PipelineModel[] = [];
constructor(repository: CreateProjectRepository) { constructor(repository: CreateProjectRepository) {
this.repository = repository; this.repository = repository;
@ -16,6 +19,10 @@ class ProcessStore {
this.loadPipelines(); this.loadPipelines();
} }
async addPipeline(model: PipelineModel) {
this.newProjectViews.push(model);
}
async loadPipelines() { async loadPipelines() {
this.isLoading = true; this.isLoading = true;
const result = await this.repository.getAllPipelines(); const result = await this.repository.getAllPipelines();
@ -29,6 +36,42 @@ class ProcessStore {
); );
this.isLoading = false; this.isLoading = false;
} }
setDescriptionToNewProject(value: string): void {
this.newProjectDescription = value;
}
async saveProject(): Promise<void> {
if (this.newProjectDescription.isEmpty()) {
message.error("project description is not empty");
return;
}
if (this.newProjectViews.isEmpty()) {
message.error("project view is not empty");
return;
}
this.isLoading = true;
const result = await this.repository.saveProject({
description: this.newProjectDescription,
pipelines: this.newProjectViews.map((el) => el._id ?? ""),
});
this.newProjectDescription = "";
this.newProjectViews = [];
this.isLoading = false;
result.fold(
(_s) => {
message.success("save");
},
(_e) => {
this.isError = true;
}
);
}
} }
export const processStore = new ProcessStore(new CreateProjectRepository()); export const createProjectStore = new CreateProjectStore(
new CreateProjectRepository()
);

View file

@ -0,0 +1,5 @@
export interface ICreateProjectViewModel {
pipelines: string[];
description: string;
}

View file

@ -7,12 +7,6 @@ import { IProjectModel } from "../model/project_model";
export class SelectProjectRepository extends HttpRepository { export class SelectProjectRepository extends HttpRepository {
async getAllProjects(page = 1): Promise<Result<Error, IProjectModel[]>> { async getAllProjects(page = 1): Promise<Result<Error, IProjectModel[]>> {
try { return await this.jsonRequest(HttpMethod.GET, `/project?${page}`);
return Result.ok(
await this.jsonRequest(HttpMethod.GET, `/project?${page}`)
);
} catch (error) {
return Result.error(error as Error);
}
} }
} }

View file

@ -1,10 +1,8 @@
import * as React from "react"; import * as React from "react";
import { selectProjectStore } from "./select_project_store"; import { selectProjectStore } from "./select_project_store";
import { Loader } from "../../../core/ui/loader/loader";
import { observer } from "mobx-react-lite"; import { observer } from "mobx-react-lite";
import { Header } from "../../../core/ui/header/header";
import { CreatePipelineScreenPath } from "../../create_pipeline/presentation/create_pipeline_screen";
import { LoadPage } from "../../../core/ui/pages/load_page"; import { LoadPage } from "../../../core/ui/pages/load_page";
import { CreateProjectScreenPath } from "../../create_project/create_project_screen";
export const SelectProjectScreenPath = "/select_project"; export const SelectProjectScreenPath = "/select_project";
@ -12,7 +10,7 @@ export const SelectProjectScreen: React.FunctionComponent = observer(() => {
return ( return (
<> <>
<LoadPage <LoadPage
path={CreatePipelineScreenPath} path={CreateProjectScreenPath}
largeText={"Select project"} largeText={"Select project"}
minText={"add new project?"} minText={"add new project?"}
isLoading={selectProjectStore.isLoading} isLoading={selectProjectStore.isLoading}

View file

@ -8,7 +8,9 @@ class SocketListerStore {
constructor(repository: SocketRepository) { constructor(repository: SocketRepository) {
this.repository = repository; this.repository = repository;
makeAutoObservable(this); makeAutoObservable(this);
repository.connect()
} }
async reconnect() { async reconnect() {
await this.repository.connect() await this.repository.connect()
this.socketDisconnect = false this.socketDisconnect = false