Skip to content

Commit 22ce478

Browse files
refactor: deduplicate ensureTemplateObjectKeysForV1 via stateutil.EnsureMapKeys (#3776)
* refactor: deduplicate ensureTemplateObjectKeysForV1 via stateutil.EnsureMapKeys Extracts a shared EnsureMapKeys helper into the stateutil package and replaces the near-identical private ensureTemplateObjectKeysForV1 functions in the template and componenttemplate packages with calls to the shared helper. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * ci: trigger checks --------- Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com> Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
1 parent a972848 commit 22ce478

4 files changed

Lines changed: 47 additions & 39 deletions

File tree

internal/elasticsearch/index/componenttemplate/state_upgrade.go

Lines changed: 1 addition & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ func migrateComponentTemplateStateV0ToV1(_ context.Context, req resource.Upgrade
4545
}
4646

4747
if tmpl, ok := stateMap[attrTemplate].(map[string]any); ok {
48-
ensureTemplateObjectKeysForV1(tmpl)
48+
stateutil.EnsureMapKeys(tmpl, attrAlias, attrMappings, attrSettings, attrDataStreamOptions)
4949
aliasutil.NormalizeTemplateAliasesInV1State(tmpl)
5050
}
5151

@@ -57,19 +57,3 @@ func migrateComponentTemplateStateV0ToV1(_ context.Context, req resource.Upgrade
5757

5858
stateutil.MarshalStateMap(stateMap, resp)
5959
}
60-
61-
func ensureTemplateObjectKeysForV1(tmpl map[string]any) {
62-
if _, ok := tmpl[attrAlias]; !ok {
63-
// Empty nested sets are null in Terraform JSON state, not [].
64-
tmpl[attrAlias] = nil
65-
}
66-
if _, ok := tmpl[attrMappings]; !ok {
67-
tmpl[attrMappings] = nil
68-
}
69-
if _, ok := tmpl[attrSettings]; !ok {
70-
tmpl[attrSettings] = nil
71-
}
72-
if _, ok := tmpl[attrDataStreamOptions]; !ok {
73-
tmpl[attrDataStreamOptions] = nil
74-
}
75-
}

internal/elasticsearch/index/template/state_upgrade.go

Lines changed: 1 addition & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ func migrateIndexTemplateStateV0ToV1(_ context.Context, req resource.UpgradeStat
7575
}
7676

7777
if tmpl, ok := stateMap[attrTemplate].(map[string]any); ok {
78-
ensureTemplateObjectKeysForV1(tmpl)
78+
stateutil.EnsureMapKeys(tmpl, attrAlias, attrMappings, attrSettings, attrLifecycle, attrDataStreamOptions)
7979
aliasutil.NormalizeTemplateAliasesInV1State(tmpl)
8080
}
8181

@@ -87,24 +87,3 @@ func migrateIndexTemplateStateV0ToV1(_ context.Context, req resource.UpgradeStat
8787

8888
stateutil.MarshalStateMap(stateMap, resp)
8989
}
90-
91-
// ensureTemplateObjectKeysForV1 fills keys the Plugin Framework v1 schema expects on the template
92-
// object so RawState JSON decodes after upgrade. Plugin SDK state may omit optional empty blocks.
93-
func ensureTemplateObjectKeysForV1(tmpl map[string]any) {
94-
if _, ok := tmpl[attrAlias]; !ok {
95-
// Empty nested sets are null in Terraform JSON state, not [].
96-
tmpl[attrAlias] = nil
97-
}
98-
if _, ok := tmpl[attrMappings]; !ok {
99-
tmpl[attrMappings] = nil
100-
}
101-
if _, ok := tmpl[attrSettings]; !ok {
102-
tmpl[attrSettings] = nil
103-
}
104-
if _, ok := tmpl[attrLifecycle]; !ok {
105-
tmpl[attrLifecycle] = nil
106-
}
107-
if _, ok := tmpl[attrDataStreamOptions]; !ok {
108-
tmpl[attrDataStreamOptions] = nil
109-
}
110-
}

internal/stateutil/state_upgrade.go

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,17 @@ func UnmarshalStateMap(req resource.UpgradeStateRequest, resp *resource.UpgradeS
3939
return m
4040
}
4141

42+
// EnsureMapKeys sets each key in keys to nil in m if the key is absent. Used
43+
// during state upgrades to ensure the Plugin Framework can decode the JSON
44+
// state even when optional blocks were omitted in the prior SDK state.
45+
func EnsureMapKeys(m map[string]any, keys ...string) {
46+
for _, k := range keys {
47+
if _, ok := m[k]; !ok {
48+
m[k] = nil
49+
}
50+
}
51+
}
52+
4253
// MarshalStateMap encodes m as JSON and assigns it to resp.DynamicValue.
4354
func MarshalStateMap(m map[string]any, resp *resource.UpgradeStateResponse) {
4455
stateJSON, err := json.Marshal(m)

internal/stateutil/state_upgrade_test.go

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,3 +91,37 @@ func TestMarshalStateMap_error(t *testing.T) {
9191
require.Contains(t, resp.Diagnostics[0].Summary(), "State upgrade error")
9292
require.Nil(t, resp.DynamicValue)
9393
}
94+
95+
func TestEnsureMapKeys_sets_missing_keys_to_nil(t *testing.T) {
96+
t.Parallel()
97+
m := map[string]any{"a": "existing"}
98+
stateutil.EnsureMapKeys(m, "a", "b", "c")
99+
require.Equal(t, "existing", m["a"])
100+
require.Nil(t, m["b"])
101+
require.Nil(t, m["c"])
102+
}
103+
104+
func TestEnsureMapKeys_does_not_overwrite_existing(t *testing.T) {
105+
t.Parallel()
106+
m := map[string]any{"x": "value", "y": 42}
107+
stateutil.EnsureMapKeys(m, "x", "y", "z")
108+
require.Equal(t, "value", m["x"])
109+
require.Equal(t, 42, m["y"])
110+
require.Nil(t, m["z"])
111+
}
112+
113+
func TestEnsureMapKeys_empty_map(t *testing.T) {
114+
t.Parallel()
115+
m := map[string]any{}
116+
stateutil.EnsureMapKeys(m, "a", "b")
117+
require.Nil(t, m["a"])
118+
require.Nil(t, m["b"])
119+
}
120+
121+
func TestEnsureMapKeys_no_keys(t *testing.T) {
122+
t.Parallel()
123+
m := map[string]any{"a": "keep"}
124+
stateutil.EnsureMapKeys(m)
125+
require.Equal(t, "keep", m["a"])
126+
require.Len(t, m, 1)
127+
}

0 commit comments

Comments
 (0)