v1.1.3: refSvcId — select dropdown from API for cross-service params

This commit is contained in:
2026-07-28 11:42:58 +04:00
parent 393e433b4f
commit 9090a4a9c3
5 changed files with 27 additions and 7 deletions
+6
View File
@@ -1,5 +1,11 @@
# История разработки app-autotest
## v1.1.2 (28.07.2026) — refSvcId: выпадающие списки для cross-service параметров
- `get_params.py` + `api_test.py`: параметры включают `refSvcId` из API
- Фронт: `refSvcId``fetch('/api/instances/list')` → фильтр по `serviceId``<select>` с `displayName`
- Универсально: S3, K8s-кластер, любой сервис — data-driven
## Инфраструктура
- **Кластер**: `iot-naeel` (resourceRealm K8s)
- **URL**: `https://atest.pythonk8s.dev.nubes.ru`
+1 -1
View File
@@ -18,7 +18,7 @@ from routes.api import bp as api_bp
from routes.api_test import bp as api_test_bp
# Версия — меняется при КАЖДОМ изменении кода. Показывается в топбаре UI.
VERSION = "1.1.2"
VERSION = "1.1.3"
app = Flask(__name__, template_folder="templates", static_folder="static")
app.config["NUBES_API_ENDPOINT"] = os.getenv("NUBES_API_ENDPOINT", "https://lk-api-gateway-test.ngcloud.ru/api/v1/svc")
+2 -5
View File
@@ -64,12 +64,9 @@ def get_params_with_current_values(client, op_id, instance_uid):
"name": code,
"dataType": data_type,
"isRequired": required,
"defaultValue": merged_value, # ← фронт использует это для value="..." в инпутах
"defaultValue": merged_value,
"valueList": value_list,
# dataDescriptor — только для map/map-fixed типов.
# Содержит словарь {sub_code: {dataType, valueList, isRequired}} —
# это нужно фронту чтобы отрисовать вложенную таблицу подполей.
# Для не-map типов — None.
"refSvcId": p.get("refSvcId"), # если параметр ссылается на другой сервис
"dataDescriptor": (
{
k: {
+1
View File
@@ -176,6 +176,7 @@ def api_params(op_id):
"isRequired": p.get("isRequired", False),
"defaultValue": p.get("defaultValue"),
"valueList": p.get("valueList"),
"refSvcId": p.get("refSvcId"),
"dataDescriptor": {k: {"dataType": v.get("dataType",""), "valueList": v.get("valueList",""), "isRequired": v.get("isRequired", False)} for k, v in dd.items()} if isinstance(dd, dict) else None,
})
_log(f"[api_params] DEFAULT returning {len(result)} params")
+17 -1
View File
@@ -134,8 +134,18 @@ function showParams(opId,opName){
const isCreate=opName==='create';
const qs=isCreate?'' : `?instanceUid=${selectedInst}`;
fetch('/api/params/'+opId+qs).then(r=>r.json()).then(data=>{
fetch('/api/params/'+opId+qs).then(r=>r.json()).then(async data=>{
const params=data.params||data;
// Заранее загрузить все инстансы для refSvcId-параметров
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():'';
form.innerHTML=(opName==='create'?`<div class="param-row"><label class="req">displayName</label><span style="font-size:12px;color:var(--muted);white-space:nowrap;">${AUTOTEST_PREFIX}</span><input type="text" id="param-displayname" value="${_esc(displayNameValue)}" autocomplete="off"></div>`:'')
@@ -149,6 +159,12 @@ function showParams(opId,opName){
let input;
if(vl&&Array.isArray(vl)){
input=`<select name="p_${p.svcOperationCfsParamId}" data-type="${_esc(p.dataType)}">${vl.map(v=>`<option value="${_esc(v)}" ${v==dfl?'selected':''}>${_esc(v)}</option>`).join('')}</select>`;
}else if(p.refSvcId){
// Параметр ссылается на другой сервис — выпадающий список инстансов
const refInsts=allInst.filter(i=>i.serviceId===p.refSvcId&&i.explainedStatus!=='deleted');
let opts=refInsts.map(i=>`<option value="${_esc(i.instanceUid)}" ${i.instanceUid===dfl?'selected':''}>${_esc(i.displayName)}</option>`).join('');
if(!dfl) opts='<option value="">— выбрать —</option>'+opts;
input=`<select name="p_${p.svcOperationCfsParamId}" data-type="ref">${opts}</select>`;
}else if(p.dataType==='boolean'){
input=`<select name="p_${p.svcOperationCfsParamId}" data-type="boolean"><option value="true" ${dfl==='true'||dfl===true?'selected':''}>true</option><option value="false" ${dfl==='false'||dfl===false?'selected':''}>false</option></select>`;
}else{