feat(gen): GenModifier расширение (DeleteStrategy/Idempotency/DeleteParams) + LoadSpecs + ValidateSpec

This commit is contained in:
Repinoid
2026-09-22 21:07:15 +03:00
parent 6e297cc649
commit 8e24f8107e
2 changed files with 91 additions and 5 deletions
@@ -22,6 +22,7 @@ import (
"resource-generator/internal/params"
"resource-generator/internal/types"
"tf-tools/lib"
)
// LoadSpecs загружает все YAML-спеки из директории и строит модели всех ресурсов.
@@ -63,12 +64,15 @@ func LoadSpecs(dir string) ([]types.GenResource, []types.GenSubresource, []types
name = strings.TrimSpace(op.Action)
}
modifier := types.GenModifier{
ServiceName: spec.Name,
ServiceID: spec.ServiceID,
ModifierName: name,
OperationName: op.Action,
Params: ConvertParams(op.Params),
ServiceName: spec.Name,
ServiceID: spec.ServiceID,
ModifierName: name,
OperationName: op.Action,
Params: ConvertParams(op.Params),
DeleteStrategy: normalizeDeleteStrategy(op.DeleteStrategy),
Idempotency: normalizeIdempotency(op.Idempotency),
}
modifier.DeleteParams = convertDeleteParams(op.DeleteParams)
modifier.SchemaParams = modifier.Params
for idx := range modifier.SchemaParams {
if modifier.SchemaParams[idx].HasSubParams {
@@ -375,6 +379,76 @@ func ValidateSpec(path string, spec *types.ServiceSpec) error {
if op.Kind == "modifier" && strings.TrimSpace(op.Action) == "" {
return fmt.Errorf("operation[%d] %q (kind=modifier): missing required field: action", i, op.Name)
}
if op.Kind == "modifier" {
if err := validateModifierOperation(i, &op); err != nil {
return err
}
}
}
return nil
}
// normalizeDeleteStrategy возвращает каноническое значение delete_strategy.
// Пусто → noop_warn; неизвестное — как есть (валидация в ValidateSpec отклонит раньше).
func normalizeDeleteStrategy(raw string) string {
v := strings.ToLower(strings.TrimSpace(raw))
if v == "" {
return "noop_warn"
}
return v
}
// normalizeIdempotency возвращает каноническое значение idempotency.
// Пусто → none.
func normalizeIdempotency(raw string) string {
v := strings.ToLower(strings.TrimSpace(raw))
if v == "" {
return "none"
}
return v
}
// convertDeleteParams переносит lib.DeleteParam → types.DeleteParam (wire-строки).
func convertDeleteParams(raw []lib.DeleteParam) []types.DeleteParam {
if len(raw) == 0 {
return nil
}
out := make([]types.DeleteParam, 0, len(raw))
for _, p := range raw {
out = append(out, types.DeleteParam{Code: strings.TrimSpace(p.Code), Value: p.Value})
}
return out
}
// validateModifierOperation — fail-fast для полей modifier-операции.
func validateModifierOperation(i int, op *lib.OperationSpec) error {
ds := normalizeDeleteStrategy(op.DeleteStrategy)
switch ds {
case "noop_warn", "inverse", "error":
default:
return fmt.Errorf("operation[%d] %q (kind=modifier): unknown delete_strategy %q (valid: noop_warn, inverse, error)", i, op.Name, op.DeleteStrategy)
}
idem := normalizeIdempotency(op.Idempotency)
switch idem {
case "none", "check_before_run":
default:
return fmt.Errorf("operation[%d] %q (kind=modifier): unknown idempotency %q (valid: none, check_before_run)", i, op.Name, op.Idempotency)
}
if ds == "inverse" && len(op.DeleteParams) == 0 {
return fmt.Errorf("operation[%d] %q (kind=modifier): delete_strategy=inverse требует delete_params", i, op.Name)
}
// каждый delete_params.code обязан существовать среди params (по lower-code)
paramCodes := make(map[string]struct{}, len(op.Params))
for _, p := range op.Params {
paramCodes[strings.ToLower(strings.TrimSpace(p.Code))] = struct{}{}
}
for _, dp := range op.DeleteParams {
if _, ok := paramCodes[strings.ToLower(strings.TrimSpace(dp.Code))]; !ok {
return fmt.Errorf("operation[%d] %q (kind=modifier): delete_params.code %q отсутствует в params", i, op.Name, dp.Code)
}
}
return nil
}
@@ -158,6 +158,12 @@ type GenModifier struct {
OperationName string
Params []Param
SchemaParams []Param
// DeleteStrategy — noop_warn | inverse | error (нормализовано из YAML, пусто → noop_warn).
DeleteStrategy string
// Idempotency — none | check_before_run (нормализовано из YAML, пусто → none).
Idempotency string
// DeleteParams — обратные значения (wire-строки), только при DeleteStrategy==inverse.
DeleteParams []DeleteParam
UsesBool bool
UsesInt64 bool
UsesString bool
@@ -167,3 +173,9 @@ type GenModifier struct {
NeedsStringDefault bool
NeedsJsonPlanMod bool
}
// DeleteParam — обратное значение параметра inverse-Delete (code → wire-строка).
type DeleteParam struct {
Code string
Value string
}