feat: standardize tool names and http clients
Some checks failed
Deploy to OCIWP / deploy (push) Has been cancelled
Some checks failed
Deploy to OCIWP / deploy (push) Has been cancelled
This commit is contained in:
@@ -1,9 +1,11 @@
|
||||
package io.shinhanlife.dap.lib.config;
|
||||
|
||||
import io.shinhanlife.glow.communication.module.http.component.GlowHttpComponent;
|
||||
import java.net.http.HttpClient;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.http.client.JdkClientHttpRequestFactory;
|
||||
import org.springframework.web.client.RestClient;
|
||||
|
||||
/**
|
||||
@@ -16,6 +18,12 @@ public class AxhubHttpConfiguration {
|
||||
@Bean
|
||||
@ConditionalOnMissingBean(GlowHttpComponent.class)
|
||||
public GlowHttpComponent glowHttpComponent(RestClient.Builder restClientBuilder) {
|
||||
return new GlowHttpComponent(restClientBuilder);
|
||||
// WireMock and legacy internal endpoints can only support HTTP/1.1.
|
||||
// Avoid JDK HTTP/2 negotiation that may cause RST_STREAM responses.
|
||||
HttpClient http11Client = HttpClient.newBuilder()
|
||||
.version(HttpClient.Version.HTTP_1_1)
|
||||
.build();
|
||||
JdkClientHttpRequestFactory requestFactory = new JdkClientHttpRequestFactory(http11Client);
|
||||
return new GlowHttpComponent(restClientBuilder.requestFactory(requestFactory));
|
||||
}
|
||||
}
|
||||
@@ -9,17 +9,16 @@ import io.shinhanlife.glow.communication.module.http.component.GlowHttpComponent
|
||||
import io.shinhanlife.glow.communication.module.http.dto.HttpBody;
|
||||
import io.shinhanlife.glow.communication.module.http.dto.HttpHeader;
|
||||
import io.shinhanlife.glow.communication.module.http.dto.HttpTransfer;
|
||||
import java.util.Map;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* Tool Pod outbound HTTP component modelled after the ShinhanLife HTTP component.
|
||||
* It resolves an API domain from configuration and delegates the assembled HttpTransfer to Glow.
|
||||
* Tool Pod outbound HTTP component using the Glow HTTP client.
|
||||
* Target URL, HTTP method, content type and Pod-to-Pod behaviour are resolved from
|
||||
* {@code glow.communication.http.api-list}; business source code does not own endpoint values.
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
@@ -33,47 +32,74 @@ public class AxhubHttpComponent {
|
||||
private final GlowCommunicationProperties communicationProperties;
|
||||
private final AxhubHttpProperties properties;
|
||||
|
||||
public <T, R> R call(AxhubHttpDomain domain, String uri, T inputDto, Class<R> responseBodyClass) {
|
||||
return call(domain, uri, inputDto, responseBodyClass, 0);
|
||||
/** Calls the exact URL configured for the API name. */
|
||||
public <T, R> R call(String apiName, T inputDto, Class<R> responseBodyClass) {
|
||||
return call(apiName, "", inputDto, responseBodyClass, 0);
|
||||
}
|
||||
|
||||
/** Calls the configured URL with an optional resource suffix. */
|
||||
public <T, R> R call(String apiName, String uri, T inputDto, Class<R> responseBodyClass) {
|
||||
return call(apiName, uri, inputDto, responseBodyClass, 0);
|
||||
}
|
||||
|
||||
public <T, R> R call(String apiName, String uri, T inputDto, Class<R> responseBodyClass, int timeout) {
|
||||
AxhubHttpProperties.ApiDefinition api = resolveApi(apiName);
|
||||
return execute(api, uri, inputDto, responseBodyClass, timeout);
|
||||
}
|
||||
|
||||
/** Backward-compatible enum overload. */
|
||||
public <T, R> R call(AxhubHttpDomain domain, String uri, T inputDto, Class<R> responseBodyClass) {
|
||||
return call(domain.getCode(), uri, inputDto, responseBodyClass, 0);
|
||||
}
|
||||
|
||||
/** Backward-compatible enum overload. */
|
||||
public <T, R> R call(AxhubHttpDomain domain, String uri, T inputDto, Class<R> responseBodyClass, int timeout) {
|
||||
if (uri == null || uri.isBlank()) {
|
||||
throw new IllegalArgumentException("URI is required.");
|
||||
return call(domain.getCode(), uri, inputDto, responseBodyClass, timeout);
|
||||
}
|
||||
|
||||
/** Calls the configured URL only when the target is marked as a business Pod. */
|
||||
public <T, R> R callBizPod(String apiName, T inputDto, Class<R> responseBodyClass) {
|
||||
AxhubHttpProperties.ApiDefinition api = resolveApi(apiName);
|
||||
if (!api.bizPod()) {
|
||||
throw new IllegalArgumentException("Configured API is not a business Pod: " + apiName);
|
||||
}
|
||||
AxhubHttpProperties.ApiDefinition api = resolveApi(domain);
|
||||
return execute(api, "", inputDto, responseBodyClass, 0);
|
||||
}
|
||||
|
||||
/** Backward-compatible enum overload. */
|
||||
public <T, R> R callBizPod(AxhubHttpDomain domain, String uri, T inputDto, Class<R> responseBodyClass) {
|
||||
AxhubHttpProperties.ApiDefinition api = resolveApi(domain.getCode());
|
||||
if (!api.bizPod()) {
|
||||
throw new IllegalArgumentException("Configured API is not a business Pod: " + domain.getCode());
|
||||
}
|
||||
return execute(api, uri, inputDto, responseBodyClass, 0);
|
||||
}
|
||||
|
||||
private <T, R> R execute(AxhubHttpProperties.ApiDefinition api, String uri, T inputDto,
|
||||
Class<R> responseBodyClass, int timeout) {
|
||||
HttpHeader header = createHeader(api, timeout);
|
||||
String requestUri = joinPath(api.path(), uri);
|
||||
String requestUri = joinPath(api.url(), uri);
|
||||
HttpTransfer<T> request = HttpTransfer.<T>http()
|
||||
.header(header)
|
||||
.domain(api.domain())
|
||||
.uri(requestUri)
|
||||
.method(api.method())
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.contentType(contentType(api))
|
||||
.responseEntity(responseBodyClass)
|
||||
.body(inputDto)
|
||||
.build();
|
||||
|
||||
log.info("[AxhubHttpComponent] Glow HTTP call. domain={}, method={}, uri={}",
|
||||
domain.getCode(), api.method(), requestUri);
|
||||
log.info("[AxhubHttpComponent] Glow HTTP call. apiName={}, method={}, uri={}",
|
||||
api.name(), api.method(), requestUri);
|
||||
ResponseEntity<HttpBody> response = http.sync(request);
|
||||
return convertResponse(response.getBody(), responseBodyClass);
|
||||
}
|
||||
|
||||
/** Convenience method for APIs configured as internal business Pods. */
|
||||
public <T, R> R callBizPod(AxhubHttpDomain domain, String uri, T inputDto, Class<R> responseBodyClass) {
|
||||
AxhubHttpProperties.ApiDefinition api = resolveApi(domain);
|
||||
if (!api.bizPod()) {
|
||||
throw new IllegalArgumentException("Configured API is not a business Pod: " + domain.getCode());
|
||||
}
|
||||
return call(domain, uri, inputDto, responseBodyClass);
|
||||
}
|
||||
|
||||
private AxhubHttpProperties.ApiDefinition resolveApi(AxhubHttpDomain domain) {
|
||||
private AxhubHttpProperties.ApiDefinition resolveApi(String apiName) {
|
||||
return properties.getApiList().stream()
|
||||
.filter(api -> domain.getCode().equals(api.name()))
|
||||
.filter(api -> apiName != null && apiName.equals(api.name()))
|
||||
.findFirst()
|
||||
.orElseThrow(() -> new IllegalArgumentException("No HTTP API configuration for domain: " + domain.getCode()));
|
||||
.orElseThrow(() -> new IllegalArgumentException("No HTTP API configuration for name: " + apiName));
|
||||
}
|
||||
|
||||
private HttpHeader createHeader(AxhubHttpProperties.ApiDefinition api, int timeout) {
|
||||
@@ -98,6 +124,12 @@ public class AxhubHttpComponent {
|
||||
? 0 : communicationProperties.getHttp().getReadTimeout();
|
||||
}
|
||||
|
||||
private MediaType contentType(AxhubHttpProperties.ApiDefinition api) {
|
||||
return api.contentType() == null || api.contentType().isBlank()
|
||||
? MediaType.APPLICATION_JSON
|
||||
: MediaType.parseMediaType(api.contentType());
|
||||
}
|
||||
|
||||
private <R> R convertResponse(HttpBody responseBody, Class<R> responseBodyClass) {
|
||||
String content = responseBody == null ? null : responseBody.content();
|
||||
if (responseBodyClass == String.class) {
|
||||
@@ -116,9 +148,12 @@ public class AxhubHttpComponent {
|
||||
}
|
||||
}
|
||||
|
||||
private String joinPath(String basePath, String uri) {
|
||||
String left = basePath == null ? "" : basePath.replaceAll("/+$", "");
|
||||
String right = uri.startsWith("/") ? uri : "/" + uri;
|
||||
private String joinPath(String configuredUrl, String suffix) {
|
||||
String left = configuredUrl == null ? "" : configuredUrl.replaceAll("/+$", "");
|
||||
if (suffix == null || suffix.isBlank()) {
|
||||
return left.isEmpty() ? "/" : left;
|
||||
}
|
||||
String right = suffix.startsWith("/") ? suffix : "/" + suffix;
|
||||
return left + right;
|
||||
}
|
||||
}
|
||||
@@ -8,7 +8,12 @@ import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/** Domain-to-endpoint configuration for outbound Tool HTTP calls. */
|
||||
/**
|
||||
* Glow HTTP target catalog.
|
||||
*
|
||||
* <p>Each target follows the ShinhanLife standard: name, domain, url, method,
|
||||
* content-type, and biz-pod. Target-specific values belong in application-glow*.yml.</p>
|
||||
*/
|
||||
@Getter
|
||||
@Setter
|
||||
@Component
|
||||
@@ -17,6 +22,13 @@ public class AxhubHttpProperties {
|
||||
|
||||
private List<ApiDefinition> apiList = new ArrayList<>();
|
||||
|
||||
public record ApiDefinition(String name, String domain, String path, HttpMethod method, boolean bizPod) {
|
||||
public record ApiDefinition(
|
||||
String name,
|
||||
String domain,
|
||||
String url,
|
||||
HttpMethod method,
|
||||
String contentType,
|
||||
boolean bizPod
|
||||
) {
|
||||
}
|
||||
}
|
||||
@@ -14,28 +14,28 @@ import java.util.Set;
|
||||
import java.util.Scanner;
|
||||
|
||||
/**
|
||||
* MCP Tool 코드를 자동 생성(Scaffolding)하는 유틸리티 클래스
|
||||
* MCP Tool 肄붾뱶瑜??먮룞 ?앹꽦(Scaffolding)?섎뒗 ?좏떥由ы떚 ?대옒??
|
||||
*
|
||||
* [실행 방법]
|
||||
* 방법 1. IDE(IntelliJ 등)에서 직접 실행 (대화형 모드 추천 ⭐)
|
||||
* - 이 클래스(ToolScaffolder.java)를 열고 main 메서드를 직접 실행(Run)합니다.
|
||||
* - 콘솔 창에 뜨는 질문에 차례대로 값을 입력하기만 하면 파일이 생성됩니다.
|
||||
* [?ㅽ뻾 諛⑸쾿]
|
||||
* 諛⑸쾿 1. IDE(IntelliJ ???먯꽌 吏곸젒 ?ㅽ뻾 (??뷀삎 紐⑤뱶 異붿쿇 狩?
|
||||
* - ???대옒??ToolScaffolder.java)瑜??닿퀬 main 硫붿꽌?쒕? 吏곸젒 ?ㅽ뻾(Run)?⑸땲??
|
||||
* - 肄섏넄 李쎌뿉 ?⑤뒗 吏덈Ц??李⑤??濡?媛믪쓣 ?낅젰?섍린留??섎㈃ ?뚯씪???앹꽦?⑸땲??
|
||||
*
|
||||
* 방법 2. 커맨드라인(터미널)에서 실행 (명령어 기반)
|
||||
* - 컴파일: javac -encoding UTF-8 dap-was-lib/src/main/java/io/shinhanlife/dap/mcc/util/ToolScaffolder.java
|
||||
* - 실행: java -cp dap-was-lib/src/main/java io.shinhanlife.dap.lib.util.ToolScaffolder [이름] [ID] "[설명]" "[그룹]" "[통신방식]" "[모듈명]"
|
||||
* 諛⑸쾿 2. 而ㅻ㎤?쒕씪???곕????먯꽌 ?ㅽ뻾 (紐낅졊??湲곕컲)
|
||||
* - 而댄뙆?? javac -encoding UTF-8 dap-was-lib/src/main/java/io/shinhanlife/dap/mcc/util/ToolScaffolder.java
|
||||
* - ?ㅽ뻾: java -cp dap-was-lib/src/main/java io.shinhanlife.dap.lib.util.ToolScaffolder [?대쫫] [ID] "[?ㅻ챸]" "[洹몃9]" "[?듭떊諛⑹떇]" "[紐⑤뱢紐?"
|
||||
*/
|
||||
/**
|
||||
* @package io.shinhanlife.dap.lib.util
|
||||
* @className ToolScaffolder
|
||||
* @description AX HUB 시스템 처리 클래스
|
||||
* @description AX HUB ?쒖뒪??泥섎━ ?대옒??
|
||||
* @author 0986406
|
||||
* @create 2026.09.01
|
||||
* <pre>
|
||||
* ---------- 개정이력 ----------
|
||||
* 수정일 수정자 수정내용
|
||||
* ---------- 媛쒖젙?대젰 ----------
|
||||
* ?섏젙?? ?섏젙?? ?섏젙?댁슜
|
||||
* ---------- -------- ---------------------------
|
||||
* 2026.09.01 0986406 최초생성
|
||||
* 2026.09.01 0986406 理쒖큹?앹꽦
|
||||
*
|
||||
* </pre>
|
||||
*/
|
||||
@@ -54,16 +54,17 @@ public class ToolScaffolder {
|
||||
System.out.println(" MCP Tool Scaffolder (Java CLI) ");
|
||||
System.out.println("=========================================\n");
|
||||
|
||||
String baseName = getOrAsk(args, 0, scanner, "1. 생성할 Tool의 기본 이름 (예: ExchangeRate) [영문 PascalCase]: ");
|
||||
String interfaceId = getOrAsk(args, 1, scanner, "2. 레거시 API 인터페이스 ID (예: EXCH_001): ");
|
||||
String description = getOrAsk(args, 2, scanner, "3. Tool 기능 설명 (예: 환율 조회): ");
|
||||
String group = getOrAsk(args, 3, scanner, "4. Tool 소속 그룹 (예: SAMPLE, NOTIFICATION, CLAIM, POLICY, HR, CONTRACT, CUSTOMER 등): ");
|
||||
String baseName = getOrAsk(args, 0, scanner, "1. ?앹꽦??Tool??湲곕낯 ?대쫫 (?? ExchangeRate) [?곷Ц PascalCase]: ");
|
||||
String interfaceId = getOrAsk(args, 1, scanner, "2. ?덇굅??API ?명꽣?섏씠??ID (?? EXCH_001): ");
|
||||
String title = getOrAsk(args, 2, scanner, "3. Tool title: ");
|
||||
String description = getOrAsk(args, 3, scanner, "4. Tool description for LLM: ");
|
||||
String group = getOrAsk(args, 4, scanner, "5. Tool category: ");
|
||||
if (group.isEmpty()) group = "COMMON";
|
||||
String routingType = getOrAsk(args, 4, scanner, "5. 통신 프로토콜 (예: HTTP, TCP, MCI, EAI): ");
|
||||
String routingType = getOrAsk(args, 5, scanner, "6. Routing type (HTTP, TCP, MCI, EAI): ");
|
||||
if (routingType.trim().isEmpty()) {
|
||||
routingType = "HTTP";
|
||||
}
|
||||
String moduleName = getOrAsk(args, 5, scanner, "6. 코드를 생성할 모듈 (기본: dap-was-oth): ");
|
||||
String moduleName = getOrAsk(args, 6, scanner, "7. Target module (default dap-was-oth): ");
|
||||
if (moduleName.trim().isEmpty()) {
|
||||
moduleName = "dap-was-oth";
|
||||
}
|
||||
@@ -71,19 +72,19 @@ public class ToolScaffolder {
|
||||
String defaultAuthor = System.getProperty("user.name");
|
||||
String defaultDate = LocalDate.now().format(DateTimeFormatter.ofPattern("yyyy.MM.dd"));
|
||||
|
||||
String author = getOrAsk(args, 6, scanner, "7. 작성자 (엔터 입력 시 '" + defaultAuthor + "'): ");
|
||||
String author = getOrAsk(args, 7, scanner, "7. ?묒꽦??(?뷀꽣 ?낅젰 ??'" + defaultAuthor + "'): ");
|
||||
if (author.trim().isEmpty()) author = defaultAuthor;
|
||||
String createDate = getOrAsk(args, 7, scanner, "8. 작성일 (엔터 입력 시 '" + defaultDate + "'): ");
|
||||
String createDate = getOrAsk(args, 8, scanner, "8. ?묒꽦??(?뷀꽣 ?낅젰 ??'" + defaultDate + "'): ");
|
||||
if (createDate.trim().isEmpty()) createDate = defaultDate;
|
||||
|
||||
String useSchemaResourceStr = getOrAsk(args, 8, scanner, "9. input/output JSON Schema 파일 자동 생성 여부 (y/N): ");
|
||||
String useSchemaResourceStr = getOrAsk(args, 9, scanner, "9. input/output JSON Schema ?뚯씪 ?먮룞 ?앹꽦 ?щ? (y/N): ");
|
||||
boolean useSchemaResource = "y".equalsIgnoreCase(useSchemaResourceStr.trim());
|
||||
|
||||
String schemaResourceDirectory = "classpath:tool-schemas/" + group.toLowerCase() + "/";
|
||||
String inputSchemaResource = useSchemaResource ? schemaResourceDirectory + toKebabCase(baseName) + "-resource-input-schema.json" : null;
|
||||
String outputSchemaResource = useSchemaResource ? schemaResourceDirectory + toKebabCase(baseName) + "-resource-output-schema.json" : null;
|
||||
|
||||
String result = scaffold(baseName, interfaceId, description, group, routingType, moduleName, author, createDate, false, null, inputSchemaResource, outputSchemaResource);
|
||||
String result = scaffold(baseName, interfaceId, title, description, group, routingType, moduleName, author, createDate, false, null, inputSchemaResource, outputSchemaResource, List.of(new FieldDefinition("query", "String", "Search query", "example", false)), List.of());
|
||||
System.out.println(result);
|
||||
}
|
||||
|
||||
@@ -106,7 +107,27 @@ 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, String clientSystemCode, String inputSchemaResource, String outputSchemaResource, List<FieldDefinition> inputFields, List<FieldDefinition> outputFields) throws IOException {
|
||||
return scaffold(baseName, interfaceId, description, description, group, routingType, moduleName, author, createDate,
|
||||
register, clientSystemCode, inputSchemaResource, outputSchemaResource, inputFields, outputFields);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a Tool with a human-facing title and an LLM-facing description.
|
||||
* Existing overloads keep their previous behavior by using the description as the title.
|
||||
*/
|
||||
public static String scaffold(String baseName, String interfaceId, String title, String description, String group, String routingType, String moduleName, String author, String createDate, boolean register, String clientSystemCode, String inputSchemaResource, String outputSchemaResource, List<FieldDefinition> inputFields, List<FieldDefinition> outputFields) throws IOException {
|
||||
return scaffold(baseName, interfaceId, title, description, group, routingType, moduleName, author, createDate,
|
||||
register, clientSystemCode, inputSchemaResource, outputSchemaResource, inputFields, outputFields, "sample");
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a Tool using an HTTP API name that is resolved from glow.communication.http.api-list.
|
||||
*/
|
||||
public static String scaffold(String baseName, String interfaceId, String title, String description, String group, String routingType, String moduleName, String author, String createDate, boolean register, String clientSystemCode, String inputSchemaResource, String outputSchemaResource, List<FieldDefinition> inputFields, List<FieldDefinition> outputFields, String httpApiName) throws IOException {
|
||||
baseName = toPascalCase(baseName);
|
||||
title = title == null || title.isBlank() ? baseName : title.trim();
|
||||
description = description == null ? "" : description.trim();
|
||||
httpApiName = httpApiName == null || httpApiName.isBlank() ? "sample" : httpApiName.trim();
|
||||
String envSourceDir = System.getenv("AXHUB_SOURCE_DIR");
|
||||
Path rootDir = envSourceDir != null ? Paths.get(envSourceDir) : Paths.get(".");
|
||||
|
||||
@@ -117,7 +138,7 @@ public class ToolScaffolder {
|
||||
Path legacyDtoDir = rootDir.resolve(Paths.get(moduleName, BASE_PACKAGE_PATH, "biz", group.toLowerCase(), "legacy"));
|
||||
Path converterDir = rootDir.resolve(Paths.get(moduleName, BASE_PACKAGE_PATH, "biz", group.toLowerCase(), "converter"));
|
||||
|
||||
// schema resource 파일 경로 (useSchemaResource=true 일 때만 생성)
|
||||
// schema resource ?뚯씪 寃쎈줈 (useSchemaResource=true ???뚮쭔 ?앹꽦)
|
||||
boolean useSchemaResource = (inputSchemaResource != null && !inputSchemaResource.trim().isEmpty()) || (outputSchemaResource != null && !outputSchemaResource.trim().isEmpty());
|
||||
String schemaBaseName = toKebabCase(baseName);
|
||||
String inputSchemaFileName = schemaBaseName + "-resource-input-schema.json";
|
||||
@@ -128,6 +149,7 @@ public class ToolScaffolder {
|
||||
|
||||
String bizPackage = BASE_PACKAGE + ".biz." + group.toLowerCase();
|
||||
boolean isMci = "MCI".equalsIgnoreCase(routingType);
|
||||
boolean isHttp = "HTTP".equalsIgnoreCase(routingType);
|
||||
String mciGroupPath = "infra/itrf/mci/" + group.toLowerCase();
|
||||
String clientPrefixCap = "";
|
||||
Path mciClientDir = null;
|
||||
@@ -140,6 +162,11 @@ public class ToolScaffolder {
|
||||
}
|
||||
|
||||
Path mciIoDir = rootDir.resolve(Paths.get(moduleName, BASE_PACKAGE_PATH, mciGroupPath, "io"));
|
||||
String httpApiPackage = toPackageSegment(httpApiName);
|
||||
String httpApiClass = toPascalCase(httpApiName);
|
||||
String httpGroupPath = "infra/itrf/http/" + httpApiPackage;
|
||||
Path httpClientDir = rootDir.resolve(Paths.get(moduleName, BASE_PACKAGE_PATH, httpGroupPath));
|
||||
Path httpIoDir = httpClientDir.resolve("io");
|
||||
|
||||
Files.createDirectories(usecaseDir);
|
||||
Files.createDirectories(usecaseImplDir);
|
||||
@@ -149,6 +176,8 @@ public class ToolScaffolder {
|
||||
if (mciClientDir != null) {
|
||||
Files.createDirectories(mciClientDir);
|
||||
}
|
||||
} else if (isHttp) {
|
||||
Files.createDirectories(httpIoDir);
|
||||
} else {
|
||||
Files.createDirectories(legacyDtoDir);
|
||||
}
|
||||
@@ -166,25 +195,25 @@ public class ToolScaffolder {
|
||||
/**
|
||||
* @package %s.dto
|
||||
* @className %sRequest
|
||||
* @description AX HUB 시스템 처리 클래스
|
||||
* @description AX HUB ?쒖뒪??泥섎━ ?대옒??
|
||||
* @author %s
|
||||
* @create %s
|
||||
* <pre>
|
||||
* ---------- 개정이력 ----------
|
||||
* 수정일 수정자 수정내용
|
||||
* ---------- 媛쒖젙?대젰 ----------
|
||||
* ?섏젙?? ?섏젙?? ?섏젙?댁슜
|
||||
* ---------- -------- ---------------------------
|
||||
* %s %s 최초생성
|
||||
* %s %s 理쒖큹?앹꽦
|
||||
*
|
||||
* </pre>
|
||||
*/
|
||||
@Data
|
||||
@JsonInclude(JsonInclude.Include.NON_NULL)
|
||||
public class %sRequest {
|
||||
@McpToolParam(description = "수신자 전화번호", required = true)
|
||||
@McpToolParam(description = "?섏떊???꾪솕踰덊샇", required = true)
|
||||
-(?:\\\\d{3}|\\\\d{4})-\\\\d{4}$", examples = {"010-1234-5678"})
|
||||
private String phoneNumber;
|
||||
|
||||
@McpToolParam(description = "전송할 메시지 내용", required = true)
|
||||
@McpToolParam(description = "?꾩넚??硫붿떆吏 ?댁슜", required = true)
|
||||
private String message;
|
||||
}
|
||||
""".formatted(bizPackage, bizPackage, baseName, author, createDate, createDate, author, baseName);
|
||||
@@ -194,7 +223,7 @@ public class ToolScaffolder {
|
||||
.replaceAll("(?m)^\\s*@McpToolParam\\([^\\r\\n]*\\)\\R", "")
|
||||
.replaceAll("(?m)^\\s*-\\(\\?:[^\\r\\n]*\\R", "")
|
||||
.replace("private String phoneNumber;", "@Schema(example = \"01012345678\")\n private String phoneNumber;")
|
||||
.replace("private String message;", "@Schema(example = \"테스트 메시지입니다.\")\n private String message;");
|
||||
.replace("private String message;", "@Schema(example = \"?뚯뒪??硫붿떆吏?낅땲??\")\n private String message;");
|
||||
reqContent = dtoContent(bizPackage + ".dto", baseName + "Request", inputFields, author, createDate, true);
|
||||
Files.writeString(dtoDir.resolve(baseName + "Request.java"), reqContent);
|
||||
|
||||
@@ -208,14 +237,14 @@ public class ToolScaffolder {
|
||||
/**
|
||||
* @package %s.dto
|
||||
* @className %sResponse
|
||||
* @description AX HUB 시스템 처리 클래스
|
||||
* @description AX HUB ?쒖뒪??泥섎━ ?대옒??
|
||||
* @author %s
|
||||
* @create %s
|
||||
* <pre>
|
||||
* ---------- 개정이력 ----------
|
||||
* 수정일 수정자 수정내용
|
||||
* ---------- 媛쒖젙?대젰 ----------
|
||||
* ?섏젙?? ?섏젙?? ?섏젙?댁슜
|
||||
* ---------- -------- ---------------------------
|
||||
* %s %s 최초생성
|
||||
* %s %s 理쒖큹?앹꽦
|
||||
*
|
||||
* </pre>
|
||||
*/
|
||||
@@ -254,14 +283,14 @@ public class ToolScaffolder {
|
||||
/**
|
||||
* @package %s.usecase
|
||||
* @className %sUseCase
|
||||
* @description AX HUB 시스템 처리 클래스
|
||||
* @description AX HUB ?쒖뒪??泥섎━ ?대옒??
|
||||
* @author %s
|
||||
* @create %s
|
||||
* <pre>
|
||||
* ---------- 개정이력 ----------
|
||||
* 수정일 수정자 수정내용
|
||||
* ---------- 媛쒖젙?대젰 ----------
|
||||
* ?섏젙?? ?섏젙?? ?섏젙?댁슜
|
||||
* ---------- -------- ---------------------------
|
||||
* %s %s 최초생성
|
||||
* %s %s 理쒖큹?앹꽦
|
||||
*
|
||||
* </pre>
|
||||
*/
|
||||
@@ -277,7 +306,7 @@ public class ToolScaffolder {
|
||||
bizPackage, baseName,
|
||||
bizPackage, baseName, author, createDate, createDate, author,
|
||||
baseName,
|
||||
toolName, description, description,
|
||||
toolName, title, description,
|
||||
toolHintLine,
|
||||
baseName, baseName
|
||||
);
|
||||
@@ -305,14 +334,14 @@ public class ToolScaffolder {
|
||||
/**
|
||||
* @package %s.usecase.impl
|
||||
* @className %sUseCaseImpl
|
||||
* @description AX HUB 시스템 처리 클래스
|
||||
* @description AX HUB ?쒖뒪??泥섎━ ?대옒??
|
||||
* @author %s
|
||||
* @create %s
|
||||
* <pre>
|
||||
* ---------- 개정이력 ----------
|
||||
* 수정일 수정자 수정내용
|
||||
* ---------- 媛쒖젙?대젰 ----------
|
||||
* ?섏젙?? ?섏젙?? ?섏젙?댁슜
|
||||
* ---------- -------- ---------------------------
|
||||
* %s %s 최초생성
|
||||
* %s %s 理쒖큹?앹꽦
|
||||
*
|
||||
* </pre>
|
||||
*/
|
||||
@@ -326,9 +355,9 @@ public class ToolScaffolder {
|
||||
|
||||
@Override
|
||||
public %sResponse execute(%sRequest req) {
|
||||
log.info("[MCI Tool] {} 요청 수신.", "%s");
|
||||
log.info("[MCI Tool] {} ?붿껌 ?섏떊.", "%s");
|
||||
try {
|
||||
// MapStruct를 이용한 자동 매핑 (AI DTO -> MCI DTO)
|
||||
// MapStruct瑜??댁슜???먮룞 留ㅽ븨 (AI DTO -> MCI DTO)
|
||||
%s_I mciReq = converter.toLegacyRequest(req);
|
||||
|
||||
Transfer<Object> resTransfer = mci.callTo(
|
||||
@@ -344,7 +373,7 @@ public class ToolScaffolder {
|
||||
: "MCI call completed without a response body.");
|
||||
return response;
|
||||
} catch (Exception e) {
|
||||
log.error("[MCI Tool] 연동 중 오류 발생: {}", e.getMessage(), e);
|
||||
log.error("[MCI Tool] ?곕룞 以??ㅻ쪟 諛쒖깮: {}", e.getMessage(), e);
|
||||
%sResponse response = new %sResponse();
|
||||
response.setResultCode("ERROR");
|
||||
response.setResultMessage(e.getMessage() != null ? e.getMessage() : "Unknown error");
|
||||
@@ -388,8 +417,8 @@ public class ToolScaffolder {
|
||||
.replace("response.setResultCode(\"SUCCESS\");",
|
||||
"if (resTransfer.getBody() != null) {\n response = converter.toResponse(resTransfer.getBody());\n }\n response.setResultCode(\"SUCCESS\");");
|
||||
} else {
|
||||
serviceImplContent = "HTTP".equalsIgnoreCase(routingType)
|
||||
? httpUseCaseImplContent(bizPackage, baseName, interfaceId, author, createDate)
|
||||
serviceImplContent = isHttp
|
||||
? httpUseCaseImplContent(bizPackage, baseName, httpGroupPath.replace("/", "."), httpApiClass, author, createDate)
|
||||
: """
|
||||
package %s.usecase.impl;
|
||||
|
||||
@@ -405,14 +434,14 @@ public class ToolScaffolder {
|
||||
/**
|
||||
* @package %s.usecase.impl
|
||||
* @className %sUseCaseImpl
|
||||
* @description AX HUB 시스템 처리 클래스
|
||||
* @description AX HUB ?쒖뒪??泥섎━ ?대옒??
|
||||
* @author %s
|
||||
* @create %s
|
||||
* <pre>
|
||||
* ---------- 개정이력 ----------
|
||||
* 수정일 수정자 수정내용
|
||||
* ---------- 媛쒖젙?대젰 ----------
|
||||
* ?섏젙?? ?섏젙?? ?섏젙?댁슜
|
||||
* ---------- -------- ---------------------------
|
||||
* %s %s 최초생성
|
||||
* %s %s 理쒖큹?앹꽦
|
||||
*
|
||||
* </pre>
|
||||
*/
|
||||
@@ -469,26 +498,26 @@ public class ToolScaffolder {
|
||||
/**
|
||||
* @package %s.%s.io
|
||||
* @className %s_I
|
||||
* @description AX HUB 시스템 처리 클래스
|
||||
* @description AX HUB ?쒖뒪??泥섎━ ?대옒??
|
||||
* @author %s
|
||||
* @create %s
|
||||
* <pre>
|
||||
* ---------- 개정이력 ----------
|
||||
* 수정일 수정자 수정내용
|
||||
* ---------- 媛쒖젙?대젰 ----------
|
||||
* ?섏젙?? ?섏젙?? ?섏젙?댁슜
|
||||
* ---------- -------- ---------------------------
|
||||
* %s %s 최초생성
|
||||
* %s %s 理쒖큹?앹꽦
|
||||
*
|
||||
* </pre>
|
||||
*/
|
||||
@Data
|
||||
public class %s_I {
|
||||
/**
|
||||
* EAI 시스템이 요구하는 수신자 번호 파라미터명
|
||||
* EAI ?쒖뒪?쒖씠 ?붽뎄?섎뒗 ?섏떊??踰덊샇 ?뚮씪誘명꽣紐?
|
||||
*/
|
||||
private String phone;
|
||||
|
||||
/**
|
||||
* EAI 시스템이 요구하는 메시지 내용 파라미터명
|
||||
* EAI ?쒖뒪?쒖씠 ?붽뎄?섎뒗 硫붿떆吏 ?댁슜 ?뚮씪誘명꽣紐?
|
||||
*/
|
||||
private String content;
|
||||
}
|
||||
@@ -504,14 +533,14 @@ public class ToolScaffolder {
|
||||
/**
|
||||
* @package %s.%s.io
|
||||
* @className %s_O
|
||||
* @description AX HUB 시스템 처리 클래스
|
||||
* @description AX HUB ?쒖뒪??泥섎━ ?대옒??
|
||||
* @author %s
|
||||
* @create %s
|
||||
* <pre>
|
||||
* ---------- 개정이력 ----------
|
||||
* 수정일 수정자 수정내용
|
||||
* ---------- 媛쒖젙?대젰 ----------
|
||||
* ?섏젙?? ?섏젙?? ?섏젙?댁슜
|
||||
* ---------- -------- ---------------------------
|
||||
* %s %s 최초생성
|
||||
* %s %s 理쒖큹?앹꽦
|
||||
*
|
||||
* </pre>
|
||||
*/
|
||||
@@ -537,14 +566,14 @@ public class ToolScaffolder {
|
||||
/**
|
||||
* @package %s.converter
|
||||
* @className %sConverter
|
||||
* @description AX HUB 시스템 처리 클래스
|
||||
* @description AX HUB ?쒖뒪??泥섎━ ?대옒??
|
||||
* @author %s
|
||||
* @create %s
|
||||
* <pre>
|
||||
* ---------- 개정이력 ----------
|
||||
* 수정일 수정자 수정내용
|
||||
* ---------- 媛쒖젙?대젰 ----------
|
||||
* ?섏젙?? ?섏젙?? ?섏젙?댁슜
|
||||
* ---------- -------- ---------------------------
|
||||
* %s %s 최초생성
|
||||
* %s %s 理쒖큹?앹꽦
|
||||
*
|
||||
* </pre>
|
||||
*/
|
||||
@@ -598,14 +627,14 @@ public class ToolScaffolder {
|
||||
/**
|
||||
* @package %s.%s
|
||||
* @className Mci%sClient
|
||||
* @description AX HUB 시스템 처리 클래스
|
||||
* @description AX HUB ?쒖뒪??泥섎━ ?대옒??
|
||||
* @author %s
|
||||
* @create %s
|
||||
* <pre>
|
||||
* ---------- 개정이력 ----------
|
||||
* 수정일 수정자 수정내용
|
||||
* ---------- 媛쒖젙?대젰 ----------
|
||||
* ?섏젙?? ?섏젙?? ?섏젙?댁슜
|
||||
* ---------- -------- ---------------------------
|
||||
* %s %s 최초생성
|
||||
* %s %s 理쒖큹?앹꽦
|
||||
*
|
||||
* </pre>
|
||||
*/
|
||||
@@ -626,6 +655,32 @@ public class ToolScaffolder {
|
||||
log.append("[MCI Client] ").append(mciClientDir.resolve("Mci" + clientPrefixCap + "Client.java")).append("\n");
|
||||
}
|
||||
|
||||
} else if (isHttp) {
|
||||
String httpPackage = BASE_PACKAGE + "." + httpGroupPath.replace("/", ".");
|
||||
String httpRequestClass = baseName + "HttpRequest";
|
||||
String httpResponseClass = baseName + "HttpResponse";
|
||||
String httpClientClass = httpApiClass + "Client";
|
||||
|
||||
Files.writeString(httpIoDir.resolve(httpRequestClass + ".java"),
|
||||
dtoContent(httpPackage + ".io", httpRequestClass, inputFields, author, createDate, true));
|
||||
Files.writeString(httpIoDir.resolve(httpResponseClass + ".java"),
|
||||
dtoContent(httpPackage + ".io", httpResponseClass, outputFields, author, createDate, false));
|
||||
Files.writeString(httpClientDir.resolve(httpClientClass + ".java"),
|
||||
httpClientContent(httpPackage, httpClientClass, httpApiName));
|
||||
Files.writeString(converterDir.resolve(baseName + "Converter.java"),
|
||||
httpConverterContent(bizPackage, baseName, httpPackage));
|
||||
|
||||
log.append("\n=========================================\n");
|
||||
log.append(" Scaffolding Complete! (Routing: HTTP)\n");
|
||||
log.append("=========================================\n");
|
||||
log.append("[Usecase Interface] ").append(usecaseDir.resolve(baseName + "UseCase.java")).append("\n");
|
||||
log.append("[Usecase Impl] ").append(usecaseImplDir.resolve(baseName + "UseCaseImpl.java")).append("\n");
|
||||
log.append("[Request DTO] ").append(dtoDir.resolve(baseName + "Request.java")).append("\n");
|
||||
log.append("[Response DTO] ").append(dtoDir.resolve(baseName + "Response.java")).append("\n");
|
||||
log.append("[HTTP Request IO] ").append(httpIoDir.resolve(httpRequestClass + ".java")).append("\n");
|
||||
log.append("[HTTP Response IO] ").append(httpIoDir.resolve(httpResponseClass + ".java")).append("\n");
|
||||
log.append("[HTTP Client] ").append(httpClientDir.resolve(httpClientClass + ".java")).append("\n");
|
||||
log.append("[HTTP Converter] ").append(converterDir.resolve(baseName + "Converter.java")).append("\n");
|
||||
} else {
|
||||
String legacyReqContent = """
|
||||
package %s.legacy;
|
||||
@@ -635,26 +690,26 @@ public class ToolScaffolder {
|
||||
/**
|
||||
* @package %s.legacy
|
||||
* @className %sLegacyRequest
|
||||
* @description AX HUB 시스템 처리 클래스
|
||||
* @description AX HUB ?쒖뒪??泥섎━ ?대옒??
|
||||
* @author %s
|
||||
* @create %s
|
||||
* <pre>
|
||||
* ---------- 개정이력 ----------
|
||||
* 수정일 수정자 수정내용
|
||||
* ---------- 媛쒖젙?대젰 ----------
|
||||
* ?섏젙?? ?섏젙?? ?섏젙?댁슜
|
||||
* ---------- -------- ---------------------------
|
||||
* %s %s 최초생성
|
||||
* %s %s 理쒖큹?앹꽦
|
||||
*
|
||||
* </pre>
|
||||
*/
|
||||
@Data
|
||||
public class %sLegacyRequest {
|
||||
/**
|
||||
* EAI 시스템이 요구하는 수신자 번호 파라미터명
|
||||
* EAI ?쒖뒪?쒖씠 ?붽뎄?섎뒗 ?섏떊??踰덊샇 ?뚮씪誘명꽣紐?
|
||||
*/
|
||||
private String phone;
|
||||
|
||||
/**
|
||||
* EAI 시스템이 요구하는 메시지 내용 파라미터명
|
||||
* EAI ?쒖뒪?쒖씠 ?붽뎄?섎뒗 硫붿떆吏 ?댁슜 ?뚮씪誘명꽣紐?
|
||||
*/
|
||||
private String content;
|
||||
}
|
||||
@@ -670,14 +725,14 @@ public class ToolScaffolder {
|
||||
/**
|
||||
* @package %s.legacy
|
||||
* @className %sLegacyResponse
|
||||
* @description AX HUB 시스템 처리 클래스
|
||||
* @description AX HUB ?쒖뒪??泥섎━ ?대옒??
|
||||
* @author %s
|
||||
* @create %s
|
||||
* <pre>
|
||||
* ---------- 개정이력 ----------
|
||||
* 수정일 수정자 수정내용
|
||||
* ---------- 媛쒖젙?대젰 ----------
|
||||
* ?섏젙?? ?섏젙?? ?섏젙?댁슜
|
||||
* ---------- -------- ---------------------------
|
||||
* %s %s 최초생성
|
||||
* %s %s 理쒖큹?앹꽦
|
||||
*
|
||||
* </pre>
|
||||
*/
|
||||
@@ -703,14 +758,14 @@ public class ToolScaffolder {
|
||||
/**
|
||||
* @package %s.converter
|
||||
* @className %sConverter
|
||||
* @description AX HUB 시스템 처리 클래스
|
||||
* @description AX HUB ?쒖뒪??泥섎━ ?대옒??
|
||||
* @author %s
|
||||
* @create %s
|
||||
* <pre>
|
||||
* ---------- 개정이력 ----------
|
||||
* 수정일 수정자 수정내용
|
||||
* ---------- 媛쒖젙?대젰 ----------
|
||||
* ?섏젙?? ?섏젙?? ?섏젙?댁슜
|
||||
* ---------- -------- ---------------------------
|
||||
* %s %s 최초생성
|
||||
* %s %s 理쒖큹?앹꽦
|
||||
*
|
||||
* </pre>
|
||||
*/
|
||||
@@ -750,7 +805,7 @@ public class ToolScaffolder {
|
||||
log.append("[Legacy Response DTO] ").append(legacyDtoDir.resolve(baseName + "LegacyResponse.java")).append("\n");
|
||||
log.append("[Legacy Converter] ").append(converterDir.resolve(baseName + "Converter.java")).append("\n");
|
||||
}
|
||||
// schema resource 파일 생성 (useSchemaResource=true 일 때)
|
||||
// schema resource ?뚯씪 ?앹꽦 (useSchemaResource=true ????
|
||||
if (useSchemaResource) {
|
||||
Files.createDirectories(schemaDir);
|
||||
String inputSchema = """
|
||||
@@ -760,7 +815,7 @@ public class ToolScaffolder {
|
||||
"properties": {
|
||||
"TODO_FIELD": {
|
||||
"type": "string",
|
||||
"description": "TODO: 파라미터 설명을 입력하세요."
|
||||
"description": "TODO: ?뚮씪誘명꽣 ?ㅻ챸???낅젰?섏꽭??"
|
||||
}
|
||||
},
|
||||
"required": []
|
||||
@@ -773,12 +828,12 @@ public class ToolScaffolder {
|
||||
"properties": {
|
||||
"status": {
|
||||
"type": "string",
|
||||
"description": "처리 결과 상태 (SUCCESS / FAILURE)",
|
||||
"description": "泥섎━ 寃곌낵 ?곹깭 (SUCCESS / FAILURE)",
|
||||
"enum": ["SUCCESS", "FAILURE"]
|
||||
},
|
||||
"message": {
|
||||
"type": "string",
|
||||
"description": "처리 결과 메시지"
|
||||
"description": "泥섎━ 寃곌낵 硫붿떆吏"
|
||||
}
|
||||
},
|
||||
"required": ["status"]
|
||||
@@ -790,18 +845,32 @@ public class ToolScaffolder {
|
||||
log.append("[Output Schema] ").append(schemaDir.resolve(outputSchemaFileName)).append("\n");
|
||||
}
|
||||
|
||||
Path mockResponsePath = rootDir.resolve(Paths.get(moduleName, "src/main/resources/mock-responses", toolName + ".json"));
|
||||
Files.createDirectories(mockResponsePath.getParent());
|
||||
Files.writeString(mockResponsePath, mockResponseContent(outputFields));
|
||||
String mockResponse = mockResponseContent(outputFields);
|
||||
if (isHttp) {
|
||||
Path moduleRoot = rootDir.resolve(moduleName).toAbsolutePath().normalize();
|
||||
Path projectRoot = moduleRoot.getParent();
|
||||
Path wireMockBodyPath = projectRoot.resolve(Paths.get("mci-mock", "__files", toolName + ".json"));
|
||||
Path wireMockMappingPath = projectRoot.resolve(Paths.get("mci-mock", "mappings", toolName + ".json"));
|
||||
Files.createDirectories(wireMockBodyPath.getParent());
|
||||
Files.createDirectories(wireMockMappingPath.getParent());
|
||||
Files.writeString(wireMockBodyPath, mockResponse);
|
||||
Files.writeString(wireMockMappingPath, wireMockMappingContent(interfaceId, wireMockBodyPath.getFileName().toString()));
|
||||
log.append("[WireMock Response] ").append(wireMockBodyPath).append("\n");
|
||||
log.append("[WireMock Mapping] ").append(wireMockMappingPath).append("\n");
|
||||
} else {
|
||||
Path mockResponsePath = rootDir.resolve(Paths.get(moduleName, "src/main/resources/mock-responses", toolName + ".json"));
|
||||
Files.createDirectories(mockResponsePath.getParent());
|
||||
Files.writeString(mockResponsePath, mockResponse);
|
||||
log.append("[Mock Response] ").append(mockResponsePath).append("\n");
|
||||
}
|
||||
|
||||
Path generatedTestDir = rootDir.resolve(Paths.get(moduleName, "src/test/java/io/shinhanlife/dap/mcc/biz", group.toLowerCase(), "usecase"));
|
||||
Files.createDirectories(generatedTestDir);
|
||||
Path generatedTestPath = generatedTestDir.resolve(baseName + "UseCaseTest.java");
|
||||
Files.writeString(generatedTestPath, useCaseTestContent(bizPackage, baseName));
|
||||
log.append("[Mock Response] ").append(mockResponsePath).append("\\n");
|
||||
log.append("[Unit Test] ").append(generatedTestPath).append("\\n");
|
||||
log.append("[Test Command] .\\gradlew.bat :").append(moduleName.substring(moduleName.lastIndexOf(java.io.File.separator) + 1)).append(":test --tests \"*").append(baseName).append("UseCaseTest\"\\n");
|
||||
log.append("\n Tip: ").append(interfaceId).append(" 목업 데이터를 mock-responses.json에 추가하세요.\n");
|
||||
log.append("\n Tip: HTTP Tool? WireMock???ㅽ뻾?????앹꽦??mapping URL濡??몄텧???뺤씤?섏꽭??\n");
|
||||
|
||||
return log.toString();
|
||||
}
|
||||
@@ -847,56 +916,110 @@ public class ToolScaffolder {
|
||||
""".formatted(BASE_PACKAGE, packageSuffix, className, fieldLines(fields));
|
||||
}
|
||||
|
||||
private static String httpUseCaseImplContent(String bizPackage, String baseName, String interfaceId, String author, String createDate) {
|
||||
private static String httpUseCaseImplContent(String bizPackage, String baseName, String httpPackage,
|
||||
String httpApiClass, String author, String createDate) {
|
||||
String httpRequestClass = baseName + "HttpRequest";
|
||||
String httpResponseClass = baseName + "HttpResponse";
|
||||
String httpClientClass = httpApiClass + "Client";
|
||||
String clientVariable = Character.toLowerCase(httpClientClass.charAt(0)) + httpClientClass.substring(1);
|
||||
return """
|
||||
package %s.usecase.impl;
|
||||
|
||||
import %s.converter.%sConverter;
|
||||
import %s.dto.%sRequest;
|
||||
import %s.dto.%sResponse;
|
||||
import %s.legacy.%sLegacyRequest;
|
||||
import %s.legacy.%sLegacyResponse;
|
||||
import %s.%s.%s;
|
||||
import %s.%s.io.%s;
|
||||
import %s.%s.io.%s;
|
||||
import %s.usecase.%sUseCase;
|
||||
import io.shinhanlife.dap.lib.integration.http.component.AxhubHttpComponent;
|
||||
import io.shinhanlife.dap.lib.integration.http.component.AxhubHttpDomain;
|
||||
import io.shinhanlife.dap.mcc.usecase.AbstractMcpToolUseCase;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
/**
|
||||
* HTTP Tool implementation. Calls the Glow HTTP adapter through AxhubHttpComponent.
|
||||
* Configure the target domain in AxhubHttpDomain and glow.communication.http.api-list before use.
|
||||
*/
|
||||
@Slf4j
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class %sUseCaseImpl extends AbstractMcpToolUseCase implements %sUseCase {
|
||||
public class %sUseCaseImpl implements %sUseCase {
|
||||
|
||||
private final %sConverter converter;
|
||||
private final AxhubHttpComponent http;
|
||||
private final %s %s;
|
||||
|
||||
@Override
|
||||
public %sResponse execute(%sRequest req) {
|
||||
%sLegacyRequest legacyRequest = converter.toLegacyRequest(req);
|
||||
%sLegacyResponse legacyResponse = http.call(
|
||||
AxhubHttpDomain.SAMPLE,
|
||||
"/%s",
|
||||
legacyRequest,
|
||||
%sLegacyResponse.class
|
||||
);
|
||||
%s httpRequest = converter.toHttpRequest(req);
|
||||
%s httpResponse = %s.call(httpRequest, %s.class);
|
||||
|
||||
%sResponse response = converter.toResponse(legacyResponse);
|
||||
%sResponse response = converter.toResponse(httpResponse);
|
||||
response.setResultCode("SUCCESS");
|
||||
response.setResultMessage("HTTP API call completed.");
|
||||
return response;
|
||||
}
|
||||
}
|
||||
""".formatted(
|
||||
bizPackage, bizPackage, baseName, bizPackage, baseName, bizPackage, baseName,
|
||||
bizPackage, baseName, bizPackage, baseName, bizPackage, baseName,
|
||||
baseName, baseName, baseName, baseName, baseName, baseName, baseName,
|
||||
interfaceId, baseName, baseName);
|
||||
bizPackage,
|
||||
bizPackage, baseName,
|
||||
bizPackage, baseName,
|
||||
bizPackage, baseName,
|
||||
BASE_PACKAGE, httpPackage, httpClientClass,
|
||||
BASE_PACKAGE, httpPackage, httpRequestClass,
|
||||
BASE_PACKAGE, httpPackage, httpResponseClass,
|
||||
bizPackage, baseName,
|
||||
baseName, baseName,
|
||||
baseName, httpClientClass, clientVariable,
|
||||
baseName, baseName,
|
||||
httpRequestClass, httpResponseClass, clientVariable, httpResponseClass,
|
||||
baseName);
|
||||
}
|
||||
|
||||
private static String httpClientContent(String httpPackage, String clientClass, String apiName) {
|
||||
return """
|
||||
package %s;
|
||||
|
||||
import io.shinhanlife.dap.lib.integration.http.component.AxhubHttpComponent;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Component
|
||||
@RequiredArgsConstructor
|
||||
public class %s {
|
||||
private static final String API_NAME = "%s";
|
||||
|
||||
private final AxhubHttpComponent http;
|
||||
|
||||
public <I, O> O call(I request, Class<O> responseType) {
|
||||
return http.call(API_NAME, request, responseType);
|
||||
}
|
||||
}
|
||||
""".formatted(httpPackage, clientClass, apiName);
|
||||
}
|
||||
|
||||
private static String httpConverterContent(String bizPackage, String baseName, String httpPackage) {
|
||||
return """
|
||||
package %s.converter;
|
||||
|
||||
import %s.dto.%sRequest;
|
||||
import %s.dto.%sResponse;
|
||||
import %s.%s.io.%sHttpRequest;
|
||||
import %s.%s.io.%sHttpResponse;
|
||||
import org.mapstruct.Mapper;
|
||||
import org.mapstruct.ReportingPolicy;
|
||||
|
||||
@Mapper(componentModel = "spring", unmappedTargetPolicy = ReportingPolicy.IGNORE)
|
||||
public interface %sConverter {
|
||||
%sHttpRequest toHttpRequest(%sRequest request);
|
||||
%sResponse toResponse(%sHttpResponse httpResponse);
|
||||
}
|
||||
""".formatted(bizPackage,
|
||||
bizPackage, baseName,
|
||||
bizPackage, baseName,
|
||||
BASE_PACKAGE, httpPackage, baseName,
|
||||
BASE_PACKAGE, httpPackage, baseName,
|
||||
baseName, baseName, baseName, baseName, baseName);
|
||||
}
|
||||
|
||||
private static String toPackageSegment(String value) {
|
||||
String normalized = value == null ? "" : value.toLowerCase(Locale.ROOT)
|
||||
.replaceAll("[^a-z0-9]+", "_")
|
||||
.replaceAll("^_+|_+$", "");
|
||||
return normalized.isBlank() ? "sample" : normalized;
|
||||
}
|
||||
private static String mciConverterContent(String bizPackage, String baseName, String mciPackage, String interfaceId) {
|
||||
return """
|
||||
@@ -976,6 +1099,23 @@ public class ToolScaffolder {
|
||||
}
|
||||
|
||||
|
||||
static String wireMockMappingContent(String interfaceId, String bodyFileName) {
|
||||
return """
|
||||
{
|
||||
"request" : {
|
||||
"method" : "POST",
|
||||
"urlPath" : "/%s"
|
||||
},
|
||||
"response" : {
|
||||
"status" : 200,
|
||||
"headers" : {
|
||||
"Content-Type" : "application/json;charset=UTF-8"
|
||||
},
|
||||
"bodyFileName" : "%s"
|
||||
}
|
||||
}
|
||||
""".formatted(jsonEscape(interfaceId), jsonEscape(bodyFileName));
|
||||
}
|
||||
private static String mockResponseContent(List<FieldDefinition> outputFields) {
|
||||
StringBuilder json = new StringBuilder("{\n");
|
||||
List<FieldDefinition> fields = outputFields == null ? List.of() : outputFields;
|
||||
@@ -1031,10 +1171,6 @@ public class ToolScaffolder {
|
||||
baseName, baseName, baseName);
|
||||
}
|
||||
private static String toToolName(String moduleName, String group, String baseName) {
|
||||
String moduleDirectory = Path.of(moduleName).getFileName().toString();
|
||||
String pod = moduleDirectory.startsWith("dap-was-")
|
||||
? moduleDirectory.substring("dap-was-".length())
|
||||
: "oth";
|
||||
String normalizedName = baseName.replaceAll("([a-z0-9])([A-Z])", "$1 $2")
|
||||
.toLowerCase(Locale.ROOT)
|
||||
.replaceAll("[^a-z0-9]+", " ")
|
||||
@@ -1042,8 +1178,7 @@ public class ToolScaffolder {
|
||||
String[] words = normalizedName.split("\\s+");
|
||||
String service = words[0];
|
||||
String action = words.length == 1 ? "execute" : words[words.length - 1];
|
||||
return "%s_%s_%s_%s".formatted(
|
||||
pod.toLowerCase(Locale.ROOT),
|
||||
return "%s_%s_%s".formatted(
|
||||
group.toLowerCase(Locale.ROOT),
|
||||
service,
|
||||
action);
|
||||
|
||||
@@ -8,6 +8,17 @@ glow:
|
||||
communication:
|
||||
common:
|
||||
env-type: D
|
||||
# Direct local process uses WireMock host port mapped by docker-compose.
|
||||
http:
|
||||
connection-timeout: 5
|
||||
read-timeout: 5
|
||||
api-list:
|
||||
- name: sample
|
||||
domain: ${AXHUB_SAMPLE_HTTP_DOMAIN:http://localhost:8089}
|
||||
url: ${AXHUB_SAMPLE_HTTP_URL:/CLCNNB00001}
|
||||
method: POST
|
||||
content-type: application/json;charset=UTF-8
|
||||
biz-pod: false
|
||||
mci:
|
||||
host: ${GLOW_COMMUNICATION_MCI_HOST:http://localhost}
|
||||
port: ${GLOW_COMMUNICATION_MCI_PORT:8080}
|
||||
|
||||
@@ -23,10 +23,15 @@ glow:
|
||||
read-timeout: 5
|
||||
# HTTP Tool target catalog. Replace or add entries after the business endpoint is agreed.
|
||||
api-list:
|
||||
# WireMock/개발환경 샘플입니다. 컨테이너 내부에서는 localhost가 Tool Pod 자신을 뜻하므로
|
||||
# Docker 서비스명(mci-mock)과 컨테이너 포트(8080)를 기본값으로 사용합니다.
|
||||
# 로컬 PC에서 실행할 때는 AXHUB_SAMPLE_HTTP_DOMAIN=http://localhost:8089 로 덮어씁니다.
|
||||
- name: sample
|
||||
domain: ${AXHUB_SAMPLE_HTTP_DOMAIN:http://localhost:8099}
|
||||
path: ""
|
||||
method: GET
|
||||
domain: ${AXHUB_SAMPLE_HTTP_DOMAIN:http://mci-mock:8080}
|
||||
# mci-mock/mappings/smp_employee_search.json의 urlPath와 동일해야 합니다.
|
||||
url: ${AXHUB_SAMPLE_HTTP_URL:/CLCNNB00001}
|
||||
method: POST
|
||||
content-type: application/json;charset=UTF-8
|
||||
biz-pod: false
|
||||
mci:
|
||||
uri: /ntl_mci/dap_rcv
|
||||
|
||||
@@ -3,6 +3,7 @@ package io.shinhanlife.dap.lib.integration.http.component;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.springframework.http.MediaType.APPLICATION_JSON;
|
||||
import static org.springframework.test.web.client.match.MockRestRequestMatchers.header;
|
||||
import static org.springframework.test.web.client.match.MockRestRequestMatchers.method;
|
||||
import static org.springframework.test.web.client.match.MockRestRequestMatchers.requestTo;
|
||||
import static org.springframework.test.web.client.response.MockRestResponseCreators.withSuccess;
|
||||
|
||||
@@ -24,7 +25,7 @@ class AxhubHttpComponentTest {
|
||||
GlowHttpComponent glowHttpComponent = new GlowHttpComponent(builder);
|
||||
AxhubHttpProperties properties = new AxhubHttpProperties();
|
||||
properties.setApiList(List.of(new AxhubHttpProperties.ApiDefinition(
|
||||
"sample", "https://api.example.test", "/v1", HttpMethod.GET, false)));
|
||||
"sample", "https://api.example.test", "/v1", HttpMethod.GET, "application/json", false)));
|
||||
AxhubHttpComponent component = new AxhubHttpComponent(
|
||||
glowHttpComponent, new ObjectMapper(), new GlowCommunicationProperties(), properties);
|
||||
|
||||
@@ -38,6 +39,29 @@ class AxhubHttpComponentTest {
|
||||
server.verify();
|
||||
}
|
||||
|
||||
@Test
|
||||
void callByApiNameUsesConfiguredUrlMethodContentTypeAndBizPodHeader() {
|
||||
RestClient.Builder builder = RestClient.builder();
|
||||
MockRestServiceServer server = MockRestServiceServer.bindTo(builder).build();
|
||||
GlowHttpComponent glowHttpComponent = new GlowHttpComponent(builder);
|
||||
AxhubHttpProperties properties = new AxhubHttpProperties();
|
||||
properties.setApiList(List.of(new AxhubHttpProperties.ApiDefinition(
|
||||
"employee", "https://employee.example.test", "/itrf/employee", HttpMethod.POST,
|
||||
"application/json;charset=UTF-8", true)));
|
||||
AxhubHttpComponent component = new AxhubHttpComponent(
|
||||
glowHttpComponent, new ObjectMapper(), new GlowCommunicationProperties(), properties);
|
||||
|
||||
server.expect(requestTo("https://employee.example.test/itrf/employee"))
|
||||
.andExpect(method(HttpMethod.POST))
|
||||
.andExpect(header("Content-Type", "application/json;charset=UTF-8"))
|
||||
.andExpect(header("X-POD-TO-POD", "true"))
|
||||
.andRespond(withSuccess("{\"status\":\"OK\"}", APPLICATION_JSON));
|
||||
|
||||
SampleResponse response = component.call("employee", "{\"employeeId\":\"EMP10001\"}", SampleResponse.class);
|
||||
|
||||
assertThat(response.status()).isEqualTo("OK");
|
||||
server.verify();
|
||||
}
|
||||
record SampleResponse(String status) {
|
||||
}
|
||||
}
|
||||
@@ -18,10 +18,10 @@ class McpToolMethodRegistryTest {
|
||||
|
||||
registry.initialize();
|
||||
|
||||
McpToolMethodRegistry.RegisteredTool tool = registry.find("oth_cmm_echo_search");
|
||||
McpToolMethodRegistry.RegisteredTool tool = registry.find("cmm_echo_search");
|
||||
assertNotNull(tool);
|
||||
assertEquals("execute", tool.method().getName());
|
||||
assertEquals("oth_cmm_echo_search", tool.annotation().name());
|
||||
assertEquals("cmm_echo_search", tool.annotation().name());
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -42,14 +42,14 @@ class McpToolMethodRegistryTest {
|
||||
}
|
||||
|
||||
static class EchoTool {
|
||||
@McpTool(name = "oth_cmm_echo_search")
|
||||
@McpTool(name = "cmm_echo_search")
|
||||
public String execute(String request) {
|
||||
return request;
|
||||
}
|
||||
}
|
||||
|
||||
static class DuplicateEchoTool {
|
||||
@McpTool(name = "oth_cmm_echo_search")
|
||||
@McpTool(name = "cmm_echo_search")
|
||||
public String execute(String request) {
|
||||
return request;
|
||||
}
|
||||
|
||||
@@ -25,7 +25,7 @@ class ToolScaffolderTest {
|
||||
String useCase = Files.readString(root.resolve("usecase/ClaimSearchUseCase.java"));
|
||||
String response = Files.readString(root.resolve("dto/ClaimSearchResponse.java"));
|
||||
|
||||
assertTrue(useCase.contains("name = \"oth_cmm_claim_search\""));
|
||||
assertTrue(useCase.contains("name = \"cmm_claim_search\""));
|
||||
assertTrue(useCase.contains("@ToolHint(register = true, categoryKey = \"cmm\", mappingId = \"CLM0001\")"));
|
||||
assertTrue(response.contains("private String resultCode;"));
|
||||
assertTrue(response.contains("private String resultMessage;"));
|
||||
@@ -41,7 +41,7 @@ class ToolScaffolderTest {
|
||||
"src/main/java/io/shinhanlife/dap/mcc/biz/cmm/usecase/NotificationSendUseCase.java");
|
||||
String useCase = Files.readString(useCasePath);
|
||||
|
||||
assertTrue(useCase.contains("name = \"sms_cmm_notification_send\""));
|
||||
assertTrue(useCase.contains("name = \"cmm_notification_send\""));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -121,7 +121,7 @@ class ToolScaffolderTest {
|
||||
ToolScaffolder.scaffold("claim search", "CLM0001", "Claim search", "cmm", "MCI", moduleName,
|
||||
"tester", "2026.08.10", true, null, null, null, List.of(), outputFields);
|
||||
|
||||
Path mockResponse = root.resolve("dap-was-oth/src/main/resources/mock-responses/oth_cmm_claim_search.json");
|
||||
Path mockResponse = root.resolve("dap-was-oth/src/main/resources/mock-responses/cmm_claim_search.json");
|
||||
Path useCaseTest = root.resolve("dap-was-oth/src/test/java/io/shinhanlife/dap/mcc/biz/cmm/usecase/ClaimSearchUseCaseTest.java");
|
||||
|
||||
assertTrue(Files.exists(mockResponse));
|
||||
@@ -129,6 +129,15 @@ class ToolScaffolderTest {
|
||||
assertTrue(Files.exists(useCaseTest));
|
||||
assertTrue(Files.readString(useCaseTest).contains("class ClaimSearchUseCaseTest"));
|
||||
}
|
||||
@Test
|
||||
void createsWireMockMappingForHttpTool() {
|
||||
String mapping = ToolScaffolder.wireMockMappingContent("HR_EMPLOYEE_SEARCH", "smp_employee_search.json");
|
||||
|
||||
assertTrue(mapping.contains("\"method\" : \"POST\""), mapping);
|
||||
assertTrue(mapping.contains("\"urlPath\" : \"/HR_EMPLOYEE_SEARCH\""), mapping);
|
||||
assertTrue(mapping.contains("\"bodyFileName\" : \"smp_employee_search.json\""), mapping);
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
void generatesDtoPackageAndRemovesDuplicateResponseFields() throws Exception {
|
||||
@@ -152,16 +161,56 @@ class ToolScaffolderTest {
|
||||
assertTrue(response.indexOf("private String resultCode;") == response.lastIndexOf("private String resultCode;"), response);
|
||||
assertTrue(response.indexOf("private String employeeName;") == response.lastIndexOf("private String employeeName;"), response);
|
||||
String converter = Files.readString(root.resolve("dap-was-http/src/main/java/io/shinhanlife/dap/mcc/biz/smp/converter/EmployeeSearchConverter.java"));
|
||||
String legacyRequest = Files.readString(root.resolve("dap-was-http/src/main/java/io/shinhanlife/dap/mcc/biz/smp/legacy/EmployeeSearchLegacyRequest.java"));
|
||||
String httpRequest = Files.readString(root.resolve("dap-was-http/src/main/java/io/shinhanlife/dap/mcc/infra/itrf/http/sample/io/EmployeeSearchHttpRequest.java"));
|
||||
String httpResponse = Files.readString(root.resolve("dap-was-http/src/main/java/io/shinhanlife/dap/mcc/infra/itrf/http/sample/io/EmployeeSearchHttpResponse.java"));
|
||||
String httpClient = Files.readString(root.resolve("dap-was-http/src/main/java/io/shinhanlife/dap/mcc/infra/itrf/http/sample/SampleClient.java"));
|
||||
assertFalse(converter.contains("phoneNumber"), converter);
|
||||
assertTrue(converter.contains("unmappedTargetPolicy = ReportingPolicy.IGNORE"), converter);
|
||||
assertTrue(legacyRequest.contains("private String employeeId;"), legacyRequest);
|
||||
assertTrue(converter.contains("infra.itrf.http.sample.io.EmployeeSearchHttpRequest"), converter);
|
||||
assertTrue(httpRequest.contains("private String employeeId;"), httpRequest);
|
||||
assertTrue(httpResponse.contains("private String employeeName;"), httpResponse);
|
||||
assertTrue(httpClient.contains("http.call(API_NAME, request, responseType)"), httpClient);
|
||||
assertFalse(Files.exists(root.resolve("dap-was-http/src/main/java/io/shinhanlife/dap/mcc/biz/smp/legacy")));
|
||||
String implementation = Files.readString(root.resolve("dap-was-http/src/main/java/io/shinhanlife/dap/mcc/biz/smp/usecase/impl/EmployeeSearchUseCaseImpl.java"));
|
||||
assertTrue(implementation.contains("public EmployeeSearchResponse execute(EmployeeSearchRequest req)"), implementation);
|
||||
assertTrue(implementation.contains("import io.shinhanlife.dap.lib.integration.http.component.AxhubHttpComponent;"), implementation);
|
||||
assertTrue(implementation.contains("private final AxhubHttpComponent http;"), implementation);
|
||||
assertTrue(implementation.contains("AxhubHttpDomain.SAMPLE,"), implementation);
|
||||
assertTrue(implementation.contains("\"/HR_EMPLOYEE_SEARCH\""), implementation);
|
||||
assertTrue(implementation.contains("import io.shinhanlife.dap.mcc.infra.itrf.http.sample.SampleClient;"), implementation);
|
||||
assertTrue(implementation.contains("private final SampleClient sampleClient;"), implementation);
|
||||
assertTrue(implementation.contains("sampleClient.call(httpRequest, EmployeeSearchHttpResponse.class)"), implementation);
|
||||
assertFalse(implementation.contains("AxhubHttpComponent"), implementation);
|
||||
assertFalse(implementation.contains("executeLegacy(\"HTTP\""), implementation);
|
||||
Path wireMockResponse = root.resolve("mci-mock/__files/smp_employee_search.json");
|
||||
Path wireMockMapping = root.resolve("mci-mock/mappings/smp_employee_search.json");
|
||||
assertTrue(Files.exists(wireMockResponse), wireMockResponse.toString());
|
||||
assertTrue(Files.exists(wireMockMapping), wireMockMapping.toString());
|
||||
assertTrue(Files.readString(wireMockMapping).contains("\"urlPath\" : \"/HR_EMPLOYEE_SEARCH\""));
|
||||
}
|
||||
@Test
|
||||
void generatesSeparateToolTitleAndDescription() throws Exception {
|
||||
String moduleName = root.resolve("dap-was-title").toString();
|
||||
|
||||
ToolScaffolder.scaffold("employee search", "HR_EMPLOYEE_SEARCH", "직원 정보 조회",
|
||||
"사번을 입력받아 재직 중인 직원의 기본 정보를 조회한다.", "smp", "HTTP", moduleName,
|
||||
"tester", "2026.08.11", false, null, null, null, List.of(), List.of());
|
||||
|
||||
Path useCasePath = root.resolve("dap-was-title/src/main/java/io/shinhanlife/dap/mcc/biz/smp/usecase/EmployeeSearchUseCase.java");
|
||||
String useCase = Files.readString(useCasePath);
|
||||
|
||||
assertTrue(useCase.contains("title = \"직원 정보 조회\""), useCase);
|
||||
assertTrue(useCase.contains("description = \"사번을 입력받아 재직 중인 직원의 기본 정보를 조회한다.\""), useCase);
|
||||
}
|
||||
@Test
|
||||
void generatesHttpToolUsingConfiguredApiNameAndConfiguredUrlOnly() throws Exception {
|
||||
String moduleName = root.resolve("dap-was-http-api-name").toString();
|
||||
|
||||
ToolScaffolder.scaffold("employee search", "HR_EMPLOYEE_SEARCH", "직원 정보 조회",
|
||||
"사번으로 직원을 조회한다.", "smp", "HTTP", moduleName, "tester", "2026.08.11",
|
||||
false, null, null, null, List.of(), List.of(), "employee");
|
||||
|
||||
Path implementationPath = root.resolve("dap-was-http-api-name/src/main/java/io/shinhanlife/dap/mcc/biz/smp/usecase/impl/EmployeeSearchUseCaseImpl.java");
|
||||
String implementation = Files.readString(implementationPath);
|
||||
|
||||
assertTrue(implementation.contains("import io.shinhanlife.dap.mcc.infra.itrf.http.employee.EmployeeClient;"), implementation);
|
||||
assertTrue(implementation.contains("employeeClient.call(httpRequest, EmployeeSearchHttpResponse.class)"), implementation);
|
||||
assertFalse(implementation.contains("AxhubHttpDomain"), implementation);
|
||||
assertFalse(implementation.contains("\"/HR_EMPLOYEE_SEARCH\""), implementation);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,16 +21,16 @@ class ToolSourceUpdaterTest {
|
||||
import org.springaicommunity.mcp.annotation.McpTool;
|
||||
import io.shinhanlife.dap.lib.annotation.ToolHint;
|
||||
interface SampleUseCase {
|
||||
@McpTool(name = "oth_cmm_sample_search", description = "old")
|
||||
@McpTool(name = "cmm_sample_search", description = "old")
|
||||
@ToolHint(register = false, requiresApproval = false)
|
||||
void search();
|
||||
}
|
||||
""");
|
||||
|
||||
ToolSourceUpdater.updateToolSource(temporaryRoot, "oth_cmm_sample_search", "customer", "new", true, true);
|
||||
ToolSourceUpdater.updateToolSource(temporaryRoot, "cmm_sample_search", "customer", "new", true, true);
|
||||
|
||||
String updated = Files.readString(source);
|
||||
assertTrue(updated.contains("@McpTool(name = \"oth_cmm_sample_search\", description = \"new\")"));
|
||||
assertTrue(updated.contains("@McpTool(name = \"cmm_sample_search\", description = \"new\")"));
|
||||
assertTrue(updated.contains("@ToolHint(register = true, requiresApproval = true"));
|
||||
assertTrue(updated.contains("categoryKey = \"customer\""), updated);
|
||||
}
|
||||
|
||||
@@ -89,11 +89,11 @@ class McpToolNameValidatorTest {
|
||||
Path root = findProjectRoot();
|
||||
|
||||
assertToolName(root, "dap-was-oth/src/main/java/io/shinhanlife/dap/mcc/biz/cmm/usecase/CustomerInfoUseCase.java",
|
||||
"oth_cmm_customer_detail", "detail");
|
||||
"cmm_customer_detail", "detail");
|
||||
assertToolName(root, "dap-was-oth/src/main/java/io/shinhanlife/dap/mcc/biz/cmm/usecase/BillingProcessUseCase.java",
|
||||
"oth_cmm_billing_process", "process");
|
||||
"cmm_billing_process", "process");
|
||||
assertToolName(root, "dap-was-oth/src/main/java/io/shinhanlife/dap/mcc/biz/cmm/usecase/BondIssueUseCase.java",
|
||||
"oth_cmm_bond_issue", "issue");
|
||||
"cmm_bond_issue", "issue");
|
||||
}
|
||||
|
||||
private void assertToolName(Path root, String relativePath, String expectedName, String legacyName) throws IOException {
|
||||
|
||||
Reference in New Issue
Block a user