mvp progress
31
.vscode/settings.json
vendored
|
@ -5,28 +5,15 @@
|
|||
"**/.hg": false,
|
||||
"**/CVS": false,
|
||||
"**/__pycache__": false,
|
||||
"ui/*README": false,
|
||||
"ui/*README.*": false,
|
||||
"ui/*config": false,
|
||||
"ui/*config.*": false,
|
||||
"ui/*node_modules": false,
|
||||
"ui/*node_modules.*": false,
|
||||
"ui/*package-lock": false,
|
||||
"ui/*package-lock.*": false,
|
||||
"ui/*package": false,
|
||||
"ui/*package.*": false,
|
||||
"ui/*public": false,
|
||||
"ui/*public.*": false,
|
||||
"ui/*scripts.*": false,
|
||||
"ui/*src": false,
|
||||
"ui/*src.*": false,
|
||||
"ui/*tsconfig": false,
|
||||
"ui/*tsconfig.*": false,
|
||||
"ui/*yarn": false,
|
||||
"ui/*yarn.*": false,
|
||||
"**/*.map": true,
|
||||
"**/*.js": true,
|
||||
".git": true
|
||||
"*.DS_Store": false,
|
||||
"*.DS_Store.*": false,
|
||||
"*.git": false,
|
||||
"*.git.*": false,
|
||||
"*.vscode": false,
|
||||
"*.vscode.*": false,
|
||||
"*server.*": false,
|
||||
"*ui": false,
|
||||
"*ui.*": false
|
||||
},
|
||||
"cSpell.words": [
|
||||
"uuidv"
|
||||
|
|
|
@ -4,20 +4,23 @@ import cors from "cors";
|
|||
import locator from "../di/register_di";
|
||||
import { DevEnv, UnitTestEnv } from "../di/env";
|
||||
import mongoose from "mongoose";
|
||||
// import http from "http";
|
||||
// import { Server } from "socket.io";
|
||||
import { Server } from "socket.io";
|
||||
import { createServer } from "http";
|
||||
import { TypedEvent } from "../helper/typed_event";
|
||||
import { SocketSubscriber } from "./socket_controller";
|
||||
|
||||
export class App {
|
||||
public app: express.Application;
|
||||
public port: number;
|
||||
public env: string;
|
||||
public computedFolder: string;
|
||||
// public io:
|
||||
constructor(routes: Routes[], computedFolder: string) {
|
||||
this.port = 3000;
|
||||
public socketSubscribers: SocketSubscriber<any>[];
|
||||
public io: Server;
|
||||
|
||||
constructor(routes: Routes[], socketSubscribers: SocketSubscriber<any>[]) {
|
||||
this.port = 4001;
|
||||
this.socketSubscribers = socketSubscribers;
|
||||
this.env = "dev";
|
||||
this.app = express();
|
||||
this.computedFolder = computedFolder;
|
||||
this.loadAppDependencies().then(() => {
|
||||
this.initializeMiddlewares();
|
||||
this.initializeRoutes(routes);
|
||||
|
@ -25,26 +28,27 @@ export class App {
|
|||
}
|
||||
|
||||
public listen() {
|
||||
// const httpServer = new http.Server();
|
||||
// const io = new Server(httpServer);
|
||||
const httpServer = createServer(this.app);
|
||||
const io = new Server(httpServer, {
|
||||
cors: { origin: "*" },
|
||||
});
|
||||
|
||||
this.app.listen(this.port, () => {
|
||||
io.on("connection", (socket) => {
|
||||
this.socketSubscribers.map((el) => {
|
||||
el.emitter.on((e) => {
|
||||
socket.emit(el.event, e);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
httpServer.listen(this.port, () => {
|
||||
console.info(`=================================`);
|
||||
console.info(`======= ENV: ${this.env} =======`);
|
||||
console.info(`🚀 HTTP http://localhost:${this.port}`);
|
||||
console.info(`🚀 WS ws://localhost:${this.port}`);
|
||||
console.info(`=================================`);
|
||||
});
|
||||
// io.on("connection", (socket) => {
|
||||
// socket.on("disconnect", function (msg) {
|
||||
// console.log("Disconnected");
|
||||
// });
|
||||
// });
|
||||
|
||||
// setInterval(function () {
|
||||
// io.emit("goodbye");
|
||||
// console.log(200);
|
||||
// }, 1000);
|
||||
this.io = io;
|
||||
}
|
||||
|
||||
public getServer() {
|
||||
|
@ -63,20 +67,17 @@ export class App {
|
|||
});
|
||||
}
|
||||
async loadAppDependencies() {
|
||||
await locator(
|
||||
this.env == "development"
|
||||
? new DevEnv(this.computedFolder)
|
||||
: new UnitTestEnv(this.computedFolder)
|
||||
);
|
||||
// await locator(
|
||||
// this.env == "development"
|
||||
// ? new DevEnv(this.computedFolder)
|
||||
// : new UnitTestEnv(this.computedFolder)
|
||||
// );
|
||||
|
||||
mongoose
|
||||
.connect("mongodb://127.0.0.1:27017/test")
|
||||
.then(() => console.log("Connected!"))
|
||||
.then(() => {})
|
||||
.catch((e) => {
|
||||
console.log("ERROR:", e);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -1,15 +1,11 @@
|
|||
// import path from "path";
|
||||
// import { TypedEvent } from "../helper/typed_event";
|
||||
// import { StackService } from "../services/stack_service";
|
||||
// // TODO(IDONTSUDO): up to do
|
||||
import { TypedEvent } from "../helper/typed_event";
|
||||
|
||||
// class SocketController<T>{
|
||||
// emitter:TypedEvent<T>;
|
||||
// constructor(emitter:TypedEvent<T>, ){
|
||||
// this.emitter = emitter
|
||||
// }
|
||||
// call = () =>{
|
||||
|
||||
// }
|
||||
// }
|
||||
export class SocketSubscriber<T> {
|
||||
emitter: TypedEvent<T>;
|
||||
event: string;
|
||||
constructor(emitter: TypedEvent<T>, event: string) {
|
||||
this.emitter = emitter;
|
||||
this.event = event;
|
||||
}
|
||||
}
|
||||
|
|
@ -17,7 +17,6 @@ export const validationModelMiddleware = (
|
|||
}
|
||||
const model = plainToInstance(type, req[value]);
|
||||
validate(model, { skipMissingProperties, whitelist, forbidNonWhitelisted }).then((errors: ValidationError[]) => {
|
||||
console.log(errors)
|
||||
if (errors.length > 0) {
|
||||
const message = errors.map((error: ValidationError) => Object.values(error.constraints)).join(', ');
|
||||
return res.status(400).json(message)
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
export class ExecError extends Error {
|
||||
static isExecError(e: any) {
|
||||
if ("type" in e && "script" in e && "unixTime" in e && 'error' in e) {
|
||||
static isExecError(e: any): ExecError | void {
|
||||
if ("type" in e && "script" in e && "unixTime" in e && "error" in e) {
|
||||
return new ExecError(e.type, e.event, e.data);
|
||||
}
|
||||
return;
|
||||
|
@ -8,15 +8,12 @@ export class ExecError extends Error {
|
|||
script: string;
|
||||
unixTime: number;
|
||||
type = EXEC_TYPE.EXEC;
|
||||
error:any;
|
||||
constructor(
|
||||
script: string,
|
||||
...args: any
|
||||
) {
|
||||
error: any;
|
||||
constructor(script: string, ...args: any) {
|
||||
super(...args);
|
||||
this.script = script;
|
||||
this.unixTime = Date.now();
|
||||
this.error = args[0]
|
||||
this.error = args[0];
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -26,17 +23,13 @@ export class SpawnError extends Error {
|
|||
unixTime: number;
|
||||
type = EXEC_TYPE.SPAWN;
|
||||
|
||||
constructor(
|
||||
environment: string,
|
||||
script: string,
|
||||
...args: any
|
||||
) {
|
||||
constructor(environment: string, script: string, ...args: any) {
|
||||
super(...args);
|
||||
this.environment = environment;
|
||||
this.script = script;
|
||||
this.unixTime = Date.now();
|
||||
}
|
||||
static isError(errorType: any): void | SpawnError {
|
||||
static isError(errorType: any): SpawnError | void {
|
||||
if (
|
||||
"command" in errorType &&
|
||||
"error" in errorType &&
|
||||
|
|
7
server/src/core/model/pipiline_meta.ts
Normal file
|
@ -0,0 +1,7 @@
|
|||
export interface IPipelineMeta {
|
||||
pipelineIsRunning: boolean;
|
||||
projectUUID?: string | null;
|
||||
lastProcessCompleteCount: number | null;
|
||||
error: any;
|
||||
}
|
||||
|
|
@ -3,7 +3,7 @@ import { EXEC_TYPE } from "./exec_error_model";
|
|||
|
||||
export interface IPipeline {
|
||||
process: IProcess;
|
||||
trigger: Trigger;
|
||||
trigger?: Trigger;
|
||||
env: Env | null;
|
||||
stackGenerateType: StackGenerateType;
|
||||
}
|
||||
|
|
|
@ -9,7 +9,9 @@ import { EXEC_TYPE, ExecError, SpawnError } from "../model/exec_error_model";
|
|||
abstract class IExecutorProgramService {
|
||||
abstract execPath: string;
|
||||
}
|
||||
class P{
|
||||
|
||||
}
|
||||
export class ExecutorProgramService
|
||||
extends TypedEvent<Result<ExecError | SpawnError, ExecutorResult>>
|
||||
implements IExecutorProgramService
|
||||
|
|
81
server/src/core/services/pipeline_real_time_service.ts
Normal file
|
@ -0,0 +1,81 @@
|
|||
import { TypedEvent } from "../helper/typed_event";
|
||||
import { ExecError } from "../model/exec_error_model";
|
||||
import { ExecutorResult } from "../model/executor_result";
|
||||
import { IPipelineMeta } from "../model/pipiline_meta";
|
||||
import { IPipeline } from "../model/process_model";
|
||||
import { Iteration, StackService } from "./stack_service";
|
||||
|
||||
|
||||
export class PipelineRealTimeService extends TypedEvent<IPipelineMeta> {
|
||||
status: IPipelineMeta;
|
||||
pipelineModels?: IPipeline[];
|
||||
constructor() {
|
||||
super();
|
||||
this.init();
|
||||
}
|
||||
init() {
|
||||
this.status = {
|
||||
pipelineIsRunning: false,
|
||||
projectUUID: null,
|
||||
lastProcessCompleteCount: null,
|
||||
error: null,
|
||||
};
|
||||
}
|
||||
pipelineSubscriber = (iterations: Iteration[]): void => {
|
||||
if (this.status["lastProcessCompleteCount"] === 0) {
|
||||
this.status["lastProcessCompleteCount"] = 0;
|
||||
}
|
||||
this.status.lastProcessCompleteCount += 1;
|
||||
this.pipelineCompleted();
|
||||
this.iterationHelper(iterations[iterations.length - 1]);
|
||||
this.emit(this.status);
|
||||
};
|
||||
|
||||
iterationHelper(iteration: Iteration): void {
|
||||
this.iterationErrorObserver(iteration);
|
||||
|
||||
// TODO(IDONTSUDO): implements
|
||||
// this.iterationLogSaver()
|
||||
}
|
||||
|
||||
iterationLogSaver() {
|
||||
throw new Error("Method not implemented.");
|
||||
}
|
||||
|
||||
iterationErrorObserver(iteration: Iteration): void {
|
||||
if (this.status.lastProcessCompleteCount === 1) {
|
||||
return;
|
||||
}
|
||||
const result = iteration?.result;
|
||||
const executorResult = ExecutorResult.isExecutorResult(result);
|
||||
const execError = ExecError.isExecError(result);
|
||||
|
||||
if (executorResult instanceof ExecutorResult) {
|
||||
this.status.error = executorResult;
|
||||
}
|
||||
|
||||
if (execError instanceof ExecError) {
|
||||
this.status.error = execError;
|
||||
}
|
||||
}
|
||||
pipelineCompleted() {
|
||||
const pipelineIsCompleted =
|
||||
this.status.lastProcessCompleteCount === this.pipelineModels?.length;
|
||||
if (pipelineIsCompleted) {
|
||||
this.status.pipelineIsRunning = false;
|
||||
}
|
||||
}
|
||||
|
||||
runPipeline(
|
||||
pipelineModels: IPipeline[],
|
||||
path: string,
|
||||
projectUUID: string
|
||||
): void {
|
||||
const testPath = path + "/context";
|
||||
const stack = new StackService(pipelineModels, testPath);
|
||||
this.status["projectUUID"] = projectUUID;
|
||||
this.status["pipelineIsRunning"] = true;
|
||||
stack.on(this.pipelineSubscriber);
|
||||
stack.call();
|
||||
}
|
||||
}
|
|
@ -24,7 +24,10 @@ export abstract class IStackService {
|
|||
abstract init(processed: IPipeline[], path: string): void;
|
||||
}
|
||||
|
||||
export class StackService extends TypedEvent<string> implements IStackService {
|
||||
export class StackService
|
||||
extends TypedEvent<Iteration[]>
|
||||
implements IStackService
|
||||
{
|
||||
callStack: Iteration[];
|
||||
path: string;
|
||||
constructor(processed: IPipeline[], path: string) {
|
||||
|
@ -56,6 +59,7 @@ export class StackService extends TypedEvent<string> implements IStackService {
|
|||
for await (const el of this.callStack!) {
|
||||
await this.execStack(inc, el);
|
||||
inc += 1;
|
||||
this.emit(this.callStack);
|
||||
}
|
||||
}
|
||||
async execStack(
|
||||
|
@ -121,14 +125,17 @@ export class StackService extends TypedEvent<string> implements IStackService {
|
|||
return promise;
|
||||
}
|
||||
private async triggerExec(
|
||||
trigger: Trigger,
|
||||
trigger: Trigger | null,
|
||||
stackNumber: number
|
||||
): Promise<Result<boolean, boolean>> {
|
||||
const hashes = this.callStack[stackNumber].hashes;
|
||||
if (trigger !== null) {
|
||||
const hashes = this.callStack[stackNumber].hashes;
|
||||
|
||||
if (hashes != null) {
|
||||
return await new TriggerService(trigger, hashes, this.path).call();
|
||||
if (hashes != null) {
|
||||
return await new TriggerService(trigger, hashes, this.path).call();
|
||||
}
|
||||
throw new Error("Hashes is null");
|
||||
}
|
||||
throw new Error("Hashes is null");
|
||||
return Result.ok();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
export class RegExpSearchDataBaseModelUseCase {
|
||||
call = () => {};
|
||||
}
|
|
@ -1,6 +1,6 @@
|
|||
import { IsMongoId, IsEnum } from "class-validator";
|
||||
import { Schema, model } from "mongoose";
|
||||
import { StackGenerateType } from "../../core/model/process_model";
|
||||
import { IProcess, StackGenerateType } from "../../core/model/process_model";
|
||||
import {
|
||||
TriggerModel,
|
||||
triggerSchema,
|
||||
|
@ -19,10 +19,7 @@ export const PipelineSchema = new Schema({
|
|||
ref: triggerSchema,
|
||||
autopopulate: true,
|
||||
default: null,
|
||||
},
|
||||
command: {
|
||||
type: String,
|
||||
},
|
||||
}
|
||||
}).plugin(require("mongoose-autopopulate"));
|
||||
|
||||
export const schemaPipeline = "Pipeline";
|
||||
|
@ -34,7 +31,7 @@ export const PipelineDBModel = model<PipelineModel>(
|
|||
|
||||
export class PipelineModel {
|
||||
@IsMongoId()
|
||||
public process: PipelineModel;
|
||||
public process: IProcess;
|
||||
|
||||
@IsMongoId()
|
||||
//TODO(IDONTSUDO):NEED OPTION DECORATOR??
|
||||
|
|
|
@ -6,16 +6,16 @@ import {
|
|||
IsBoolean,
|
||||
} from "class-validator";
|
||||
import { Schema, model } from "mongoose";
|
||||
import {
|
||||
IProcess,
|
||||
IssueType,
|
||||
} from "../../core/model/process_model";
|
||||
import { IProcess, IssueType } from "../../core/model/process_model";
|
||||
import { EXEC_TYPE } from "../../core/model/exec_error_model";
|
||||
|
||||
export const ProcessSchema = new Schema({
|
||||
type: {
|
||||
type: String,
|
||||
},
|
||||
description: {
|
||||
type: String,
|
||||
},
|
||||
command: {
|
||||
type: String,
|
||||
},
|
||||
|
@ -46,6 +46,9 @@ export class ProcessModel implements IProcess {
|
|||
@IsEnum(EXEC_TYPE)
|
||||
public type: EXEC_TYPE;
|
||||
|
||||
@IsString()
|
||||
public description: string;
|
||||
|
||||
@IsString()
|
||||
public command: string;
|
||||
|
||||
|
@ -66,4 +69,3 @@ export class ProcessModel implements IProcess {
|
|||
@IsString()
|
||||
public commit?: string;
|
||||
}
|
||||
|
|
@ -5,6 +5,7 @@ import { IsMongoId, IsString } from "class-validator";
|
|||
export interface IProjectModel {
|
||||
pipelines: [PipelineModel];
|
||||
rootDir: string;
|
||||
description: string;
|
||||
}
|
||||
|
||||
export const ProjectSchema = new Schema({
|
||||
|
@ -17,6 +18,9 @@ export const ProjectSchema = new Schema({
|
|||
rootDir: {
|
||||
type: String,
|
||||
},
|
||||
description: {
|
||||
type: String,
|
||||
},
|
||||
}).plugin(require("mongoose-autopopulate"));
|
||||
|
||||
const schema = "Projects";
|
||||
|
@ -25,7 +29,11 @@ export const ProjectDBModel = model<IProjectModel>(schema, ProjectSchema);
|
|||
|
||||
export class ProjectModel implements IProjectModel {
|
||||
@IsMongoId()
|
||||
pipelines: [PipelineModel];
|
||||
public pipelines: [PipelineModel];
|
||||
|
||||
@IsString()
|
||||
rootDir: string;
|
||||
public rootDir: string;
|
||||
|
||||
@IsString()
|
||||
public description: string;
|
||||
}
|
||||
|
|
29
server/src/features/realtime/realtime_presentation.ts
Normal file
|
@ -0,0 +1,29 @@
|
|||
import { CoreHttpController } from "../../core/controllers/http_controller";
|
||||
import { Result } from "../../core/helper/result";
|
||||
import { IPipelineMeta } from "../../core/model/pipiline_meta";
|
||||
import {
|
||||
PipelineRealTimeService,
|
||||
} from "../../core/services/pipeline_real_time_service";
|
||||
|
||||
export const pipelineRealTimeService = new PipelineRealTimeService();
|
||||
|
||||
class PipelineStatusUseCase {
|
||||
async call(): Promise<Result<Error, IPipelineMeta>> {
|
||||
try {
|
||||
return Result.ok(pipelineRealTimeService.status);
|
||||
} catch (error) {
|
||||
return Result.error(error as Error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export class RealTimePresentation extends CoreHttpController<void> {
|
||||
constructor() {
|
||||
super({
|
||||
validationModel: null,
|
||||
url: "realtime",
|
||||
databaseModel: null,
|
||||
});
|
||||
super.get(new PipelineStatusUseCase().call);
|
||||
}
|
||||
}
|
|
@ -1,9 +1,10 @@
|
|||
import { IsArray, IsOptional, IsEnum} from "class-validator";
|
||||
import { IsArray, IsOptional, IsEnum, IsString } from "class-validator";
|
||||
import { Schema, model } from "mongoose";
|
||||
|
||||
export interface ITriggerModel {
|
||||
_id?: string;
|
||||
type: string;
|
||||
description: string;
|
||||
value: string[];
|
||||
}
|
||||
|
||||
|
@ -12,6 +13,9 @@ export const TriggerSchema = new Schema({
|
|||
type: String,
|
||||
require: true,
|
||||
},
|
||||
description: {
|
||||
type: String,
|
||||
},
|
||||
value: {
|
||||
type: Array,
|
||||
require: true,
|
||||
|
@ -20,7 +24,10 @@ export const TriggerSchema = new Schema({
|
|||
|
||||
export const triggerSchema = "Trigger";
|
||||
|
||||
export const TriggerDBModel = model<ITriggerModel>(triggerSchema, TriggerSchema);
|
||||
export const TriggerDBModel = model<ITriggerModel>(
|
||||
triggerSchema,
|
||||
TriggerSchema
|
||||
);
|
||||
|
||||
export enum TriggerType {
|
||||
PROCESS = "PROCESS",
|
||||
|
@ -30,6 +37,8 @@ export enum TriggerType {
|
|||
export class TriggerModel implements ITriggerModel {
|
||||
@IsOptional()
|
||||
public _id: string;
|
||||
@IsString()
|
||||
public description;
|
||||
@IsEnum(TriggerType)
|
||||
public type: TriggerType;
|
||||
@IsArray()
|
||||
|
@ -40,4 +49,3 @@ export interface Trigger {
|
|||
type: TriggerType;
|
||||
value: string[];
|
||||
}
|
||||
|
|
@ -5,17 +5,22 @@ import { TriggerPresentation } from "./features/triggers/triggers_presentation";
|
|||
import { ProjectsPresentation } from "./features/projects/projects_presentation";
|
||||
import { PipelinePresentation } from "./features/pipelines/pipeline_presentation";
|
||||
import { ProcessPresentation } from "./features/process/process_presentation";
|
||||
|
||||
import { SocketSubscriber } from "./core/controllers/socket_controller";
|
||||
import {
|
||||
RealTimePresentation,
|
||||
pipelineRealTimeService,
|
||||
} from "./features/realtime/realtime_presentation";
|
||||
|
||||
const httpRoutes: Routes[] = [
|
||||
new TriggerPresentation(),
|
||||
new ProjectsPresentation(),
|
||||
new ProcessPresentation(),
|
||||
new PipelinePresentation(),
|
||||
new RealTimePresentation(),
|
||||
].map((el) => el.call());
|
||||
|
||||
const computedFolder = "";
|
||||
|
||||
new App(httpRoutes, computedFolder).listen();
|
||||
|
||||
const socketSubscribers = [
|
||||
new SocketSubscriber(pipelineRealTimeService, "realtime"),
|
||||
];
|
||||
|
||||
new App(httpRoutes, socketSubscribers).listen();
|
||||
|
|
46
server/test/model/mock_pipelines.ts
Normal file
|
@ -0,0 +1,46 @@
|
|||
import { EXEC_TYPE } from "../../src/core/model/exec_error_model";
|
||||
import {
|
||||
IssueType,
|
||||
StackGenerateType,
|
||||
} from "../../src/core/model/process_model";
|
||||
import { TriggerType } from "../../src/features/triggers/trigger_model";
|
||||
|
||||
export const mockSimplePipeline = [
|
||||
{
|
||||
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: null,
|
||||
env: null,
|
||||
stackGenerateType: StackGenerateType.SINGLETON,
|
||||
},
|
||||
];
|
13
server/test/services/pipeline_real_time_service_test.ts
Normal file
|
@ -0,0 +1,13 @@
|
|||
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();
|
||||
this.init();
|
||||
}
|
||||
async test() {
|
||||
this.runPipeline(mockSimplePipeline, dirname__, "");
|
||||
}
|
||||
}
|
|
@ -1,15 +1,9 @@
|
|||
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 { delay } from "../../src/core/helper/delay";
|
||||
import { assert, dirname__ } from "../test";
|
||||
import { TriggerType } from "../../src/features/triggers/trigger_model";
|
||||
import { mockSimplePipeline } from "../model/mock_pipelines";
|
||||
|
||||
abstract class IStackServiceTest {
|
||||
abstract test(): Promise<boolean>;
|
||||
|
@ -40,51 +34,7 @@ class SimpleTestStackServiceTest
|
|||
implements IStackServiceTest
|
||||
{
|
||||
constructor() {
|
||||
super(
|
||||
[
|
||||
{
|
||||
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/"
|
||||
);
|
||||
super(mockSimplePipeline, dirname__ + "/context/");
|
||||
}
|
||||
async test(): Promise<boolean> {
|
||||
await this.call();
|
||||
|
|
|
@ -12,6 +12,7 @@ import { DeleteDataBaseModelUseCaseTest } from "./usecases/delete_database_model
|
|||
import { ReadDataBaseModelUseCaseTest } from "./usecases/read_database_model_usecase_test";
|
||||
import { UpdateDataBaseModelUseCaseTest } from "./usecases/update_database_model_usecase";
|
||||
import { PaginationDataBaseModelUseCaseTest } from "./usecases/pagination_database_model_usecase_test";
|
||||
// import { PipelineRealTimeServiceTest } from "./services/pipeline_real_time_service_test";
|
||||
|
||||
|
||||
const testCore = TestCore.instance;
|
||||
|
@ -29,19 +30,20 @@ const init = async () =>{
|
|||
}
|
||||
|
||||
const test = async () =>{
|
||||
await new ExecutorProgramServiceTest(dirname__).test();
|
||||
await new FilesChangerTest(dirname__).test();
|
||||
// await new ExecutorProgramServiceTest(dirname__).test();
|
||||
// await new FilesChangerTest(dirname__).test();
|
||||
await new StackServiceTest(dirname__ + "/context/").test();
|
||||
await new TriggerServiceTest().test();
|
||||
await new CreateDataBaseModelUseCaseTest().test()
|
||||
// await new TriggerServiceTest().test();
|
||||
// await new CreateDataBaseModelUseCaseTest().test()
|
||||
|
||||
await new CreateDataBaseModelUseCaseTest().test()
|
||||
await new DeleteDataBaseModelUseCaseTest().test()
|
||||
await new ReadDataBaseModelUseCaseTest().test()
|
||||
await new UpdateDataBaseModelUseCaseTest().test()
|
||||
for await (const usecase of tests) {
|
||||
testCore.assert(await new usecase().test(), usecase.name)
|
||||
}
|
||||
// await new CreateDataBaseModelUseCaseTest().test()
|
||||
// await new DeleteDataBaseModelUseCaseTest().test()
|
||||
// await new ReadDataBaseModelUseCaseTest().test()
|
||||
// await new UpdateDataBaseModelUseCaseTest().test()
|
||||
// await new PipelineRealTimeServiceTest().test()
|
||||
// for await (const usecase of tests) {
|
||||
// testCore.assert(await new usecase().test(), usecase.name)
|
||||
// }
|
||||
}
|
||||
const main = async () => {
|
||||
await init()
|
||||
|
|
|
@ -1,19 +1,19 @@
|
|||
{
|
||||
"compileOnSave": false,
|
||||
"compilerOptions": {
|
||||
"target": "es2017",
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"experimentalDecorators": true,
|
||||
"emitDecoratorMetadata": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"moduleResolution": "node",
|
||||
"pretty": true,
|
||||
"declaration": true,
|
||||
"outDir": "./build",
|
||||
"allowJs": true,
|
||||
"noEmit": false,
|
||||
"esModuleInterop": true,
|
||||
"resolveJsonModule": true,
|
||||
"importHelpers": true,
|
||||
}
|
||||
"compileOnSave": false,
|
||||
"compilerOptions": {
|
||||
"target": "es2017",
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"experimentalDecorators": true,
|
||||
"emitDecoratorMetadata": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"moduleResolution": "node",
|
||||
"pretty": true,
|
||||
"declaration": true,
|
||||
"outDir": "./build",
|
||||
"allowJs": true,
|
||||
"noEmit": false,
|
||||
"esModuleInterop": true,
|
||||
"resolveJsonModule": true,
|
||||
"importHelpers": true
|
||||
}
|
||||
}
|
||||
|
|
1
ui/.gitignore
vendored
|
@ -22,3 +22,4 @@ npm-debug.log*
|
|||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
package-lock.json
|
||||
todo.md
|
|
@ -1,5 +1,5 @@
|
|||
{
|
||||
"name": "infinite-todo-list",
|
||||
"name": "ui-robossembler",
|
||||
"version": "0.1.0",
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
|
@ -11,14 +11,20 @@
|
|||
"@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",
|
||||
"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",
|
||||
"typescript": "^4.9.5",
|
||||
"uuid": "^9.0.0",
|
||||
"web-vitals": "^2.1.4"
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
/>
|
||||
<link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />
|
||||
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
|
||||
<title>Infinite Todo List</title>
|
||||
<title>robossembler: pipeline </title>
|
||||
</head>
|
||||
<body>
|
||||
<noscript>You need to enable JavaScript to run this app.</noscript>
|
||||
|
|
Before Width: | Height: | Size: 769 B After Width: | Height: | Size: 769 B |
Before Width: | Height: | Size: 628 B After Width: | Height: | Size: 628 B |
Before Width: | Height: | Size: 664 B After Width: | Height: | Size: 664 B |
Before Width: | Height: | Size: 701 B After Width: | Height: | Size: 701 B |
1
ui/src/core/assets/icons/error.svg
Normal file
|
@ -0,0 +1 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" data-name="Layer 1" viewBox="0 0 64 64" id="error"><circle cx="32" cy="32" r="28" fill="none" stroke="#010101" stroke-miterlimit="10" stroke-width="4"></circle><line x1="32" x2="32" y1="18" y2="38" fill="none" stroke="#010101" stroke-miterlimit="10" stroke-width="4"></line><line x1="32" x2="32" y1="42" y2="46" fill="none" stroke="#010101" stroke-miterlimit="10" stroke-width="4"></line></svg>
|
After Width: | Height: | Size: 434 B |
3
ui/src/core/assets/icons/left_icon.svg
Normal file
|
@ -0,0 +1,3 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" width="800px" height="800px" viewBox="0 0 24 24" fill="none">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M11.7071 4.29289C12.0976 4.68342 12.0976 5.31658 11.7071 5.70711L6.41421 11H20C20.5523 11 21 11.4477 21 12C21 12.5523 20.5523 13 20 13H6.41421L11.7071 18.2929C12.0976 18.6834 12.0976 19.3166 11.7071 19.7071C11.3166 20.0976 10.6834 20.0976 10.2929 19.7071L3.29289 12.7071C3.10536 12.5196 3 12.2652 3 12C3 11.7348 3.10536 11.4804 3.29289 11.2929L10.2929 4.29289C10.6834 3.90237 11.3166 3.90237 11.7071 4.29289Z" fill="#000000"/>
|
||||
</svg>
|
After Width: | Height: | Size: 584 B |
3
ui/src/core/assets/icons/reload.svg
Normal file
|
@ -0,0 +1,3 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" width="800px" height="800px" viewBox="0 0 24 24" fill="none">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M13.7071 1.29289C14.0976 1.68342 14.0976 2.31658 13.7071 2.70711L12.4053 4.00896C17.1877 4.22089 21 8.16524 21 13C21 17.9706 16.9706 22 12 22C7.02944 22 3 17.9706 3 13C3 12.4477 3.44772 12 4 12C4.55228 12 5 12.4477 5 13C5 16.866 8.13401 20 12 20C15.866 20 19 16.866 19 13C19 9.2774 16.0942 6.23349 12.427 6.01281L13.7071 7.29289C14.0976 7.68342 14.0976 8.31658 13.7071 8.70711C13.3166 9.09763 12.6834 9.09763 12.2929 8.70711L9.29289 5.70711C9.10536 5.51957 9 5.26522 9 5C9 4.73478 9.10536 4.48043 9.29289 4.29289L12.2929 1.29289C12.6834 0.902369 13.3166 0.902369 13.7071 1.29289Z" fill="#0F1729"/>
|
||||
</svg>
|
After Width: | Height: | Size: 755 B |
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 1.4 KiB |
45
ui/src/core/extensions/array.ts
Normal file
|
@ -0,0 +1,45 @@
|
|||
export {};
|
||||
|
||||
declare global {
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
export const ArrayExtensions = () => {
|
||||
if ([].equals === undefined) {
|
||||
// eslint-disable-next-line no-extend-native
|
||||
Array.prototype.equals = function (array, strict = true) {
|
||||
if (!array) return false;
|
||||
|
||||
if (arguments.length === 1) strict = true;
|
||||
|
||||
if (this.length !== array.length) return false;
|
||||
|
||||
for (let i = 0; i < this.length; i++) {
|
||||
if (this[i] instanceof Array && array[i] instanceof Array) {
|
||||
if (!this[i].equals(array[i], strict)) return false;
|
||||
} else if (strict && this[i] !== array[i]) {
|
||||
return false;
|
||||
} else if (!strict) {
|
||||
return this.sort().equals(array.sort(), true);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
};
|
||||
}
|
||||
if ([].lastElement === undefined) {
|
||||
// eslint-disable-next-line no-extend-native
|
||||
Array.prototype.lastElement = function () {
|
||||
let instanceCheck = this;
|
||||
if (instanceCheck === undefined) {
|
||||
return undefined;
|
||||
} else {
|
||||
let instance = instanceCheck as [];
|
||||
return instance[instance.length - 1];
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
6
ui/src/core/extensions/extensions.ts
Normal file
|
@ -0,0 +1,6 @@
|
|||
import { ArrayExtensions } from "./array";
|
||||
|
||||
export const extensions = () =>{
|
||||
ArrayExtensions()
|
||||
}
|
||||
|
535
ui/src/core/helper/result.ts
Normal file
|
@ -0,0 +1,535 @@
|
|||
/* 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;
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-redeclare
|
||||
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);
|
||||
}
|
||||
}
|
3
ui/src/core/helper/validate.ts
Normal file
|
@ -0,0 +1,3 @@
|
|||
export function validateRequired(value: string) {
|
||||
return value ? undefined : "required";
|
||||
}
|
15
ui/src/core/l10n/i18n.ts
Normal file
|
@ -0,0 +1,15 @@
|
|||
import i18n from "i18next";
|
||||
import { initReactI18next } from "react-i18next";
|
||||
import { resources } from "./locale";
|
||||
i18n
|
||||
.use(initReactI18next) // passes i18n down to react-i18next
|
||||
.init({
|
||||
resources,
|
||||
lng: "en", // language to use, more information here: https://www.i18next.com/overview/configuration-options#languages-namespaces-resources
|
||||
// you can use the i18n.changeLanguage function to change the language manually: https://www.i18next.com/overview/api#changelanguage
|
||||
// if you're using a language detector, do not define the lng option
|
||||
|
||||
interpolation: {
|
||||
escapeValue: false, // react already safes from xss
|
||||
},
|
||||
});
|
12
ui/src/core/l10n/locale.ts
Normal file
|
@ -0,0 +1,12 @@
|
|||
export const resources = {
|
||||
en: {
|
||||
translation: {
|
||||
"Welcome to React": "Welcome to React and react-i18next",
|
||||
},
|
||||
},
|
||||
fr: {
|
||||
translation: {
|
||||
"Welcome to React": "Bienvenue à React et react-i18next",
|
||||
},
|
||||
},
|
||||
};
|
|
@ -1,6 +1,7 @@
|
|||
export interface ITriggerModel {
|
||||
_id?: string;
|
||||
type: string;
|
||||
description:string;
|
||||
value: string[];
|
||||
}
|
||||
|
|
@ -5,7 +5,7 @@ export enum HttpMethod {
|
|||
|
||||
export class HttpRepository {
|
||||
|
||||
private server = 'http://localhost:3000'
|
||||
private server = 'http://localhost:4001'
|
||||
|
||||
public async jsonRequest<T>(method: HttpMethod, url: string, data?: any): Promise<T> {
|
||||
const reqInit = {
|
||||
|
|
14
ui/src/core/repository/socket_repository.ts
Normal file
|
@ -0,0 +1,14 @@
|
|||
import { Socket, io } from "socket.io-client";
|
||||
|
||||
export class SocketRepository {
|
||||
serverURL = "ws://localhost:4001";
|
||||
socket: Socket | undefined;
|
||||
async connect() {
|
||||
const socket = io(this.serverURL);
|
||||
this.socket = socket;
|
||||
socket.connect();
|
||||
socket.on('mock', (d) =>{
|
||||
console.log(d)
|
||||
})
|
||||
}
|
||||
}
|
36
ui/src/core/routers/routers.tsx
Normal file
|
@ -0,0 +1,36 @@
|
|||
import { createBrowserRouter } from "react-router-dom";
|
||||
import {
|
||||
AllProjectScreen,
|
||||
AllProjectScreenPath,
|
||||
} from "../../features/all_projects/presentation/all_projects_screen";
|
||||
import {
|
||||
PipelineInstanceScreen,
|
||||
PipelineScreenPath,
|
||||
} from "../../features/pipeline_instance_main_screen/pipeline_instance_screen";
|
||||
import {
|
||||
SelectProjectScreen,
|
||||
SelectProjectScreenPath,
|
||||
} from "../../features/select_project/presentation/select_project";
|
||||
import {
|
||||
CreatePipelineScreen,
|
||||
CreatePipelineScreenPath,
|
||||
} from "../../features/create_pipeline/presentation/create_pipeline_screen";
|
||||
|
||||
export const router = createBrowserRouter([
|
||||
{
|
||||
path: AllProjectScreenPath,
|
||||
element: <AllProjectScreen />,
|
||||
},
|
||||
{
|
||||
path: PipelineScreenPath,
|
||||
element: <PipelineInstanceScreen />,
|
||||
},
|
||||
{
|
||||
path: SelectProjectScreenPath,
|
||||
element: <SelectProjectScreen />,
|
||||
},
|
||||
{
|
||||
path: CreatePipelineScreenPath,
|
||||
element: <CreatePipelineScreen />,
|
||||
},
|
||||
]);
|
94
ui/src/core/ui/header/header.tsx
Normal file
|
@ -0,0 +1,94 @@
|
|||
import * as React from "react";
|
||||
import { Typography } from "antd";
|
||||
import { Col, Row } from "antd";
|
||||
import { LinkTypography } from "../link/link";
|
||||
import { ReactComponent as LeftIcon } from "../../assets/icons/left_icon.svg";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
|
||||
const { Title } = Typography;
|
||||
|
||||
export interface IHeader {
|
||||
largeText: string;
|
||||
minText?: string;
|
||||
path?: string;
|
||||
needBackButton?: undefined | any;
|
||||
}
|
||||
|
||||
export const Header: React.FunctionComponent<IHeader> = (props: IHeader) => {
|
||||
const navigate = useNavigate();
|
||||
const needBackButton = props.needBackButton !== undefined ? false : true;
|
||||
|
||||
return (
|
||||
<Col style={{ textAlign: "center" }}>
|
||||
<Row
|
||||
style={{
|
||||
marginTop: "20px",
|
||||
marginRight: "20px",
|
||||
display: "contents",
|
||||
}}
|
||||
>
|
||||
{needBackButton ? (
|
||||
<>
|
||||
<div
|
||||
onClick={() => {
|
||||
navigate(-1);
|
||||
}}
|
||||
style={{
|
||||
position: "absolute",
|
||||
zIndex: 1,
|
||||
left: "10px",
|
||||
backgroundColor: "#456BD9",
|
||||
border: "0.1875em solid #0F1C3F",
|
||||
borderRadius: "50%",
|
||||
height: "60px",
|
||||
width: "60px",
|
||||
cursor: "pointer",
|
||||
}}
|
||||
>
|
||||
<LeftIcon
|
||||
style={{
|
||||
pointerEvents: "none",
|
||||
|
||||
cursor: "pointer",
|
||||
width: "40px",
|
||||
height: "40px",
|
||||
position: "relative",
|
||||
bottom: "-8px",
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<Title
|
||||
style={{
|
||||
position: "relative",
|
||||
bottom: "-8px",
|
||||
left: "20px",
|
||||
width: "100vw",
|
||||
}}
|
||||
level={2}
|
||||
>
|
||||
{props.largeText}
|
||||
</Title>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<Title style={{ justifyContent: "center" }} level={2}>
|
||||
{props.largeText}
|
||||
</Title>
|
||||
</>
|
||||
)}
|
||||
</Row>
|
||||
|
||||
{props.minText !== undefined ? (
|
||||
<LinkTypography
|
||||
style={{
|
||||
marginBottom: "40px",
|
||||
}}
|
||||
path={props.path!}
|
||||
text={props.minText}
|
||||
/>
|
||||
) : (
|
||||
<></>
|
||||
)}
|
||||
</Col>
|
||||
);
|
||||
};
|
28
ui/src/core/ui/link/link.tsx
Normal file
|
@ -0,0 +1,28 @@
|
|||
import * as React from "react";
|
||||
import { Typography } from "antd";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
|
||||
const { Link } = Typography;
|
||||
|
||||
export interface ILinkTypography {
|
||||
path: string;
|
||||
text: string;
|
||||
style?: React.CSSProperties;
|
||||
}
|
||||
|
||||
export const LinkTypography: React.FunctionComponent<ILinkTypography> = (
|
||||
props: ILinkTypography
|
||||
) => {
|
||||
const navigate = useNavigate();
|
||||
|
||||
return (
|
||||
<Link
|
||||
style={props.style}
|
||||
onClick={() => {
|
||||
navigate(props.path);
|
||||
}}
|
||||
>
|
||||
{props.text}
|
||||
</Link>
|
||||
);
|
||||
};
|
66
ui/src/core/ui/list/list.tsx
Normal file
|
@ -0,0 +1,66 @@
|
|||
import { Row } from "antd";
|
||||
import { ReactComponent as AddIcon } from "../../assets/icons/add.svg";
|
||||
import { observer } from "mobx-react-lite";
|
||||
|
||||
export type CallBackFunction = (a: string) => void;
|
||||
|
||||
export interface ListElement {
|
||||
text: string;
|
||||
color?: string;
|
||||
}
|
||||
|
||||
export interface IPropsList {
|
||||
values: ListElement[];
|
||||
headers?: string;
|
||||
onClick?: CallBackFunction;
|
||||
}
|
||||
|
||||
export const List: React.FunctionComponent<IPropsList> = observer(
|
||||
(props) => {
|
||||
return (
|
||||
<div>
|
||||
{props.headers !== undefined ? <>{props.headers}</> : <></>}
|
||||
{props.values.map((el) => {
|
||||
return (
|
||||
<Row
|
||||
style={{
|
||||
width: "300px",
|
||||
backgroundColor: el.color ?? "ActiveBorder",
|
||||
}}
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
width: "-webkit-fill-available",
|
||||
margin: "5px",
|
||||
marginLeft: "40px",
|
||||
}}
|
||||
/>
|
||||
|
||||
<div
|
||||
style={{
|
||||
marginLeft: "40px",
|
||||
}}
|
||||
>
|
||||
{el.text}
|
||||
</div>
|
||||
<div style={{ flexGrow: "1" }}></div>
|
||||
<AddIcon
|
||||
style={{
|
||||
width: "50px",
|
||||
cursor: "pointer",
|
||||
height: "50px",
|
||||
marginRight: "40px",
|
||||
}}
|
||||
onClick={() => {
|
||||
if (props.onClick !== undefined) {
|
||||
props.onClick(el.text);
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</Row>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
);
|
31
ui/src/core/ui/loader/loader.css
Normal file
|
@ -0,0 +1,31 @@
|
|||
.loader {
|
||||
position: relative;
|
||||
width: 64px;
|
||||
height: 64px;
|
||||
background-color: rgba(0, 0, 0, 0.5);
|
||||
transform: rotate(45deg);
|
||||
overflow: hidden;
|
||||
}
|
||||
.loader:after{
|
||||
content: '';
|
||||
position: absolute;
|
||||
inset: 8px;
|
||||
margin: auto;
|
||||
background: #222b32;
|
||||
}
|
||||
.loader:before{
|
||||
content: '';
|
||||
position: absolute;
|
||||
inset: -15px;
|
||||
margin: auto;
|
||||
background: #de3500;
|
||||
animation: diamondLoader 2s linear infinite;
|
||||
}
|
||||
@keyframes diamondLoader {
|
||||
0% ,10% {
|
||||
transform: translate(-64px , -64px) rotate(-45deg)
|
||||
}
|
||||
90% , 100% {
|
||||
transform: translate(0px , 0px) rotate(-45deg)
|
||||
}
|
||||
}
|
16
ui/src/core/ui/loader/loader.tsx
Normal file
|
@ -0,0 +1,16 @@
|
|||
import * as React from "react";
|
||||
import "./loader.css";
|
||||
|
||||
export const Loader: React.FunctionComponent = () => {
|
||||
return (
|
||||
<div
|
||||
style={{
|
||||
display: "flex",
|
||||
justifyContent: "center",
|
||||
placeItems: "center",
|
||||
}}
|
||||
>
|
||||
<div className="loader" />
|
||||
</div>
|
||||
);
|
||||
};
|
49
ui/src/core/ui/pages/load_page.tsx
Normal file
|
@ -0,0 +1,49 @@
|
|||
import * as React from "react";
|
||||
import { Header, IHeader } from "../header/header";
|
||||
import { Loader } from "../loader/loader";
|
||||
import { ReactComponent as ErrorIcon } from "../../assets/icons/error.svg";
|
||||
import { Typography } from "antd";
|
||||
import { observer } from "mobx-react-lite";
|
||||
const { Title } = Typography;
|
||||
|
||||
interface ILoadPage extends IHeader {
|
||||
isLoading: boolean;
|
||||
isError: boolean;
|
||||
children?: JSX.Element | JSX.Element[];
|
||||
}
|
||||
|
||||
export const LoadPage: React.FunctionComponent<ILoadPage> = observer((
|
||||
props: ILoadPage
|
||||
) => {
|
||||
return (
|
||||
<>
|
||||
<Header
|
||||
path={props.path}
|
||||
largeText={props.largeText}
|
||||
minText={props.minText}
|
||||
/>
|
||||
{props.isError ? (
|
||||
<>
|
||||
<ErrorIcon
|
||||
style={{
|
||||
height: "100px",
|
||||
width: "-webkit-fill-available",
|
||||
}}
|
||||
/>
|
||||
<Title style={{ textAlign: "center" }} level={3} type="danger">
|
||||
not expected error
|
||||
</Title>
|
||||
</>
|
||||
) : (
|
||||
<></>
|
||||
)}
|
||||
{props.isLoading ? (
|
||||
<div style={{'marginTop':'50px'}}>
|
||||
<Loader />
|
||||
</div>
|
||||
) : (
|
||||
<>{props.children}</>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
});
|
3
ui/src/features/all_projects/data/project_repository.ts
Normal file
|
@ -0,0 +1,3 @@
|
|||
import { HttpRepository } from "../../../core/repository/http_repository";
|
||||
|
||||
export class ProjectRepository extends HttpRepository {}
|
|
@ -0,0 +1,18 @@
|
|||
import * as React from "react";
|
||||
import { SelectProjectScreenPath } from "../../select_project/presentation/select_project";
|
||||
import { Header } from "../../../core/ui/header/header";
|
||||
|
||||
export const AllProjectScreenPath = "/";
|
||||
|
||||
export const AllProjectScreen: React.FunctionComponent = () => {
|
||||
return (
|
||||
<>
|
||||
<Header
|
||||
path={SelectProjectScreenPath}
|
||||
largeText={"All Projects"}
|
||||
minText={"create new pipiline?"}
|
||||
needBackButton={true}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
};
|
|
@ -0,0 +1,12 @@
|
|||
import { makeAutoObservable } from "mobx";
|
||||
import { ProjectRepository } from "../data/project_repository";
|
||||
|
||||
class AllProjectStore {
|
||||
constructor(repository: ProjectRepository) {
|
||||
|
||||
makeAutoObservable(this);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export const allProjectStore = new AllProjectStore(new ProjectRepository());
|
|
@ -0,0 +1,29 @@
|
|||
import {
|
||||
HttpMethod,
|
||||
HttpRepository,
|
||||
} from "../../../core/repository/http_repository";
|
||||
import { ITriggerModel } from "../../../core/model/trigger_model";
|
||||
import { Result } from "../../../core/helper/result";
|
||||
import { IProcess } from "../../create_process/model/process_model";
|
||||
|
||||
export class CreatePipelineRepository extends HttpRepository {
|
||||
async getTriggers(page = 1): Promise<Result<Error, ITriggerModel[]>> {
|
||||
try {
|
||||
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[]>> {
|
||||
try {
|
||||
return Result.ok(
|
||||
await this.jsonRequest<IProcess[]>(HttpMethod.GET, `/process?${page}`)
|
||||
);
|
||||
} catch (error) {
|
||||
return Result.error(error as Error);
|
||||
}
|
||||
}
|
||||
}
|
3
ui/src/features/create_pipeline/model/pipiline_model.ts
Normal file
|
@ -0,0 +1,3 @@
|
|||
export interface IColor {
|
||||
color:string;
|
||||
}
|
|
@ -0,0 +1,51 @@
|
|||
import * as React from "react";
|
||||
import { Row, Input, 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";
|
||||
|
||||
export const CreatePipelineScreenPath = "/create_pipeline";
|
||||
|
||||
export const CreatePipelineScreen: React.FunctionComponent = observer(() => {
|
||||
return (
|
||||
<>
|
||||
<LoadPage
|
||||
largeText={"Create pipeline"}
|
||||
isError={createPipelineStore.isError}
|
||||
isLoading={createPipelineStore.isLoading}
|
||||
children={
|
||||
<>
|
||||
<Row>
|
||||
<List
|
||||
headers={"process"}
|
||||
values={createPipelineStore.processModels.map((el) => {
|
||||
return { text: el.description };
|
||||
})}
|
||||
onClick={(e) => createPipelineStore.addProcess(e)}
|
||||
/>
|
||||
<div style={{ flexGrow: "1" }}>
|
||||
<Input style={{ width: "300px" }} placeholder="description" />
|
||||
<Button onClick={() => createPipelineStore.createPipeline()}>
|
||||
Save result
|
||||
</Button>
|
||||
<List
|
||||
headers="new pipeline"
|
||||
values={createPipelineStore.pipelineViewModel }
|
||||
/>
|
||||
</div>
|
||||
|
||||
<List
|
||||
headers="triggers"
|
||||
values={createPipelineStore.triggersModels.map((el) => {
|
||||
return { text: el.description };
|
||||
})}
|
||||
onClick={(e) => createPipelineStore.addTrigger(e)}
|
||||
/>
|
||||
</Row>
|
||||
</>
|
||||
}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
});
|
|
@ -0,0 +1,95 @@
|
|||
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 {
|
||||
PROCESS,
|
||||
TRIGGER,
|
||||
}
|
||||
interface CommonView {
|
||||
text: string;
|
||||
color: string;
|
||||
type: Direction;
|
||||
}
|
||||
export class CreatePipelineStore {
|
||||
repository: CreatePipelineRepository;
|
||||
triggersModels: ITriggerModel[] = [];
|
||||
processModels: IProcess[] = [];
|
||||
pipelineViewModel: CommonView[] = [];
|
||||
|
||||
isLoading = false;
|
||||
isError = false;
|
||||
page = 1;
|
||||
|
||||
constructor(repository: CreatePipelineRepository) {
|
||||
this.repository = repository;
|
||||
makeAutoObservable(this);
|
||||
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,
|
||||
});
|
||||
}
|
||||
|
||||
addTrigger(e: string): void {
|
||||
const lastElement = this.pipelineViewModel.lastElement()
|
||||
|
||||
if(lastElement !== undefined){
|
||||
if(lastElement.type !== Direction.PROCESS){
|
||||
// need UI say
|
||||
return
|
||||
}
|
||||
}
|
||||
this.pipelineViewModel.push({
|
||||
text: e,
|
||||
color: "blanchedalmond",
|
||||
type: Direction.TRIGGER,
|
||||
});
|
||||
}
|
||||
createPipeline(): void {}
|
||||
async loadProcess() {
|
||||
this.isLoading = true;
|
||||
const result = await this.repository.getProcessed();
|
||||
result.fold(
|
||||
(s) => {
|
||||
this.processModels = s;
|
||||
},
|
||||
(_e) => {
|
||||
this.isError = true;
|
||||
}
|
||||
);
|
||||
this.isLoading = false;
|
||||
}
|
||||
async loadTriggers() {
|
||||
this.isLoading = true;
|
||||
const result = await this.repository.getTriggers(this.page);
|
||||
result.fold(
|
||||
(s) => {
|
||||
this.triggersModels = s;
|
||||
},
|
||||
(_e) => {
|
||||
this.isError = true;
|
||||
}
|
||||
);
|
||||
this.isLoading = false;
|
||||
}
|
||||
}
|
||||
export const createPipelineStore = new CreatePipelineStore(
|
||||
new CreatePipelineRepository()
|
||||
);
|
11
ui/src/features/create_process/data/process_repostiory.ts
Normal file
|
@ -0,0 +1,11 @@
|
|||
import {
|
||||
HttpMethod,
|
||||
HttpRepository,
|
||||
} from "../../../core/repository/http_repository";
|
||||
import { IProcess } from "../model/process_model";
|
||||
|
||||
export class ProcessRepository extends HttpRepository {
|
||||
async save(model: IProcess): Promise<void> {
|
||||
await this.jsonRequest(HttpMethod.POST, "/process", model);
|
||||
}
|
||||
}
|
26
ui/src/features/create_process/model/process_model.ts
Normal file
|
@ -0,0 +1,26 @@
|
|||
export interface IProcess {
|
||||
description: string;
|
||||
type: EXEC_TYPE | string;
|
||||
command: string;
|
||||
isGenerating: boolean;
|
||||
isLocaleCode: boolean;
|
||||
issueType: IssueType | string;
|
||||
timeout?: number;
|
||||
commit?: string | undefined;
|
||||
}
|
||||
export enum EXEC_TYPE {
|
||||
SPAWN = "SPAWN",
|
||||
EXEC = "EXEC",
|
||||
}
|
||||
export enum IssueType {
|
||||
WARNING = "WARNING",
|
||||
ERROR = "ERROR",
|
||||
}
|
||||
export const processModelMock: IProcess = {
|
||||
description: "",
|
||||
type: EXEC_TYPE.SPAWN,
|
||||
command: "",
|
||||
isGenerating: true,
|
||||
isLocaleCode: true,
|
||||
issueType: IssueType.WARNING,
|
||||
};
|
|
@ -0,0 +1,83 @@
|
|||
import * as React from "react";
|
||||
import { processStore } from "./logic/process_store";
|
||||
import { observer } from "mobx-react-lite";
|
||||
import {
|
||||
SubmitButton,
|
||||
Input,
|
||||
ResetButton,
|
||||
Form,
|
||||
Radio,
|
||||
Switch,
|
||||
} from "formik-antd";
|
||||
import { Formik } from "formik";
|
||||
import { Row, Col } from "antd";
|
||||
import { EXEC_TYPE, IssueType, processModelMock } from "../model/process_model";
|
||||
|
||||
export const CreateProcessScreen = observer(() => {
|
||||
return (
|
||||
<div>
|
||||
<div style={{ marginTop: 80 }}>
|
||||
<Formik
|
||||
initialValues={processModelMock}
|
||||
onSubmit={async (values, actions) => {
|
||||
await processStore.saveResult(values);
|
||||
actions.setSubmitting(false);
|
||||
actions.resetForm();
|
||||
}}
|
||||
validate={(values) => {
|
||||
if (!values.command) {
|
||||
return { command: "required" };
|
||||
}
|
||||
if (!values.description) {
|
||||
return { description: "required" };
|
||||
}
|
||||
return {};
|
||||
}}
|
||||
render={() => (
|
||||
<Form>
|
||||
<div
|
||||
style={{
|
||||
background: "white",
|
||||
flex: 1,
|
||||
padding: 40,
|
||||
width: "400px",
|
||||
}}
|
||||
>
|
||||
<Input name="description" placeholder="Description" />
|
||||
|
||||
<Input name="command" placeholder="Command process" />
|
||||
|
||||
<Radio.Group name="type" options={Object.values(EXEC_TYPE)} />
|
||||
|
||||
<Radio.Group
|
||||
name="issueType"
|
||||
options={Object.values(IssueType)}
|
||||
/>
|
||||
|
||||
<Col style={{ marginTop: 20, justifyContent: "center" }}>
|
||||
<Switch
|
||||
name="isGenerating"
|
||||
checkedChildren="is generating"
|
||||
unCheckedChildren="is generating"
|
||||
/>
|
||||
<Switch
|
||||
name="isLocalCode"
|
||||
checkedChildren="is local code"
|
||||
unCheckedChildren="is local code"
|
||||
/>
|
||||
</Col>
|
||||
|
||||
<Row style={{ marginTop: 20, justifyContent: "center" }}>
|
||||
<Col>
|
||||
<ResetButton>Reset</ResetButton>
|
||||
<SubmitButton>Submit</SubmitButton>
|
||||
</Col>
|
||||
</Row>
|
||||
</div>
|
||||
</Form>
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
});
|
|
@ -0,0 +1,17 @@
|
|||
import { makeAutoObservable } from "mobx";
|
||||
import { v4 as uuidv4 } from "uuid";
|
||||
import { ProcessRepository } from "../../data/process_repostiory";
|
||||
import { IProcess } from "../../model/process_model";
|
||||
|
||||
class ProcessStore {
|
||||
repository: ProcessRepository;
|
||||
constructor(repository: ProcessRepository) {
|
||||
this.repository = repository;
|
||||
makeAutoObservable(this);
|
||||
}
|
||||
async saveResult(model:IProcess) {
|
||||
await this.repository.save(model)
|
||||
}
|
||||
}
|
||||
|
||||
export const processStore = new ProcessStore(new ProcessRepository());
|
|
@ -2,7 +2,7 @@ import {
|
|||
HttpMethod,
|
||||
HttpRepository,
|
||||
} from "../../../core/repository/http_repository";
|
||||
import { ITriggerModel } from "../model/trigger_model";
|
||||
import { ITriggerModel } from "../../../core/model/trigger_model";
|
||||
|
||||
export class TriggerRepository extends HttpRepository {
|
||||
public async save(model: ITriggerModel) {
|
|
@ -1,4 +1,4 @@
|
|||
import { TriggerType } from "./trigger_model";
|
||||
import { TriggerType } from "../../../core/model/trigger_model";
|
||||
|
||||
export interface ITriggerViewValueModel {
|
||||
value: string;
|
|
@ -2,7 +2,7 @@ import * as React from "react";
|
|||
import Editor from "@monaco-editor/react";
|
||||
import { Button } from "antd";
|
||||
import { observer } from "mobx-react-lite";
|
||||
import { triggerStore } from "../logic/trigger_store";
|
||||
import { triggerStore } from "../trigger_store";
|
||||
|
||||
export const CodeTriggerForm: React.FunctionComponent = observer(() => {
|
||||
return (
|
||||
|
@ -16,13 +16,11 @@ export const CodeTriggerForm: React.FunctionComponent = observer(() => {
|
|||
onChange={(v) => {
|
||||
triggerStore.writeNewTrigger(v);
|
||||
}}
|
||||
onValidate={(m) => {
|
||||
console.log(m);
|
||||
}}
|
||||
onValidate={(_m) => {}}
|
||||
/>
|
||||
|
||||
<div style={{ width: "100%", backgroundColor: "black", height: "1px" }} />
|
||||
<div style={{ height: "10px" }}/>
|
||||
<div style={{ height: "10px" }} />
|
||||
|
||||
<Button
|
||||
onClick={() => triggerStore.saveCode()}
|
|
@ -3,12 +3,9 @@ import { Formik } from "formik";
|
|||
import { SubmitButton, Input, ResetButton, Form, FormItem } from "formik-antd";
|
||||
import { Row, Col } from "antd";
|
||||
import { observer } from "mobx-react-lite";
|
||||
import { triggerStore } from "../logic/trigger_store";
|
||||
import { TriggerType } from "../../model/trigger_model";
|
||||
|
||||
function validateRequired(value: string) {
|
||||
return value ? undefined : "required";
|
||||
}
|
||||
import { triggerStore } from "../trigger_store";
|
||||
import { TriggerType } from "../../../../core/model/trigger_model";
|
||||
import { validateRequired } from "../../../../core/helper/validate";
|
||||
|
||||
export const FileTriggerForm: React.FunctionComponent = observer(() => {
|
||||
return (
|
|
@ -1,10 +1,11 @@
|
|||
import * as React from "react";
|
||||
import { Button, Col, Row, Switch, Typography } from "antd";
|
||||
import { Button, Col, Row, Switch, Typography, Input } from "antd";
|
||||
import { CodeTriggerForm } from "./components/code_trigger_form";
|
||||
import { observer } from "mobx-react-lite";
|
||||
import { triggerStore } from "./logic/trigger_store";
|
||||
import { triggerStore } from "./trigger_store";
|
||||
import { FileTriggerForm } from "./components/file_trigger_form";
|
||||
import { ReactComponent as DeleteIcon } from "../../../assets/icons/delete.svg";
|
||||
import { Loader } from "../../../core/ui/loader/loader";
|
||||
|
||||
const { Title } = Typography;
|
||||
|
||||
|
@ -48,18 +49,31 @@ const Bottom = observer(() => {
|
|||
export const TriggerScreen: React.FunctionComponent = observer(() => {
|
||||
return (
|
||||
<>
|
||||
<main className="test">
|
||||
<Header />,
|
||||
{triggerStore.getTriggerType() ? (
|
||||
<main>
|
||||
{!triggerStore.isLoading ? (
|
||||
<>
|
||||
<FileTriggerForm />
|
||||
<Header />,
|
||||
<Input
|
||||
placeholder="trigger description"
|
||||
onChange={() => triggerStore.changeTriggerDescription}
|
||||
/>
|
||||
{triggerStore.getTriggerType() ? (
|
||||
<>
|
||||
<FileTriggerForm />
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<CodeTriggerForm />
|
||||
</>
|
||||
)}
|
||||
<Bottom />
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<CodeTriggerForm />
|
||||
|
||||
<Loader/>
|
||||
</>
|
||||
)}
|
||||
<Bottom />
|
||||
</main>
|
||||
</>
|
||||
);
|
|
@ -1,8 +1,8 @@
|
|||
import { makeAutoObservable } from "mobx";
|
||||
import { v4 as uuidv4 } from "uuid";
|
||||
import { TriggerType } from "../../model/trigger_model";
|
||||
import { TriggerRepository } from "../../data/trigger_repository";
|
||||
import { TriggerViewModel } from "../../model/trigger_form_view_model";
|
||||
import { TriggerType } from "../../../core/model/trigger_model";
|
||||
import { TriggerRepository } from "../data/trigger_repository";
|
||||
import { TriggerViewModel } from "../model/trigger_form_view_model";
|
||||
|
||||
class TriggerStore {
|
||||
constructor(repository: TriggerRepository) {
|
||||
|
@ -11,15 +11,19 @@ class TriggerStore {
|
|||
makeAutoObservable(this);
|
||||
}
|
||||
|
||||
isLoading = false;
|
||||
triggerDescription: string = "";
|
||||
triggerType: TriggerType;
|
||||
codeTriggerValue = "";
|
||||
triggers: TriggerViewModel[] = [];
|
||||
|
||||
repository: TriggerRepository;
|
||||
|
||||
changeTriggerDescription(value: string): void {
|
||||
this.triggerDescription = value;
|
||||
}
|
||||
|
||||
deleteItem(id: string): void {
|
||||
this.triggers = this.triggers.filter((el) => el.id !== id);
|
||||
console.log(id);
|
||||
}
|
||||
|
||||
getTriggerType = (): boolean => {
|
||||
|
@ -39,7 +43,6 @@ class TriggerStore {
|
|||
: TriggerType.PROCESS;
|
||||
};
|
||||
pushTrigger = (value: string, type: TriggerType): void => {
|
||||
console.log(value);
|
||||
this.triggers.push({
|
||||
value: value,
|
||||
id: uuidv4(),
|
||||
|
@ -68,12 +71,15 @@ class TriggerStore {
|
|||
}
|
||||
}
|
||||
async saveResult(): Promise<void> {
|
||||
this.isLoading = true
|
||||
await this.repository.save({
|
||||
type: this.getTriggerDescription(),
|
||||
description: this.triggerDescription,
|
||||
value: this.triggers.map((el) => {
|
||||
return el.value;
|
||||
}),
|
||||
});
|
||||
this.isLoading = false
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
import * as React from "react";
|
||||
import { Button } from "antd";
|
||||
|
||||
|
||||
|
||||
export const PipelineScreenPath = '/pipeline_instance/:id'
|
||||
export const PipelineInstanceScreen: React.FunctionComponent = () => {
|
||||
return (
|
||||
<>
|
||||
|
||||
<Button></Button>
|
||||
</>
|
||||
);
|
||||
};
|
|
@ -0,0 +1,18 @@
|
|||
import { Result } from "../../../core/helper/result";
|
||||
import {
|
||||
HttpMethod,
|
||||
HttpRepository,
|
||||
} from "../../../core/repository/http_repository";
|
||||
import { IProjectModel } from "../model/project_model";
|
||||
|
||||
export class SelectProjectRepository extends HttpRepository {
|
||||
async getAllProjects(page = 1): Promise<Result<Error, IProjectModel[]>> {
|
||||
try {
|
||||
return Result.ok(
|
||||
await this.jsonRequest(HttpMethod.GET, `/project?${page}`)
|
||||
);
|
||||
} catch (error) {
|
||||
return Result.error(error as Error);
|
||||
}
|
||||
}
|
||||
}
|
8
ui/src/features/select_project/model/project_model.ts
Normal file
|
@ -0,0 +1,8 @@
|
|||
import { IProcess } from "../../create_process/model/process_model";
|
||||
|
||||
export interface IProjectModel {
|
||||
pipelines: [IProcess];
|
||||
rootDir: string;
|
||||
description: string;
|
||||
}
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
import * as React from "react";
|
||||
import { selectProjectStore } from "./select_project_store";
|
||||
import { Loader } from "../../../core/ui/loader/loader";
|
||||
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";
|
||||
|
||||
export const SelectProjectScreenPath = "/select_project";
|
||||
|
||||
export const SelectProjectScreen: React.FunctionComponent = observer(() => {
|
||||
return (
|
||||
<>
|
||||
<LoadPage
|
||||
path={CreatePipelineScreenPath}
|
||||
largeText={"Select project"}
|
||||
minText={"add new project?"}
|
||||
isLoading={selectProjectStore.isLoading}
|
||||
isError={selectProjectStore.isError}
|
||||
children={selectProjectStore.projects.map((el) => {
|
||||
return (
|
||||
<>
|
||||
<div>{el.description}</div>
|
||||
<div>+(РЕАЛИЗУЙ ТУТ ПЛЮСИК БЛЯТЬ ИЛИ КНОПКУ)</div>
|
||||
</>
|
||||
);
|
||||
})}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
});
|
|
@ -0,0 +1,35 @@
|
|||
import { makeAutoObservable } from "mobx";
|
||||
import { SelectProjectRepository } from "../data/select_project_repository";
|
||||
import { IProjectModel } from "../model/project_model";
|
||||
|
||||
class SelectProjectStore {
|
||||
repository: SelectProjectRepository;
|
||||
isLoading = false;
|
||||
isError = false;
|
||||
page = 1;
|
||||
projects: IProjectModel[] = [];
|
||||
|
||||
constructor(repository: SelectProjectRepository) {
|
||||
this.repository = repository;
|
||||
makeAutoObservable(this);
|
||||
this.getPipelines();
|
||||
}
|
||||
|
||||
async getPipelines(): Promise<void> {
|
||||
this.isLoading = true;
|
||||
const result = await this.repository.getAllProjects(this.page);
|
||||
result.fold(
|
||||
(s) => {
|
||||
this.projects = s;
|
||||
},
|
||||
(_e) => {
|
||||
this.isError = true;
|
||||
}
|
||||
);
|
||||
this.isLoading = false;
|
||||
}
|
||||
}
|
||||
|
||||
export const selectProjectStore = new SelectProjectStore(
|
||||
new SelectProjectRepository()
|
||||
);
|
33
ui/src/features/socket_lister/socket_lister.tsx
Normal file
|
@ -0,0 +1,33 @@
|
|||
import * as React from "react";
|
||||
import { ReactComponent as ReloadIcon } from "../../core/assets/icons/reload.svg";
|
||||
import { socketListerStore } from "./socket_lister_store";
|
||||
import { observer } from "mobx-react-lite";
|
||||
|
||||
export interface ISocketListerProps {
|
||||
children?: JSX.Element;
|
||||
}
|
||||
|
||||
export const SocketLister = observer((props: ISocketListerProps) => {
|
||||
return (
|
||||
<>
|
||||
{socketListerStore.socketDisconnect ? (
|
||||
<ReloadIcon
|
||||
onClick={() => {
|
||||
socketListerStore.reconnect();
|
||||
}}
|
||||
style={{
|
||||
height: "70px",
|
||||
backgroundColor: "silver",
|
||||
width: "-webkit-fill-available",
|
||||
padding: "10px",
|
||||
cursor: "pointer",
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
<></>
|
||||
)}
|
||||
|
||||
{props.children}
|
||||
</>
|
||||
);
|
||||
});
|
18
ui/src/features/socket_lister/socket_lister_store.ts
Normal file
|
@ -0,0 +1,18 @@
|
|||
import { makeAutoObservable } from "mobx";
|
||||
import { SocketRepository } from "../../core/repository/socket_repository";
|
||||
|
||||
class SocketListerStore {
|
||||
repository: SocketRepository;
|
||||
socketDisconnect = false;
|
||||
|
||||
constructor(repository: SocketRepository) {
|
||||
this.repository = repository;
|
||||
makeAutoObservable(this);
|
||||
}
|
||||
async reconnect() {
|
||||
await this.repository.connect()
|
||||
this.socketDisconnect = false
|
||||
}
|
||||
}
|
||||
|
||||
export const socketListerStore = new SocketListerStore(new SocketRepository());
|
|
@ -1,16 +1,22 @@
|
|||
import React from 'react';
|
||||
import ReactDOM from 'react-dom/client';
|
||||
import React from "react";
|
||||
import ReactDOM from "react-dom/client";
|
||||
|
||||
import "antd/dist/antd.min.css";
|
||||
import { RouterProvider } from "react-router-dom";
|
||||
|
||||
import { TriggerScreen } from './features/trigger/presentation/trigger_presentation';
|
||||
import "antd/dist/antd.css";
|
||||
import { router } from "./core/routers/routers";
|
||||
import { SocketLister } from "./features/socket_lister/socket_lister";
|
||||
import { extensions } from "./core/extensions/extensions";
|
||||
|
||||
extensions();
|
||||
const root = ReactDOM.createRoot(
|
||||
document.getElementById('root') as HTMLElement
|
||||
document.getElementById("root") as HTMLElement
|
||||
);
|
||||
|
||||
root.render(
|
||||
<React.StrictMode>
|
||||
<TriggerScreen />
|
||||
</React.StrictMode>
|
||||
<>
|
||||
<SocketLister>
|
||||
<RouterProvider router={router} />
|
||||
</SocketLister>
|
||||
</>
|
||||
);
|
||||
|
|
|
@ -1,11 +1,7 @@
|
|||
{
|
||||
"compilerOptions": {
|
||||
"target": "es5",
|
||||
"lib": [
|
||||
"dom",
|
||||
"dom.iterable",
|
||||
"esnext"
|
||||
],
|
||||
"lib": ["dom", "dom.iterable", "esnext"],
|
||||
"allowJs": true,
|
||||
"skipLibCheck": true,
|
||||
"esModuleInterop": true,
|
||||
|
@ -21,7 +17,5 @@
|
|||
"jsx": "react-jsx",
|
||||
"useDefineForClassFields": true
|
||||
},
|
||||
"include": [
|
||||
"src"
|
||||
]
|
||||
"include": ["src"]
|
||||
}
|
||||
|
|