From 7d3bc02123055a3eaded97766270fddc612c5a6b Mon Sep 17 00:00:00 2001 From: devmrko Date: Tue, 23 Jun 2026 11:54:57 +0900 Subject: [PATCH] [Developer] #424 show ORDS request response details Refs #424 --- .../domain/probe/ProbeResult.java | 32 ++++++- .../service/OrdsProbeService.java | 96 ++++++++++++++++++- src/main/resources/static/css/app.css | 34 +++++++ .../templates/fragments/probe-result.html | 19 ++++ 4 files changed, 174 insertions(+), 7 deletions(-) diff --git a/src/main/java/com/cloudhandson/vpdbackoffice/domain/probe/ProbeResult.java b/src/main/java/com/cloudhandson/vpdbackoffice/domain/probe/ProbeResult.java index e942032..6f5b64d 100644 --- a/src/main/java/com/cloudhandson/vpdbackoffice/domain/probe/ProbeResult.java +++ b/src/main/java/com/cloudhandson/vpdbackoffice/domain/probe/ProbeResult.java @@ -10,10 +10,38 @@ public record ProbeResult( int rowCount, List maskedColumns, String errorCode, - String errorMessage + String errorMessage, + String requestHeaders, + String requestPayload, + String responseHeaders, + String responseBody ) { public static ProbeResult blocked(ProbeStatus status, String errorCode, String errorMessage) { - return new ProbeResult(status, List.of(), List.of(), 0, List.of(), errorCode, errorMessage); + return blocked(status, errorCode, errorMessage, null, null, null, null); + } + + public static ProbeResult blocked( + ProbeStatus status, + String errorCode, + String errorMessage, + String requestHeaders, + String requestPayload, + String responseHeaders, + String responseBody + ) { + return new ProbeResult( + status, + List.of(), + List.of(), + 0, + List.of(), + errorCode, + errorMessage, + requestHeaders, + requestPayload, + responseHeaders, + responseBody + ); } } diff --git a/src/main/java/com/cloudhandson/vpdbackoffice/service/OrdsProbeService.java b/src/main/java/com/cloudhandson/vpdbackoffice/service/OrdsProbeService.java index f303327..4f5c95d 100644 --- a/src/main/java/com/cloudhandson/vpdbackoffice/service/OrdsProbeService.java +++ b/src/main/java/com/cloudhandson/vpdbackoffice/service/OrdsProbeService.java @@ -97,21 +97,47 @@ public class OrdsProbeService { ProbeStatus.OBJECT_DISABLED, "OBJECT_DISABLED", e.getMessage())); } + String requestHeaders = null; + String requestPayload = null; try { URI uri = buildUri(object.ordsPath(), command.limit()); HttpHeaders headers = new HttpHeaders(); headers.setBearerAuth(command.bearerToken()); + requestHeaders = prettyHeaders(maskedRequestHeaders(headers)); + requestPayload = prettyJson("{}"); ResponseEntity response = ordsRestTemplate.exchange( uri, HttpMethod.POST, new HttpEntity<>(headers), String.class); - ProbeResult result = parseSuccess(response.getBody(), object.objectId()); + ProbeResult result = parseSuccess( + response.getBody(), + object.objectId(), + requestHeaders, + requestPayload, + prettyHeaders(response.getHeaders()), + prettyJson(response.getBody()) + ); return auditAndReturn(command, result); } catch (HttpStatusCodeException e) { ProbeStatus status = errorClassifier.classify(e.getStatusCode(), e.getResponseBodyAsString()); return auditAndReturn(command, ProbeResult.blocked( - status, status.name(), trimMessage(e.getResponseBodyAsString()))); + status, + status.name(), + trimMessage(e.getResponseBodyAsString()), + requestHeaders, + requestPayload, + prettyHeaders(e.getResponseHeaders()), + prettyJson(e.getResponseBodyAsString()) + )); } catch (ResourceAccessException e) { ProbeStatus status = classifyResourceAccess(e); - return auditAndReturn(command, ProbeResult.blocked(status, status.name(), resourceAccessMessage(status, e))); + return auditAndReturn(command, ProbeResult.blocked( + status, + status.name(), + resourceAccessMessage(status, e), + requestHeaders, + requestPayload, + "{}", + "" + )); } catch (Exception e) { return auditAndReturn(command, ProbeResult.blocked( ProbeStatus.INVALID_ORDS_RESPONSE, "INVALID_ORDS_RESPONSE", e.getMessage())); @@ -129,7 +155,14 @@ public class OrdsProbeService { .toUri(); } - private ProbeResult parseSuccess(String body, long objectId) throws Exception { + private ProbeResult parseSuccess( + String body, + long objectId, + String requestHeaders, + String requestPayload, + String responseHeaders, + String responseBody + ) throws Exception { JsonNode root = objectMapper.readTree(body); JsonNode rowsNode = root.has("rows") ? root.get("rows") : root; rowsNode = root.has("items") ? root.get("items") : rowsNode; @@ -153,7 +186,11 @@ public class OrdsProbeService { rows.size(), findMaskedColumns(objectId, rows), null, - null + null, + requestHeaders, + requestPayload, + responseHeaders, + responseBody ); } @@ -230,4 +267,53 @@ public class OrdsProbeService { } return body.length() <= 500 ? body : body.substring(0, 500); } + + private HttpHeaders maskedRequestHeaders(HttpHeaders headers) { + HttpHeaders masked = new HttpHeaders(); + masked.putAll(headers); + List authorization = headers.get(HttpHeaders.AUTHORIZATION); + if (authorization != null && !authorization.isEmpty()) { + masked.set(HttpHeaders.AUTHORIZATION, maskBearer(authorization.get(0))); + } + return masked; + } + + private String maskBearer(String value) { + if (value == null || value.isBlank()) { + return ""; + } + if (!value.toLowerCase(Locale.ROOT).startsWith("bearer ")) { + return "****"; + } + String token = value.substring("Bearer ".length()); + String suffix = token.length() <= 6 ? "" : token.substring(token.length() - 6); + return "Bearer ****" + suffix; + } + + private String prettyHeaders(HttpHeaders headers) { + if (headers == null || headers.isEmpty()) { + return "{}"; + } + return prettyObject(headers); + } + + private String prettyJson(String body) { + if (body == null || body.isBlank()) { + return ""; + } + try { + return objectMapper.writerWithDefaultPrettyPrinter() + .writeValueAsString(objectMapper.readTree(body)); + } catch (Exception ignored) { + return body; + } + } + + private String prettyObject(Object value) { + try { + return objectMapper.writerWithDefaultPrettyPrinter().writeValueAsString(value); + } catch (Exception ignored) { + return String.valueOf(value); + } + } } diff --git a/src/main/resources/static/css/app.css b/src/main/resources/static/css/app.css index 3da194b..d05b45b 100644 --- a/src/main/resources/static/css/app.css +++ b/src/main/resources/static/css/app.css @@ -105,3 +105,37 @@ body { gap: 1rem; margin-bottom: .75rem; } + +.probe-exchange-grid { + display: grid; + gap: .75rem; + grid-template-columns: repeat(auto-fit, minmax(260px, 1fr)); + margin: 1rem 0; +} + +.probe-exchange { + border: 1px solid #e4e7ec; + border-radius: 8px; + overflow: hidden; +} + +.probe-exchange h3 { + background: #f2f4f7; + border-bottom: 1px solid #e4e7ec; + font-size: .85rem; + font-weight: 700; + margin: 0; + padding: .55rem .75rem; +} + +.probe-exchange pre { + background: #101828; + color: #f9fafb; + font-size: .78rem; + margin: 0; + max-height: 320px; + overflow: auto; + padding: .75rem; + white-space: pre-wrap; + word-break: break-word; +} diff --git a/src/main/resources/templates/fragments/probe-result.html b/src/main/resources/templates/fragments/probe-result.html index add45c7..e7e97f9 100644 --- a/src/main/resources/templates/fragments/probe-result.html +++ b/src/main/resources/templates/fragments/probe-result.html @@ -20,6 +20,25 @@ +
+
+

Request Headers

+
{}
+
+
+

Request Payload

+
{}
+
+
+

Response Headers

+
{}
+
+
+

Response Body

+

+    
+
+