fix: resolve workspace path resolution and enhance GrowToolHint generation in ToolScaffolder
Some checks failed
Deploy to OCIWP / deploy (push) Failing after 1m8s

This commit is contained in:
jade
2026-08-19 13:50:49 +09:00
parent d5a07cd846
commit 870412f0c7
3 changed files with 192 additions and 51 deletions

View File

@@ -108,6 +108,13 @@ public class ScaffoldingController {
parseDelimited(req.get("tags")),
req.get("ownerOrg"));
String defaultWorkspace = "c:\\eGovFrameDev-4.3.1-64bit\\workspace-egov\\dap-was-dapmt";
String workspacePath = req.getOrDefault("workspacePath", defaultWorkspace);
if (workspacePath.trim().isEmpty()) {
workspacePath = defaultWorkspace;
}
System.setProperty("AXHUB_SOURCE_DIR", workspacePath);
return ToolScaffolder.scaffold(baseName, interfaceId, title, description, group, routingType,
moduleName, author, date, register, clientSystemCode, inputSchemaResource,
outputSchemaResource, inputFields, outputFields, httpApiName, definitionOptions);
@@ -129,6 +136,9 @@ public class ScaffoldingController {
? System.getProperty("user.name") : request.author().trim();
String date = request.date() == null || request.date().isBlank()
? LocalDate.now().format(DateTimeFormatter.ofPattern("yyyy.MM.dd")) : request.date().trim();
String workspacePath = request.workspacePath() != null && !request.workspacePath().isBlank()
? request.workspacePath().trim() : "c:\\eGovFrameDev-4.3.1-64bit\\workspace-egov\\dap-was-dapmt";
System.setProperty("AXHUB_SOURCE_DIR", workspacePath);
return ToolScaffolder.scaffoldUseCase(request.useCaseName().trim(), moduleName, author, date,
request.tools() == null ? List.of() : request.tools());
} catch (Exception e) {
@@ -137,14 +147,15 @@ public class ScaffoldingController {
}
@GetMapping("/usecases")
public List<String> listUseCases(@RequestParam String moduleName, @RequestParam String categoryKey) {
public List<String> listUseCases(@RequestParam String moduleName, @RequestParam String categoryKey, @RequestParam(required = false) String workspacePath) {
if (moduleName == null || !moduleName.matches("^dap-was-[a-z0-9-]+$")) {
throw new IllegalArgumentException("Invalid target module.");
}
if (categoryKey == null || !categoryKey.matches("^[a-z0-9]{3}$")) {
throw new IllegalArgumentException("Invalid domain category.");
}
String sourceDir = System.getenv("AXHUB_SOURCE_DIR");
String sourceDir = (workspacePath != null && !workspacePath.isBlank()) ? workspacePath : System.getProperty("AXHUB_SOURCE_DIR");
if (sourceDir == null || sourceDir.isBlank()) sourceDir = System.getenv("AXHUB_SOURCE_DIR");
if (sourceDir == null || sourceDir.isBlank()) sourceDir = System.getProperty("user.dir");
File useCaseDir = new File(sourceDir, moduleName + "/src/main/java/io/shinhanlife/dap/mcc/biz/"
+ categoryKey + "/usecase");
@@ -156,11 +167,12 @@ public class ScaffoldingController {
}
@GetMapping("/categories")
public List<String> listCategories(@RequestParam String moduleName) {
public List<String> listCategories(@RequestParam String moduleName, @RequestParam(required = false) String workspacePath) {
if (moduleName == null || !moduleName.matches("^dap-was-[a-z0-9-]+$")) {
throw new IllegalArgumentException("Invalid target module.");
}
String sourceDir = System.getenv("AXHUB_SOURCE_DIR");
String sourceDir = (workspacePath != null && !workspacePath.isBlank()) ? workspacePath : System.getProperty("AXHUB_SOURCE_DIR");
if (sourceDir == null || sourceDir.isBlank()) sourceDir = System.getenv("AXHUB_SOURCE_DIR");
if (sourceDir == null || sourceDir.isBlank()) sourceDir = System.getProperty("user.dir");
File bizDir = new File(sourceDir, moduleName + "/src/main/java/io/shinhanlife/dap/mcc/biz");
File[] dirs = bizDir.listFiles(File::isDirectory);
@@ -256,9 +268,10 @@ public class ScaffoldingController {
}
@GetMapping("/modules")
public List<String> listModules() {
public List<String> listModules(@RequestParam(required = false) String workspacePath) {
try {
String sourceDir = System.getenv("AXHUB_SOURCE_DIR");
String sourceDir = (workspacePath != null && !workspacePath.isBlank()) ? workspacePath : System.getProperty("AXHUB_SOURCE_DIR");
if (sourceDir == null) sourceDir = System.getenv("AXHUB_SOURCE_DIR");
if (sourceDir == null) sourceDir = System.getProperty("user.dir");
File dir = new File(sourceDir);
@@ -426,7 +439,7 @@ public class ScaffoldingController {
List<ToolScaffolder.FieldDefinition> outputFields) {
}
private record ToolGroupRequest(String useCaseName, String moduleName, String author, String date,
private record ToolGroupRequest(String useCaseName, String moduleName, String author, String date, String workspacePath,
List<ToolScaffolder.ToolMethodDefinition> tools) {
}
}

View File

@@ -825,6 +825,20 @@
<!-- Tool Creation Form -->
<div class="tab-pane fade" id="tool" role="tabpanel">
<form id="toolForm">
<div class="row mb-4">
<div class="col-md-12">
<label class="form-label">Workspace Path</label>
<div class="input-group">
<input type="text" class="form-control" name="workspacePath" id="workspacePathInput" placeholder="직접 폴더 경로를 입력(붙여넣기) 하거나 선택하세요" value="c:\eGovFrameDev-4.3.1-64bit\workspace-egov\dap-was-dapmt">
<button class="btn btn-outline-secondary dropdown-toggle" type="button" data-bs-toggle="dropdown" aria-expanded="false" style="border-color: #3f3f46; color: #a1a1aa;">폴더 선택</button>
<ul class="dropdown-menu dropdown-menu-end dropdown-menu-dark" style="background-color: #18181b; border-color: #3f3f46;">
<li><a class="dropdown-item" href="#" onclick="const input = document.getElementById('workspacePathInput'); input.value='c:\\eGovFrameDev-4.3.1-64bit\\workspace-egov\\dap-was-dapmt'; input.dispatchEvent(new Event('change')); return false;" style="color: #f4f4f5;">dap-was-dapmt (기본)</a></li>
<li><a class="dropdown-item" href="#" onclick="const input = document.getElementById('workspacePathInput'); input.value='c:\\eGovFrameDev-4.3.1-64bit\\workspace-egov\\axhub-backend-main'; input.dispatchEvent(new Event('change')); return false;" style="color: #f4f4f5;">axhub-backend-main</a></li>
</ul>
</div>
<div class="input-hint">직접 경로를 붙여넣거나 우측 <b>[폴더 선택]</b> 버튼을 눌러 선택하세요.</div>
</div>
</div>
<div class="row mb-4">
<div class="col-md-6">
<label class="form-label">Target Module</label>
@@ -1521,18 +1535,27 @@
}
document.addEventListener('DOMContentLoaded', function() {
fetch('/api/v1/scaffold/modules')
.then(res => res.json())
.then(modules => {
const select = document.getElementById('targetModuleSelect');
if (modules && modules.length > 0) {
select.innerHTML = modules.map(m => `<option value="${m}" ${m === 'dap-was-cus' ? 'selected' : ''}>${m}</option>`).join('');
}
if (typeof loadCategoriesForSelection === 'function') {
loadCategoriesForSelection();
}
})
.catch(err => console.error('Failed to load modules:', err));
const loadModules = () => {
const workspacePath = document.querySelector('input[name="workspacePath"]')?.value || '';
fetch(`/api/v1/scaffold/modules?workspacePath=${encodeURIComponent(workspacePath)}`)
.then(res => res.json())
.then(modules => {
const select = document.getElementById('targetModuleSelect');
if (modules && modules.length > 0) {
select.innerHTML = modules.map(m => `<option value="${m}" ${m === 'dap-was-cus' ? 'selected' : ''}>${m}</option>`).join('');
}
if (typeof loadCategoriesForSelection === 'function') {
loadCategoriesForSelection();
}
})
.catch(err => console.error('Failed to load modules:', err));
};
loadModules();
const wsInput = document.querySelector('input[name="workspacePath"]');
if (wsInput) {
wsInput.addEventListener('change', loadModules);
wsInput.addEventListener('blur', loadModules); // Typing custom path and clicking away
}
});
handleFormSubmit('podForm', '/api/v1/scaffold/pod');
@@ -1603,12 +1626,13 @@
async function loadUseCasesForSelection() {
const moduleName = document.getElementById('targetModuleSelect').value;
const workspacePath = document.querySelector('input[name="workspacePath"]')?.value || '';
const categoryKey = document.querySelector('#toolForm [name="categoryKey"]').value.trim();
const select = document.getElementById('toolGroupUseCaseSelect');
select.innerHTML = '<option value="">새 UseCase 생성</option>';
if (!/^[a-z0-9]{3}$/.test(categoryKey)) return;
try {
const response = await fetch(`/api/v1/scaffold/usecases?moduleName=${encodeURIComponent(moduleName)}&categoryKey=${encodeURIComponent(categoryKey)}`);
const response = await fetch(`/api/v1/scaffold/usecases?moduleName=${encodeURIComponent(moduleName)}&categoryKey=${encodeURIComponent(categoryKey)}&workspacePath=${encodeURIComponent(workspacePath)}`);
if (!response.ok) throw new Error('UseCase 목록 조회 실패');
const useCases = await response.json();
useCases.forEach(useCaseName => select.add(new Option(`기존 ${useCaseName}에 함수 추가`, useCaseName)));
@@ -1619,11 +1643,12 @@
async function loadCategoriesForSelection() {
const moduleName = document.getElementById('targetModuleSelect').value;
const workspacePath = document.querySelector('input[name="workspacePath"]')?.value || '';
const select = document.getElementById('domainCategorySelect');
const nameInput = document.getElementById('domainCategoryName');
select.innerHTML = '<option value="">새 도메인 생성</option>';
try {
const response = await fetch(`/api/v1/scaffold/categories?moduleName=${encodeURIComponent(moduleName)}`);
const response = await fetch(`/api/v1/scaffold/categories?moduleName=${encodeURIComponent(moduleName)}&workspacePath=${encodeURIComponent(workspacePath)}`);
if (!response.ok) throw new Error('Category 목록 조회 실패');
const categories = await response.json();
categories.forEach(cat => select.add(new Option(`기존 ${cat} 도메인`, cat)));
@@ -1687,7 +1712,7 @@
fetch('/api/v1/scaffold/tool-group', {
method: 'POST', headers: {'Content-Type': 'application/json'},
body: JSON.stringify({useCaseName, moduleName: data.moduleName, author: data.author, date: data.date, tools})
body: JSON.stringify({useCaseName, moduleName: data.moduleName, author: data.author, date: data.date, workspacePath: data.workspacePath, tools})
}).then(response => response.text()).then(result => {
const resultBox = document.getElementById('resultBox');
resultBox.style.display = 'block'; resultBox.className = result.startsWith('Error:') ? 'error' : 'success';