feat: Add procedure and function management in flow controller
- Introduced new endpoints in FlowController for listing procedures and retrieving procedure parameters, enhancing the flow management capabilities. - Updated FlowDataMoveService to support procedure calls during data movement, ensuring seamless integration with external and internal databases. - Enhanced NodeFlowExecutionService to execute procedure call actions, allowing for dynamic execution of stored procedures within flow nodes. - Updated frontend components to support procedure selection and parameter management, improving user experience in configuring flow steps. - Added necessary types and API functions for handling procedure-related data, ensuring type safety and clarity in implementation.
This commit is contained in:
@@ -11,6 +11,7 @@
|
||||
import { query, queryOne, transaction } from "../database/db";
|
||||
import { logger } from "../utils/logger";
|
||||
import axios from "axios";
|
||||
import { FlowProcedureService } from "./flowProcedureService";
|
||||
|
||||
// ===== 타입 정의 =====
|
||||
|
||||
@@ -36,6 +37,7 @@ export type NodeType =
|
||||
| "emailAction" // 이메일 발송 액션
|
||||
| "scriptAction" // 스크립트 실행 액션
|
||||
| "httpRequestAction" // HTTP 요청 액션
|
||||
| "procedureCallAction" // 프로시저/함수 호출 액션
|
||||
| "comment"
|
||||
| "log";
|
||||
|
||||
@@ -663,6 +665,9 @@ export class NodeFlowExecutionService {
|
||||
case "httpRequestAction":
|
||||
return this.executeHttpRequestAction(node, inputData, context);
|
||||
|
||||
case "procedureCallAction":
|
||||
return this.executeProcedureCallAction(node, inputData, context, client);
|
||||
|
||||
case "comment":
|
||||
case "log":
|
||||
// 로그/코멘트는 실행 없이 통과
|
||||
@@ -4856,4 +4861,105 @@ export class NodeFlowExecutionService {
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 프로시저/함수 호출 액션 노드 실행
|
||||
*/
|
||||
private static async executeProcedureCallAction(
|
||||
node: FlowNode,
|
||||
inputData: any,
|
||||
context: ExecutionContext,
|
||||
client?: any
|
||||
): Promise<any> {
|
||||
const {
|
||||
dbSource = "internal",
|
||||
connectionId,
|
||||
procedureName,
|
||||
procedureSchema = "public",
|
||||
callType = "function",
|
||||
parameters = [],
|
||||
} = node.data;
|
||||
|
||||
logger.info(
|
||||
`🔧 프로시저 호출 노드 실행: ${node.data.displayName || node.id}`
|
||||
);
|
||||
logger.info(
|
||||
` 프로시저: ${procedureSchema}.${procedureName} (${callType}), DB: ${dbSource}`
|
||||
);
|
||||
|
||||
if (!procedureName) {
|
||||
throw new Error("프로시저/함수가 선택되지 않았습니다.");
|
||||
}
|
||||
|
||||
const dataArray = Array.isArray(inputData)
|
||||
? inputData
|
||||
: inputData
|
||||
? [inputData]
|
||||
: [{}];
|
||||
|
||||
const procedureService = new FlowProcedureService();
|
||||
const results: any[] = [];
|
||||
|
||||
const config = {
|
||||
type: "procedure" as const,
|
||||
dbSource: dbSource as "internal" | "external",
|
||||
connectionId,
|
||||
procedureName,
|
||||
procedureSchema,
|
||||
callType: callType as "procedure" | "function",
|
||||
parameters: parameters.map((p: any) => ({
|
||||
name: p.name,
|
||||
dataType: p.dataType,
|
||||
mode: p.mode || "IN",
|
||||
source: p.source || "static",
|
||||
field: p.field,
|
||||
value: p.value,
|
||||
})),
|
||||
};
|
||||
|
||||
for (const record of dataArray) {
|
||||
try {
|
||||
logger.info(` 입력 레코드 키: ${Object.keys(record).join(", ")}`);
|
||||
|
||||
const execResult = await procedureService.executeProcedure(
|
||||
config,
|
||||
record,
|
||||
dbSource === "internal" ? client : undefined
|
||||
);
|
||||
|
||||
logger.info(` ✅ 프로시저 실행 성공: ${procedureName}`);
|
||||
|
||||
// 프로시저 반환값을 레코드에 평탄화하여 다음 노드에서 필드로 참조 가능하게 함
|
||||
let flatResult: Record<string, any> = {};
|
||||
if (Array.isArray(execResult.result) && execResult.result.length > 0) {
|
||||
const row = execResult.result[0];
|
||||
for (const [key, val] of Object.entries(row)) {
|
||||
// 함수명과 동일한 키(SELECT fn() 결과)는 _procedureReturn으로 매핑
|
||||
if (key === procedureName) {
|
||||
flatResult["_procedureReturn"] = val;
|
||||
} else {
|
||||
flatResult[key] = val;
|
||||
}
|
||||
}
|
||||
logger.info(` 반환 필드: ${Object.keys(flatResult).join(", ")}`);
|
||||
}
|
||||
|
||||
results.push({
|
||||
...record,
|
||||
...flatResult,
|
||||
_procedureResult: execResult.result,
|
||||
_procedureSuccess: true,
|
||||
});
|
||||
} catch (error: any) {
|
||||
logger.error(` ❌ 프로시저 실행 실패: ${error.message}`);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
logger.info(
|
||||
`🔧 프로시저 호출 완료: ${results.length}건 처리`
|
||||
);
|
||||
|
||||
return results;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user