From 40303ee9b8fea434fd1297c5eed1ffe3ebe526f3 Mon Sep 17 00:00:00 2001 From: jade Date: Mon, 10 Aug 2026 17:24:28 +0900 Subject: [PATCH] feat: add Glow HTTP tool integration sample --- .../http/component/AxhubHttpComponent.java | 124 ++++++++++++++++++ .../http/component/AxhubHttpDomain.java | 16 +++ .../http/component/AxhubHttpProperties.java | 22 ++++ .../glow/communication/ICommunication.java | 6 + .../http/component/GlowHttpComponent.java | 98 ++++++++++++++ .../module/http/dto/HttpBody.java | 5 + .../module/http/dto/HttpHeader.java | 18 +++ .../module/http/dto/HttpTransfer.java | 19 +++ .../component/AxhubHttpComponentTest.java | 43 ++++++ .../biz/smp/dto/SampleHttpStatusResponse.java | 9 ++ .../smp/usecase/SampleHttpStatusUseCase.java | 15 +++ .../impl/SampleHttpStatusUseCaseImpl.java | 21 +++ .../itrf/http/sample/SampleHttpApiClient.java | 18 +++ .../http/sample/SampleHttpApiResponse.java | 5 + .../src/main/resources/application-local.yml | 8 ++ 15 files changed, 427 insertions(+) create mode 100644 dap-was-lib/src/main/java/io/shinhanlife/dap/lib/integration/http/component/AxhubHttpComponent.java create mode 100644 dap-was-lib/src/main/java/io/shinhanlife/dap/lib/integration/http/component/AxhubHttpDomain.java create mode 100644 dap-was-lib/src/main/java/io/shinhanlife/dap/lib/integration/http/component/AxhubHttpProperties.java create mode 100644 dap-was-lib/src/main/java/io/shinhanlife/glow/communication/ICommunication.java create mode 100644 dap-was-lib/src/main/java/io/shinhanlife/glow/communication/module/http/component/GlowHttpComponent.java create mode 100644 dap-was-lib/src/main/java/io/shinhanlife/glow/communication/module/http/dto/HttpBody.java create mode 100644 dap-was-lib/src/main/java/io/shinhanlife/glow/communication/module/http/dto/HttpHeader.java create mode 100644 dap-was-lib/src/main/java/io/shinhanlife/glow/communication/module/http/dto/HttpTransfer.java create mode 100644 dap-was-lib/src/test/java/io/shinhanlife/dap/lib/integration/http/component/AxhubHttpComponentTest.java create mode 100644 dap-was-oth/src/main/java/io/shinhanlife/dap/mcc/biz/smp/dto/SampleHttpStatusResponse.java create mode 100644 dap-was-oth/src/main/java/io/shinhanlife/dap/mcc/biz/smp/usecase/SampleHttpStatusUseCase.java create mode 100644 dap-was-oth/src/main/java/io/shinhanlife/dap/mcc/biz/smp/usecase/impl/SampleHttpStatusUseCaseImpl.java create mode 100644 dap-was-oth/src/main/java/io/shinhanlife/dap/mcc/infra/itrf/http/sample/SampleHttpApiClient.java create mode 100644 dap-was-oth/src/main/java/io/shinhanlife/dap/mcc/infra/itrf/http/sample/SampleHttpApiResponse.java diff --git a/dap-was-lib/src/main/java/io/shinhanlife/dap/lib/integration/http/component/AxhubHttpComponent.java b/dap-was-lib/src/main/java/io/shinhanlife/dap/lib/integration/http/component/AxhubHttpComponent.java new file mode 100644 index 00000000..37323b7f --- /dev/null +++ b/dap-was-lib/src/main/java/io/shinhanlife/dap/lib/integration/http/component/AxhubHttpComponent.java @@ -0,0 +1,124 @@ +package io.shinhanlife.dap.lib.integration.http.component; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import io.shinhanlife.dap.lib.config.GlowCommunicationProperties; +import io.shinhanlife.dap.lib.mcp.McpRequestHeaderContext; +import io.shinhanlife.dap.lib.mcp.McpRequestHeaders; +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. + */ +@Slf4j +@Component +@RequiredArgsConstructor +public class AxhubHttpComponent { + + private static final String ANONYMOUS_REQUEST = "AXHUB-TOOL"; + + private final GlowHttpComponent http; + private final ObjectMapper json; + private final GlowCommunicationProperties communicationProperties; + private final AxhubHttpProperties properties; + + public R call(AxhubHttpDomain domain, String uri, T inputDto, Class responseBodyClass) { + return call(domain, uri, inputDto, responseBodyClass, 0); + } + + public R call(AxhubHttpDomain domain, String uri, T inputDto, Class responseBodyClass, int timeout) { + if (uri == null || uri.isBlank()) { + throw new IllegalArgumentException("URI is required."); + } + AxhubHttpProperties.ApiDefinition api = resolveApi(domain); + HttpHeader header = createHeader(api, timeout); + String requestUri = joinPath(api.path(), uri); + HttpTransfer request = HttpTransfer.http() + .header(header) + .domain(api.domain()) + .uri(requestUri) + .method(api.method()) + .contentType(MediaType.APPLICATION_JSON) + .responseEntity(responseBodyClass) + .body(inputDto) + .build(); + + log.info("[AxhubHttpComponent] Glow HTTP call. domain={}, method={}, uri={}", + domain.getCode(), api.method(), requestUri); + ResponseEntity response = http.sync(request); + return convertResponse(response.getBody(), responseBodyClass); + } + + /** Convenience method for APIs configured as internal business Pods. */ + public R callBizPod(AxhubHttpDomain domain, String uri, T inputDto, Class 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) { + return properties.getApiList().stream() + .filter(api -> domain.getCode().equals(api.name())) + .findFirst() + .orElseThrow(() -> new IllegalArgumentException("No HTTP API configuration for domain: " + domain.getCode())); + } + + private HttpHeader createHeader(AxhubHttpProperties.ApiDefinition api, int timeout) { + HttpHeader header = new HttpHeader(); + if (api.bizPod()) { + header.set("X-POD-TO-POD", "true"); + } + McpRequestHeaders inbound = McpRequestHeaderContext.current(); + if (inbound == null) { + header.set("X-ANONYMOUS-REQ", ANONYMOUS_REQUEST); + } else { + putIfPresent(header, "trace-id", inbound.traceId()); + putIfPresent(header, "request-id", inbound.requestId()); + putIfPresent(header, "X-USER-ID", inbound.encryptedEmployeeId()); + } + header.setReadTimeout(timeout == 0 ? defaultReadTimeout() : timeout); + return header; + } + + private int defaultReadTimeout() { + return communicationProperties == null || communicationProperties.getHttp() == null + ? 0 : communicationProperties.getHttp().getReadTimeout(); + } + + private R convertResponse(HttpBody responseBody, Class responseBodyClass) { + String content = responseBody == null ? null : responseBody.content(); + if (responseBodyClass == String.class) { + return responseBodyClass.cast(content); + } + try { + return json.readValue(content, responseBodyClass); + } catch (JsonProcessingException e) { + throw new IllegalStateException("Failed to convert HTTP response to " + responseBodyClass.getSimpleName(), e); + } + } + + private void putIfPresent(HttpHeader header, String name, String value) { + if (value != null && !value.isBlank()) { + header.set(name, value); + } + } + + private String joinPath(String basePath, String uri) { + String left = basePath == null ? "" : basePath.replaceAll("/+$", ""); + String right = uri.startsWith("/") ? uri : "/" + uri; + return left + right; + } +} \ No newline at end of file diff --git a/dap-was-lib/src/main/java/io/shinhanlife/dap/lib/integration/http/component/AxhubHttpDomain.java b/dap-was-lib/src/main/java/io/shinhanlife/dap/lib/integration/http/component/AxhubHttpDomain.java new file mode 100644 index 00000000..4dc24c18 --- /dev/null +++ b/dap-was-lib/src/main/java/io/shinhanlife/dap/lib/integration/http/component/AxhubHttpDomain.java @@ -0,0 +1,16 @@ +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; + } +} \ No newline at end of file diff --git a/dap-was-lib/src/main/java/io/shinhanlife/dap/lib/integration/http/component/AxhubHttpProperties.java b/dap-was-lib/src/main/java/io/shinhanlife/dap/lib/integration/http/component/AxhubHttpProperties.java new file mode 100644 index 00000000..8237898c --- /dev/null +++ b/dap-was-lib/src/main/java/io/shinhanlife/dap/lib/integration/http/component/AxhubHttpProperties.java @@ -0,0 +1,22 @@ +package io.shinhanlife.dap.lib.integration.http.component; + +import java.util.ArrayList; +import java.util.List; +import lombok.Getter; +import lombok.Setter; +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. */ +@Getter +@Setter +@Component +@ConfigurationProperties(prefix = "axhub.http") +public class AxhubHttpProperties { + + private List apiList = new ArrayList<>(); + + public record ApiDefinition(String name, String domain, String path, HttpMethod method, boolean bizPod) { + } +} \ No newline at end of file diff --git a/dap-was-lib/src/main/java/io/shinhanlife/glow/communication/ICommunication.java b/dap-was-lib/src/main/java/io/shinhanlife/glow/communication/ICommunication.java new file mode 100644 index 00000000..f93b1ea2 --- /dev/null +++ b/dap-was-lib/src/main/java/io/shinhanlife/glow/communication/ICommunication.java @@ -0,0 +1,6 @@ +package io.shinhanlife.glow.communication; + +/** Minimal Glow communication contract used by the temporary compatibility layer. */ +public interface ICommunication { + O sync(I request); +} \ No newline at end of file diff --git a/dap-was-lib/src/main/java/io/shinhanlife/glow/communication/module/http/component/GlowHttpComponent.java b/dap-was-lib/src/main/java/io/shinhanlife/glow/communication/module/http/component/GlowHttpComponent.java new file mode 100644 index 00000000..34265031 --- /dev/null +++ b/dap-was-lib/src/main/java/io/shinhanlife/glow/communication/module/http/component/GlowHttpComponent.java @@ -0,0 +1,98 @@ +package io.shinhanlife.glow.communication.module.http.component; + +import io.shinhanlife.glow.communication.ICommunication; +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.List; +import java.util.Map; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpMethod; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Component; +import org.springframework.util.StringUtils; +import org.springframework.web.client.RestClient; +import org.springframework.web.util.UriComponentsBuilder; + +/** + * Temporary compatibility implementation of the internal Glow HTTP component. + * Replace this class with the official Glow HTTP JAR when it is supplied. + */ +@Component +public class GlowHttpComponent implements ICommunication, ResponseEntity> { + + private final RestClient restClient; + + public GlowHttpComponent(RestClient.Builder restClientBuilder) { + this.restClient = restClientBuilder.build(); + } + + @Override + public ResponseEntity sync(HttpTransfer request) { + if (request == null || request.getHeader() == null || request.getMethod() == null + || !StringUtils.hasText(request.getDomain())) { + throw new IllegalArgumentException("Glow HTTP request header, domain, and method are required."); + } + if (HttpMethod.GET.equals(request.getMethod())) { + return get(request); + } + if (HttpMethod.POST.equals(request.getMethod())) { + return post(request); + } + if (HttpMethod.PUT.equals(request.getMethod())) { + return put(request); + } + if (HttpMethod.DELETE.equals(request.getMethod())) { + return delete(request); + } + throw new IllegalArgumentException("Unsupported HTTP method: " + request.getMethod()); + } + + private ResponseEntity get(HttpTransfer request) { + return toHttpBody(restClient.get().uri(buildUri(request, true)) + .headers(headers -> applyHeaders(headers, request.getHeader())) + .retrieve().toEntity(String.class)); + } + + private ResponseEntity post(HttpTransfer request) { + return toHttpBody(restClient.post().uri(buildUri(request, false)) + .contentType(contentType(request)).headers(headers -> applyHeaders(headers, request.getHeader())) + .body(request.getBody()).retrieve().toEntity(String.class)); + } + + private ResponseEntity put(HttpTransfer request) { + return toHttpBody(restClient.put().uri(buildUri(request, false)) + .contentType(contentType(request)).headers(headers -> applyHeaders(headers, request.getHeader())) + .body(request.getBody()).retrieve().toEntity(String.class)); + } + + private ResponseEntity delete(HttpTransfer request) { + return toHttpBody(restClient.delete().uri(buildUri(request, true)) + .headers(headers -> applyHeaders(headers, request.getHeader())) + .retrieve().toEntity(String.class)); + } + + private String buildUri(HttpTransfer request, boolean includeQueryParameters) { + String uri = request.getDomain() + (request.getUri() == null ? "" : request.getUri()); + if (!includeQueryParameters || !(request.getBody() instanceof Map parameters)) { + return uri; + } + UriComponentsBuilder builder = UriComponentsBuilder.fromUriString(uri); + parameters.forEach((key, value) -> { if (key != null && value != null) builder.queryParam(String.valueOf(key), value); }); + return builder.build().encode().toUriString(); + } + + private MediaType contentType(HttpTransfer request) { + return request.getContentType() == null ? MediaType.APPLICATION_JSON : request.getContentType(); + } + + private void applyHeaders(HttpHeaders target, HttpHeader source) { + target.setAccept(List.of(MediaType.APPLICATION_JSON)); + source.getValues().forEach((name, value) -> { if (StringUtils.hasText(name) && StringUtils.hasText(value)) target.set(name, value); }); + } + + private ResponseEntity toHttpBody(ResponseEntity response) { + return new ResponseEntity<>(new HttpBody(response.getBody()), response.getHeaders(), response.getStatusCode()); + } +} \ No newline at end of file diff --git a/dap-was-lib/src/main/java/io/shinhanlife/glow/communication/module/http/dto/HttpBody.java b/dap-was-lib/src/main/java/io/shinhanlife/glow/communication/module/http/dto/HttpBody.java new file mode 100644 index 00000000..2406e7b5 --- /dev/null +++ b/dap-was-lib/src/main/java/io/shinhanlife/glow/communication/module/http/dto/HttpBody.java @@ -0,0 +1,5 @@ +package io.shinhanlife.glow.communication.module.http.dto; + +/** Raw body returned by the Glow HTTP transport. */ +public record HttpBody(String content) { +} \ No newline at end of file diff --git a/dap-was-lib/src/main/java/io/shinhanlife/glow/communication/module/http/dto/HttpHeader.java b/dap-was-lib/src/main/java/io/shinhanlife/glow/communication/module/http/dto/HttpHeader.java new file mode 100644 index 00000000..396f4a7d --- /dev/null +++ b/dap-was-lib/src/main/java/io/shinhanlife/glow/communication/module/http/dto/HttpHeader.java @@ -0,0 +1,18 @@ +package io.shinhanlife.glow.communication.module.http.dto; + +import java.util.LinkedHashMap; +import java.util.Map; +import lombok.Getter; +import lombok.Setter; + +/** Glow HTTP request headers and timeout metadata. */ +@Getter +@Setter +public class HttpHeader { + private Map values = new LinkedHashMap<>(); + private int readTimeout; + + public void set(String name, String value) { + values.put(name, value); + } +} \ No newline at end of file diff --git a/dap-was-lib/src/main/java/io/shinhanlife/glow/communication/module/http/dto/HttpTransfer.java b/dap-was-lib/src/main/java/io/shinhanlife/glow/communication/module/http/dto/HttpTransfer.java new file mode 100644 index 00000000..30fb28e9 --- /dev/null +++ b/dap-was-lib/src/main/java/io/shinhanlife/glow/communication/module/http/dto/HttpTransfer.java @@ -0,0 +1,19 @@ +package io.shinhanlife.glow.communication.module.http.dto; + +import lombok.Builder; +import lombok.Getter; +import org.springframework.http.HttpMethod; +import org.springframework.http.MediaType; + +/** Glow HTTP request envelope. Use HttpTransfer.http() to build a request. */ +@Getter +@Builder(builderMethodName = "http") +public class HttpTransfer { + private final HttpHeader header; + private final String domain; + private final String uri; + private final HttpMethod method; + private final MediaType contentType; + private final Class responseEntity; + private final T body; +} \ No newline at end of file diff --git a/dap-was-lib/src/test/java/io/shinhanlife/dap/lib/integration/http/component/AxhubHttpComponentTest.java b/dap-was-lib/src/test/java/io/shinhanlife/dap/lib/integration/http/component/AxhubHttpComponentTest.java new file mode 100644 index 00000000..edb6c79f --- /dev/null +++ b/dap-was-lib/src/test/java/io/shinhanlife/dap/lib/integration/http/component/AxhubHttpComponentTest.java @@ -0,0 +1,43 @@ +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.requestTo; +import static org.springframework.test.web.client.response.MockRestResponseCreators.withSuccess; + +import com.fasterxml.jackson.databind.ObjectMapper; +import io.shinhanlife.dap.lib.config.GlowCommunicationProperties; +import io.shinhanlife.glow.communication.module.http.component.GlowHttpComponent; +import java.util.List; +import org.junit.jupiter.api.Test; +import org.springframework.http.HttpMethod; +import org.springframework.test.web.client.MockRestServiceServer; +import org.springframework.web.client.RestClient; + +class AxhubHttpComponentTest { + + @Test + void callResolvesDomainBuildsGlowTransferAndDeserializesJsonResponse() { + 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, false))); + AxhubHttpComponent component = new AxhubHttpComponent( + glowHttpComponent, new ObjectMapper(), new GlowCommunicationProperties(), properties); + + server.expect(requestTo("https://api.example.test/v1/status")) + .andExpect(header("X-ANONYMOUS-REQ", "AXHUB-TOOL")) + .andRespond(withSuccess("{\"status\":\"OK\"}", APPLICATION_JSON)); + + SampleResponse response = component.call(AxhubHttpDomain.SAMPLE, "/status", null, SampleResponse.class); + + assertThat(response.status()).isEqualTo("OK"); + server.verify(); + } + + record SampleResponse(String status) { + } +} \ No newline at end of file diff --git a/dap-was-oth/src/main/java/io/shinhanlife/dap/mcc/biz/smp/dto/SampleHttpStatusResponse.java b/dap-was-oth/src/main/java/io/shinhanlife/dap/mcc/biz/smp/dto/SampleHttpStatusResponse.java new file mode 100644 index 00000000..c39aaa86 --- /dev/null +++ b/dap-was-oth/src/main/java/io/shinhanlife/dap/mcc/biz/smp/dto/SampleHttpStatusResponse.java @@ -0,0 +1,9 @@ +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) { +} \ No newline at end of file diff --git a/dap-was-oth/src/main/java/io/shinhanlife/dap/mcc/biz/smp/usecase/SampleHttpStatusUseCase.java b/dap-was-oth/src/main/java/io/shinhanlife/dap/mcc/biz/smp/usecase/SampleHttpStatusUseCase.java new file mode 100644 index 00000000..cb74912b --- /dev/null +++ b/dap-was-oth/src/main/java/io/shinhanlife/dap/mcc/biz/smp/usecase/SampleHttpStatusUseCase.java @@ -0,0 +1,15 @@ +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 = "oth.smp.sample.http.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(); +} \ No newline at end of file diff --git a/dap-was-oth/src/main/java/io/shinhanlife/dap/mcc/biz/smp/usecase/impl/SampleHttpStatusUseCaseImpl.java b/dap-was-oth/src/main/java/io/shinhanlife/dap/mcc/biz/smp/usecase/impl/SampleHttpStatusUseCaseImpl.java new file mode 100644 index 00000000..0ddee3f3 --- /dev/null +++ b/dap-was-oth/src/main/java/io/shinhanlife/dap/mcc/biz/smp/usecase/impl/SampleHttpStatusUseCaseImpl.java @@ -0,0 +1,21 @@ +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()); + } +} \ No newline at end of file diff --git a/dap-was-oth/src/main/java/io/shinhanlife/dap/mcc/infra/itrf/http/sample/SampleHttpApiClient.java b/dap-was-oth/src/main/java/io/shinhanlife/dap/mcc/infra/itrf/http/sample/SampleHttpApiClient.java new file mode 100644 index 00000000..22767044 --- /dev/null +++ b/dap-was-oth/src/main/java/io/shinhanlife/dap/mcc/infra/itrf/http/sample/SampleHttpApiClient.java @@ -0,0 +1,18 @@ +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); + } +} \ No newline at end of file diff --git a/dap-was-oth/src/main/java/io/shinhanlife/dap/mcc/infra/itrf/http/sample/SampleHttpApiResponse.java b/dap-was-oth/src/main/java/io/shinhanlife/dap/mcc/infra/itrf/http/sample/SampleHttpApiResponse.java new file mode 100644 index 00000000..9eb93085 --- /dev/null +++ b/dap-was-oth/src/main/java/io/shinhanlife/dap/mcc/infra/itrf/http/sample/SampleHttpApiResponse.java @@ -0,0 +1,5 @@ +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) { +} \ No newline at end of file diff --git a/dap-was-oth/src/main/resources/application-local.yml b/dap-was-oth/src/main/resources/application-local.yml index 1e73a06d..b0854d89 100644 --- a/dap-was-oth/src/main/resources/application-local.yml +++ b/dap-was-oth/src/main/resources/application-local.yml @@ -43,6 +43,14 @@ axhub: url: http://localhost:8081 tool: url: ${AXHUB_TOOL_URL:http://localhost:${server.port}} + # Registered outbound HTTP APIs. Tool code selects the domain name, never a free-form URL. + http: + api-list: + - name: sample + domain: ${AXHUB_SAMPLE_HTTP_DOMAIN:http://localhost:8099} + path: "" + method: GET + biz-pod: false sol: req-detail: