feat: implement function management with FunctionEntity, FunctionService, and FunctionController; enhance QueryExecuterService to utilize functions; refactor CommandController and QueryController to extend BaseQueryController; update Vm class to handle functions; remove obsolete log entities
This commit is contained in:
71
src/query/base/base-query.controller.ts
Normal file
71
src/query/base/base-query.controller.ts
Normal file
@ -0,0 +1,71 @@
|
||||
import {
|
||||
Body,
|
||||
Headers,
|
||||
Inject,
|
||||
Param,
|
||||
Post,
|
||||
Res,
|
||||
UseGuards,
|
||||
} from "@nestjs/common";
|
||||
import { Response } from "express";
|
||||
import { QueryHandlerService } from "../handler/query.handler.service";
|
||||
import { ApiTokenGuard } from "src/api/guards/api-token.guard";
|
||||
import { QueryExecuterService } from "../executer/query.executer.service";
|
||||
|
||||
@UseGuards(ApiTokenGuard)
|
||||
export abstract class BaseQueryController {
|
||||
constructor(
|
||||
@Inject(QueryHandlerService)
|
||||
protected readonly queryHandlerService: QueryHandlerService,
|
||||
@Inject(QueryExecuterService)
|
||||
protected readonly queryExecuterService: QueryExecuterService
|
||||
) {}
|
||||
|
||||
protected abstract getIsCommand(): boolean;
|
||||
|
||||
@Post("create")
|
||||
async createQuery(
|
||||
@Body() queryData: { projectToken: string; source: string }
|
||||
) {
|
||||
return this.queryHandlerService.createQuery(queryData, this.getIsCommand());
|
||||
}
|
||||
|
||||
@Post("update/:id")
|
||||
async updateQuery(
|
||||
@Body() updateData: Partial<{ source: string }>,
|
||||
@Inject("id") id: string
|
||||
) {
|
||||
return this.queryHandlerService.updateQuery(id, updateData);
|
||||
}
|
||||
|
||||
@Post("/run/:token")
|
||||
async runQuery(
|
||||
@Param("token") token: string,
|
||||
@Body() query: Record<string, any>,
|
||||
@Headers() headers: Record<string, any>,
|
||||
@Res() res: Response
|
||||
) {
|
||||
const queryResult = await this.queryExecuterService.runQueryQueued(
|
||||
token,
|
||||
query,
|
||||
headers
|
||||
);
|
||||
|
||||
res.status(queryResult?.statusCode || 200);
|
||||
|
||||
if (
|
||||
queryResult?.statusCode === 302 &&
|
||||
queryResult?.headers &&
|
||||
queryResult?.headers["Location"]
|
||||
) {
|
||||
res.redirect(queryResult?.headers["Location"]);
|
||||
return;
|
||||
}
|
||||
|
||||
for (const [key, value] of Object.entries(queryResult?.headers || {})) {
|
||||
res.setHeader(key, value);
|
||||
}
|
||||
|
||||
res.send(queryResult?.response || null);
|
||||
}
|
||||
}
|
||||
@ -1,67 +1,20 @@
|
||||
import {
|
||||
Body,
|
||||
Controller,
|
||||
Headers,
|
||||
Inject,
|
||||
Param,
|
||||
Post,
|
||||
Res,
|
||||
UseGuards,
|
||||
} from "@nestjs/common";
|
||||
import { Controller, Inject } from "@nestjs/common";
|
||||
import { QueryHandlerService } from "../handler/query.handler.service";
|
||||
import { QueryExecuterService } from "../executer/query.executer.service";
|
||||
import { Response } from "express";
|
||||
import { ApiTokenGuard } from "src/api/guards/api-token.guard";
|
||||
import { BaseQueryController } from "../base/base-query.controller";
|
||||
|
||||
@Controller("command")
|
||||
@UseGuards(ApiTokenGuard)
|
||||
export class CommandController {
|
||||
export class CommandController extends BaseQueryController {
|
||||
constructor(
|
||||
@Inject(QueryHandlerService)
|
||||
private readonly queryHandlerService: QueryHandlerService,
|
||||
queryHandlerService: QueryHandlerService,
|
||||
@Inject(QueryExecuterService)
|
||||
private readonly queryExecuterService: QueryExecuterService
|
||||
) {}
|
||||
|
||||
@Post("create")
|
||||
async createQuery(
|
||||
@Body() queryData: { projectToken: string; source: string }
|
||||
queryExecuterService: QueryExecuterService
|
||||
) {
|
||||
return this.queryHandlerService.createQuery(queryData, true);
|
||||
super(queryHandlerService, queryExecuterService);
|
||||
}
|
||||
|
||||
@Post("update/:id")
|
||||
async updateQuery(
|
||||
@Body() updateData: Partial<{ source: string }>,
|
||||
@Inject("id") id: string
|
||||
) {
|
||||
return this.queryHandlerService.updateQuery(id, updateData);
|
||||
}
|
||||
|
||||
@Post("/run/:token")
|
||||
async runQuery(
|
||||
@Param("token") token: string,
|
||||
@Body() query: Record<string, any>,
|
||||
@Headers() headers: Record<string, any>,
|
||||
@Res() res: Response
|
||||
) {
|
||||
const queryResult = await this.queryExecuterService.runQueryQueued(
|
||||
token,
|
||||
query,
|
||||
headers
|
||||
);
|
||||
|
||||
res.status(queryResult.statusCode);
|
||||
|
||||
if (queryResult.statusCode === 302 && queryResult.headers["Location"]) {
|
||||
res.redirect(queryResult.headers["Location"]);
|
||||
return;
|
||||
}
|
||||
|
||||
for (const [key, value] of Object.entries(queryResult.headers)) {
|
||||
res.setHeader(key, value);
|
||||
}
|
||||
|
||||
res.send(queryResult.response);
|
||||
protected getIsCommand(): boolean {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
24
src/query/entities/function.entity.ts
Normal file
24
src/query/entities/function.entity.ts
Normal file
@ -0,0 +1,24 @@
|
||||
import { Project } from "../../project/entities/project.entity";
|
||||
import {
|
||||
Column,
|
||||
Entity,
|
||||
Index,
|
||||
ManyToOne,
|
||||
PrimaryGeneratedColumn,
|
||||
} from "typeorm";
|
||||
|
||||
@Entity("function")
|
||||
@Index("IDX_FUNCTION_NAME_PROJECT", ["name", "project"], { unique: true })
|
||||
export class FunctionEntity {
|
||||
@PrimaryGeneratedColumn("uuid")
|
||||
id: string;
|
||||
|
||||
@Column({ type: "varchar", length: 255 })
|
||||
name: string;
|
||||
|
||||
@Column({ type: "longtext" })
|
||||
source: string;
|
||||
|
||||
@ManyToOne(() => Project, (project) => project.queries)
|
||||
project: Project;
|
||||
}
|
||||
@ -1,4 +1,4 @@
|
||||
import { Injectable } from "@nestjs/common";
|
||||
import { Inject, Injectable } from "@nestjs/common";
|
||||
import { InjectRepository } from "@nestjs/typeorm";
|
||||
import { Query } from "../entities/query.entity";
|
||||
import { Repository } from "typeorm";
|
||||
@ -13,6 +13,7 @@ import { DatabaseManagerService } from "src/databaseManager/database/database.ma
|
||||
import { InjectQueue } from "@nestjs/bullmq";
|
||||
import { QUEUE_NAMES } from "src/queue/constants";
|
||||
import { Queue, QueueEvents } from "bullmq";
|
||||
import { FunctionService } from "src/project/function/function.service";
|
||||
|
||||
@Injectable()
|
||||
export class QueryExecuterService {
|
||||
@ -21,6 +22,8 @@ export class QueryExecuterService {
|
||||
constructor(
|
||||
@InjectRepository(Query)
|
||||
readonly queryRepository: Repository<Query>,
|
||||
@Inject(FunctionService)
|
||||
private readonly functionService: FunctionService,
|
||||
readonly databaseManagerService: DatabaseManagerService,
|
||||
@InjectQueue(QUEUE_NAMES.QUERY) private queryQueue: Queue
|
||||
) {
|
||||
@ -110,6 +113,16 @@ export class QueryExecuterService {
|
||||
|
||||
const moduleNames = importsParsed.filter((imp) => imp.type === "module");
|
||||
const pluginNames = importsParsed.filter((imp) => imp.type === "plugin");
|
||||
const functionNames = importsParsed.filter(
|
||||
(imp) => imp.type === "function"
|
||||
);
|
||||
|
||||
const functions = (
|
||||
await this.functionService.findByNames(
|
||||
query.project.id,
|
||||
functionNames.map((fn) => fn.name)
|
||||
)
|
||||
).map((fn) => fn.source);
|
||||
|
||||
const modules = moduleNames.map((mod) => {
|
||||
if (registeredModules[mod.name]) {
|
||||
@ -134,24 +147,18 @@ export class QueryExecuterService {
|
||||
cpuTimeLimit: BigInt(5e9),
|
||||
modules: modules,
|
||||
plugins: plugins,
|
||||
functions: functions,
|
||||
});
|
||||
|
||||
return await vm.init();
|
||||
}
|
||||
|
||||
private checkResponse(obj: any): obj is QueryResponse {
|
||||
return (
|
||||
obj !== null &&
|
||||
typeof obj === "object" &&
|
||||
typeof obj.statusCode === "number" &&
|
||||
typeof obj.response === "object" &&
|
||||
obj.response !== null &&
|
||||
typeof obj.headers === "object" &&
|
||||
obj.headers !== null &&
|
||||
Object.keys(obj.headers).every(
|
||||
(key) => typeof obj.headers[key] === "string"
|
||||
)
|
||||
);
|
||||
private checkResponse(obj: any): boolean {
|
||||
if (obj?.statusCode && obj.response && typeof obj.statusCode === "number") {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
async onModuleDestroy() {
|
||||
|
||||
@ -1,67 +1,20 @@
|
||||
import {
|
||||
Body,
|
||||
Controller,
|
||||
Headers,
|
||||
Inject,
|
||||
Param,
|
||||
Post,
|
||||
Res,
|
||||
UseGuards,
|
||||
} from "@nestjs/common";
|
||||
import { Response } from "express";
|
||||
import { Controller, Inject } from "@nestjs/common";
|
||||
import { QueryHandlerService } from "./query.handler.service";
|
||||
import { ApiTokenGuard } from "src/api/guards/api-token.guard";
|
||||
import { QueryExecuterService } from "../executer/query.executer.service";
|
||||
import { BaseQueryController } from "../base/base-query.controller";
|
||||
|
||||
@Controller("query")
|
||||
@UseGuards(ApiTokenGuard)
|
||||
export class QueryController {
|
||||
export class QueryController extends BaseQueryController {
|
||||
constructor(
|
||||
@Inject(QueryHandlerService)
|
||||
private readonly queryHandlerService: QueryHandlerService,
|
||||
queryHandlerService: QueryHandlerService,
|
||||
@Inject(QueryExecuterService)
|
||||
private readonly queryExecuterService: QueryExecuterService
|
||||
) {}
|
||||
|
||||
@Post("create")
|
||||
async createQuery(
|
||||
@Body() queryData: { projectToken: string; source: string }
|
||||
queryExecuterService: QueryExecuterService
|
||||
) {
|
||||
return this.queryHandlerService.createQuery(queryData);
|
||||
super(queryHandlerService, queryExecuterService);
|
||||
}
|
||||
|
||||
@Post("update/:id")
|
||||
async updateQuery(
|
||||
@Body() updateData: Partial<{ source: string }>,
|
||||
@Inject("id") id: string
|
||||
) {
|
||||
return this.queryHandlerService.updateQuery(id, updateData);
|
||||
}
|
||||
|
||||
@Post("/run/:token")
|
||||
async runQuery(
|
||||
@Param("token") token: string,
|
||||
@Body() query: Record<string, any>,
|
||||
@Headers() headers: Record<string, any>,
|
||||
@Res() res: Response
|
||||
) {
|
||||
const queryResult = await this.queryExecuterService.runQueryQueued(
|
||||
token,
|
||||
query,
|
||||
headers
|
||||
);
|
||||
|
||||
res.status(queryResult.statusCode);
|
||||
|
||||
if (queryResult.statusCode === 302 && queryResult.headers["Location"]) {
|
||||
res.redirect(queryResult.headers["Location"]);
|
||||
return;
|
||||
}
|
||||
|
||||
for (const [key, value] of Object.entries(queryResult.headers)) {
|
||||
res.setHeader(key, value);
|
||||
}
|
||||
|
||||
res.send(queryResult.response);
|
||||
protected getIsCommand(): boolean {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,20 +0,0 @@
|
||||
import { Column, Entity, PrimaryGeneratedColumn } from "typeorm";
|
||||
import { LogRecord } from "../logs.type";
|
||||
|
||||
@Entity("logs")
|
||||
export class LogEntity {
|
||||
@PrimaryGeneratedColumn("uuid")
|
||||
id: string;
|
||||
|
||||
@Column({
|
||||
type: "longtext",
|
||||
nullable: false,
|
||||
transformer: {
|
||||
to: (value: any) => JSON.stringify(value),
|
||||
from: (value: any) => JSON.parse(value),
|
||||
},
|
||||
})
|
||||
record: LogRecord;
|
||||
|
||||
// TODO: projectId
|
||||
}
|
||||
@ -1,12 +0,0 @@
|
||||
export type LogLevel = "DEBUG" | "INFO" | "WARN" | "ERROR" | "FATAL";
|
||||
|
||||
export type LogLine = {
|
||||
timestamp: string;
|
||||
message: string;
|
||||
level: LogLevel;
|
||||
};
|
||||
|
||||
export type LogRecord = {
|
||||
id: string;
|
||||
lines: LogLine[];
|
||||
};
|
||||
@ -9,6 +9,9 @@ import { DatabaseManagerModule } from "src/databaseManager/database.manager.modu
|
||||
import { CommandController } from "./command/command.controller";
|
||||
import { ApiModule } from "src/api/api.module";
|
||||
import { QueueModule } from "src/queue/queue.module";
|
||||
import { FunctionEntity } from "./entities/function.entity";
|
||||
import { FunctionService } from "src/project/function/function.service";
|
||||
import { FunctionController } from "src/project/function/function.controller";
|
||||
|
||||
@Module({
|
||||
imports: [
|
||||
@ -16,10 +19,10 @@ import { QueueModule } from "src/queue/queue.module";
|
||||
forwardRef(() => DatabaseManagerModule),
|
||||
forwardRef(() => ApiModule),
|
||||
forwardRef(() => QueueModule),
|
||||
TypeOrmModule.forFeature([Query]),
|
||||
TypeOrmModule.forFeature([Query, FunctionEntity]),
|
||||
],
|
||||
controllers: [QueryController, CommandController],
|
||||
providers: [QueryExecuterService, QueryHandlerService],
|
||||
controllers: [QueryController, CommandController, FunctionController],
|
||||
providers: [QueryExecuterService, QueryHandlerService, FunctionService],
|
||||
exports: [QueryExecuterService, TypeOrmModule],
|
||||
})
|
||||
export class QueryModule {}
|
||||
|
||||
Reference in New Issue
Block a user