Skip to content

Commit 582c446

Browse files
committed
Implement proposed new for sets
1 parent b38f8b9 commit 582c446

File tree

3 files changed

+214
-155
lines changed

3 files changed

+214
-155
lines changed

internal/fwserver/schema_propose_new_plan.go

Lines changed: 104 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,16 @@ func SchemaProposeNewState(ctx context.Context, s fwschema.Schema, req ProposeNe
5050
func proposedNew(ctx context.Context, s fwschema.Schema, path *tftypes.AttributePath, prior, config tftypes.Value) tftypes.Value {
5151
// TODO: This is in core's logic, but I'm not sure what how this scenario would be triggered
5252
// Need to verify if it's relevant...
53-
if config.IsNull() || !config.IsKnown() {
53+
//if config.IsNull() || !config.IsKnown() {
54+
// return prior
55+
//}
56+
57+
// TODO: double check this logic
58+
if config.IsNull() {
59+
return config
60+
}
61+
62+
if !config.IsKnown() {
5463
return prior
5564
}
5665

@@ -81,25 +90,44 @@ func proposedNewAttributes(ctx context.Context, s fwschema.Schema, attrs map[str
8190
attrPath := path.WithAttributeName(name)
8291

8392
var priorVal tftypes.Value
84-
if priorObj.IsNull() {
93+
switch {
94+
case priorObj.IsNull():
8595
priorObjType := priorObj.Type().(tftypes.Object) //nolint
8696
// TODO: validate before doing this? To avoid panic
8797
priorVal = tftypes.NewValue(priorObjType.AttributeTypes[name], nil)
88-
} else {
98+
case !priorObj.IsKnown():
99+
priorObjType := priorObj.Type().(tftypes.Object) //nolint
100+
// TODO: validate before doing this? To avoid panic
101+
priorVal = tftypes.NewValue(priorObjType.AttributeTypes[name], tftypes.UnknownValue)
102+
default:
89103
// TODO: handle error
90104
attrVal, err := priorObj.ApplyTerraform5AttributePathStep(tftypes.AttributeName(name))
91105
if err != nil {
92106
panic(err)
93107
}
94108
priorVal = attrVal.(tftypes.Value) //nolint
109+
95110
}
96111

97-
// TODO: handle error
98-
configIface, err := configObj.ApplyTerraform5AttributePathStep(tftypes.AttributeName(name))
99-
if err != nil {
100-
panic(err)
112+
var configVal tftypes.Value
113+
switch {
114+
case configObj.IsNull():
115+
configObjType := configObj.Type().(tftypes.Object) //nolint
116+
// TODO: validate before doing this? To avoid panic
117+
configVal = tftypes.NewValue(configObjType.AttributeTypes[name], nil)
118+
case !configObj.IsKnown():
119+
configObjType := configObj.Type().(tftypes.Object) //nolint
120+
// TODO: validate before doing this? To avoid panic
121+
configVal = tftypes.NewValue(configObjType.AttributeTypes[name], tftypes.UnknownValue)
122+
default:
123+
// TODO: handle error
124+
configIface, err := configObj.ApplyTerraform5AttributePathStep(tftypes.AttributeName(name))
125+
if err != nil {
126+
panic(err)
127+
}
128+
configVal = configIface.(tftypes.Value) //nolint
129+
101130
}
102-
configVal := configIface.(tftypes.Value) //nolint
103131

104132
var newVal tftypes.Value
105133
if attr.IsComputed() && configVal.IsNull() {
@@ -196,9 +224,9 @@ func proposeNewNestedAttribute(ctx context.Context, s fwschema.Schema, attr fwsc
196224
case fwschema.NestingModeList:
197225
newVal = proposedNewListNested(ctx, s, attr, path, prior, config)
198226
case fwschema.NestingModeMap:
199-
// TODO: handle map
227+
200228
case fwschema.NestingModeSet:
201-
// TODO: handle set
229+
newVal = proposedNewSetNested(ctx, s, attr, path, prior, config)
202230
default:
203231
// TODO: Shouldn't happen, return diag
204232
panic(fmt.Sprintf("unsupported attribute nesting mode %d", attr.GetNestingMode()))
@@ -361,6 +389,72 @@ func proposedNewListNested(ctx context.Context, s fwschema.Schema, attr fwschema
361389
return newVal
362390
}
363391

392+
func proposedNewSetNested(ctx context.Context, s fwschema.Schema, attr fwschema.NestedAttribute, path *tftypes.AttributePath, prior, config tftypes.Value) tftypes.Value {
393+
newVal := config
394+
395+
configVals := make([]tftypes.Value, 0)
396+
priorVals := make([]tftypes.Value, 0)
397+
398+
configValLen := 0
399+
if !config.IsNull() {
400+
err := config.As(&configVals)
401+
// TODO: handle err
402+
if err != nil {
403+
panic(err)
404+
}
405+
configValLen = len(configVals)
406+
}
407+
408+
if !prior.IsNull() {
409+
err := prior.As(&priorVals)
410+
// TODO: handle err
411+
if err != nil {
412+
panic(err)
413+
}
414+
}
415+
416+
if configValLen > 0 {
417+
// track which prior elements have been used
418+
used := make([]bool, len(priorVals))
419+
newVals := make([]tftypes.Value, 0, configValLen)
420+
for _, configEV := range configVals {
421+
var priorEV tftypes.Value
422+
for i, priorCmp := range priorVals {
423+
if used[i] {
424+
continue
425+
}
426+
427+
// It is possible that multiple prior elements could be valid
428+
// matches for a configuration value, in which case we will end up
429+
// picking the first match encountered (but it will always be
430+
// consistent due to cty's iteration order). Because configured set
431+
// elements must also be entirely unique in order to be included in
432+
// the set, these matches either will not matter because they only
433+
// differ by computed values, or could not have come from a valid
434+
// config with all unique set elements.
435+
if validPriorFromConfig(ctx, s, path, priorCmp, configEV) {
436+
priorEV = priorCmp
437+
used[i] = true
438+
break
439+
}
440+
}
441+
442+
if priorEV.IsNull() {
443+
// TODO might have to come back to figure out how to get elem type
444+
priorEV = tftypes.NewValue(attr.GetNestedObject().Type().TerraformType(ctx), nil)
445+
}
446+
//block.GetNestedObject().GetAttributes()
447+
// TODO create proposed new nested block object
448+
newVals = append(newVals, proposedNewObjectAttributes(ctx, s, attr, path.WithElementKeyValue(priorEV), priorEV, configEV))
449+
}
450+
451+
// TODO: should work for tuples + lists
452+
newVal = tftypes.NewValue(config.Type(), newVals)
453+
}
454+
455+
return newVal
456+
}
457+
364458
func proposedNewObjectAttributes(ctx context.Context, s fwschema.Schema, attr fwschema.NestedAttribute, path *tftypes.AttributePath, prior, config tftypes.Value) tftypes.Value {
365459
if config.IsNull() {
366460
return config

internal/fwserver/schema_propose_new_plan_test.go

Lines changed: 98 additions & 97 deletions
Original file line numberDiff line numberDiff line change
@@ -917,103 +917,104 @@ func TestSchemaProposeNewState(t *testing.T) {
917917
),
918918
},
919919
},
920-
"prior nested map": {
921-
schema: schema.Schema{
922-
Attributes: map[string]schema.Attribute{
923-
"map_nested_attribute": schema.MapNestedAttribute{
924-
Optional: true,
925-
NestedObject: schema.NestedAttributeObject{
926-
Attributes: map[string]schema.Attribute{
927-
"required_nested_attribute": schema.StringAttribute{
928-
Required: true,
929-
},
930-
},
931-
},
932-
},
933-
},
934-
},
935-
priorVal: map[string]tftypes.Value{
936-
"map_nested_attribute": tftypes.NewValue(
937-
tftypes.Map{
938-
ElementType: tftypes.Object{
939-
AttributeTypes: map[string]tftypes.Type{
940-
"required_nested_attribute": tftypes.String,
941-
},
942-
},
943-
},
944-
map[string]tftypes.Value{
945-
"a": tftypes.NewValue(tftypes.Object{
946-
AttributeTypes: map[string]tftypes.Type{
947-
"required_nested_attribute": tftypes.String,
948-
},
949-
}, map[string]tftypes.Value{
950-
"required_nested_attribute": tftypes.NewValue(tftypes.String, "glub"),
951-
}),
952-
"b": tftypes.NewValue(tftypes.Object{
953-
AttributeTypes: map[string]tftypes.Type{
954-
"required_nested_attribute": tftypes.String,
955-
},
956-
}, map[string]tftypes.Value{
957-
"required_nested_attribute": tftypes.NewValue(tftypes.String, "blub"),
958-
}),
959-
},
960-
),
961-
},
962-
configVal: map[string]tftypes.Value{
963-
"map_nested_attribute": tftypes.NewValue(
964-
tftypes.Map{
965-
ElementType: tftypes.Object{
966-
AttributeTypes: map[string]tftypes.Type{
967-
"required_nested_attribute": tftypes.String,
968-
},
969-
},
970-
},
971-
map[string]tftypes.Value{
972-
"a": tftypes.NewValue(tftypes.Object{
973-
AttributeTypes: map[string]tftypes.Type{
974-
"required_nested_attribute": tftypes.String,
975-
},
976-
}, map[string]tftypes.Value{
977-
"required_nested_attribute": tftypes.NewValue(tftypes.String, "glub"),
978-
}),
979-
"c": tftypes.NewValue(tftypes.Object{
980-
AttributeTypes: map[string]tftypes.Type{
981-
"required_nested_attribute": tftypes.String,
982-
},
983-
}, map[string]tftypes.Value{
984-
"required_nested_attribute": tftypes.NewValue(tftypes.String, "blub"),
985-
}),
986-
},
987-
),
988-
},
989-
expectedVal: map[string]tftypes.Value{
990-
"map_nested_attribute": tftypes.NewValue(
991-
tftypes.Map{
992-
ElementType: tftypes.Object{
993-
AttributeTypes: map[string]tftypes.Type{
994-
"required_nested_attribute": tftypes.String,
995-
},
996-
},
997-
},
998-
map[string]tftypes.Value{
999-
"a": tftypes.NewValue(tftypes.Object{
1000-
AttributeTypes: map[string]tftypes.Type{
1001-
"required_nested_attribute": tftypes.String,
1002-
},
1003-
}, map[string]tftypes.Value{
1004-
"required_nested_attribute": tftypes.NewValue(tftypes.String, "glub"),
1005-
}),
1006-
"c": tftypes.NewValue(tftypes.Object{
1007-
AttributeTypes: map[string]tftypes.Type{
1008-
"required_nested_attribute": tftypes.String,
1009-
},
1010-
}, map[string]tftypes.Value{
1011-
"required_nested_attribute": tftypes.NewValue(tftypes.String, "blub"),
1012-
}),
1013-
},
1014-
),
1015-
},
1016-
},
920+
// TODO: uncomment after implementing map logic
921+
//"prior nested map": {
922+
// schema: schema.Schema{
923+
// Attributes: map[string]schema.Attribute{
924+
// "map_nested_attribute": schema.MapNestedAttribute{
925+
// Optional: true,
926+
// NestedObject: schema.NestedAttributeObject{
927+
// Attributes: map[string]schema.Attribute{
928+
// "required_nested_attribute": schema.StringAttribute{
929+
// Required: true,
930+
// },
931+
// },
932+
// },
933+
// },
934+
// },
935+
// },
936+
// priorVal: map[string]tftypes.Value{
937+
// "map_nested_attribute": tftypes.NewValue(
938+
// tftypes.Map{
939+
// ElementType: tftypes.Object{
940+
// AttributeTypes: map[string]tftypes.Type{
941+
// "required_nested_attribute": tftypes.String,
942+
// },
943+
// },
944+
// },
945+
// map[string]tftypes.Value{
946+
// "a": tftypes.NewValue(tftypes.Object{
947+
// AttributeTypes: map[string]tftypes.Type{
948+
// "required_nested_attribute": tftypes.String,
949+
// },
950+
// }, map[string]tftypes.Value{
951+
// "required_nested_attribute": tftypes.NewValue(tftypes.String, "glub"),
952+
// }),
953+
// "b": tftypes.NewValue(tftypes.Object{
954+
// AttributeTypes: map[string]tftypes.Type{
955+
// "required_nested_attribute": tftypes.String,
956+
// },
957+
// }, map[string]tftypes.Value{
958+
// "required_nested_attribute": tftypes.NewValue(tftypes.String, "blub"),
959+
// }),
960+
// },
961+
// ),
962+
// },
963+
// configVal: map[string]tftypes.Value{
964+
// "map_nested_attribute": tftypes.NewValue(
965+
// tftypes.Map{
966+
// ElementType: tftypes.Object{
967+
// AttributeTypes: map[string]tftypes.Type{
968+
// "required_nested_attribute": tftypes.String,
969+
// },
970+
// },
971+
// },
972+
// map[string]tftypes.Value{
973+
// "a": tftypes.NewValue(tftypes.Object{
974+
// AttributeTypes: map[string]tftypes.Type{
975+
// "required_nested_attribute": tftypes.String,
976+
// },
977+
// }, map[string]tftypes.Value{
978+
// "required_nested_attribute": tftypes.NewValue(tftypes.String, "glub"),
979+
// }),
980+
// "c": tftypes.NewValue(tftypes.Object{
981+
// AttributeTypes: map[string]tftypes.Type{
982+
// "required_nested_attribute": tftypes.String,
983+
// },
984+
// }, map[string]tftypes.Value{
985+
// "required_nested_attribute": tftypes.NewValue(tftypes.String, "blub"),
986+
// }),
987+
// },
988+
// ),
989+
// },
990+
// expectedVal: map[string]tftypes.Value{
991+
// "map_nested_attribute": tftypes.NewValue(
992+
// tftypes.Map{
993+
// ElementType: tftypes.Object{
994+
// AttributeTypes: map[string]tftypes.Type{
995+
// "required_nested_attribute": tftypes.String,
996+
// },
997+
// },
998+
// },
999+
// map[string]tftypes.Value{
1000+
// "a": tftypes.NewValue(tftypes.Object{
1001+
// AttributeTypes: map[string]tftypes.Type{
1002+
// "required_nested_attribute": tftypes.String,
1003+
// },
1004+
// }, map[string]tftypes.Value{
1005+
// "required_nested_attribute": tftypes.NewValue(tftypes.String, "glub"),
1006+
// }),
1007+
// "c": tftypes.NewValue(tftypes.Object{
1008+
// AttributeTypes: map[string]tftypes.Type{
1009+
// "required_nested_attribute": tftypes.String,
1010+
// },
1011+
// }, map[string]tftypes.Value{
1012+
// "required_nested_attribute": tftypes.NewValue(tftypes.String, "blub"),
1013+
// }),
1014+
// },
1015+
// ),
1016+
// },
1017+
//},
10171018
"prior optional computed nested map elem to null": {
10181019
schema: schema.Schema{
10191020
Attributes: map[string]schema.Attribute{

0 commit comments

Comments
 (0)