diff --git a/internal/services/costmanagement/resource_group_cost_management_view_resource.go b/internal/services/costmanagement/resource_group_cost_management_view_resource.go index d8395db3250a..72ebd50867ad 100644 --- a/internal/services/costmanagement/resource_group_cost_management_view_resource.go +++ b/internal/services/costmanagement/resource_group_cost_management_view_resource.go @@ -4,6 +4,14 @@ package costmanagement import ( + "context" + "fmt" + "time" + + "github.com/hashicorp/go-azure-helpers/lang/pointer" + "github.com/hashicorp/go-azure-helpers/lang/response" + "github.com/hashicorp/go-azure-sdk/resource-manager/costmanagement/2023-08-01/views" + "github.com/hashicorp/terraform-provider-azurerm/helpers/tf" "github.com/hashicorp/terraform-provider-azurerm/internal/sdk" "github.com/hashicorp/terraform-provider-azurerm/internal/services/costmanagement/validate" resourceValidate "github.com/hashicorp/terraform-provider-azurerm/internal/services/resource/validate" @@ -15,6 +23,19 @@ type ResourceGroupCostManagementViewResource struct { base costManagementViewBaseResource } +type ResourceGroupCostManagementViewModel struct { + Name string `tfschema:"name"` + ResourceGroupId string `tfschema:"resource_group_id"` + DisplayName string `tfschema:"display_name"` + ChartType string `tfschema:"chart_type"` + Accumulated bool `tfschema:"accumulated"` + ReportType string `tfschema:"report_type"` + Timeframe string `tfschema:"timeframe"` + Dataset []CostManagementViewDatasetModel `tfschema:"dataset"` + Kpi []CostManagementViewKpiModel `tfschema:"kpi"` + Pivot []CostManagementViewPivotModel `tfschema:"pivot"` +} + var _ sdk.Resource = ResourceGroupCostManagementViewResource{} func (r ResourceGroupCostManagementViewResource) Arguments() map[string]*pluginsdk.Schema { @@ -40,7 +61,7 @@ func (r ResourceGroupCostManagementViewResource) Attributes() map[string]*plugin } func (r ResourceGroupCostManagementViewResource) ModelObject() interface{} { - return nil + return &ResourceGroupCostManagementViewModel{} } func (r ResourceGroupCostManagementViewResource) ResourceType() string { @@ -52,11 +73,110 @@ func (r ResourceGroupCostManagementViewResource) IDValidationFunc() pluginsdk.Sc } func (r ResourceGroupCostManagementViewResource) Create() sdk.ResourceFunc { - return r.base.createFunc(r.ResourceType(), "resource_group_id") + return sdk.ResourceFunc{ + Timeout: 30 * time.Minute, + Func: func(ctx context.Context, metadata sdk.ResourceMetaData) error { + client := metadata.Client.CostManagement.ViewsClient + + var config ResourceGroupCostManagementViewModel + if err := metadata.Decode(&config); err != nil { + return fmt.Errorf("decoding: %+v", err) + } + + id := views.NewScopedViewID(config.ResourceGroupId, config.Name) + + existing, err := client.GetByScope(ctx, id) + if err != nil { + if !response.WasNotFound(existing.HttpResponse) { + return fmt.Errorf("checking for presence of existing %s: %+v", id, err) + } + } + + if !response.WasNotFound(existing.HttpResponse) { + return tf.ImportAsExistsError(r.ResourceType(), id.ID()) + } + + accumulated := views.AccumulatedTypeFalse + if config.Accumulated { + accumulated = views.AccumulatedTypeTrue + } + + props := views.View{ + Properties: &views.ViewProperties{ + Accumulated: pointer.To(accumulated), + DisplayName: pointer.To(config.DisplayName), + Chart: pointer.To(views.ChartType(config.ChartType)), + Query: &views.ReportConfigDefinition{ + DataSet: expandDatasetFromModel(config.Dataset), + Timeframe: views.ReportTimeframeType(config.Timeframe), + Type: views.ReportTypeUsage, + }, + Kpis: expandKpisFromModel(config.Kpi), + Pivots: expandPivotsFromModel(config.Pivot), + }, + } + + if _, err = client.CreateOrUpdateByScope(ctx, id, props); err != nil { + return fmt.Errorf("creating %s: %+v", id, err) + } + + metadata.SetID(id) + return nil + }, + } } func (r ResourceGroupCostManagementViewResource) Read() sdk.ResourceFunc { - return r.base.readFunc("resource_group_id") + return sdk.ResourceFunc{ + Timeout: 5 * time.Minute, + Func: func(ctx context.Context, metadata sdk.ResourceMetaData) error { + client := metadata.Client.CostManagement.ViewsClient + + id, err := views.ParseScopedViewID(metadata.ResourceData.Id()) + if err != nil { + return err + } + + resp, err := client.GetByScope(ctx, *id) + if err != nil { + if response.WasNotFound(resp.HttpResponse) { + return metadata.MarkAsGone(id) + } + return fmt.Errorf("reading %s: %+v", *id, err) + } + + state := ResourceGroupCostManagementViewModel{ + Name: id.ViewName, + ResourceGroupId: id.Scope, + } + + if model := resp.Model; model != nil { + if props := model.Properties; props != nil { + state.ChartType = string(pointer.From(props.Chart)) + state.DisplayName = pointer.From(props.DisplayName) + + accumulated := false + if props.Accumulated != nil { + accumulated = views.AccumulatedTypeTrue == *props.Accumulated + } + state.Accumulated = accumulated + + state.Kpi = flattenKpisToModel(props.Kpis) + state.Pivot = flattenPivotsToModel(props.Pivots) + + if query := props.Query; query != nil { + state.Timeframe = string(query.Timeframe) + state.ReportType = string(query.Type) + if query.DataSet != nil { + state.Dataset = flattenDatasetToModel(query.DataSet) + } + } + } + } + + return metadata.Encode(&state) + }, + } } func (r ResourceGroupCostManagementViewResource) Delete() sdk.ResourceFunc { @@ -64,5 +184,67 @@ func (r ResourceGroupCostManagementViewResource) Delete() sdk.ResourceFunc { } func (r ResourceGroupCostManagementViewResource) Update() sdk.ResourceFunc { - return r.base.updateFunc() + return sdk.ResourceFunc{ + Timeout: 30 * time.Minute, + Func: func(ctx context.Context, metadata sdk.ResourceMetaData) error { + client := metadata.Client.CostManagement.ViewsClient + + id, err := views.ParseScopedViewID(metadata.ResourceData.Id()) + if err != nil { + return err + } + + var config ResourceGroupCostManagementViewModel + if err := metadata.Decode(&config); err != nil { + return fmt.Errorf("decoding: %+v", err) + } + + // Update operation requires latest eTag to be set in the request. + existing, err := client.GetByScope(ctx, *id) + if err != nil { + return fmt.Errorf("reading %s: %+v", *id, err) + } + model := existing.Model + + if model != nil { + if model.ETag == nil { + return fmt.Errorf("add %s: etag was nil", *id) + } + } + + if model.Properties == nil { + return fmt.Errorf("retreiving properties for %s for update: %+v", *id, err) + } + + if metadata.ResourceData.HasChange("display_name") { + model.Properties.DisplayName = pointer.To(config.DisplayName) + } + + if metadata.ResourceData.HasChange("chart_type") { + model.Properties.Chart = pointer.To(views.ChartType(config.ChartType)) + } + + if metadata.ResourceData.HasChange("dataset") { + model.Properties.Query.DataSet = expandDatasetFromModel(config.Dataset) + } + + if metadata.ResourceData.HasChange("timeframe") { + model.Properties.Query.Timeframe = views.ReportTimeframeType(config.Timeframe) + } + + if metadata.ResourceData.HasChange("kpi") { + model.Properties.Kpis = expandKpisFromModel(config.Kpi) + } + + if metadata.ResourceData.HasChange("pivot") { + model.Properties.Pivots = expandPivotsFromModel(config.Pivot) + } + + if _, err = client.CreateOrUpdateByScope(ctx, *id, *model); err != nil { + return fmt.Errorf("updating %s: %+v", *id, err) + } + + return nil + }, + } } diff --git a/internal/services/costmanagement/subscription_cost_management_view_resource.go b/internal/services/costmanagement/subscription_cost_management_view_resource.go index 7d4967e71cd3..5a47f914f524 100644 --- a/internal/services/costmanagement/subscription_cost_management_view_resource.go +++ b/internal/services/costmanagement/subscription_cost_management_view_resource.go @@ -4,7 +4,15 @@ package costmanagement import ( + "context" + "fmt" + "time" + + "github.com/hashicorp/go-azure-helpers/lang/pointer" + "github.com/hashicorp/go-azure-helpers/lang/response" "github.com/hashicorp/go-azure-helpers/resourcemanager/commonids" + "github.com/hashicorp/go-azure-sdk/resource-manager/costmanagement/2023-08-01/views" + "github.com/hashicorp/terraform-provider-azurerm/helpers/tf" "github.com/hashicorp/terraform-provider-azurerm/internal/sdk" "github.com/hashicorp/terraform-provider-azurerm/internal/services/costmanagement/validate" "github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk" @@ -15,6 +23,19 @@ type SubscriptionCostManagementViewResource struct { base costManagementViewBaseResource } +type SubscriptionCostManagementViewModel struct { + Name string `tfschema:"name"` + SubId string `tfschema:"subscription_id"` + DisplayName string `tfschema:"display_name"` + ChartType string `tfschema:"chart_type"` + Accumulated bool `tfschema:"accumulated"` + ReportType string `tfschema:"report_type"` + Timeframe string `tfschema:"timeframe"` + Dataset []CostManagementViewDatasetModel `tfschema:"dataset"` + Kpi []CostManagementViewKpiModel `tfschema:"kpi"` + Pivot []CostManagementViewPivotModel `tfschema:"pivot"` +} + var _ sdk.Resource = SubscriptionCostManagementViewResource{} func (r SubscriptionCostManagementViewResource) Arguments() map[string]*pluginsdk.Schema { @@ -40,7 +61,7 @@ func (r SubscriptionCostManagementViewResource) Attributes() map[string]*plugins } func (r SubscriptionCostManagementViewResource) ModelObject() interface{} { - return nil + return &SubscriptionCostManagementViewModel{} } func (r SubscriptionCostManagementViewResource) ResourceType() string { @@ -52,11 +73,110 @@ func (r SubscriptionCostManagementViewResource) IDValidationFunc() pluginsdk.Sch } func (r SubscriptionCostManagementViewResource) Create() sdk.ResourceFunc { - return r.base.createFunc(r.ResourceType(), "subscription_id") + return sdk.ResourceFunc{ + Timeout: 30 * time.Minute, + Func: func(ctx context.Context, metadata sdk.ResourceMetaData) error { + client := metadata.Client.CostManagement.ViewsClient + + var config SubscriptionCostManagementViewModel + if err := metadata.Decode(&config); err != nil { + return fmt.Errorf("decoding: %+v", err) + } + + id := views.NewScopedViewID(config.SubId, config.Name) + + existing, err := client.GetByScope(ctx, id) + if err != nil { + if !response.WasNotFound(existing.HttpResponse) { + return fmt.Errorf("checking for presence of existing %s: %+v", id, err) + } + } + + if !response.WasNotFound(existing.HttpResponse) { + return tf.ImportAsExistsError(r.ResourceType(), id.ID()) + } + + accumulated := views.AccumulatedTypeFalse + if config.Accumulated { + accumulated = views.AccumulatedTypeTrue + } + + props := views.View{ + Properties: &views.ViewProperties{ + Accumulated: pointer.To(accumulated), + DisplayName: pointer.To(config.DisplayName), + Chart: pointer.To(views.ChartType(config.ChartType)), + Query: &views.ReportConfigDefinition{ + DataSet: expandDatasetFromModel(config.Dataset), + Timeframe: views.ReportTimeframeType(config.Timeframe), + Type: views.ReportTypeUsage, + }, + Kpis: expandKpisFromModel(config.Kpi), + Pivots: expandPivotsFromModel(config.Pivot), + }, + } + + if _, err = client.CreateOrUpdateByScope(ctx, id, props); err != nil { + return fmt.Errorf("creating %s: %+v", id, err) + } + + metadata.SetID(id) + return nil + }, + } } func (r SubscriptionCostManagementViewResource) Read() sdk.ResourceFunc { - return r.base.readFunc("subscription_id") + return sdk.ResourceFunc{ + Timeout: 5 * time.Minute, + Func: func(ctx context.Context, metadata sdk.ResourceMetaData) error { + client := metadata.Client.CostManagement.ViewsClient + + id, err := views.ParseScopedViewID(metadata.ResourceData.Id()) + if err != nil { + return err + } + + resp, err := client.GetByScope(ctx, *id) + if err != nil { + if response.WasNotFound(resp.HttpResponse) { + return metadata.MarkAsGone(id) + } + return fmt.Errorf("reading %s: %+v", *id, err) + } + + state := SubscriptionCostManagementViewModel{ + Name: id.ViewName, + SubId: id.Scope, + } + + if model := resp.Model; model != nil { + if props := model.Properties; props != nil { + state.ChartType = string(pointer.From(props.Chart)) + state.DisplayName = pointer.From(props.DisplayName) + + accumulated := false + if props.Accumulated != nil { + accumulated = views.AccumulatedTypeTrue == *props.Accumulated + } + state.Accumulated = accumulated + + state.Kpi = flattenKpisToModel(props.Kpis) + state.Pivot = flattenPivotsToModel(props.Pivots) + + if query := props.Query; query != nil { + state.Timeframe = string(query.Timeframe) + state.ReportType = string(query.Type) + if query.DataSet != nil { + state.Dataset = flattenDatasetToModel(query.DataSet) + } + } + } + } + + return metadata.Encode(&state) + }, + } } func (r SubscriptionCostManagementViewResource) Delete() sdk.ResourceFunc { @@ -64,5 +184,67 @@ func (r SubscriptionCostManagementViewResource) Delete() sdk.ResourceFunc { } func (r SubscriptionCostManagementViewResource) Update() sdk.ResourceFunc { - return r.base.updateFunc() + return sdk.ResourceFunc{ + Timeout: 30 * time.Minute, + Func: func(ctx context.Context, metadata sdk.ResourceMetaData) error { + client := metadata.Client.CostManagement.ViewsClient + + id, err := views.ParseScopedViewID(metadata.ResourceData.Id()) + if err != nil { + return err + } + + var config SubscriptionCostManagementViewModel + if err := metadata.Decode(&config); err != nil { + return fmt.Errorf("decoding: %+v", err) + } + + // Update operation requires latest eTag to be set in the request. + existing, err := client.GetByScope(ctx, *id) + if err != nil { + return fmt.Errorf("reading %s: %+v", *id, err) + } + model := existing.Model + + if model != nil { + if model.ETag == nil { + return fmt.Errorf("add %s: etag was nil", *id) + } + } + + if model.Properties == nil { + return fmt.Errorf("retreiving properties for %s for update: %+v", *id, err) + } + + if metadata.ResourceData.HasChange("display_name") { + model.Properties.DisplayName = pointer.To(config.DisplayName) + } + + if metadata.ResourceData.HasChange("chart_type") { + model.Properties.Chart = pointer.To(views.ChartType(config.ChartType)) + } + + if metadata.ResourceData.HasChange("dataset") { + model.Properties.Query.DataSet = expandDatasetFromModel(config.Dataset) + } + + if metadata.ResourceData.HasChange("timeframe") { + model.Properties.Query.Timeframe = views.ReportTimeframeType(config.Timeframe) + } + + if metadata.ResourceData.HasChange("kpi") { + model.Properties.Kpis = expandKpisFromModel(config.Kpi) + } + + if metadata.ResourceData.HasChange("pivot") { + model.Properties.Pivots = expandPivotsFromModel(config.Pivot) + } + + if _, err = client.CreateOrUpdateByScope(ctx, *id, *model); err != nil { + return fmt.Errorf("updating %s: %+v", *id, err) + } + + return nil + }, + } } diff --git a/internal/services/costmanagement/view_resource_base.go b/internal/services/costmanagement/view_resource_base.go index 8eaacdd48ebb..151e1b6e2d26 100644 --- a/internal/services/costmanagement/view_resource_base.go +++ b/internal/services/costmanagement/view_resource_base.go @@ -9,14 +9,45 @@ import ( "time" "github.com/hashicorp/go-azure-helpers/lang/pointer" - "github.com/hashicorp/go-azure-helpers/lang/response" "github.com/hashicorp/go-azure-sdk/resource-manager/costmanagement/2023-08-01/views" - "github.com/hashicorp/terraform-provider-azurerm/helpers/tf" "github.com/hashicorp/terraform-provider-azurerm/internal/sdk" "github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk" "github.com/hashicorp/terraform-provider-azurerm/internal/tf/validation" ) +// Shared nested model structs for cost management view resources + +type CostManagementViewAggregationModel struct { + Name string `tfschema:"name"` + ColumnName string `tfschema:"column_name"` +} + +type CostManagementViewSortingModel struct { + Direction string `tfschema:"direction"` + Name string `tfschema:"name"` +} + +type CostManagementViewGroupingModel struct { + Type string `tfschema:"type"` + Name string `tfschema:"name"` +} + +type CostManagementViewDatasetModel struct { + Granularity string `tfschema:"granularity"` + Aggregation []CostManagementViewAggregationModel `tfschema:"aggregation"` + Sorting []CostManagementViewSortingModel `tfschema:"sorting"` + Grouping []CostManagementViewGroupingModel `tfschema:"grouping"` +} + +type CostManagementViewKpiModel struct { + Type string `tfschema:"type"` +} + +type CostManagementViewPivotModel struct { + Name string `tfschema:"name"` + Type string `tfschema:"type"` +} + type costManagementViewBaseResource struct{} func (br costManagementViewBaseResource) arguments(fields map[string]*pluginsdk.Schema) map[string]*pluginsdk.Schema { @@ -166,106 +197,6 @@ func (br costManagementViewBaseResource) attributes() map[string]*pluginsdk.Sche return map[string]*pluginsdk.Schema{} } -func (br costManagementViewBaseResource) createFunc(resourceName, scopeFieldName string) sdk.ResourceFunc { - return sdk.ResourceFunc{ - Timeout: 30 * time.Minute, - Func: func(ctx context.Context, metadata sdk.ResourceMetaData) error { - client := metadata.Client.CostManagement.ViewsClient - id := views.NewScopedViewID(metadata.ResourceData.Get(scopeFieldName).(string), metadata.ResourceData.Get("name").(string)) - - existing, err := client.GetByScope(ctx, id) - if err != nil { - if !response.WasNotFound(existing.HttpResponse) { - return fmt.Errorf("checking for presence of existing %s: %+v", id, err) - } - } - - if !response.WasNotFound(existing.HttpResponse) { - return tf.ImportAsExistsError(resourceName, id.ID()) - } - - accumulated := views.AccumulatedTypeFalse - if accumulatedRaw := metadata.ResourceData.Get("accumulated").(bool); accumulatedRaw { - accumulated = views.AccumulatedTypeTrue - } - - props := views.View{ - Properties: &views.ViewProperties{ - Accumulated: pointer.To(accumulated), - DisplayName: pointer.To(metadata.ResourceData.Get("display_name").(string)), - Chart: pointer.To(views.ChartType(metadata.ResourceData.Get("chart_type").(string))), - Query: &views.ReportConfigDefinition{ - DataSet: expandDataset(metadata.ResourceData.Get("dataset").([]interface{})), - Timeframe: views.ReportTimeframeType(metadata.ResourceData.Get("timeframe").(string)), - Type: views.ReportTypeUsage, - }, - Kpis: expandKpis(metadata.ResourceData.Get("kpi").([]interface{})), - Pivots: expandPivots(metadata.ResourceData.Get("pivot").([]interface{})), - }, - } - - if _, err = client.CreateOrUpdateByScope(ctx, id, props); err != nil { - return fmt.Errorf("creating %s: %+v", id, err) - } - - metadata.SetID(id) - return nil - }, - } -} - -func (br costManagementViewBaseResource) readFunc(scopeFieldName string) sdk.ResourceFunc { - return sdk.ResourceFunc{ - Timeout: 5 * time.Minute, - Func: func(ctx context.Context, metadata sdk.ResourceMetaData) error { - client := metadata.Client.CostManagement.ViewsClient - - id, err := views.ParseScopedViewID(metadata.ResourceData.Id()) - if err != nil { - return err - } - - resp, err := client.GetByScope(ctx, *id) - if err != nil { - if response.WasNotFound(resp.HttpResponse) { - return metadata.MarkAsGone(id) - } - return fmt.Errorf("reading %s: %+v", *id, err) - } - - metadata.ResourceData.Set("name", id.ViewName) - // lintignore:R001 - metadata.ResourceData.Set(scopeFieldName, id.Scope) - - if model := resp.Model; model != nil { - if props := model.Properties; props != nil { - metadata.ResourceData.Set("chart_type", string(pointer.From(props.Chart))) - - accumulated := false - if props.Accumulated != nil { - accumulated = views.AccumulatedTypeTrue == *props.Accumulated - } - metadata.ResourceData.Set("accumulated", accumulated) - - metadata.ResourceData.Set("display_name", props.DisplayName) - metadata.ResourceData.Set("kpi", flattenKpis(props.Kpis)) - metadata.ResourceData.Set("pivot", flattenPivots(props.Pivots)) - - if query := props.Query; query != nil { - metadata.ResourceData.Set("timeframe", string(query.Timeframe)) - metadata.ResourceData.Set("report_type", string(query.Type)) - if query.DataSet != nil { - metadata.ResourceData.Set("dataset", flattenDataset(query.DataSet)) - } - } - } - } - - return nil - }, - } -} - func (br costManagementViewBaseResource) deleteFunc() sdk.ResourceFunc { return sdk.ResourceFunc{ Timeout: 30 * time.Minute, @@ -286,284 +217,163 @@ func (br costManagementViewBaseResource) deleteFunc() sdk.ResourceFunc { } } -func (br costManagementViewBaseResource) updateFunc() sdk.ResourceFunc { - return sdk.ResourceFunc{ - Timeout: 30 * time.Minute, - Func: func(ctx context.Context, metadata sdk.ResourceMetaData) error { - client := metadata.Client.CostManagement.ViewsClient +// Typed model expand/flatten helpers - id, err := views.ParseScopedViewID(metadata.ResourceData.Id()) - if err != nil { - return err - } - - // Update operation requires latest eTag to be set in the request. - existing, err := client.GetByScope(ctx, *id) - if err != nil { - return fmt.Errorf("reading %s: %+v", *id, err) - } - model := existing.Model - - if model != nil { - if model.ETag == nil { - return fmt.Errorf("add %s: etag was nil", *id) - } - } - - if model.Properties == nil { - return fmt.Errorf("retreiving properties for %s for update: %+v", *id, err) - } - - if metadata.ResourceData.HasChange("display_name") { - model.Properties.DisplayName = pointer.To(metadata.ResourceData.Get("display_name").(string)) - } - - if metadata.ResourceData.HasChange("chart_type") { - model.Properties.Chart = pointer.To(views.ChartType(metadata.ResourceData.Get("chart_type").(string))) - } - - if metadata.ResourceData.HasChange("dataset") { - model.Properties.Query.DataSet = expandDataset(metadata.ResourceData.Get("dataset").([]interface{})) - } - if metadata.ResourceData.HasChange("timeframe") { - model.Properties.Query.Timeframe = views.ReportTimeframeType(metadata.ResourceData.Get("timeframe").(string)) - } - - if metadata.ResourceData.HasChange("kpi") { - model.Properties.Kpis = expandKpis(metadata.ResourceData.Get("kpi").([]interface{})) - } - - if metadata.ResourceData.HasChange("pivot") { - model.Properties.Pivots = expandPivots(metadata.ResourceData.Get("pivot").([]interface{})) - } - - if _, err = client.CreateOrUpdateByScope(ctx, *id, *model); err != nil { - return fmt.Errorf("updating %s: %+v", *id, err) - } - - return nil - }, - } -} - -func expandDataset(input []interface{}) *views.ReportConfigDataset { - if len(input) == 0 || input[0] == nil { +func expandDatasetFromModel(input []CostManagementViewDatasetModel) *views.ReportConfigDataset { + if len(input) == 0 { return nil } - attrs := input[0].(map[string]interface{}) + ds := input[0] dataset := &views.ReportConfigDataset{ - Granularity: pointer.To(views.ReportGranularityType(attrs["granularity"].(string))), + Granularity: pointer.To(views.ReportGranularityType(ds.Granularity)), } - if aggregation := attrs["aggregation"].(*pluginsdk.Set).List(); len(aggregation) > 0 { - dataset.Aggregation = expandAggregation(aggregation) + if len(ds.Aggregation) > 0 { + aggregation := map[string]views.ReportConfigAggregation{} + for _, a := range ds.Aggregation { + aggregation[a.Name] = views.ReportConfigAggregation{ + Name: a.ColumnName, + Function: views.FunctionTypeSum, + } + } + dataset.Aggregation = &aggregation } - if sorting := attrs["sorting"].([]interface{}); len(sorting) > 0 { - dataset.Sorting = expandSorting(sorting) + if len(ds.Sorting) > 0 { + sorting := make([]views.ReportConfigSorting, 0) + for _, s := range ds.Sorting { + sorting = append(sorting, views.ReportConfigSorting{ + Direction: pointer.To(views.ReportConfigSortingType(s.Direction)), + Name: s.Name, + }) + } + dataset.Sorting = &sorting } - if grouping := attrs["grouping"].([]interface{}); len(grouping) > 0 { - dataset.Grouping = expandGrouping(grouping) + if len(ds.Grouping) > 0 { + grouping := make([]views.ReportConfigGrouping, 0) + for _, g := range ds.Grouping { + grouping = append(grouping, views.ReportConfigGrouping{ + Type: views.QueryColumnType(g.Type), + Name: g.Name, + }) + } + dataset.Grouping = &grouping } return dataset } -func expandAggregation(input []interface{}) *map[string]views.ReportConfigAggregation { - outputSorting := map[string]views.ReportConfigAggregation{} - if len(input) == 0 || input[0] == nil { - return &outputSorting - } - - for _, item := range input { - v := item.(map[string]interface{}) - name := v["name"].(string) - outputSorting[name] = views.ReportConfigAggregation{ - Name: v["column_name"].(string), - Function: views.FunctionTypeSum, - } +func flattenDatasetToModel(input *views.ReportConfigDataset) []CostManagementViewDatasetModel { + if input == nil { + return []CostManagementViewDatasetModel{} } - return &outputSorting -} + ds := CostManagementViewDatasetModel{} -func expandGrouping(input []interface{}) *[]views.ReportConfigGrouping { - outputGrouping := []views.ReportConfigGrouping{} - if len(input) == 0 || input[0] == nil { - return &outputGrouping + if input.Granularity != nil { + ds.Granularity = string(*input.Granularity) } - for _, item := range input { - v := item.(map[string]interface{}) - outputGrouping = append(outputGrouping, views.ReportConfigGrouping{ - Type: views.QueryColumnType(v["type"].(string)), - Name: v["name"].(string), - }) + if input.Aggregation != nil { + aggregation := make([]CostManagementViewAggregationModel, 0) + for name, item := range *input.Aggregation { + aggregation = append(aggregation, CostManagementViewAggregationModel{ + Name: name, + ColumnName: item.Name, + }) + } + ds.Aggregation = aggregation } - return &outputGrouping -} - -func expandSorting(input []interface{}) *[]views.ReportConfigSorting { - outputSorting := []views.ReportConfigSorting{} - if len(input) == 0 || input[0] == nil { - return &outputSorting + if input.Sorting != nil { + sorting := make([]CostManagementViewSortingModel, 0) + for _, item := range *input.Sorting { + if item.Direction == nil { + continue + } + sorting = append(sorting, CostManagementViewSortingModel{ + Name: item.Name, + Direction: string(*item.Direction), + }) + } + ds.Sorting = sorting } - for _, item := range input { - v := item.(map[string]interface{}) - outputSorting = append(outputSorting, views.ReportConfigSorting{ - Direction: pointer.To(views.ReportConfigSortingType(v["direction"].(string))), - Name: v["name"].(string), - }) + if input.Grouping != nil { + grouping := make([]CostManagementViewGroupingModel, 0) + for _, item := range *input.Grouping { + grouping = append(grouping, CostManagementViewGroupingModel{ + Name: item.Name, + Type: string(item.Type), + }) + } + ds.Grouping = grouping } - return &outputSorting + return []CostManagementViewDatasetModel{ds} } -func expandKpis(input []interface{}) *[]views.KpiProperties { - outputKpis := []views.KpiProperties{} - if len(input) == 0 || input[0] == nil { - return &outputKpis - } - - for _, item := range input { - v := item.(map[string]interface{}) - outputKpis = append(outputKpis, views.KpiProperties{ - Type: pointer.To(views.KpiTypeType(v["type"].(string))), +func expandKpisFromModel(input []CostManagementViewKpiModel) *[]views.KpiProperties { + kpis := make([]views.KpiProperties, 0) + for _, k := range input { + kpis = append(kpis, views.KpiProperties{ + Type: pointer.To(views.KpiTypeType(k.Type)), Enabled: pointer.To(true), }) } - - return &outputKpis + return &kpis } -func expandPivots(input []interface{}) *[]views.PivotProperties { - outputPivots := []views.PivotProperties{} - if len(input) == 0 || input[0] == nil { - return &outputPivots - } - - for _, item := range input { - v := item.(map[string]interface{}) - outputPivots = append(outputPivots, views.PivotProperties{ - Type: pointer.To(views.PivotTypeType(v["type"].(string))), - Name: pointer.To((v["name"].(string))), - }) - } - - return &outputPivots -} - -func flattenKpis(input *[]views.KpiProperties) []interface{} { - outputKpis := []interface{}{} +func flattenKpisToModel(input *[]views.KpiProperties) []CostManagementViewKpiModel { if input == nil || len(*input) == 0 { - return outputKpis + return []CostManagementViewKpiModel{} } + result := make([]CostManagementViewKpiModel, 0) for _, item := range *input { kpiType := "" if v := item.Type; v != nil && item.Enabled != nil && *item.Enabled { kpiType = string(*v) } - - outputKpis = append(outputKpis, map[string]interface{}{ - "type": kpiType, + result = append(result, CostManagementViewKpiModel{ + Type: kpiType, }) } + return result +} - return outputKpis +func expandPivotsFromModel(input []CostManagementViewPivotModel) *[]views.PivotProperties { + pivots := make([]views.PivotProperties, 0) + for _, p := range input { + pivots = append(pivots, views.PivotProperties{ + Type: pointer.To(views.PivotTypeType(p.Type)), + Name: pointer.To(p.Name), + }) + } + return &pivots } -func flattenPivots(input *[]views.PivotProperties) []interface{} { - outputPivots := []interface{}{} +func flattenPivotsToModel(input *[]views.PivotProperties) []CostManagementViewPivotModel { if input == nil || len(*input) == 0 { - return outputPivots + return []CostManagementViewPivotModel{} } + result := make([]CostManagementViewPivotModel, 0) for _, item := range *input { pivotType := "" if v := item.Type; v != nil { pivotType = string(*v) } - name := "" if p := item.Name; p != nil { name = *p } - - outputPivots = append(outputPivots, map[string]interface{}{ - "name": name, - "type": pivotType, - }) - } - - return outputPivots -} - -func flattenDataset(input *views.ReportConfigDataset) []interface{} { - outputDataset := map[string]interface{}{ - "aggregation": flattenAggregation(input.Aggregation), - "sorting": flattenSorting(input.Sorting), - "grouping": flattenGrouping(input.Grouping), - } - - if input.Granularity != nil { - outputDataset["granularity"] = string(*input.Granularity) - } - - return []interface{}{outputDataset} -} - -func flattenAggregation(input *map[string]views.ReportConfigAggregation) []interface{} { - outputAggregations := []interface{}{} - if input == nil || len(*input) == 0 { - return outputAggregations - } - - for name, item := range *input { - outputAggregations = append(outputAggregations, map[string]interface{}{ - "name": name, - "column_name": item.Name, + result = append(result, CostManagementViewPivotModel{ + Name: name, + Type: pivotType, }) } - - return outputAggregations -} - -func flattenGrouping(input *[]views.ReportConfigGrouping) []interface{} { - outputGroupings := []interface{}{} - if input == nil || len(*input) == 0 { - return outputGroupings - } - - for _, item := range *input { - outputGroupings = append(outputGroupings, map[string]interface{}{ - "name": item.Name, - "type": string(item.Type), - }) - } - - return outputGroupings -} - -func flattenSorting(input *[]views.ReportConfigSorting) []interface{} { - outputSortings := []interface{}{} - if input == nil || len(*input) == 0 { - return outputSortings - } - - for _, item := range *input { - if item.Direction == nil { - continue - } - outputSortings = append(outputSortings, map[string]interface{}{ - "name": item.Name, - "direction": string(*item.Direction), - }) - } - - return outputSortings + return result }