diff --git a/CHANGELOG.md b/CHANGELOG.md
index 1f2b99897..2aa9f468e 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,6 +1,7 @@
## [Unreleased]
-- Add support for Kibana synthetics http and tcp monitors ([#699](https://github.com/elastic/terraform-provider-elasticstack/pull/699))
+- Improve validation for index settings and mappings ([#719](https://github.com/elastic/terraform-provider-elasticstack/pull/719))
+- Add support for Kibana synthetics http and tcp monitors ([#699](https://github.com/elastic/terraform-provider-elasticstack/pull/699))
- Add `elasticstack_kibana_spaces` data source ([#682](https://github.com/elastic/terraform-provider-elasticstack/pull/682))
## [0.11.5] - 2024-08-12
diff --git a/docs/resources/elasticsearch_component_template.md b/docs/resources/elasticsearch_component_template.md
index 5908917ba..0e150b797 100644
--- a/docs/resources/elasticsearch_component_template.md
+++ b/docs/resources/elasticsearch_component_template.md
@@ -63,7 +63,7 @@ resource "elasticstack_elasticsearch_index_template" "my_template" {
Optional:
- `alias` (Block Set) Alias to add. (see [below for nested schema](#nestedblock--template--alias))
-- `mappings` (String) Mapping for fields in the index.
+- `mappings` (String) Mapping for fields in the index. Should be specified as a JSON object of field mappings. See the documentation (https://www.elastic.co/guide/en/elasticsearch/reference/current/explicit-mapping.html) for more details
- `settings` (String) Configuration options for the index. See, https://www.elastic.co/guide/en/elasticsearch/reference/current/index-modules.html#index-modules-settings
diff --git a/docs/resources/elasticsearch_index_template.md b/docs/resources/elasticsearch_index_template.md
index 4eae1398e..f93dfbe6f 100644
--- a/docs/resources/elasticsearch_index_template.md
+++ b/docs/resources/elasticsearch_index_template.md
@@ -102,7 +102,7 @@ Optional:
Optional:
- `alias` (Block Set) Alias to add. (see [below for nested schema](#nestedblock--template--alias))
-- `mappings` (String) Mapping for fields in the index.
+- `mappings` (String) Mapping for fields in the index. Should be specified as a JSON object of field mappings. See the documentation (https://www.elastic.co/guide/en/elasticsearch/reference/current/explicit-mapping.html) for more details
- `settings` (String) Configuration options for the index. See, https://www.elastic.co/guide/en/elasticsearch/reference/current/index-modules.html#index-modules-settings
diff --git a/internal/elasticsearch/index/component_template.go b/internal/elasticsearch/index/component_template.go
index 52f419e44..f129d6fa6 100644
--- a/internal/elasticsearch/index/component_template.go
+++ b/internal/elasticsearch/index/component_template.go
@@ -93,18 +93,22 @@ func ResourceComponentTemplate() *schema.Resource {
},
},
"mappings": {
- Description: "Mapping for fields in the index.",
+ Description: "Mapping for fields in the index. Should be specified as a JSON object of field mappings. See the documentation (https://www.elastic.co/guide/en/elasticsearch/reference/current/explicit-mapping.html) for more details",
Type: schema.TypeString,
Optional: true,
DiffSuppressFunc: utils.DiffJsonSuppress,
- ValidateFunc: validation.StringIsJSON,
+ ValidateFunc: validation.All(
+ validation.StringIsJSON, stringIsJSONObject,
+ ),
},
"settings": {
Description: "Configuration options for the index. See, https://www.elastic.co/guide/en/elasticsearch/reference/current/index-modules.html#index-modules-settings",
Type: schema.TypeString,
Optional: true,
DiffSuppressFunc: utils.DiffIndexSettingSuppress,
- ValidateFunc: validation.StringIsJSON,
+ ValidateFunc: validation.All(
+ validation.StringIsJSON, stringIsJSONObject,
+ ),
},
},
},
diff --git a/internal/elasticsearch/index/index.go b/internal/elasticsearch/index/index.go
index 5a861a03e..27591e4c2 100644
--- a/internal/elasticsearch/index/index.go
+++ b/internal/elasticsearch/index/index.go
@@ -489,8 +489,10 @@ If specified, this mapping can include: field names, [field data types](https://
Type: schema.TypeString,
Optional: true,
DiffSuppressFunc: utils.DiffJsonSuppress,
- ValidateFunc: validation.StringIsJSON,
- Default: "{}",
+ ValidateFunc: validation.All(
+ validation.StringIsJSON, stringIsJSONObject,
+ ),
+ Default: "{}",
},
// Deprecated: individual setting field should be used instead
"settings": {
diff --git a/internal/elasticsearch/index/template.go b/internal/elasticsearch/index/template.go
index 3d079dcd1..834565096 100644
--- a/internal/elasticsearch/index/template.go
+++ b/internal/elasticsearch/index/template.go
@@ -140,18 +140,22 @@ func ResourceTemplate() *schema.Resource {
},
},
"mappings": {
- Description: "Mapping for fields in the index.",
+ Description: "Mapping for fields in the index. Should be specified as a JSON object of field mappings. See the documentation (https://www.elastic.co/guide/en/elasticsearch/reference/current/explicit-mapping.html) for more details",
Type: schema.TypeString,
Optional: true,
DiffSuppressFunc: utils.DiffJsonSuppress,
- ValidateFunc: validation.StringIsJSON,
+ ValidateFunc: validation.All(
+ validation.StringIsJSON, stringIsJSONObject,
+ ),
},
"settings": {
Description: "Configuration options for the index. See, https://www.elastic.co/guide/en/elasticsearch/reference/current/index-modules.html#index-modules-settings",
Type: schema.TypeString,
Optional: true,
DiffSuppressFunc: utils.DiffIndexSettingSuppress,
- ValidateFunc: validation.StringIsJSON,
+ ValidateFunc: validation.All(
+ validation.StringIsJSON, stringIsJSONObject,
+ ),
},
},
},
diff --git a/internal/elasticsearch/index/validation.go b/internal/elasticsearch/index/validation.go
new file mode 100644
index 000000000..0d5d8601f
--- /dev/null
+++ b/internal/elasticsearch/index/validation.go
@@ -0,0 +1,22 @@
+package index
+
+import (
+ "encoding/json"
+ "fmt"
+)
+
+func stringIsJSONObject(i interface{}, s string) (warnings []string, errors []error) {
+ iStr, ok := i.(string)
+ if !ok {
+ errors = append(errors, fmt.Errorf("expected type of %s to be string", s))
+ return warnings, errors
+ }
+
+ m := map[string]interface{}{}
+ if err := json.Unmarshal([]byte(iStr), &m); err != nil {
+ errors = append(errors, fmt.Errorf("expected %s to be a JSON object. Check the documentation for the expected format. %w", s, err))
+ return
+ }
+
+ return
+}
diff --git a/internal/elasticsearch/index/validation_test.go b/internal/elasticsearch/index/validation_test.go
new file mode 100644
index 000000000..2637f3cd9
--- /dev/null
+++ b/internal/elasticsearch/index/validation_test.go
@@ -0,0 +1,51 @@
+package index
+
+import (
+ "testing"
+
+ "github.com/stretchr/testify/require"
+)
+
+func Test_stringIsJSONObject(t *testing.T) {
+ tests := []struct {
+ name string
+ fieldVal interface{}
+ expectedErrsToContain []string
+ }{
+ {
+ name: "should not return an error for a valid json object",
+ fieldVal: "{}",
+ },
+ {
+ name: "should not return an error for a null",
+ fieldVal: "null",
+ },
+
+ {
+ name: "should return an error if the field is not a string",
+ fieldVal: true,
+ expectedErrsToContain: []string{
+ "expected type of field-name to be string",
+ },
+ },
+ {
+ name: "should return an error if the field is valid json, but not an object",
+ fieldVal: "[]",
+ expectedErrsToContain: []string{
+ "expected field-name to be a JSON object. Check the documentation for the expected format.",
+ },
+ },
+ }
+
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ warnings, errors := stringIsJSONObject(tt.fieldVal, "field-name")
+ require.Empty(t, warnings)
+
+ require.Equal(t, len(tt.expectedErrsToContain), len(errors))
+ for i, err := range errors {
+ require.ErrorContains(t, err, tt.expectedErrsToContain[i])
+ }
+ })
+ }
+}