Files
tf_provider/TOOLS/resource-generator/internal/params/params.go
T
“Naeel” 46e847a3c1 docs: add detailed package-level comments to all TOOLS/ and provider/
TOOLS:
- yaml-generator: types (API↔YAML), client (proxy/REST + retry)
- resource-generator: types (3 resource kinds), loader (pipeline),
  writers (generated files), params (merge/CreateOnly/ForceNew)
- docs-generator, ops-generator — already documented

provider:
- main.go: entrypoint + build instructions
- core: API client (GetInstance, RunOperation, GetInstanceState)
- provider: provider registration (New, Resources, Schema)
- resources_core: shared CRUD, state refresh, params, validation
2026-07-05 10:24:43 +04:00

271 lines
7.4 KiB
Go

// Package params — слияние, фильтрация, анализ параметров и ForceNew.
//
// Основные операции:
// - Merge — объединяет параметры из нескольких операций по Code
// - ComputeCreateOnly — поля, которые есть в create, но отсутствуют в modify
// - BuildSubresourceForceNewCodes — поля, требующие пересоздания subresource
// - Analyze — определяет используемые типы (bool/int64/string) для импортов
// - AnalyzePlanModifiers — ForceNew-поля, требующие planmodifier.*.RequiresReplace()
package params
import (
"sort"
"strings"
"resource-generator/internal/types"
)
// Merge объединяет несколько наборов параметров, убирая дубликаты по Code.
func Merge(sets ...[]types.Param) []types.Param {
seen := map[string]types.Param{}
for _, s := range sets {
for _, p := range s {
key := strings.ToLower(strings.TrimSpace(p.Code))
if key == "" {
continue
}
if _, ok := seen[key]; !ok {
seen[key] = p
}
}
}
out := make([]types.Param, 0, len(seen))
for _, p := range seen {
out = append(out, p)
}
sort.Slice(out, func(i, j int) bool { return out[i].Code < out[j].Code })
return out
}
// AlignParamTypes выравнивает типы параметров по схеме.
func AlignParamTypes(params []types.Param, schema []types.Param) []types.Param {
if len(params) == 0 || len(schema) == 0 {
return params
}
codeType := make(map[string]string, len(schema))
for _, p := range schema {
key := strings.ToLower(strings.TrimSpace(p.Code))
if key == "" {
continue
}
if strings.TrimSpace(p.Type) != "" {
codeType[key] = p.Type
}
}
if len(codeType) == 0 {
return params
}
for i, p := range params {
key := strings.ToLower(strings.TrimSpace(p.Code))
if key == "" {
continue
}
if t, ok := codeType[key]; ok && strings.TrimSpace(t) != "" {
params[i].Type = t
}
}
return params
}
// ComputeCreateOnly вычисляет набор полей, которые есть только в create (не в modify).
func ComputeCreateOnly(createParams []types.Param, modifyParams []types.Param) map[string]struct{} {
modifyCodes := make(map[string]struct{}, len(modifyParams))
for _, p := range modifyParams {
key := strings.ToLower(strings.TrimSpace(p.Code))
if key == "" {
continue
}
modifyCodes[key] = struct{}{}
}
createOnly := make(map[string]struct{})
for _, p := range createParams {
key := strings.ToLower(strings.TrimSpace(p.Code))
if key == "" {
continue
}
if _, ok := modifyCodes[key]; !ok {
createOnly[key] = struct{}{}
}
}
return createOnly
}
// MarkCreateOnly проставляет CreateOnly=true для полей, которых нет в modify.
func MarkCreateOnly(schema []types.Param, createOnly map[string]struct{}) []types.Param {
if len(schema) == 0 || len(createOnly) == 0 {
return schema
}
for i, p := range schema {
key := strings.ToLower(strings.TrimSpace(p.Code))
if key == "" {
continue
}
if _, ok := createOnly[key]; ok {
schema[i].CreateOnly = true
}
}
return schema
}
// FilterCreateOnly возвращает только CreateOnly-параметры.
func FilterCreateOnly(schema []types.Param) []types.Param {
if len(schema) == 0 {
return []types.Param{}
}
result := make([]types.Param, 0, len(schema))
for _, p := range schema {
if p.CreateOnly {
result = append(result, p)
}
}
return result
}
// FilterCreateOnlyRequired возвращает обязательные CreateOnly-параметры.
func FilterCreateOnlyRequired(schema []types.Param) []types.Param {
if len(schema) == 0 {
return []types.Param{}
}
result := make([]types.Param, 0, len(schema))
for _, p := range schema {
if p.CreateOnly && p.Required {
result = append(result, p)
}
}
return result
}
// Analyze анализирует типы параметров и проставляет флаги uses/needs.
func Analyze(usesBool *bool, usesInt64 *bool, usesString *bool, hasDefaults *bool, needsBoolDefault *bool, needsInt64Default *bool, needsStringDefault *bool, params []types.Param) {
for _, p := range params {
switch strings.ToLower(p.Type) {
case "bool":
*usesBool = true
if p.Default != "" {
*needsBoolDefault = true
*hasDefaults = true
}
case "int", "int64", "number":
*usesInt64 = true
if p.Default != "" {
*needsInt64Default = true
*hasDefaults = true
}
default:
*usesString = true
if p.Default != "" {
*needsStringDefault = true
*hasDefaults = true
}
}
}
}
// BuildSubresourceForceNewCodes строит карту параметров, требующих ForceNew.
func BuildSubresourceForceNewCodes(sr *types.GenSubresource, createOnly map[string]struct{}) map[string]struct{} {
forceNew := map[string]struct{}{}
if sr.ModifyOpName == "" {
for _, p := range sr.SchemaParams {
key := strings.ToLower(strings.TrimSpace(p.Code))
if key != "" {
forceNew[key] = struct{}{}
}
}
return forceNew
}
for key := range createOnly {
forceNew[key] = struct{}{}
}
for _, p := range sr.DeleteParams {
key := strings.ToLower(strings.TrimSpace(p.Code))
if key != "" {
forceNew[key] = struct{}{}
}
}
return forceNew
}
// MarkForceNew проставляет ForceNew=true для указанных параметров.
func MarkForceNew(params []types.Param, forceNew map[string]struct{}) []types.Param {
if len(params) == 0 || len(forceNew) == 0 {
return params
}
for i, p := range params {
key := strings.ToLower(strings.TrimSpace(p.Code))
if key == "" {
continue
}
if _, ok := forceNew[key]; ok {
params[i].ForceNew = true
}
}
return params
}
// FilterForceNew возвращает только ForceNew-параметры.
func FilterForceNew(params []types.Param) []types.Param {
result := []types.Param{}
for _, p := range params {
if p.ForceNew {
result = append(result, p)
}
}
return result
}
// AnalyzeJsonPlanMod возвращает true, если хотя бы один параметр — JSON.
func AnalyzeJsonPlanMod(params []types.Param) bool {
for _, p := range params {
if p.IsJson {
return true
}
}
return false
}
// HasRestoreCasingParams возвращает true, если есть ref_svc-параметры (кроме S3).
func HasRestoreCasingParams(params []types.Param) bool {
for _, p := range params {
if p.RefSvcId > 0 && p.RefSvcId != 12 && strings.EqualFold(strings.TrimSpace(p.Type), "string") {
return true
}
}
return false
}
// AnalyzeNeedsStrings возвращает true, если есть строковые параметры.
func AnalyzeNeedsStrings(params []types.Param) bool {
for _, p := range params {
t := strings.ToLower(strings.TrimSpace(p.Type))
if t != "bool" && t != "int" && t != "int64" && t != "number" {
return true
}
}
return false
}
// AnalyzePlanModifiers анализирует ForceNew-параметры для добавления planmodifier-импортов.
func AnalyzePlanModifiers(needsBool *bool, needsInt64 *bool, needsString *bool, params []types.Param) {
for _, p := range params {
if !p.ForceNew {
continue
}
switch strings.ToLower(strings.TrimSpace(p.Type)) {
case "bool":
*needsBool = true
case "int", "int64", "number":
*needsInt64 = true
default:
*needsString = true
}
}
}