Skip to content

Commit da33c25

Browse files
committed
support list and set of strings at root level
1 parent 39b9484 commit da33c25

File tree

2 files changed

+79
-3
lines changed

2 files changed

+79
-3
lines changed

internal/common/autogeneration/marshal.go

Lines changed: 57 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,8 @@ func Marshal(model any, isUpdate bool) ([]byte, error) {
3939
}
4040

4141
// Unmarshal gets a JSON (e.g. from an Atlas response) and unmarshals it into a Terraform model.
42-
// It supports the following Terraform model types: String, Bool, Int64, Float64.
42+
// It supports the following Terraform model types: String, Bool, Int64, Float64, Object, List, Set.
43+
// Map is not supported yet as it is not used in the Atlas API.
4344
// Attributes that are in JSON but not in the model are ignored, no error is returned.
4445
// Object attributes that are unknown are converted to null as all values must be known in the response state.
4546
func Unmarshal(raw []byte, model any) error {
@@ -192,6 +193,22 @@ func unmarshalAttr(attrNameJSON string, attrObjJSON any, valModel reflect.Value)
192193
return err
193194
}
194195
return setAttrTfModel(attrNameModel, fieldModel, objNew)
196+
case []any:
197+
switch collection := fieldModel.Interface().(type) {
198+
case types.List:
199+
list, err := setListAttrModel(collection, v)
200+
if err != nil {
201+
return err
202+
}
203+
return setAttrTfModel(attrNameModel, fieldModel, list)
204+
case types.Set:
205+
set, err := setSetAttrModel(collection, v)
206+
if err != nil {
207+
return err
208+
}
209+
return setAttrTfModel(attrNameModel, fieldModel, set)
210+
}
211+
return fmt.Errorf("unmarshal expects collection for field %s", attrNameJSON)
195212
default:
196213
return fmt.Errorf("unmarshal not supported yet for type %T for field %s", v, attrNameJSON)
197214
}
@@ -270,6 +287,45 @@ func setObjAttrModel(obj types.Object, objJSON map[string]any) (attr.Value, erro
270287
return objNew, nil
271288
}
272289

290+
func setListAttrModel(list types.List, arrayJSON []any) (attr.Value, error) {
291+
elmType := list.ElementType(context.Background())
292+
elms, err := getCollectionElements(arrayJSON, elmType)
293+
if err != nil {
294+
return nil, err
295+
}
296+
listNew, diags := types.ListValue(elmType, elms)
297+
if diags.HasError() {
298+
return nil, fmt.Errorf("unmarshal failed to convert list to object: %v", diags)
299+
}
300+
return listNew, nil
301+
}
302+
303+
func setSetAttrModel(set types.Set, arrayJSON []any) (attr.Value, error) {
304+
elmType := set.ElementType(context.Background())
305+
elms, err := getCollectionElements(arrayJSON, elmType)
306+
if err != nil {
307+
return nil, err
308+
}
309+
setNew, diags := types.SetValue(elmType, elms)
310+
if diags.HasError() {
311+
return nil, fmt.Errorf("unmarshal failed to convert set to object: %v", diags)
312+
}
313+
return setNew, nil
314+
}
315+
316+
func getCollectionElements(arrayJSON []any, _ attr.Type) ([]attr.Value, error) {
317+
elms := make([]attr.Value, len(arrayJSON))
318+
for i, item := range arrayJSON {
319+
switch v := item.(type) {
320+
case string:
321+
elms[i] = types.StringValue(v)
322+
default:
323+
return nil, fmt.Errorf("unmarshal not supported yet for type %T in list", v)
324+
}
325+
}
326+
return elms, nil
327+
}
328+
273329
func getObjAttrsAndTypes(obj types.Object) (mapAttrs map[string]attr.Value, mapTypes map[string]attr.Type, err error) {
274330
// mapTypes has all attributes, mapAttrs might not have them, e.g. in null or unknown objects
275331
mapAttrs = obj.Attributes()

internal/common/autogeneration/marshal_test.go

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -333,6 +333,8 @@ func TestUnmarshalNestedAllTypes(t *testing.T) {
333333
AttrObjUnknownNotSent types.Object `tfsdk:"attr_obj_unknown_not_sent"`
334334
AttrObjUnknownSent types.Object `tfsdk:"attr_obj_unknown_sent"`
335335
AttrObjParent types.Object `tfsdk:"attr_obj_parent"`
336+
AttrListString types.List `tfsdk:"attr_list_string"`
337+
AttrSetString types.Set `tfsdk:"attr_set_string"`
336338
}
337339
model := modelst{
338340
AttrObj: types.ObjectValueMust(objTypeTest.AttrTypes, map[string]attr.Value{
@@ -347,6 +349,8 @@ func TestUnmarshalNestedAllTypes(t *testing.T) {
347349
AttrObjUnknownNotSent: types.ObjectUnknown(objTypeTest.AttrTypes), // unknown values are changed to null
348350
AttrObjUnknownSent: types.ObjectUnknown(objTypeTest.AttrTypes),
349351
AttrObjParent: types.ObjectNull(objTypeParentTest.AttrTypes),
352+
AttrListString: types.ListUnknown(types.StringType),
353+
AttrSetString: types.SetUnknown(types.StringType),
350354
}
351355
// attrUnexisting is ignored because it is in JSON but not in the model, no error is returned
352356
const (
@@ -372,7 +376,15 @@ func TestUnmarshalNestedAllTypes(t *testing.T) {
372376
"attrParentObj": {
373377
"attrString": "inside parent string"
374378
}
375-
}
379+
},
380+
"attrListString": [
381+
"list1",
382+
"list2"
383+
],
384+
"attrSetString": [
385+
"set1",
386+
"set2"
387+
]
376388
}
377389
`
378390
)
@@ -407,6 +419,14 @@ func TestUnmarshalNestedAllTypes(t *testing.T) {
407419
"attr_bool": types.BoolNull(),
408420
}),
409421
}),
422+
AttrListString: types.ListValueMust(types.StringType, []attr.Value{
423+
types.StringValue("list1"),
424+
types.StringValue("list2"),
425+
}),
426+
AttrSetString: types.SetValueMust(types.StringType, []attr.Value{
427+
types.StringValue("set1"),
428+
types.StringValue("set2"),
429+
}),
410430
}
411431
require.NoError(t, autogeneration.Unmarshal([]byte(jsonResp), &model))
412432
assert.Equal(t, modelExpected, model)
@@ -537,7 +557,7 @@ func TestUnmarshalUnsupportedResponse(t *testing.T) {
537557
AttrList types.List `tfsdk:"attr_list"`
538558
}{},
539559
responseJSON: `{"attrList": [{"key": "value"}]}`,
540-
errorStr: "unmarshal not supported yet for type []interface {} for field attrList",
560+
errorStr: "unmarshal not supported yet for type map[string]interface {} in list",
541561
},
542562
"JSON maps not supported yet": {
543563
model: &struct {

0 commit comments

Comments
 (0)