[Developer] #424 show generated ORDS handler source

This commit is contained in:
devmrko
2026-06-25 14:35:01 +09:00
parent 28dcde6975
commit daa302b9e7
4 changed files with 80 additions and 26 deletions

View File

@@ -48,6 +48,20 @@ public class OrdsMetadataService {
.toList();
}
public String objectQueryHandlerSource(long objectId) {
ProtectedObject object = protectedObjectService.assertEnabled(objectId);
List<String> columns = protectedObjectService.findColumns(objectId).stream()
.map(ProtectedColumn::columnName)
.toList();
if (columns.isEmpty()) {
throw new AppException("보호 객체 컬럼이 없어 ORDS 조회 Handler Source를 만들 수 없습니다.");
}
requireIdentifier(object.owner(), "owner");
requireIdentifier(object.objectName(), "objectName");
columns.forEach(column -> requireIdentifier(column, "column"));
return objectQuerySource(object, columns);
}
@Transactional
public void updateHandlerSource(OrdsHandlerUpdateCommand command) {
if (command.source() == null || command.source().isBlank()) {
@@ -97,14 +111,14 @@ public class OrdsMetadataService {
String currentUser = jdbcTemplate.queryForObject("SELECT USER FROM dual", String.class);
if (currentUser == null || !currentUser.equalsIgnoreCase("CB_ORDS")) {
throw new AppException("ORDS 조회 Handler 생성은 ORDS parsing schema(CB_ORDS)로 DB에 연결했을 때만 가능합니다. 현재 연결 사용자: "
+ currentUser);
+ currentUser + ". 현재 연결에서는 소스 보기를 눌러 기본 PL/SQL을 확인한 뒤 ORDS Handler에 적용하세요.");
}
String moduleName = "cb.object.query";
String basePath = "cb-object-query/";
String template = object.owner().toLowerCase(Locale.ROOT) + "/" + object.objectName().toLowerCase(Locale.ROOT);
String ordsPath = "cb-ords/" + basePath + template;
String source = objectQuerySource(object, columns);
String source = objectQueryHandlerSource(objectId);
jdbcTemplate.update("""
BEGIN

View File

@@ -79,6 +79,17 @@ public class ProtectedObjectController {
return "redirect:/objects";
}
@GetMapping("/objects/ords-handler-source")
public String ordsHandlerSource(@RequestParam long objectId, Model model) {
try {
model.addAttribute("object", protectedObjectService.assertEnabled(objectId));
model.addAttribute("source", ordsMetadataService.objectQueryHandlerSource(objectId));
} catch (AppException exception) {
model.addAttribute("errorMessage", exception.getMessage());
}
return "fragments/ords-handler-source :: source";
}
@PostMapping("/objects/disable")
public String disable(@RequestParam long objectId, RedirectAttributes redirectAttributes) {
protectedObjectService.disableObject(objectId);

View File

@@ -0,0 +1,15 @@
<!doctype html>
<html lang="ko" xmlns:th="http://www.thymeleaf.org">
<body>
<div th:fragment="source">
<div class="alert alert-danger mb-0" th:if="${errorMessage}" th:text="${errorMessage}"></div>
<section class="probe-exchange" th:if="${source}">
<h3>
<span th:text="${object.owner() + '.' + object.objectName()}">ADMIN.TABLE_NAME</span>
<span class="text-muted"> / 기본 ORDS Handler PL/SQL</span>
</h3>
<pre th:text="${source}">source</pre>
</section>
</div>
</body>
</html>

View File

@@ -51,33 +51,47 @@
</tr>
</thead>
<tbody>
<tr th:each="object : ${objects}">
<td th:text="${object.objectId()}">1</td>
<td th:text="${object.owner()}">ADMIN</td>
<td th:text="${object.objectName()}">CB_V_SEARCH_DOCUMENTS</td>
<td>
<form method="post" action="/objects/ords-path" class="inline-form">
<input type="hidden" th:name="${_csrf.parameterName}" th:value="${_csrf.token}">
<input type="hidden" name="objectId" th:value="${object.objectId()}">
<input class="form-control form-control-sm" name="ordsPath" th:value="${object.ordsPath()}" required>
<button class="btn btn-sm btn-outline-primary" type="submit">저장</button>
</form>
</td>
<td>
<div class="action-stack">
<form method="post" action="/objects/ords-handler" class="inline-form">
<th:block th:each="object : ${objects}">
<tr>
<td th:text="${object.objectId()}">1</td>
<td th:text="${object.owner()}">ADMIN</td>
<td th:text="${object.objectName()}">CB_V_SEARCH_DOCUMENTS</td>
<td>
<form method="post" action="/objects/ords-path" class="inline-form">
<input type="hidden" th:name="${_csrf.parameterName}" th:value="${_csrf.token}">
<input type="hidden" name="objectId" th:value="${object.objectId()}">
<button class="btn btn-sm btn-outline-primary" type="submit">Handler 생성</button>
<input class="form-control form-control-sm" name="ordsPath" th:value="${object.ordsPath()}" required>
<button class="btn btn-sm btn-outline-primary" type="submit">저장</button>
</form>
<form method="post" action="/objects/disable" class="inline-form">
<input type="hidden" th:name="${_csrf.parameterName}" th:value="${_csrf.token}">
<input type="hidden" name="objectId" th:value="${object.objectId()}">
<button class="btn btn-sm btn-outline-danger" type="submit">비활성화</button>
</form>
</div>
</td>
</tr>
</td>
<td>
<div class="action-stack">
<form method="post" action="/objects/ords-handler" class="inline-form">
<input type="hidden" th:name="${_csrf.parameterName}" th:value="${_csrf.token}">
<input type="hidden" name="objectId" th:value="${object.objectId()}">
<button class="btn btn-sm btn-outline-primary" type="submit">Handler 생성</button>
</form>
<button class="btn btn-sm btn-outline-secondary"
type="button"
th:hx-get="@{/objects/ords-handler-source(objectId=${object.objectId()})}"
th:hx-target="${'#handler-source-' + object.objectId()}"
hx-swap="innerHTML">소스 보기</button>
<form method="post" action="/objects/disable" class="inline-form">
<input type="hidden" th:name="${_csrf.parameterName}" th:value="${_csrf.token}">
<input type="hidden" name="objectId" th:value="${object.objectId()}">
<button class="btn btn-sm btn-outline-danger" type="submit">비활성화</button>
</form>
</div>
</td>
</tr>
<tr>
<td colspan="5" class="policy-source-cell">
<div th:id="${'handler-source-' + object.objectId()}" class="text-muted small">
소스 보기를 누르면 기본 Handler PL/SQL이 표시됩니다.
</div>
</td>
</tr>
</th:block>
<tr th:if="${#lists.isEmpty(objects)}">
<td colspan="5" class="text-muted">등록된 조회 Handler 대상이 없습니다.</td>
</tr>