diff --git a/CHANGELOG.md b/CHANGELOG.md index 779010e41..cd4130ec3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ - Fix a provider crash when interacting with elasticstack_kibana_data_view resources created with 0.11.0. ([#979](https://github.com/elastic/terraform-provider-elasticstack/pull/979)) - Add `max_primary_shard_docs` condition to ILM rollover ([#845](https://github.com/elastic/terraform-provider-elasticstack/pull/845)) +- Add missing entries to `data_view.field_formats.params` ([#1001](https://github.com/elastic/terraform-provider-elasticstack/pull/1001)) ## [0.11.13] - 2025-01-09 diff --git a/docs/resources/kibana_data_view.md b/docs/resources/kibana_data_view.md index 2ac12cf65..15bf155cd 100644 --- a/docs/resources/kibana_data_view.md +++ b/docs/resources/kibana_data_view.md @@ -26,6 +26,117 @@ resource "elasticstack_kibana_data_view" "my_data_view" { namespaces = ["backend"] } } + +resource "elasticstack_kibana_data_view" "custom_fields_data_view" { + data_view = { + name = "custom-data-view" + id = "custom-data-view" + title = "logs-*" + time_field_name = "@timestamp" + namespaces = ["default"] + field_formats = { + "host.uptime" = { + id = "duration" + params = { + input_format = "hours" + output_format = "humanizePrecise" + output_precision = 2 + include_space_with_suffix = true + use_short_suffix = true + } + } + "user.last_password_change" = { + id = "relative_date" + params = {} + } + "user.last_login" = { + id = "date" + params = { + pattern = "MMM D, YYYY @ HH:mm:ss.SSS" + timezone = "America/New_York" + } + } + "user.is_active" = { + id = "boolean" + params = {} + } + "user.status" = { + id = "color" + params = { + field_type = "string" + colors = [ + { + range = "-Infinity:Infinity" + regex = "inactive*" + text = "#000000" + background = "#ffffff" + } + ] + } + } + "user.message" = { + id = "truncate" + params = { + field_length = 10 + } + } + "host.name" = { + id = "string" + params = { + transform = "upper" + } + } + "response.code" = { + id = "static_lookup" + params = { + lookup_entries = [ + { + key = "200" + value = "OK" + }, + { + key = "404" + value = "Not Found" + } + ] + unknown_key_value = "Unknown" + } + } + "url.original" = { + id = "url" + params = { + type = "a" + urltemplate = "https://test.com/{{value}}" + labeltemplate = "{{value}}" + } + } + "user.profile_picture" = { + id = "url" + params = { + type = "img" + urltemplate = "https://test.com/{{value}}" + labeltemplate = "{{value}}" + width = 6 + height = 4 + } + } + "user.answering_message" = { + id = "url" + params = { + type = "audio" + urltemplate = "https://test.com/{{value}}" + labeltemplate = "{{value}}" + } + } + } + field_attrs = { + "response.code" = { + custom_label = "Response Code" + count = 0 + } + } + } +} ``` @@ -77,7 +188,7 @@ Optional: Required: -- `id` (String) +- `id` (String) The ID of the field format. Valid values include: `boolean`, `color`, `date`, `duration`, `number`, `percent`, `relative_date`, `static_lookup`, `string`, `truncate`, `url`. Optional: @@ -88,9 +199,44 @@ Optional: Optional: -- `labeltemplate` (String) -- `pattern` (String) -- `urltemplate` (String) +- `colors` (Attributes List) Color rules for the field. (see [below for nested schema](#nestedatt--data_view--field_formats--params--colors)) +- `field_length` (Number) Length to truncate the field value. +- `field_type` (String) Field type for color formatting (e.g., `string`, `number`). +- `height` (Number) Height for image type URLs. +- `include_space_with_suffix` (Boolean) Whether to include a space before the suffix in duration format. +- `input_format` (String) Input format for duration fields (e.g., `hours`, `minutes`). +- `labeltemplate` (String) Label template for the field value. +- `lookup_entries` (Attributes List) Key-value pairs for static lookup. (see [below for nested schema](#nestedatt--data_view--field_formats--params--lookup_entries)) +- `output_format` (String) Output format for duration fields (e.g., `humanizePrecise`, `humanize`). +- `output_precision` (Number) Precision for duration output. +- `pattern` (String) Pattern for formatting the field value. +- `timezone` (String) Timezone for date formatting (e.g., `America/New_York`). +- `transform` (String) Transform to apply to string fields (e.g., `upper`, `lower`). +- `type` (String) Type of URL format (e.g., `a`, `img`, `audio`). +- `unknown_key_value` (String) Value to display when key is not found in lookup. +- `urltemplate` (String) URL template for the field value. +- `use_short_suffix` (Boolean) Whether to use short suffixes in duration format. +- `width` (Number) Width for image type URLs. + + +### Nested Schema for `data_view.field_formats.params.width` + +Optional: + +- `background` (String) Background color in hex format. +- `range` (String) Range for the color rule (e.g., `-Infinity:Infinity`). +- `regex` (String) Regex pattern for the color rule. +- `text` (String) Text color in hex format. + + + +### Nested Schema for `data_view.field_formats.params.width` + +Required: + +- `key` (String) Key for the lookup entry. +- `value` (String) Value for the lookup entry. + diff --git a/examples/resources/elasticstack_kibana_data_view/resource.tf b/examples/resources/elasticstack_kibana_data_view/resource.tf index 81a77987d..347b40069 100644 --- a/examples/resources/elasticstack_kibana_data_view/resource.tf +++ b/examples/resources/elasticstack_kibana_data_view/resource.tf @@ -11,3 +11,114 @@ resource "elasticstack_kibana_data_view" "my_data_view" { namespaces = ["backend"] } } + +resource "elasticstack_kibana_data_view" "custom_fields_data_view" { + data_view = { + name = "custom-data-view" + id = "custom-data-view" + title = "logs-*" + time_field_name = "@timestamp" + namespaces = ["default"] + field_formats = { + "host.uptime" = { + id = "duration" + params = { + input_format = "hours" + output_format = "humanizePrecise" + output_precision = 2 + include_space_with_suffix = true + use_short_suffix = true + } + } + "user.last_password_change" = { + id = "relative_date" + params = {} + } + "user.last_login" = { + id = "date" + params = { + pattern = "MMM D, YYYY @ HH:mm:ss.SSS" + timezone = "America/New_York" + } + } + "user.is_active" = { + id = "boolean" + params = {} + } + "user.status" = { + id = "color" + params = { + field_type = "string" + colors = [ + { + range = "-Infinity:Infinity" + regex = "inactive*" + text = "#000000" + background = "#ffffff" + } + ] + } + } + "user.message" = { + id = "truncate" + params = { + field_length = 10 + } + } + "host.name" = { + id = "string" + params = { + transform = "upper" + } + } + "response.code" = { + id = "static_lookup" + params = { + lookup_entries = [ + { + key = "200" + value = "OK" + }, + { + key = "404" + value = "Not Found" + } + ] + unknown_key_value = "Unknown" + } + } + "url.original" = { + id = "url" + params = { + type = "a" + urltemplate = "https://test.com/{{value}}" + labeltemplate = "{{value}}" + } + } + "user.profile_picture" = { + id = "url" + params = { + type = "img" + urltemplate = "https://test.com/{{value}}" + labeltemplate = "{{value}}" + width = 6 + height = 4 + } + } + "user.answering_message" = { + id = "url" + params = { + type = "audio" + urltemplate = "https://test.com/{{value}}" + labeltemplate = "{{value}}" + } + } + } + field_attrs = { + "response.code" = { + custom_label = "Response Code" + count = 0 + } + } + } +} diff --git a/generated/kbapi/Makefile b/generated/kbapi/Makefile index 70a7690e7..b35dc2f56 100644 --- a/generated/kbapi/Makefile +++ b/generated/kbapi/Makefile @@ -2,7 +2,7 @@ SHELL := /bin/bash ROOT_DIR := $(shell dirname $(realpath $(firstword $(MAKEFILE_LIST)))) -github_ref := refs/heads/main +github_ref ?= refs/heads/main oas_url := https://raw.githubusercontent.com/elastic/kibana/$(github_ref)/oas_docs/output/kibana.yaml .PHONY: all @@ -13,7 +13,7 @@ all: download transform generate ## Fetch, bundle, transform, and generate the A download: oas.yaml ## Download the remote schema oas.yaml: - curl -sSo oas.yaml "$(oas_url)" + curl -sSfo oas.yaml "$(oas_url)" .PHONY: transform transform: download ## Transform and filter the schema diff --git a/generated/kbapi/kibana.gen.go b/generated/kbapi/kibana.gen.go index b37d067f8..93472041d 100644 --- a/generated/kbapi/kibana.gen.go +++ b/generated/kbapi/kibana.gen.go @@ -667,9 +667,38 @@ type DataViewsFieldformat struct { // DataViewsFieldformatParams defines model for Data_views_fieldformat_params. type DataViewsFieldformatParams struct { - LabelTemplate *string `json:"labelTemplate,omitempty"` - Pattern *string `json:"pattern,omitempty"` - UrlTemplate *string `json:"urlTemplate,omitempty"` + Colors *[]DataViewsFieldformatParamsColor `json:"colors,omitempty"` + FieldLength *int `json:"fieldLength,omitempty"` + FieldType *string `json:"fieldType,omitempty"` + Height *int `json:"height,omitempty"` + IncludeSpaceWithSuffix *bool `json:"includeSpaceWithSuffix,omitempty"` + InputFormat *string `json:"inputFormat,omitempty"` + LabelTemplate *string `json:"labelTemplate,omitempty"` + LookupEntries *[]DataViewsFieldformatParamsLookup `json:"lookupEntries,omitempty"` + OutputFormat *string `json:"outputFormat,omitempty"` + OutputPrecision *int `json:"outputPrecision,omitempty"` + Pattern *string `json:"pattern,omitempty"` + Timezone *string `json:"timezone,omitempty"` + Transform *string `json:"transform,omitempty"` + Type *string `json:"type,omitempty"` + UnknownKeyValue *string `json:"unknownKeyValue,omitempty"` + UrlTemplate *string `json:"urlTemplate,omitempty"` + UseShortSuffix *bool `json:"useShortSuffix,omitempty"` + Width *int `json:"width,omitempty"` +} + +// DataViewsFieldformatParamsColor defines model for Data_views_fieldformat_params_color. +type DataViewsFieldformatParamsColor struct { + Background *string `json:"background,omitempty"` + Range *string `json:"range,omitempty"` + Regex *string `json:"regex,omitempty"` + Text *string `json:"text,omitempty"` +} + +// DataViewsFieldformatParamsLookup defines model for Data_views_fieldformat_params_lookup. +type DataViewsFieldformatParamsLookup struct { + Key *string `json:"key,omitempty"` + Value *string `json:"value,omitempty"` } // DataViewsFieldformats A map of field formats by field name. @@ -2469,8 +2498,9 @@ type PostFleetAgentPoliciesJSONBody struct { Namespace string `json:"namespace"` // Overrides Override settings that are defined in the agent policy. Input settings cannot be overridden. The override option should be used only in unusual circumstances and not as a routine procedure. - Overrides *map[string]interface{} `json:"overrides,omitempty"` - SpaceIds *[]string `json:"space_ids,omitempty"` + Overrides *map[string]interface{} `json:"overrides,omitempty"` + RequiredVersions *interface{} `json:"required_versions,omitempty"` + SpaceIds *[]string `json:"space_ids,omitempty"` // SupportsAgentless Indicates whether the agent policy supports agentless integrations. SupportsAgentless *bool `json:"supports_agentless,omitempty"` @@ -2572,8 +2602,9 @@ type PutFleetAgentPoliciesAgentpolicyidJSONBody struct { Namespace string `json:"namespace"` // Overrides Override settings that are defined in the agent policy. Input settings cannot be overridden. The override option should be used only in unusual circumstances and not as a routine procedure. - Overrides *map[string]interface{} `json:"overrides,omitempty"` - SpaceIds *[]string `json:"space_ids,omitempty"` + Overrides *map[string]interface{} `json:"overrides,omitempty"` + RequiredVersions *interface{} `json:"required_versions,omitempty"` + SpaceIds *[]string `json:"space_ids,omitempty"` // SupportsAgentless Indicates whether the agent policy supports agentless integrations. SupportsAgentless *bool `json:"supports_agentless,omitempty"` diff --git a/generated/kbapi/transform_schema.go b/generated/kbapi/transform_schema.go index 1bf32292d..62a5cfd53 100644 --- a/generated/kbapi/transform_schema.go +++ b/generated/kbapi/transform_schema.go @@ -767,9 +767,46 @@ func transformKibanaPaths(schema *Schema) { schema.Components.Set("schemas.Data_views_fieldformat_params", Map{ "type": "object", "properties": Map{ - "pattern": Map{"type": "string"}, - "urlTemplate": Map{"type": "string"}, - "labelTemplate": Map{"type": "string"}, + "pattern": Map{"type": "string"}, + "urlTemplate": Map{"type": "string"}, + "labelTemplate": Map{"type": "string"}, + "inputFormat": Map{"type": "string"}, + "outputFormat": Map{"type": "string"}, + "outputPrecision": Map{"type": "integer"}, + "includeSpaceWithSuffix": Map{"type": "boolean"}, + "useShortSuffix": Map{"type": "boolean"}, + "timezone": Map{"type": "string"}, + "fieldType": Map{"type": "string"}, + "colors": Map{ + "type": "array", + "items": Map{"$ref": "#/components/schemas/Data_views_fieldformat_params_color"}, + }, + "fieldLength": Map{"type": "integer"}, + "transform": Map{"type": "string"}, + "lookupEntries": Map{ + "type": "array", + "items": Map{"$ref": "#/components/schemas/Data_views_fieldformat_params_lookup"}, + }, + "unknownKeyValue": Map{"type": "string"}, + "type": Map{"type": "string"}, + "width": Map{"type": "integer"}, + "height": Map{"type": "integer"}, + }, + }) + schema.Components.Set("schemas.Data_views_fieldformat_params_color", Map{ + "type": "object", + "properties": Map{ + "range": Map{"type": "string"}, + "regex": Map{"type": "string"}, + "text": Map{"type": "string"}, + "background": Map{"type": "string"}, + }, + }) + schema.Components.Set("schemas.Data_views_fieldformat_params_lookup", Map{ + "type": "object", + "properties": Map{ + "key": Map{"type": "string"}, + "value": Map{"type": "string"}, }, }) @@ -795,7 +832,8 @@ func transformFleetPaths(schema *Schema) { // [request body.keep_monitoring_alive]: expected value of type [boolean] but got [null] // [request body.supports_agentless]: expected value of type [boolean] but got [null] // [request body.overrides]: expected value of type [boolean] but got [null] - for _, key := range []string{"keep_monitoring_alive", "supports_agentless", "overrides"} { + // [request body.required_versions]: definition for this key is missing"} + for _, key := range []string{"keep_monitoring_alive", "supports_agentless", "overrides", "required_versions"} { agentPoliciesPath.Post.Set(fmt.Sprintf("requestBody.content.application/json.schema.properties.%s.x-omitempty", key), true) agentPolicyPath.Put.Set(fmt.Sprintf("requestBody.content.application/json.schema.properties.%s.x-omitempty", key), true) } diff --git a/internal/kibana/data_view/models.go b/internal/kibana/data_view/models.go index abe38d4d3..e73dc4d80 100644 --- a/internal/kibana/data_view/models.go +++ b/internal/kibana/data_view/models.go @@ -118,9 +118,38 @@ func (model *dataViewModel) populateFromAPI(ctx context.Context, data *kbapi.Dat Params: utils.StructToObjectType(ctx, item.Params, getFieldFormatParamsAttrTypes(), meta.Path.AtName("params"), &diags, func(item kbapi.DataViewsFieldformatParams, meta utils.ObjectMeta) fieldFormatParamsModel { return fieldFormatParamsModel{ - Pattern: types.StringPointerValue(item.Pattern), - UrlTemplate: types.StringPointerValue(item.UrlTemplate), - LabelTemplate: types.StringPointerValue(item.LabelTemplate), + Pattern: types.StringPointerValue(item.Pattern), + UrlTemplate: types.StringPointerValue(item.UrlTemplate), + LabelTemplate: types.StringPointerValue(item.LabelTemplate), + InputFormat: types.StringPointerValue(item.InputFormat), + OutputFormat: types.StringPointerValue(item.OutputFormat), + OutputPrecision: types.Int64PointerValue(utils.Itol(item.OutputPrecision)), + IncludeSpaceWithSuffix: types.BoolPointerValue(item.IncludeSpaceWithSuffix), + UseShortSuffix: types.BoolPointerValue(item.UseShortSuffix), + Timezone: types.StringPointerValue(item.Timezone), + FieldType: types.StringPointerValue(item.FieldType), + Colors: utils.SliceToListType(ctx, utils.Deref(item.Colors), getFieldFormatParamsColorsElemType(), meta.Path.AtName("colors"), meta.Diags, + func(item kbapi.DataViewsFieldformatParamsColor, meta utils.ListMeta) colorConfigModel { + return colorConfigModel{ + Range: types.StringPointerValue(item.Range), + Regex: types.StringPointerValue(item.Regex), + Text: types.StringPointerValue(item.Text), + Background: types.StringPointerValue(item.Background), + } + }), + FieldLength: types.Int64PointerValue(utils.Itol(item.FieldLength)), + Transform: types.StringPointerValue(item.Transform), + LookupEntries: utils.SliceToListType(ctx, utils.Deref(item.LookupEntries), getFieldFormatParamsLookupEntryElemType(), meta.Path.AtName("lookup_entries"), meta.Diags, + func(item kbapi.DataViewsFieldformatParamsLookup, meta utils.ListMeta) lookupEntryModel { + return lookupEntryModel{ + Key: types.StringPointerValue(item.Key), + Value: types.StringPointerValue(item.Value), + } + }), + UnknownKeyValue: types.StringPointerValue(item.UnknownKeyValue), + Type: types.StringPointerValue(item.Type), + Width: types.Int64PointerValue(utils.Itol(item.Width)), + Height: types.Int64PointerValue(utils.Itol(item.Height)), } }), } @@ -210,9 +239,38 @@ func convertFieldFormat(item fieldFormatModel, meta utils.MapMeta) kbapi.DataVie Params: utils.ObjectTypeToStruct(meta.Context, item.Params, meta.Path.AtName("params"), meta.Diags, func(item fieldFormatParamsModel, meta utils.ObjectMeta) kbapi.DataViewsFieldformatParams { return kbapi.DataViewsFieldformatParams{ - LabelTemplate: item.LabelTemplate.ValueStringPointer(), - Pattern: item.Pattern.ValueStringPointer(), - UrlTemplate: item.UrlTemplate.ValueStringPointer(), + LabelTemplate: item.LabelTemplate.ValueStringPointer(), + Pattern: item.Pattern.ValueStringPointer(), + UrlTemplate: item.UrlTemplate.ValueStringPointer(), + InputFormat: item.InputFormat.ValueStringPointer(), + OutputFormat: item.OutputFormat.ValueStringPointer(), + OutputPrecision: utils.Ltoi(item.OutputPrecision.ValueInt64Pointer()), + IncludeSpaceWithSuffix: item.IncludeSpaceWithSuffix.ValueBoolPointer(), + UseShortSuffix: item.UseShortSuffix.ValueBoolPointer(), + Timezone: item.Timezone.ValueStringPointer(), + FieldType: item.FieldType.ValueStringPointer(), + Colors: utils.SliceRef(utils.ListTypeToSlice(meta.Context, item.Colors, meta.Path.AtName("colors"), meta.Diags, + func(item colorConfigModel, meta utils.ListMeta) kbapi.DataViewsFieldformatParamsColor { + return kbapi.DataViewsFieldformatParamsColor{ + Background: item.Background.ValueStringPointer(), + Range: item.Range.ValueStringPointer(), + Regex: item.Regex.ValueStringPointer(), + Text: item.Text.ValueStringPointer(), + } + })), + FieldLength: utils.Ltoi(item.FieldLength.ValueInt64Pointer()), + Transform: item.Transform.ValueStringPointer(), + LookupEntries: utils.SliceRef(utils.ListTypeToSlice(meta.Context, item.LookupEntries, meta.Path.AtName("lookup_entries"), meta.Diags, + func(item lookupEntryModel, meta utils.ListMeta) kbapi.DataViewsFieldformatParamsLookup { + return kbapi.DataViewsFieldformatParamsLookup{ + Key: item.Key.ValueStringPointer(), + Value: item.Value.ValueStringPointer(), + } + })), + UnknownKeyValue: item.UnknownKeyValue.ValueStringPointer(), + Type: item.Type.ValueStringPointer(), + Width: utils.Ltoi(item.Width.ValueInt64Pointer()), + Height: utils.Ltoi(item.Height.ValueInt64Pointer()), } }), } @@ -281,7 +339,34 @@ type fieldFormatModel struct { } type fieldFormatParamsModel struct { - Pattern types.String `tfsdk:"pattern"` - UrlTemplate types.String `tfsdk:"urltemplate"` - LabelTemplate types.String `tfsdk:"labeltemplate"` + Pattern types.String `tfsdk:"pattern"` + UrlTemplate types.String `tfsdk:"urltemplate"` + LabelTemplate types.String `tfsdk:"labeltemplate"` + InputFormat types.String `tfsdk:"input_format"` + OutputFormat types.String `tfsdk:"output_format"` + OutputPrecision types.Int64 `tfsdk:"output_precision"` + IncludeSpaceWithSuffix types.Bool `tfsdk:"include_space_with_suffix"` + UseShortSuffix types.Bool `tfsdk:"use_short_suffix"` + Timezone types.String `tfsdk:"timezone"` + FieldType types.String `tfsdk:"field_type"` + Colors types.List `tfsdk:"colors"` //> colorConfigModel + FieldLength types.Int64 `tfsdk:"field_length"` + Transform types.String `tfsdk:"transform"` + LookupEntries types.List `tfsdk:"lookup_entries"` //> lookupEntryModel + UnknownKeyValue types.String `tfsdk:"unknown_key_value"` + Type types.String `tfsdk:"type"` + Width types.Int64 `tfsdk:"width"` + Height types.Int64 `tfsdk:"height"` +} + +type colorConfigModel struct { + Range types.String `tfsdk:"range"` + Regex types.String `tfsdk:"regex"` + Text types.String `tfsdk:"text"` + Background types.String `tfsdk:"background"` +} + +type lookupEntryModel struct { + Key types.String `tfsdk:"key"` + Value types.String `tfsdk:"value"` } diff --git a/internal/kibana/data_view/models_test.go b/internal/kibana/data_view/models_test.go index b21fafc28..bc65e7c30 100644 --- a/internal/kibana/data_view/models_test.go +++ b/internal/kibana/data_view/models_test.go @@ -54,7 +54,8 @@ func TestPopulateFromAPI(t *testing.T) { }, FieldFormats: &kbapi.DataViewsFieldformats{ "field1": kbapi.DataViewsFieldformat{ - Id: utils.Pointer("field1"), + Id: utils.Pointer("field1"), + Params: nil, }, }, RuntimeFieldMap: &map[string]kbapi.DataViewsRuntimefieldmap{ @@ -151,8 +152,119 @@ func TestToAPICreateModel(t *testing.T) { Pattern: types.StringValue("0.00"), UrlTemplate: types.StringValue("https://test.com/{{value}}"), LabelTemplate: types.StringValue("{{value}}"), + Colors: types.ListNull(getFieldFormatParamsColorsElemType()), + LookupEntries: types.ListNull(getFieldFormatParamsLookupEntryElemType()), }, getFieldFormatParamsAttrTypes(), path.Root("data_view").AtName("field_formats").AtMapKey("field1").AtName("params"), &diags), }, + "host.uptime": { + ID: types.StringValue("duration"), + Params: utils.ObjectValueFrom(ctx, &fieldFormatParamsModel{ + InputFormat: types.StringValue("hours"), + OutputFormat: types.StringValue("humanizePrecise"), + OutputPrecision: types.Int64Value(2), + IncludeSpaceWithSuffix: types.BoolValue(true), + UseShortSuffix: types.BoolValue(true), + Colors: types.ListNull(getFieldFormatParamsColorsElemType()), + LookupEntries: types.ListNull(getFieldFormatParamsLookupEntryElemType()), + }, getFieldFormatParamsAttrTypes(), path.Root("data_view").AtName("field_formats").AtMapKey("host.uptime").AtName("params"), &diags), + }, + "user.last_password_change": { + ID: types.StringValue("relative_date"), + Params: types.ObjectNull(getFieldFormatParamsAttrTypes()), + }, + "user.last_login": { + ID: types.StringValue("date"), + Params: utils.ObjectValueFrom(ctx, &fieldFormatParamsModel{ + Pattern: types.StringValue("MMM D, YYYY @ HH:mm:ss.SSS"), + Timezone: types.StringValue("America/New_York"), + Colors: types.ListNull(getFieldFormatParamsColorsElemType()), + LookupEntries: types.ListNull(getFieldFormatParamsLookupEntryElemType()), + }, getFieldFormatParamsAttrTypes(), path.Root("data_view").AtName("field_formats").AtMapKey("user.last_login").AtName("params"), &diags), + }, + "user.is_active": { + ID: types.StringValue("boolean"), + Params: types.ObjectNull(getFieldFormatParamsAttrTypes()), + }, + "user.status": { + ID: types.StringValue("color"), + Params: utils.ObjectValueFrom(ctx, &fieldFormatParamsModel{ + FieldType: types.StringValue("string"), + Colors: utils.ListValueFrom(ctx, []colorConfigModel{ + { + Range: types.StringValue("-Infinity:Infinity"), + Regex: types.StringValue("inactive*"), + Text: types.StringValue("#000000"), + Background: types.StringValue("#ffffff"), + }, + }, getFieldFormatParamsColorsElemType(), path.Root("data_view").AtName("field_formats").AtMapKey("user.status").AtName("params").AtName("colors"), &diags), + LookupEntries: types.ListNull(getFieldFormatParamsLookupEntryElemType()), + }, getFieldFormatParamsAttrTypes(), path.Root("data_view").AtName("field_formats").AtMapKey("user.status").AtName("params"), &diags), + }, + "user.message": { + ID: types.StringValue("truncate"), + Params: utils.ObjectValueFrom(ctx, &fieldFormatParamsModel{ + FieldLength: types.Int64Value(10), + Colors: types.ListNull(getFieldFormatParamsColorsElemType()), + LookupEntries: types.ListNull(getFieldFormatParamsLookupEntryElemType()), + }, getFieldFormatParamsAttrTypes(), path.Root("data_view").AtName("field_formats").AtMapKey("user.message").AtName("params"), &diags), + }, + "host.name": { + ID: types.StringValue("string"), + Params: utils.ObjectValueFrom(ctx, &fieldFormatParamsModel{ + Transform: types.StringValue("upper"), + Colors: types.ListNull(getFieldFormatParamsColorsElemType()), + LookupEntries: types.ListNull(getFieldFormatParamsLookupEntryElemType()), + }, getFieldFormatParamsAttrTypes(), path.Root("data_view").AtName("field_formats").AtMapKey("host.name").AtName("params"), &diags), + }, + "response.code": { + ID: types.StringValue("static_lookup"), + Params: utils.ObjectValueFrom(ctx, &fieldFormatParamsModel{ + Colors: types.ListNull(getFieldFormatParamsColorsElemType()), + LookupEntries: utils.ListValueFrom(ctx, []lookupEntryModel{ + { + Key: types.StringValue("200"), + Value: types.StringValue("OK"), + }, + { + Key: types.StringValue("404"), + Value: types.StringValue("Not Found"), + }, + }, getFieldFormatParamsLookupEntryElemType(), path.Root("data_view").AtName("field_formats").AtMapKey("response.code").AtName("params").AtName("lookup_entries"), &diags), + UnknownKeyValue: types.StringValue("Unknown"), + }, getFieldFormatParamsAttrTypes(), path.Root("data_view").AtName("field_formats").AtMapKey("response.code").AtName("params"), &diags), + }, + "url.original": { + ID: types.StringValue("url"), + Params: utils.ObjectValueFrom(ctx, &fieldFormatParamsModel{ + Type: types.StringValue("a"), + UrlTemplate: types.StringValue("URL TEMPLATE"), + LabelTemplate: types.StringValue("LABEL TEMPLATE"), + Colors: types.ListNull(getFieldFormatParamsColorsElemType()), + LookupEntries: types.ListNull(getFieldFormatParamsLookupEntryElemType()), + }, getFieldFormatParamsAttrTypes(), path.Root("data_view").AtName("field_formats").AtMapKey("url.original").AtName("params"), &diags), + }, + "user.profile_picture": { + ID: types.StringValue("url"), + Params: utils.ObjectValueFrom(ctx, &fieldFormatParamsModel{ + Type: types.StringValue("img"), + UrlTemplate: types.StringValue("URL TEMPLATE"), + LabelTemplate: types.StringValue("LABEL TEMPLATE"), + Width: types.Int64Value(6), + Height: types.Int64Value(4), + Colors: types.ListNull(getFieldFormatParamsColorsElemType()), + LookupEntries: types.ListNull(getFieldFormatParamsLookupEntryElemType()), + }, getFieldFormatParamsAttrTypes(), path.Root("data_view").AtName("field_formats").AtMapKey("user.profile_picture").AtName("params"), &diags), + }, + "user.answering_message": { + ID: types.StringValue("url"), + Params: utils.ObjectValueFrom(ctx, &fieldFormatParamsModel{ + Type: types.StringValue("audio"), + UrlTemplate: types.StringValue("URL TEMPLATE"), + LabelTemplate: types.StringValue("LABEL TEMPLATE"), + Colors: types.ListNull(getFieldFormatParamsColorsElemType()), + LookupEntries: types.ListNull(getFieldFormatParamsLookupEntryElemType()), + }, getFieldFormatParamsAttrTypes(), path.Root("data_view").AtName("field_formats").AtMapKey("user.answering_message").AtName("params"), &diags), + }, }, getFieldFormatElemType(), path.Root("data_view").AtName("field_formats"), &diags), AllowNoIndex: types.BoolValue(true), Namespaces: utils.ListValueFrom(ctx, []string{"backend", "o11y"}, types.StringType, path.Root("data_view").AtName("namespaces"), &diags), @@ -177,6 +289,97 @@ func TestToAPICreateModel(t *testing.T) { LabelTemplate: utils.Pointer("{{value}}"), }, }, + "host.uptime": kbapi.DataViewsFieldformat{ + Id: utils.Pointer("duration"), + Params: &kbapi.DataViewsFieldformatParams{ + InputFormat: utils.Pointer("hours"), + OutputFormat: utils.Pointer("humanizePrecise"), + OutputPrecision: utils.Pointer(2), + IncludeSpaceWithSuffix: utils.Pointer(true), + UseShortSuffix: utils.Pointer(true), + }, + }, + "user.last_password_change": kbapi.DataViewsFieldformat{ + Id: utils.Pointer("relative_date"), + }, + "user.last_login": kbapi.DataViewsFieldformat{ + Id: utils.Pointer("date"), + Params: &kbapi.DataViewsFieldformatParams{ + Pattern: utils.Pointer("MMM D, YYYY @ HH:mm:ss.SSS"), + Timezone: utils.Pointer("America/New_York"), + }, + }, + "user.is_active": kbapi.DataViewsFieldformat{ + Id: utils.Pointer("boolean"), + }, + "user.status": kbapi.DataViewsFieldformat{ + Id: utils.Pointer("color"), + Params: &kbapi.DataViewsFieldformatParams{ + FieldType: utils.Pointer("string"), + Colors: &[]kbapi.DataViewsFieldformatParamsColor{ + { + Range: utils.Pointer("-Infinity:Infinity"), + Regex: utils.Pointer("inactive*"), + Text: utils.Pointer("#000000"), + Background: utils.Pointer("#ffffff"), + }, + }, + }, + }, + "user.message": kbapi.DataViewsFieldformat{ + Id: utils.Pointer("truncate"), + Params: &kbapi.DataViewsFieldformatParams{ + FieldLength: utils.Pointer(10), + }, + }, + "host.name": kbapi.DataViewsFieldformat{ + Id: utils.Pointer("string"), + Params: &kbapi.DataViewsFieldformatParams{ + Transform: utils.Pointer("upper"), + }, + }, + "response.code": kbapi.DataViewsFieldformat{ + Id: utils.Pointer("static_lookup"), + Params: &kbapi.DataViewsFieldformatParams{ + LookupEntries: &[]kbapi.DataViewsFieldformatParamsLookup{ + { + Key: utils.Pointer("200"), + Value: utils.Pointer("OK"), + }, + { + Key: utils.Pointer("404"), + Value: utils.Pointer("Not Found"), + }, + }, + UnknownKeyValue: utils.Pointer("Unknown"), + }, + }, + "url.original": kbapi.DataViewsFieldformat{ + Id: utils.Pointer("url"), + Params: &kbapi.DataViewsFieldformatParams{ + Type: utils.Pointer("a"), + UrlTemplate: utils.Pointer("URL TEMPLATE"), + LabelTemplate: utils.Pointer("LABEL TEMPLATE"), + }, + }, + "user.profile_picture": kbapi.DataViewsFieldformat{ + Id: utils.Pointer("url"), + Params: &kbapi.DataViewsFieldformatParams{ + Type: utils.Pointer("img"), + UrlTemplate: utils.Pointer("URL TEMPLATE"), + LabelTemplate: utils.Pointer("LABEL TEMPLATE"), + Width: utils.Pointer(6), + Height: utils.Pointer(4), + }, + }, + "user.answering_message": kbapi.DataViewsFieldformat{ + Id: utils.Pointer("url"), + Params: &kbapi.DataViewsFieldformatParams{ + Type: utils.Pointer("audio"), + UrlTemplate: utils.Pointer("URL TEMPLATE"), + LabelTemplate: utils.Pointer("LABEL TEMPLATE"), + }, + }, }, Id: utils.Pointer("id"), Name: utils.Pointer("name"), @@ -277,7 +480,118 @@ func TestToAPIUpdateModel(t *testing.T) { Pattern: types.StringValue("0.00"), UrlTemplate: types.StringValue("https://test.com/{{value}}"), LabelTemplate: types.StringValue("{{value}}"), - }, getFieldFormatParamsAttrTypes(), path.Root("data_view").AtMapKey("field1").AtName("params"), &diags), + Colors: types.ListNull(getFieldFormatParamsColorsElemType()), + LookupEntries: types.ListNull(getFieldFormatParamsLookupEntryElemType()), + }, getFieldFormatParamsAttrTypes(), path.Root("data_view").AtName("field_formats").AtMapKey("field1").AtName("params"), &diags), + }, + "host.uptime": { + ID: types.StringValue("duration"), + Params: utils.ObjectValueFrom(ctx, &fieldFormatParamsModel{ + InputFormat: types.StringValue("hours"), + OutputFormat: types.StringValue("humanizePrecise"), + OutputPrecision: types.Int64Value(2), + IncludeSpaceWithSuffix: types.BoolValue(true), + UseShortSuffix: types.BoolValue(true), + Colors: types.ListNull(getFieldFormatParamsColorsElemType()), + LookupEntries: types.ListNull(getFieldFormatParamsLookupEntryElemType()), + }, getFieldFormatParamsAttrTypes(), path.Root("data_view").AtName("field_formats").AtMapKey("host.uptime").AtName("params"), &diags), + }, + "user.last_password_change": { + ID: types.StringValue("relative_date"), + Params: types.ObjectNull(getFieldFormatParamsAttrTypes()), + }, + "user.last_login": { + ID: types.StringValue("date"), + Params: utils.ObjectValueFrom(ctx, &fieldFormatParamsModel{ + Pattern: types.StringValue("MMM D, YYYY @ HH:mm:ss.SSS"), + Timezone: types.StringValue("America/New_York"), + Colors: types.ListNull(getFieldFormatParamsColorsElemType()), + LookupEntries: types.ListNull(getFieldFormatParamsLookupEntryElemType()), + }, getFieldFormatParamsAttrTypes(), path.Root("data_view").AtName("field_formats").AtMapKey("user.last_login").AtName("params"), &diags), + }, + "user.is_active": { + ID: types.StringValue("boolean"), + Params: types.ObjectNull(getFieldFormatParamsAttrTypes()), + }, + "user.status": { + ID: types.StringValue("color"), + Params: utils.ObjectValueFrom(ctx, &fieldFormatParamsModel{ + FieldType: types.StringValue("string"), + Colors: utils.ListValueFrom(ctx, []colorConfigModel{ + { + Range: types.StringValue("-Infinity:Infinity"), + Regex: types.StringValue("inactive*"), + Text: types.StringValue("#000000"), + Background: types.StringValue("#ffffff"), + }, + }, getFieldFormatParamsColorsElemType(), path.Root("data_view").AtName("field_formats").AtMapKey("user.status").AtName("params").AtName("colors"), &diags), + LookupEntries: types.ListNull(getFieldFormatParamsLookupEntryElemType()), + }, getFieldFormatParamsAttrTypes(), path.Root("data_view").AtName("field_formats").AtMapKey("user.status").AtName("params"), &diags), + }, + "user.message": { + ID: types.StringValue("truncate"), + Params: utils.ObjectValueFrom(ctx, &fieldFormatParamsModel{ + FieldLength: types.Int64Value(10), + Colors: types.ListNull(getFieldFormatParamsColorsElemType()), + LookupEntries: types.ListNull(getFieldFormatParamsLookupEntryElemType()), + }, getFieldFormatParamsAttrTypes(), path.Root("data_view").AtName("field_formats").AtMapKey("user.message").AtName("params"), &diags), + }, + "host.name": { + ID: types.StringValue("string"), + Params: utils.ObjectValueFrom(ctx, &fieldFormatParamsModel{ + Transform: types.StringValue("upper"), + Colors: types.ListNull(getFieldFormatParamsColorsElemType()), + LookupEntries: types.ListNull(getFieldFormatParamsLookupEntryElemType()), + }, getFieldFormatParamsAttrTypes(), path.Root("data_view").AtName("field_formats").AtMapKey("host.name").AtName("params"), &diags), + }, + "response.code": { + ID: types.StringValue("static_lookup"), + Params: utils.ObjectValueFrom(ctx, &fieldFormatParamsModel{ + LookupEntries: utils.ListValueFrom(ctx, []lookupEntryModel{ + { + Key: types.StringValue("200"), + Value: types.StringValue("OK"), + }, + { + Key: types.StringValue("404"), + Value: types.StringValue("Not Found"), + }, + }, getFieldFormatParamsLookupEntryElemType(), path.Root("data_view").AtName("field_formats").AtMapKey("response.code").AtName("params").AtName("lookup_entries"), &diags), + UnknownKeyValue: types.StringValue("Unknown"), + Colors: types.ListNull(getFieldFormatParamsColorsElemType()), + }, getFieldFormatParamsAttrTypes(), path.Root("data_view").AtName("field_formats").AtMapKey("response.code").AtName("params"), &diags), + }, + "url.original": { + ID: types.StringValue("url"), + Params: utils.ObjectValueFrom(ctx, &fieldFormatParamsModel{ + Type: types.StringValue("a"), + UrlTemplate: types.StringValue("URL TEMPLATE"), + LabelTemplate: types.StringValue("LABEL TEMPLATE"), + Colors: types.ListNull(getFieldFormatParamsColorsElemType()), + LookupEntries: types.ListNull(getFieldFormatParamsLookupEntryElemType()), + }, getFieldFormatParamsAttrTypes(), path.Root("data_view").AtName("field_formats").AtMapKey("url.original").AtName("params"), &diags), + }, + "user.profile_picture": { + ID: types.StringValue("url"), + Params: utils.ObjectValueFrom(ctx, &fieldFormatParamsModel{ + Type: types.StringValue("img"), + UrlTemplate: types.StringValue("URL TEMPLATE"), + LabelTemplate: types.StringValue("LABEL TEMPLATE"), + Width: types.Int64Value(6), + Height: types.Int64Value(4), + Colors: types.ListNull(getFieldFormatParamsColorsElemType()), + LookupEntries: types.ListNull(getFieldFormatParamsLookupEntryElemType()), + }, getFieldFormatParamsAttrTypes(), path.Root("data_view").AtName("field_formats").AtMapKey("user.profile_picture").AtName("params"), &diags), + }, + "user.answering_message": { + ID: types.StringValue("url"), + Params: utils.ObjectValueFrom(ctx, &fieldFormatParamsModel{ + Type: types.StringValue("audio"), + UrlTemplate: types.StringValue("URL TEMPLATE"), + LabelTemplate: types.StringValue("LABEL TEMPLATE"), + Colors: types.ListNull(getFieldFormatParamsColorsElemType()), + LookupEntries: types.ListNull(getFieldFormatParamsLookupEntryElemType()), + }, getFieldFormatParamsAttrTypes(), path.Root("data_view").AtName("field_formats").AtMapKey("user.answering_message").AtName("params"), &diags), }, }, getFieldFormatElemType(), path.Root("data_view").AtName("field_formats"), &diags), AllowNoIndex: types.BoolValue(true), @@ -296,6 +610,97 @@ func TestToAPIUpdateModel(t *testing.T) { LabelTemplate: utils.Pointer("{{value}}"), }, }, + "host.uptime": { + Id: utils.Pointer("duration"), + Params: &kbapi.DataViewsFieldformatParams{ + InputFormat: utils.Pointer("hours"), + OutputFormat: utils.Pointer("humanizePrecise"), + OutputPrecision: utils.Pointer(2), + IncludeSpaceWithSuffix: utils.Pointer(true), + UseShortSuffix: utils.Pointer(true), + }, + }, + "user.last_password_change": { + Id: utils.Pointer("relative_date"), + }, + "user.last_login": { + Id: utils.Pointer("date"), + Params: &kbapi.DataViewsFieldformatParams{ + Pattern: utils.Pointer("MMM D, YYYY @ HH:mm:ss.SSS"), + Timezone: utils.Pointer("America/New_York"), + }, + }, + "user.is_active": { + Id: utils.Pointer("boolean"), + }, + "user.status": { + Id: utils.Pointer("color"), + Params: &kbapi.DataViewsFieldformatParams{ + FieldType: utils.Pointer("string"), + Colors: &[]kbapi.DataViewsFieldformatParamsColor{ + { + Range: utils.Pointer("-Infinity:Infinity"), + Regex: utils.Pointer("inactive*"), + Text: utils.Pointer("#000000"), + Background: utils.Pointer("#ffffff"), + }, + }, + }, + }, + "user.message": { + Id: utils.Pointer("truncate"), + Params: &kbapi.DataViewsFieldformatParams{ + FieldLength: utils.Pointer(10), + }, + }, + "host.name": { + Id: utils.Pointer("string"), + Params: &kbapi.DataViewsFieldformatParams{ + Transform: utils.Pointer("upper"), + }, + }, + "response.code": { + Id: utils.Pointer("static_lookup"), + Params: &kbapi.DataViewsFieldformatParams{ + LookupEntries: &[]kbapi.DataViewsFieldformatParamsLookup{ + { + Key: utils.Pointer("200"), + Value: utils.Pointer("OK"), + }, + { + Key: utils.Pointer("404"), + Value: utils.Pointer("Not Found"), + }, + }, + UnknownKeyValue: utils.Pointer("Unknown"), + }, + }, + "url.original": { + Id: utils.Pointer("url"), + Params: &kbapi.DataViewsFieldformatParams{ + Type: utils.Pointer("a"), + UrlTemplate: utils.Pointer("URL TEMPLATE"), + LabelTemplate: utils.Pointer("LABEL TEMPLATE"), + }, + }, + "user.profile_picture": { + Id: utils.Pointer("url"), + Params: &kbapi.DataViewsFieldformatParams{ + Type: utils.Pointer("img"), + UrlTemplate: utils.Pointer("URL TEMPLATE"), + LabelTemplate: utils.Pointer("LABEL TEMPLATE"), + Width: utils.Pointer(6), + Height: utils.Pointer(4), + }, + }, + "user.answering_message": { + Id: utils.Pointer("url"), + Params: &kbapi.DataViewsFieldformatParams{ + Type: utils.Pointer("audio"), + UrlTemplate: utils.Pointer("URL TEMPLATE"), + LabelTemplate: utils.Pointer("LABEL TEMPLATE"), + }, + }, }, Name: utils.Pointer("name"), RuntimeFieldMap: &map[string]kbapi.DataViewsRuntimefieldmap{ diff --git a/internal/kibana/data_view/schema.go b/internal/kibana/data_view/schema.go index 6f13b985e..f803b57bd 100644 --- a/internal/kibana/data_view/schema.go +++ b/internal/kibana/data_view/schema.go @@ -129,19 +129,115 @@ func getSchema() schema.Schema { NestedObject: schema.NestedAttributeObject{ Attributes: map[string]schema.Attribute{ "id": schema.StringAttribute{ - Required: true, + Required: true, + MarkdownDescription: "The ID of the field format. Valid values include: `boolean`, `color`, `date`, `duration`, `number`, `percent`, `relative_date`, `static_lookup`, `string`, `truncate`, `url`.", }, "params": schema.SingleNestedAttribute{ Optional: true, Attributes: map[string]schema.Attribute{ "pattern": schema.StringAttribute{ - Optional: true, + Optional: true, + MarkdownDescription: "Pattern for formatting the field value.", }, "urltemplate": schema.StringAttribute{ - Optional: true, + Optional: true, + MarkdownDescription: "URL template for the field value.", }, "labeltemplate": schema.StringAttribute{ - Optional: true, + Optional: true, + MarkdownDescription: "Label template for the field value.", + }, + "input_format": schema.StringAttribute{ + Optional: true, + MarkdownDescription: "Input format for duration fields (e.g., `hours`, `minutes`).", + }, + "output_format": schema.StringAttribute{ + Optional: true, + MarkdownDescription: "Output format for duration fields (e.g., `humanizePrecise`, `humanize`).", + }, + "output_precision": schema.Int64Attribute{ + Optional: true, + MarkdownDescription: "Precision for duration output.", + }, + "include_space_with_suffix": schema.BoolAttribute{ + Optional: true, + MarkdownDescription: "Whether to include a space before the suffix in duration format.", + }, + "use_short_suffix": schema.BoolAttribute{ + Optional: true, + MarkdownDescription: "Whether to use short suffixes in duration format.", + }, + "timezone": schema.StringAttribute{ + Optional: true, + MarkdownDescription: "Timezone for date formatting (e.g., `America/New_York`).", + }, + "field_type": schema.StringAttribute{ + Optional: true, + MarkdownDescription: "Field type for color formatting (e.g., `string`, `number`).", + }, + "colors": schema.ListNestedAttribute{ + Optional: true, + MarkdownDescription: "Color rules for the field.", + NestedObject: schema.NestedAttributeObject{ + Attributes: map[string]schema.Attribute{ + "range": schema.StringAttribute{ + Optional: true, + MarkdownDescription: "Range for the color rule (e.g., `-Infinity:Infinity`).", + }, + "regex": schema.StringAttribute{ + Optional: true, + MarkdownDescription: "Regex pattern for the color rule.", + }, + "text": schema.StringAttribute{ + Optional: true, + MarkdownDescription: "Text color in hex format.", + }, + "background": schema.StringAttribute{ + Optional: true, + MarkdownDescription: "Background color in hex format.", + }, + }, + }, + }, + "field_length": schema.Int64Attribute{ + Optional: true, + MarkdownDescription: "Length to truncate the field value.", + }, + "transform": schema.StringAttribute{ + Optional: true, + MarkdownDescription: "Transform to apply to string fields (e.g., `upper`, `lower`).", + }, + "lookup_entries": schema.ListNestedAttribute{ + Optional: true, + MarkdownDescription: "Key-value pairs for static lookup.", + NestedObject: schema.NestedAttributeObject{ + Attributes: map[string]schema.Attribute{ + "key": schema.StringAttribute{ + Required: true, + MarkdownDescription: "Key for the lookup entry.", + }, + "value": schema.StringAttribute{ + Required: true, + MarkdownDescription: "Value for the lookup entry.", + }, + }, + }, + }, + "unknown_key_value": schema.StringAttribute{ + Optional: true, + MarkdownDescription: "Value to display when key is not found in lookup.", + }, + "type": schema.StringAttribute{ + Optional: true, + MarkdownDescription: "Type of URL format (e.g., `a`, `img`, `audio`).", + }, + "width": schema.Int64Attribute{ + Optional: true, + MarkdownDescription: "Width for image type URLs.", + }, + "height": schema.Int64Attribute{ + Optional: true, + MarkdownDescription: "Height for image type URLs.", }, }, }, @@ -194,3 +290,11 @@ func getFieldFormatAttrTypes() map[string]attr.Type { func getFieldFormatParamsAttrTypes() map[string]attr.Type { return getFieldFormatAttrTypes()["params"].(attr.TypeWithAttributeTypes).AttributeTypes() } + +func getFieldFormatParamsColorsElemType() attr.Type { + return getFieldFormatParamsAttrTypes()["colors"].(attr.TypeWithElementType).ElementType() +} + +func getFieldFormatParamsLookupEntryElemType() attr.Type { + return getFieldFormatParamsAttrTypes()["lookup_entries"].(attr.TypeWithElementType).ElementType() +}