// operations.js — запуск операций: create, modify, delete, poll, stages
let pollTimer=null;
function makeCreateDisplayName(){
const rand = (Math.random() * 46656 | 0).toString(36).padStart(3, '0');
return currentSvcShort ? currentSvcShort+'-'+rand : rand;
}
function findInstName(){
const i=svcInstances.find(x=>x.instanceUid===selectedInst);
return i?i.displayName:selectedInst;
}
function stopPoll(){
if(pollTimer){clearInterval(pollTimer);pollTimer=null;}
}
async function startCreate(){
if(busy) return;
selectedInst=null; stopPoll();
document.querySelectorAll('[data-iuid]').forEach(e=>e.classList.remove('active'));
document.querySelectorAll('.inst-ops').forEach(e=>e.classList.remove('open'));
try{
const r=await fetch('/api/operations/'+currentSvcId);
const d=await r.json();
const createOp=d.operations.find(o=>o.operation==='create');
const opId=createOp?createOp.svcOperationId:18;
showParams(opId,'create');
}catch(e){
showParams(18,'create');
}
}
function runOp(opName,opId){
if(busy) return;
stopPoll();
showParams(opId,opName);
}
function showParams(opId,opName){
selectedOp={opId,opName,svcId:currentSvcId};
document.getElementById('params-area').style.display='block';
document.querySelectorAll('.stages-box').forEach(e=>e.remove());
document.getElementById('test-status').textContent='';
const isCreate=opName==='create';
const qs=isCreate?'' : `?instanceUid=${selectedInst}`;
fetch('/api/params/'+opId+qs).then(r=>r.json()).then(async data=>{
if(data.error){document.getElementById('params-form').innerHTML=`Ошибка: ${_esc(data.error)}`;return;}
const params=data.params||data;
if(!Array.isArray(params)){document.getElementById('params-form').innerHTML=`Неверный формат параметров`;return;}
let allInst=[];
const needRefs=params.some(p=>p.refSvcId);
if(needRefs){
try{const r=await fetch('/api/instances/list');const list=await r.json();allInst=list||[];}catch(e){}
}
const form=document.getElementById('params-form');
const displayNameValue=isCreate?makeCreateDisplayName():'';
const createHeader=isCreate?`
Создание: ${_esc(currentSvcName)}
`:'';
const opHeader=!isCreate?`${_esc(opName)} → ${_esc(findInstName())}
`:'';
form.innerHTML=createHeader+opHeader+(opName==='create'?`${AUTOTEST_PREFIX}
`:'')
+ params.map(p=>renderParamRow(p,allInst)).join('');
const btn=document.getElementById('btn-test');
btn.style.display='inline-flex';
btn.textContent='Запустить';
btn.onclick=()=>{
let bad=[];
document.querySelectorAll('#params-form [data-type="map"]').forEach(el=>{if(!validateJson(el,true)) bad.push(el);});
if(bad.length>0){
document.getElementById('test-status').innerHTML=`Ошибка ${bad.length} полей с невалидным JSON — поправьте и нажмите Запустить снова`;
return;
}
const pp=collectParams();
executeOp(pp);
};
}).catch(e=>{
document.getElementById('params-form').innerHTML=`Ошибка загрузки: ${_esc(e.message)}`;
});
}
// renderParamRow, renderMapFixedRow, collectParams — теперь в params-render.js (общий модуль)
function setFinishedState(statusText, statusClass, statusError, statusDuration, instanceName){
busy=false;
document.querySelectorAll('.inst-ops.open').forEach(e=>e.classList.remove('open'));
const btn=document.getElementById('btn-test');
btn.disabled=false;
btn.textContent='Готово';
btn.onclick=null;
const opName=selectedOp?.opName||'операция';
const durationText=statusDuration!==undefined&&statusDuration!==null?`${statusDuration}s`:'0s';
const extra=_esc(statusError||'');
const instName=_esc(instanceName||findInstName()||selectedInst||'');
document.getElementById('test-status').innerHTML=` ${statusText} ${opName} ${durationText} ${instName} ${extra}`;
}
function setRunningState(opName, displayName){
const btn=document.getElementById('btn-test');
btn.textContent=opName+'...';
document.getElementById('test-status').textContent=' ⏳ '+ (displayName||opName) +' выполняется...';
}
async function executeOp(params){
busy=true;
stopPoll();
const isCreate=selectedOp?.opName==='create';
const displayNameInput=document.getElementById('param-displayname');
const displayNameSuffix=(displayNameInput?.value||'').trim();
const displayName=isCreate ? `${AUTOTEST_PREFIX}${displayNameSuffix || makeCreateDisplayName()}` : findInstName();
document.getElementById('params-area').style.display='block';
document.getElementById('params-form').innerHTML='';
const btn=document.getElementById('btn-test');
btn.style.display='inline-flex';
btn.textContent=selectedOp.opName+'...';
btn.disabled=true;
setRunningState(selectedOp.opName, displayName);
const instId=selectedInst||'';
const afterEl=instId?document.getElementById('ops-'+instId):null;
document.querySelectorAll('.stages-box').forEach(e=>e.remove());
const stagesBox=document.createElement('div');
stagesBox.className='stages-box';
stagesBox.style.cssText='max-height:200px;overflow-y:auto;padding:4px 8px;margin-left:16px;background:var(--brand-grey-light);border-radius:4px;';
if(afterEl){afterEl.after(stagesBox);}
else{document.getElementById('create-btn-area').after(stagesBox);}
try{
const r=await fetch('/api/test',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({
serviceId:currentSvcId, operation:selectedOp.opName, svcOperationId:selectedOp.opId,
params, instanceUid:selectedInst||'', displayName:isCreate?displayName:''
})});
const d=await r.json();
if(d.status==='FAIL'){busy=false;document.getElementById('test-status').innerHTML=` FAIL ${_esc(d.error||'')}`;btn.disabled=false;return;}
if(d.status==='OK'&&(d.opUid||'').startsWith('cmdb-')){
busy=false;
setFinishedState('OK','badge-success','',0,d.displayName);
try{await refreshInstances();}catch(e){}
if(historyOpen) try{await loadHistory();}catch(e){}
return;
}
const opUid=d.opUid;
const instanceName=d.displayName||displayName;
let _pollErrors=0;
pollTimer=setInterval(async()=>{
try{
const sr=await fetch('/api/test/status/'+opUid);
const sd=await sr.json();
_pollErrors=0;
showStages(sd.stages||[]);
if(sd.status!=='RUNNING'){
stopPoll();
setFinishedState(sd.status, sd.status==='OK'?'badge-success':'badge', sd.error||sd.errorLog, sd.duration, sd.displayName||instanceName);
await refreshInstances();
if(historyOpen) await loadHistory();
}
}catch(e){
_pollErrors++;
if(_pollErrors>=5){stopPoll();setFinishedState('TIMEOUT','','Связь потеряна',0,instanceName);}
}
},2000);
}catch(e){
busy=false;
document.getElementById('test-status').textContent=' Ошибка: '+e.message;
btn.disabled=false;
}
}
function showStages(stages){
if(!stages||!stages.length) return;
let html='Этапы
';
stages.forEach(s=>{
const done=!!s.dtFinish;
const icon=done?(s.isSuccessful?'✅':'❌'):'⏳';
html+=`${icon} ${_esc(s.stage)} — ${(s.duration||0).toFixed(1)}s
`;
});
const boxes=document.querySelectorAll('.stages-box');
if(boxes.length) boxes[boxes.length-1].innerHTML=html;
}