Files
tf_provider/provider/internal/core/refsvc.go
T

122 lines
4.3 KiB
Go

package core
import (
"context"
"encoding/json"
"fmt"
"strings"
)
// ===== Резолв refSvc-параметров (по ID) =====
//
// NOTE(resourceRealm): доступные resourceRealm для сервиса получают через
// GET /resourceRealms/available?svcId=<serviceId> (через index.cfm proxy).
//
// NOTE(refSvcId lookup): если параметр операции create ссылается на другой сервис,
// то в cfsParams будет проставлен refSvcId — значение должно быть UUID инстанса
// указанного сервиса.
//
// ВАЖНО (uuid-case, 2026-03): API облака возвращает UUID всегда в нижнем регистре.
// Для отправки в API нормализуем к lowercase, но в state сохраняем регистр
// пользователя (см. шаблон instance.go, блоки "Restore user-provided casing").
func (c *UniversalClient) resolveRefSvcParamValues(ctx context.Context, opParams []universalCfsParam, params map[int]string) (map[int]string, error) {
if len(params) == 0 || len(opParams) == 0 {
return params, nil
}
refById := make(map[int]int)
for _, p := range opParams {
if p.RefSvcId != nil && *p.RefSvcId > 0 {
refById[p.SvcOperationCfsParamId] = *p.RefSvcId
}
}
if len(refById) == 0 {
// Даже без refSvcId — резолвим s3Uid внутри map-fixed (соглашение об именах).
return c.ResolveS3UidInAllMapFixed(params, opParams), nil
}
resolved := make(map[int]string, len(params))
for paramId, value := range params {
newValue := value
if refSvcId, ok := refById[paramId]; ok {
if strings.TrimSpace(value) == "" {
newValue = ""
} else if !isUUIDLike(value) {
uid, err := c.findInstanceUidByDisplayNameRefSvc(ctx, refSvcId, value)
if err != nil {
return nil, err
}
if uid == "" {
return nil, fmt.Errorf("не удалось разрешить параметр %d: не найден экземпляр для serviceId=%d displayName=%s", paramId, refSvcId, value)
}
newValue = uid
} else {
newValue = strings.ToLower(value)
}
} else if strings.TrimSpace(value) != "" {
// map-fixed: резолвим s3Uid-подобные uuid sub-params внутри JSON.
newValue = c.resolveS3UidInMapFixed(paramId, value, opParams)
}
resolved[paramId] = newValue
}
return resolved, nil
}
// ResolveS3UidInAllMapFixed проходит по всем параметрам и для map-fixed
// резолвит s3Uid-подобные значения в UUID. Используется когда нет ни одного
// параметра с refSvcId (ранний выход resolveRefSvcParamValues).
func (c *UniversalClient) ResolveS3UidInAllMapFixed(params map[int]string, opParams []universalCfsParam) map[int]string {
result := make(map[int]string, len(params))
for paramId, value := range params {
if strings.TrimSpace(value) != "" && value != "{}" && strings.HasPrefix(strings.TrimSpace(value), "{") {
result[paramId] = c.resolveS3UidInMapFixed(paramId, value, opParams)
} else {
result[paramId] = value
}
}
return result
}
// resolveS3UidInMapFixed парсит JSON map-fixed-параметра, находит ключи
// по паттерну s3.*uid (case-insensitive) и резолвит значения-не-UUID
// в UUID через S3 (сервис 12). Соглашение об именах: s3Uid, s3_uid, S3Uid → S3.
func (c *UniversalClient) resolveS3UidInMapFixed(paramId int, jsonValue string, opParams []universalCfsParam) string {
if jsonValue == "" || jsonValue == "{}" {
return jsonValue
}
var obj map[string]interface{}
if err := json.Unmarshal([]byte(jsonValue), &obj); err != nil {
return jsonValue
}
changed := false
for key, val := range obj {
lowKey := strings.ToLower(key)
if !strings.Contains(lowKey, "s3") || !strings.Contains(lowKey, "uid") {
continue
}
strVal, ok := val.(string)
if !ok || strVal == "" || isUUIDLike(strVal) {
continue
}
uid, err := c.findInstanceUidByDisplayNameRefSvc(context.Background(), 12, strVal)
if err != nil || uid == "" {
continue
}
obj[key] = uid
changed = true
}
if !changed {
return jsonValue
}
b, err := json.Marshal(obj)
if err != nil {
return jsonValue
}
return string(b)
}