update: apply recent changes from local workspace
All checks were successful
Deploy to OCIWP / deploy (push) Successful in 1m54s
All checks were successful
Deploy to OCIWP / deploy (push) Successful in 1m54s
This commit is contained in:
@@ -827,12 +827,13 @@
|
||||
<div class="mb-4 p-3 rounded" style="background:#18181b; border:1px solid #3f3f46;">
|
||||
<div class="d-flex justify-content-between align-items-center gap-3">
|
||||
<div>
|
||||
<label class="form-label mb-1">Multi Tool UseCase (MCI)</label>
|
||||
<label class="form-label mb-1">Multi Tool UseCase (HTTP / MCI)</label>
|
||||
<div class="input-hint mt-0">현재 Tool을 같은 UseCase에 추가하면, Tool별 Client를 호출하는 여러 MCP Tool 메서드가 생성됩니다.</div>
|
||||
</div>
|
||||
<button type="button" class="btn-secondary-action" onclick="addCurrentToolToGroup()">현재 Tool 묶음에 추가</button>
|
||||
</div>
|
||||
<div class="row g-2 mt-1">
|
||||
<div class="col-md-4"><select id="toolGroupUseCaseSelect" class="form-select"><option value="">새 UseCase 생성</option></select></div>
|
||||
<div class="col-md-5"><input id="toolGroupUseCaseName" type="text" class="form-control" placeholder="UseCase 이름 e.g. Customer"></div>
|
||||
<div class="col-md-7"><div id="toolGroupSummary" class="input-hint pt-2">묶음에 추가된 Tool 없음 — 일반 Generate Tool은 기존 단일 Tool 생성입니다.</div></div>
|
||||
</div>
|
||||
@@ -1172,6 +1173,7 @@
|
||||
<th style="min-width: 150px;">Name</th>
|
||||
<th style="min-width: 125px;">Type</th>
|
||||
<th style="min-width: 170px;">Enum values / List item type</th>
|
||||
<th style="min-width: 260px;">Object list item fields (JSON)</th>
|
||||
<th style="min-width: 210px;">Description</th>
|
||||
<th style="min-width: 180px;">Example</th>
|
||||
<th style="min-width: 125px;">Required</th>
|
||||
@@ -1501,11 +1503,17 @@
|
||||
function currentToolDefinition() {
|
||||
const form = document.getElementById('toolForm');
|
||||
const data = Object.fromEntries(new FormData(form).entries());
|
||||
if (!data.baseName || !data.interfaceId || !data.clientSystemCode) {
|
||||
throw new Error('Base Name, Legacy Interface ID, Client System Code를 입력해주세요.');
|
||||
if (!data.baseName) {
|
||||
throw new Error('Base Name을 입력해주세요.');
|
||||
}
|
||||
if (data.routingType !== 'MCI') {
|
||||
throw new Error('여러 Tool을 하나의 UseCase로 생성하는 기능은 현재 MCI 전용입니다.');
|
||||
if (data.routingType === 'MCI' && (!data.interfaceId || !data.clientSystemCode)) {
|
||||
throw new Error('MCI Tool은 Legacy Interface ID와 Target System Code를 입력해야 합니다.');
|
||||
}
|
||||
if (data.routingType === 'HTTP' && !data.httpApiName) {
|
||||
throw new Error('HTTP Tool은 HTTP API Name을 입력해야 합니다.');
|
||||
}
|
||||
if (!['MCI', 'HTTP'].includes(data.routingType)) {
|
||||
throw new Error('여러 Tool UseCase는 MCI 또는 HTTP 프로토콜만 지원합니다.');
|
||||
}
|
||||
return {
|
||||
baseName: data.baseName,
|
||||
@@ -1542,6 +1550,35 @@
|
||||
}
|
||||
}
|
||||
|
||||
async function loadUseCasesForSelection() {
|
||||
const moduleName = document.getElementById('targetModuleSelect').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)}`);
|
||||
if (!response.ok) throw new Error('UseCase 목록 조회 실패');
|
||||
const useCases = await response.json();
|
||||
useCases.forEach(useCaseName => select.add(new Option(`기존 ${useCaseName}에 함수 추가`, useCaseName)));
|
||||
} catch (error) {
|
||||
console.warn(error);
|
||||
}
|
||||
}
|
||||
|
||||
document.querySelector('#toolForm [name="categoryKey"]').addEventListener('change', loadUseCasesForSelection);
|
||||
document.getElementById('targetModuleSelect').addEventListener('change', loadUseCasesForSelection);
|
||||
document.getElementById('toolGroupUseCaseSelect').addEventListener('change', function() {
|
||||
const nameInput = document.getElementById('toolGroupUseCaseName');
|
||||
if (this.value) {
|
||||
nameInput.value = this.value.replace(/UseCase$/, '');
|
||||
nameInput.readOnly = true;
|
||||
} else {
|
||||
nameInput.value = '';
|
||||
nameInput.readOnly = false;
|
||||
}
|
||||
});
|
||||
|
||||
function renderToolGroupSummary() {
|
||||
const summary = document.getElementById('toolGroupSummary');
|
||||
summary.textContent = groupedTools.length
|
||||
@@ -1706,17 +1743,22 @@
|
||||
const option = new Option(type, type, false, (field.type || 'String') === type);
|
||||
typeSelect.add(option);
|
||||
});
|
||||
typeSelect.addEventListener('change', () => {
|
||||
if (!exampleInput.value.trim()) {
|
||||
exampleInput.value = typeExamples[typeSelect.value];
|
||||
const createDetailsInput = (type, value) => {
|
||||
let input;
|
||||
if (type === 'List') {
|
||||
input = document.createElement('select');
|
||||
input.className = 'form-select form-select-sm';
|
||||
['String', 'Integer', 'Long', 'Double', 'Boolean', 'BigDecimal', 'Object'].forEach(itemType => {
|
||||
input.add(new Option(itemType, itemType, false, (value || 'String') === itemType));
|
||||
});
|
||||
} else {
|
||||
input = makeInput(type === 'Enum' ? value : '', 'Enum: OPEN, CLOSED');
|
||||
}
|
||||
updateFieldEditorPreview();
|
||||
});
|
||||
|
||||
const detailsInput = makeInput(
|
||||
field.type === 'Enum' ? (field.enumValues || []).join(', ') : (field.type === 'List' ? (field.itemType || 'String') : ''),
|
||||
'Enum: OPEN, CLOSED / List: String');
|
||||
detailsInput.dataset.field = 'details';
|
||||
input.dataset.field = 'details';
|
||||
return input;
|
||||
};
|
||||
let detailsInput = createDetailsInput(field.type || 'String',
|
||||
field.type === 'Enum' ? (field.enumValues || []).join(', ') : field.itemType);
|
||||
|
||||
const requiredSelect = document.createElement('select');
|
||||
requiredSelect.className = 'form-select form-select-sm';
|
||||
@@ -1724,12 +1766,41 @@
|
||||
requiredSelect.add(new Option('Required', 'true', false, field.required === true || field.required === 'true'));
|
||||
requiredSelect.add(new Option('Optional', 'false', false, !(field.required === true || field.required === 'true')));
|
||||
|
||||
[nameInput, descriptionInput, exampleInput, requiredSelect, detailsInput].forEach(control => {
|
||||
const itemFieldsInput = makeInput(field.type === 'List' && field.itemType === 'Object'
|
||||
? JSON.stringify(field.itemFields || []) : '', 'Object fields JSON e.g. [{"name":"date","type":"String"}]');
|
||||
itemFieldsInput.dataset.field = 'itemFields';
|
||||
const updateObjectListFieldsState = () => {
|
||||
const objectList = typeSelect.value === 'List' && detailsInput.value === 'Object';
|
||||
itemFieldsInput.disabled = !objectList;
|
||||
itemFieldsInput.placeholder = objectList
|
||||
? 'Object fields JSON e.g. [{"name":"date","type":"String"}]'
|
||||
: 'Select List > Object to enter item fields';
|
||||
if (!objectList) itemFieldsInput.value = '';
|
||||
};
|
||||
const bindDetailsInput = () => {
|
||||
detailsInput.addEventListener('input', updateFieldEditorPreview);
|
||||
detailsInput.addEventListener('change', () => {
|
||||
updateObjectListFieldsState();
|
||||
updateFieldEditorPreview();
|
||||
});
|
||||
};
|
||||
typeSelect.addEventListener('change', () => {
|
||||
if (!exampleInput.value.trim()) exampleInput.value = typeExamples[typeSelect.value];
|
||||
const replacement = createDetailsInput(typeSelect.value, typeSelect.value === 'List' ? 'String' : '');
|
||||
detailsInput.replaceWith(replacement);
|
||||
detailsInput = replacement;
|
||||
bindDetailsInput();
|
||||
updateObjectListFieldsState();
|
||||
updateFieldEditorPreview();
|
||||
});
|
||||
bindDetailsInput();
|
||||
updateObjectListFieldsState();
|
||||
[nameInput, descriptionInput, exampleInput, requiredSelect, itemFieldsInput].forEach(control => {
|
||||
control.addEventListener('input', updateFieldEditorPreview);
|
||||
control.addEventListener('change', updateFieldEditorPreview);
|
||||
});
|
||||
|
||||
[nameInput, typeSelect, detailsInput, descriptionInput, exampleInput, requiredSelect].forEach(control => {
|
||||
[nameInput, typeSelect, detailsInput, itemFieldsInput, descriptionInput, exampleInput, requiredSelect].forEach(control => {
|
||||
const cell = document.createElement('td');
|
||||
cell.appendChild(control);
|
||||
row.appendChild(cell);
|
||||
@@ -1754,6 +1825,13 @@
|
||||
.map(row => {
|
||||
const type = row.querySelector('[data-field="type"]').value;
|
||||
const details = row.querySelector('[data-field="details"]').value.trim();
|
||||
let itemFields = [];
|
||||
if (type === 'List' && details === 'Object') {
|
||||
const itemFieldsText = row.querySelector('[data-field="itemFields"]').value.trim();
|
||||
if (!itemFieldsText) throw new Error('List Object는 항목 필드를 입력해야 합니다.');
|
||||
itemFields = JSON.parse(itemFieldsText);
|
||||
if (!Array.isArray(itemFields) || itemFields.length === 0) throw new Error('List Object 항목 필드는 JSON 배열이어야 합니다.');
|
||||
}
|
||||
return {
|
||||
name: row.querySelector('[data-field="name"]').value.trim(), type,
|
||||
description: row.querySelector('[data-field="description"]').value.trim(),
|
||||
@@ -1761,7 +1839,7 @@
|
||||
required: row.querySelector('[data-field="required"]').value === 'true',
|
||||
enumValues: type === 'Enum' ? details.split(',').map(value => value.trim()).filter(Boolean) : [],
|
||||
itemType: type === 'List' ? (details || 'String') : null,
|
||||
itemFields: []
|
||||
itemFields
|
||||
};
|
||||
})
|
||||
.filter(field => field.name);
|
||||
|
||||
Reference in New Issue
Block a user