diff --git a/src/main/java/com/cloudhandson/vpdbackoffice/domain/ords/OrdsHandlerUpdateCommand.java b/src/main/java/com/cloudhandson/vpdbackoffice/domain/ords/OrdsHandlerUpdateCommand.java new file mode 100644 index 0000000..074093d --- /dev/null +++ b/src/main/java/com/cloudhandson/vpdbackoffice/domain/ords/OrdsHandlerUpdateCommand.java @@ -0,0 +1,9 @@ +package com.cloudhandson.vpdbackoffice.domain.ords; + +import jakarta.validation.constraints.NotBlank; + +public record OrdsHandlerUpdateCommand( + long handlerId, + @NotBlank String source +) { +} diff --git a/src/main/java/com/cloudhandson/vpdbackoffice/domain/ords/OrdsHandlerView.java b/src/main/java/com/cloudhandson/vpdbackoffice/domain/ords/OrdsHandlerView.java index 303ee01..c88f053 100644 --- a/src/main/java/com/cloudhandson/vpdbackoffice/domain/ords/OrdsHandlerView.java +++ b/src/main/java/com/cloudhandson/vpdbackoffice/domain/ords/OrdsHandlerView.java @@ -1,7 +1,11 @@ package com.cloudhandson.vpdbackoffice.domain.ords; +import java.util.Locale; + public record OrdsHandlerView( + Long handlerId, String schemaName, + String parsingSchema, String moduleName, String basePath, String template, @@ -15,4 +19,8 @@ public record OrdsHandlerView( public String fullPath() { return "/ords/" + schemaName + "/" + basePath + template; } + + public boolean plsqlEditable() { + return sourceType != null && sourceType.toLowerCase(Locale.ROOT).contains("plsql"); + } } diff --git a/src/main/java/com/cloudhandson/vpdbackoffice/mapper/OrdsMetadataMapper.java b/src/main/java/com/cloudhandson/vpdbackoffice/mapper/OrdsMetadataMapper.java index f0f2ab7..1914484 100644 --- a/src/main/java/com/cloudhandson/vpdbackoffice/mapper/OrdsMetadataMapper.java +++ b/src/main/java/com/cloudhandson/vpdbackoffice/mapper/OrdsMetadataMapper.java @@ -3,11 +3,14 @@ package com.cloudhandson.vpdbackoffice.mapper; import com.cloudhandson.vpdbackoffice.domain.ords.OrdsHandlerView; import java.util.List; import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; @Mapper public interface OrdsMetadataMapper { List findHandlers(); + OrdsHandlerView findHandler(@Param("handlerId") long handlerId); + String findHandlerPackageSource(); } diff --git a/src/main/java/com/cloudhandson/vpdbackoffice/service/OrdsMetadataService.java b/src/main/java/com/cloudhandson/vpdbackoffice/service/OrdsMetadataService.java index 74aa297..6bd8ed7 100644 --- a/src/main/java/com/cloudhandson/vpdbackoffice/service/OrdsMetadataService.java +++ b/src/main/java/com/cloudhandson/vpdbackoffice/service/OrdsMetadataService.java @@ -1,24 +1,32 @@ package com.cloudhandson.vpdbackoffice.service; +import com.cloudhandson.vpdbackoffice.domain.ords.OrdsHandlerUpdateCommand; import com.cloudhandson.vpdbackoffice.domain.ords.OrdsHandlerView; import com.cloudhandson.vpdbackoffice.mapper.OrdsMetadataMapper; import java.util.List; +import java.util.Locale; +import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; @Service public class OrdsMetadataService { private final OrdsMetadataMapper mapper; + private final JdbcTemplate jdbcTemplate; - public OrdsMetadataService(OrdsMetadataMapper mapper) { + public OrdsMetadataService(OrdsMetadataMapper mapper, JdbcTemplate jdbcTemplate) { this.mapper = mapper; + this.jdbcTemplate = jdbcTemplate; } public List findHandlers() { String packageSource = mapper.findHandlerPackageSource(); return mapper.findHandlers().stream() .map(handler -> new OrdsHandlerView( + handler.handlerId(), handler.schemaName(), + handler.parsingSchema(), handler.moduleName(), handler.basePath(), handler.template(), @@ -30,4 +38,37 @@ public class OrdsMetadataService { )) .toList(); } + + @Transactional + public void updateHandlerSource(OrdsHandlerUpdateCommand command) { + if (command.source() == null || command.source().isBlank()) { + throw new AppException("ORDS Handler Source는 필수입니다."); + } + OrdsHandlerView handler = mapper.findHandler(command.handlerId()); + if (handler == null) { + throw new AppException("수정할 ORDS Handler를 찾을 수 없습니다."); + } + String sourceType = handler.sourceType() == null ? "" : handler.sourceType().toLowerCase(Locale.ROOT); + if (!sourceType.contains("plsql")) { + throw new AppException("현재는 PL/SQL ORDS Handler만 수정할 수 있습니다: " + handler.sourceType()); + } + String currentUser = jdbcTemplate.queryForObject("SELECT USER FROM dual", String.class); + if (currentUser == null || !currentUser.equalsIgnoreCase(handler.parsingSchema())) { + throw new AppException("ORDS Handler 수정은 parsing schema로 DB에 연결했을 때만 가능합니다. 현재 연결 사용자: " + + currentUser + ", Handler parsing schema: " + handler.parsingSchema()); + } + jdbcTemplate.update(""" + BEGIN + ORDS.DEFINE_HANDLER( + p_module_name => ?, + p_pattern => ?, + p_method => ?, + p_source_type => ORDS.source_type_plsql, + p_source => ?, + p_items_per_page => 0 + ); + COMMIT; + END; + """, handler.moduleName(), handler.template(), handler.method(), command.source()); + } } diff --git a/src/main/java/com/cloudhandson/vpdbackoffice/web/OrdsHandlerController.java b/src/main/java/com/cloudhandson/vpdbackoffice/web/OrdsHandlerController.java index bcae68e..2797ccb 100644 --- a/src/main/java/com/cloudhandson/vpdbackoffice/web/OrdsHandlerController.java +++ b/src/main/java/com/cloudhandson/vpdbackoffice/web/OrdsHandlerController.java @@ -1,9 +1,15 @@ package com.cloudhandson.vpdbackoffice.web; +import com.cloudhandson.vpdbackoffice.domain.ords.OrdsHandlerUpdateCommand; +import com.cloudhandson.vpdbackoffice.service.AppException; import com.cloudhandson.vpdbackoffice.service.OrdsMetadataService; +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.PostMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.servlet.mvc.support.RedirectAttributes; @Controller public class OrdsHandlerController { @@ -19,4 +25,22 @@ public class OrdsHandlerController { model.addAttribute("handlers", ordsMetadataService.findHandlers()); return "ords-handlers"; } + + @PostMapping("/ords-handlers/update") + public String update( + @RequestParam long handlerId, + @RequestParam String source, + RedirectAttributes redirectAttributes + ) { + try { + ordsMetadataService.updateHandlerSource(new OrdsHandlerUpdateCommand(handlerId, source)); + redirectAttributes.addFlashAttribute("message", "ORDS Handler Source를 저장했습니다."); + } catch (AppException exception) { + redirectAttributes.addFlashAttribute("errorMessage", exception.getMessage()); + } catch (DataAccessException exception) { + RuntimeErrorMessage error = RuntimeErrorMessages.dataAccess(exception); + redirectAttributes.addFlashAttribute("errorMessage", error.message()); + } + return "redirect:/ords-handlers"; + } } diff --git a/src/main/resources/mapper/OrdsMetadataMapper.xml b/src/main/resources/mapper/OrdsMetadataMapper.xml index 5c6c3af..c8fe7fc 100644 --- a/src/main/resources/mapper/OrdsMetadataMapper.xml +++ b/src/main/resources/mapper/OrdsMetadataMapper.xml @@ -2,21 +2,27 @@ + + h.id AS handler_id, + s.pattern AS schema_name, + s.parsing_schema, + m.name AS module_name, + m.uri_prefix AS base_path, + t.uri_template AS template, + h.method, + h.source_type, + DBMS_LOB.SUBSTR(h.source, 12000, 1) AS source, + ( + SELECT LISTAGG(p.name || ' -> :' || p.bind_variable_name || ' (' || p.source_type || ')', CHR(10)) + WITHIN GROUP (ORDER BY p.name) + FROM dba_ords_parameters p + WHERE p.handler_id = h.id + ) AS parameters, + CAST(NULL AS VARCHAR2(1)) AS package_source + + + + + + +
+ +
+

Bearer Token -> User Context Package