forked from kimhyungsik/ax_hub_mcp_tool
fix: ToolSourceUpdater bug and add docker volume mount
This commit is contained in:
@@ -44,7 +44,10 @@ public class PodScaffolder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static String scaffoldPod(String moduleName, String portStr, String shortName, String author, String createDate) throws IOException {
|
public static String scaffoldPod(String moduleName, String portStr, String shortName, String author, String createDate) throws IOException {
|
||||||
Path modulePath = Paths.get(moduleName);
|
String envSourceDir = System.getenv("AXHUB_SOURCE_DIR");
|
||||||
|
Path rootDir = envSourceDir != null ? Paths.get(envSourceDir) : Paths.get(".");
|
||||||
|
|
||||||
|
Path modulePath = rootDir.resolve(Paths.get(moduleName));
|
||||||
if (Files.exists(modulePath)) {
|
if (Files.exists(modulePath)) {
|
||||||
return "[오류] 이미 존재하는 모듈입니다: " + moduleName;
|
return "[오류] 이미 존재하는 모듈입니다: " + moduleName;
|
||||||
}
|
}
|
||||||
@@ -202,7 +205,7 @@ public class PodScaffolder {
|
|||||||
Files.writeString(resPath.resolve("logback-spring.xml"), logbackXml);
|
Files.writeString(resPath.resolve("logback-spring.xml"), logbackXml);
|
||||||
|
|
||||||
log.append("[5/6] settings.gradle 에 모듈 등록 중...\n");
|
log.append("[5/6] settings.gradle 에 모듈 등록 중...\n");
|
||||||
Path settingsPath = Paths.get("settings.gradle");
|
Path settingsPath = rootDir.resolve(Paths.get("settings.gradle"));
|
||||||
if (Files.exists(settingsPath)) {
|
if (Files.exists(settingsPath)) {
|
||||||
String settings = Files.readString(settingsPath);
|
String settings = Files.readString(settingsPath);
|
||||||
if (!settings.contains("include '" + moduleName + "'")) {
|
if (!settings.contains("include '" + moduleName + "'")) {
|
||||||
@@ -211,7 +214,7 @@ public class PodScaffolder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
log.append("[6/6] docker-compose.yml 에 서비스 추가 중...\n");
|
log.append("[6/6] docker-compose.yml 에 서비스 추가 중...\n");
|
||||||
Path dockerComposePath = Paths.get("docker-compose.yml");
|
Path dockerComposePath = rootDir.resolve(Paths.get("docker-compose.yml"));
|
||||||
if (Files.exists(dockerComposePath)) {
|
if (Files.exists(dockerComposePath)) {
|
||||||
String compose = Files.readString(dockerComposePath);
|
String compose = Files.readString(dockerComposePath);
|
||||||
String serviceName = moduleName.replace("axhub-", ""); // e.g. tool-payment
|
String serviceName = moduleName.replace("axhub-", ""); // e.g. tool-payment
|
||||||
|
|||||||
@@ -81,8 +81,11 @@ public class ToolScaffolder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static String scaffold(String baseName, String interfaceId, String description, String group, String routingType, String moduleName, String author, String createDate, boolean register) throws IOException {
|
public static String scaffold(String baseName, String interfaceId, String description, String group, String routingType, String moduleName, String author, String createDate, boolean register) throws IOException {
|
||||||
Path serviceDir = Paths.get(moduleName, BASE_PACKAGE_PATH, "service");
|
String envSourceDir = System.getenv("AXHUB_SOURCE_DIR");
|
||||||
Path dtoDir = Paths.get(moduleName, BASE_PACKAGE_PATH, "dto");
|
Path rootDir = envSourceDir != null ? Paths.get(envSourceDir) : Paths.get(".");
|
||||||
|
|
||||||
|
Path serviceDir = rootDir.resolve(Paths.get(moduleName, BASE_PACKAGE_PATH, "service"));
|
||||||
|
Path dtoDir = rootDir.resolve(Paths.get(moduleName, BASE_PACKAGE_PATH, "dto"));
|
||||||
|
|
||||||
Files.createDirectories(serviceDir);
|
Files.createDirectories(serviceDir);
|
||||||
Files.createDirectories(dtoDir);
|
Files.createDirectories(dtoDir);
|
||||||
@@ -204,7 +207,7 @@ public class ToolScaffolder {
|
|||||||
Files.writeString(serviceDir.resolve(baseName + "Service.java"), serviceContent);
|
Files.writeString(serviceDir.resolve(baseName + "Service.java"), serviceContent);
|
||||||
|
|
||||||
// Append to YAML
|
// Append to YAML
|
||||||
Path resourcesDir = Paths.get(moduleName, "src/main/resources");
|
Path resourcesDir = rootDir.resolve(Paths.get(moduleName, "src/main/resources"));
|
||||||
Files.createDirectories(resourcesDir);
|
Files.createDirectories(resourcesDir);
|
||||||
Path yamlPath = resourcesDir.resolve("application-local.yml");
|
Path yamlPath = resourcesDir.resolve("application-local.yml");
|
||||||
|
|
||||||
|
|||||||
@@ -14,7 +14,8 @@ public class ToolSourceUpdater {
|
|||||||
|
|
||||||
public static void updateToolSource(String toolName, String domainGroup, String description, boolean register) throws Exception {
|
public static void updateToolSource(String toolName, String domainGroup, String description, boolean register) throws Exception {
|
||||||
// 1. Find all *Service.java files in axhub-tool-* directories
|
// 1. Find all *Service.java files in axhub-tool-* directories
|
||||||
Path rootDir = Paths.get(".");
|
String envSourceDir = System.getenv("AXHUB_SOURCE_DIR");
|
||||||
|
Path rootDir = envSourceDir != null ? Paths.get(envSourceDir) : Paths.get(".");
|
||||||
|
|
||||||
List<Path> javaFiles;
|
List<Path> javaFiles;
|
||||||
try (Stream<Path> paths = Files.walk(rootDir)) {
|
try (Stream<Path> paths = Files.walk(rootDir)) {
|
||||||
@@ -29,7 +30,13 @@ public class ToolSourceUpdater {
|
|||||||
String content = null;
|
String content = null;
|
||||||
|
|
||||||
// 2. Find the specific file for the tool
|
// 2. Find the specific file for the tool
|
||||||
Pattern namePattern = Pattern.compile("@McpFunction\\\\s*\\\\([^)]*name\\\\s*=\\\\s*\\\"" + Pattern.quote(toolName) + "\\\"", Pattern.DOTALL);
|
String functionName = toolName;
|
||||||
|
if (toolName.contains("_")) {
|
||||||
|
functionName = toolName.substring(toolName.indexOf("_") + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
Pattern namePattern = Pattern.compile("@McpFunction\\s*\\([^)]*name\\s*=\\s*\"" + Pattern.quote(toolName) + "\"", Pattern.DOTALL);
|
||||||
|
Pattern namePattern2 = Pattern.compile("@McpFunction\\s*\\([^)]*name\\s*=\\s*\"" + Pattern.quote(functionName) + "\"", Pattern.DOTALL);
|
||||||
|
|
||||||
for (Path path : javaFiles) {
|
for (Path path : javaFiles) {
|
||||||
String text = Files.readString(path);
|
String text = Files.readString(path);
|
||||||
@@ -37,6 +44,11 @@ public class ToolSourceUpdater {
|
|||||||
targetFile = path;
|
targetFile = path;
|
||||||
content = text;
|
content = text;
|
||||||
break;
|
break;
|
||||||
|
} else if (namePattern2.matcher(text).find()) {
|
||||||
|
targetFile = path;
|
||||||
|
content = text;
|
||||||
|
toolName = functionName; // Use baseName for subsequent replacements
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -46,7 +58,7 @@ public class ToolSourceUpdater {
|
|||||||
|
|
||||||
// 3. Update @McpTool group
|
// 3. Update @McpTool group
|
||||||
if (domainGroup != null && !domainGroup.trim().isEmpty()) {
|
if (domainGroup != null && !domainGroup.trim().isEmpty()) {
|
||||||
Pattern groupPattern = Pattern.compile("(@McpTool\\\\s*\\\\([^)]*group\\\\s*=\\\\s*\\\")([^\\\"]+)(\\\")", Pattern.DOTALL);
|
Pattern groupPattern = Pattern.compile("(@McpTool\\s*\\([^)]*group\\s*=\\s*\")([^\"]+)(\")", Pattern.DOTALL);
|
||||||
Matcher groupMatcher = groupPattern.matcher(content);
|
Matcher groupMatcher = groupPattern.matcher(content);
|
||||||
if (groupMatcher.find()) {
|
if (groupMatcher.find()) {
|
||||||
content = groupMatcher.replaceFirst("$1" + domainGroup + "$3");
|
content = groupMatcher.replaceFirst("$1" + domainGroup + "$3");
|
||||||
@@ -55,7 +67,7 @@ public class ToolSourceUpdater {
|
|||||||
|
|
||||||
// 4. Update @McpFunction description
|
// 4. Update @McpFunction description
|
||||||
if (description != null) {
|
if (description != null) {
|
||||||
Pattern funcPattern = Pattern.compile("(@McpFunction\\\\s*\\\\([^)]*name\\\\s*=\\\\s*\\\"" + Pattern.quote(toolName) + "\\\"[^)]*description\\\\s*=\\\\s*\\\")([^\\\"]+)(\\\")", Pattern.DOTALL);
|
Pattern funcPattern = Pattern.compile("(@McpFunction\\s*\\([^)]*name\\s*=\\s*\"" + Pattern.quote(toolName) + "\"[^)]*description\\s*=\\s*\")([^\"]+)(\")", Pattern.DOTALL);
|
||||||
Matcher funcMatcher = funcPattern.matcher(content);
|
Matcher funcMatcher = funcPattern.matcher(content);
|
||||||
if (funcMatcher.find()) {
|
if (funcMatcher.find()) {
|
||||||
content = funcMatcher.replaceFirst("$1" + description.replace("\\", "\\\\").replace("$", "\\\\$") + "$3");
|
content = funcMatcher.replaceFirst("$1" + description.replace("\\", "\\\\").replace("$", "\\\\$") + "$3");
|
||||||
@@ -63,12 +75,12 @@ public class ToolSourceUpdater {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 5. Update register flag
|
// 5. Update register flag
|
||||||
Pattern regPattern = Pattern.compile("(@McpFunction\\\\s*\\\\([^)]*name\\\\s*=\\\\s*\\\"" + Pattern.quote(toolName) + "\\\"[^)]*register\\\\s*=\\\\s*)(true|false)([^a-zA-Z0-9])", Pattern.DOTALL);
|
Pattern regPattern = Pattern.compile("(@McpFunction\\s*\\([^)]*name\\s*=\\s*\"" + Pattern.quote(toolName) + "\"[^)]*register\\s*=\\s*)(true|false)([^a-zA-Z0-9])", Pattern.DOTALL);
|
||||||
Matcher regMatcher = regPattern.matcher(content);
|
Matcher regMatcher = regPattern.matcher(content);
|
||||||
if (regMatcher.find()) {
|
if (regMatcher.find()) {
|
||||||
content = regMatcher.replaceFirst("$1" + register + "$3");
|
content = regMatcher.replaceFirst("$1" + register + "$3");
|
||||||
} else {
|
} else {
|
||||||
Pattern addRegPattern = Pattern.compile("(@McpFunction\\\\s*\\\\([^)]*name\\\\s*=\\\\s*\\\"" + Pattern.quote(toolName) + "\\\")", Pattern.DOTALL);
|
Pattern addRegPattern = Pattern.compile("(@McpFunction\\s*\\([^)]*name\\s*=\\s*\"" + Pattern.quote(toolName) + "\")", Pattern.DOTALL);
|
||||||
Matcher addRegMatcher = addRegPattern.matcher(content);
|
Matcher addRegMatcher = addRegPattern.matcher(content);
|
||||||
if (addRegMatcher.find()) {
|
if (addRegMatcher.find()) {
|
||||||
content = addRegMatcher.replaceFirst("$1, register = " + register);
|
content = addRegMatcher.replaceFirst("$1, register = " + register);
|
||||||
|
|||||||
@@ -0,0 +1,50 @@
|
|||||||
|
package io.shinhanlife.glow.util;
|
||||||
|
|
||||||
|
import io.shinhanlife.glow.GlowMciFieldInfo;
|
||||||
|
import org.junit.jupiter.api.DisplayName;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||||
|
|
||||||
|
public class GlowMciParserTest {
|
||||||
|
|
||||||
|
public static class DummyMciDto {
|
||||||
|
@GlowMciFieldInfo(order = 1, length = 10)
|
||||||
|
private String customerId;
|
||||||
|
|
||||||
|
@GlowMciFieldInfo(order = 2, length = 15)
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
@GlowMciFieldInfo(order = 3, length = 3)
|
||||||
|
private int age;
|
||||||
|
|
||||||
|
public String getCustomerId() { return customerId; }
|
||||||
|
public String getName() { return name; }
|
||||||
|
public int getAge() { return age; }
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("고정 길이 MCI 문자열을 DTO로 파싱하는 테스트")
|
||||||
|
public void testParseFixedLengthString() {
|
||||||
|
// given: 고정 길이 텍스트 (총 28자리)
|
||||||
|
// ID(10) + Name(15) + Age(3)
|
||||||
|
String rawMciString = "CUST000001KIM SHINHAN 035";
|
||||||
|
|
||||||
|
// when
|
||||||
|
DummyMciDto result = GlowMciParser.parse(rawMciString, DummyMciDto.class);
|
||||||
|
|
||||||
|
// then
|
||||||
|
System.out.println("==================================================");
|
||||||
|
System.out.println("✅ [원본 MCI 전문] : [" + rawMciString + "]");
|
||||||
|
System.out.println("✅ [파싱된 ID (10자리)] : [" + result.getCustomerId() + "]");
|
||||||
|
System.out.println("✅ [파싱된 Name (15자리)] : [" + result.getName() + "]");
|
||||||
|
System.out.println("✅ [파싱된 Age (3자리)] : [" + result.getAge() + "]");
|
||||||
|
System.out.println("==================================================");
|
||||||
|
|
||||||
|
assertNotNull(result);
|
||||||
|
assertEquals("CUST000001", result.getCustomerId());
|
||||||
|
assertEquals("KIM SHINHAN", result.getName());
|
||||||
|
assertEquals(35, result.getAge());
|
||||||
|
}
|
||||||
|
}
|
||||||
4
axhub-tool-hr/Dockerfile
Normal file
4
axhub-tool-hr/Dockerfile
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
FROM eclipse-temurin:21-jdk-alpine
|
||||||
|
WORKDIR /app
|
||||||
|
COPY build/libs/axhub-tool-hr-0.0.1-SNAPSHOT.jar app.jar
|
||||||
|
ENTRYPOINT ["java", "-jar", "app.jar"]
|
||||||
10
axhub-tool-hr/build.gradle
Normal file
10
axhub-tool-hr/build.gradle
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
plugins {
|
||||||
|
id 'org.springframework.boot'
|
||||||
|
}
|
||||||
|
dependencies {
|
||||||
|
implementation project(':axhub-tool-core')
|
||||||
|
}
|
||||||
|
dependencies {
|
||||||
|
compileOnly 'org.projectlombok:lombok:1.18.32'
|
||||||
|
annotationProcessor 'org.projectlombok:lombok:1.18.32'
|
||||||
|
}
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
package io.shinhanlife.axhub.biz.mcp.tool.hr;
|
||||||
|
|
||||||
|
import org.springframework.boot.SpringApplication;
|
||||||
|
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||||
|
import org.springframework.cache.annotation.EnableCaching;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @package io.shinhanlife.axhub.biz.mcp.tool.hr
|
||||||
|
* @className HrToolApplication
|
||||||
|
* @description AX HUB 시스템 처리 클래스
|
||||||
|
* @author
|
||||||
|
* @create
|
||||||
|
* <pre>
|
||||||
|
* ---------- 개정이력 ----------
|
||||||
|
* 수정일 수정자 수정내용
|
||||||
|
* ---------- -------- ---------------------------
|
||||||
|
* 최초생성
|
||||||
|
*
|
||||||
|
* </pre>
|
||||||
|
*/
|
||||||
|
@SpringBootApplication(scanBasePackages = {"io.shinhanlife.axhub.biz.mcp.tool", "io.shinhanlife.axhub.biz.mcp.adapter", "io.shinhanlife.axhub.common.mcp", "io.shinhanlife.axhub.common.config"})
|
||||||
|
@org.springframework.boot.context.properties.ConfigurationPropertiesScan(basePackages = {"io.shinhanlife.axhub.biz.mcp.tool", "io.shinhanlife.axhub.biz.mcp.adapter", "io.shinhanlife.axhub.common.mcp", "io.shinhanlife.axhub.common.config"})
|
||||||
|
@EnableCaching
|
||||||
|
public class HrToolApplication {
|
||||||
|
public static void main(String[] args) {
|
||||||
|
SpringApplication.run(HrToolApplication.class, args);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
# Local 환경 전용 설정 (H2 메모리 DB 등)
|
||||||
|
spring.datasource.url=jdbc:p6spy:h2:mem:testdb;DB_CLOSE_DELAY=-1;
|
||||||
|
spring.datasource.driverClassName=com.p6spy.engine.spy.P6SpyDriver
|
||||||
|
spring.datasource.username=sa
|
||||||
|
spring.datasource.password=password
|
||||||
|
|
||||||
|
spring.h2.console.enabled=true
|
||||||
|
# EIMS 동적 라우팅 접속 정보
|
||||||
|
eims.http.url=http://localhost:${server.port}/api/gateway
|
||||||
|
eims.tcp.host=127.0.0.1
|
||||||
|
eims.tcp.port=8090
|
||||||
|
eims.tcp.timeout=5000
|
||||||
|
eims.jsp.form.url=http://localhost:${server.port}/mock/jsp-form
|
||||||
|
eims.jsp.json.url=http://localhost:${server.port}/mock/jsp-json
|
||||||
|
eims.mci.url=http://localhost:${server.port}/api/mock/esb/api
|
||||||
|
eims.mcistring.url=http://localhost:${server.port}/api/mock/esb/string
|
||||||
|
|
||||||
|
# API 보안 키 설정
|
||||||
|
mcp.security.api-keys.SHINHAN_MCP_SECRET_KEY_2026=mcp-client-1
|
||||||
|
mcp.security.api-keys.SHINHAN_MCP_TEST_KEY_9999=mcp-client-2
|
||||||
|
mcp.security.tenant-domains.mcp-client-1=CUSTOMER,COMMON
|
||||||
|
mcp.security.tenant-domains.mcp-client-2=ALL
|
||||||
|
|
||||||
|
# Gateway/Tool URLs
|
||||||
|
axhub.gateway.url=http://localhost:8081
|
||||||
|
axhub.tool.url=http://localhost:${server.port}
|
||||||
|
|
||||||
|
# Disable Kafka
|
||||||
|
spring.autoconfigure.exclude=org.springframework.boot.autoconfigure.kafka.KafkaAutoConfiguration
|
||||||
|
|
||||||
|
# Suppress Kafka Connection Logs
|
||||||
|
logging.level.org.apache.kafka=ERROR
|
||||||
12
axhub-tool-hr/src/main/resources/application-local.yml
Normal file
12
axhub-tool-hr/src/main/resources/application-local.yml
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
spring:
|
||||||
|
application:
|
||||||
|
name: axhub-tool-hr
|
||||||
|
config:
|
||||||
|
import: "classpath:config/application-glow-local.yml"
|
||||||
|
|
||||||
|
server:
|
||||||
|
port: 8086
|
||||||
|
|
||||||
|
axhub:
|
||||||
|
tool:
|
||||||
|
url: "http://tool-hr:8086"
|
||||||
2
axhub-tool-hr/src/main/resources/application.properties
Normal file
2
axhub-tool-hr/src/main/resources/application.properties
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
spring.profiles.active=local
|
||||||
|
mcp.namespace=hr
|
||||||
@@ -25,12 +25,12 @@ import io.shinhanlife.axhub.biz.mcp.tool.dto.BondIssueReq;
|
|||||||
*/
|
*/
|
||||||
public class BondIssueService extends AbstractMcpToolService {
|
public class BondIssueService extends AbstractMcpToolService {
|
||||||
|
|
||||||
@McpFunction(name = "check", description = "발행 가능 여부 조회", prompt = "디지털 증권 발행 한도가 충분한지 확인해줘.", mappingId = "BOND_001")
|
@McpFunction(name = "check", register = true, description = "발행 가능 여부 조회 테스트", prompt = "디지털 증권 발행 한도가 충분한지 확인해줘.", mappingId = "BOND_001")
|
||||||
public Object check(BondCheckReq data) {
|
public Object check(BondCheckReq data) {
|
||||||
return executeLegacy("EAI", "BOND_001", data);
|
return executeLegacy("EAI", "BOND_001", data);
|
||||||
}
|
}
|
||||||
|
|
||||||
@McpFunction(name = "issue", description = "증권 발행", prompt = "디지털 증권 발행 프로세스를 실행해.", mappingId = "BOND_002")
|
@McpFunction(name = "issue", register = true, description = "증권 발행 테스트", prompt = "디지털 증권 발행 프로세스를 실행해.", mappingId = "BOND_002")
|
||||||
public Object issue(BondIssueReq data) {
|
public Object issue(BondIssueReq data) {
|
||||||
return executeLegacy("EAI", "BOND_002", data);
|
return executeLegacy("EAI", "BOND_002", data);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -36,4 +36,9 @@ public class CommonUtilityService extends AbstractMcpToolService {
|
|||||||
return executeLegacy("HTTP", "HR_VAC_02", data);
|
return executeLegacy("HTTP", "HR_VAC_02", data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@McpFunction(name = "secret_tool", description = "비공개 툴 테스트", prompt = "숨겨진 툴 강제 호출", mappingId = "SECRET_001", visible = false)
|
||||||
|
public Object secretTool(LeaveCountReq data) {
|
||||||
|
return executeLegacy("HTTP", "SECRET_001", data);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -16,6 +16,8 @@ services:
|
|||||||
context: ./axhub-gateway
|
context: ./axhub-gateway
|
||||||
ports:
|
ports:
|
||||||
- "8081:8081"
|
- "8081:8081"
|
||||||
|
volumes:
|
||||||
|
- ./:/src
|
||||||
depends_on:
|
depends_on:
|
||||||
redis:
|
redis:
|
||||||
condition: service_healthy
|
condition: service_healthy
|
||||||
@@ -25,6 +27,7 @@ services:
|
|||||||
- SPRING_REDIS_PORT=6379
|
- SPRING_REDIS_PORT=6379
|
||||||
- SPRING_DATA_REDIS_HOST=redis
|
- SPRING_DATA_REDIS_HOST=redis
|
||||||
- SPRING_DATA_REDIS_PORT=6379
|
- SPRING_DATA_REDIS_PORT=6379
|
||||||
|
- AXHUB_SOURCE_DIR=/src
|
||||||
|
|
||||||
tool-sms:
|
tool-sms:
|
||||||
build:
|
build:
|
||||||
|
|||||||
Reference in New Issue
Block a user