refactor: remove sample HTTP tool integration
Some checks failed
Deploy to OCIWP / deploy (push) Has been cancelled

This commit is contained in:
jade
2026-08-11 23:41:57 +09:00
parent 0e27937687
commit 6662641903
28 changed files with 72 additions and 371 deletions

View File

@@ -90,7 +90,7 @@ public class ScaffoldingController {
if (date == null || date.trim().isEmpty()) date = LocalDate.now().format(DateTimeFormatter.ofPattern("yyyy.MM.dd"));
boolean register = Boolean.parseBoolean(req.getOrDefault("register", "true"));
String clientSystemCode = req.get("clientSystemCode");
String httpApiName = req.getOrDefault("httpApiName", "sample");
String httpApiName = req.get("httpApiName");
String inputSchemaResource = req.get("inputSchemaResource");
String outputSchemaResource = req.get("outputSchemaResource");
List<ToolScaffolder.FieldDefinition> inputFields = parseFields(req.get("inputFields"));
@@ -259,7 +259,7 @@ public class ScaffoldingController {
if (!Set.of("HTTP", "MCI").contains(routingType)) {
throw new IllegalArgumentException("AI가 지원하지 않는 Protocol을 생성했습니다.");
}
String httpApiName = draft.httpApiName() == null ? "sample" : draft.httpApiName().trim();
String httpApiName = draft.httpApiName() == null ? "http-api" : draft.httpApiName().trim();
if (!httpApiName.matches("^[A-Za-z0-9_-]+$")) {
throw new IllegalArgumentException("AI가 올바르지 않은 HTTP API Name을 생성했습니다.");
}

View File

@@ -36,6 +36,7 @@ mcp:
fallback:
default-url: http://localhost:8084
routes:
cmm_memo_retriever: http://localhost:8082
sms: http://localhost:8082
email: http://localhost:8083
payment: http://localhost:8085

View File

@@ -568,7 +568,7 @@
<div class="row mb-4">
<div class="col-md-6">
<label class="form-label">HTTP API Name (Glow api-list)</label>
<input type="text" class="form-control" name="httpApiName" value="sample" pattern="^[a-zA-Z0-9_-]+$" placeholder="e.g. employee">
<input type="text" class="form-control" name="httpApiName" pattern="^[a-zA-Z0-9_-]+$" placeholder="비우면 Tool 이름으로 자동 생성">
<div class="input-hint">HTTP only. Must match glow.communication.http.api-list[].name; URL and method are read from Glow YAML.</div>
</div>
<div class="col-md-6 mt-3 mt-md-0">
@@ -1053,7 +1053,7 @@
form.elements.description.value = result.description || '';
form.elements.categoryKey.value = result.categoryKey || '';
form.elements.routingType.value = result.routingType || 'HTTP';
form.elements.httpApiName.value = result.httpApiName || 'sample';
form.elements.httpApiName.value = result.httpApiName || '';
document.getElementById('inputFields').value = JSON.stringify(result.inputFields || [], null, 2);
document.getElementById('outputFields').value = JSON.stringify(result.outputFields || [], null, 2);
alert('Tool 초안을 채웠습니다. Legacy Interface ID와 Client System Code는 실제 연계 명세를 확인해 입력해주세요.');

View File

@@ -47,16 +47,6 @@ public class AxhubHttpComponent {
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) {
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);
@@ -66,15 +56,6 @@ public class AxhubHttpComponent {
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);

View File

@@ -1,16 +0,0 @@
package io.shinhanlife.dap.lib.integration.http.component;
/** Registered outbound HTTP API domains. Add a domain only after its endpoint is configured. */
public enum AxhubHttpDomain {
SAMPLE("sample");
private final String code;
AxhubHttpDomain(String code) {
this.code = code;
}
public String getCode() {
return code;
}
}

View File

@@ -117,7 +117,8 @@ public class ToolScaffolder {
*/
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");
register, clientSystemCode, inputSchemaResource, outputSchemaResource, inputFields, outputFields,
toKebabCase(toPascalCase(baseName)));
}
/**
@@ -127,7 +128,7 @@ public class ToolScaffolder {
baseName = toPascalCase(baseName);
title = title == null || title.isBlank() ? baseName : title.trim();
description = description == null ? "" : description.trim();
httpApiName = httpApiName == null || httpApiName.isBlank() ? "sample" : httpApiName.trim();
httpApiName = httpApiName == null || httpApiName.isBlank() ? toKebabCase(baseName) : httpApiName.trim();
String envSourceDir = System.getenv("AXHUB_SOURCE_DIR");
Path rootDir = envSourceDir != null ? Paths.get(envSourceDir) : Paths.get(".");
@@ -860,7 +861,7 @@ public class ToolScaffolder {
Files.writeString(wireMockBodyPath, mockResponse);
Files.writeString(wireMockMappingPath, wireMockMappingContent(interfaceId, wireMockBodyPath.getFileName().toString()));
Files.writeString(podMockResponsePath, mockResponse);
ensureLocalHttpApiConfiguration(moduleRoot, httpApiName, toolName);
ensureLocalHttpApiConfiguration(projectRoot, httpApiName, toolName);
log.append("[WireMock Response] ").append(wireMockBodyPath).append("\n");
log.append("[WireMock Mapping] ").append(wireMockMappingPath).append("\n");
log.append("[Pod Mock Response] ").append(podMockResponsePath).append("\n");
@@ -889,8 +890,8 @@ public class ToolScaffolder {
.toLowerCase(Locale.ROOT);
}
private static void ensureLocalHttpApiConfiguration(Path moduleRoot, String httpApiName, String toolName) throws IOException {
Path localConfigPath = moduleRoot.resolve("src/main/resources/application-local.yml");
private static void ensureLocalHttpApiConfiguration(Path projectRoot, String httpApiName, String toolName) throws IOException {
Path localConfigPath = projectRoot.resolve("dap-was-lib/src/main/resources/glow/application-glow-local.yml");
Files.createDirectories(localConfigPath.getParent());
String existing = Files.exists(localConfigPath) ? Files.readString(localConfigPath) : "";
if (java.util.regex.Pattern.compile("(?m)^\\s*-\\s+name:\\s*"
@@ -898,17 +899,7 @@ public class ToolScaffolder {
return;
}
String environmentKey = toPackageSegment(httpApiName).toUpperCase(Locale.ROOT).replace('-', '_');
String config = """
axhub:
mock:
http:
enabled: true
glow:
communication:
http:
api-list:
String apiEntry = """
- name: %s
domain: ${AXHUB_%s_HTTP_DOMAIN:http://localhost:${server.port}}
url: ${AXHUB_%s_HTTP_URL:/api/mock/http/%s}
@@ -916,7 +907,37 @@ public class ToolScaffolder {
content-type: application/json;charset=UTF-8
biz-pod: false
""".formatted(httpApiName, environmentKey, environmentKey, toolName);
Files.writeString(localConfigPath, existing + config);
if (existing.isBlank()) {
existing = """
spring:
config:
activate:
on-profile: local
glow:
communication:
http:
api-list:
""" + apiEntry;
} else if (existing.contains("\n mci:")) {
existing = existing.replace("\n mci:", apiEntry + " mci:");
} else if (existing.contains("\naxhub:")) {
existing = existing.replace("\naxhub:", apiEntry + "axhub:");
} else if (existing.contains("api-list:")) {
existing += apiEntry;
} else {
throw new IllegalStateException("application-glow-local.yml must define glow.communication.http.api-list");
}
if (!existing.contains("axhub:\n mock:\n http:\n enabled: true")) {
existing += """
axhub:
mock:
http:
enabled: true
""";
}
Files.writeString(localConfigPath, existing);
}
private static String dtoContent(String packageName, String className, List<FieldDefinition> fields,
@@ -1059,7 +1080,7 @@ public class ToolScaffolder {
String normalized = value == null ? "" : value.toLowerCase(Locale.ROOT)
.replaceAll("[^a-z0-9]+", "_")
.replaceAll("^_+|_+$", "");
return normalized.isBlank() ? "sample" : normalized;
return normalized.isBlank() ? "http_api" : normalized;
}
private static String mciConverterContent(String bizPackage, String baseName, String mciPackage, String interfaceId) {
return """

View File

@@ -13,9 +13,9 @@ glow:
connection-timeout: 5
read-timeout: 5
api-list:
- name: sample
domain: ${AXHUB_SAMPLE_HTTP_DOMAIN:http://localhost:8089}
url: ${AXHUB_SAMPLE_HTTP_URL:/CLCNNB00001}
- name: memo
domain: ${AXHUB_MEMO_HTTP_DOMAIN:http://localhost:${server.port}}
url: ${AXHUB_MEMO_HTTP_URL:/api/mock/http/cmm_memo_retriever}
method: POST
content-type: application/json;charset=UTF-8
biz-pod: false
@@ -28,3 +28,8 @@ glow:
eai:
host: ${GLOW_COMMUNICATION_EAI_HOST:http://localhost}
port: ${GLOW_COMMUNICATION_EAI_PORT:8080}
axhub:
mock:
http:
enabled: true

View File

@@ -21,18 +21,8 @@ glow:
http:
connection-timeout: 5
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://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
# HTTP Tool target catalog. Scaffold adds local mock entries after a Tool is created.
api-list: []
mci:
uri: /ntl_mci/dap_rcv
receive-uri: /itrf/mciReceive

View File

@@ -19,13 +19,13 @@ import org.springframework.web.client.RestClient;
class AxhubHttpComponentTest {
@Test
void callResolvesDomainBuildsGlowTransferAndDeserializesJsonResponse() {
void callByApiNameBuildsGlowTransferAndDeserializesJsonResponse() {
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(
"sample", "https://api.example.test", "/v1", HttpMethod.GET, "application/json", false)));
"status", "https://api.example.test", "/v1", HttpMethod.GET, "application/json", false)));
AxhubHttpComponent component = new AxhubHttpComponent(
glowHttpComponent, new ObjectMapper(), new GlowCommunicationProperties(), properties);
@@ -33,7 +33,7 @@ class AxhubHttpComponentTest {
.andExpect(header("X-ANONYMOUS-REQ", "AXHUB-TOOL"))
.andRespond(withSuccess("{\"status\":\"OK\"}", APPLICATION_JSON));
SampleResponse response = component.call(AxhubHttpDomain.SAMPLE, "/status", null, SampleResponse.class);
SampleResponse response = component.call("status", "/status", null, SampleResponse.class);
assertThat(response.status()).isEqualTo("OK");
server.verify();

View File

@@ -162,11 +162,11 @@ 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 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"));
String httpRequest = Files.readString(root.resolve("dap-was-http/src/main/java/io/shinhanlife/dap/mcc/infra/itrf/http/employee_search/io/EmployeeSearchHttpRequest.java"));
String httpResponse = Files.readString(root.resolve("dap-was-http/src/main/java/io/shinhanlife/dap/mcc/infra/itrf/http/employee_search/io/EmployeeSearchHttpResponse.java"));
String httpClient = Files.readString(root.resolve("dap-was-http/src/main/java/io/shinhanlife/dap/mcc/infra/itrf/http/employee_search/EmployeeSearchClient.java"));
assertFalse(converter.contains("phoneNumber"), converter);
assertTrue(converter.contains("infra.itrf.http.sample.io.EmployeeSearchHttpRequest"), converter);
assertTrue(converter.contains("infra.itrf.http.employee_search.io.EmployeeSearchHttpRequest"), converter);
assertFalse(converter.contains("io.shinhanlife.dap.mcc.io.shinhanlife.dap.mcc"), converter);
assertTrue(converter.contains("// @Mapping(source = \"sourceField\", target = \"targetField\")"), converter);
assertTrue(httpRequest.contains("private String employeeId;"), httpRequest);
@@ -175,9 +175,9 @@ class ToolScaffolderTest {
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.mcc.infra.itrf.http.sample.SampleClient;"), implementation);
assertTrue(implementation.contains("private final SampleClient sampleClient;"), implementation);
assertTrue(implementation.contains("sampleClient.call(httpRequest, EmployeeSearchHttpResponse.class)"), implementation);
assertTrue(implementation.contains("import io.shinhanlife.dap.mcc.infra.itrf.http.employee_search.EmployeeSearchClient;"), implementation);
assertTrue(implementation.contains("private final EmployeeSearchClient employeeSearchClient;"), implementation);
assertTrue(implementation.contains("employeeSearchClient.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");
@@ -185,17 +185,17 @@ class ToolScaffolderTest {
assertTrue(Files.exists(wireMockResponse), wireMockResponse.toString());
assertTrue(Files.exists(wireMockMapping), wireMockMapping.toString());
assertTrue(Files.readString(wireMockMapping).contains("\"urlPath\" : \"/HR_EMPLOYEE_SEARCH\""));
Path localConfig = root.resolve("dap-was-http/src/main/resources/application-local.yml");
Path localConfig = root.resolve("dap-was-lib/src/main/resources/glow/application-glow-local.yml");
assertTrue(Files.exists(localConfig), localConfig.toString());
assertTrue(Files.readString(localConfig).contains("name: sample"));
assertTrue(Files.readString(localConfig).contains("url: ${AXHUB_SAMPLE_HTTP_URL:/api/mock/http/smp_employee_search}"));
assertTrue(Files.readString(localConfig).contains("name: employee-search"));
assertTrue(Files.readString(localConfig).contains("url: ${AXHUB_EMPLOYEE_SEARCH_HTTP_URL:/api/mock/http/smp_employee_search}"));
Path podMockResponse = root.resolve("dap-was-http/src/main/resources/mock-responses/smp_employee_search.json");
assertTrue(Files.exists(podMockResponse), podMockResponse.toString());
ToolScaffolder.scaffold("employee search", "HR_EMPLOYEE_SEARCH", "Employee search", "smp", "HTTP", moduleName,
"tester", "2026.08.11", false, null, null, null, inputFields, outputFields);
long apiNameCount = Files.readAllLines(localConfig).stream()
.filter(line -> line.trim().equals("- name: sample"))
.filter(line -> line.trim().equals("- name: employee-search"))
.count();
assertEquals(1, apiNameCount);
}

View File

@@ -1,9 +0,0 @@
package io.shinhanlife.dap.mcc.biz.smp.dto;
import io.swagger.v3.oas.annotations.media.Schema;
/** Tool response for the HTTP integration sample. */
public record SampleHttpStatusResponse(
@Schema(description = "External API processing status", example = "OK") String status,
@Schema(description = "External API response message", example = "Sample API is available") String message) {
}

View File

@@ -1,15 +0,0 @@
package io.shinhanlife.dap.mcc.biz.smp.usecase;
import io.shinhanlife.dap.lib.annotation.ToolHint;
import io.shinhanlife.dap.mcc.biz.smp.dto.SampleHttpStatusResponse;
import org.springaicommunity.mcp.annotation.McpTool;
/** Sample Tool that demonstrates a configured HTTP API integration. */
public interface SampleHttpStatusUseCase {
@McpTool(name = "smp_sample_status",
title = "Sample external HTTP API status",
description = "Calls the configured sample HTTP API and returns its status.")
@ToolHint(register = false, categoryKey = "smp", mappingId = "HTTP_SAMPLE_001")
SampleHttpStatusResponse execute();
}

View File

@@ -1,21 +0,0 @@
package io.shinhanlife.dap.mcc.biz.smp.usecase.impl;
import io.shinhanlife.dap.mcc.biz.smp.dto.SampleHttpStatusResponse;
import io.shinhanlife.dap.mcc.biz.smp.usecase.SampleHttpStatusUseCase;
import io.shinhanlife.dap.mcc.infra.itrf.http.sample.SampleHttpApiClient;
import io.shinhanlife.dap.mcc.infra.itrf.http.sample.SampleHttpApiResponse;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
@Service
@RequiredArgsConstructor
public class SampleHttpStatusUseCaseImpl implements SampleHttpStatusUseCase {
private final SampleHttpApiClient sampleHttpApiClient;
@Override
public SampleHttpStatusResponse execute() {
SampleHttpApiResponse response = sampleHttpApiClient.getStatus();
return new SampleHttpStatusResponse(response.status(), response.message());
}
}

View File

@@ -1,18 +0,0 @@
package io.shinhanlife.dap.mcc.infra.itrf.http.sample;
import io.shinhanlife.dap.lib.integration.http.component.AxhubHttpComponent;
import io.shinhanlife.dap.lib.integration.http.component.AxhubHttpDomain;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Component;
/** Example of a Tool-specific HTTP client using the configured AX HUB HTTP domain. */
@Component
@RequiredArgsConstructor
public class SampleHttpApiClient {
private final AxhubHttpComponent http;
public SampleHttpApiResponse getStatus() {
return http.call(AxhubHttpDomain.SAMPLE, "/status", null, SampleHttpApiResponse.class);
}
}

View File

@@ -1,5 +0,0 @@
package io.shinhanlife.dap.mcc.infra.itrf.http.sample;
/** DTO matching the external API JSON response: { "status": "OK", "message": "..." }. */
public record SampleHttpApiResponse(String status, String message) {
}

View File

@@ -1,14 +0,0 @@
package io.shinhanlife.dap.mcc.biz.smp.converter;
import io.shinhanlife.dap.mcc.biz.smp.dto.EmployeeSearchRequest;
import io.shinhanlife.dap.mcc.biz.smp.dto.EmployeeSearchResponse;
import io.shinhanlife.dap.mcc.infra.itrf.http.sample.io.EmployeeSearchHttpRequest;
import io.shinhanlife.dap.mcc.infra.itrf.http.sample.io.EmployeeSearchHttpResponse;
import org.mapstruct.Mapper;
import org.mapstruct.ReportingPolicy;
@Mapper(componentModel = "spring", unmappedTargetPolicy = ReportingPolicy.IGNORE)
public interface EmployeeSearchConverter {
EmployeeSearchHttpRequest toHttpRequest(EmployeeSearchRequest request);
EmployeeSearchResponse toResponse(EmployeeSearchHttpResponse httpResponse);
}

View File

@@ -1,16 +0,0 @@
package io.shinhanlife.dap.mcc.biz.smp.dto;
import com.fasterxml.jackson.annotation.JsonInclude;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
@Data
@JsonInclude(JsonInclude.Include.NON_NULL)
public class EmployeeSearchRequest {
@Schema(description = "조회할 사번", example = "EMP10001", requiredMode = Schema.RequiredMode.REQUIRED)
private String employeeId;
@Schema(description = "직원명. 사번 없이 이름으로 조회할 때 사용", example = "홍길동")
private String employeeName;
}

View File

@@ -1,19 +0,0 @@
package io.shinhanlife.dap.mcc.biz.smp.dto;
import com.fasterxml.jackson.annotation.JsonInclude;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
@Data
@JsonInclude(JsonInclude.Include.NON_NULL)
public class EmployeeSearchResponse {
private String resultCode;
private String resultMessage;
@Schema(description = "직원명", example = "홍길동")
private String employeeName;
@Schema(description = "소속 부서명", example = "AX추진팀")
private String departmentName;
}

View File

@@ -1,27 +0,0 @@
package io.shinhanlife.dap.mcc.biz.smp.usecase;
import org.springaicommunity.mcp.annotation.McpTool;
import io.shinhanlife.dap.lib.annotation.ToolHint;
import io.shinhanlife.dap.mcc.biz.smp.dto.EmployeeSearchRequest;
import io.shinhanlife.dap.mcc.biz.smp.dto.EmployeeSearchResponse;
/**
* @package io.shinhanlife.dap.mcc.biz.smp.usecase
* @className EmployeeSearchUseCase
* @description AX HUB ?쒖뒪??泥섎━ ?대옒??
* @author jade
* @create 2026.08.11
* <pre>
* ---------- 媛쒖젙?대젰 ----------
* ?섏젙?? ?섏젙?? ?섏젙?댁슜
* ---------- -------- ---------------------------
* 2026.08.11 jade 理쒖큹?앹꽦
*
* </pre>
*/
public interface EmployeeSearchUseCase {
@McpTool(name = "smp_employee_search", title = "직원 정보 조회", description = "사번 또는 직원명을 기준으로 직원 정보를 조회합니다.")
@ToolHint(register = false, categoryKey = "smp", mappingId = "CLCNNB00001")
EmployeeSearchResponse execute(EmployeeSearchRequest req);
}

View File

@@ -1,30 +0,0 @@
package io.shinhanlife.dap.mcc.biz.smp.usecase.impl;
import io.shinhanlife.dap.mcc.biz.smp.converter.EmployeeSearchConverter;
import io.shinhanlife.dap.mcc.biz.smp.dto.EmployeeSearchRequest;
import io.shinhanlife.dap.mcc.biz.smp.dto.EmployeeSearchResponse;
import io.shinhanlife.dap.mcc.biz.smp.usecase.EmployeeSearchUseCase;
import io.shinhanlife.dap.mcc.infra.itrf.http.sample.SampleClient;
import io.shinhanlife.dap.mcc.infra.itrf.http.sample.io.EmployeeSearchHttpRequest;
import io.shinhanlife.dap.mcc.infra.itrf.http.sample.io.EmployeeSearchHttpResponse;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
@Service
@RequiredArgsConstructor
public class EmployeeSearchUseCaseImpl implements EmployeeSearchUseCase {
private final EmployeeSearchConverter converter;
private final SampleClient sampleClient;
@Override
public EmployeeSearchResponse execute(EmployeeSearchRequest request) {
EmployeeSearchHttpRequest httpRequest = converter.toHttpRequest(request);
EmployeeSearchHttpResponse httpResponse = sampleClient.call(httpRequest, EmployeeSearchHttpResponse.class);
EmployeeSearchResponse response = converter.toResponse(httpResponse);
response.setResultCode("SUCCESS");
response.setResultMessage("HTTP API call completed.");
return response;
}
}

View File

@@ -1,17 +0,0 @@
package io.shinhanlife.dap.mcc.infra.itrf.http.sample;
import io.shinhanlife.dap.lib.integration.http.component.AxhubHttpComponent;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Component;
@Component
@RequiredArgsConstructor
public class SampleClient {
private static final String API_NAME = "sample";
private final AxhubHttpComponent http;
public <I, O> O call(I request, Class<O> responseType) {
return http.call(API_NAME, request, responseType);
}
}

View File

@@ -1,11 +0,0 @@
package io.shinhanlife.dap.mcc.infra.itrf.http.sample.io;
import com.fasterxml.jackson.annotation.JsonInclude;
import lombok.Data;
@Data
@JsonInclude(JsonInclude.Include.NON_NULL)
public class EmployeeSearchHttpRequest {
private String employeeId;
private String employeeName;
}

View File

@@ -1,12 +0,0 @@
package io.shinhanlife.dap.mcc.infra.itrf.http.sample.io;
import com.fasterxml.jackson.annotation.JsonInclude;
import lombok.Data;
@Data
@JsonInclude(JsonInclude.Include.NON_NULL)
public class EmployeeSearchHttpResponse {
private String resultCode;
private String employeeName;
private String departmentName;
}

View File

@@ -26,17 +26,3 @@ axhub:
url: http://localhost:8081
tool:
url: ${AXHUB_TOOL_URL:http://localhost:${server.port}}
mock:
http:
enabled: true
glow:
communication:
http:
api-list:
- name: memo
domain: ${AXHUB_MEMO_HTTP_DOMAIN:http://localhost:${server.port}}
url: ${AXHUB_MEMO_HTTP_URL:/api/mock/http/cmm_memo_retriever}
method: POST
content-type: application/json;charset=UTF-8
biz-pod: false

View File

@@ -1,27 +0,0 @@
package io.shinhanlife.dap.mcc.biz.smp.usecase;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import io.shinhanlife.dap.mcc.infra.itrf.http.sample.SampleClient;
import io.shinhanlife.dap.mcc.biz.smp.dto.EmployeeSearchRequest;
import io.shinhanlife.dap.mcc.biz.smp.dto.EmployeeSearchResponse;
import io.shinhanlife.dap.mcc.biz.smp.usecase.impl.EmployeeSearchUseCaseImpl;
import org.junit.jupiter.api.Test;
class EmployeeSearchUseCaseTest {
@Test
void createsToolRequestAndResponseDtos() {
assertNotNull(new EmployeeSearchRequest());
assertNotNull(new EmployeeSearchResponse());
}
@Test
void delegatesHttpCallThroughApiSpecificClient() {
assertNotNull(EmployeeSearchUseCaseImpl.class.getDeclaredFields());
assertTrue(java.util.Arrays.stream(EmployeeSearchUseCaseImpl.class.getDeclaredFields())
.anyMatch(field -> field.getType().equals(SampleClient.class)));
}
}

View File

@@ -61,10 +61,6 @@ services:
- AXHUB_GATEWAY_URL=http://gateway:8081
- AXHUB_SOURCE_DIR=/src
- AXHUB_TOOL_URL=http://was-sms:8082
# HTTP Tool sample target inside the Docker network.
# WireMock 서비스명과 컨테이너 포트(8080)를 사용합니다.
- AXHUB_SAMPLE_HTTP_DOMAIN=http://mci-mock:8080
- AXHUB_SAMPLE_HTTP_URL=/CLCNNB00001
- GLOW_COMMUNICATION_MCI_HOMT=http://mci-mock
- GLOW_COMMUNICATION_MCI_PORT=8080
- GLOW_COMMUNICATION_EXTMCI_HOMT=http://mci-mock
@@ -88,10 +84,6 @@ services:
- SPRING_DATA_REDIS_PORT=6379
- AXHUB_GATEWAY_URL=http://gateway:8081
- AXHUB_TOOL_URL=http://was-oth:8084
# HTTP Tool sample target inside the Docker network.
# WireMock 서비스명과 컨테이너 포트(8080)를 사용합니다.
- AXHUB_SAMPLE_HTTP_DOMAIN=http://mci-mock:8080
- AXHUB_SAMPLE_HTTP_URL=/CLCNNB00001
- GLOW_COMMUNICATION_MCI_HOMT=http://mci-mock
- GLOW_COMMUNICATION_MCI_PORT=8080
- GLOW_COMMUNICATION_EXTMCI_HOMT=http://mci-mock

View File

@@ -1,5 +0,0 @@
{
"resultCode" : "SUCCESS",
"employeeName" : "홍길동",
"departmentName" : "AX추진팀"
}

View File

@@ -1,13 +0,0 @@
{
"request" : {
"method" : "POST",
"urlPath" : "/CLCNNB00001"
},
"response" : {
"status" : 200,
"headers" : {
"Content-Type" : "application/json;charset=UTF-8"
},
"bodyFileName" : "smp_employee_search.json"
}
}