refs #723: externalize backoffice catalogs and MCP tools

This commit is contained in:
devmrko
2026-07-23 19:41:54 +09:00
parent 1917df09a2
commit 0d9028ef13
58 changed files with 1671 additions and 531 deletions

View File

@@ -1,6 +1,5 @@
package com.cloudhandson.vpdbackoffice.service;
import com.cloudhandson.vpdbackoffice.domain.structured.StructuredDataCatalog;
import com.cloudhandson.vpdbackoffice.domain.structured.StructuredDataPreview;
import com.cloudhandson.vpdbackoffice.domain.structured.StructuredDataTable;
import java.util.List;
@@ -12,18 +11,20 @@ import org.springframework.stereotype.Service;
@Service
public class StructuredDataService {
private static final int ROW_LIMIT = 50;
private final JdbcTemplate jdbcTemplate;
private final StructuredDataCatalog catalog;
private final DataCatalog catalog;
public StructuredDataService(
JdbcTemplate jdbcTemplate,
StructuredDataCatalogProvider catalogProvider
DataCatalog catalog
) {
this.jdbcTemplate = jdbcTemplate;
this.catalog = catalogProvider.catalog();
this.catalog = catalog;
}
public StructuredDataCatalog catalog() {
public DataCatalog catalog() {
return catalog;
}
@@ -32,18 +33,15 @@ public class StructuredDataService {
}
public List<StructuredDataTable> tables() {
return catalog.tables();
return catalog.objects();
}
public String defaultKey() {
return catalog.tables().getFirst().key();
return catalog.objects().getFirst().key();
}
public StructuredDataTable requireTable(String key) {
return catalog.tables().stream()
.filter(table -> table.key().equals(key))
.findFirst()
.orElseThrow(() -> new AppException("선택할 수 없는 정형 데이터 테이블입니다."));
return catalog.require(key);
}
public StructuredDataPreview preview(String key) {
@@ -61,17 +59,17 @@ public class StructuredDataService {
if (columns.isEmpty()) {
throw new AppException("정형 데이터 테이블의 컬럼 정보를 찾을 수 없습니다.");
}
List<String> previewColumns = table.previewColumns().isEmpty() ? columns : table.previewColumns();
List<String> previewColumns =
table.previewColumns().isEmpty() ? columns : table.previewColumns();
if (!columns.containsAll(previewColumns)) {
throw new AppException("정형 데이터 JSON의 미리보기 컬럼이 실제 테이블과 일치하지 않습니다.");
throw new AppException("환경 카탈로그의 미리보기 컬럼이 실제 객체와 일치하지 않습니다.");
}
List<Map<String, Object>> rows = jdbcTemplate.queryForList(
previewSql(table, previewColumns), catalog.rowLimit());
return new StructuredDataPreview(table, previewColumns, rows, catalog.rowLimit());
previewSql(table, previewColumns), ROW_LIMIT);
return new StructuredDataPreview(table, previewColumns, rows, ROW_LIMIT);
} catch (DataAccessException exception) {
throw new AppException("정형 데이터를 조회할 수 없습니다. " + catalog.sourceName() + ""
+ catalog.owner() + " 조회 권한과 대상 테이블 상태를 확인하세요.");
throw new AppException("정형 데이터를 조회할 수 없습니다. " + catalog.owner()
+ " 조회 권한과 대상 객체 상태를 확인하세요.");
}
}
@@ -83,15 +81,20 @@ public class StructuredDataService {
return previewSql(table, table.previewColumns());
}
private String previewSql(StructuredDataTable table, List<String> previewColumns) {
private String previewSql(
StructuredDataTable table,
List<String> previewColumns
) {
StructuredDataTable approved = requireTable(table.key());
if (!approved.tableName().equals(table.tableName())) {
throw new AppException("선택할 수 없는 정형 데이터 테이블입니다.");
throw new AppException("선택할 수 없는 카탈로그 객체입니다.");
}
String projection = previewColumns == null || previewColumns.isEmpty()
? "*"
: previewColumns.stream().map(column -> "\"" + column + "\"")
.reduce((left, right) -> left + ", " + right).orElseThrow();
: previewColumns.stream()
.map(column -> "\"" + column + "\"")
.reduce((left, right) -> left + ", " + right)
.orElseThrow();
return "SELECT " + projection + " FROM \"" + catalog.owner() + "\".\"" + approved.tableName()
+ "\" WHERE ROWNUM <= ?";
}