Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 35 additions & 11 deletions internal/common/autogen/marshal_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,25 +56,49 @@ func TestMarshalBasic(t *testing.T) {

func TestMarshalDynamicJSONAttr(t *testing.T) {
model := struct {
AttrDynamicJSONObject jsontypes.Normalized `tfsdk:"attr_dynamic_json_object"`
AttrDynamicJSONBoolean jsontypes.Normalized `tfsdk:"attr_dynamic_json_boolean"`
AttrDynamicJSONString jsontypes.Normalized `tfsdk:"attr_dynamic_json_string"`
AttrDynamicJSONNumber jsontypes.Normalized `tfsdk:"attr_dynamic_json_number"`
AttrDynamicJSONArray jsontypes.Normalized `tfsdk:"attr_dynamic_json_array"`
AttrDynamicJSONObject jsontypes.Normalized `tfsdk:"attr_dynamic_json_object"`
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no changes to marshal/unmarshal logic needed, adding testing to ensure coverage

AttrDynamicJSONBoolean jsontypes.Normalized `tfsdk:"attr_dynamic_json_boolean"`
AttrDynamicJSONString jsontypes.Normalized `tfsdk:"attr_dynamic_json_string"`
AttrDynamicJSONNumber jsontypes.Normalized `tfsdk:"attr_dynamic_json_number"`
AttrDynamicJSONArray jsontypes.Normalized `tfsdk:"attr_dynamic_json_array"`
AttrListOfDynamicJSONObjects customtypes.ListValue[jsontypes.Normalized] `tfsdk:"attr_list_of_dynamic_json_objects"`
AttrSetOfDynamicJSONObjects customtypes.SetValue[jsontypes.Normalized] `tfsdk:"attr_set_of_dynamic_json_objects"`
AttrMapOfDynamicJSONObjects customtypes.MapValue[jsontypes.Normalized] `tfsdk:"attr_map_of_dynamic_json_objects"`
AttrListOfDynamicJSONBooleans customtypes.ListValue[jsontypes.Normalized] `tfsdk:"attr_list_of_dynamic_json_booleans"`
AttrSetOfDynamicJSONBooleans customtypes.SetValue[jsontypes.Normalized] `tfsdk:"attr_set_of_dynamic_json_booleans"`
AttrMapOfDynamicJSONBooleans customtypes.MapValue[jsontypes.Normalized] `tfsdk:"attr_map_of_dynamic_json_booleans"`
}{
AttrDynamicJSONObject: jsontypes.NewNormalizedValue("{\"hello\": \"there\"}"),
AttrDynamicJSONBoolean: jsontypes.NewNormalizedValue("true"),
AttrDynamicJSONString: jsontypes.NewNormalizedValue("\"hello\""),
AttrDynamicJSONNumber: jsontypes.NewNormalizedValue("1.234"),
AttrDynamicJSONArray: jsontypes.NewNormalizedValue("[1, 2, 3]"),
AttrDynamicJSONObject: jsontypes.NewNormalizedValue("{\"hello\": \"there\"}"),
AttrDynamicJSONBoolean: jsontypes.NewNormalizedValue("true"),
AttrDynamicJSONString: jsontypes.NewNormalizedValue("\"hello\""),
AttrDynamicJSONNumber: jsontypes.NewNormalizedValue("1.234"),
AttrDynamicJSONArray: jsontypes.NewNormalizedValue("[1, 2, 3]"),
AttrListOfDynamicJSONObjects: customtypes.NewListValue[jsontypes.Normalized](t.Context(), []attr.Value{jsontypes.NewNormalizedValue("{\"hello\": \"there\"}")}),
AttrSetOfDynamicJSONObjects: customtypes.NewSetValue[jsontypes.Normalized](t.Context(), []attr.Value{jsontypes.NewNormalizedValue("{\"hello\": \"there\"}")}),
AttrMapOfDynamicJSONObjects: customtypes.NewMapValue[jsontypes.Normalized](t.Context(), map[string]attr.Value{
"key1": jsontypes.NewNormalizedValue("{\"hello\": \"there\"}"),
"key2": jsontypes.NewNormalizedValue("{\"hello\": \"there\"}"),
}),
AttrListOfDynamicJSONBooleans: customtypes.NewListValue[jsontypes.Normalized](t.Context(), []attr.Value{jsontypes.NewNormalizedValue("true")}),
AttrSetOfDynamicJSONBooleans: customtypes.NewSetValue[jsontypes.Normalized](t.Context(), []attr.Value{jsontypes.NewNormalizedValue("true")}),
AttrMapOfDynamicJSONBooleans: customtypes.NewMapValue[jsontypes.Normalized](t.Context(), map[string]attr.Value{
"key1": jsontypes.NewNormalizedValue("true"),
"key2": jsontypes.NewNormalizedValue("false"),
}),
}
const expectedJSON = `
{
"attrDynamicJSONObject": {"hello": "there"},
"attrDynamicJSONBoolean": true,
"attrDynamicJSONString": "hello",
"attrDynamicJSONNumber": 1.234,
"attrDynamicJSONArray": [1, 2, 3]
"attrDynamicJSONArray": [1, 2, 3],
"attrListOfDynamicJSONObjects": [{"hello": "there"}],
"attrSetOfDynamicJSONObjects": [{"hello": "there"}],
"attrMapOfDynamicJSONObjects": {"key1": {"hello": "there"}, "key2": {"hello": "there"}},
"attrListOfDynamicJSONBooleans": [true],
"attrSetOfDynamicJSONBooleans": [true],
"attrMapOfDynamicJSONBooleans": {"key1": true, "key2": false}
}
`
raw, err := autogen.Marshal(&model, false)
Expand Down
64 changes: 52 additions & 12 deletions internal/common/autogen/unmarshal_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,28 +58,68 @@ func TestUnmarshalBasic(t *testing.T) {
}

func TestUnmarshalDynamicJSONAttr(t *testing.T) {
var model struct {
AttrDynamicJSONObject jsontypes.Normalized `tfsdk:"attr_dynamic_json_object"`
AttrDynamicJSONBoolean jsontypes.Normalized `tfsdk:"attr_dynamic_json_boolean"`
AttrDynamicJSONNumber jsontypes.Normalized `tfsdk:"attr_dynamic_json_number"`
AttrDynamicJSONString jsontypes.Normalized `tfsdk:"attr_dynamic_json_string"`
AttrDynamicJSONArray jsontypes.Normalized `tfsdk:"attr_dynamic_json_array"`
ctx := context.Background()

type modelst struct {
AttrDynamicJSONObject jsontypes.Normalized `tfsdk:"attr_dynamic_json_object"`
AttrDynamicJSONBoolean jsontypes.Normalized `tfsdk:"attr_dynamic_json_boolean"`
AttrDynamicJSONNumber jsontypes.Normalized `tfsdk:"attr_dynamic_json_number"`
AttrDynamicJSONString jsontypes.Normalized `tfsdk:"attr_dynamic_json_string"`
AttrDynamicJSONArray jsontypes.Normalized `tfsdk:"attr_dynamic_json_array"`
AttrListOfDynamicJSONObjects customtypes.ListValue[jsontypes.Normalized] `tfsdk:"attr_list_of_dynamic_json_objects"`
AttrSetOfDynamicJSONObjects customtypes.SetValue[jsontypes.Normalized] `tfsdk:"attr_set_of_dynamic_json_objects"`
AttrMapOfDynamicJSONObjects customtypes.MapValue[jsontypes.Normalized] `tfsdk:"attr_map_of_dynamic_json_objects"`
AttrListOfDynamicJSONBooleans customtypes.ListValue[jsontypes.Normalized] `tfsdk:"attr_list_of_dynamic_json_booleans"`
AttrSetOfDynamicJSONBooleans customtypes.SetValue[jsontypes.Normalized] `tfsdk:"attr_set_of_dynamic_json_booleans"`
AttrMapOfDynamicJSONBooleans customtypes.MapValue[jsontypes.Normalized] `tfsdk:"attr_map_of_dynamic_json_booleans"`
}

var model modelst
const jsonResp = `
{
"attrDynamicJSONObject": {"hello":"there"},
"attrDynamicJSONBoolean": true,
"attrDynamicJSONNumber": 1.234,
"attrDynamicJSONString": "hello",
"attrDynamicJSONArray": [1, 2, 3]
"attrDynamicJSONArray": [1, 2, 3],
"attrListOfDynamicJSONObjects": [{"hello": "there"}],
"attrSetOfDynamicJSONObjects": [{"hello": "there"}],
"attrMapOfDynamicJSONObjects": {"key1": {"hello": "there"}, "key2": {"hello": "there"}},
"attrListOfDynamicJSONBooleans": [true],
"attrSetOfDynamicJSONBooleans": [true],
"attrMapOfDynamicJSONBooleans": {"key1": true, "key2": false}
}
`
modelExpected := modelst{
AttrDynamicJSONObject: jsontypes.NewNormalizedValue("{\"hello\":\"there\"}"),
AttrDynamicJSONBoolean: jsontypes.NewNormalizedValue("true"),
AttrDynamicJSONNumber: jsontypes.NewNormalizedValue("1.234"),
AttrDynamicJSONString: jsontypes.NewNormalizedValue("\"hello\""),
AttrDynamicJSONArray: jsontypes.NewNormalizedValue("[1,2,3]"),
AttrListOfDynamicJSONObjects: customtypes.NewListValue[jsontypes.Normalized](ctx, []attr.Value{
jsontypes.NewNormalizedValue("{\"hello\":\"there\"}"),
}),
AttrSetOfDynamicJSONObjects: customtypes.NewSetValue[jsontypes.Normalized](ctx, []attr.Value{
jsontypes.NewNormalizedValue("{\"hello\":\"there\"}"),
}),
AttrMapOfDynamicJSONObjects: customtypes.NewMapValue[jsontypes.Normalized](ctx, map[string]attr.Value{
"key1": jsontypes.NewNormalizedValue("{\"hello\":\"there\"}"),
"key2": jsontypes.NewNormalizedValue("{\"hello\":\"there\"}"),
}),
AttrListOfDynamicJSONBooleans: customtypes.NewListValue[jsontypes.Normalized](ctx, []attr.Value{
jsontypes.NewNormalizedValue("true"),
}),
AttrSetOfDynamicJSONBooleans: customtypes.NewSetValue[jsontypes.Normalized](ctx, []attr.Value{
jsontypes.NewNormalizedValue("true"),
}),
AttrMapOfDynamicJSONBooleans: customtypes.NewMapValue[jsontypes.Normalized](ctx, map[string]attr.Value{
"key1": jsontypes.NewNormalizedValue("true"),
"key2": jsontypes.NewNormalizedValue("false"),
}),
}

require.NoError(t, autogen.Unmarshal([]byte(jsonResp), &model))
assert.JSONEq(t, "{\"hello\":\"there\"}", model.AttrDynamicJSONObject.ValueString())
assert.JSONEq(t, "true", model.AttrDynamicJSONBoolean.ValueString())
assert.JSONEq(t, "1.234", model.AttrDynamicJSONNumber.ValueString())
assert.JSONEq(t, "\"hello\"", model.AttrDynamicJSONString.ValueString())
assert.JSONEq(t, "[1, 2, 3]", model.AttrDynamicJSONArray.ValueString())
assert.Equal(t, modelExpected, model)
}

type unmarshalModelEmpty struct{}
Expand Down
18 changes: 16 additions & 2 deletions internal/serviceapi/searchindexapi/resource_schema.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

87 changes: 87 additions & 0 deletions internal/serviceapi/searchindexapi/resource_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,29 @@ func TestAccSearchIndexAPI_basic(t *testing.T) {
})
}

func TestAccSearchIndexAPI_withMappingsFields(t *testing.T) {
var (
projectID, clusterName = acc.ClusterNameExecution(t, true)
indexName = acc.RandomName()
)
resource.ParallelTest(t, resource.TestCase{
PreCheck: func() { acc.PreCheckBasic(t) },
ProtoV6ProviderFactories: acc.TestAccProviderV6Factories,
CheckDestroy: checkDestroy,
Steps: []resource.TestStep{
{
Config: configWithMappingsFields(projectID, clusterName, indexName, true),
Check: checkWithMappingsFields(projectID, clusterName, indexName, true),
},
// TODO: revise update behavior as part of CLOUDP-352324
// {
// Config: configWithMappingsFields(projectID, clusterName, indexName, false),
// Check: checkWithMappingsFields(projectID, clusterName, indexName, false),
// },
},
})
}

func configBasic(projectID, clusterName, indexName string) string {
return fmt.Sprintf(`
resource "mongodbatlas_search_index_api" "test" {
Expand All @@ -61,6 +84,59 @@ func configBasic(projectID, clusterName, indexName string) string {
`, projectID, clusterName, indexName, database, collection)
}

func configWithMappingsFields(projectID, clusterName, indexName string, with bool) string {
var fields string
if with {
fields = `
fields = {
address = jsonencode({
type = "document"
fields = {
city = {
type = "string"
analyzer = "lucene.simple"
ignoreAbove = 255
}
state = {
type = "string"
analyzer = "lucene.english"
}
}
})
company = jsonencode({
type = "string"
analyzer = "lucene.whitespace"
multi = {
mySecondaryAnalyzer = {
type = "string"
analyzer = "lucene.french"
}
}
})
employees = jsonencode({
type = "string"
analyzer = "lucene.standard"
})
}`
}
return fmt.Sprintf(`
resource "mongodbatlas_search_index_api" "test" {
group_id = %[1]q
cluster_name = %[2]q
name = %[3]q
database = %[4]q
collection_name = %[5]q

definition = {
mappings = {
dynamic = jsonencode(true)
%[6]s
}
}
}
`, projectID, clusterName, indexName, database, collection, fields)
}

func checkBasic(projectID, clusterName, indexName string) resource.TestCheckFunc {
attributes := map[string]string{
"group_id": projectID,
Expand All @@ -78,6 +154,17 @@ func checkBasic(projectID, clusterName, indexName string) resource.TestCheckFunc
return resource.ComposeAggregateTestCheckFunc(checks...)
}

func checkWithMappingsFields(projectID, clusterName, indexName string, has bool) resource.TestCheckFunc {
count := "0"
if has {
count = "3"
}
return resource.ComposeAggregateTestCheckFunc(
checkBasic(projectID, clusterName, indexName),
resource.TestCheckResourceAttr(resourceName, "latest_definition.mappings.fields.%", count),
)
}

func checkExists(resourceName string) resource.TestCheckFunc {
return func(s *terraform.State) error {
rs, ok := s.RootModule().Resources[resourceName]
Expand Down
71 changes: 71 additions & 0 deletions tools/codegen/codespec/api_to_provider_spec_mapper_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -720,6 +720,77 @@ func TestConvertToProviderSpec_typeOverride(t *testing.T) {
runTestCase(t, tc)
}

func TestConvertToProviderSpec_dynamicJSONProperties(t *testing.T) {
tc := convertToSpecTestCase{
inputOpenAPISpecPath: testDataAPISpecPath,
inputConfigPath: testDataConfigPath,
inputResourceName: "test_dynamic_json_properties",

expectedResult: &codespec.Model{
Resources: []codespec.Resource{{
Schema: &codespec.Schema{
Description: conversion.StringPtr(testResourceDesc),
Attributes: codespec.Attributes{
{
TFSchemaName: "array_of_dynamic_values",
TFModelName: "ArrayOfDynamicValues",
ComputedOptionalRequired: codespec.Optional,
CustomType: codespec.NewCustomListType(codespec.CustomTypeJSON),
List: &codespec.ListAttribute{
ElementType: codespec.CustomTypeJSON,
},
Description: conversion.StringPtr("Array of dynamic values."),
ReqBodyUsage: codespec.AllRequestBodies,
},
{
TFSchemaName: "dynamic_value",
TFModelName: "DynamicValue",
ComputedOptionalRequired: codespec.Optional,
String: &codespec.StringAttribute{},
CustomType: &codespec.CustomTypeJSONVar,
Description: conversion.StringPtr("Dynamic value."),
ReqBodyUsage: codespec.AllRequestBodies,
},
{
TFSchemaName: "object_of_dynamic_values",
TFModelName: "ObjectOfDynamicValues",
ComputedOptionalRequired: codespec.Optional,
CustomType: codespec.NewCustomMapType(codespec.CustomTypeJSON),
Map: &codespec.MapAttribute{
ElementType: codespec.CustomTypeJSON,
},
Description: conversion.StringPtr("Object of dynamic values."),
ReqBodyUsage: codespec.AllRequestBodies,
},
},
},
Name: "test_dynamic_json_properties",
PackageName: "testdynamicjsonproperties",
Operations: codespec.APIOperations{
Create: codespec.APIOperation{
Path: "/api/atlas/v2/dynamicJsonProperties",
HTTPMethod: "POST",
},
Read: codespec.APIOperation{
Path: "/api/atlas/v2/dynamicJsonProperties",
HTTPMethod: "GET",
},
Update: codespec.APIOperation{
Path: "/api/atlas/v2/dynamicJsonProperties",
HTTPMethod: "PATCH",
},
Delete: &codespec.APIOperation{
Path: "/api/atlas/v2/dynamicJsonProperties",
HTTPMethod: "DELETE",
},
VersionHeader: "application/vnd.atlas.2024-05-30+json",
},
}},
},
}
runTestCase(t, tc)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: i know it's not only from this PR but don't see the need to extract runTestCase logic instead of having it in each test.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah agree runTestCase is very lightweight, don't see harm in having it unified

}

func runTestCase(t *testing.T, tc convertToSpecTestCase) {
t.Helper()
result, err := codespec.ToCodeSpecModel(tc.inputOpenAPISpecPath, tc.inputConfigPath, &tc.inputResourceName)
Expand Down
Loading