internal/kibana/data_view/models.go (330 lines of code) (raw):

package data_view import ( "context" "slices" "github.com/elastic/terraform-provider-elasticstack/generated/kbapi" "github.com/elastic/terraform-provider-elasticstack/internal/clients" "github.com/elastic/terraform-provider-elasticstack/internal/utils" "github.com/hashicorp/terraform-plugin-framework/diag" "github.com/hashicorp/terraform-plugin-framework/path" "github.com/hashicorp/terraform-plugin-framework/types" ) func (model *dataViewModel) populateFromAPI(ctx context.Context, data *kbapi.DataViewsDataViewResponseObject, spaceID string) diag.Diagnostics { if data == nil { return nil } var diags diag.Diagnostics resourceID := clients.CompositeId{ ClusterId: model.SpaceID.ValueString(), ResourceId: *data.DataView.Id, } // An existing null map should should be semantically equal to an empty map. semanticEqualEmptyMap := func(existing types.Map, incoming types.Map) types.Map { if !utils.IsKnown(existing) && len(incoming.Elements()) == 0 { return types.MapNull(incoming.ElementType(ctx)) } return incoming } // An existing null slice should be semantically equal to an empty slice. semanticEqualEmptySlice := func(existing types.List, incoming types.List) types.List { if !utils.IsKnown(existing) && len(incoming.Elements()) == 0 { return types.ListNull(incoming.ElementType(ctx)) } return incoming } handleNamespaces := func(existingList types.List, incoming []string) types.List { p := path.Root("data_view").AtName("namespaces") existing := utils.ListTypeToSlice_String(ctx, existingList, p, &diags) // incoming is typically [] except in <= v8.8.2 where it is omitted if incoming == nil { if existing == nil { return types.ListNull(types.StringType) } else { return existingList } } // An existing null slice should be semantically equal to a slice only containing the current space ID. if existing == nil && len(incoming) == 1 && incoming[0] == model.SpaceID.ValueString() { return types.ListNull(types.StringType) } // Keep the original ordering if equal but unordered // The API response is ordered by name. // Additionally, allow for the response containing an extra namespace that is the current SpaceID // If the SpaceID is not included in the `namespaces` field, when trying to GET the object it will 404 if (len(existing) == len(incoming)) || (len(existing) == len(incoming)-1) { useExisting := true for _, ns := range existing { if !slices.Contains(incoming, ns) { if ns == spaceID { continue } useExisting = false break } } if useExisting { return existingList } } return utils.SliceToListType_String(ctx, incoming, p, &diags) } model.ID = types.StringValue(resourceID.String()) model.DataView = utils.StructToObjectType(ctx, data.DataView, getDataViewAttrTypes(), path.Root("data_view"), &diags, func(item kbapi.DataViewsDataViewResponseObjectInner, meta utils.ObjectMeta) innerModel { dvInner := utils.ObjectTypeAs[innerModel](ctx, model.DataView, meta.Path, &diags) if dvInner == nil { dvInner = &innerModel{} } return innerModel{ Title: types.StringPointerValue(item.Title), Name: types.StringPointerValue(item.Name), ID: types.StringPointerValue(item.Id), TimeFieldName: types.StringPointerValue(item.TimeFieldName), SourceFilters: semanticEqualEmptySlice(dvInner.SourceFilters, utils.SliceToListType(ctx, utils.Deref(item.SourceFilters), types.StringType, meta.Path.AtName("source_filters"), &diags, func(item kbapi.DataViewsSourcefilterItem, meta utils.ListMeta) string { return item.Value })), FieldAttributes: semanticEqualEmptyMap(dvInner.FieldAttributes, utils.MapToMapType(ctx, utils.Deref(item.FieldAttrs), getFieldAttrElemType(), meta.Path.AtName("field_attrs"), &diags, func(item kbapi.DataViewsFieldattrs, meta utils.MapMeta) fieldAttrModel { return fieldAttrModel{ CustomLabel: types.StringPointerValue(item.CustomLabel), Count: types.Int64PointerValue(utils.Itol(item.Count)), } })), RuntimeFieldMap: semanticEqualEmptyMap(dvInner.RuntimeFieldMap, utils.MapToMapType(ctx, utils.Deref(item.RuntimeFieldMap), getRuntimeFieldMapElemType(), meta.Path.AtName("runtime_field_map"), &diags, func(item kbapi.DataViewsRuntimefieldmap, meta utils.MapMeta) runtimeFieldModel { return runtimeFieldModel{ Type: types.StringValue(item.Type), ScriptSource: types.StringPointerValue(item.Script.Source), } })), FieldFormats: semanticEqualEmptyMap(dvInner.FieldFormats, utils.MapToMapType(ctx, utils.Deref(item.FieldFormats), getFieldFormatElemType(), meta.Path.AtName("field_formats"), &diags, func(item kbapi.DataViewsFieldformat, meta utils.MapMeta) fieldFormatModel { return fieldFormatModel{ ID: types.StringPointerValue(item.Id), 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), 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)), } }), } })), AllowNoIndex: types.BoolPointerValue(item.AllowNoIndex), Namespaces: handleNamespaces(dvInner.Namespaces, utils.Deref(item.Namespaces)), } }) return diags } func (model dataViewModel) toAPICreateModel(ctx context.Context) (kbapi.DataViewsCreateDataViewRequestObject, diag.Diagnostics) { var diags diag.Diagnostics body := kbapi.DataViewsCreateDataViewRequestObject{ DataView: *utils.ObjectTypeToStruct(ctx, model.DataView, path.Root("data_view"), &diags, func(item innerModel, meta utils.ObjectMeta) kbapi.DataViewsCreateDataViewRequestObjectInner { // Add spaceID to namespaces if missing spaceID := model.SpaceID.ValueString() namespaces := utils.ListTypeToSlice_String(ctx, item.Namespaces, meta.Path.AtName("namespaces"), &diags) if namespaces != nil && !slices.Contains(namespaces, spaceID) { namespaces = append(namespaces, spaceID) } return kbapi.DataViewsCreateDataViewRequestObjectInner{ AllowNoIndex: item.AllowNoIndex.ValueBoolPointer(), FieldAttrs: utils.MapRef(utils.MapTypeToMap(ctx, item.FieldAttributes, meta.Path.AtName("field_attrs"), &diags, func(item fieldAttrModel, meta utils.MapMeta) kbapi.DataViewsFieldattrs { return kbapi.DataViewsFieldattrs{ Count: utils.Ltoi(item.Count.ValueInt64Pointer()), CustomLabel: item.CustomLabel.ValueStringPointer(), } })), FieldFormats: utils.MapRef(convertFieldFormats(utils.MapTypeToMap(ctx, item.FieldFormats, meta.Path.AtName("field_formats"), &diags, convertFieldFormat))), Id: utils.ValueStringPointer(item.ID), Name: utils.ValueStringPointer(item.Name), Namespaces: utils.SliceRef(namespaces), RuntimeFieldMap: utils.MapRef(utils.MapTypeToMap(ctx, item.RuntimeFieldMap, meta.Path.AtName("runtime_field_map"), &diags, convertRuntimeFieldMap)), SourceFilters: utils.SliceRef(utils.ListTypeToSlice(ctx, item.SourceFilters, meta.Path.AtName("source_filters"), &diags, convertSourceFilter)), TimeFieldName: utils.ValueStringPointer(item.TimeFieldName), Title: item.Title.ValueString(), } }), Override: model.Override.ValueBoolPointer(), } return body, diags } func (model dataViewModel) toAPIUpdateModel(ctx context.Context) (kbapi.DataViewsUpdateDataViewRequestObject, diag.Diagnostics) { var diags diag.Diagnostics body := kbapi.DataViewsUpdateDataViewRequestObject{ DataView: utils.Deref(utils.ObjectTypeToStruct(ctx, model.DataView, path.Root("data_view"), &diags, func(item innerModel, meta utils.ObjectMeta) kbapi.DataViewsUpdateDataViewRequestObjectInner { return kbapi.DataViewsUpdateDataViewRequestObjectInner{ AllowNoIndex: item.AllowNoIndex.ValueBoolPointer(), FieldFormats: utils.MapRef(convertFieldFormats(utils.MapTypeToMap(ctx, item.FieldFormats, meta.Path.AtName("field_formats"), &diags, convertFieldFormat))), Name: utils.ValueStringPointer(item.Name), RuntimeFieldMap: utils.MapRef(utils.MapTypeToMap(ctx, item.RuntimeFieldMap, meta.Path.AtName("runtime_field_map"), &diags, convertRuntimeFieldMap)), SourceFilters: utils.SliceRef(utils.ListTypeToSlice(ctx, item.SourceFilters, meta.Path.AtName("source_filters"), &diags, convertSourceFilter)), TimeFieldName: utils.ValueStringPointer(item.TimeFieldName), Title: item.Title.ValueStringPointer(), } })), } return body, diags } func convertFieldFormats(src map[string]kbapi.DataViewsFieldformat) kbapi.DataViewsFieldformats { if src == nil { return nil } dst := make(kbapi.DataViewsFieldformats, len(src)) for k, v := range src { dst[k] = v } return dst } func convertFieldFormat(item fieldFormatModel, meta utils.MapMeta) kbapi.DataViewsFieldformat { return kbapi.DataViewsFieldformat{ Id: item.ID.ValueStringPointer(), 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(), 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()), } }), } } func convertRuntimeFieldMap(item runtimeFieldModel, meta utils.MapMeta) kbapi.DataViewsRuntimefieldmap { return kbapi.DataViewsRuntimefieldmap{ Type: item.Type.ValueString(), Script: kbapi.DataViewsRuntimefieldmapScript{ Source: item.ScriptSource.ValueStringPointer(), }, } } func convertSourceFilter(item string, meta utils.ListMeta) kbapi.DataViewsSourcefilterItem { return kbapi.DataViewsSourcefilterItem{Value: item} } func (model dataViewModel) getViewIDAndSpaceID() (viewID string, spaceID string) { viewID = model.ID.ValueString() spaceID = model.SpaceID.ValueString() resourceID := model.ID.ValueString() maybeCompositeID, _ := clients.CompositeIdFromStr(resourceID) if maybeCompositeID != nil { viewID = maybeCompositeID.ResourceId spaceID = maybeCompositeID.ClusterId } return } type dataViewModel struct { ID types.String `tfsdk:"id"` SpaceID types.String `tfsdk:"space_id"` Override types.Bool `tfsdk:"override"` DataView types.Object `tfsdk:"data_view"` //> innerModel } type innerModel struct { Title types.String `tfsdk:"title"` Name types.String `tfsdk:"name"` ID types.String `tfsdk:"id"` TimeFieldName types.String `tfsdk:"time_field_name"` SourceFilters types.List `tfsdk:"source_filters"` //> string FieldAttributes types.Map `tfsdk:"field_attrs"` //> fieldAttrModel RuntimeFieldMap types.Map `tfsdk:"runtime_field_map"` //> runtimeFieldModel FieldFormats types.Map `tfsdk:"field_formats"` //> fieldFormatModel AllowNoIndex types.Bool `tfsdk:"allow_no_index"` Namespaces types.List `tfsdk:"namespaces"` //> string } type fieldAttrModel struct { CustomLabel types.String `tfsdk:"custom_label"` Count types.Int64 `tfsdk:"count"` } type runtimeFieldModel struct { Type types.String `tfsdk:"type"` ScriptSource types.String `tfsdk:"script_source"` } type fieldFormatModel struct { ID types.String `tfsdk:"id"` Params types.Object `tfsdk:"params"` //> fieldFormatParamsModel } type fieldFormatParamsModel struct { 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"` }