feat(gen): GenModifier расширение (DeleteStrategy/Idempotency/DeleteParams) + LoadSpecs + ValidateSpec
This commit is contained in:
@@ -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
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user