diff --git a/src/main/java/com/cloudhandson/vpdbackoffice/domain/vpd/VpdFunctionSource.java b/src/main/java/com/cloudhandson/vpdbackoffice/domain/vpd/VpdFunctionSource.java new file mode 100644 index 0000000..fb30f9d --- /dev/null +++ b/src/main/java/com/cloudhandson/vpdbackoffice/domain/vpd/VpdFunctionSource.java @@ -0,0 +1,13 @@ +package com.cloudhandson.vpdbackoffice.domain.vpd; + +public record VpdFunctionSource( + String owner, + String objectName, + String objectType, + String source +) { + + public boolean found() { + return source != null && !source.isBlank(); + } +} diff --git a/src/main/java/com/cloudhandson/vpdbackoffice/mapper/VpdPolicyMapper.java b/src/main/java/com/cloudhandson/vpdbackoffice/mapper/VpdPolicyMapper.java index 4356e4d..0a79d0d 100644 --- a/src/main/java/com/cloudhandson/vpdbackoffice/mapper/VpdPolicyMapper.java +++ b/src/main/java/com/cloudhandson/vpdbackoffice/mapper/VpdPolicyMapper.java @@ -3,9 +3,16 @@ package com.cloudhandson.vpdbackoffice.mapper; import com.cloudhandson.vpdbackoffice.domain.vpd.VpdPolicyView; import java.util.List; import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; @Mapper public interface VpdPolicyMapper { List findPolicies(); + + String findFunctionSource( + @Param("owner") String owner, + @Param("objectName") String objectName, + @Param("objectType") String objectType + ); } diff --git a/src/main/java/com/cloudhandson/vpdbackoffice/service/VpdPolicyService.java b/src/main/java/com/cloudhandson/vpdbackoffice/service/VpdPolicyService.java index f77daf2..df06a24 100644 --- a/src/main/java/com/cloudhandson/vpdbackoffice/service/VpdPolicyService.java +++ b/src/main/java/com/cloudhandson/vpdbackoffice/service/VpdPolicyService.java @@ -1,8 +1,10 @@ package com.cloudhandson.vpdbackoffice.service; +import com.cloudhandson.vpdbackoffice.domain.vpd.VpdFunctionSource; import com.cloudhandson.vpdbackoffice.domain.vpd.VpdPolicyView; import com.cloudhandson.vpdbackoffice.mapper.VpdPolicyMapper; import java.util.List; +import java.util.Locale; import org.springframework.stereotype.Service; @Service @@ -17,4 +19,33 @@ public class VpdPolicyService { public List findPolicies() { return mapper.findPolicies(); } + + public VpdFunctionSource findFunctionSource(String owner, String packageName, String functionName) { + String normalizedOwner = requiredIdentifier(owner, "Function owner"); + String normalizedFunction = requiredIdentifier(functionName, "Function name"); + String normalizedPackage = normalizeOptionalIdentifier(packageName); + String objectName = normalizedPackage == null ? normalizedFunction : normalizedPackage; + String objectType = normalizedPackage == null ? "FUNCTION" : "PACKAGE BODY"; + String source = mapper.findFunctionSource(normalizedOwner, objectName, objectType); + return new VpdFunctionSource(normalizedOwner, objectName, objectType, source); + } + + private String requiredIdentifier(String value, String label) { + String normalized = normalizeOptionalIdentifier(value); + if (normalized == null) { + throw new AppException(label + " 값이 없습니다."); + } + return normalized; + } + + private String normalizeOptionalIdentifier(String value) { + if (value == null || value.isBlank()) { + return null; + } + String normalized = value.trim().toUpperCase(Locale.ROOT); + if (!normalized.matches("[A-Z][A-Z0-9_$#]{0,127}")) { + throw new AppException("Oracle identifier 형식이 올바르지 않습니다: " + value); + } + return normalized; + } } diff --git a/src/main/java/com/cloudhandson/vpdbackoffice/web/VpdPolicyController.java b/src/main/java/com/cloudhandson/vpdbackoffice/web/VpdPolicyController.java index d62f310..6369ad0 100644 --- a/src/main/java/com/cloudhandson/vpdbackoffice/web/VpdPolicyController.java +++ b/src/main/java/com/cloudhandson/vpdbackoffice/web/VpdPolicyController.java @@ -1,11 +1,13 @@ package com.cloudhandson.vpdbackoffice.web; +import com.cloudhandson.vpdbackoffice.service.AppException; import com.cloudhandson.vpdbackoffice.service.VpdPolicyService; import java.util.List; import org.springframework.dao.DataAccessException; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestParam; @Controller public class VpdPolicyController { @@ -27,4 +29,22 @@ public class VpdPolicyController { } return "vpd-policies"; } + + @GetMapping("/vpd-policies/function-source") + public String functionSource( + @RequestParam String owner, + @RequestParam(required = false) String packageName, + @RequestParam String functionName, + Model model + ) { + try { + model.addAttribute("source", vpdPolicyService.findFunctionSource(owner, packageName, functionName)); + } catch (AppException exception) { + model.addAttribute("errorMessage", exception.getMessage()); + } catch (DataAccessException exception) { + RuntimeErrorMessage message = RuntimeErrorMessages.dataAccess(exception); + model.addAttribute("errorMessage", message.message()); + } + return "fragments/vpd-function-source :: source"; + } } diff --git a/src/main/resources/mapper/VpdPolicyMapper.xml b/src/main/resources/mapper/VpdPolicyMapper.xml index 34364d0..e53df28 100644 --- a/src/main/resources/mapper/VpdPolicyMapper.xml +++ b/src/main/resources/mapper/VpdPolicyMapper.xml @@ -27,4 +27,12 @@ FROM all_policies ORDER BY object_owner, object_name, policy_name + + diff --git a/src/main/resources/static/css/app.css b/src/main/resources/static/css/app.css index c900b55..a64fc21 100644 --- a/src/main/resources/static/css/app.css +++ b/src/main/resources/static/css/app.css @@ -457,6 +457,18 @@ body { white-space: pre-wrap; } +.policy-function-button { + border-radius: 8px; + max-width: 100%; + overflow-wrap: anywhere; + text-align: left; +} + +.policy-source-cell { + background: var(--rw-surface) !important; + padding: 0 0 .75rem !important; +} + .ai-answer { background: var(--rw-primary-soft); border: 1px solid var(--rw-border); diff --git a/src/main/resources/templates/fragments/vpd-function-source.html b/src/main/resources/templates/fragments/vpd-function-source.html new file mode 100644 index 0000000..d6828f2 --- /dev/null +++ b/src/main/resources/templates/fragments/vpd-function-source.html @@ -0,0 +1,16 @@ + + + +
+
+
+

+ OWNER.OBJECT + / FUNCTION +

+

+    
ALL_SOURCE에서 조회 가능한 소스가 없습니다. 현재 DB 계정의 소스 조회 권한을 확인하세요.
+
+
+ + diff --git a/src/main/resources/templates/vpd-policies.html b/src/main/resources/templates/vpd-policies.html index a79c733..ec4d882 100644 --- a/src/main/resources/templates/vpd-policies.html +++ b/src/main/resources/templates/vpd-policies.html @@ -34,13 +34,22 @@ - + + ADMIN.TABLE
SYS_DEFAULT
POLICY - OWNER.FUNC + + + SELECT Long: NO + + +
+ Policy function을 클릭하면 소스가 표시됩니다. +
+ + +
조회 가능한 VPD policy가 없습니다. 정책이 없거나 현재 DB 계정에 dictionary 조회 권한이 없을 수 있습니다.