fix: nil-guard for nested map-fixed params in generated code
This commit is contained in:
@@ -2,7 +2,7 @@ terraform {
|
|||||||
required_providers {
|
required_providers {
|
||||||
nubes = {
|
nubes = {
|
||||||
source = "registry.kube5s.ru/nubes-test/nubes"
|
source = "registry.kube5s.ru/nubes-test/nubes"
|
||||||
version = "5.1.3"
|
version = "5.1.4"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -116,6 +116,24 @@ Each operation has a kind:
|
|||||||
- GET operations (GetInstanceState, GetInstanceStateRaw) retry 401 with 2s/4s/8s backoff.
|
- GET operations (GetInstanceState, GetInstanceStateRaw) retry 401 with 2s/4s/8s backoff.
|
||||||
- `doRequest` treats 401 as retryable for GET requests (alongside 429, 502, 503, 504).
|
- `doRequest` treats 401 as retryable for GET requests (alongside 429, 502, 503, 504).
|
||||||
|
|
||||||
|
### Generated Code Resilience
|
||||||
|
|
||||||
|
- **Zero-value fallback** (`normalizeUniversalValueV6`): если параметр отсутствует
|
||||||
|
в пользовательском `.tf`, подставлять zero-value по `dataType`:
|
||||||
|
- `integer` → `"0"`, `boolean` → `"false"`, `map-fixed` → `"{}"`, `array` → `"[]"`, `string` → `""`
|
||||||
|
- Это предотвращает NullPointerException на стороне API при добавлении новых полей.
|
||||||
|
- **modify всегда через WithDefaults** (`RunInstanceOperationUniversalWithDefaults`):
|
||||||
|
modify-операции запрашивают `cfsParams` у API и отправляют все параметры,
|
||||||
|
включая новые, с дефолтами из API.
|
||||||
|
- **Nil-guard для nested-параметров** (шаблон `instance.go`): `map-fixed`-параметры
|
||||||
|
(указатели на вложенные структуры) проверяются на nil перед доступом к sub-полям.
|
||||||
|
Если состояние создано до добавления нового `map-fixed`-параметра — он будет nil,
|
||||||
|
и код не должен падать с nil pointer dereference. Вместо этого параметр пропускается,
|
||||||
|
и zero-value fallback подставит `{}`.
|
||||||
|
- **Merge SubParams union** (`params.go`): при совпадении `Code` параметра в разных
|
||||||
|
операциях (create/modify) с разными наборами sub-params, `Merge()` объединяет их
|
||||||
|
union'ом, а не отбрасывает второй. Это гарантирует что структура содержит все поля.
|
||||||
|
|
||||||
### Subresource Resources
|
### Subresource Resources
|
||||||
|
|
||||||
Subresource operations are exposed as standard resources.
|
Subresource operations are exposed as standard resources.
|
||||||
|
|||||||
@@ -235,13 +235,18 @@ func (r *{{ToCamel .Name}}Resource) ModifyPlan(ctx context.Context, req resource
|
|||||||
{{- end }}
|
{{- end }}
|
||||||
params := map[int]string{
|
params := map[int]string{
|
||||||
{{- range .CreateParams }}
|
{{- range .CreateParams }}
|
||||||
{{- if (IsNested .) }}
|
{{- if not (IsNested .) }}
|
||||||
{{.ID}}: {{NestedJSONExpr . "config"}},
|
|
||||||
{{- else }}
|
|
||||||
{{.ID}}: {{ParamFormat . (printf "config.%s" (ToCamel .Code))}},
|
{{.ID}}: {{ParamFormat . (printf "config.%s" (ToCamel .Code))}},
|
||||||
{{- end }}
|
{{- end }}
|
||||||
{{- end }}
|
{{- end }}
|
||||||
}
|
}
|
||||||
|
{{- range .CreateParams }}
|
||||||
|
{{- if (IsNested .) }}
|
||||||
|
if config.{{ToCamel .Code}} != nil {
|
||||||
|
params[{{.ID}}] = {{NestedJSONExpr . "config"}}
|
||||||
|
}
|
||||||
|
{{- end }}
|
||||||
|
{{- end }}
|
||||||
desiredDomain := ""
|
desiredDomain := ""
|
||||||
{{- if .HasDomainParam }}
|
{{- if .HasDomainParam }}
|
||||||
if !config.Domain.IsNull() && !config.Domain.IsUnknown() {
|
if !config.Domain.IsNull() && !config.Domain.IsUnknown() {
|
||||||
@@ -325,13 +330,18 @@ func (r *{{ToCamel .Name}}Resource) Create(ctx context.Context, req resource.Cre
|
|||||||
|
|
||||||
params := map[int]string{
|
params := map[int]string{
|
||||||
{{- range .CreateParams }}
|
{{- range .CreateParams }}
|
||||||
{{- if (IsNested .) }}
|
{{- if not (IsNested .) }}
|
||||||
{{.ID}}: {{NestedJSONExpr . "data"}},
|
|
||||||
{{- else }}
|
|
||||||
{{.ID}}: {{ParamFormat . (printf "data.%s" (ToCamel .Code))}},
|
{{.ID}}: {{ParamFormat . (printf "data.%s" (ToCamel .Code))}},
|
||||||
{{- end }}
|
{{- end }}
|
||||||
{{- end }}
|
{{- end }}
|
||||||
}
|
}
|
||||||
|
{{- range .CreateParams }}
|
||||||
|
{{- if (IsNested .) }}
|
||||||
|
if data.{{ToCamel .Code}} != nil {
|
||||||
|
params[{{.ID}}] = {{NestedJSONExpr . "data"}}
|
||||||
|
}
|
||||||
|
{{- end }}
|
||||||
|
{{- end }}
|
||||||
|
|
||||||
operationTimeout := ""
|
operationTimeout := ""
|
||||||
if !data.OperationTimeout.IsNull() && !data.OperationTimeout.IsUnknown() {
|
if !data.OperationTimeout.IsNull() && !data.OperationTimeout.IsUnknown() {
|
||||||
@@ -547,13 +557,18 @@ func (r *{{ToCamel .Name}}Resource) Update(ctx context.Context, req resource.Upd
|
|||||||
{{- end }}
|
{{- end }}
|
||||||
params := map[int]string{
|
params := map[int]string{
|
||||||
{{- range .ModifyParams }}
|
{{- range .ModifyParams }}
|
||||||
{{- if (IsNested .) }}
|
{{- if not (IsNested .) }}
|
||||||
{{.ID}}: {{NestedJSONExpr . "plan"}},
|
|
||||||
{{- else }}
|
|
||||||
{{.ID}}: {{ParamFormat . (printf "plan.%s" (ToCamel .Code))}},
|
{{.ID}}: {{ParamFormat . (printf "plan.%s" (ToCamel .Code))}},
|
||||||
{{- end }}
|
{{- end }}
|
||||||
{{- end }}
|
{{- end }}
|
||||||
}
|
}
|
||||||
|
{{- range .ModifyParams }}
|
||||||
|
{{- if (IsNested .) }}
|
||||||
|
if plan.{{ToCamel .Code}} != nil {
|
||||||
|
params[{{.ID}}] = {{NestedJSONExpr . "plan"}}
|
||||||
|
}
|
||||||
|
{{- end }}
|
||||||
|
{{- end }}
|
||||||
if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil {
|
if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil {
|
||||||
resp.Diagnostics.AddError("Ошибка клиента", err.Error())
|
resp.Diagnostics.AddError("Ошибка клиента", err.Error())
|
||||||
return
|
return
|
||||||
|
|||||||
Reference in New Issue
Block a user