From 6f86377685158f96ccbf4eae4a94f46502c46013 Mon Sep 17 00:00:00 2001 From: IDONTSUDO Date: Fri, 10 Nov 2023 21:43:57 +0300 Subject: [PATCH] progress --- .../src/features/pipelines/pipeline_model.ts | 12 +- server/src/main.ts | 1 + ui/src/core/extensions/array.ts | 16 +-- ui/src/core/extensions/extensions.ts | 18 ++- ui/src/core/extensions/string.ts | 8 ++ ui/src/core/model/database_model.ts | 3 + ui/src/core/model/trigger_model.ts | 7 +- ui/src/core/ui/header/header.tsx | 2 +- ui/src/core/ui/list/list.tsx | 106 ++++++++++++------ .../data/create_pipeline_repository.ts | 10 ++ .../create_pipeline/model/pipiline_model.ts | 7 +- .../presentation/create_pipeline_screen.tsx | 21 ++-- .../presentation/create_pipeline_store.ts | 99 ++++++++++------ .../create_process/model/process_model.ts | 5 +- .../presentation/logic/process_store.ts | 1 - .../create_project_repository.ts | 25 +++++ .../create_project/create_project_screen.tsx | 6 + .../create_project/create_project_store.ts | 34 ++++++ 18 files changed, 274 insertions(+), 107 deletions(-) create mode 100644 ui/src/core/extensions/string.ts create mode 100644 ui/src/core/model/database_model.ts create mode 100644 ui/src/features/create_project/create_project_repository.ts create mode 100644 ui/src/features/create_project/create_project_screen.tsx create mode 100644 ui/src/features/create_project/create_project_store.ts diff --git a/server/src/features/pipelines/pipeline_model.ts b/server/src/features/pipelines/pipeline_model.ts index 4aeaec9..f321f52 100644 --- a/server/src/features/pipelines/pipeline_model.ts +++ b/server/src/features/pipelines/pipeline_model.ts @@ -1,10 +1,7 @@ -import { IsMongoId, IsEnum } from "class-validator"; +import { IsMongoId, IsEnum, IsOptional } from "class-validator"; import { Schema, model } from "mongoose"; import { IProcess, 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"; export const PipelineSchema = new Schema({ @@ -19,7 +16,7 @@ export const PipelineSchema = new Schema({ ref: triggerSchema, autopopulate: true, default: null, - } + }, }).plugin(require("mongoose-autopopulate")); export const schemaPipeline = "Pipeline"; @@ -37,8 +34,9 @@ export class PipelineModel { //TODO(IDONTSUDO):NEED OPTION DECORATOR?? public trigger: TriggerModel; + @IsOptional() public env = null; - @IsEnum(StackGenerateType) + @IsOptional() public stackGenerateType: StackGenerateType; } diff --git a/server/src/main.ts b/server/src/main.ts index 9b0a44f..6409186 100644 --- a/server/src/main.ts +++ b/server/src/main.ts @@ -24,3 +24,4 @@ const socketSubscribers = [ ]; new App(httpRoutes, socketSubscribers).listen(); + diff --git a/ui/src/core/extensions/array.ts b/ui/src/core/extensions/array.ts index cfbac22..65ba388 100644 --- a/ui/src/core/extensions/array.ts +++ b/ui/src/core/extensions/array.ts @@ -1,13 +1,3 @@ -export {}; - -declare global { - interface Array { - // @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, strict: boolean): boolean; - lastElement(): T | undefined; - } -} - export const ArrayExtensions = () => { if ([].equals === undefined) { // eslint-disable-next-line no-extend-native @@ -42,4 +32,10 @@ export const ArrayExtensions = () => { } }; } + if ([].isEmpty === undefined) { + // eslint-disable-next-line no-extend-native + Array.prototype.isEmpty = function () { + return this.length === 0; + }; + } }; diff --git a/ui/src/core/extensions/extensions.ts b/ui/src/core/extensions/extensions.ts index e4111fd..9afea5e 100644 --- a/ui/src/core/extensions/extensions.ts +++ b/ui/src/core/extensions/extensions.ts @@ -1,6 +1,18 @@ import { ArrayExtensions } from "./array"; +import { StringExtensions } from "./string"; -export const extensions = () =>{ - ArrayExtensions() +declare global { + interface Array { + // @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, strict: boolean): boolean; + lastElement(): T | undefined; + isEmpty(): boolean; + } + interface String { + isEmpty(): boolean; + } } - \ No newline at end of file +export const extensions = () => { + ArrayExtensions(); + StringExtensions(); +}; diff --git a/ui/src/core/extensions/string.ts b/ui/src/core/extensions/string.ts new file mode 100644 index 0000000..030607e --- /dev/null +++ b/ui/src/core/extensions/string.ts @@ -0,0 +1,8 @@ +export const StringExtensions = () => { + if ("".isEmpty === undefined) { + // eslint-disable-next-line no-extend-native + String.prototype.isEmpty = function () { + return this.length === 0; + }; + } +}; diff --git a/ui/src/core/model/database_model.ts b/ui/src/core/model/database_model.ts new file mode 100644 index 0000000..d2e7f7a --- /dev/null +++ b/ui/src/core/model/database_model.ts @@ -0,0 +1,3 @@ +export interface DatabaseModel { + _id?: string; +} diff --git a/ui/src/core/model/trigger_model.ts b/ui/src/core/model/trigger_model.ts index f3411be..5ea29e5 100644 --- a/ui/src/core/model/trigger_model.ts +++ b/ui/src/core/model/trigger_model.ts @@ -1,7 +1,8 @@ -export interface ITriggerModel { - _id?: string; +import { DatabaseModel } from "./database_model"; + +export interface ITriggerModel extends DatabaseModel { type: string; - description:string; + description: string; value: string[]; } diff --git a/ui/src/core/ui/header/header.tsx b/ui/src/core/ui/header/header.tsx index 83f6e11..3312300 100644 --- a/ui/src/core/ui/header/header.tsx +++ b/ui/src/core/ui/header/header.tsx @@ -25,7 +25,7 @@ export const Header: React.FunctionComponent = (props: IHeader) => { marginTop: "20px", marginRight: "20px", display: "contents", - }} + }} > {needBackButton ? ( <> diff --git a/ui/src/core/ui/list/list.tsx b/ui/src/core/ui/list/list.tsx index 9861cdb..c60af76 100644 --- a/ui/src/core/ui/list/list.tsx +++ b/ui/src/core/ui/list/list.tsx @@ -1,50 +1,82 @@ import { Row } from "antd"; import { ReactComponent as AddIcon } from "../../assets/icons/add.svg"; -import { observer } from "mobx-react-lite"; +import { ReactComponent as DeleteIcon } from "../../assets/icons/delete.svg"; -export type CallBackFunction = (a: string) => void; +import { observer } from "mobx-react-lite"; +import { v4 } from "uuid"; + +export type CallBackFunction = (el: ListElement, index: number) => void; export interface ListElement { + id?: string; text: string; color?: string; } +export enum Icon { + add, + delete, +} export interface IPropsList { values: ListElement[]; headers?: string; onClick?: CallBackFunction; + icon: Icon; } -export const List: React.FunctionComponent = observer( - (props) => { - return ( -
- {props.headers !== undefined ? <>{props.headers} : <>} - {props.values.map((el) => { - return ( - = observer((props) => { + props.values.map((el) => { + if (el.id === undefined) { + el.id = v4(); + return el + } + return el + }); + return ( +
+ {props.headers !== undefined ? <>{props.headers} : <>} + {props.values.map((el, index) => { + return ( + +
+ +
-
- -
- {el.text} -
-
- +
+ {props.icon === Icon.add ? ( + <> + { + if (props.onClick !== undefined) { + props.onClick(el, index); + } + }} + /> + + ) : ( + = observer( }} onClick={() => { if (props.onClick !== undefined) { - props.onClick(el.text); + props.onClick(el, index); } }} /> - - ); - })} -
- ); - } -); + )} + + ); + })} +
+ ); +}); diff --git a/ui/src/features/create_pipeline/data/create_pipeline_repository.ts b/ui/src/features/create_pipeline/data/create_pipeline_repository.ts index 99e595e..2d2001d 100644 --- a/ui/src/features/create_pipeline/data/create_pipeline_repository.ts +++ b/ui/src/features/create_pipeline/data/create_pipeline_repository.ts @@ -5,8 +5,18 @@ import { import { ITriggerModel } from "../../../core/model/trigger_model"; import { Result } from "../../../core/helper/result"; import { IProcess } from "../../create_process/model/process_model"; +import { PipelineModelDataBase } from "../model/pipiline_model"; export class CreatePipelineRepository extends HttpRepository { + async savePipeline(model: PipelineModelDataBase): Promise> { + try { + return await Result.ok( + this.jsonRequest(HttpMethod.POST, `/pipeline`, model) + ); + } catch (error) { + return Result.error(error as Error); + } + } async getTriggers(page = 1): Promise> { try { return Result.ok( diff --git a/ui/src/features/create_pipeline/model/pipiline_model.ts b/ui/src/features/create_pipeline/model/pipiline_model.ts index c87f201..74352a1 100644 --- a/ui/src/features/create_pipeline/model/pipiline_model.ts +++ b/ui/src/features/create_pipeline/model/pipiline_model.ts @@ -1,3 +1,8 @@ export interface IColor { - color:string; + color: string; +} + +export interface PipelineModelDataBase { + process: string; + trigger: string; } \ No newline at end of file diff --git a/ui/src/features/create_pipeline/presentation/create_pipeline_screen.tsx b/ui/src/features/create_pipeline/presentation/create_pipeline_screen.tsx index cdfce17..74b966b 100644 --- a/ui/src/features/create_pipeline/presentation/create_pipeline_screen.tsx +++ b/ui/src/features/create_pipeline/presentation/create_pipeline_screen.tsx @@ -1,9 +1,9 @@ import * as React from "react"; -import { Row, Input, Button } from "antd"; +import { Row, Button } from "antd"; import { LoadPage } from "../../../core/ui/pages/load_page"; import { createPipelineStore } from "./create_pipeline_store"; import { observer } from "mobx-react-lite"; -import { List } from "../../../core/ui/list/list"; +import { Icon, List } from "../../../core/ui/list/list"; export const CreatePipelineScreenPath = "/create_pipeline"; @@ -20,27 +20,32 @@ export const CreatePipelineScreen: React.FunctionComponent = observer(() => { { - return { text: el.description }; + return { text: el.description, id: el._id }; })} - onClick={(e) => createPipelineStore.addProcess(e)} + onClick={(e) => createPipelineStore.addProcess(e.text, e.id!)} + icon={Icon.add} />
- { + createPipelineStore.filterPipelineViewModel(index); + }} />
{ - return { text: el.description }; + return { text: el.description, id: el._id }; })} - onClick={(e) => createPipelineStore.addTrigger(e)} + onClick={(e) => createPipelineStore.addTrigger(e.text, e.id!)} + icon={Icon.add} /> diff --git a/ui/src/features/create_pipeline/presentation/create_pipeline_store.ts b/ui/src/features/create_pipeline/presentation/create_pipeline_store.ts index 3e00f6a..71c3266 100644 --- a/ui/src/features/create_pipeline/presentation/create_pipeline_store.ts +++ b/ui/src/features/create_pipeline/presentation/create_pipeline_store.ts @@ -2,26 +2,26 @@ import { makeAutoObservable } from "mobx"; import { CreatePipelineRepository } from "../data/create_pipeline_repository"; import { ITriggerModel } from "../../../core/model/trigger_model"; import { IProcess } from "../../create_process/model/process_model"; -// TODO:()rename - -enum Direction { +import { message } from "antd"; + +enum Type { PROCESS, TRIGGER, } -interface CommonView { +export interface UnionView { text: string; color: string; - type: Direction; + type: Type; + uuid?: string; } + export class CreatePipelineStore { repository: CreatePipelineRepository; triggersModels: ITriggerModel[] = []; processModels: IProcess[] = []; - pipelineViewModel: CommonView[] = []; - + pipelineViewModels: UnionView[] = []; isLoading = false; isError = false; - page = 1; constructor(repository: CreatePipelineRepository) { this.repository = repository; @@ -29,40 +29,68 @@ export class CreatePipelineStore { this.loadTriggers(); this.loadProcess(); } - - addProcess(e: string): void { - const lastElement = this.pipelineViewModel.lastElement() - - if(lastElement !== undefined){ - if(lastElement.type !== Direction.TRIGGER){ - // need UI say - return - } - } - - this.pipelineViewModel.push({ - text: e, - color: "activeborder", - type: Direction.PROCESS, - }); + filterPipelineViewModel(index: number): void { + this.pipelineViewModels = this.pipelineViewModels.filter( + (_el, i) => i !== index + ); } + addTrigger(e: string, id: string): void { + const lastElement = this.pipelineViewModels.lastElement(); + if (this.pipelineViewModels.length === 2) { + return; + } + if (lastElement !== undefined) { + if (lastElement.type !== Type.PROCESS) { + message.error("Need process"); - addTrigger(e: string): void { - const lastElement = this.pipelineViewModel.lastElement() - - if(lastElement !== undefined){ - if(lastElement.type !== Direction.PROCESS){ - // need UI say - return + return; } } - this.pipelineViewModel.push({ + this.pipelineViewModels.push({ + uuid: id, text: e, color: "blanchedalmond", - type: Direction.TRIGGER, + type: Type.TRIGGER, }); } - createPipeline(): void {} + addProcess(e: string, id: string): void { + const lastElement = this.pipelineViewModels.lastElement(); + if (this.pipelineViewModels.length === 2) { + return; + } + if (lastElement !== undefined) { + if (lastElement.type !== Type.TRIGGER) { + message.error("Need trigger"); + return; + } + } + + this.pipelineViewModels.push({ + uuid: id, + text: e, + color: "activeborder", + type: Type.PROCESS, + }); + } + + async createPipeline(): Promise { + if (this.pipelineViewModels.isEmpty()) { + message.error("not found pipelines process"); + return; + } + const triggerId = this.pipelineViewModels.find( + (el) => el.type === Type.TRIGGER + )!.uuid as string; + const processId = this.pipelineViewModels.find( + (el) => el.type === Type.PROCESS + )!.uuid as string; + + this.repository.savePipeline({ + process: processId, + trigger: triggerId, + }); + } + async loadProcess() { this.isLoading = true; const result = await this.repository.getProcessed(); @@ -76,9 +104,10 @@ export class CreatePipelineStore { ); this.isLoading = false; } + async loadTriggers() { this.isLoading = true; - const result = await this.repository.getTriggers(this.page); + const result = await this.repository.getTriggers(1); result.fold( (s) => { this.triggersModels = s; diff --git a/ui/src/features/create_process/model/process_model.ts b/ui/src/features/create_process/model/process_model.ts index fb4be3e..f047388 100644 --- a/ui/src/features/create_process/model/process_model.ts +++ b/ui/src/features/create_process/model/process_model.ts @@ -1,4 +1,6 @@ -export interface IProcess { +import { DatabaseModel } from "../../../core/model/database_model"; + +export interface IProcess extends DatabaseModel { description: string; type: EXEC_TYPE | string; command: string; @@ -17,6 +19,7 @@ export enum IssueType { ERROR = "ERROR", } export const processModelMock: IProcess = { + _id: "", description: "", type: EXEC_TYPE.SPAWN, command: "", diff --git a/ui/src/features/create_process/presentation/logic/process_store.ts b/ui/src/features/create_process/presentation/logic/process_store.ts index d31299a..127cb56 100644 --- a/ui/src/features/create_process/presentation/logic/process_store.ts +++ b/ui/src/features/create_process/presentation/logic/process_store.ts @@ -1,5 +1,4 @@ import { makeAutoObservable } from "mobx"; -import { v4 as uuidv4 } from "uuid"; import { ProcessRepository } from "../../data/process_repostiory"; import { IProcess } from "../../model/process_model"; diff --git a/ui/src/features/create_project/create_project_repository.ts b/ui/src/features/create_project/create_project_repository.ts new file mode 100644 index 0000000..b2c4667 --- /dev/null +++ b/ui/src/features/create_project/create_project_repository.ts @@ -0,0 +1,25 @@ +import { Result } from "../../core/helper/result"; +import { DatabaseModel } from "../../core/model/database_model"; +import { ITriggerModel } from "../../core/model/trigger_model"; +import { + HttpMethod, + HttpRepository, +} from "../../core/repository/http_repository"; +import { IProcess } from "../create_process/model/process_model"; + +export interface PipelineModel extends DatabaseModel { + process: IProcess; + trigger: ITriggerModel; +} + +export class CreateProjectRepository extends HttpRepository { + async getAllPipelines(page = 1): Promise> { + try { + return Result.ok( + await this.jsonRequest(HttpMethod.GET, "/pipeline") + ); + } catch (error) { + return Result.error(error as Error); + } + } +} diff --git a/ui/src/features/create_project/create_project_screen.tsx b/ui/src/features/create_project/create_project_screen.tsx new file mode 100644 index 0000000..38d8b93 --- /dev/null +++ b/ui/src/features/create_project/create_project_screen.tsx @@ -0,0 +1,6 @@ +import * as React from "react"; +export const createProjectScreenPath = "/create_project"; + +export const CreateProjectScreen: React.FunctionComponent = () => { + return <>; +}; diff --git a/ui/src/features/create_project/create_project_store.ts b/ui/src/features/create_project/create_project_store.ts new file mode 100644 index 0000000..5a0cb73 --- /dev/null +++ b/ui/src/features/create_project/create_project_store.ts @@ -0,0 +1,34 @@ +import { makeAutoObservable } from "mobx"; +import { + CreateProjectRepository, + PipelineModel, +} from "./create_project_repository"; + +class ProcessStore { + repository: CreateProjectRepository; + isLoading = false; + isError = false; + pipelineModels?: PipelineModel[]; + + constructor(repository: CreateProjectRepository) { + this.repository = repository; + makeAutoObservable(this); + this.loadPipelines(); + } + + async loadPipelines() { + this.isLoading = true; + const result = await this.repository.getAllPipelines(); + result.fold( + (s) => { + this.pipelineModels = s; + }, + (_e) => { + this.isError = true; + } + ); + this.isLoading = false; + } +} + +export const processStore = new ProcessStore(new CreateProjectRepository());