From fd63614899f7e4644ad506562ba3a4cd31abf7f0 Mon Sep 17 00:00:00 2001 From: Fabien COMTE Date: Tue, 4 Apr 2023 17:20:23 +0200 Subject: [PATCH] New resource aws_quicksight_template --- internal/flex/flex.go | 28 + internal/flex/flex_test.go | 42 + .../service/quicksight/data_source_test.go | 22 +- internal/service/quicksight/folder.go | 2 +- .../service/quicksight/service_package_gen.go | 4 + internal/service/quicksight/status.go | 17 + internal/service/quicksight/template.go | 1418 ++++++++++++++ .../service/quicksight/template_control.go | 1087 +++++++++++ .../service/quicksight/template_filter.go | 1298 +++++++++++++ .../service/quicksight/template_format.go | 710 +++++++ .../service/quicksight/template_parameter.go | 766 ++++++++ internal/service/quicksight/template_sheet.go | 1669 +++++++++++++++++ internal/service/quicksight/template_test.go | 268 +++ internal/service/quicksight/visual.go | 1480 +++++++++++++++ internal/service/quicksight/visual_actions.go | 642 +++++++ .../service/quicksight/visual_bar_chart.go | 277 +++ .../service/quicksight/visual_box_plot.go | 343 ++++ .../quicksight/visual_chart_configuration.go | 1195 ++++++++++++ .../service/quicksight/visual_combo_chart.go | 269 +++ .../visual_conditional_formatting.go | 400 ++++ .../quicksight/visual_custom_content.go | 102 + internal/service/quicksight/visual_empty.go | 53 + internal/service/quicksight/visual_fields.go | 443 +++++ .../service/quicksight/visual_filled_map.go | 351 ++++ .../service/quicksight/visual_funnel_chart.go | 272 +++ .../service/quicksight/visual_gauge_chart.go | 441 +++++ .../quicksight/visual_geospatial_map.go | 306 +++ .../service/quicksight/visual_heat_map.go | 232 +++ .../service/quicksight/visual_histogram.go | 294 +++ internal/service/quicksight/visual_insight.go | 716 +++++++ internal/service/quicksight/visual_kpi.go | 457 +++++ .../service/quicksight/visual_line_chart.go | 951 ++++++++++ internal/service/quicksight/visual_map.go | 135 ++ .../service/quicksight/visual_pie_chart.go | 336 ++++ .../service/quicksight/visual_pivot_table.go | 1176 ++++++++++++ .../service/quicksight/visual_radar_chart.go | 317 ++++ .../quicksight/visual_sankey_diagram.go | 200 ++ .../service/quicksight/visual_scatter_plot.go | 244 +++ internal/service/quicksight/visual_sort.go | 141 ++ internal/service/quicksight/visual_table.go | 996 ++++++++++ .../service/quicksight/visual_tree_map.go | 228 +++ .../service/quicksight/visual_waterfall.go | 264 +++ .../service/quicksight/visual_word_cloud.go | 254 +++ internal/service/quicksight/wait.go | 62 + .../docs/r/quicksight_template.html.markdown | 53 + 45 files changed, 20959 insertions(+), 2 deletions(-) create mode 100644 internal/service/quicksight/template.go create mode 100644 internal/service/quicksight/template_control.go create mode 100644 internal/service/quicksight/template_filter.go create mode 100644 internal/service/quicksight/template_format.go create mode 100644 internal/service/quicksight/template_parameter.go create mode 100644 internal/service/quicksight/template_sheet.go create mode 100644 internal/service/quicksight/template_test.go create mode 100644 internal/service/quicksight/visual.go create mode 100644 internal/service/quicksight/visual_actions.go create mode 100644 internal/service/quicksight/visual_bar_chart.go create mode 100644 internal/service/quicksight/visual_box_plot.go create mode 100644 internal/service/quicksight/visual_chart_configuration.go create mode 100644 internal/service/quicksight/visual_combo_chart.go create mode 100644 internal/service/quicksight/visual_conditional_formatting.go create mode 100644 internal/service/quicksight/visual_custom_content.go create mode 100644 internal/service/quicksight/visual_empty.go create mode 100644 internal/service/quicksight/visual_fields.go create mode 100644 internal/service/quicksight/visual_filled_map.go create mode 100644 internal/service/quicksight/visual_funnel_chart.go create mode 100644 internal/service/quicksight/visual_gauge_chart.go create mode 100644 internal/service/quicksight/visual_geospatial_map.go create mode 100644 internal/service/quicksight/visual_heat_map.go create mode 100644 internal/service/quicksight/visual_histogram.go create mode 100644 internal/service/quicksight/visual_insight.go create mode 100644 internal/service/quicksight/visual_kpi.go create mode 100644 internal/service/quicksight/visual_line_chart.go create mode 100644 internal/service/quicksight/visual_map.go create mode 100644 internal/service/quicksight/visual_pie_chart.go create mode 100644 internal/service/quicksight/visual_pivot_table.go create mode 100644 internal/service/quicksight/visual_radar_chart.go create mode 100644 internal/service/quicksight/visual_sankey_diagram.go create mode 100644 internal/service/quicksight/visual_scatter_plot.go create mode 100644 internal/service/quicksight/visual_sort.go create mode 100644 internal/service/quicksight/visual_table.go create mode 100644 internal/service/quicksight/visual_tree_map.go create mode 100644 internal/service/quicksight/visual_waterfall.go create mode 100644 internal/service/quicksight/visual_word_cloud.go create mode 100644 website/docs/r/quicksight_template.html.markdown diff --git a/internal/flex/flex.go b/internal/flex/flex.go index abf300fd9665..dd08723699fa 100644 --- a/internal/flex/flex.go +++ b/internal/flex/flex.go @@ -4,6 +4,7 @@ import ( "fmt" "strconv" "strings" + "time" "github.com/aws/aws-sdk-go/aws" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" @@ -27,6 +28,23 @@ func ExpandStringList(configured []interface{}) []*string { return vs } +// Takes the result of flatmap.Expand for an array of strings +// and returns a []*time.Time +func ExpandStringTimeList(configured []interface{}, format string) ([]*time.Time, error) { + vs := make([]*time.Time, 0, len(configured)) + for _, v := range configured { + val, ok := v.(string) + if ok && val != "" { + t, err := time.Parse(format, v.(string)) + if err != nil { + return nil, fmt.Errorf("error parsing date: %w", err) + } + vs = append(vs, aws.Time(t)) + } + } + return vs, nil +} + // ExpandStringValueList takes the result of flatmap.Expand for an array of strings // and returns a []string func ExpandStringValueList(configured []interface{}) []string { @@ -150,6 +168,16 @@ func ExpandInt64List(configured []interface{}) []*int64 { return vs } +// Takes the result of flatmap.Expand for an array of float64 +// and returns a []*float64 +func ExpandFloat64List(configured []interface{}) []*float64 { + vs := make([]*float64, 0, len(configured)) + for _, v := range configured { + vs = append(vs, aws.Float64(float64(v.(float64)))) + } + return vs +} + // Takes list of pointers to int64s. Expand to an array // of raw ints and returns a []interface{} // to keep compatibility w/ schema.NewSet diff --git a/internal/flex/flex_test.go b/internal/flex/flex_test.go index 055c3d0435ea..29e23a4fc9f1 100644 --- a/internal/flex/flex_test.go +++ b/internal/flex/flex_test.go @@ -3,6 +3,7 @@ package flex import ( "strings" "testing" + "time" "github.com/aws/aws-sdk-go/aws" "github.com/google/go-cmp/cmp" @@ -39,6 +40,47 @@ func TestExpandStringListEmptyItems(t *testing.T) { } } +func TestExpandStringTimeList(t *testing.T) { + t.Parallel() + + configured := []interface{}{"2006-01-02T15:04:05+07:00", "2023-04-13T10:25:05+01:00"} + got, _ := ExpandStringTimeList(configured, time.RFC3339) + want := []*time.Time{ + aws.Time(time.Date(2006, 1, 2, 15, 4, 5, 0, time.FixedZone("UTC-7", 7*60*60))), + aws.Time(time.Date(2023, 4, 13, 10, 25, 5, 0, time.FixedZone("UTC-1", 60*60))), + } + + if !cmp.Equal(got, want) { + t.Errorf("expanded = %v, want = %v", got, want) + } +} + +func TestExpandStringTimeListEmptyItems(t *testing.T) { + t.Parallel() + + configured := []interface{}{"2006-01-02T15:04:05+07:00", "", "2023-04-13T10:25:05+01:00"} + got, _ := ExpandStringTimeList(configured, time.RFC3339) + want := []*time.Time{ + aws.Time(time.Date(2006, 1, 2, 15, 4, 5, 0, time.FixedZone("UTC+7", 7*60*60))), + aws.Time(time.Date(2023, 4, 13, 10, 25, 5, 0, time.FixedZone("UTC+1", 60*60))), + } + + if !cmp.Equal(got, want) { + t.Errorf("expanded = %v, want = %v", got, want) + } +} + +func TestExpandStringTimeListFormatError(t *testing.T) { + t.Parallel() + + configured := []interface{}{"abc"} + _, err := ExpandStringTimeList(configured, time.RFC3339) + + if err == nil { + t.Error("Expecting format error") + } +} + func TestExpandStringValueList(t *testing.T) { t.Parallel() diff --git a/internal/service/quicksight/data_source_test.go b/internal/service/quicksight/data_source_test.go index 642cd0ca0662..91bc8dfddc51 100644 --- a/internal/service/quicksight/data_source_test.go +++ b/internal/service/quicksight/data_source_test.go @@ -296,6 +296,26 @@ resource "aws_s3_bucket_acl" "test" { acl = "public-read" } +resource "aws_s3_object" "test_data" { + depends_on = [aws_s3_bucket_acl.test] + + bucket = aws_s3_bucket.test.bucket + key = "%[1]s-test-data" + content = < 0 && v.([]interface{})[0] != nil { + input.SourceEntity = expandSourceEntity(v.([]interface{})) + } + + if v, ok := d.GetOk("definition"); ok && len(v.([]interface{})) > 0 && v.([]interface{})[0] != nil { + value, err := expandDefinition(d.Get("definition").([]interface{})) + if err != nil { + return create.DiagError(names.QuickSight, create.ErrActionCreating, ResNameTemplate, d.Get("name").(string), err) + } + input.Definition = value + } + + if v, ok := d.GetOk("permissions"); ok && len(v.([]interface{})) > 0 && v.([]interface{})[0] != nil { + input.Permissions = expandResourcePermissions(v.([]interface{})) + } + + tags := defaultTagsConfig.MergeTags(tftags.New(ctx, d.Get("tags").(map[string]interface{}))) + if len(tags) > 0 { + input.Tags = Tags(tags.IgnoreAWS()) + } + + _, err := conn.CreateTemplateWithContext(ctx, input) + if err != nil { + return create.DiagError(names.QuickSight, create.ErrActionCreating, ResNameTemplate, d.Get("name").(string), err) + } + + if _, err := waitTemplateCreated(ctx, conn, d.Id(), d.Timeout(schema.TimeoutCreate)); err != nil { + return create.DiagError(names.QuickSight, create.ErrActionWaitingForCreation, ResNameTemplate, d.Id(), err) + } + + return resourceTemplateRead(ctx, d, meta) +} + +func resourceTemplateRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + conn := meta.(*conns.AWSClient).QuickSightConn() + + awsAccountId, templateId, err := ParseTemplateId(d.Id()) + if err != nil { + return diag.FromErr(err) + } + + out, err := FindTemplateByID(ctx, conn, d.Id()) + + if !d.IsNewResource() && tfresource.NotFound(err) { + log.Printf("[WARN] QuickSight Template (%s) not found, removing from state", d.Id()) + d.SetId("") + return nil + } + + if err != nil { + return create.DiagError(names.QuickSight, create.ErrActionReading, ResNameTemplate, d.Id(), err) + } + + d.Set("arn", out.Arn) + d.Set("aws_account_id", awsAccountId) + d.Set("created_time", out.CreatedTime.Format(time.RFC3339)) + d.Set("last_updated_time", out.LastUpdatedTime.Format(time.RFC3339)) + d.Set("name", out.Name) + d.Set("status", out.Version.Status) + d.Set("template_id", out.TemplateId) + d.Set("version_description", out.Version.Description) + d.Set("version_number", out.Version.VersionNumber) + + tags, err := ListTags(ctx, conn, aws.StringValue(out.Arn)) + if err != nil { + return create.DiagError(names.QuickSight, create.ErrActionReading, ResNameTemplate, d.Id(), err) + } + + defaultTagsConfig := meta.(*conns.AWSClient).DefaultTagsConfig + ignoreTagsConfig := meta.(*conns.AWSClient).IgnoreTagsConfig + tags = tags.IgnoreAWS().IgnoreConfig(ignoreTagsConfig) + + if err := d.Set("tags", tags.RemoveDefaultConfig(defaultTagsConfig).Map()); err != nil { + return create.DiagError(names.QuickSight, create.ErrActionSetting, ResNameTemplate, d.Id(), err) + } + + if err := d.Set("tags_all", tags.Map()); err != nil { + return create.DiagError(names.QuickSight, create.ErrActionSetting, ResNameTemplate, d.Id(), err) + } + + permsResp, err := conn.DescribeTemplatePermissionsWithContext(ctx, &quicksight.DescribeTemplatePermissionsInput{ + AwsAccountId: aws.String(awsAccountId), + TemplateId: aws.String(templateId), + }) + + if err != nil { + return diag.Errorf("error describing QuickSight Template (%s) Permissions: %s", d.Id(), err) + } + + if err := d.Set("permissions", flattenPermissions(permsResp.Permissions)); err != nil { + return diag.Errorf("error setting permissions: %s", err) + } + + return nil +} + +func resourceTemplateUpdate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + conn := meta.(*conns.AWSClient).QuickSightConn() + + awsAccountId, templateId, err := ParseTemplateId(d.Id()) + if err != nil { + return diag.FromErr(err) + } + + if d.HasChangesExcept("permission", "tags", "tags_all") { + in := &quicksight.UpdateTemplateInput{ + AwsAccountId: aws.String(awsAccountId), + TemplateId: aws.String(templateId), + Name: aws.String(d.Get("name").(string)), + VersionDescription: aws.String(d.Get("version_description").(string)), + } + + if d.HasChange("source_entity") { + in.SourceEntity = expandSourceEntity(d.Get("source_entity").([]interface{})) + } + + if d.HasChange("definition") { + value, err := expandDefinition(d.Get("definition").([]interface{})) + if err != nil { + return create.DiagError(names.QuickSight, create.ErrActionUpdating, ResNameTemplate, d.Id(), err) + } + in.Definition = value + } + + log.Printf("[DEBUG] Updating QuickSight Template (%s): %#v", d.Id(), in) + _, err := conn.UpdateTemplateWithContext(ctx, in) + if err != nil { + return create.DiagError(names.QuickSight, create.ErrActionUpdating, ResNameTemplate, d.Id(), err) + } + + if _, err := waitTemplateUpdated(ctx, conn, d.Id(), d.Timeout(schema.TimeoutUpdate)); err != nil { + return create.DiagError(names.QuickSight, create.ErrActionWaitingForUpdate, ResNameTemplate, d.Id(), err) + } + } + + if d.HasChange("permissions") { + oraw, nraw := d.GetChange("permissions") + o := oraw.([]interface{}) + n := nraw.([]interface{}) + + toGrant, toRevoke := DiffPermissions(o, n) + + params := &quicksight.UpdateTemplatePermissionsInput{ + AwsAccountId: aws.String(awsAccountId), + TemplateId: aws.String(templateId), + } + + if len(toGrant) > 0 { + params.GrantPermissions = toGrant + } + + if len(toRevoke) > 0 { + params.RevokePermissions = toRevoke + } + + _, err = conn.UpdateTemplatePermissionsWithContext(ctx, params) + + if err != nil { + return diag.Errorf("error updating QuickSight Template (%s) permissions: %s", templateId, err) + } + } + + if d.HasChange("tags_all") { + o, n := d.GetChange("tags_all") + + if err := UpdateTags(ctx, conn, d.Get("arn").(string), o, n); err != nil { + return diag.Errorf("error updating QuickSight Template (%s) tags: %s", d.Id(), err) + } + } + + return resourceTemplateRead(ctx, d, meta) +} + +func resourceTemplateDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + conn := meta.(*conns.AWSClient).QuickSightConn() + + awsAccountId, templateId, err := ParseTemplateId(d.Id()) + if err != nil { + return diag.FromErr(err) + } + + log.Printf("[INFO] Deleting QuickSight Template %s", d.Id()) + _, err = conn.DeleteTemplateWithContext(ctx, &quicksight.DeleteTemplateInput{ + AwsAccountId: aws.String(awsAccountId), + TemplateId: aws.String(templateId), + }) + + if tfawserr.ErrCodeEquals(err, quicksight.ErrCodeResourceNotFoundException) { + return nil + } + + if err != nil { + return create.DiagError(names.QuickSight, create.ErrActionDeleting, ResNameTemplate, d.Id(), err) + } + + return nil +} + +func FindTemplateByID(ctx context.Context, conn *quicksight.QuickSight, id string) (*quicksight.Template, error) { + awsAccountId, templateId, err := ParseTemplateId(id) + if err != nil { + return nil, err + } + + descOpts := &quicksight.DescribeTemplateInput{ + AwsAccountId: aws.String(awsAccountId), + TemplateId: aws.String(templateId), + } + + out, err := conn.DescribeTemplateWithContext(ctx, descOpts) + + if tfawserr.ErrCodeEquals(err, quicksight.ErrCodeResourceNotFoundException) { + return nil, &retry.NotFoundError{ + LastError: err, + LastRequest: descOpts, + } + } + + if err != nil { + return nil, err + } + + if out == nil || out.Template == nil { + return nil, tfresource.NewEmptyResultError(descOpts) + } + + return out.Template, nil +} + +func expandSourceEntity(tfList []interface{}) *quicksight.TemplateSourceEntity { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + sourceEntity := &quicksight.TemplateSourceEntity{} + + if v, ok := tfMap["source_analysis"].([]interface{}); ok && len(v) > 0 { + sourceEntity.SourceAnalysis = expandSourceAnalysis(v[0].(map[string]interface{})) + } else if v, ok := tfMap["source_template"].([]interface{}); ok && len(v) > 0 { + sourceEntity.SourceTemplate = expandSourceTemplate(v[0].(map[string]interface{})) + } + + return sourceEntity +} + +func expandSourceAnalysis(tfMap map[string]interface{}) *quicksight.TemplateSourceAnalysis { + if tfMap == nil { + return nil + } + + sourceAnalysis := &quicksight.TemplateSourceAnalysis{} + if v, ok := tfMap["arn"].(string); ok && v != "" { + sourceAnalysis.Arn = aws.String(v) + } + if v, ok := tfMap["data_set_references"].([]interface{}); ok && len(v) > 0 { + sourceAnalysis.DataSetReferences = expandDataSetReferences(v) + } + + return sourceAnalysis +} + +func expandDataSetReferences(tfList []interface{}) []*quicksight.DataSetReference { + if len(tfList) == 0 { + return nil + } + + var dataSetReferences []*quicksight.DataSetReference + for _, tfMapRaw := range tfList { + tfMap, ok := tfMapRaw.(map[string]interface{}) + if !ok { + continue + } + + dataSetReference := expandDataSetReference(tfMap) + if dataSetReference == nil { + continue + } + + dataSetReferences = append(dataSetReferences, dataSetReference) + } + + return dataSetReferences +} + +func expandDataSetReference(tfMap map[string]interface{}) *quicksight.DataSetReference { + if tfMap == nil { + return nil + } + + dataSetReference := &quicksight.DataSetReference{} + if v, ok := tfMap["data_set_arn"].(string); ok { + dataSetReference.DataSetArn = aws.String(v) + } + if v, ok := tfMap["data_set_placeholder"].(string); ok { + dataSetReference.DataSetPlaceholder = aws.String(v) + } + + return dataSetReference +} + +func expandSourceTemplate(tfMap map[string]interface{}) *quicksight.TemplateSourceTemplate { + if tfMap == nil { + return nil + } + + sourceTemplate := &quicksight.TemplateSourceTemplate{} + if v, ok := tfMap["arn"].(string); ok && v != "" { + sourceTemplate.Arn = aws.String(v) + } + + return sourceTemplate +} + +func expandDefinition(tfList []interface{}) (*quicksight.TemplateVersionDefinition, error) { + if len(tfList) == 0 || tfList[0] == nil { + return nil, nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil, nil + } + + definition := &quicksight.TemplateVersionDefinition{} + + if v, ok := tfMap["analysis_defaults"].([]interface{}); ok && len(v) > 0 { + definition.AnalysisDefaults = expandAnalysisDefaults(v) + } + if v, ok := tfMap["calculated_fields"].([]interface{}); ok && len(v) > 0 { + definition.CalculatedFields = expandCalculatedFields(v) + } + if v, ok := tfMap["column_configurations"].([]interface{}); ok && len(v) > 0 { + definition.ColumnConfigurations = expandColumnConfigurations(v) + } + if v, ok := tfMap["data_set_configuration"].([]interface{}); ok && len(v) > 0 { + definition.DataSetConfigurations = expandDataSetConfigurations(v) + } + if v, ok := tfMap["filter_groups"].([]interface{}); ok && len(v) > 0 { + value, err := expandFilterGroups(v) + if err != nil { + return nil, err + } + definition.FilterGroups = value + } + if v, ok := tfMap["parameters_declarations"].([]interface{}); ok && len(v) > 0 { + value, err := expandParameterDeclarations(v) + if err != nil { + return nil, err + } + definition.ParameterDeclarations = value + } + if v, ok := tfMap["sheets"].([]interface{}); ok && len(v) > 0 { + value, err := expandSheetDefinitions(v) + if err != nil { + return nil, err + } + definition.Sheets = value + } + + return definition, nil +} + +func expandCalculatedFields(tfList []interface{}) []*quicksight.CalculatedField { + if len(tfList) == 0 { + return nil + } + + var fields []*quicksight.CalculatedField + for _, tfMapRaw := range tfList { + tfMap, ok := tfMapRaw.(map[string]interface{}) + if !ok { + continue + } + + field := expandCalculatedField(tfMap) + if field == nil { + continue + } + + fields = append(fields, field) + } + + return fields +} + +func expandCalculatedField(tfMap map[string]interface{}) *quicksight.CalculatedField { + if tfMap == nil { + return nil + } + + field := &quicksight.CalculatedField{} + + if v, ok := tfMap["data_set_identifier"].(string); ok && v != "" { + field.DataSetIdentifier = aws.String(v) + } + if v, ok := tfMap["expression"].(string); ok && v != "" { + field.Expression = aws.String(v) + } + if v, ok := tfMap["name"].(string); ok && v != "" { + field.Name = aws.String(v) + } + + return field +} + +func expandColumnConfigurations(tfList []interface{}) []*quicksight.ColumnConfiguration { + if len(tfList) == 0 { + return nil + } + + var configs []*quicksight.ColumnConfiguration + for _, tfMapRaw := range tfList { + tfMap, ok := tfMapRaw.(map[string]interface{}) + if !ok { + continue + } + + column := expandColumnConfiguration(tfMap) + if column == nil { + continue + } + + configs = append(configs, column) + } + + return configs +} + +func expandColumnConfiguration(tfMap map[string]interface{}) *quicksight.ColumnConfiguration { + if tfMap == nil { + return nil + } + + column := &quicksight.ColumnConfiguration{} + + if v, ok := tfMap["column"].([]interface{}); ok && len(v) > 0 { + column.Column = expandColumnIdentifier(v) + } + + if v, ok := tfMap["format_configuration"].([]interface{}); ok && len(v) > 0 { + column.FormatConfiguration = expandFormatConfiguration(v) + } + + if v, ok := tfMap["role"].(string); ok && v != "" { + column.Role = aws.String(v) + } + + return column +} + +func expandColumnIdentifier(tfList []interface{}) *quicksight.ColumnIdentifier { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + return expandColumnIdentifierInternal(tfMap) +} + +func expandColumnIdentifierInternal(tfMap map[string]interface{}) *quicksight.ColumnIdentifier { + column := &quicksight.ColumnIdentifier{} + + if v, ok := tfMap["data_set_identifier"].(string); ok && v != "" { + column.DataSetIdentifier = aws.String(v) + } + if v, ok := tfMap["column_name"].(string); ok && v != "" { + column.ColumnName = aws.String(v) + } + + return column +} + +func expandColumnIdentifiers(tfList []interface{}) []*quicksight.ColumnIdentifier { + if len(tfList) == 0 { + return nil + } + + var columns []*quicksight.ColumnIdentifier + for _, tfMapRaw := range tfList { + tfMap, ok := tfMapRaw.(map[string]interface{}) + if !ok { + continue + } + + col := expandColumnIdentifierInternal(tfMap) + if col == nil { + continue + } + + columns = append(columns, col) + } + + return columns +} + +func expandDataSetConfigurations(tfList []interface{}) []*quicksight.DataSetConfiguration { + if len(tfList) == 0 { + return nil + } + + var configs []*quicksight.DataSetConfiguration + for _, tfMapRaw := range tfList { + tfMap, ok := tfMapRaw.(map[string]interface{}) + if !ok { + continue + } + + config := expandDataSetConfiguration(tfMap) + if config == nil { + continue + } + + configs = append(configs, config) + } + + return configs +} + +func expandDataSetConfiguration(tfMap map[string]interface{}) *quicksight.DataSetConfiguration { + if tfMap == nil { + return nil + } + + config := &quicksight.DataSetConfiguration{} + + if v, ok := tfMap["column_group_schema_list"].([]interface{}); ok && len(v) > 0 { + config.ColumnGroupSchemaList = expandColumnGroupSchemas(v) + } + if v, ok := tfMap["data_set_schema"].([]interface{}); ok && len(v) > 0 { + config.DataSetSchema = expandDataSetSchema(v) + } + if v, ok := tfMap["placeholder"].(string); ok && v != "" { + config.Placeholder = aws.String(v) + } + + return config +} + +func expandColumnGroupSchemas(tfList []interface{}) []*quicksight.ColumnGroupSchema { + if len(tfList) == 0 { + return nil + } + + var groups []*quicksight.ColumnGroupSchema + for _, tfMapRaw := range tfList { + tfMap, ok := tfMapRaw.(map[string]interface{}) + if !ok { + continue + } + + group := expandColumnGroupSchema(tfMap) + if group == nil { + continue + } + + groups = append(groups, group) + } + + return groups +} + +func expandColumnGroupSchema(tfMap map[string]interface{}) *quicksight.ColumnGroupSchema { + if tfMap == nil { + return nil + } + + group := &quicksight.ColumnGroupSchema{} + + if v, ok := tfMap["column_group_schema_list"].([]interface{}); ok && len(v) > 0 { + group.ColumnGroupColumnSchemaList = expandColumnGroupColumnSchemas(v) + } + if v, ok := tfMap["name"].(string); ok && v != "" { + group.Name = aws.String(v) + } + + return group +} + +func expandColumnGroupColumnSchemas(tfList []interface{}) []*quicksight.ColumnGroupColumnSchema { + if len(tfList) == 0 { + return nil + } + + var columns []*quicksight.ColumnGroupColumnSchema + for _, tfMapRaw := range tfList { + tfMap, ok := tfMapRaw.(map[string]interface{}) + if !ok { + continue + } + + column := expandColumnGroupColumnSchema(tfMap) + if column == nil { + continue + } + + columns = append(columns, column) + } + + return columns +} + +func expandColumnGroupColumnSchema(tfMap map[string]interface{}) *quicksight.ColumnGroupColumnSchema { + if tfMap == nil { + return nil + } + + column := &quicksight.ColumnGroupColumnSchema{} + + if v, ok := tfMap["name"].(string); ok && v != "" { + column.Name = aws.String(v) + } + + return column +} + +func expandDataSetSchema(tfList []interface{}) *quicksight.DataSetSchema { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + schema := &quicksight.DataSetSchema{} + + if v, ok := tfMap["column_schema_list"].([]interface{}); ok && len(v) > 0 { + schema.ColumnSchemaList = expandColumnSchemas(v) + } + + return schema +} + +func expandColumnSchemas(tfList []interface{}) []*quicksight.ColumnSchema { + if len(tfList) == 0 { + return nil + } + + var columns []*quicksight.ColumnSchema + for _, tfMapRaw := range tfList { + tfMap, ok := tfMapRaw.(map[string]interface{}) + if !ok { + continue + } + + column := expandColumnSchema(tfMap) + if column == nil { + continue + } + + columns = append(columns, column) + } + + return columns +} + +func expandColumnSchema(tfMap map[string]interface{}) *quicksight.ColumnSchema { + if tfMap == nil { + return nil + } + + column := &quicksight.ColumnSchema{} + + if v, ok := tfMap["data_type"].(string); ok && v != "" { + column.DataType = aws.String(v) + } + if v, ok := tfMap["geographic_role"].(string); ok && v != "" { + column.GeographicRole = aws.String(v) + } + if v, ok := tfMap["name"].(string); ok && v != "" { + column.Name = aws.String(v) + } + + return column +} + +func expandFilterGroups(tfList []interface{}) ([]*quicksight.FilterGroup, error) { + if len(tfList) == 0 { + return nil, nil + } + + var groups []*quicksight.FilterGroup + for _, tfMapRaw := range tfList { + tfMap, ok := tfMapRaw.(map[string]interface{}) + if !ok { + continue + } + + group, err := expandFilterGroup(tfMap) + if err != nil { + return nil, err + } + if group == nil { + continue + } + + groups = append(groups, group) + } + + return groups, nil +} + +func expandFilterGroup(tfMap map[string]interface{}) (*quicksight.FilterGroup, error) { + if tfMap == nil { + return nil, nil + } + + group := &quicksight.FilterGroup{} + + if v, ok := tfMap["cross_dataset"].(string); ok && v != "" { + group.CrossDataset = aws.String(v) + } + if v, ok := tfMap["filter_group_id"].(string); ok && v != "" { + group.FilterGroupId = aws.String(v) + } + if v, ok := tfMap["status"].(string); ok && v != "" { + group.Status = aws.String(v) + } + if v, ok := tfMap["filters"].([]interface{}); ok && len(v) > 0 { + value, err := expandFilters(v) + if err != nil { + return nil, err + } + group.Filters = value + } + if v, ok := tfMap["scope_configuration"].([]interface{}); ok && len(v) > 0 { + group.ScopeConfiguration = expandFilterScopeConfiguration(v) + } + + return group, nil +} + +func expandAggregationFunction(tfList []interface{}) *quicksight.AggregationFunction { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + function := &quicksight.AggregationFunction{} + + if v, ok := tfMap["categorical_aggregation_function"].(string); ok && v != "" { + function.CategoricalAggregationFunction = aws.String(v) + } + if v, ok := tfMap["date_aggregation_function"].(string); ok && v != "" { + function.DateAggregationFunction = aws.String(v) + } + if v, ok := tfMap["numerical_aggregation_function"].([]interface{}); ok && len(v) > 0 { + function.NumericalAggregationFunction = expandNumericalAggregationFunction(v) + } + + return function +} + +func expandNumericalAggregationFunction(tfList []interface{}) *quicksight.NumericalAggregationFunction { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + function := &quicksight.NumericalAggregationFunction{} + + if v, ok := tfMap["simple_numerical_aggregation"].(string); ok && v != "" { + function.SimpleNumericalAggregation = aws.String(v) + } + if v, ok := tfMap["percentile_aggregation"].([]interface{}); ok && len(v) > 0 { + function.PercentileAggregation = expandPercentileAggregation(v) + } + + return function +} + +func expandPercentileAggregation(tfList []interface{}) *quicksight.PercentileAggregation { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + agg := &quicksight.PercentileAggregation{} + + if v, ok := tfMap["simple_numerical_aggregation"].(float64); ok { + agg.PercentileValue = aws.Float64(v) + } + + return agg +} + +func expandRollingDateConfiguration(tfList []interface{}) *quicksight.RollingDateConfiguration { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + config := &quicksight.RollingDateConfiguration{} + + if v, ok := tfMap["data_set_identifier"].(string); ok { + config.DataSetIdentifier = aws.String(v) + } + if v, ok := tfMap["expression"].(string); ok { + config.Expression = aws.String(v) + } + + return config +} + +func expandParameterDeclarations(tfList []interface{}) ([]*quicksight.ParameterDeclaration, error) { + if len(tfList) == 0 { + return nil, nil + } + + var params []*quicksight.ParameterDeclaration + for _, tfMapRaw := range tfList { + tfMap, ok := tfMapRaw.(map[string]interface{}) + if !ok { + continue + } + + param, err := expandParameterDeclaration(tfMap) + if err != nil { + return nil, err + } + if param == nil { + continue + } + + params = append(params, param) + } + + return params, nil +} + +func expandParameterDeclaration(tfMap map[string]interface{}) (*quicksight.ParameterDeclaration, error) { + if tfMap == nil { + return nil, nil + } + + param := &quicksight.ParameterDeclaration{} + + if v, ok := tfMap["date_time_parameter_declaration"].([]interface{}); ok && len(v) > 0 { + value, err := expandDateTimeParameterDeclaration(v) + if err != nil { + return nil, err + } + param.DateTimeParameterDeclaration = value + } + if v, ok := tfMap["decimal_parameter_declaration"].([]interface{}); ok && len(v) > 0 { + param.DecimalParameterDeclaration = expandDecimalParameterDeclaration(v) + } + if v, ok := tfMap["integer_parameter_declaration"].([]interface{}); ok && len(v) > 0 { + param.IntegerParameterDeclaration = expandIntegerParameterDeclaration(v) + } + if v, ok := tfMap["string_parameter_declaration"].([]interface{}); ok && len(v) > 0 { + param.StringParameterDeclaration = expandStringParameterDeclaration(v) + } + + return param, nil +} + +func expandSheetDefinitions(tfList []interface{}) ([]*quicksight.SheetDefinition, error) { + if len(tfList) == 0 { + return nil, nil + } + + var sheets []*quicksight.SheetDefinition + for _, tfMapRaw := range tfList { + tfMap, ok := tfMapRaw.(map[string]interface{}) + if !ok { + continue + } + + sheet, err := expandSheetDefinition(tfMap) + if err != nil { + return nil, err + } + if sheet == nil { + continue + } + + sheets = append(sheets, sheet) + } + + return sheets, nil +} + +func ParseTemplateId(id string) (string, string, error) { + parts := strings.SplitN(id, ",", 2) + if len(parts) != 2 || parts[0] == "" || parts[1] == "" { + return "", "", fmt.Errorf("unexpected format of ID (%s), expected AWS_ACCOUNT_ID,TEMPLATE_ID", id) + } + return parts[0], parts[1], nil +} + +func createTemplateId(awsAccountID, templateId string) string { + return fmt.Sprintf("%s,%s", awsAccountID, templateId) +} diff --git a/internal/service/quicksight/template_control.go b/internal/service/quicksight/template_control.go new file mode 100644 index 000000000000..59a264e010c0 --- /dev/null +++ b/internal/service/quicksight/template_control.go @@ -0,0 +1,1087 @@ +package quicksight + +import ( + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/quicksight" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" + "github.com/hashicorp/terraform-provider-aws/internal/flex" +) + +func filterControlsSchema() *schema.Schema { + return &schema.Schema{ // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_FilterControl.html + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 200, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "date_time_picker": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_FilterDateTimePickerControl.html + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "filter_control_id": idSchema(), + "source_filter_id": idSchema(), + "title": stringSchema(true, validation.StringLenBetween(1, 2048)), + "display_options": dateTimePickerControlDisplayOptionsSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_DateTimePickerControlDisplayOptions.html + "type": stringSchema(false, validation.StringInSlice(quicksight.SheetControlDateTimePickerType_Values(), false)), + }, + }, + }, + "dropdown": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_FilterDropDownControl.html + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "filter_control_id": idSchema(), + "source_filter_id": idSchema(), + "title": stringSchema(true, validation.StringLenBetween(1, 2048)), + "cascading_control_configuration": cascadingControlConfigurationSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_CascadingControlConfiguration.html + "display_options": dropDownControlDisplayOptionsSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_DropDownControlDisplayOptions.html + "selectable_values": filterSelectableValuesSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_FilterSelectableValues.html + "type": stringSchema(false, validation.StringInSlice(quicksight.SheetControlListType_Values(), false)), + }, + }, + }, + "list": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_FilterListControl.html + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "filter_control_id": idSchema(), + "source_filter_id": idSchema(), + "title": stringSchema(true, validation.StringLenBetween(1, 2048)), + "cascading_control_configuration": cascadingControlConfigurationSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_CascadingControlConfiguration.html + "display_options": listControlDisplayOptionsSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_ListControlDisplayOptions.html + "selectable_values": filterSelectableValuesSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_FilterSelectableValues.html + "type": stringSchema(false, validation.StringInSlice(quicksight.SheetControlListType_Values(), false)), + }, + }, + }, + "relative_date_time": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_FilterRelativeDateTimeControl.html + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "filter_control_id": idSchema(), + "source_filter_id": idSchema(), + "title": stringSchema(true, validation.StringLenBetween(1, 2048)), + "display_options": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_RelativeDateTimeControlDisplayOptions.html + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "date_time_format": stringSchema(false, validation.StringLenBetween(1, 128)), + "title_options": labelOptionsSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_LabelOptions.html + }, + }, + }, + }, + }, + }, + "slider": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_FilterSliderControl.html + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "filter_control_id": idSchema(), + "source_filter_id": idSchema(), + "title": stringSchema(true, validation.StringLenBetween(1, 2048)), + "maximum_value": { + Type: schema.TypeFloat, + Required: true, + }, + "minimum_value": { + Type: schema.TypeFloat, + Required: true, + }, + "step_size": { + Type: schema.TypeFloat, + Required: true, + }, + "display_options": sliderControlDisplayOptionsSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_SliderControlDisplayOptions.html + "type": stringSchema(false, validation.StringInSlice(quicksight.SheetControlSliderType_Values(), false)), + }, + }, + }, + "text_area": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_FilterTextAreaControl.html + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "filter_control_id": idSchema(), + "source_filter_id": idSchema(), + "title": stringSchema(true, validation.StringLenBetween(1, 2048)), + "delimiter": stringSchema(false, validation.StringLenBetween(1, 2048)), + "display_options": textAreaControlDisplayOptionsSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_TextAreaControlDisplayOptions.html + }, + }, + }, + "text_field": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_FilterTextFieldControl.html + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "filter_control_id": idSchema(), + "source_filter_id": idSchema(), + "title": stringSchema(true, validation.StringLenBetween(1, 2048)), + "display_options": textFieldControlDisplayOptionsSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_TextFieldControlDisplayOptions.html + }, + }, + }, + }, + }, + } +} + +func textFieldControlDisplayOptionsSchema() *schema.Schema { + return &schema.Schema{ // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_TextFieldControlDisplayOptions.html + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "placeholder_options": placeholderOptionsSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_TextControlPlaceholderOptions.html + "title_options": labelOptionsSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_LabelOptions.html + }, + }, + } +} + +func textAreaControlDisplayOptionsSchema() *schema.Schema { + return &schema.Schema{ // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_TextAreaControlDisplayOptions.html + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "placeholder_options": placeholderOptionsSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_TextControlPlaceholderOptions.html + "title_options": labelOptionsSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_LabelOptions.html + }, + }, + } +} + +func sliderControlDisplayOptionsSchema() *schema.Schema { + return &schema.Schema{ // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_SliderControlDisplayOptions.html + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "title_options": labelOptionsSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_LabelOptions.html + }, + }, + } +} + +func dateTimePickerControlDisplayOptionsSchema() *schema.Schema { + return &schema.Schema{ // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_DateTimePickerControlDisplayOptions.html + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "date_time_format": stringSchema(false, validation.StringLenBetween(1, 128)), + "title_options": labelOptionsSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_LabelOptions.html + }, + }, + } +} + +func listControlDisplayOptionsSchema() *schema.Schema { + return &schema.Schema{ // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_ListControlDisplayOptions.html + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "search_options": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_ListControlSearchOptions.html + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "visibility": stringSchema(false, validation.StringInSlice(quicksight.Visibility_Values(), false)), + }, + }, + }, + "select_all_options": selectAllOptionsSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_ListControlSelectAllOptions.html + "title_options": labelOptionsSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_LabelOptions.html + }, + }, + } +} + +func cascadingControlConfigurationSchema() *schema.Schema { + return &schema.Schema{ // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_CascadingControlConfiguration.html + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "source_controls": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_CascadingControlSource.html + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 200, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "column_to_match": columnSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_ColumnIdentifier.html + "source_sheet_control_id": { + Type: schema.TypeString, + Optional: true, + }, + }, + }, + }, + }, + }, + } +} + +func selectAllOptionsSchema() *schema.Schema { + return &schema.Schema{ // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_ListControlSelectAllOptions.html + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "visibility": stringSchema(false, validation.StringInSlice(quicksight.Visibility_Values(), false)), + }, + }, + } +} + +func dropDownControlDisplayOptionsSchema() *schema.Schema { + return &schema.Schema{ // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_DropDownControlDisplayOptions.html + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "select_all_options": selectAllOptionsSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_ListControlSelectAllOptions.html + "title_options": labelOptionsSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_LabelOptions.html + }, + }, + } +} + +func placeholderOptionsSchema() *schema.Schema { + return &schema.Schema{ // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_TextControlPlaceholderOptions.html + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "visibility": stringSchema(false, validation.StringInSlice(quicksight.Visibility_Values(), false)), + }, + }, + } +} + +func expandFilterControl(tfMap map[string]interface{}) *quicksight.FilterControl { + if tfMap == nil { + return nil + } + + control := &quicksight.FilterControl{} + + if v, ok := tfMap["date_time_picker"].([]interface{}); ok && len(v) > 0 { + control.DateTimePicker = expandFilterDateTimePickerControl(v) + } + if v, ok := tfMap["dropdown"].([]interface{}); ok && len(v) > 0 { + control.Dropdown = expandFilterDropDownControl(v) + } + if v, ok := tfMap["list"].([]interface{}); ok && len(v) > 0 { + control.List = expandFilterListControl(v) + } + if v, ok := tfMap["relative_date_time"].([]interface{}); ok && len(v) > 0 { + control.RelativeDateTime = expandFilterRelativeDateTimeControl(v) + } + if v, ok := tfMap["slider"].([]interface{}); ok && len(v) > 0 { + control.Slider = expandFilterSliderControl(v) + } + if v, ok := tfMap["text_area"].([]interface{}); ok && len(v) > 0 { + control.TextArea = expandFilterTextAreaControl(v) + } + if v, ok := tfMap["text_field"].([]interface{}); ok && len(v) > 0 { + control.TextField = expandFilterTextFieldControl(v) + } + return control +} + +func expandFilterDateTimePickerControl(tfList []interface{}) *quicksight.FilterDateTimePickerControl { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + control := &quicksight.FilterDateTimePickerControl{} + + if v, ok := tfMap["filter_control_id"].(string); ok && v != "" { + control.FilterControlId = aws.String(v) + } + if v, ok := tfMap["source_filter_id"].(string); ok && v != "" { + control.SourceFilterId = aws.String(v) + } + if v, ok := tfMap["title"].(string); ok && v != "" { + control.Title = aws.String(v) + } + if v, ok := tfMap["type"].(string); ok && v != "" { + control.Type = aws.String(v) + } + if v, ok := tfMap["display_options"].([]interface{}); ok && len(v) > 0 { + control.DisplayOptions = expandDateTimePickerControlDisplayOptions(v) + } + + return control +} + +func expandDateTimePickerControlDisplayOptions(tfList []interface{}) *quicksight.DateTimePickerControlDisplayOptions { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + options := &quicksight.DateTimePickerControlDisplayOptions{} + + if v, ok := tfMap["date_time_format"].(string); ok && v != "" { + options.DateTimeFormat = aws.String(v) + } + if v, ok := tfMap["title_options"].([]interface{}); ok && len(v) > 0 { + options.TitleOptions = expandLabelOptions(v) + } + + return options +} + +func expandFilterDropDownControl(tfList []interface{}) *quicksight.FilterDropDownControl { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + control := &quicksight.FilterDropDownControl{} + + if v, ok := tfMap["filter_control_id"].(string); ok && v != "" { + control.FilterControlId = aws.String(v) + } + if v, ok := tfMap["source_filter_id"].(string); ok && v != "" { + control.SourceFilterId = aws.String(v) + } + if v, ok := tfMap["title"].(string); ok && v != "" { + control.Title = aws.String(v) + } + if v, ok := tfMap["type"].(string); ok && v != "" { + control.Type = aws.String(v) + } + if v, ok := tfMap["cascading_control_configuration"].([]interface{}); ok && len(v) > 0 { + control.CascadingControlConfiguration = expandCascadingControlConfiguration(v) + } + if v, ok := tfMap["display_options"].([]interface{}); ok && len(v) > 0 { + control.DisplayOptions = expandDropDownControlDisplayOptions(v) + } + if v, ok := tfMap["selectable_values"].([]interface{}); ok && len(v) > 0 { + control.SelectableValues = expandFilterSelectableValues(v) + } + + return control +} + +func expandCascadingControlConfiguration(tfList []interface{}) *quicksight.CascadingControlConfiguration { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + config := &quicksight.CascadingControlConfiguration{} + + if v, ok := tfMap["source_controls"].([]interface{}); ok && len(v) > 0 { + config.SourceControls = expandCascadingControlSources(v) + } + + return config +} + +func expandCascadingControlSources(tfList []interface{}) []*quicksight.CascadingControlSource { + if len(tfList) == 0 { + return nil + } + + var sources []*quicksight.CascadingControlSource + for _, tfMapRaw := range tfList { + tfMap, ok := tfMapRaw.(map[string]interface{}) + if !ok { + continue + } + + source := expandCascadingControlSource(tfMap) + if source == nil { + continue + } + + sources = append(sources, source) + } + + return sources +} + +func expandCascadingControlSource(tfMap map[string]interface{}) *quicksight.CascadingControlSource { + if tfMap == nil { + return nil + } + + source := &quicksight.CascadingControlSource{} + + if v, ok := tfMap["source_sheet_control_id"].(string); ok && v != "" { + source.SourceSheetControlId = aws.String(v) + } + if v, ok := tfMap["column_to_match"].([]interface{}); ok && len(v) > 0 { + source.ColumnToMatch = expandColumnIdentifier(v) + } + + return source +} + +func expandDropDownControlDisplayOptions(tfList []interface{}) *quicksight.DropDownControlDisplayOptions { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + options := &quicksight.DropDownControlDisplayOptions{} + + if v, ok := tfMap["select_all_options"].([]interface{}); ok && len(v) > 0 { + options.SelectAllOptions = expandListControlSelectAllOptions(v) + } + if v, ok := tfMap["title_options"].([]interface{}); ok && len(v) > 0 { + options.TitleOptions = expandLabelOptions(v) + } + + return options +} + +func expandListControlSelectAllOptions(tfList []interface{}) *quicksight.ListControlSelectAllOptions { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + options := &quicksight.ListControlSelectAllOptions{} + + if v, ok := tfMap["visibility"].(string); ok && v != "" { + options.Visibility = aws.String(v) + } + + return options +} + +func expandFilterSelectableValues(tfList []interface{}) *quicksight.FilterSelectableValues { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + values := &quicksight.FilterSelectableValues{} + + if v, ok := tfMap["values"].([]interface{}); ok { + values.Values = flex.ExpandStringList(v) + } + + return values +} + +func expandFilterListControl(tfList []interface{}) *quicksight.FilterListControl { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + control := &quicksight.FilterListControl{} + + if v, ok := tfMap["filter_control_id"].(string); ok && v != "" { + control.FilterControlId = aws.String(v) + } + if v, ok := tfMap["source_filter_id"].(string); ok && v != "" { + control.SourceFilterId = aws.String(v) + } + if v, ok := tfMap["title"].(string); ok && v != "" { + control.Title = aws.String(v) + } + if v, ok := tfMap["type"].(string); ok && v != "" { + control.Type = aws.String(v) + } + if v, ok := tfMap["cascading_control_configuration"].([]interface{}); ok && len(v) > 0 { + control.CascadingControlConfiguration = expandCascadingControlConfiguration(v) + } + if v, ok := tfMap["display_options"].([]interface{}); ok && len(v) > 0 { + control.DisplayOptions = expandListControlDisplayOptions(v) + } + if v, ok := tfMap["selectable_values"].([]interface{}); ok && len(v) > 0 { + control.SelectableValues = expandFilterSelectableValues(v) + } + + return control +} + +func expandListControlDisplayOptions(tfList []interface{}) *quicksight.ListControlDisplayOptions { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + options := &quicksight.ListControlDisplayOptions{} + + if v, ok := tfMap["select_all_options"].([]interface{}); ok && len(v) > 0 { + options.SelectAllOptions = expandListControlSelectAllOptions(v) + } + if v, ok := tfMap["title_options"].([]interface{}); ok && len(v) > 0 { + options.TitleOptions = expandLabelOptions(v) + } + if v, ok := tfMap["search_options"].([]interface{}); ok && len(v) > 0 { + options.SearchOptions = expandListControlSearchOptions(v) + } + + return options +} + +func expandListControlSearchOptions(tfList []interface{}) *quicksight.ListControlSearchOptions { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + options := &quicksight.ListControlSearchOptions{} + + if v, ok := tfMap["visibility"].(string); ok && v != "" { + options.Visibility = aws.String(v) + } + + return options +} + +func expandFilterRelativeDateTimeControl(tfList []interface{}) *quicksight.FilterRelativeDateTimeControl { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + control := &quicksight.FilterRelativeDateTimeControl{} + + if v, ok := tfMap["filter_control_id"].(string); ok && v != "" { + control.FilterControlId = aws.String(v) + } + if v, ok := tfMap["source_filter_id"].(string); ok && v != "" { + control.SourceFilterId = aws.String(v) + } + if v, ok := tfMap["title"].(string); ok && v != "" { + control.Title = aws.String(v) + } + if v, ok := tfMap["display_options"].([]interface{}); ok && len(v) > 0 { + control.DisplayOptions = expandRelativeDateTimeControlDisplayOptions(v) + } + + return control +} + +func expandRelativeDateTimeControlDisplayOptions(tfList []interface{}) *quicksight.RelativeDateTimeControlDisplayOptions { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + options := &quicksight.RelativeDateTimeControlDisplayOptions{} + + if v, ok := tfMap["date_time_format"].(string); ok && v != "" { + options.DateTimeFormat = aws.String(v) + } + if v, ok := tfMap["title_options"].([]interface{}); ok && len(v) > 0 { + options.TitleOptions = expandLabelOptions(v) + } + + return options +} + +func expandFilterSliderControl(tfList []interface{}) *quicksight.FilterSliderControl { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + control := &quicksight.FilterSliderControl{} + + if v, ok := tfMap["filter_control_id"].(string); ok && v != "" { + control.FilterControlId = aws.String(v) + } + if v, ok := tfMap["source_filter_id"].(string); ok && v != "" { + control.SourceFilterId = aws.String(v) + } + if v, ok := tfMap["title"].(string); ok && v != "" { + control.Title = aws.String(v) + } + if v, ok := tfMap["type"].(string); ok && v != "" { + control.Type = aws.String(v) + } + if v, ok := tfMap["maximum_value"].(float64); ok { + control.MaximumValue = aws.Float64(v) + } + if v, ok := tfMap["minimum_value"].(float64); ok { + control.MinimumValue = aws.Float64(v) + } + if v, ok := tfMap["step_size"].(float64); ok { + control.StepSize = aws.Float64(v) + } + if v, ok := tfMap["display_options"].([]interface{}); ok && len(v) > 0 { + control.DisplayOptions = expandSliderControlDisplayOptions(v) + } + + return control +} + +func expandSliderControlDisplayOptions(tfList []interface{}) *quicksight.SliderControlDisplayOptions { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + options := &quicksight.SliderControlDisplayOptions{} + + if v, ok := tfMap["title_options"].([]interface{}); ok && len(v) > 0 { + options.TitleOptions = expandLabelOptions(v) + } + + return options +} + +func expandFilterTextAreaControl(tfList []interface{}) *quicksight.FilterTextAreaControl { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + control := &quicksight.FilterTextAreaControl{} + + if v, ok := tfMap["filter_control_id"].(string); ok && v != "" { + control.FilterControlId = aws.String(v) + } + if v, ok := tfMap["source_filter_id"].(string); ok && v != "" { + control.SourceFilterId = aws.String(v) + } + if v, ok := tfMap["title"].(string); ok && v != "" { + control.Title = aws.String(v) + } + if v, ok := tfMap["delimiter"].(string); ok && v != "" { + control.Delimiter = aws.String(v) + } + if v, ok := tfMap["display_options"].([]interface{}); ok && len(v) > 0 { + control.DisplayOptions = expandTextAreaControlDisplayOptions(v) + } + + return control +} + +func expandTextAreaControlDisplayOptions(tfList []interface{}) *quicksight.TextAreaControlDisplayOptions { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + options := &quicksight.TextAreaControlDisplayOptions{} + + if v, ok := tfMap["placeholder_options"].([]interface{}); ok && len(v) > 0 { + options.PlaceholderOptions = expandTextControlPlaceholderOptions(v) + } + if v, ok := tfMap["title_options"].([]interface{}); ok && len(v) > 0 { + options.TitleOptions = expandLabelOptions(v) + } + + return options +} + +func expandTextControlPlaceholderOptions(tfList []interface{}) *quicksight.TextControlPlaceholderOptions { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + options := &quicksight.TextControlPlaceholderOptions{} + + if v, ok := tfMap["visibility"].(string); ok && v != "" { + options.Visibility = aws.String(v) + } + + return options +} + +func expandFilterTextFieldControl(tfList []interface{}) *quicksight.FilterTextFieldControl { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + control := &quicksight.FilterTextFieldControl{} + + if v, ok := tfMap["filter_control_id"].(string); ok && v != "" { + control.FilterControlId = aws.String(v) + } + if v, ok := tfMap["source_filter_id"].(string); ok && v != "" { + control.SourceFilterId = aws.String(v) + } + if v, ok := tfMap["title"].(string); ok && v != "" { + control.Title = aws.String(v) + } + if v, ok := tfMap["display_options"].([]interface{}); ok && len(v) > 0 { + control.DisplayOptions = expandTextFieldControlDisplayOptions(v) + } + + return control +} + +func expandTextFieldControlDisplayOptions(tfList []interface{}) *quicksight.TextFieldControlDisplayOptions { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + options := &quicksight.TextFieldControlDisplayOptions{} + + if v, ok := tfMap["placeholder_options"].([]interface{}); ok && len(v) > 0 { + options.PlaceholderOptions = expandTextControlPlaceholderOptions(v) + } + if v, ok := tfMap["title_options"].([]interface{}); ok && len(v) > 0 { + options.TitleOptions = expandLabelOptions(v) + } + + return options +} + +func expandParameterControl(tfMap map[string]interface{}) *quicksight.ParameterControl { + if tfMap == nil { + return nil + } + + control := &quicksight.ParameterControl{} + + if v, ok := tfMap["date_time_picker"].([]interface{}); ok && len(v) > 0 { + control.DateTimePicker = expandParameterDateTimePickerControl(v) + } + if v, ok := tfMap["dropdown"].([]interface{}); ok && len(v) > 0 { + control.Dropdown = expandParameterDropDownControl(v) + } + if v, ok := tfMap["list"].([]interface{}); ok && len(v) > 0 { + control.List = expandParameterListControl(v) + } + if v, ok := tfMap["slider"].([]interface{}); ok && len(v) > 0 { + control.Slider = expandParameterSliderControl(v) + } + if v, ok := tfMap["text_area"].([]interface{}); ok && len(v) > 0 { + control.TextArea = expandParameterTextAreaControl(v) + } + if v, ok := tfMap["text_field"].([]interface{}); ok && len(v) > 0 { + control.TextField = expandParameterTextFieldControl(v) + } + + return control +} + +func expandParameterDateTimePickerControl(tfList []interface{}) *quicksight.ParameterDateTimePickerControl { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + control := &quicksight.ParameterDateTimePickerControl{} + + if v, ok := tfMap["parameter_control_id"].(string); ok && v != "" { + control.ParameterControlId = aws.String(v) + } + if v, ok := tfMap["source_parameter_name"].(string); ok && v != "" { + control.SourceParameterName = aws.String(v) + } + if v, ok := tfMap["title"].(string); ok && v != "" { + control.Title = aws.String(v) + } + if v, ok := tfMap["display_options"].([]interface{}); ok && len(v) > 0 { + control.DisplayOptions = expandDateTimePickerControlDisplayOptions(v) + } + + return control +} + +func expandParameterDropDownControl(tfList []interface{}) *quicksight.ParameterDropDownControl { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + control := &quicksight.ParameterDropDownControl{} + + if v, ok := tfMap["parameter_control_id"].(string); ok && v != "" { + control.ParameterControlId = aws.String(v) + } + if v, ok := tfMap["source_parameter_name"].(string); ok && v != "" { + control.SourceParameterName = aws.String(v) + } + if v, ok := tfMap["title"].(string); ok && v != "" { + control.Title = aws.String(v) + } + if v, ok := tfMap["type"].(string); ok && v != "" { + control.Type = aws.String(v) + } + if v, ok := tfMap["cascading_control_configuration"].([]interface{}); ok && len(v) > 0 { + control.CascadingControlConfiguration = expandCascadingControlConfiguration(v) + } + if v, ok := tfMap["display_options"].([]interface{}); ok && len(v) > 0 { + control.DisplayOptions = expandDropDownControlDisplayOptions(v) + } + if v, ok := tfMap["selectable_values"].([]interface{}); ok && len(v) > 0 { + control.SelectableValues = expandParameterSelectableValues(v) + } + + return control +} + +func expandParameterListControl(tfList []interface{}) *quicksight.ParameterListControl { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + control := &quicksight.ParameterListControl{} + + if v, ok := tfMap["parameter_control_id"].(string); ok && v != "" { + control.ParameterControlId = aws.String(v) + } + if v, ok := tfMap["source_parameter_name"].(string); ok && v != "" { + control.SourceParameterName = aws.String(v) + } + if v, ok := tfMap["title"].(string); ok && v != "" { + control.Title = aws.String(v) + } + if v, ok := tfMap["type"].(string); ok && v != "" { + control.Type = aws.String(v) + } + if v, ok := tfMap["cascading_control_configuration"].([]interface{}); ok && len(v) > 0 { + control.CascadingControlConfiguration = expandCascadingControlConfiguration(v) + } + if v, ok := tfMap["display_options"].([]interface{}); ok && len(v) > 0 { + control.DisplayOptions = expandListControlDisplayOptions(v) + } + if v, ok := tfMap["selectable_values"].([]interface{}); ok && len(v) > 0 { + control.SelectableValues = expandParameterSelectableValues(v) + } + + return control +} + +func expandParameterSliderControl(tfList []interface{}) *quicksight.ParameterSliderControl { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + control := &quicksight.ParameterSliderControl{} + + if v, ok := tfMap["parameter_control_id"].(string); ok && v != "" { + control.ParameterControlId = aws.String(v) + } + if v, ok := tfMap["source_parameter_name"].(string); ok && v != "" { + control.SourceParameterName = aws.String(v) + } + if v, ok := tfMap["title"].(string); ok && v != "" { + control.Title = aws.String(v) + } + if v, ok := tfMap["maximum_value"].(float64); ok { + control.MaximumValue = aws.Float64(v) + } + if v, ok := tfMap["minimum_value"].(float64); ok { + control.MinimumValue = aws.Float64(v) + } + if v, ok := tfMap["step_size"].(float64); ok { + control.StepSize = aws.Float64(v) + } + if v, ok := tfMap["display_options"].([]interface{}); ok && len(v) > 0 { + control.DisplayOptions = expandSliderControlDisplayOptions(v) + } + + return control +} + +func expandParameterTextAreaControl(tfList []interface{}) *quicksight.ParameterTextAreaControl { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + control := &quicksight.ParameterTextAreaControl{} + + if v, ok := tfMap["parameter_control_id"].(string); ok && v != "" { + control.ParameterControlId = aws.String(v) + } + if v, ok := tfMap["source_parameter_name"].(string); ok && v != "" { + control.SourceParameterName = aws.String(v) + } + if v, ok := tfMap["title"].(string); ok && v != "" { + control.Title = aws.String(v) + } + if v, ok := tfMap["delimiter"].(string); ok && v != "" { + control.Delimiter = aws.String(v) + } + if v, ok := tfMap["display_options"].([]interface{}); ok && len(v) > 0 { + control.DisplayOptions = expandTextAreaControlDisplayOptions(v) + } + + return control +} + +func expandParameterTextFieldControl(tfList []interface{}) *quicksight.ParameterTextFieldControl { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + control := &quicksight.ParameterTextFieldControl{} + + if v, ok := tfMap["parameter_control_id"].(string); ok && v != "" { + control.ParameterControlId = aws.String(v) + } + if v, ok := tfMap["source_parameter_name"].(string); ok && v != "" { + control.SourceParameterName = aws.String(v) + } + if v, ok := tfMap["title"].(string); ok && v != "" { + control.Title = aws.String(v) + } + if v, ok := tfMap["display_options"].([]interface{}); ok && len(v) > 0 { + control.DisplayOptions = expandTextFieldControlDisplayOptions(v) + } + + return control +} diff --git a/internal/service/quicksight/template_filter.go b/internal/service/quicksight/template_filter.go new file mode 100644 index 000000000000..9ffcba0a7b1e --- /dev/null +++ b/internal/service/quicksight/template_filter.go @@ -0,0 +1,1298 @@ +package quicksight + +import ( + "fmt" + "regexp" + "time" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/quicksight" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" + "github.com/hashicorp/terraform-provider-aws/internal/flex" + "github.com/hashicorp/terraform-provider-aws/internal/verify" +) + +func filtersSchema() *schema.Schema { + return &schema.Schema{ + Type: schema.TypeList, + MinItems: 1, + MaxItems: 20, + Required: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "category_filter": categoryFilterSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_CategoryFilter.html + "numeric_equality_filter": numericEqualityFilterSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_NumericEqualityFilter.html + "numeric_range_filter": numericRangeFilterSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_NumericRangeFilter.html + "relative_dates_filter": relativeDatesFilterSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_RelativeDatesFilter.html + "time_equality_filter": timeEqualityFilterSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_TimeEqualityFilter.html + "time_range_filter": timeRangeFilterSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_TimeRangeFilter.html + "top_bottom_filter": topBottomFilterSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_TopBottomFilter.html + }, + }, + } +} + +func categoryFilterSchema() *schema.Schema { + return &schema.Schema{ // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_CategoryFilter.html + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "column": columnSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_ColumnIdentifier.html + "configuration": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_CategoryFilterConfiguration.html + Type: schema.TypeList, + Required: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "custom_filter_configuration": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_CustomFilterConfiguration.html + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "match_operator": stringSchema(true, validation.StringInSlice(quicksight.CategoryFilterMatchOperator_Values(), false)), + "null_option": stringSchema(true, validation.StringInSlice(quicksight.FilterNullOption_Values(), false)), + "category_value": { + Type: schema.TypeString, + Optional: true, + ValidateFunc: validation.StringLenBetween(1, 512), + }, + "parameter_name": { + Type: schema.TypeString, + Optional: true, + ValidateFunc: validation.All( + validation.StringLenBetween(1, 2048), + validation.StringMatch(regexp.MustCompile(`^[a-zA-Z0-9]+`), ""), + ), + }, + "select_all_options": stringSchema(false, validation.StringInSlice(quicksight.SelectAllValueOptions_Values(), false)), + }, + }, + }, + "custom_filter_list_configuration": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_CustomFilterListConfiguration.html + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "match_operator": stringSchema(true, validation.StringInSlice(quicksight.CategoryFilterMatchOperator_Values(), false)), + "null_option": stringSchema(true, validation.StringInSlice(quicksight.FilterNullOption_Values(), false)), + "category_values": { + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 100000, + Elem: &schema.Schema{ + Type: schema.TypeString, + ValidateFunc: validation.StringLenBetween(1, 512), + }, + }, + "select_all_options": stringSchema(false, validation.StringInSlice(quicksight.SelectAllValueOptions_Values(), false)), + }, + }, + }, + "filter_list_configuration": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_FilterListConfiguration.html + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "match_operator": stringSchema(true, validation.StringInSlice(quicksight.CategoryFilterMatchOperator_Values(), false)), + "category_values": { + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 100000, + Elem: &schema.Schema{ + Type: schema.TypeString, + ValidateFunc: validation.StringLenBetween(1, 512), + }, + }, + "select_all_options": stringSchema(false, validation.StringInSlice(quicksight.SelectAllValueOptions_Values(), false)), + }, + }, + }, + }, + }, + }, + "filter_id": idSchema(), + }, + }, + } +} + +func numericEqualityFilterSchema() *schema.Schema { + return &schema.Schema{ // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_NumericEqualityFilter.html + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "column": columnSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_ColumnIdentifier.html + "filter_id": idSchema(), + "match_operator": stringSchema(true, validation.StringInSlice(quicksight.CategoryFilterMatchOperator_Values(), false)), + "null_option": stringSchema(true, validation.StringInSlice(quicksight.FilterNullOption_Values(), false)), + "aggregation_function": aggregationFunctionSchema(false), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_AggregationFunction.html + "parameter_name": parameterNameSchema(false), + "select_all_options": stringSchema(false, validation.StringInSlice(quicksight.SelectAllValueOptions_Values(), false)), + "value": { + Type: schema.TypeFloat, + Optional: true, + }, + }, + }, + } +} + +func numericRangeFilterSchema() *schema.Schema { + return &schema.Schema{ // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_NumericRangeFilter.html + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "column": columnSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_ColumnIdentifier.html + "filter_id": idSchema(), + "null_option": stringSchema(true, validation.StringInSlice(quicksight.FilterNullOption_Values(), false)), + "aggregation_function": aggregationFunctionSchema(false), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_AggregationFunction.html + "include_maximum": { + Type: schema.TypeBool, + Optional: true, + }, + "include_minimum": { + Type: schema.TypeBool, + Optional: true, + }, + "range_maximum": numericRangeFilterValueSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_NumericRangeFilterValue.html + "range_minimum": numericRangeFilterValueSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_NumericRangeFilterValue.html + "select_all_options": stringSchema(false, validation.StringInSlice(quicksight.SelectAllValueOptions_Values(), false)), + }, + }, + } +} + +func relativeDatesFilterSchema() *schema.Schema { + return &schema.Schema{ // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_RelativeDatesFilter.html + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "anchor_date_configuration": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_AnchorDateConfiguration.html + Type: schema.TypeList, + Required: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "anchor_option": stringSchema(false, validation.StringInSlice(quicksight.AnchorOption_Values(), false)), + "parameter_name": parameterNameSchema(false), + }, + }, + }, + "column": columnSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_ColumnIdentifier.html + "filter_id": idSchema(), + "null_option": stringSchema(true, validation.StringInSlice(quicksight.FilterNullOption_Values(), false)), + "relative_date_type": stringSchema(true, validation.StringInSlice(quicksight.RelativeDateType_Values(), false)), + "time_granularity": stringSchema(true, validation.StringInSlice(quicksight.TimeGranularity_Values(), false)), + "exclude_period_configuration": excludePeriodConfigurationSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_ExcludePeriodConfiguration.html + "minimum_granularity": stringSchema(true, validation.StringInSlice(quicksight.TimeGranularity_Values(), false)), + "parameter_name": parameterNameSchema(false), + "relative_date_value": { + Type: schema.TypeInt, + Optional: true, + }, + }, + }, + } +} + +func timeEqualityFilterSchema() *schema.Schema { + return &schema.Schema{ // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_TimeEqualityFilter.html + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "column": columnSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_ColumnIdentifier.html + "filter_id": idSchema(), + "time_granularity": stringSchema(true, validation.StringInSlice(quicksight.TimeGranularity_Values(), false)), + "parameter_name": parameterNameSchema(false), + "value": { + Type: schema.TypeString, + Optional: true, + ValidateFunc: verify.ValidUTCTimestamp, + }, + }, + }, + } +} + +func timeRangeFilterSchema() *schema.Schema { + return &schema.Schema{ // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_TimeRangeFilter.html + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "column": columnSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_ColumnIdentifier.html + "filter_id": idSchema(), + "null_option": stringSchema(true, validation.StringInSlice(quicksight.FilterNullOption_Values(), false)), + "exclude_period_configuration": excludePeriodConfigurationSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_ExcludePeriodConfiguration.html + "include_maximum": { + Type: schema.TypeBool, + Optional: true, + }, + "include_minimum": { + Type: schema.TypeBool, + Optional: true, + }, + "range_maximum_value": timeRangeFilterValueSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_TimeRangeFilterValue.html + "range_minimum_value": timeRangeFilterValueSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_TimeRangeFilterValue.html + "time_granularity": stringSchema(true, validation.StringInSlice(quicksight.TimeGranularity_Values(), false)), + }, + }, + } +} + +func topBottomFilterSchema() *schema.Schema { + return &schema.Schema{ // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_TopBottomFilter.html + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "aggregation_sort_configuration": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_AnchorDateConfiguration.html + Type: schema.TypeList, + Required: true, + MinItems: 1, + MaxItems: 100, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "aggregation_function": aggregationFunctionSchema(true), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_AggregationFunction.html + "column": columnSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_ColumnIdentifier.html + "sort_direction": stringSchema(true, validation.StringInSlice(quicksight.SortDirection_Values(), false)), + }, + }, + }, + "column": columnSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_ColumnIdentifier.html + "filter_id": idSchema(), + "limit": { + Type: schema.TypeInt, + Optional: true, + }, + "parameter_name": parameterNameSchema(false), + "time_granularity": stringSchema(true, validation.StringInSlice(quicksight.TimeGranularity_Values(), false)), + }, + }, + } +} + +func excludePeriodConfigurationSchema() *schema.Schema { + return &schema.Schema{ // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_ExcludePeriodConfiguration.html + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "amount": { + Type: schema.TypeInt, + Required: true, + }, + "granularity": stringSchema(true, validation.StringInSlice(quicksight.TimeGranularity_Values(), false)), + "status": stringSchema(false, validation.StringInSlice(quicksight.Status_Values(), false)), + }, + }, + } +} + +func numericRangeFilterValueSchema() *schema.Schema { + return &schema.Schema{ // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_NumericRangeFilterValue.html + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "parameter": { + Type: schema.TypeString, + Optional: true, + ValidateFunc: validation.All( + validation.StringLenBetween(1, 2048), + validation.StringMatch(regexp.MustCompile(`^[a-zA-Z0-9]+$`), ""), + ), + }, + "static_value": { + Type: schema.TypeFloat, + Optional: true, + }, + }, + }, + } +} + +func timeRangeFilterValueSchema() *schema.Schema { + return &schema.Schema{ // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_TimeRangeFilterValue.html + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "parameter": { + Type: schema.TypeString, + Optional: true, + ValidateFunc: validation.All( + validation.StringLenBetween(1, 2048), + validation.StringMatch(regexp.MustCompile(`^[a-zA-Z0-9]+$`), ""), + ), + }, + "rolling_date": rollingDateConfigurationSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_RollingDateConfiguration.html, + "static_value": stringSchema(false, verify.ValidUTCTimestamp), + }, + }, + } +} + +func drillDownFilterSchema() *schema.Schema { + return &schema.Schema{ // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_DrillDownFilter.html + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 10, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "category_filter": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_CategoryDrillDownFilter.html + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "category_values": { + Type: schema.TypeList, + Required: true, + MinItems: 1, + MaxItems: 100000, + Elem: &schema.Schema{ + Type: schema.TypeString, + ValidateFunc: validation.StringLenBetween(1, 512), + }, + }, + "column": columnSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_ColumnIdentifier.html + }, + }, + }, + "numeric_equality_filter": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_NumericEqualityDrillDownFilter.html + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "column": columnSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_ColumnIdentifier.html + "value": { + Type: schema.TypeFloat, + Required: true, + }, + }, + }, + }, + "time_range_filter": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_TimeRangeDrillDownFilter.html + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "column": columnSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_ColumnIdentifier.html + "range_maximum": stringSchema(true, verify.ValidUTCTimestamp), + "range_minimum": stringSchema(true, verify.ValidUTCTimestamp), + "time_granularity": stringSchema(true, validation.StringInSlice(quicksight.TimeGranularity_Values(), false)), + }, + }, + }, + }, + }, + } +} + +func filterSelectableValuesSchema() *schema.Schema { + return &schema.Schema{ // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_FilterSelectableValues.html + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "values": { + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 50000, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + }, + }, + }, + } +} + +func filterScopeConfigurationSchema() *schema.Schema { + return &schema.Schema{ // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_FilterScopeConfiguration.html + Type: schema.TypeList, + MinItems: 1, + MaxItems: 1, + Required: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "selected_sheets": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_SelectedSheetsFilterScopeConfiguration.html + Type: schema.TypeList, + MinItems: 1, + MaxItems: 1, + Optional: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "sheet_visual_scoping_configurations": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_SheetVisualScopingConfiguration.html + Type: schema.TypeList, + MinItems: 1, + MaxItems: 50, + Optional: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "scope": stringSchema(true, validation.StringInSlice(quicksight.FilterVisualScope_Values(), false)), + "sheet_id": idSchema(), + "visual_ids": { + Type: schema.TypeSet, + Optional: true, + MinItems: 1, + MaxItems: 50, + Elem: schema.TypeString, + }, + }, + }, + }, + }, + }, + }, + }, + }, + } +} + +func expandFilters(tfList []interface{}) ([]*quicksight.Filter, error) { + if len(tfList) == 0 { + return nil, nil + } + + var filters []*quicksight.Filter + for _, tfMapRaw := range tfList { + tfMap, ok := tfMapRaw.(map[string]interface{}) + if !ok { + continue + } + + filter, err := expandFilter(tfMap) + if err != nil { + return nil, err + } + if filter == nil { + continue + } + + filters = append(filters, filter) + } + + return filters, nil +} + +func expandFilter(tfMap map[string]interface{}) (*quicksight.Filter, error) { + if tfMap == nil { + return nil, nil + } + + filter := &quicksight.Filter{} + + if v, ok := tfMap["category_filter"].([]interface{}); ok && len(v) > 0 { + filter.CategoryFilter = expandCategoryFilter(v) + } + if v, ok := tfMap["numeric_equality_filter"].([]interface{}); ok && len(v) > 0 { + filter.NumericEqualityFilter = expandNumericEqualityFilter(v) + } + if v, ok := tfMap["numeric_range_filter"].([]interface{}); ok && len(v) > 0 { + filter.NumericRangeFilter = expandNumericRangeFilter(v) + } + if v, ok := tfMap["relative_dates_filter"].([]interface{}); ok && len(v) > 0 { + filter.RelativeDatesFilter = expandRelativeDatesFilter(v) + } + if v, ok := tfMap["time_equality_filter"].([]interface{}); ok && len(v) > 0 { + value, err := expandTimeEqualityFilter(v) + if err != nil { + return nil, err + } + filter.TimeEqualityFilter = value + } + if v, ok := tfMap["time_range_filter"].([]interface{}); ok && len(v) > 0 { + value, err := expandTimeRangeFilter(v) + if err != nil { + return nil, err + } + filter.TimeRangeFilter = value + } + if v, ok := tfMap["top_bottom_filter"].([]interface{}); ok && len(v) > 0 { + filter.TopBottomFilter = expandTopBottomFilter(v) + } + + return filter, nil +} + +func expandCategoryFilter(tfList []interface{}) *quicksight.CategoryFilter { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + filter := &quicksight.CategoryFilter{} + + if v, ok := tfMap["filter_id"].(string); ok && v != "" { + filter.FilterId = aws.String(v) + } + if v, ok := tfMap["column"].([]interface{}); ok && len(v) > 0 { + filter.Column = expandColumnIdentifier(v) + } + if v, ok := tfMap["configuration"].([]interface{}); ok && len(v) > 0 { + filter.Configuration = expandCategoryFilterConfiguration(v) + } + + return filter +} + +func expandCategoryFilterConfiguration(tfList []interface{}) *quicksight.CategoryFilterConfiguration { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + config := &quicksight.CategoryFilterConfiguration{} + + if v, ok := tfMap["custom_filter_configuration"].([]interface{}); ok && len(v) > 0 { + config.CustomFilterConfiguration = expandCustomFilterConfiguration(v) + } + if v, ok := tfMap["custom_filter_list_configuration"].([]interface{}); ok && len(v) > 0 { + config.CustomFilterListConfiguration = expandCustomFilterListConfiguration(v) + } + if v, ok := tfMap["filter_list_configuration"].([]interface{}); ok && len(v) > 0 { + config.FilterListConfiguration = expandFilterListConfiguration(v) + } + + return config +} + +func expandCustomFilterConfiguration(tfList []interface{}) *quicksight.CustomFilterConfiguration { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + config := &quicksight.CustomFilterConfiguration{} + + if v, ok := tfMap["category_value"].(string); ok && v != "" { + config.CategoryValue = aws.String(v) + } + if v, ok := tfMap["match_operator"].(string); ok && v != "" { + config.MatchOperator = aws.String(v) + } + if v, ok := tfMap["null_option"].(string); ok && v != "" { + config.NullOption = aws.String(v) + } + if v, ok := tfMap["parameter_name"].(string); ok && v != "" { + config.ParameterName = aws.String(v) + } + if v, ok := tfMap["select_all_options"].(string); ok && v != "" { + config.SelectAllOptions = aws.String(v) + } + + return config +} + +func expandCustomFilterListConfiguration(tfList []interface{}) *quicksight.CustomFilterListConfiguration { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + config := &quicksight.CustomFilterListConfiguration{} + + if v, ok := tfMap["category_values"].([]interface{}); ok { + config.CategoryValues = flex.ExpandStringList(v) + } + if v, ok := tfMap["match_operator"].(string); ok && v != "" { + config.MatchOperator = aws.String(v) + } + if v, ok := tfMap["null_option"].(string); ok && v != "" { + config.NullOption = aws.String(v) + } + if v, ok := tfMap["select_all_options"].(string); ok && v != "" { + config.SelectAllOptions = aws.String(v) + } + + return config +} + +func expandFilterListConfiguration(tfList []interface{}) *quicksight.FilterListConfiguration { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + config := &quicksight.FilterListConfiguration{} + + if v, ok := tfMap["category_values"].([]interface{}); ok { + config.CategoryValues = flex.ExpandStringList(v) + } + if v, ok := tfMap["match_operator"].(string); ok && v != "" { + config.MatchOperator = aws.String(v) + } + if v, ok := tfMap["select_all_options"].(string); ok && v != "" { + config.SelectAllOptions = aws.String(v) + } + + return config +} + +func expandNumericEqualityFilter(tfList []interface{}) *quicksight.NumericEqualityFilter { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + filter := &quicksight.NumericEqualityFilter{} + + if v, ok := tfMap["filter_id"].(string); ok && v != "" { + filter.FilterId = aws.String(v) + } + if v, ok := tfMap["column"].([]interface{}); ok && len(v) > 0 { + filter.Column = expandColumnIdentifier(v) + } + if v, ok := tfMap["match_operator"].(string); ok && v != "" { + filter.MatchOperator = aws.String(v) + } + if v, ok := tfMap["null_option"].(string); ok && v != "" { + filter.NullOption = aws.String(v) + } + if v, ok := tfMap["parameter_name"].(string); ok && v != "" { + filter.ParameterName = aws.String(v) + } + if v, ok := tfMap["select_all_options"].(string); ok && v != "" { + filter.SelectAllOptions = aws.String(v) + } + if v, ok := tfMap["value"].(float64); ok { + filter.Value = aws.Float64(v) + } + if v, ok := tfMap["aggregation_function"].([]interface{}); ok && len(v) > 0 { + filter.AggregationFunction = expandAggregationFunction(v) + } + + return filter +} + +func expandFilterScopeConfiguration(tfList []interface{}) *quicksight.FilterScopeConfiguration { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + config := &quicksight.FilterScopeConfiguration{} + + if v, ok := tfMap["selected_sheets"].([]interface{}); ok && len(v) > 0 { + config.SelectedSheets = expandSelectedSheetsFilterScopeConfiguration(v) + } + + return config +} + +func expandSelectedSheetsFilterScopeConfiguration(tfList []interface{}) *quicksight.SelectedSheetsFilterScopeConfiguration { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + config := &quicksight.SelectedSheetsFilterScopeConfiguration{} + + if v, ok := tfMap["sheet_visual_scoping_configurations"].([]interface{}); ok && len(v) > 0 { + config.SheetVisualScopingConfigurations = expandSheetVisualScopingConfigurations(v) + } + + return config +} + +func expandSheetVisualScopingConfigurations(tfList []interface{}) []*quicksight.SheetVisualScopingConfiguration { + if len(tfList) == 0 { + return nil + } + + var configs []*quicksight.SheetVisualScopingConfiguration + for _, tfMapRaw := range tfList { + tfMap, ok := tfMapRaw.(map[string]interface{}) + if !ok { + continue + } + + config := expandSheetVisualScopingConfiguration(tfMap) + if config == nil { + continue + } + + configs = append(configs, config) + } + + return configs +} + +func expandSheetVisualScopingConfiguration(tfMap map[string]interface{}) *quicksight.SheetVisualScopingConfiguration { + if tfMap == nil { + return nil + } + + config := &quicksight.SheetVisualScopingConfiguration{} + + if v, ok := tfMap["scope"].(string); ok && v != "" { + config.Scope = aws.String(v) + } + if v, ok := tfMap["sheet_id"].(string); ok && v != "" { + config.SheetId = aws.String(v) + } + if v, ok := tfMap["visual_ids"].(*schema.Set); ok { + config.VisualIds = flex.ExpandStringSet(v) + } + + return config +} + +func expandNumericRangeFilter(tfList []interface{}) *quicksight.NumericRangeFilter { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + filter := &quicksight.NumericRangeFilter{} + + if v, ok := tfMap["filter_id"].(string); ok && v != "" { + filter.FilterId = aws.String(v) + } + if v, ok := tfMap["column"].([]interface{}); ok && len(v) > 0 { + filter.Column = expandColumnIdentifier(v) + } + if v, ok := tfMap["null_option"].(string); ok && v != "" { + filter.NullOption = aws.String(v) + } + if v, ok := tfMap["select_all_options"].(string); ok && v != "" { + filter.SelectAllOptions = aws.String(v) + } + if v, ok := tfMap["aggregation_function"].([]interface{}); ok && len(v) > 0 { + filter.AggregationFunction = expandAggregationFunction(v) + } + if v, ok := tfMap["include_maximum"].(bool); ok { + filter.IncludeMaximum = aws.Bool(v) + } + if v, ok := tfMap["include_minimum"].(bool); ok { + filter.IncludeMinimum = aws.Bool(v) + } + if v, ok := tfMap["range_maximum"].([]interface{}); ok && len(v) > 0 { + filter.RangeMaximum = expandNumericRangeFilterValue(v) + } + if v, ok := tfMap["range_minimum"].([]interface{}); ok && len(v) > 0 { + filter.RangeMinimum = expandNumericRangeFilterValue(v) + } + + return filter +} + +func expandNumericRangeFilterValue(tfList []interface{}) *quicksight.NumericRangeFilterValue { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + filter := &quicksight.NumericRangeFilterValue{} + + if v, ok := tfMap["parameter"].(string); ok && v != "" { + filter.Parameter = aws.String(v) + } + if v, ok := tfMap["static_value"].(float64); ok { + filter.StaticValue = aws.Float64(v) + } + + return filter +} + +func expandRelativeDatesFilter(tfList []interface{}) *quicksight.RelativeDatesFilter { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + filter := &quicksight.RelativeDatesFilter{} + + if v, ok := tfMap["filter_id"].(string); ok && v != "" { + filter.FilterId = aws.String(v) + } + if v, ok := tfMap["column"].([]interface{}); ok && len(v) > 0 { + filter.Column = expandColumnIdentifier(v) + } + if v, ok := tfMap["null_option"].(string); ok && v != "" { + filter.NullOption = aws.String(v) + } + if v, ok := tfMap["relative_date_type"].(string); ok && v != "" { + filter.RelativeDateType = aws.String(v) + } + if v, ok := tfMap["time_granularity"].(string); ok && v != "" { + filter.TimeGranularity = aws.String(v) + } + if v, ok := tfMap["minimum_granularity"].(string); ok && v != "" { + filter.MinimumGranularity = aws.String(v) + } + if v, ok := tfMap["parameter_name"].(string); ok && v != "" { + filter.ParameterName = aws.String(v) + } + if v, ok := tfMap["relative_date_value"].(int64); ok { + filter.RelativeDateValue = aws.Int64(v) + } + if v, ok := tfMap["anchor_date_configuration"].([]interface{}); ok && len(v) > 0 { + filter.AnchorDateConfiguration = expandAnchorDateConfiguration(v) + } + if v, ok := tfMap["exclude_period_configuration"].([]interface{}); ok && len(v) > 0 { + filter.ExcludePeriodConfiguration = expandExcludePeriodConfiguration(v) + } + + return filter +} + +func expandAnchorDateConfiguration(tfList []interface{}) *quicksight.AnchorDateConfiguration { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + config := &quicksight.AnchorDateConfiguration{} + + if v, ok := tfMap["anchor_option"].(string); ok && v != "" { + config.AnchorOption = aws.String(v) + } + if v, ok := tfMap["parameter_name"].(string); ok && v != "" { + config.ParameterName = aws.String(v) + } + + return config +} + +func expandExcludePeriodConfiguration(tfList []interface{}) *quicksight.ExcludePeriodConfiguration { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + config := &quicksight.ExcludePeriodConfiguration{} + + if v, ok := tfMap["amount"].(int64); ok { + config.Amount = aws.Int64(v) + } + if v, ok := tfMap["granularity"].(string); ok && v != "" { + config.Granularity = aws.String(v) + } + if v, ok := tfMap["status"].(string); ok && v != "" { + config.Status = aws.String(v) + } + + return config +} + +func expandTimeEqualityFilter(tfList []interface{}) (*quicksight.TimeEqualityFilter, error) { + if len(tfList) == 0 || tfList[0] == nil { + return nil, nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil, nil + } + + filter := &quicksight.TimeEqualityFilter{} + + if v, ok := tfMap["filter_id"].(string); ok && v != "" { + filter.FilterId = aws.String(v) + } + if v, ok := tfMap["column"].([]interface{}); ok && len(v) > 0 { + filter.Column = expandColumnIdentifier(v) + } + if v, ok := tfMap["time_granularity"].(string); ok && v != "" { + filter.TimeGranularity = aws.String(v) + } + if v, ok := tfMap["parameter_name"].(string); ok && v != "" { + filter.ParameterName = aws.String(v) + } + if v, ok := tfMap["value"].(string); ok && v != "" { + t, err := time.Parse(time.RFC3339, v) + if err != nil { + return nil, fmt.Errorf("error parsing TimeEqualityFilter value date: %w", err) + } + filter.Value = aws.Time(t) + } + + return filter, nil +} + +func expandTimeRangeFilter(tfList []interface{}) (*quicksight.TimeRangeFilter, error) { + if len(tfList) == 0 || tfList[0] == nil { + return nil, nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil, nil + } + + filter := &quicksight.TimeRangeFilter{} + + if v, ok := tfMap["filter_id"].(string); ok && v != "" { + filter.FilterId = aws.String(v) + } + if v, ok := tfMap["column"].([]interface{}); ok && len(v) > 0 { + filter.Column = expandColumnIdentifier(v) + } + if v, ok := tfMap["null_option"].(string); ok && v != "" { + filter.NullOption = aws.String(v) + } + if v, ok := tfMap["time_granularity"].(string); ok && v != "" { + filter.TimeGranularity = aws.String(v) + } + if v, ok := tfMap["exclude_period_configuration"].([]interface{}); ok && len(v) > 0 { + filter.ExcludePeriodConfiguration = expandExcludePeriodConfiguration(v) + } + if v, ok := tfMap["include_maximum"].(bool); ok { + filter.IncludeMaximum = aws.Bool(v) + } + if v, ok := tfMap["include_minimum"].(bool); ok { + filter.IncludeMinimum = aws.Bool(v) + } + if v, ok := tfMap["range_maximum_value"].([]interface{}); ok && len(v) > 0 { + value, err := expandTimeRangeFilterValue(v) + if err != nil { + return nil, err + } + filter.RangeMaximumValue = value + } + if v, ok := tfMap["range_minimum_value"].([]interface{}); ok && len(v) > 0 { + value, err := expandTimeRangeFilterValue(v) + if err != nil { + return nil, err + } + filter.RangeMinimumValue = value + } + + return filter, nil +} + +func expandTimeRangeFilterValue(tfList []interface{}) (*quicksight.TimeRangeFilterValue, error) { + if len(tfList) == 0 || tfList[0] == nil { + return nil, nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil, nil + } + + filter := &quicksight.TimeRangeFilterValue{} + + if v, ok := tfMap["parameter"].(string); ok && v != "" { + filter.Parameter = aws.String(v) + } + if v, ok := tfMap["static_value"].(string); ok && v != "" { + t, err := time.Parse(time.RFC3339, v) + if err != nil { + return nil, fmt.Errorf("error parsing TimeRangeFilterValue static_value date: %w", err) + } + filter.StaticValue = aws.Time(t) + } + if v, ok := tfMap["range_minimum_value"].([]interface{}); ok && len(v) > 0 { + filter.RollingDate = expandRollingDateConfiguration(v) + } + + return filter, nil +} + +func expandTopBottomFilter(tfList []interface{}) *quicksight.TopBottomFilter { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + filter := &quicksight.TopBottomFilter{} + + if v, ok := tfMap["filter_id"].(string); ok && v != "" { + filter.FilterId = aws.String(v) + } + if v, ok := tfMap["column"].([]interface{}); ok && len(v) > 0 { + filter.Column = expandColumnIdentifier(v) + } + if v, ok := tfMap["limit"].(int64); ok { + filter.Limit = aws.Int64(v) + } + if v, ok := tfMap["parameter_name"].(string); ok && v != "" { + filter.ParameterName = aws.String(v) + } + if v, ok := tfMap["time_granularity"].(string); ok && v != "" { + filter.TimeGranularity = aws.String(v) + } + if v, ok := tfMap["aggregation_sort_configuration"].([]interface{}); ok && len(v) > 0 { + filter.AggregationSortConfigurations = expandAggregationSortConfigurations(v) + } + + return filter +} + +func expandAggregationSortConfigurations(tfList []interface{}) []*quicksight.AggregationSortConfiguration { + if len(tfList) == 0 { + return nil + } + + var configs []*quicksight.AggregationSortConfiguration + for _, tfMapRaw := range tfList { + tfMap, ok := tfMapRaw.(map[string]interface{}) + if !ok { + continue + } + + config := expandAggregationSortConfiguration(tfMap) + if config == nil { + continue + } + + configs = append(configs, config) + } + + return configs +} + +func expandAggregationSortConfiguration(tfMap map[string]interface{}) *quicksight.AggregationSortConfiguration { + if tfMap == nil { + return nil + } + + config := &quicksight.AggregationSortConfiguration{} + + if v, ok := tfMap["sort_direction"].(string); ok && v != "" { + config.SortDirection = aws.String(v) + } + if v, ok := tfMap["aggregation_function"].([]interface{}); ok && len(v) > 0 { + config.AggregationFunction = expandAggregationFunction(v) + } + if v, ok := tfMap["column"].([]interface{}); ok && len(v) > 0 { + config.Column = expandColumnIdentifier(v) + } + + return config +} + +func expandDrillDownFilters(tfList []interface{}) ([]*quicksight.DrillDownFilter, error) { + if len(tfList) == 0 { + return nil, nil + } + + var options []*quicksight.DrillDownFilter + for _, tfMapRaw := range tfList { + tfMap, ok := tfMapRaw.(map[string]interface{}) + if !ok { + continue + } + + opts, err := expandDrillDownFilter(tfMap) + if err != nil { + return nil, err + } + if opts == nil { + continue + } + + options = append(options, opts) + } + + return options, nil +} + +func expandDrillDownFilter(tfMap map[string]interface{}) (*quicksight.DrillDownFilter, error) { + if tfMap == nil { + return nil, nil + } + + options := &quicksight.DrillDownFilter{} + + if v, ok := tfMap["category_filter"].([]interface{}); ok && len(v) > 0 { + options.CategoryFilter = expandCategoryDrillDownFilter(v) + } + if v, ok := tfMap["numeric_equality_filter"].([]interface{}); ok && len(v) > 0 { + options.NumericEqualityFilter = expandNumericEqualityDrillDownFilter(v) + } + if v, ok := tfMap["time_range_filter"].([]interface{}); ok && len(v) > 0 { + value, err := expandTimeRangeDrillDownFilter(v) + if err != nil { + return nil, err + } + options.TimeRangeFilter = value + } + + return options, nil +} + +func expandCategoryDrillDownFilter(tfList []interface{}) *quicksight.CategoryDrillDownFilter { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + filter := &quicksight.CategoryDrillDownFilter{} + + if v, ok := tfMap["column"].([]interface{}); ok && len(v) > 0 { + filter.Column = expandColumnIdentifier(v) + } + if v, ok := tfMap["category_values"].([]interface{}); ok && len(v) > 0 { + filter.CategoryValues = flex.ExpandStringList(v) + } + + return filter +} + +func expandNumericEqualityDrillDownFilter(tfList []interface{}) *quicksight.NumericEqualityDrillDownFilter { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + filter := &quicksight.NumericEqualityDrillDownFilter{} + + if v, ok := tfMap["column"].([]interface{}); ok && len(v) > 0 { + filter.Column = expandColumnIdentifier(v) + } + if v, ok := tfMap["value"].(float64); ok { + filter.Value = aws.Float64(v) + } + + return filter +} + +func expandTimeRangeDrillDownFilter(tfList []interface{}) (*quicksight.TimeRangeDrillDownFilter, error) { + if len(tfList) == 0 || tfList[0] == nil { + return nil, nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil, nil + } + + filter := &quicksight.TimeRangeDrillDownFilter{} + + if v, ok := tfMap["range_maximum"].(string); ok && v != "" { + t, err := time.Parse(time.RFC3339, v) + if err != nil { + return nil, fmt.Errorf("error parsing TimeRangeFilterValue static_value date: %w", err) + } + filter.RangeMaximum = aws.Time(t) + } + if v, ok := tfMap["range_minimum"].(string); ok && v != "" { + t, err := time.Parse(time.RFC3339, v) + if err != nil { + return nil, fmt.Errorf("error parsing TimeRangeFilterValue static_value date: %w", err) + } + filter.RangeMinimum = aws.Time(t) + } + if v, ok := tfMap["time_granularity"].(string); ok && v != "" { + filter.TimeGranularity = aws.String(v) + } + if v, ok := tfMap["column"].([]interface{}); ok && len(v) > 0 { + filter.Column = expandColumnIdentifier(v) + } + + return filter, nil +} diff --git a/internal/service/quicksight/template_format.go b/internal/service/quicksight/template_format.go new file mode 100644 index 000000000000..027ca46f241a --- /dev/null +++ b/internal/service/quicksight/template_format.go @@ -0,0 +1,710 @@ +package quicksight + +import ( + "regexp" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/quicksight" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" +) + +func numericFormatConfigurationSchema() *schema.Schema { + return &schema.Schema{ + Type: schema.TypeList, + MinItems: 1, + MaxItems: 1, + Optional: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "currency_display_format_configuration": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_CurrencyDisplayFormatConfiguration.html + Type: schema.TypeList, + MinItems: 1, + MaxItems: 1, + Optional: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "decimal_places_configuration": decimalPlacesConfigurationSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_DecimalPlacesConfiguration.html + "negative_value_configuration": negativeValueConfigurationSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_NegativeValueConfiguration.html + "null_value_format_configuration": nullValueConfigurationSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_NullValueFormatConfiguration.html + "number_scale": stringSchema(false, validation.StringInSlice(quicksight.NumberScale_Values(), false)), + "prefix": stringSchema(false, validation.StringLenBetween(1, 128)), + "separator_configuration": separatorConfigurationSchema(), + "suffix": stringSchema(false, validation.StringLenBetween(1, 128)), + "symbol": stringSchema(false, validation.StringMatch(regexp.MustCompile(`[A-Z]{3}`), "must be a 3 character currency symbol")), + }, + }, + }, + "number_display_format_configuration": numberDisplayFormatConfigurationSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_NumberDisplayFormatConfiguration.html + "percentage_display_format_configuration": percentageDisplayFormatConfigurationSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_PercentageDisplayFormatConfiguration.html + }, + }, + } +} + +func dateTimeFormatConfigurationSchema() *schema.Schema { + return &schema.Schema{ + Type: schema.TypeList, + MinItems: 1, + MaxItems: 1, + Optional: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "date_time_format": stringSchema(false, validation.StringLenBetween(1, 128)), + "null_value_format_configuration": nullValueConfigurationSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_NullValueFormatConfiguration.html + "numeric_format_configuration": numericFormatConfigurationSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_NumericFormatConfiguration.html + }, + }, + } +} + +func numberDisplayFormatConfigurationSchema() *schema.Schema { + return &schema.Schema{ // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_NumberDisplayFormatConfiguration.html + Type: schema.TypeList, + MinItems: 1, + MaxItems: 1, + Optional: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "decimal_places_configuration": decimalPlacesConfigurationSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_DecimalPlacesConfiguration.html + "negative_value_configuration": negativeValueConfigurationSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_NegativeValueConfiguration.html + "null_value_format_configuration": nullValueConfigurationSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_NullValueFormatConfiguration.html + "number_scale": stringSchema(false, validation.StringInSlice(quicksight.NumberScale_Values(), false)), + "prefix": stringSchema(false, validation.StringLenBetween(1, 128)), + "separator_configuration": separatorConfigurationSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_NumericSeparatorConfiguration.html + "suffix": stringSchema(false, validation.StringLenBetween(1, 128)), + }, + }, + } +} + +func percentageDisplayFormatConfigurationSchema() *schema.Schema { + return &schema.Schema{ // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_PercentageDisplayFormatConfiguration.html + Type: schema.TypeList, + MinItems: 1, + MaxItems: 1, + Optional: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "decimal_places_configuration": decimalPlacesConfigurationSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_DecimalPlacesConfiguration.html + "negative_value_configuration": negativeValueConfigurationSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_NegativeValueConfiguration.html + "null_value_format_configuration": nullValueConfigurationSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_NullValueFormatConfiguration.html + "prefix": stringSchema(false, validation.StringLenBetween(1, 128)), + "separator_configuration": separatorConfigurationSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_NumericSeparatorConfiguration.html + "suffix": stringSchema(false, validation.StringLenBetween(1, 128)), + }, + }, + } +} + +func numberFormatConfigurationSchema() *schema.Schema { + return &schema.Schema{ // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_NumberFormatConfiguration.html + Type: schema.TypeList, + MinItems: 1, + MaxItems: 1, + Optional: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "numeric_format_configuration": numericFormatConfigurationSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_NumericFormatConfiguration.html + }, + }, + } +} + +func stringFormatConfigurationSchema() *schema.Schema { + return &schema.Schema{ + Type: schema.TypeList, + MinItems: 1, + MaxItems: 1, + Optional: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "null_value_format_configuration": nullValueConfigurationSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_NullValueFormatConfiguration.html + "numeric_format_configuration": numericFormatConfigurationSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_NumericFormatConfiguration.html + }, + }, + } +} + +func decimalPlacesConfigurationSchema() *schema.Schema { + return &schema.Schema{ // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_DecimalPlacesConfiguration.html + Type: schema.TypeList, + MinItems: 1, + MaxItems: 1, + Optional: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "decimal_places": { + Type: schema.TypeInt, + Required: true, + ValidateFunc: validation.IntBetween(0, 20), + }, + }, + }, + } +} + +func negativeValueConfigurationSchema() *schema.Schema { + return &schema.Schema{ + Type: schema.TypeList, + MinItems: 1, + MaxItems: 1, + Optional: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "display_mode": stringSchema(true, validation.StringInSlice(quicksight.NegativeValueDisplayMode_Values(), false)), + }, + }, + } +} + +func nullValueConfigurationSchema() *schema.Schema { + return &schema.Schema{ + Type: schema.TypeList, + MinItems: 1, + MaxItems: 1, + Optional: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "null_string": stringSchema(true, validation.StringLenBetween(1, 128)), + }, + }, + } +} + +func separatorConfigurationSchema() *schema.Schema { + return &schema.Schema{ + Type: schema.TypeList, + MinItems: 1, + MaxItems: 1, + Optional: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "decimal_separator": stringSchema(false, validation.StringInSlice(quicksight.NumericSeparatorSymbol_Values(), false)), + "thousands_separator": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_ThousandSeparatorOptions.html + Type: schema.TypeList, + MinItems: 1, + MaxItems: 1, + Optional: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "symbol": stringSchema(false, validation.StringInSlice(quicksight.NumericSeparatorSymbol_Values(), false)), + "visibility": stringSchema(false, validation.StringInSlice(quicksight.Visibility_Values(), false)), + }, + }, + }, + }, + }, + } +} + +func labelOptionsSchema() *schema.Schema { + return &schema.Schema{ // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_LabelOptions.html + Type: schema.TypeList, + MinItems: 1, + MaxItems: 1, + Optional: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "custom_label": { + Type: schema.TypeString, + Optional: true, + }, + "font_configuration": fontConfigurationSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_FontConfiguration.html + "visibility": stringSchema(false, validation.StringInSlice(quicksight.Visibility_Values(), false)), + }, + }, + } +} + +func fontConfigurationSchema() *schema.Schema { + return &schema.Schema{ // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_FontConfiguration.html + Type: schema.TypeList, + MinItems: 1, + MaxItems: 1, + Optional: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "font_color": stringSchema(false, validation.StringMatch(regexp.MustCompile(`^#[A-F0-9]{6}$`), "")), + "font_decoration": stringSchema(false, validation.StringInSlice(quicksight.FontDecoration_Values(), false)), + "font_size": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_FontSize.html + Type: schema.TypeList, + MinItems: 1, + MaxItems: 1, + Optional: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "relative": stringSchema(false, validation.StringInSlice(quicksight.RelativeFontSize_Values(), false)), + }, + }, + }, + "font_style": stringSchema(false, validation.StringInSlice(quicksight.FontStyle_Values(), false)), + "font_weight": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_FontWeight.html + Type: schema.TypeList, + MinItems: 1, + MaxItems: 1, + Optional: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "name": stringSchema(false, validation.StringInSlice(quicksight.FontWeightName_Values(), false)), + }, + }, + }, + }, + }, + } +} + +func formatConfigurationSchema() *schema.Schema { + return &schema.Schema{ // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_FormatConfiguration.html + Type: schema.TypeList, + MinItems: 1, + MaxItems: 1, + Optional: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "date_time_format_configuration": dateTimeFormatConfigurationSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_DateTimeFormatConfiguration.html + "number_format_configuration": numberFormatConfigurationSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_NumberFormatConfiguration.html + "string_format_configuration": stringFormatConfigurationSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_StringFormatConfiguration.html + }, + }, + } +} + +func expandFormatConfiguration(tfList []interface{}) *quicksight.FormatConfiguration { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + config := &quicksight.FormatConfiguration{} + + if v, ok := tfMap["date_time_format_configuration"].([]interface{}); ok && len(v) > 0 { + config.DateTimeFormatConfiguration = expandDateTimeFormatConfiguration(v) + } + if v, ok := tfMap["number_format_configuration"].([]interface{}); ok && len(v) > 0 { + config.NumberFormatConfiguration = expandNumberFormatConfiguration(v) + } + if v, ok := tfMap["string_format_configuration"].([]interface{}); ok && len(v) > 0 { + config.StringFormatConfiguration = expandStringFormatConfiguration(v) + } + + return config +} + +func expandDateTimeFormatConfiguration(tfList []interface{}) *quicksight.DateTimeFormatConfiguration { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + config := &quicksight.DateTimeFormatConfiguration{} + + if v, ok := tfMap["date_time_format"].(string); ok && v != "" { + config.DateTimeFormat = aws.String(v) + } + if v, ok := tfMap["null_value_format_configuration"].([]interface{}); ok && len(v) > 0 { + config.NullValueFormatConfiguration = expandNullValueFormatConfiguration(v) + } + if v, ok := tfMap["numeric_format_configuration"].([]interface{}); ok && len(v) > 0 { + config.NumericFormatConfiguration = expandNumericFormatConfiguration(v) + } + + return config +} + +func expandNullValueFormatConfiguration(tfList []interface{}) *quicksight.NullValueFormatConfiguration { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + config := &quicksight.NullValueFormatConfiguration{} + + if v, ok := tfMap["null_string"].(string); ok && v != "" { + config.NullString = aws.String(v) + } + + return config +} + +func expandNumericFormatConfiguration(tfList []interface{}) *quicksight.NumericFormatConfiguration { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + config := &quicksight.NumericFormatConfiguration{} + + if v, ok := tfMap["currency_display_format_configuration"].([]interface{}); ok && len(v) > 0 { + config.CurrencyDisplayFormatConfiguration = expandCurrencyDisplayFormatConfiguration(v) + } + + return config +} + +func expandCurrencyDisplayFormatConfiguration(tfList []interface{}) *quicksight.CurrencyDisplayFormatConfiguration { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + config := &quicksight.CurrencyDisplayFormatConfiguration{} + + if v, ok := tfMap["decimal_places_configuration"].([]interface{}); ok && len(v) > 0 { + config.DecimalPlacesConfiguration = expandDecimalPlacesConfiguration(v) + } + if v, ok := tfMap["negative_value_configuration"].([]interface{}); ok && len(v) > 0 { + config.NegativeValueConfiguration = expandNegativeValueConfiguration(v) + } + if v, ok := tfMap["null_value_format_configuration"].([]interface{}); ok && len(v) > 0 { + config.NullValueFormatConfiguration = expandNullValueFormatConfiguration(v) + } + if v, ok := tfMap["number_scale"].(string); ok && v != "" { + config.NumberScale = aws.String(v) + } + if v, ok := tfMap["prefix"].(string); ok && v != "" { + config.Prefix = aws.String(v) + } + if v, ok := tfMap["separator_configuration"].([]interface{}); ok && len(v) > 0 { + config.SeparatorConfiguration = expandNumericSeparatorConfiguration(v) + } + if v, ok := tfMap["suffix"].(string); ok && v != "" { + config.Suffix = aws.String(v) + } + if v, ok := tfMap["symbol"].(string); ok && v != "" { + config.Symbol = aws.String(v) + } + + return config +} + +func expandDecimalPlacesConfiguration(tfList []interface{}) *quicksight.DecimalPlacesConfiguration { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + config := &quicksight.DecimalPlacesConfiguration{} + + if v, ok := tfMap["decimal_places"].(int64); ok { + config.DecimalPlaces = aws.Int64(v) + } + + return config +} + +func expandNegativeValueConfiguration(tfList []interface{}) *quicksight.NegativeValueConfiguration { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + config := &quicksight.NegativeValueConfiguration{} + + if v, ok := tfMap["display_mode"].(string); ok { + config.DisplayMode = aws.String(v) + } + + return config +} + +func expandNumericSeparatorConfiguration(tfList []interface{}) *quicksight.NumericSeparatorConfiguration { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + config := &quicksight.NumericSeparatorConfiguration{} + + if v, ok := tfMap["decimal_separator"].(string); ok { + config.DecimalSeparator = aws.String(v) + } + if v, ok := tfMap["thousands_separator"].([]interface{}); ok && len(v) > 0 { + config.ThousandsSeparator = expandThousandSeparatorOptions(v) + } + + return config +} + +func expandThousandSeparatorOptions(tfList []interface{}) *quicksight.ThousandSeparatorOptions { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + config := &quicksight.ThousandSeparatorOptions{} + + if v, ok := tfMap["symbol"].(string); ok { + config.Symbol = aws.String(v) + } + if v, ok := tfMap["visibility"].(string); ok { + config.Visibility = aws.String(v) + } + + return config +} + +func expandNumberFormatConfiguration(tfList []interface{}) *quicksight.NumberFormatConfiguration { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + config := &quicksight.NumberFormatConfiguration{} + + if v, ok := tfMap["numeric_format_configuration"].([]interface{}); ok && len(v) > 0 { + config.FormatConfiguration = expandNumericFormatConfiguration(v) + } + + return config +} + +func expandStringFormatConfiguration(tfList []interface{}) *quicksight.StringFormatConfiguration { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + config := &quicksight.StringFormatConfiguration{} + + if v, ok := tfMap["null_value_format_configuration"].([]interface{}); ok && len(v) > 0 { + config.NullValueFormatConfiguration = expandNullValueFormatConfiguration(v) + } + if v, ok := tfMap["numeric_format_configuration"].([]interface{}); ok && len(v) > 0 { + config.NumericFormatConfiguration = expandNumericFormatConfiguration(v) + } + + return config +} + +func expandLabelOptions(tfList []interface{}) *quicksight.LabelOptions { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + options := &quicksight.LabelOptions{} + + if v, ok := tfMap["custom_label"].(string); ok { + options.CustomLabel = aws.String(v) + } + if v, ok := tfMap["visibility"].(string); ok { + options.Visibility = aws.String(v) + } + if v, ok := tfMap["font_configuration"].([]interface{}); ok && len(v) > 0 { + options.FontConfiguration = expandFontConfiguration(v) + } + + return options +} + +func expandFontConfiguration(tfList []interface{}) *quicksight.FontConfiguration { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + config := &quicksight.FontConfiguration{} + + if v, ok := tfMap["font_color"].(string); ok { + config.FontColor = aws.String(v) + } + if v, ok := tfMap["font_decoration"].(string); ok { + config.FontDecoration = aws.String(v) + } + if v, ok := tfMap["font_style"].(string); ok { + config.FontStyle = aws.String(v) + } + if v, ok := tfMap["font_size"].([]interface{}); ok && len(v) > 0 { + config.FontSize = expandFontSize(v) + } + if v, ok := tfMap["font_weight"].([]interface{}); ok && len(v) > 0 { + config.FontWeight = expandFontWeight(v) + } + + return config +} + +func expandFontSize(tfList []interface{}) *quicksight.FontSize { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + config := &quicksight.FontSize{} + + if v, ok := tfMap["relative"].(string); ok { + config.Relative = aws.String(v) + } + + return config +} + +func expandFontWeight(tfList []interface{}) *quicksight.FontWeight { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + config := &quicksight.FontWeight{} + + if v, ok := tfMap["name"].(string); ok { + config.Name = aws.String(v) + } + + return config +} + +func expandComparisonFormatConfiguration(tfList []interface{}) *quicksight.ComparisonFormatConfiguration { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + config := &quicksight.ComparisonFormatConfiguration{} + + if v, ok := tfMap["number_display_format_configuration"].([]interface{}); ok && len(v) > 0 { + config.NumberDisplayFormatConfiguration = expandNumberDisplayFormatConfiguration(v) + } + if v, ok := tfMap["percentage_display_format_configuration"].([]interface{}); ok && len(v) > 0 { + config.PercentageDisplayFormatConfiguration = expandPercentageDisplayFormatConfiguration(v) + } + + return config +} + +func expandNumberDisplayFormatConfiguration(tfList []interface{}) *quicksight.NumberDisplayFormatConfiguration { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + config := &quicksight.NumberDisplayFormatConfiguration{} + + if v, ok := tfMap["number_scale"].(string); ok { + config.NumberScale = aws.String(v) + } + if v, ok := tfMap["prefix"].(string); ok { + config.Prefix = aws.String(v) + } + if v, ok := tfMap["suffix"].(string); ok { + config.Suffix = aws.String(v) + } + if v, ok := tfMap["decimal_places_configuration"].([]interface{}); ok && len(v) > 0 { + config.DecimalPlacesConfiguration = expandDecimalPlacesConfiguration(v) + } + if v, ok := tfMap["negative_value_configuration"].([]interface{}); ok && len(v) > 0 { + config.NegativeValueConfiguration = expandNegativeValueConfiguration(v) + } + if v, ok := tfMap["null_value_format_configuration"].([]interface{}); ok && len(v) > 0 { + config.NullValueFormatConfiguration = expandNullValueFormatConfiguration(v) + } + if v, ok := tfMap["separator_configuration"].([]interface{}); ok && len(v) > 0 { + config.SeparatorConfiguration = expandNumericSeparatorConfiguration(v) + } + + return config +} + +func expandPercentageDisplayFormatConfiguration(tfList []interface{}) *quicksight.PercentageDisplayFormatConfiguration { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + config := &quicksight.PercentageDisplayFormatConfiguration{} + + if v, ok := tfMap["prefix"].(string); ok { + config.Prefix = aws.String(v) + } + if v, ok := tfMap["suffix"].(string); ok { + config.Suffix = aws.String(v) + } + if v, ok := tfMap["decimal_places_configuration"].([]interface{}); ok && len(v) > 0 { + config.DecimalPlacesConfiguration = expandDecimalPlacesConfiguration(v) + } + if v, ok := tfMap["negative_value_configuration"].([]interface{}); ok && len(v) > 0 { + config.NegativeValueConfiguration = expandNegativeValueConfiguration(v) + } + if v, ok := tfMap["null_value_format_configuration"].([]interface{}); ok && len(v) > 0 { + config.NullValueFormatConfiguration = expandNullValueFormatConfiguration(v) + } + if v, ok := tfMap["separator_configuration"].([]interface{}); ok && len(v) > 0 { + config.SeparatorConfiguration = expandNumericSeparatorConfiguration(v) + } + + return config +} diff --git a/internal/service/quicksight/template_parameter.go b/internal/service/quicksight/template_parameter.go new file mode 100644 index 000000000000..336cddbfb3d1 --- /dev/null +++ b/internal/service/quicksight/template_parameter.go @@ -0,0 +1,766 @@ +package quicksight + +import ( + "fmt" + "regexp" + "time" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/quicksight" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" + "github.com/hashicorp/terraform-provider-aws/internal/flex" + "github.com/hashicorp/terraform-provider-aws/internal/verify" +) + +func dateTimeParameterDeclarationSchema() *schema.Schema { + return &schema.Schema{ // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_DateTimeParameterDeclaration.html + Type: schema.TypeList, + MinItems: 1, + MaxItems: 1, + Optional: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "name": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.All( + validation.StringLenBetween(1, 2048), + validation.StringMatch(regexp.MustCompile(`^[a-zA-Z0-9]+$`), ""), + ), + }, + "default_values": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_DateTimeDefaultValues.html + Type: schema.TypeList, + MinItems: 1, + MaxItems: 1, + Optional: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "dynamic_value": dynamicValueSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_DynamicDefaultValue.html + "rolling_date": rollingDateConfigurationSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_RollingDateConfiguration.html, + "static_values": { + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 50000, + Elem: &schema.Schema{ + Type: schema.TypeString, + ValidateFunc: verify.ValidUTCTimestamp, + }, + }, + }, + }, + }, + "time_granularity": stringSchema(false, validation.StringInSlice(quicksight.TimeGranularity_Values(), false)), + "values_when_unset": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_DateTimeValueWhenUnsetConfiguration.html + Type: schema.TypeList, + MinItems: 1, + MaxItems: 1, + Optional: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "custom_value": { + Type: schema.TypeString, + Optional: true, + ValidateFunc: verify.ValidUTCTimestamp, + }, + "value_when_unset_option": stringSchema(false, validation.StringInSlice(quicksight.ValueWhenUnsetOption_Values(), false)), + }, + }, + }, + }, + }, + } +} + +func decimalParameterDeclarationSchema() *schema.Schema { + return &schema.Schema{ // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_DecimalParameterDeclaration.html + Type: schema.TypeList, + MinItems: 1, + MaxItems: 1, + Optional: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "name": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.All( + validation.StringLenBetween(1, 2048), + validation.StringMatch(regexp.MustCompile(`^[a-zA-Z0-9]+$`), ""), + ), + }, + "parameter_value_type": stringSchema(true, validation.StringInSlice(quicksight.ParameterValueType_Values(), false)), + "default_values": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_DecimalDefaultValues.html + Type: schema.TypeList, + MinItems: 1, + MaxItems: 1, + Optional: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "dynamic_value": dynamicValueSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_DynamicDefaultValue.html + "static_values": { + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 50000, + Elem: &schema.Schema{ + Type: schema.TypeFloat, + }, + }, + }, + }, + }, + "values_when_unset": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_DecimalValueWhenUnsetConfiguration.html + Type: schema.TypeList, + MinItems: 1, + MaxItems: 1, + Optional: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "custom_value": { + Type: schema.TypeFloat, + Optional: true, + }, + "value_when_unset_option": stringSchema(false, validation.StringInSlice(quicksight.ValueWhenUnsetOption_Values(), false)), + }, + }, + }, + }, + }, + } +} + +func integerParameterDeclarationSchema() *schema.Schema { + return &schema.Schema{ // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_IntegerParameterDeclaration.html + Type: schema.TypeList, + MinItems: 1, + MaxItems: 1, + Optional: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "name": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.All( + validation.StringLenBetween(1, 2048), + validation.StringMatch(regexp.MustCompile(`^[a-zA-Z0-9]+$`), ""), + ), + }, + "parameter_value_type": stringSchema(true, validation.StringInSlice(quicksight.ParameterValueType_Values(), false)), + "default_values": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_IntegerDefaultValues.html + Type: schema.TypeList, + MinItems: 1, + MaxItems: 1, + Optional: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "dynamic_value": dynamicValueSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_DynamicDefaultValue.html + "static_values": { + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 50000, + Elem: &schema.Schema{ + Type: schema.TypeInt, + }, + }, + }, + }, + }, + "values_when_unset": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_IntegerValueWhenUnsetConfiguration.html + Type: schema.TypeList, + MinItems: 1, + MaxItems: 1, + Optional: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "custom_value": { + Type: schema.TypeInt, + Optional: true, + }, + "value_when_unset_option": stringSchema(false, validation.StringInSlice(quicksight.ValueWhenUnsetOption_Values(), false)), + }, + }, + }, + }, + }, + } +} + +func stringParameterDeclarationSchema() *schema.Schema { + return &schema.Schema{ // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_StringParameterDeclaration.html + Type: schema.TypeList, + MinItems: 1, + MaxItems: 1, + Optional: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "name": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.All( + validation.StringLenBetween(1, 2048), + validation.StringMatch(regexp.MustCompile(`^[a-zA-Z0-9]+$`), ""), + ), + }, + "parameter_value_type": stringSchema(true, validation.StringInSlice(quicksight.ParameterValueType_Values(), false)), + "default_values": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_StringDefaultValues.html + Type: schema.TypeList, + MinItems: 1, + MaxItems: 1, + Optional: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "dynamic_value": dynamicValueSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_DynamicDefaultValue.html + "static_values": { + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 50000, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + }, + }, + }, + }, + "values_when_unset": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_StringValueWhenUnsetConfiguration.html + Type: schema.TypeList, + MinItems: 1, + MaxItems: 1, + Optional: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "custom_value": { + Type: schema.TypeString, + Optional: true, + }, + "value_when_unset_option": stringSchema(false, validation.StringInSlice(quicksight.ValueWhenUnsetOption_Values(), false)), + }, + }, + }, + }, + }, + } +} + +func dynamicValueSchema() *schema.Schema { + return &schema.Schema{ // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_DynamicDefaultValue.html + Type: schema.TypeList, + MinItems: 1, + MaxItems: 1, + Optional: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "default_value_column": columnSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_ColumnIdentifier.html + "group_name_column": columnSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_ColumnIdentifier.html + "user_name_column": columnSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_ColumnIdentifier.html + }, + }, + } +} + +func parameterControlsSchema() *schema.Schema { + return &schema.Schema{ // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_ParameterControl.html + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 200, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "date_time_picker": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_ParameterDateTimePickerControl.html + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "parameter_control_id": idSchema(), + "source_parameter_name": parameterNameSchema(true), + "title": stringSchema(true, validation.StringLenBetween(1, 2048)), + "display_options": dateTimePickerControlDisplayOptionsSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_DateTimePickerControlDisplayOptions.html + }, + }, + }, + "dropdown": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_ParameterDropDownControl.html + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "parameter_control_id": idSchema(), + "source_parameter_name": parameterNameSchema(true), + "title": stringSchema(true, validation.StringLenBetween(1, 2048)), + "cascading_control_configuration": cascadingControlConfigurationSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_CascadingControlConfiguration.html + "display_options": dropDownControlDisplayOptionsSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_DropDownControlDisplayOptions.html + "selectable_values": parameterSelectableValuesSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_ParameterSelectableValues.html + "type": stringSchema(false, validation.StringInSlice(quicksight.SheetControlListType_Values(), false)), + }, + }, + }, + "list": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_ParameterListControl.html + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "parameter_control_id": idSchema(), + "source_parameter_name": parameterNameSchema(true), + "title": stringSchema(true, validation.StringLenBetween(1, 2048)), + "cascading_control_configuration": cascadingControlConfigurationSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_CascadingControlConfiguration.html + "display_options": listControlDisplayOptionsSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_ListControlDisplayOptions.html + "selectable_values": parameterSelectableValuesSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_ParameterSelectableValues.html + "type": stringSchema(false, validation.StringInSlice(quicksight.SheetControlListType_Values(), false)), + }, + }, + }, + "slider": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_ParameterSliderControl.html + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "parameter_control_id": idSchema(), + "source_parameter_name": parameterNameSchema(true), + "title": stringSchema(true, validation.StringLenBetween(1, 2048)), + "display_options": sliderControlDisplayOptionsSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_SliderControlDisplayOptions.html + "maximum_value": { + Type: schema.TypeFloat, + Required: true, + }, + "minimum_value": { + Type: schema.TypeFloat, + Required: true, + }, + "step_size": { + Type: schema.TypeFloat, + Required: true, + }, + }, + }, + }, + "text_area": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_ParameterTextAreaControl.html + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "parameter_control_id": idSchema(), + "source_parameter_name": parameterNameSchema(true), + "title": stringSchema(true, validation.StringLenBetween(1, 2048)), + "display_options": textAreaControlDisplayOptionsSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_TextAreaControlDisplayOptions.html + "delimiter": stringSchema(false, validation.StringLenBetween(1, 2048)), + }, + }, + }, + "text_field": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_ParameterTextFieldControl.html + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "parameter_control_id": idSchema(), + "source_parameter_name": parameterNameSchema(true), + "title": stringSchema(true, validation.StringLenBetween(1, 2048)), + "display_options": textFieldControlDisplayOptionsSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_TextFieldControlDisplayOptions.html + }, + }, + }, + }, + }, + } +} + +func parameterSelectableValuesSchema() *schema.Schema { + return &schema.Schema{ // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_ParameterSelectableValues.html + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "link_to_data_set_column": columnSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_ColumnIdentifier.html + "values": { + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 50000, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + }, + }, + }, + } +} + +func parameterNameSchema(required bool) *schema.Schema { + return &schema.Schema{ + Type: schema.TypeString, + Required: required, + Optional: !required, + ValidateFunc: validation.All( + validation.StringLenBetween(1, 2048), + validation.StringMatch(regexp.MustCompile(`^[a-zA-Z0-9]+$`), ""), + ), + } +} + +func expandDateTimeParameterDeclaration(tfList []interface{}) (*quicksight.DateTimeParameterDeclaration, error) { + if len(tfList) == 0 || tfList[0] == nil { + return nil, nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil, nil + } + + param := &quicksight.DateTimeParameterDeclaration{} + + if v, ok := tfMap["name"].(string); ok && v != "" { + param.Name = aws.String(v) + } + if v, ok := tfMap["default_values"].([]interface{}); ok && len(v) > 0 { + value, err := expandDateTimeDefaultValues(v) + if err != nil { + return nil, err + } + param.DefaultValues = value + } + if v, ok := tfMap["time_granularity"].(string); ok && v != "" { + param.TimeGranularity = aws.String(v) + } + if v, ok := tfMap["values_when_unset"].([]interface{}); ok && len(v) > 0 { + value, err := expandDateTimeValueWhenUnsetConfiguration(v) + if err != nil { + return nil, err + } + param.ValueWhenUnset = value + } + + return param, nil +} + +func expandDateTimeDefaultValues(tfList []interface{}) (*quicksight.DateTimeDefaultValues, error) { + if len(tfList) == 0 || tfList[0] == nil { + return nil, nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil, nil + } + + values := &quicksight.DateTimeDefaultValues{} + + if v, ok := tfMap["dynamic_value"].([]interface{}); ok && len(v) > 0 { + values.DynamicValue = expandDynamicDefaultValue(v) + } + if v, ok := tfMap["rolling_date"].([]interface{}); ok && len(v) > 0 { + values.RollingDate = expandRollingDateConfiguration(v) + } + if v, ok := tfMap["static_values"].([]interface{}); ok && len(v) > 0 { + times, err := flex.ExpandStringTimeList(v, time.RFC3339) + if err != nil { + return nil, err + } + values.StaticValues = times + } + + return values, nil +} + +func expandDynamicDefaultValue(tfList []interface{}) *quicksight.DynamicDefaultValue { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + value := &quicksight.DynamicDefaultValue{} + + if v, ok := tfMap["default_value_column"].([]interface{}); ok && len(v) > 0 { + value.DefaultValueColumn = expandColumnIdentifier(v) + } + if v, ok := tfMap["group_name_column"].([]interface{}); ok && len(v) > 0 { + value.GroupNameColumn = expandColumnIdentifier(v) + } + if v, ok := tfMap["user_name_column"].([]interface{}); ok && len(v) > 0 { + value.UserNameColumn = expandColumnIdentifier(v) + } + + return value +} + +func expandDateTimeValueWhenUnsetConfiguration(tfList []interface{}) (*quicksight.DateTimeValueWhenUnsetConfiguration, error) { + if len(tfList) == 0 || tfList[0] == nil { + return nil, nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil, nil + } + + config := &quicksight.DateTimeValueWhenUnsetConfiguration{} + + if v, ok := tfMap["custom_value"].(string); ok && v != "" { + t, err := time.Parse(time.RFC3339, v) + if err != nil { + return nil, fmt.Errorf("error parsing DateTimeValueWhenUnsetConfiguration CustomValue date: %w", err) + } + config.CustomValue = aws.Time(t) + } + if v, ok := tfMap["value_when_unset_option"].(string); ok && v != "" { + config.ValueWhenUnsetOption = aws.String(v) + } + + return config, nil +} + +func expandDecimalParameterDeclaration(tfList []interface{}) *quicksight.DecimalParameterDeclaration { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + param := &quicksight.DecimalParameterDeclaration{} + + if v, ok := tfMap["name"].(string); ok && v != "" { + param.Name = aws.String(v) + } + if v, ok := tfMap["parameter_value_type"].(string); ok && v != "" { + param.ParameterValueType = aws.String(v) + } + if v, ok := tfMap["default_values"].([]interface{}); ok && len(v) > 0 { + param.DefaultValues = expandDecimalDefaultValues(v) + } + if v, ok := tfMap["values_when_unset"].([]interface{}); ok && len(v) > 0 { + param.ValueWhenUnset = expandDecimalValueWhenUnsetConfiguration(v) + } + + return param +} + +func expandDecimalValueWhenUnsetConfiguration(tfList []interface{}) *quicksight.DecimalValueWhenUnsetConfiguration { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + config := &quicksight.DecimalValueWhenUnsetConfiguration{} + + if v, ok := tfMap["custom_value"].(float64); ok { + config.CustomValue = aws.Float64(v) + } + if v, ok := tfMap["value_when_unset_option"].(string); ok && v != "" { + config.ValueWhenUnsetOption = aws.String(v) + } + + return config +} + +func expandDecimalDefaultValues(tfList []interface{}) *quicksight.DecimalDefaultValues { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + values := &quicksight.DecimalDefaultValues{} + + if v, ok := tfMap["dynamic_value"].([]interface{}); ok && len(v) > 0 { + values.DynamicValue = expandDynamicDefaultValue(v) + } + if v, ok := tfMap["static_values"].([]interface{}); ok && len(v) > 0 { + values.StaticValues = flex.ExpandFloat64List(v) + } + + return values +} + +func expandIntegerParameterDeclaration(tfList []interface{}) *quicksight.IntegerParameterDeclaration { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + param := &quicksight.IntegerParameterDeclaration{} + + if v, ok := tfMap["name"].(string); ok && v != "" { + param.Name = aws.String(v) + } + if v, ok := tfMap["parameter_value_type"].(string); ok && v != "" { + param.ParameterValueType = aws.String(v) + } + if v, ok := tfMap["default_values"].([]interface{}); ok && len(v) > 0 { + param.DefaultValues = expandIntegerDefaultValues(v) + } + if v, ok := tfMap["values_when_unset"].([]interface{}); ok && len(v) > 0 { + param.ValueWhenUnset = expandIntegerValueWhenUnsetConfiguration(v) + } + + return param +} + +func expandIntegerValueWhenUnsetConfiguration(tfList []interface{}) *quicksight.IntegerValueWhenUnsetConfiguration { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + config := &quicksight.IntegerValueWhenUnsetConfiguration{} + + if v, ok := tfMap["custom_value"].(int64); ok { + config.CustomValue = aws.Int64(v) + } + if v, ok := tfMap["value_when_unset_option"].(string); ok && v != "" { + config.ValueWhenUnsetOption = aws.String(v) + } + + return config +} + +func expandIntegerDefaultValues(tfList []interface{}) *quicksight.IntegerDefaultValues { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + values := &quicksight.IntegerDefaultValues{} + + if v, ok := tfMap["dynamic_value"].([]interface{}); ok && len(v) > 0 { + values.DynamicValue = expandDynamicDefaultValue(v) + } + if v, ok := tfMap["static_values"].([]interface{}); ok && len(v) > 0 { + values.StaticValues = flex.ExpandInt64List(v) + } + + return values +} + +func expandStringParameterDeclaration(tfList []interface{}) *quicksight.StringParameterDeclaration { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + param := &quicksight.StringParameterDeclaration{} + + if v, ok := tfMap["name"].(string); ok && v != "" { + param.Name = aws.String(v) + } + if v, ok := tfMap["parameter_value_type"].(string); ok && v != "" { + param.ParameterValueType = aws.String(v) + } + if v, ok := tfMap["default_values"].([]interface{}); ok && len(v) > 0 { + param.DefaultValues = expandStringDefaultValues(v) + } + if v, ok := tfMap["values_when_unset"].([]interface{}); ok && len(v) > 0 { + param.ValueWhenUnset = expandStringValueWhenUnsetConfiguration(v) + } + + return param +} + +func expandStringValueWhenUnsetConfiguration(tfList []interface{}) *quicksight.StringValueWhenUnsetConfiguration { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + config := &quicksight.StringValueWhenUnsetConfiguration{} + + if v, ok := tfMap["custom_value"].(string); ok { + config.CustomValue = aws.String(v) + } + if v, ok := tfMap["value_when_unset_option"].(string); ok && v != "" { + config.ValueWhenUnsetOption = aws.String(v) + } + + return config +} + +func expandStringDefaultValues(tfList []interface{}) *quicksight.StringDefaultValues { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + values := &quicksight.StringDefaultValues{} + + if v, ok := tfMap["dynamic_value"].([]interface{}); ok && len(v) > 0 { + values.DynamicValue = expandDynamicDefaultValue(v) + } + if v, ok := tfMap["static_values"].([]interface{}); ok && len(v) > 0 { + values.StaticValues = flex.ExpandStringList(v) + } + + return values +} + +func expandParameterSelectableValues(tfList []interface{}) *quicksight.ParameterSelectableValues { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + values := &quicksight.ParameterSelectableValues{} + + if v, ok := tfMap["link_to_data_set_column"].([]interface{}); ok && len(v) > 0 { + values.LinkToDataSetColumn = expandColumnIdentifier(v) + } + if v, ok := tfMap["values"].([]interface{}); ok && len(v) > 0 { + values.Values = flex.ExpandStringList(v) + } + + return values +} diff --git a/internal/service/quicksight/template_sheet.go b/internal/service/quicksight/template_sheet.go new file mode 100644 index 000000000000..8329c9b2d40b --- /dev/null +++ b/internal/service/quicksight/template_sheet.go @@ -0,0 +1,1669 @@ +package quicksight + +import ( + "regexp" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/quicksight" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" +) + +func analysisDefaultSchema() *schema.Schema { + return &schema.Schema{ + Type: schema.TypeList, + MaxItems: 1, + Optional: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "default_new_sheet_configuration": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_DefaultNewSheetConfiguration.html + Type: schema.TypeList, + Required: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "interactive_layout_configuration": interactiveLayoutConfigurationSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_DefaultInteractiveLayoutConfiguration.html + "paginated_layout_configuration": paginatedLayoutConfigurationSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_DefaultPaginatedLayoutConfiguration.html, + "sheet_content_type": stringSchema(false, validation.StringInSlice(quicksight.SheetContentType_Values(), false)), + }, + }, + }, + }, + }, + } +} + +func interactiveLayoutConfigurationSchema() *schema.Schema { + return &schema.Schema{ // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_DefaultInteractiveLayoutConfiguration.html + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "free_form": { + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "canvas_size_options": { + Type: schema.TypeList, + Required: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "screen_canvas_size_options": { + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "optimized_view_port_width": { + Type: schema.TypeString, + Required: true, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + "grid": { + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "canvas_size_options": { + Type: schema.TypeList, + Required: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "screen_canvas_size_options": { + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "optimized_view_port_width": { + Type: schema.TypeString, + Optional: true, + }, + "resize_option": stringSchema(true, validation.StringInSlice(quicksight.ResizeOption_Values(), false)), + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + } +} + +func paginatedLayoutConfigurationSchema() *schema.Schema { + return &schema.Schema{ // // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_DefaultPaginatedLayoutConfiguration.html, + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "section_based": { + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "canvas_size_options": { + Type: schema.TypeList, + Required: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "paper_canvas_size_options": paperCanvasSizeOptionsSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_SectionBasedLayoutPaperCanvasSizeOptions.html + }, + }, + }, + }, + }, + }, + }, + }, + } +} + +func paperCanvasSizeOptionsSchema() *schema.Schema { + return &schema.Schema{ + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "paper_margin": spacingSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_Spacing.html + "paper_orientation": stringSchema(false, validation.StringInSlice(quicksight.PaperOrientation_Values(), false)), + "paper_size": stringSchema(false, validation.StringInSlice(quicksight.PaperSize_Values(), false)), + }, + }, + } +} + +func sheetControlLayoutsSchema() *schema.Schema { + return &schema.Schema{ // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_SheetControlLayout.html + Type: schema.TypeList, + MinItems: 0, + MaxItems: 1, + Optional: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "configuration": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_SheetControlLayoutConfiguration.html + Type: schema.TypeList, + MinItems: 1, + MaxItems: 1, + Required: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "grid_layout": gridLayoutConfigurationSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_GridLayoutConfiguration.html, + }, + }, + }, + }, + }, + } +} + +func layoutSchema() *schema.Schema { + return &schema.Schema{ // // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_Layout.html + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "configuration": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_LayoutConfiguration.html + Type: schema.TypeList, + Required: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "free_form_layout": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_FreeFormLayoutConfiguration.html + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "elements": freeFormLayoutElementsSchema(), + "canvas_size_options": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_FreeFormLayoutCanvasSizeOptions.html + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "screen_canvas_size_options": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_FreeFormLayoutScreenCanvasSizeOptions.html + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "optimized_view_port_width": { + Type: schema.TypeString, + Required: true, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + "grid_layout": gridLayoutConfigurationSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_GridLayoutConfiguration.html, + "section_based_layout": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_SectionBasedLayoutConfiguration.html + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "body_sections": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_BodySectionConfiguration.html + Type: schema.TypeList, + Required: true, + MinItems: 1, + MaxItems: 28, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "content": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_BodySectionContent.html + Type: schema.TypeList, + Required: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "layout": sectionLayoutConfigurationSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_SectionLayoutConfiguration.html + }, + }, + }, + "section_id": idSchema(), + "page_break_configuration": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_SectionPageBreakConfiguration.html + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "after": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_SectionAfterPageBreak.html + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "status": stringSchema(false, validation.StringInSlice(quicksight.Status_Values(), false)), + }, + }, + }, + }, + }, + }, + "style": sectionStyleSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_SectionStyle.html + }, + }, + }, + "canvas_size_options": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_SectionBasedLayoutCanvasSizeOptions.html + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "paper_canvas_size_options": paperCanvasSizeOptionsSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_SectionBasedLayoutPaperCanvasSizeOptions.html + + }, + }, + }, + "footer_sections": headerFooterSectionConfigurationSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_HeaderFooterSectionConfiguration.html + "header_sections": headerFooterSectionConfigurationSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_HeaderFooterSectionConfiguration.html + }, + }, + }, + }, + }, + }, + }, + }, + } +} + +func gridLayoutConfigurationSchema() *schema.Schema { + return &schema.Schema{ // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_GridLayoutConfiguration.html + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "elements": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_GridLayoutElement.html + Type: schema.TypeList, + Required: true, + MinItems: 1, + MaxItems: 430, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "column_span": { + Type: schema.TypeInt, + Required: true, + ValidateFunc: validation.IntBetween(1, 36), + }, + "element_id": idSchema(), + "element_type": stringSchema(true, validation.StringInSlice(quicksight.LayoutElementType_Values(), false)), + "row_span": { + Type: schema.TypeInt, + Required: true, + ValidateFunc: validation.IntBetween(1, 21), + }, + "column_index": { + Type: schema.TypeInt, + Optional: true, + ValidateFunc: validation.IntBetween(0, 35), + }, + "row_index": { + Type: schema.TypeInt, + Optional: true, + ValidateFunc: validation.IntBetween(0, 9009), + }, + }, + }, + }, + "canvas_size_options": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_GridLayoutCanvasSizeOptions.html + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "screen_canvas_size_options": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_GridLayoutScreenCanvasSizeOptions.html + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "optimized_view_port_width": { + Type: schema.TypeString, + Required: true, + }, + "resize_option": stringSchema(true, validation.StringInSlice(quicksight.ResizeOption_Values(), false)), + }, + }, + }, + }, + }, + }, + }, + }, + } +} + +func headerFooterSectionConfigurationSchema() *schema.Schema { + return &schema.Schema{ // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_HeaderFooterSectionConfiguration.html + Type: schema.TypeList, + Required: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "layout": sectionLayoutConfigurationSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_SectionLayoutConfiguration.html + "section_id": idSchema(), + "style": sectionStyleSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_SectionStyle.html + }, + }, + } +} + +func sectionStyleSchema() *schema.Schema { + return &schema.Schema{ // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_SectionStyle.html + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "height": { + Type: schema.TypeString, + Optional: true, + }, + "padding": spacingSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_Spacing.html + }, + }, + } +} + +func freeFormLayoutElementsSchema() *schema.Schema { + return &schema.Schema{ // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_FreeFormLayoutElement.html + Type: schema.TypeList, + Required: true, + MinItems: 1, + MaxItems: 430, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "element_id": idSchema(), + "element_type": stringSchema(true, validation.StringInSlice(quicksight.LayoutElementType_Values(), false)), + "height": { + Type: schema.TypeString, + Required: true, + }, + "width": { + Type: schema.TypeString, + Required: true, + }, + "x_axis_location": { + Type: schema.TypeString, + Required: true, + }, + "y_axis_location": { + Type: schema.TypeString, + Required: true, + }, + "background_style": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_FreeFormLayoutElementBackgroundStyle.html + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "color": stringSchema(false, validation.StringMatch(regexp.MustCompile(`^#[A-F0-9]{6}(?:[A-F0-9]{2})?$`), "")), + "visibility": stringSchema(false, validation.StringInSlice(quicksight.Visibility_Values(), false)), + }, + }, + }, + "border_style": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_FreeFormLayoutElementBorderStyle.html + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "color": stringSchema(false, validation.StringMatch(regexp.MustCompile(`^#[A-F0-9]{6}(?:[A-F0-9]{2})?$`), "")), + "visibility": stringSchema(false, validation.StringInSlice(quicksight.Visibility_Values(), false)), + }, + }, + }, + "loading_animation": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_LoadingAnimation.html + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "visibility": stringSchema(false, validation.StringInSlice(quicksight.Visibility_Values(), false)), + }, + }, + }, + "rendering_rules": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_SheetElementRenderingRule.html + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 10000, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "configuration_overrides": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_SheetElementConfigurationOverrides.html + Type: schema.TypeList, + Required: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "visibility": stringSchema(false, validation.StringInSlice(quicksight.Visibility_Values(), false)), + }, + }, + }, + "expression": stringSchema(true, validation.StringLenBetween(1, 4096)), + }, + }, + }, + "selected_border_style": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_FreeFormLayoutElementBorderStyle.html + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "color": stringSchema(false, validation.StringMatch(regexp.MustCompile(`^#[A-F0-9]{6}(?:[A-F0-9]{2})?$`), "")), + "visibility": stringSchema(false, validation.StringInSlice(quicksight.Visibility_Values(), false)), + }, + }, + }, + "visibility": stringSchema(false, validation.StringInSlice(quicksight.Visibility_Values(), false)), + }, + }, + } +} + +func sectionLayoutConfigurationSchema() *schema.Schema { + return &schema.Schema{ // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_SectionLayoutConfiguration.html + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "free_form_layout": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_FreeFormSectionLayoutConfiguration.html + Type: schema.TypeList, + Required: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "elements": freeFormLayoutElementsSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_FreeFormLayoutElement.html + }, + }, + }, + }, + }, + } +} + +func spacingSchema() *schema.Schema { + return &schema.Schema{ // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_Spacing.html + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "bottom": { + Type: schema.TypeString, + Optional: true, + }, + "left": { + Type: schema.TypeString, + Optional: true, + }, + "right": { + Type: schema.TypeString, + Optional: true, + }, + "top": { + Type: schema.TypeString, + Optional: true, + }, + }, + }, + } +} + +func expandAnalysisDefaults(tfList []interface{}) *quicksight.AnalysisDefaults { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + defaults := &quicksight.AnalysisDefaults{} + + if v, ok := tfMap["default_new_sheet_configuration"].([]interface{}); ok && len(v) > 0 { + defaults.DefaultNewSheetConfiguration = expandDefaultNewSheetConfiguration(v) + } + + return defaults +} + +func expandDefaultNewSheetConfiguration(tfList []interface{}) *quicksight.DefaultNewSheetConfiguration { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + config := &quicksight.DefaultNewSheetConfiguration{} + + if v, ok := tfMap["interactive_layout_configuration"].([]interface{}); ok && len(v) > 0 { + config.InteractiveLayoutConfiguration = expandDefaultInteractiveLayoutConfiguration(v) + } + + if v, ok := tfMap["paginated_layout_configuration"].([]interface{}); ok && len(v) > 0 { + config.PaginatedLayoutConfiguration = expandDefaultPaginatedLayoutConfiguration(v) + } + + if v, ok := tfMap["sheet_content_type"].(string); ok && v != "" { + config.SheetContentType = aws.String(v) + } + + return config +} + +func expandDefaultInteractiveLayoutConfiguration(tfList []interface{}) *quicksight.DefaultInteractiveLayoutConfiguration { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + config := &quicksight.DefaultInteractiveLayoutConfiguration{} + + if v, ok := tfMap["free_form"].([]interface{}); ok && len(v) > 0 { + config.FreeForm = expandDefaultFreeFormLayoutConfiguration(v) + } + + if v, ok := tfMap["grid"].([]interface{}); ok && len(v) > 0 { + config.Grid = expandDefaultGridLayoutConfiguration(v) + } + + return config +} + +func expandDefaultFreeFormLayoutConfiguration(tfList []interface{}) *quicksight.DefaultFreeFormLayoutConfiguration { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + config := &quicksight.DefaultFreeFormLayoutConfiguration{} + + if v, ok := tfMap["canvas_size_options"].([]interface{}); ok && len(v) > 0 { + config.CanvasSizeOptions = expandFreeFormLayoutCanvasSizeOptions(v) + } + + return config +} + +func expandFreeFormLayoutCanvasSizeOptions(tfList []interface{}) *quicksight.FreeFormLayoutCanvasSizeOptions { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + options := &quicksight.FreeFormLayoutCanvasSizeOptions{} + + if v, ok := tfMap["screen_canvas_size_options"].([]interface{}); ok && len(v) > 0 { + options.ScreenCanvasSizeOptions = expandFreeFormLayoutScreenCanvasSizeOptions(v) + } + + return options +} + +func expandFreeFormLayoutScreenCanvasSizeOptions(tfList []interface{}) *quicksight.FreeFormLayoutScreenCanvasSizeOptions { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + options := &quicksight.FreeFormLayoutScreenCanvasSizeOptions{} + + if v, ok := tfMap["optimized_view_port_width"].(string); ok && v != "" { + options.OptimizedViewPortWidth = aws.String(v) + } + + return options +} + +func expandDefaultGridLayoutConfiguration(tfList []interface{}) *quicksight.DefaultGridLayoutConfiguration { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + config := &quicksight.DefaultGridLayoutConfiguration{} + + if v, ok := tfMap["canvas_size_options"].([]interface{}); ok && len(v) > 0 { + config.CanvasSizeOptions = expandGridLayoutCanvasSizeOptions(v) + } + + return config +} + +func expandGridLayoutCanvasSizeOptions(tfList []interface{}) *quicksight.GridLayoutCanvasSizeOptions { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + options := &quicksight.GridLayoutCanvasSizeOptions{} + + if v, ok := tfMap["screen_canvas_size_options"].([]interface{}); ok && len(v) > 0 { + options.ScreenCanvasSizeOptions = expandGridLayoutScreenCanvasSizeOptions(v) + } + + return options +} + +func expandGridLayoutScreenCanvasSizeOptions(tfList []interface{}) *quicksight.GridLayoutScreenCanvasSizeOptions { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + options := &quicksight.GridLayoutScreenCanvasSizeOptions{} + + if v, ok := tfMap["optimized_view_port_width"].(string); ok && v != "" { + options.OptimizedViewPortWidth = aws.String(v) + } + + return options +} + +func expandDefaultPaginatedLayoutConfiguration(tfList []interface{}) *quicksight.DefaultPaginatedLayoutConfiguration { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + config := &quicksight.DefaultPaginatedLayoutConfiguration{} + + if v, ok := tfMap["section_based"].([]interface{}); ok && len(v) > 0 { + config.SectionBased = expandDefaultSectionBasedLayoutConfiguration(v) + } + + return config +} + +func expandDefaultSectionBasedLayoutConfiguration(tfList []interface{}) *quicksight.DefaultSectionBasedLayoutConfiguration { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + config := &quicksight.DefaultSectionBasedLayoutConfiguration{} + + if v, ok := tfMap["canvas_size_options"].([]interface{}); ok && len(v) > 0 { + config.CanvasSizeOptions = expandSectionBasedLayoutCanvasSizeOptions(v) + } + + return config +} + +func expandSectionBasedLayoutCanvasSizeOptions(tfList []interface{}) *quicksight.SectionBasedLayoutCanvasSizeOptions { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + options := &quicksight.SectionBasedLayoutCanvasSizeOptions{} + + if v, ok := tfMap["paper_canvas_size_options"].([]interface{}); ok && len(v) > 0 { + options.PaperCanvasSizeOptions = expandSectionBasedLayoutPaperCanvasSizeOptions(v) + } + + return options +} + +func expandSectionBasedLayoutPaperCanvasSizeOptions(tfList []interface{}) *quicksight.SectionBasedLayoutPaperCanvasSizeOptions { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + options := &quicksight.SectionBasedLayoutPaperCanvasSizeOptions{} + + if v, ok := tfMap["paper_margin"].([]interface{}); ok && len(v) > 0 { + options.PaperMargin = expandSpacing(v) + } + if v, ok := tfMap["paper_orientation"].(string); ok && v != "" { + options.PaperOrientation = aws.String(v) + } + if v, ok := tfMap["paper_size"].(string); ok && v != "" { + options.PaperSize = aws.String(v) + } + + return options +} + +func expandSpacing(tfList []interface{}) *quicksight.Spacing { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + spacing := &quicksight.Spacing{} + + if v, ok := tfMap["bottom"].(string); ok && v != "" { + spacing.Bottom = aws.String(v) + } + + if v, ok := tfMap["left"].(string); ok && v != "" { + spacing.Left = aws.String(v) + } + + if v, ok := tfMap["right"].(string); ok && v != "" { + spacing.Right = aws.String(v) + } + + if v, ok := tfMap["top"].(string); ok && v != "" { + spacing.Top = aws.String(v) + } + + return spacing +} + +func expandSheetDefinition(tfMap map[string]interface{}) (*quicksight.SheetDefinition, error) { + if tfMap == nil { + return nil, nil + } + + sheet := &quicksight.SheetDefinition{} + + if v, ok := tfMap["sheet_id"].(string); ok && v != "" { + sheet.SheetId = aws.String(v) + } + if v, ok := tfMap["content_type"].(string); ok && v != "" { + sheet.ContentType = aws.String(v) + } + if v, ok := tfMap["description"].(string); ok && v != "" { + sheet.Description = aws.String(v) + } + if v, ok := tfMap["name"].(string); ok && v != "" { + sheet.Name = aws.String(v) + } + if v, ok := tfMap["title"].(string); ok && v != "" { + sheet.Title = aws.String(v) + } + if v, ok := tfMap["filter_controls"].([]interface{}); ok && len(v) > 0 { + sheet.FilterControls = expandFilterControls(v) + } + if v, ok := tfMap["layouts"].([]interface{}); ok && len(v) > 0 { + sheet.Layouts = expandLayouts(v) + } + if v, ok := tfMap["parameter_controls"].([]interface{}); ok && len(v) > 0 { + sheet.ParameterControls = expandParameterControls(v) + } + if v, ok := tfMap["sheet_control_layouts"].([]interface{}); ok && len(v) > 0 { + sheet.SheetControlLayouts = expandSheetControlLayouts(v) + } + if v, ok := tfMap["text_boxes"].([]interface{}); ok && len(v) > 0 { + sheet.TextBoxes = expandSheetTextBoxes(v) + } + if v, ok := tfMap["visuals"].([]interface{}); ok && len(v) > 0 { + value, err := expandVisuals(v) + if err != nil { + return nil, err + } + sheet.Visuals = value + } + + return sheet, nil +} + +func expandFilterControls(tfList []interface{}) []*quicksight.FilterControl { + if len(tfList) == 0 { + return nil + } + + var controls []*quicksight.FilterControl + for _, tfMapRaw := range tfList { + tfMap, ok := tfMapRaw.(map[string]interface{}) + if !ok { + continue + } + + control := expandFilterControl(tfMap) + if control == nil { + continue + } + + controls = append(controls, control) + } + + return controls +} + +func expandLayouts(tfList []interface{}) []*quicksight.Layout { + if len(tfList) == 0 { + return nil + } + + var layouts []*quicksight.Layout + for _, tfMapRaw := range tfList { + tfMap, ok := tfMapRaw.(map[string]interface{}) + if !ok { + continue + } + + layout := expandLayout(tfMap) + if layout == nil { + continue + } + + layouts = append(layouts, layout) + } + + return layouts +} + +func expandLayout(tfMap map[string]interface{}) *quicksight.Layout { + if tfMap == nil { + return nil + } + + layout := &quicksight.Layout{} + + if v, ok := tfMap["configuration"].([]interface{}); ok && len(v) > 0 { + layout.Configuration = expandLayoutConfiguration(v) + } + + return layout +} + +func expandLayoutConfiguration(tfList []interface{}) *quicksight.LayoutConfiguration { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + config := &quicksight.LayoutConfiguration{} + + if v, ok := tfMap["free_form_layout"].([]interface{}); ok && len(v) > 0 { + config.FreeFormLayout = expandFreeFormLayoutConfiguration(v) + } + if v, ok := tfMap["grid_layout"].([]interface{}); ok && len(v) > 0 { + config.GridLayout = expandGridLayoutConfiguration(v) + } + if v, ok := tfMap["section_based_layout"].([]interface{}); ok && len(v) > 0 { + config.SectionBasedLayout = expandSectionBasedLayoutConfiguration(v) + } + + return config +} + +func expandFreeFormLayoutConfiguration(tfList []interface{}) *quicksight.FreeFormLayoutConfiguration { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + config := &quicksight.FreeFormLayoutConfiguration{} + + if v, ok := tfMap["elements"].([]interface{}); ok && len(v) > 0 { + config.Elements = expandFreeFormLayoutElements(v) + } + if v, ok := tfMap["canvas_size_options"].([]interface{}); ok && len(v) > 0 { + config.CanvasSizeOptions = expandFreeFormLayoutCanvasSizeOptions(v) + } + + return config +} + +func expandFreeFormLayoutElements(tfList []interface{}) []*quicksight.FreeFormLayoutElement { + if len(tfList) == 0 { + return nil + } + + var layouts []*quicksight.FreeFormLayoutElement + for _, tfMapRaw := range tfList { + tfMap, ok := tfMapRaw.(map[string]interface{}) + if !ok { + continue + } + + layout := expandFreeFormLayoutElement(tfMap) + if layout == nil { + continue + } + + layouts = append(layouts, layout) + } + + return layouts +} + +func expandFreeFormLayoutElement(tfMap map[string]interface{}) *quicksight.FreeFormLayoutElement { + if tfMap == nil { + return nil + } + + layout := &quicksight.FreeFormLayoutElement{} + + if v, ok := tfMap["element_id"].(string); ok && v != "" { + layout.ElementId = aws.String(v) + } + if v, ok := tfMap["element_type"].(string); ok && v != "" { + layout.ElementType = aws.String(v) + } + if v, ok := tfMap["height"].(string); ok && v != "" { + layout.Height = aws.String(v) + } + if v, ok := tfMap["width"].(string); ok && v != "" { + layout.Width = aws.String(v) + } + if v, ok := tfMap["x_axis_location"].(string); ok && v != "" { + layout.XAxisLocation = aws.String(v) + } + if v, ok := tfMap["y_axis_location"].(string); ok && v != "" { + layout.YAxisLocation = aws.String(v) + } + if v, ok := tfMap["visibility"].(string); ok && v != "" { + layout.Visibility = aws.String(v) + } + if v, ok := tfMap["background_style"].([]interface{}); ok && len(v) > 0 { + layout.BackgroundStyle = expandFreeFormLayoutElementBackgroundStyle(v) + } + if v, ok := tfMap["border_style"].([]interface{}); ok && len(v) > 0 { + layout.BorderStyle = expandFreeFormLayoutElementBorderStyle(v) + } + if v, ok := tfMap["loading_animation"].([]interface{}); ok && len(v) > 0 { + layout.LoadingAnimation = expandLoadingAnimation(v) + } + if v, ok := tfMap["rendering_rules"].([]interface{}); ok && len(v) > 0 { + layout.RenderingRules = expandSheetElementRenderingRules(v) + } + if v, ok := tfMap["selected_border_style"].([]interface{}); ok && len(v) > 0 { + layout.SelectedBorderStyle = expandFreeFormLayoutElementBorderStyle(v) + } + + return layout +} + +func expandFreeFormLayoutElementBackgroundStyle(tfList []interface{}) *quicksight.FreeFormLayoutElementBackgroundStyle { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + config := &quicksight.FreeFormLayoutElementBackgroundStyle{} + + if v, ok := tfMap["color"].(string); ok && v != "" { + config.Color = aws.String(v) + } + if v, ok := tfMap["visibility"].(string); ok && v != "" { + config.Visibility = aws.String(v) + } + return config +} + +func expandFreeFormLayoutElementBorderStyle(tfList []interface{}) *quicksight.FreeFormLayoutElementBorderStyle { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + config := &quicksight.FreeFormLayoutElementBorderStyle{} + + if v, ok := tfMap["color"].(string); ok && v != "" { + config.Color = aws.String(v) + } + if v, ok := tfMap["visibility"].(string); ok && v != "" { + config.Visibility = aws.String(v) + } + return config +} + +func expandLoadingAnimation(tfList []interface{}) *quicksight.LoadingAnimation { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + config := &quicksight.LoadingAnimation{} + + if v, ok := tfMap["visibility"].(string); ok && v != "" { + config.Visibility = aws.String(v) + } + return config +} + +func expandSheetElementRenderingRules(tfList []interface{}) []*quicksight.SheetElementRenderingRule { + if len(tfList) == 0 { + return nil + } + + var rules []*quicksight.SheetElementRenderingRule + for _, tfMapRaw := range tfList { + tfMap, ok := tfMapRaw.(map[string]interface{}) + if !ok { + continue + } + + rule := expandSheetElementRenderingRule(tfMap) + if rule == nil { + continue + } + + rules = append(rules, rule) + } + + return rules +} + +func expandSheetElementRenderingRule(tfMap map[string]interface{}) *quicksight.SheetElementRenderingRule { + if tfMap == nil { + return nil + } + + layout := &quicksight.SheetElementRenderingRule{} + + if v, ok := tfMap["expression"].(string); ok && v != "" { + layout.Expression = aws.String(v) + } + if v, ok := tfMap["configuration_overrides"].([]interface{}); ok && len(v) > 0 { + layout.ConfigurationOverrides = expandSheetElementConfigurationOverrides(v) + } + + return layout +} + +func expandSheetElementConfigurationOverrides(tfList []interface{}) *quicksight.SheetElementConfigurationOverrides { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + config := &quicksight.SheetElementConfigurationOverrides{} + + if v, ok := tfMap["visibility"].(string); ok && v != "" { + config.Visibility = aws.String(v) + } + return config +} + +func expandGridLayoutConfiguration(tfList []interface{}) *quicksight.GridLayoutConfiguration { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + config := &quicksight.GridLayoutConfiguration{} + + if v, ok := tfMap["elements"].([]interface{}); ok && len(v) > 0 { + config.Elements = expandGridLayoutElements(v) + } + if v, ok := tfMap["canvas_size_options"].([]interface{}); ok && len(v) > 0 { + config.CanvasSizeOptions = expandGridLayoutCanvasSizeOptions(v) + } + + return config +} + +func expandGridLayoutElements(tfList []interface{}) []*quicksight.GridLayoutElement { + if len(tfList) == 0 { + return nil + } + + var layouts []*quicksight.GridLayoutElement + for _, tfMapRaw := range tfList { + tfMap, ok := tfMapRaw.(map[string]interface{}) + if !ok { + continue + } + + layout := expandGridLayoutElement(tfMap) + if layout == nil { + continue + } + + layouts = append(layouts, layout) + } + + return layouts +} + +func expandGridLayoutElement(tfMap map[string]interface{}) *quicksight.GridLayoutElement { + if tfMap == nil { + return nil + } + + layout := &quicksight.GridLayoutElement{} + + if v, ok := tfMap["element_id"].(string); ok && v != "" { + layout.ElementId = aws.String(v) + } + if v, ok := tfMap["element_type"].(string); ok && v != "" { + layout.ElementType = aws.String(v) + } + if v, ok := tfMap["column_span"].(int64); ok { + layout.ColumnSpan = aws.Int64(v) + } + if v, ok := tfMap["row_span"].(int64); ok { + layout.RowSpan = aws.Int64(v) + } + if v, ok := tfMap["column_index"].(int64); ok { + layout.ColumnIndex = aws.Int64(v) + } + if v, ok := tfMap["row_index"].(int64); ok { + layout.RowIndex = aws.Int64(v) + } + + return layout +} + +func expandSectionBasedLayoutConfiguration(tfList []interface{}) *quicksight.SectionBasedLayoutConfiguration { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + config := &quicksight.SectionBasedLayoutConfiguration{} + + if v, ok := tfMap["body_sections"].([]interface{}); ok && len(v) > 0 { + config.BodySections = expandBodySectionConfigurations(v) + } + if v, ok := tfMap["canvas_size_options"].([]interface{}); ok && len(v) > 0 { + config.CanvasSizeOptions = expandSectionBasedLayoutCanvasSizeOptions(v) + } + if v, ok := tfMap["footer_sections"].([]interface{}); ok && len(v) > 0 { + config.FooterSections = expandHeaderFooterSectionConfigurations(v) + } + if v, ok := tfMap["header_sections"].([]interface{}); ok && len(v) > 0 { + config.HeaderSections = expandHeaderFooterSectionConfigurations(v) + } + + return config +} + +func expandBodySectionConfigurations(tfList []interface{}) []*quicksight.BodySectionConfiguration { + if len(tfList) == 0 { + return nil + } + + var configs []*quicksight.BodySectionConfiguration + for _, tfMapRaw := range tfList { + tfMap, ok := tfMapRaw.(map[string]interface{}) + if !ok { + continue + } + + config := expandBodySectionConfiguration(tfMap) + if config == nil { + continue + } + + configs = append(configs, config) + } + + return configs +} + +func expandBodySectionConfiguration(tfMap map[string]interface{}) *quicksight.BodySectionConfiguration { + if tfMap == nil { + return nil + } + + config := &quicksight.BodySectionConfiguration{} + + if v, ok := tfMap["section_id"].(string); ok && v != "" { + config.SectionId = aws.String(v) + } + if v, ok := tfMap["content"].([]interface{}); ok && len(v) > 0 { + config.Content = expandBodySectionContent(v) + } + if v, ok := tfMap["page_break_configuration"].([]interface{}); ok && len(v) > 0 { + config.PageBreakConfiguration = expandSectionPageBreakConfiguration(v) + } + + if v, ok := tfMap["style"].([]interface{}); ok && len(v) > 0 { + config.Style = expandSectionStyle(v) + } + + return config +} + +func expandBodySectionContent(tfList []interface{}) *quicksight.BodySectionContent { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + config := &quicksight.BodySectionContent{} + + if v, ok := tfMap["layout"].([]interface{}); ok && len(v) > 0 { + config.Layout = expandSectionLayoutConfiguration(v) + } + + return config +} + +func expandSectionLayoutConfiguration(tfList []interface{}) *quicksight.SectionLayoutConfiguration { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + config := &quicksight.SectionLayoutConfiguration{} + + if v, ok := tfMap["free_form_layout"].([]interface{}); ok && len(v) > 0 { + config.FreeFormLayout = expandFreeFormSectionLayoutConfiguration(v) + } + + return config +} + +func expandFreeFormSectionLayoutConfiguration(tfList []interface{}) *quicksight.FreeFormSectionLayoutConfiguration { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + config := &quicksight.FreeFormSectionLayoutConfiguration{} + + if v, ok := tfMap["elements"].([]interface{}); ok && len(v) > 0 { + config.Elements = expandFreeFormLayoutElements(v) + } + + return config +} + +func expandSectionPageBreakConfiguration(tfList []interface{}) *quicksight.SectionPageBreakConfiguration { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + config := &quicksight.SectionPageBreakConfiguration{} + + if v, ok := tfMap["after"].([]interface{}); ok && len(v) > 0 { + config.After = expandSectionAfterPageBreak(v) + } + + return config +} + +func expandSectionAfterPageBreak(tfList []interface{}) *quicksight.SectionAfterPageBreak { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + config := &quicksight.SectionAfterPageBreak{} + + if v, ok := tfMap["status"].(string); ok && v != "" { + config.Status = aws.String(v) + } + + return config +} + +func expandSectionStyle(tfList []interface{}) *quicksight.SectionStyle { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + config := &quicksight.SectionStyle{} + + if v, ok := tfMap["height"].(string); ok && v != "" { + config.Height = aws.String(v) + } + if v, ok := tfMap["padding"].([]interface{}); ok && len(v) > 0 { + config.Padding = expandSpacing(v) + } + + return config +} + +func expandHeaderFooterSectionConfigurations(tfList []interface{}) []*quicksight.HeaderFooterSectionConfiguration { + if len(tfList) == 0 { + return nil + } + + var configs []*quicksight.HeaderFooterSectionConfiguration + for _, tfMapRaw := range tfList { + tfMap, ok := tfMapRaw.(map[string]interface{}) + if !ok { + continue + } + + config := expandHeaderFooterSectionConfiguration(tfMap) + if config == nil { + continue + } + + configs = append(configs, config) + } + + return configs +} + +func expandHeaderFooterSectionConfiguration(tfMap map[string]interface{}) *quicksight.HeaderFooterSectionConfiguration { + if tfMap == nil { + return nil + } + + config := &quicksight.HeaderFooterSectionConfiguration{} + + if v, ok := tfMap["section_id"].(string); ok && v != "" { + config.SectionId = aws.String(v) + } + if v, ok := tfMap["layout"].([]interface{}); ok && len(v) > 0 { + config.Layout = expandSectionLayoutConfiguration(v) + } + if v, ok := tfMap["style"].([]interface{}); ok && len(v) > 0 { + config.Style = expandSectionStyle(v) + } + + return config +} + +func expandParameterControls(tfList []interface{}) []*quicksight.ParameterControl { + if len(tfList) == 0 { + return nil + } + + var controls []*quicksight.ParameterControl + for _, tfMapRaw := range tfList { + tfMap, ok := tfMapRaw.(map[string]interface{}) + if !ok { + continue + } + + control := expandParameterControl(tfMap) + if control == nil { + continue + } + + controls = append(controls, control) + } + + return controls +} + +func expandSheetControlLayouts(tfList []interface{}) []*quicksight.SheetControlLayout { + if len(tfList) == 0 { + return nil + } + + var layouts []*quicksight.SheetControlLayout + for _, tfMapRaw := range tfList { + tfMap, ok := tfMapRaw.(map[string]interface{}) + if !ok { + continue + } + + layout := expandSheetControlLayout(tfMap) + if layout == nil { + continue + } + + layouts = append(layouts, layout) + } + + return layouts +} + +func expandSheetControlLayout(tfMap map[string]interface{}) *quicksight.SheetControlLayout { + if tfMap == nil { + return nil + } + + layout := &quicksight.SheetControlLayout{} + + if v, ok := tfMap["configuration"].([]interface{}); ok && len(v) > 0 { + layout.Configuration = expandSheetControlLayoutConfiguration(v) + } + + return layout +} + +func expandSheetControlLayoutConfiguration(tfList []interface{}) *quicksight.SheetControlLayoutConfiguration { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + config := &quicksight.SheetControlLayoutConfiguration{} + + if v, ok := tfMap["grid_layout"].([]interface{}); ok && len(v) > 0 { + config.GridLayout = expandGridLayoutConfiguration(v) + } + + return config +} + +func expandSheetTextBoxes(tfList []interface{}) []*quicksight.SheetTextBox { + if len(tfList) == 0 { + return nil + } + + var boxes []*quicksight.SheetTextBox + for _, tfMapRaw := range tfList { + tfMap, ok := tfMapRaw.(map[string]interface{}) + if !ok { + continue + } + + box := expandSheetTextBox(tfMap) + if box == nil { + continue + } + + boxes = append(boxes, box) + } + + return boxes +} + +func expandSheetTextBox(tfMap map[string]interface{}) *quicksight.SheetTextBox { + if tfMap == nil { + return nil + } + + box := &quicksight.SheetTextBox{} + + if v, ok := tfMap["sheet_text_box_id"].(string); ok && v != "" { + box.SheetTextBoxId = aws.String(v) + } + if v, ok := tfMap["content"].(string); ok && v != "" { + box.Content = aws.String(v) + } + + return box +} + +func expandVisuals(tfList []interface{}) ([]*quicksight.Visual, error) { + if len(tfList) == 0 { + return nil, nil + } + + var visuals []*quicksight.Visual + for _, tfMapRaw := range tfList { + tfMap, ok := tfMapRaw.(map[string]interface{}) + if !ok { + continue + } + + visual, err := expandVisual(tfMap) + if err != nil { + return nil, err + } + if visual == nil { + continue + } + + visuals = append(visuals, visual) + } + + return visuals, nil +} diff --git a/internal/service/quicksight/template_test.go b/internal/service/quicksight/template_test.go new file mode 100644 index 000000000000..14253669a1c1 --- /dev/null +++ b/internal/service/quicksight/template_test.go @@ -0,0 +1,268 @@ +package quicksight_test + +import ( + "context" + "errors" + "fmt" + "testing" + + "github.com/aws/aws-sdk-go/service/quicksight" + "github.com/hashicorp/aws-sdk-go-base/v2/awsv1shim/v2/tfawserr" + sdkacctest "github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" + "github.com/hashicorp/terraform-provider-aws/internal/acctest" + "github.com/hashicorp/terraform-provider-aws/internal/conns" + "github.com/hashicorp/terraform-provider-aws/internal/create" + tfquicksight "github.com/hashicorp/terraform-provider-aws/internal/service/quicksight" + "github.com/hashicorp/terraform-provider-aws/names" +) + +func TestAccQuickSightTemplate_basic(t *testing.T) { + ctx := acctest.Context(t) + + var template quicksight.Template + resourceName := "aws_quicksight_template.test" + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + rId := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { + acctest.PreCheck(ctx, t) + }, + ErrorCheck: acctest.ErrorCheck(t, quicksight.EndpointsID), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + CheckDestroy: testAccCheckTemplateDestroy(ctx), + Steps: []resource.TestStep{ + { + Config: testAccTemplateConfig_basic(rId, rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckTemplateExists(ctx, resourceName, &template), + resource.TestCheckResourceAttr(resourceName, "template_id", rId), + resource.TestCheckResourceAttr(resourceName, "name", rName), + resource.TestCheckResourceAttr(resourceName, "status", quicksight.ResourceStatusCreationSuccessful), + ), + }, + { + ResourceName: resourceName, + ImportState: false, + RefreshState: true, + }, + }, + }) +} + +func TestAccQuickSightTemplate_disappears(t *testing.T) { + ctx := acctest.Context(t) + + var template quicksight.Template + resourceName := "aws_quicksight_template.test" + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + rId := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { + acctest.PreCheck(ctx, t) + }, + ErrorCheck: acctest.ErrorCheck(t, quicksight.EndpointsID), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + CheckDestroy: testAccCheckTemplateDestroy(ctx), + Steps: []resource.TestStep{ + { + Config: testAccTemplateConfig_basic(rId, rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckTemplateExists(ctx, resourceName, &template), + acctest.CheckResourceDisappears(ctx, acctest.Provider, tfquicksight.ResourceTemplate(), resourceName), + ), + ExpectNonEmptyPlan: true, + }, + }, + }) +} + +func testAccCheckTemplateDestroy(ctx context.Context) resource.TestCheckFunc { + return func(s *terraform.State) error { + conn := acctest.Provider.Meta().(*conns.AWSClient).QuickSightConn() + + for _, rs := range s.RootModule().Resources { + if rs.Type != "aws_quicksight_template" { + continue + } + + output, err := tfquicksight.FindTemplateByID(ctx, conn, rs.Primary.ID) + if err != nil { + if tfawserr.ErrCodeEquals(err, quicksight.ErrCodeResourceNotFoundException) { + return nil + } + return err + } + + if output != nil { + return fmt.Errorf("QuickSight Template (%s) still exists", rs.Primary.ID) + } + } + + return nil + } +} + +func testAccCheckTemplateExists(ctx context.Context, name string, template *quicksight.Template) resource.TestCheckFunc { + return func(s *terraform.State) error { + rs, ok := s.RootModule().Resources[name] + if !ok { + return create.Error(names.QuickSight, create.ErrActionCheckingExistence, tfquicksight.ResNameTemplate, name, errors.New("not found")) + } + + if rs.Primary.ID == "" { + return create.Error(names.QuickSight, create.ErrActionCheckingExistence, tfquicksight.ResNameTemplate, name, errors.New("not set")) + } + + conn := acctest.Provider.Meta().(*conns.AWSClient).QuickSightConn() + output, err := tfquicksight.FindTemplateByID(ctx, conn, rs.Primary.ID) + + if err != nil { + return create.Error(names.QuickSight, create.ErrActionCheckingExistence, tfquicksight.ResNameTemplate, rs.Primary.ID, err) + } + + *template = *output + + return nil + } +} + +//func testAccCheckTemplateNotRecreated(before, after *quicksight.DescribeTemplateResponse) resource.TestCheckFunc { +// return func(s *terraform.State) error { +// if before, after := aws.StringValue(before.TemplateId), aws.StringValue(after.TemplateId); before != after { +// return create.Error(names.QuickSight, create.ErrActionCheckingNotRecreated, tfquicksight.ResNameTemplate, aws.StringValue(before.TemplateId), errors.New("recreated")) +// } +// +// return nil +// } +//} + +func testAccTemplateConfigBase(rId string, rName string) string { + return acctest.ConfigCompose( + testAccDataSetConfigBase(rId, rName), + fmt.Sprintf(` +resource "aws_quicksight_data_set" "test" { + data_set_id = %[1]q + name = %[2]q + import_mode = "SPICE" + + physical_table_map { + physical_table_map_id = %[1]q + s3_source { + data_source_arn = aws_quicksight_data_source.test.arn + input_columns { + name = "Column1" + type = "STRING" + } + input_columns { + name = "Column2" + type = "STRING" + } + upload_settings { + format = "JSON" + } + } + } + logical_table_map { + logical_table_map_id = %[1]q + alias = "Group1" + source { + physical_table_id = %[1]q + } + data_transforms { + cast_column_type_operation { + column_name = "Column2" + new_column_type = "INTEGER" + } + } + } + + lifecycle { + ignore_changes = [ + physical_table_map + ] + } +} +`, rId, rName)) +} + +func testAccTemplateConfig_basic(rId, rName string) string { + return acctest.ConfigCompose( + testAccTemplateConfigBase(rId, rName), + fmt.Sprintf(` +resource "aws_quicksight_template" "test" { + template_id = %[1]q + name = %[2]q + version_description = "test" + definition { + data_set_configuration { + data_set_schema { + column_schema_list { + name = "Column1" + data_type = "STRING" + } + column_schema_list { + name = "Column2" + data_type = "INTEGER" + } + } + placeholder = "1" + } + sheets { + title = "Test" + sheet_id = "Test1" + visuals { + custom_content_visual { + data_set_identifier = "1" + title { + format_text { + plain_text = "Test" + } + } + visual_id = "Test1" + } + } + visuals { + line_chart_visual { + visual_id = "LineChart" + title { + format_text { + plain_text = "Line Chart Test" + } + } + chart_configuration { + field_wells { + line_chart_aggregated_field_wells { + category { + categorical_dimension_field { + field_id = "1" + column { + data_set_identifier = "1" + column_name = "Column1" + } + } + } + values { + categorical_measure_field { + field_id = "2" + column { + data_set_identifier = "1" + column_name = "Column1" + } + aggregation_function = "COUNT" + } + } + } + } + } + + } + } + } + } +} +`, rId, rName)) +} diff --git a/internal/service/quicksight/visual.go b/internal/service/quicksight/visual.go new file mode 100644 index 000000000000..6ab55dbf34b7 --- /dev/null +++ b/internal/service/quicksight/visual.go @@ -0,0 +1,1480 @@ +package quicksight + +import ( + "regexp" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/quicksight" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" +) + +const customActionsMaxItems = 10 +const dimensionsFieldMaxItems = 200 +const referenceLinesMaxItems = 20 + +func visualsSchema() *schema.Schema { + return &schema.Schema{ // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_SheetControlLayout.html + Type: schema.TypeList, + MinItems: 1, + MaxItems: 30, + Optional: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "bar_chart_visual": barCharVisualSchema(), + "box_plot_visual": boxPlotVisualSchema(), + "combo_chart_visual": comboChartVisualSchema(), + "custom_content_visual": customContentVisualSchema(), + "empty_visual": emptyVisualSchema(), + "filled_map_visual": filledMapVisualSchema(), + "funnel_chart_visual": funnelChartVisualSchema(), + "gauge_chart_visual": gaugeChartVisualSchema(), + "geospatial_map_visual": geospatialMapVisualSchema(), + "heat_map_visual": heatMapVisualSchema(), + "histogram_visual": histogramVisualSchema(), + "insight_visual": insightVisualSchema(), + "kpi_visual": kpiVisualSchema(), + "line_chart_visual": lineChartVisualSchema(), + "pie_chart_visual": pieChartVisualSchema(), + "pivot_table_visual": pivotTableVisualSchema(), + "radar_chart_visual": radarChartVisualSchema(), + "sankey_diagram_visual": sankeyDiagramVisualSchema(), + "scatter_plot_visual": scatterPlotVisualSchema(), + "table_visual": tableVisualSchema(), + "tree_map_visual": treeMapVisualSchema(), + "waterfall_visual": waterfallVisualSchema(), + "word_cloud_visual": wordCloudVisualSchema(), + }, + }, + } +} + +func legendOptionsSchema() *schema.Schema { + return &schema.Schema{ // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_LegendOptions.html + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "height": { + Type: schema.TypeString, + Optional: true, + }, + "position": stringSchema(false, validation.StringInSlice(quicksight.LegendPosition_Values(), false)), + "title": labelOptionsSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_LabelOptions.html + "visibility": stringSchema(false, validation.StringInSlice(quicksight.Visibility_Values(), false)), + "width": { + Type: schema.TypeString, + Optional: true, + }, + }, + }, + } +} + +func tooltipOptionsSchema() *schema.Schema { + return &schema.Schema{ // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_TooltipOptions.html + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "field_base_tooltip": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_FieldBasedTooltip.html + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "aggregation_visibility": stringSchema(false, validation.StringInSlice(quicksight.Visibility_Values(), false)), + "tooltip_fields": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_TooltipItem.html + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 100, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "column_tooltip_item": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_ColumnTooltipItem.html + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "column": columnSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_ColumnIdentifier.html + "aggregation": aggregationFunctionSchema(false), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_AggregationFunction.html + "label": { + Type: schema.TypeString, + Optional: true, + }, + "visibility": stringSchema(false, validation.StringInSlice(quicksight.Visibility_Values(), false)), + }, + }, + }, + "field_tooltip_item": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_FieldTooltipItem.html + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "field_id": stringSchema(true, validation.StringLenBetween(1, 512)), + "label": { + Type: schema.TypeString, + Optional: true, + }, + "visibility": stringSchema(false, validation.StringInSlice(quicksight.Visibility_Values(), false)), + }, + }, + }, + }, + }, + }, + "tooltip_title_type": stringSchema(false, validation.StringInSlice(quicksight.TooltipTitleType_Values(), false)), + }, + }, + }, + "selected_tooltip_type": stringSchema(false, validation.StringInSlice(quicksight.SelectedTooltipType_Values(), false)), + "tooltip_visibility": stringSchema(false, validation.StringInSlice(quicksight.Visibility_Values(), false)), + }, + }, + } +} + +func visualPaletteSchema() *schema.Schema { + return &schema.Schema{ // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_VisualPalette.html + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "chart_color": stringSchema(false, validation.StringMatch(regexp.MustCompile(`^#[A-F0-9]{6}$`), "")), + "color_map": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_DataPathColor.html + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 5000, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "color": stringSchema(true, validation.StringMatch(regexp.MustCompile(`^#[A-F0-9]{6}$`), "")), + "element": dataPathValueSchema(1), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_DataPathValue.html + "time_granularity": stringSchema(false, validation.StringInSlice(quicksight.TimeGranularity_Values(), false)), + }, + }, + }, + }, + }, + } +} + +func dataPathValueSchema(maxItems int) *schema.Schema { + return &schema.Schema{ // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_DataPathValue.html + Type: schema.TypeList, + Required: true, + MinItems: 1, + MaxItems: maxItems, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "field_id": stringSchema(true, validation.StringLenBetween(1, 512)), + "field_value": stringSchema(true, validation.StringLenBetween(1, 2048)), + }, + }, + } +} + +func columnHierarchiesSchema() *schema.Schema { + return &schema.Schema{ // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_ColumnHierarchy.html + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 2, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "date_time_hierarchy": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_DateTimeHierarchy.html + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "hierarchy_id": stringSchema(true, validation.StringLenBetween(1, 512)), + "drill_down_filters": drillDownFilterSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_DrillDownFilter.html + }, + }, + }, + "explicit_hierarchy": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_ExplicitHierarchy.html + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "columns": { + Type: schema.TypeList, + Required: true, + MinItems: 2, + MaxItems: 10, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_ColumnIdentifier.html + "column_name": stringSchema(true, validation.StringLenBetween(1, 128)), + "data_set_identifier": stringSchema(true, validation.StringLenBetween(1, 2048)), + }, + }, + }, + "hierarchy_id": stringSchema(true, validation.StringLenBetween(1, 512)), + "drill_down_filters": drillDownFilterSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_DrillDownFilter.html + }, + }, + }, + "predefined_hierarchy": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_PredefinedHierarchy.html + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "columns": { + Type: schema.TypeList, + Required: true, + MinItems: 1, + MaxItems: 10, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_ColumnIdentifier.html + "column_name": stringSchema(true, validation.StringLenBetween(1, 128)), + "data_set_identifier": stringSchema(true, validation.StringLenBetween(1, 2048)), + }, + }, + }, + "hierarchy_id": stringSchema(true, validation.StringLenBetween(1, 512)), + "drill_down_filters": drillDownFilterSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_DrillDownFilter.html + }, + }, + }, + }, + }, + } +} + +func visualSubtitleLabelOptionsSchema() *schema.Schema { + return &schema.Schema{ // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_VisualSubtitleLabelOptions.html + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "format_text": longFormatTextSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_LongFormatText.html + "visibility": stringSchema(false, validation.StringInSlice(quicksight.Visibility_Values(), false)), + }, + }, + } +} + +func longFormatTextSchema() *schema.Schema { + return &schema.Schema{ // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_LongFormatText.html + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "plain_text": { + Type: schema.TypeString, + Optional: true, + ValidateFunc: validation.StringLenBetween(1, 1024), + }, + "rich_text": { + Type: schema.TypeString, + Optional: true, + ValidateFunc: validation.StringLenBetween(1, 2048), + }, + }, + }, + } +} + +func shortFormatTextSchema() *schema.Schema { + return &schema.Schema{ // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_ShortFormatText.html + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "plain_text": { + Type: schema.TypeString, + Optional: true, + ValidateFunc: validation.StringLenBetween(1, 512), + }, + "rich_text": { + Type: schema.TypeString, + Optional: true, + ValidateFunc: validation.StringLenBetween(1, 1024), + }, + }, + }, + } +} + +func visualTitleLabelOptionsSchema() *schema.Schema { + return &schema.Schema{ // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_VisualTitleLabelOptions.html + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "format_text": shortFormatTextSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_ShortFormatText.html + "visibility": stringSchema(false, validation.StringInSlice(quicksight.Visibility_Values(), false)), + }, + }, + } +} + +func comparisonConfigurationSchema() *schema.Schema { + return &schema.Schema{ // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_ComparisonConfiguration.html + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "comparison_format": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_ComparisonFormatConfiguration.html + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "number_display_format_configuration": numberDisplayFormatConfigurationSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_NumberDisplayFormatConfiguration.html + "percentage_display_format_configuration": percentageDisplayFormatConfigurationSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_PercentageDisplayFormatConfiguration.html + }, + }, + }, + "comparison_method": stringSchema(false, validation.StringInSlice(quicksight.ComparisonMethod_Values(), false)), + }, + }, + } +} + +func colorScaleSchema() *schema.Schema { + return &schema.Schema{ // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_ColorScale.html + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "color_fill_type": stringSchema(true, validation.StringInSlice(quicksight.ColorFillType_Values(), false)), + "colors": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_DataColor.html + Type: schema.TypeList, + Required: true, + MinItems: 2, + MaxItems: 3, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "color": stringSchema(false, validation.StringMatch(regexp.MustCompile(`^#[A-F0-9]{6}$`), "")), + "data_value": { + Type: schema.TypeFloat, + Optional: true, + }, + }, + }, + }, + "null_value_color": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_DataColor.html + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "color": stringSchema(false, validation.StringMatch(regexp.MustCompile(`^#[A-F0-9]{6}$`), "")), + "data_value": { + Type: schema.TypeFloat, + Optional: true, + }, + }, + }, + }, + }, + }, + } +} + +func dataLabelOptionsSchema() *schema.Schema { + return &schema.Schema{ // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_DataLabelOptions.html + Type: schema.TypeList, + MinItems: 1, + MaxItems: 1, + Optional: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "category_label_visibility": stringSchema(false, validation.StringInSlice(quicksight.Visibility_Values(), false)), + "data_label_types": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_DataLabelType.html + Type: schema.TypeList, + MinItems: 1, + MaxItems: 100, + Optional: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "data_path_label_type": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_DataPathLabelType.html + Type: schema.TypeList, + MinItems: 1, + MaxItems: 1, + Optional: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "field_id": stringSchema(false, validation.StringLenBetween(1, 512)), + "field_value": stringSchema(false, validation.StringLenBetween(1, 2048)), + "visibility": stringSchema(false, validation.StringInSlice(quicksight.Visibility_Values(), false)), + }, + }, + }, + "field_label_type": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_FieldLabelType.html + Type: schema.TypeList, + MinItems: 1, + MaxItems: 1, + Optional: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "field_id": stringSchema(false, validation.StringLenBetween(1, 512)), + "visibility": stringSchema(false, validation.StringInSlice(quicksight.Visibility_Values(), false)), + }, + }, + }, + "maximum_label_type": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_MaximumLabelType.html + Type: schema.TypeList, + MinItems: 1, + MaxItems: 1, + Optional: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "visibility": stringSchema(false, validation.StringInSlice(quicksight.Visibility_Values(), false)), + }, + }, + }, + "minimum_label_type": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_MinimumLabelType.html + Type: schema.TypeList, + MinItems: 1, + MaxItems: 1, + Optional: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "visibility": stringSchema(false, validation.StringInSlice(quicksight.Visibility_Values(), false)), + }, + }, + }, + "range_ends_label_type": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_RangeEndsLabelType.html + Type: schema.TypeList, + MinItems: 1, + MaxItems: 1, + Optional: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "visibility": stringSchema(false, validation.StringInSlice(quicksight.Visibility_Values(), false)), + }, + }, + }, + }, + }, + }, + "label_color": stringSchema(false, validation.StringMatch(regexp.MustCompile(`^#[A-F0-9]{6}$`), "")), + "label_content": stringSchema(false, validation.StringInSlice(quicksight.DataLabelContent_Values(), false)), + "label_font_configuration": fontConfigurationSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_FontConfiguration.html + "measure_label_visibility": stringSchema(false, validation.StringInSlice(quicksight.Visibility_Values(), false)), + "overlap": stringSchema(false, validation.StringInSlice(quicksight.DataLabelOverlap_Values(), false)), + "position": stringSchema(false, validation.StringInSlice(quicksight.DataLabelPosition_Values(), false)), + "visibility": stringSchema(false, validation.StringInSlice(quicksight.Visibility_Values(), false)), + }, + }, + } +} + +func expandVisual(tfMap map[string]interface{}) (*quicksight.Visual, error) { + if tfMap == nil { + return nil, nil + } + + visual := &quicksight.Visual{} + + if v, ok := tfMap["bar_chart_visual"].([]interface{}); ok && len(v) > 0 { + value, err := expandBarChartVisual(v) + if err != nil { + return nil, err + } + visual.BarChartVisual = value + } + if v, ok := tfMap["box_plot_visual"].([]interface{}); ok && len(v) > 0 { + value, err := expandBoxPlotVisual(v) + if err != nil { + return nil, err + } + visual.BoxPlotVisual = value + } + if v, ok := tfMap["combo_chart_visual"].([]interface{}); ok && len(v) > 0 { + value, err := expandComboChartVisual(v) + if err != nil { + return nil, err + } + visual.ComboChartVisual = value + } + if v, ok := tfMap["custom_content_visual"].([]interface{}); ok && len(v) > 0 { + value, err := expandCustomContentVisual(v) + if err != nil { + return nil, err + } + visual.CustomContentVisual = value + } + if v, ok := tfMap["empty_visual"].([]interface{}); ok && len(v) > 0 { + value, err := expandEmptyVisual(v) + if err != nil { + return nil, err + } + visual.EmptyVisual = value + } + if v, ok := tfMap["filled_map_visual"].([]interface{}); ok && len(v) > 0 { + value, err := expandFilledMapVisual(v) + if err != nil { + return nil, err + } + visual.FilledMapVisual = value + } + if v, ok := tfMap["funnel_chart_visual"].([]interface{}); ok && len(v) > 0 { + value, err := expandFunnelChartVisual(v) + if err != nil { + return nil, err + } + visual.FunnelChartVisual = value + } + if v, ok := tfMap["gauge_chart_visual"].([]interface{}); ok && len(v) > 0 { + value, err := expandGaugeChartVisual(v) + if err != nil { + return nil, err + } + visual.GaugeChartVisual = value + } + if v, ok := tfMap["geospatial_map_visual"].([]interface{}); ok && len(v) > 0 { + value, err := expandGeospatialMapVisual(v) + if err != nil { + return nil, err + } + visual.GeospatialMapVisual = value + } + if v, ok := tfMap["heat_map_visual"].([]interface{}); ok && len(v) > 0 { + value, err := expandHeatMapVisual(v) + if err != nil { + return nil, err + } + visual.HeatMapVisual = value + } + if v, ok := tfMap["histogram_visual"].([]interface{}); ok && len(v) > 0 { + value, err := expandHistogramVisual(v) + if err != nil { + return nil, err + } + visual.HistogramVisual = value + } + if v, ok := tfMap["insight_visual"].([]interface{}); ok && len(v) > 0 { + value, err := expandInsightVisual(v) + if err != nil { + return nil, err + } + visual.InsightVisual = value + } + if v, ok := tfMap["kpi_visual"].([]interface{}); ok && len(v) > 0 { + value, err := expandKPIVisual(v) + if err != nil { + return nil, err + } + visual.KPIVisual = value + } + if v, ok := tfMap["line_chart_visual"].([]interface{}); ok && len(v) > 0 { + value, err := expandLineChartVisual(v) + if err != nil { + return nil, err + } + visual.LineChartVisual = value + } + if v, ok := tfMap["pie_chart_visual"].([]interface{}); ok && len(v) > 0 { + value, err := expandPieChartVisual(v) + if err != nil { + return nil, err + } + visual.PieChartVisual = value + } + if v, ok := tfMap["pivot_table_visual"].([]interface{}); ok && len(v) > 0 { + value, err := expandPivotTableVisual(v) + if err != nil { + return nil, err + } + visual.PivotTableVisual = value + } + if v, ok := tfMap["radar_chart_visual"].([]interface{}); ok && len(v) > 0 { + value, err := expandRadarChartVisual(v) + if err != nil { + return nil, err + } + visual.RadarChartVisual = value + } + if v, ok := tfMap["sankey_diagram_visual"].([]interface{}); ok && len(v) > 0 { + value, err := expandSankeyDiagramVisual(v) + if err != nil { + return nil, err + } + visual.SankeyDiagramVisual = value + } + if v, ok := tfMap["scatter_plot_visual"].([]interface{}); ok && len(v) > 0 { + value, err := expandScatterPlotVisual(v) + if err != nil { + return nil, err + } + visual.ScatterPlotVisual = value + } + if v, ok := tfMap["table_visual"].([]interface{}); ok && len(v) > 0 { + value, err := expandTableVisual(v) + if err != nil { + return nil, err + } + visual.TableVisual = value + } + if v, ok := tfMap["tree_map_visual"].([]interface{}); ok && len(v) > 0 { + value, err := expandTreeMapVisual(v) + if err != nil { + return nil, err + } + visual.TreeMapVisual = value + } + if v, ok := tfMap["waterfall_visual"].([]interface{}); ok && len(v) > 0 { + value, err := expandWaterfallVisual(v) + if err != nil { + return nil, err + } + visual.WaterfallVisual = value + } + if v, ok := tfMap["word_cloud_visual"].([]interface{}); ok && len(v) > 0 { + value, err := expandWordCloudVisual(v) + if err != nil { + return nil, err + } + visual.WordCloudVisual = value + } + + return visual, nil +} + +func expandDataLabelOptions(tfList []interface{}) *quicksight.DataLabelOptions { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + options := &quicksight.DataLabelOptions{} + + if v, ok := tfMap["category_label_visibility"].(string); ok && v != "" { + options.CategoryLabelVisibility = aws.String(v) + } + if v, ok := tfMap["label_color"].(string); ok && v != "" { + options.LabelColor = aws.String(v) + } + if v, ok := tfMap["label_content"].(string); ok && v != "" { + options.LabelContent = aws.String(v) + } + if v, ok := tfMap["measure_label_visibility"].(string); ok && v != "" { + options.MeasureLabelVisibility = aws.String(v) + } + if v, ok := tfMap["overlap"].(string); ok && v != "" { + options.Overlap = aws.String(v) + } + if v, ok := tfMap["position"].(string); ok && v != "" { + options.Position = aws.String(v) + } + if v, ok := tfMap["visibility"].(string); ok && v != "" { + options.Visibility = aws.String(v) + } + + if v, ok := tfMap["data_label_types"].([]interface{}); ok && len(v) > 0 { + options.DataLabelTypes = expandDataLabelTypes(v) + } + if v, ok := tfMap["label_font_configuration"].([]interface{}); ok && len(v) > 0 { + options.LabelFontConfiguration = expandFontConfiguration(v) + } + + return options +} + +func expandDataLabelTypes(tfList []interface{}) []*quicksight.DataLabelType { + if len(tfList) == 0 { + return nil + } + + var options []*quicksight.DataLabelType + for _, tfMapRaw := range tfList { + tfMap, ok := tfMapRaw.(map[string]interface{}) + if !ok { + continue + } + + opts := expandDataLabelType(tfMap) + if opts == nil { + continue + } + + options = append(options, opts) + } + + return options +} + +func expandDataLabelType(tfMap map[string]interface{}) *quicksight.DataLabelType { + if tfMap == nil { + return nil + } + + options := &quicksight.DataLabelType{} + + if v, ok := tfMap["data_path_label_type"].([]interface{}); ok && len(v) > 0 { + options.DataPathLabelType = expandDataPathLabelType(v) + } + if v, ok := tfMap["field_label_type"].([]interface{}); ok && len(v) > 0 { + options.FieldLabelType = expandFieldLabelType(v) + } + if v, ok := tfMap["maximum_label_type"].([]interface{}); ok && len(v) > 0 { + options.MaximumLabelType = expandMaximumLabelType(v) + } + if v, ok := tfMap["minimum_label_type"].([]interface{}); ok && len(v) > 0 { + options.MinimumLabelType = expandMinimumLabelType(v) + } + if v, ok := tfMap["range_ends_label_type"].([]interface{}); ok && len(v) > 0 { + options.RangeEndsLabelType = expandRangeEndsLabelType(v) + } + + return options +} + +func expandDataPathLabelType(tfList []interface{}) *quicksight.DataPathLabelType { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + options := &quicksight.DataPathLabelType{} + + if v, ok := tfMap["field_id"].(string); ok && v != "" { + options.FieldId = aws.String(v) + } + if v, ok := tfMap["field_value"].(string); ok && v != "" { + options.FieldValue = aws.String(v) + } + if v, ok := tfMap["visibility"].(string); ok && v != "" { + options.Visibility = aws.String(v) + } + + return options +} + +func expandFieldLabelType(tfList []interface{}) *quicksight.FieldLabelType { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + options := &quicksight.FieldLabelType{} + + if v, ok := tfMap["field_id"].(string); ok && v != "" { + options.FieldId = aws.String(v) + } + if v, ok := tfMap["visibility"].(string); ok && v != "" { + options.Visibility = aws.String(v) + } + + return options +} + +func expandMaximumLabelType(tfList []interface{}) *quicksight.MaximumLabelType { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + options := &quicksight.MaximumLabelType{} + + if v, ok := tfMap["visibility"].(string); ok && v != "" { + options.Visibility = aws.String(v) + } + + return options +} + +func expandMinimumLabelType(tfList []interface{}) *quicksight.MinimumLabelType { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + options := &quicksight.MinimumLabelType{} + + if v, ok := tfMap["visibility"].(string); ok && v != "" { + options.Visibility = aws.String(v) + } + + return options +} + +func expandRangeEndsLabelType(tfList []interface{}) *quicksight.RangeEndsLabelType { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + options := &quicksight.RangeEndsLabelType{} + + if v, ok := tfMap["visibility"].(string); ok && v != "" { + options.Visibility = aws.String(v) + } + + return options +} + +func expandLegendOptions(tfList []interface{}) *quicksight.LegendOptions { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + options := &quicksight.LegendOptions{} + + if v, ok := tfMap["height"].(string); ok && v != "" { + options.Height = aws.String(v) + } + if v, ok := tfMap["position"].(string); ok && v != "" { + options.Position = aws.String(v) + } + if v, ok := tfMap["visibility"].(string); ok && v != "" { + options.Visibility = aws.String(v) + } + if v, ok := tfMap["width"].(string); ok && v != "" { + options.Width = aws.String(v) + } + if v, ok := tfMap["title"].([]interface{}); ok && len(v) > 0 { + options.Title = expandLabelOptions(v) + } + + return options +} + +func expandTooltipOptions(tfList []interface{}) *quicksight.TooltipOptions { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + options := &quicksight.TooltipOptions{} + + if v, ok := tfMap["selected_tooltip_type"].(string); ok && v != "" { + options.SelectedTooltipType = aws.String(v) + } + if v, ok := tfMap["tooltip_visibility"].(string); ok && v != "" { + options.TooltipVisibility = aws.String(v) + } + if v, ok := tfMap["field_base_tooltip"].([]interface{}); ok && len(v) > 0 { + options.FieldBasedTooltip = expandFieldBasedTooltip(v) + } + + return options +} + +func expandFieldBasedTooltip(tfList []interface{}) *quicksight.FieldBasedTooltip { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + options := &quicksight.FieldBasedTooltip{} + + if v, ok := tfMap["aggregation_visibility"].(string); ok && v != "" { + options.AggregationVisibility = aws.String(v) + } + if v, ok := tfMap["tooltip_title_type"].(string); ok && v != "" { + options.TooltipTitleType = aws.String(v) + } + if v, ok := tfMap["tooltip_fields"].([]interface{}); ok && len(v) > 0 { + options.TooltipFields = expandTooltipItems(v) + } + + return options +} + +func expandTooltipItems(tfList []interface{}) []*quicksight.TooltipItem { + if len(tfList) == 0 { + return nil + } + + var items []*quicksight.TooltipItem + for _, tfMapRaw := range tfList { + tfMap, ok := tfMapRaw.(map[string]interface{}) + if !ok { + continue + } + + item := expandTooltipItem(tfMap) + if item == nil { + continue + } + + items = append(items, item) + } + + return items +} + +func expandTooltipItem(tfMap map[string]interface{}) *quicksight.TooltipItem { + if tfMap == nil { + return nil + } + + item := &quicksight.TooltipItem{} + + if v, ok := tfMap["column_tooltip_item"].([]interface{}); ok && len(v) > 0 { + item.ColumnTooltipItem = expandColumnTooltipItem(v) + } + if v, ok := tfMap["field_tooltip_item"].([]interface{}); ok && len(v) > 0 { + item.FieldTooltipItem = expandFieldTooltipItem(v) + } + + return item +} + +func expandColumnTooltipItem(tfList []interface{}) *quicksight.ColumnTooltipItem { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + item := &quicksight.ColumnTooltipItem{} + + if v, ok := tfMap["label"].(string); ok && v != "" { + item.Label = aws.String(v) + } + if v, ok := tfMap["visibility"].(string); ok && v != "" { + item.Visibility = aws.String(v) + } + if v, ok := tfMap["column"].([]interface{}); ok && len(v) > 0 { + item.Column = expandColumnIdentifier(v) + } + if v, ok := tfMap["aggregation"].([]interface{}); ok && len(v) > 0 { + item.Aggregation = expandAggregationFunction(v) + } + + return item +} + +func expandFieldTooltipItem(tfList []interface{}) *quicksight.FieldTooltipItem { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + item := &quicksight.FieldTooltipItem{} + + if v, ok := tfMap["field_id"].(string); ok && v != "" { + item.FieldId = aws.String(v) + } + if v, ok := tfMap["label"].(string); ok && v != "" { + item.Label = aws.String(v) + } + if v, ok := tfMap["visibility"].(string); ok && v != "" { + item.Visibility = aws.String(v) + } + + return item +} + +func expandVisualPalette(tfList []interface{}) *quicksight.VisualPalette { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + config := &quicksight.VisualPalette{} + + if v, ok := tfMap["chart_color"].(string); ok && v != "" { + config.ChartColor = aws.String(v) + } + if v, ok := tfMap["color_map"].([]interface{}); ok && len(v) > 0 { + config.ColorMap = expandDataPathColors(v) + } + + return config +} + +func expandDataPathColors(tfList []interface{}) []*quicksight.DataPathColor { + if len(tfList) == 0 { + return nil + } + + var colors []*quicksight.DataPathColor + for _, tfMapRaw := range tfList { + tfMap, ok := tfMapRaw.(map[string]interface{}) + if !ok { + continue + } + + color := expandDataPathColor(tfMap) + if color == nil { + continue + } + + colors = append(colors, color) + } + + return colors +} + +func expandDataPathColor(tfMap map[string]interface{}) *quicksight.DataPathColor { + if tfMap == nil { + return nil + } + + color := &quicksight.DataPathColor{} + + if v, ok := tfMap["color"].(string); ok && v != "" { + color.Color = aws.String(v) + } + if v, ok := tfMap["time_granularity"].(string); ok && v != "" { + color.TimeGranularity = aws.String(v) + } + if v, ok := tfMap["element"].([]interface{}); ok && len(v) > 0 { + color.Element = expandDataPathValue(v) + } + + return color +} + +func expandDataPathValues(tfList []interface{}) []*quicksight.DataPathValue { + if len(tfList) == 0 { + return nil + } + + var values []*quicksight.DataPathValue + for _, tfMapRaw := range tfList { + tfMap, ok := tfMapRaw.(map[string]interface{}) + if !ok { + continue + } + + value := expandDataPathValueInternal(tfMap) + if value == nil { + continue + } + + values = append(values, value) + } + + return values +} + +func expandDataPathValueInternal(tfMap map[string]interface{}) *quicksight.DataPathValue { + if tfMap == nil { + return nil + } + + value := &quicksight.DataPathValue{} + + if v, ok := tfMap["field_id"].(string); ok && v != "" { + value.FieldId = aws.String(v) + } + if v, ok := tfMap["field_value"].(string); ok && v != "" { + value.FieldValue = aws.String(v) + } + + return value +} + +func expandDataPathValue(tfList []interface{}) *quicksight.DataPathValue { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + return expandDataPathValueInternal(tfMap) +} + +func expandColumnHierarchies(tfList []interface{}) ([]*quicksight.ColumnHierarchy, error) { + if len(tfList) == 0 { + return nil, nil + } + + var options []*quicksight.ColumnHierarchy + for _, tfMapRaw := range tfList { + tfMap, ok := tfMapRaw.(map[string]interface{}) + if !ok { + continue + } + + opts, err := expandColumnHierarchy(tfMap) + if err != nil { + return nil, err + } + if opts == nil { + continue + } + + options = append(options, opts) + } + + return options, nil +} + +func expandColumnHierarchy(tfMap map[string]interface{}) (*quicksight.ColumnHierarchy, error) { + if tfMap == nil { + return nil, nil + } + + options := &quicksight.ColumnHierarchy{} + + if v, ok := tfMap["date_time_hierarchy"].([]interface{}); ok && len(v) > 0 { + value, err := expandDateTimeHierarchy(v) + if err != nil { + return nil, err + } + options.DateTimeHierarchy = value + } + if v, ok := tfMap["explicit_hierarchy"].([]interface{}); ok && len(v) > 0 { + value, err := expandExplicitHierarchy(v) + if err != nil { + return nil, err + } + options.ExplicitHierarchy = value + } + if v, ok := tfMap["predefined_hierarchy"].([]interface{}); ok && len(v) > 0 { + value, err := expandPredefinedHierarchy(v) + if err != nil { + return nil, err + } + options.PredefinedHierarchy = value + } + + return options, nil +} + +func expandDateTimeHierarchy(tfList []interface{}) (*quicksight.DateTimeHierarchy, error) { + if len(tfList) == 0 || tfList[0] == nil { + return nil, nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil, nil + } + + config := &quicksight.DateTimeHierarchy{} + + if v, ok := tfMap["hierarchy_id"].(string); ok && v != "" { + config.HierarchyId = aws.String(v) + } + if v, ok := tfMap["drill_down_filters"].([]interface{}); ok && len(v) > 0 { + value, err := expandDrillDownFilters(v) + if err != nil { + return nil, err + } + config.DrillDownFilters = value + } + + return config, nil +} + +func expandExplicitHierarchy(tfList []interface{}) (*quicksight.ExplicitHierarchy, error) { + if len(tfList) == 0 || tfList[0] == nil { + return nil, nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil, nil + } + + config := &quicksight.ExplicitHierarchy{} + + if v, ok := tfMap["hierarchy_id"].(string); ok && v != "" { + config.HierarchyId = aws.String(v) + } + if v, ok := tfMap["columns"].([]interface{}); ok && len(v) > 0 { + config.Columns = expandColumnIdentifiers(v) + } + if v, ok := tfMap["drill_down_filters"].([]interface{}); ok && len(v) > 0 { + value, err := expandDrillDownFilters(v) + if err != nil { + return nil, err + } + config.DrillDownFilters = value + } + + return config, nil +} +func expandPredefinedHierarchy(tfList []interface{}) (*quicksight.PredefinedHierarchy, error) { + if len(tfList) == 0 || tfList[0] == nil { + return nil, nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil, nil + } + + config := &quicksight.PredefinedHierarchy{} + + if v, ok := tfMap["hierarchy_id"].(string); ok && v != "" { + config.HierarchyId = aws.String(v) + } + if v, ok := tfMap["columns"].([]interface{}); ok && len(v) > 0 { + config.Columns = expandColumnIdentifiers(v) + } + if v, ok := tfMap["drill_down_filters"].([]interface{}); ok && len(v) > 0 { + value, err := expandDrillDownFilters(v) + if err != nil { + return nil, err + } + config.DrillDownFilters = value + } + + return config, nil +} + +func expandVisualSubtitleLabelOptions(tfList []interface{}) *quicksight.VisualSubtitleLabelOptions { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + options := &quicksight.VisualSubtitleLabelOptions{} + + if v, ok := tfMap["visibility"].(string); ok && v != "" { + options.Visibility = aws.String(v) + } + if v, ok := tfMap["format_text"].([]interface{}); ok && len(v) > 0 { + options.FormatText = expandLongFormatText(v) + } + + return options +} + +func expandLongFormatText(tfList []interface{}) *quicksight.LongFormatText { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + format := &quicksight.LongFormatText{} + + if v, ok := tfMap["plain_text"].(string); ok && v != "" { + format.PlainText = aws.String(v) + } + if v, ok := tfMap["rich_text"].(string); ok && v != "" { + format.RichText = aws.String(v) + } + + return format +} + +func expandVisualTitleLabelOptions(tfList []interface{}) *quicksight.VisualTitleLabelOptions { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + options := &quicksight.VisualTitleLabelOptions{} + + if v, ok := tfMap["visibility"].(string); ok && v != "" { + options.Visibility = aws.String(v) + } + if v, ok := tfMap["format_text"].([]interface{}); ok && len(v) > 0 { + options.FormatText = expandShortFormatText(v) + } + + return options +} + +func expandShortFormatText(tfList []interface{}) *quicksight.ShortFormatText { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + format := &quicksight.ShortFormatText{} + + if v, ok := tfMap["plain_text"].(string); ok && v != "" { + format.PlainText = aws.String(v) + } + if v, ok := tfMap["rich_text"].(string); ok && v != "" { + format.RichText = aws.String(v) + } + + return format +} + +func expandComparisonConfiguration(tfList []interface{}) *quicksight.ComparisonConfiguration { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + config := &quicksight.ComparisonConfiguration{} + + if v, ok := tfMap["config"].(string); ok && v != "" { + config.ComparisonMethod = aws.String(v) + } + if v, ok := tfMap["comparison_format"].([]interface{}); ok && len(v) > 0 { + config.ComparisonFormat = expandComparisonFormatConfiguration(v) + } + + return config +} + +func expandColorScale(tfList []interface{}) *quicksight.ColorScale { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + config := &quicksight.ColorScale{} + + if v, ok := tfMap["color_fill_type"].(string); ok && v != "" { + config.ColorFillType = aws.String(v) + } + if v, ok := tfMap["colors"].([]interface{}); ok && len(v) > 0 { + config.Colors = expandDataColors(v) + } + if v, ok := tfMap["null_value_color"].([]interface{}); ok && len(v) > 0 { + config.NullValueColor = expandDataColor(v) + } + + return config +} + +func expandDataColor(tfList []interface{}) *quicksight.DataColor { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + return expandDataColorInternal(tfMap) +} + +func expandDataColorInternal(tfMap map[string]interface{}) *quicksight.DataColor { + color := &quicksight.DataColor{} + + if v, ok := tfMap["color"].(string); ok && v != "" { + color.Color = aws.String(v) + } + if v, ok := tfMap["data_value"].(float64); ok { + color.DataValue = aws.Float64(v) + } + + return color +} + +func expandDataColors(tfList []interface{}) []*quicksight.DataColor { + if len(tfList) == 0 { + return nil + } + + var colors []*quicksight.DataColor + for _, tfMapRaw := range tfList { + tfMap, ok := tfMapRaw.(map[string]interface{}) + if !ok { + continue + } + + color := expandDataColorInternal(tfMap) + if color == nil { + continue + } + + colors = append(colors, color) + } + + return colors +} diff --git a/internal/service/quicksight/visual_actions.go b/internal/service/quicksight/visual_actions.go new file mode 100644 index 000000000000..e64f49bd48a5 --- /dev/null +++ b/internal/service/quicksight/visual_actions.go @@ -0,0 +1,642 @@ +package quicksight + +import ( + "time" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/quicksight" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" + "github.com/hashicorp/terraform-provider-aws/internal/flex" + "github.com/hashicorp/terraform-provider-aws/internal/verify" +) + +func visualCustomActionsSchema(maxItems int) *schema.Schema { + return &schema.Schema{ // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_VisualCustomAction.html + Type: schema.TypeList, + MinItems: 1, + MaxItems: maxItems, + Optional: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "action_operations": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_VisualCustomActionOperation.html + Type: schema.TypeList, + MinItems: 1, + MaxItems: 2, + Required: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "filter_operation": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_CustomActionFilterOperation.html + Type: schema.TypeList, + MinItems: 1, + MaxItems: 1, + Optional: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "selected_fields_configuration": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_FilterOperationSelectedFieldsConfiguration.html + Type: schema.TypeList, + MinItems: 1, + MaxItems: 1, + Required: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "selected_field_option": stringSchema(false, validation.StringInSlice(quicksight.SelectedFieldOptions_Values(), false)), + "selected_fields": { + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 20, + Elem: &schema.Schema{ + Type: schema.TypeString, + ValidateFunc: validation.StringLenBetween(1, 512), + }, + }, + }, + }, + }, + "target_visuals_configuration": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_FilterOperationTargetVisualsConfiguration.html + Type: schema.TypeList, + MinItems: 1, + MaxItems: 1, + Required: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "same_sheet_target_visual_configuration": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_SameSheetTargetVisualConfiguration.html + Type: schema.TypeList, + MinItems: 1, + MaxItems: 1, + Optional: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "target_visual_option": stringSchema(false, validation.StringInSlice(quicksight.TargetVisualOptions_Values(), false)), + "target_visuals": { + Type: schema.TypeSet, + Optional: true, + MinItems: 1, + MaxItems: 30, + Elem: schema.TypeString, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + "navigation_operation": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_CustomActionNavigationOperation.html + Type: schema.TypeList, + MinItems: 1, + MaxItems: 1, + Optional: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "local_navigation_configuration": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_LocalNavigationConfiguration.html + Type: schema.TypeList, + MinItems: 1, + MaxItems: 1, + Optional: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "target_sheet_id": idSchema(), + }, + }, + }, + }, + }, + }, + "set_parameters_operation": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_CustomActionSetParametersOperation.html + Type: schema.TypeList, + MinItems: 1, + MaxItems: 1, + Optional: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "parameter_value_configurations": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_SetParameterValueConfiguration.html + Type: schema.TypeList, + MinItems: 1, + MaxItems: 200, + Required: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "destination_parameter_name": parameterNameSchema(true), + "value": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_DestinationParameterValueConfiguration.html + Type: schema.TypeList, + MinItems: 1, + MaxItems: 1, + Required: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "custom_values_configuration": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_CustomValuesConfiguration.html + Type: schema.TypeList, + MinItems: 1, + MaxItems: 1, + Optional: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "custom_values": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_CustomParameterValues.html + Type: schema.TypeList, + MinItems: 1, + MaxItems: 1, + Required: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "date_time_values": { + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 50000, + Elem: &schema.Schema{ + Type: schema.TypeString, + ValidateFunc: verify.ValidUTCTimestamp, + }, + }, + "decimal_values": { + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 50000, + Elem: &schema.Schema{ + Type: schema.TypeFloat, + }, + }, + "integer_values": { + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 50000, + Elem: &schema.Schema{ + Type: schema.TypeInt, + }, + }, + "string_values": { + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 50000, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + }, + }, + }, + }, + "include_null_value": { + Type: schema.TypeBool, + Optional: true, + }, + }, + }, + }, + "select_all_value_options": stringSchema(false, validation.StringInSlice(quicksight.SelectAllValueOptions_Values(), false)), + "source_field": stringSchema(false, validation.StringLenBetween(1, 2048)), + "source_parameter_name": { + Type: schema.TypeString, + Optional: true, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + "url_operation": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_CustomActionURLOperation.html + Type: schema.TypeList, + MinItems: 1, + MaxItems: 1, + Optional: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "url_target": stringSchema(true, validation.StringInSlice(quicksight.URLTargetConfiguration_Values(), false)), + "url_template": stringSchema(true, validation.StringLenBetween(1, 2048)), + }, + }, + }, + }, + }, + }, + "custom_action_id": idSchema(), + "name": stringSchema(true, validation.StringLenBetween(1, 256)), + "trigger": stringSchema(true, validation.StringInSlice(quicksight.VisualCustomActionTrigger_Values(), false)), + "status": stringSchema(true, validation.StringInSlice(quicksight.Status_Values(), false)), + }, + }, + } +} + +func expandVisualCustomActions(tfList []interface{}) ([]*quicksight.VisualCustomAction, error) { + if len(tfList) == 0 { + return nil, nil + } + + var actions []*quicksight.VisualCustomAction + for _, tfMapRaw := range tfList { + tfMap, ok := tfMapRaw.(map[string]interface{}) + if !ok { + continue + } + + action, err := expandVisualCustomAction(tfMap) + if err != nil { + return nil, err + } + if action == nil { + continue + } + + actions = append(actions, action) + } + + return actions, nil +} + +func expandVisualCustomAction(tfMap map[string]interface{}) (*quicksight.VisualCustomAction, error) { + if tfMap == nil { + return nil, nil + } + + action := &quicksight.VisualCustomAction{} + + if v, ok := tfMap["custom_action_id"].(string); ok && v != "" { + action.CustomActionId = aws.String(v) + } + if v, ok := tfMap["name"].(string); ok && v != "" { + action.Name = aws.String(v) + } + if v, ok := tfMap["trigger"].(string); ok && v != "" { + action.Trigger = aws.String(v) + } + if v, ok := tfMap["status"].(string); ok && v != "" { + action.Status = aws.String(v) + } + if v, ok := tfMap["action_operations"].([]interface{}); ok && len(v) > 0 { + value, err := expandVisualCustomActionOperations(v) + if err != nil { + return nil, err + } + action.ActionOperations = value + } + + return action, nil +} + +func expandVisualCustomActionOperations(tfList []interface{}) ([]*quicksight.VisualCustomActionOperation, error) { + if len(tfList) == 0 { + return nil, nil + } + + var actions []*quicksight.VisualCustomActionOperation + for _, tfMapRaw := range tfList { + tfMap, ok := tfMapRaw.(map[string]interface{}) + if !ok { + continue + } + + action, err := expandVisualCustomActionOperation(tfMap) + if err != nil { + return nil, err + } + if action == nil { + continue + } + + actions = append(actions, action) + } + + return actions, nil +} + +func expandVisualCustomActionOperation(tfMap map[string]interface{}) (*quicksight.VisualCustomActionOperation, error) { + if tfMap == nil { + return nil, nil + } + + action := &quicksight.VisualCustomActionOperation{} + + if v, ok := tfMap["filter_operation"].([]interface{}); ok && len(v) > 0 { + action.FilterOperation = expandCustomActionFilterOperation(v) + } + if v, ok := tfMap["navigation_operation"].([]interface{}); ok && len(v) > 0 { + action.NavigationOperation = expandCustomActionNavigationOperation(v) + } + if v, ok := tfMap["set_parameters_operation"].([]interface{}); ok && len(v) > 0 { + value, err := expandCustomActionSetParametersOperation(v) + if err != nil { + return nil, err + } + action.SetParametersOperation = value + } + if v, ok := tfMap["url_operation"].([]interface{}); ok && len(v) > 0 { + action.URLOperation = expandCustomActionURLOperation(v) + } + + return action, nil +} + +func expandCustomActionFilterOperation(tfList []interface{}) *quicksight.CustomActionFilterOperation { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + action := &quicksight.CustomActionFilterOperation{} + + if v, ok := tfMap["selected_fields_configuration"].([]interface{}); ok && len(v) > 0 { + action.SelectedFieldsConfiguration = expandFilterOperationSelectedFieldsConfiguration(v) + } + if v, ok := tfMap["target_visuals_configuration"].([]interface{}); ok && len(v) > 0 { + action.TargetVisualsConfiguration = expandFilterOperationTargetVisualsConfiguration(v) + } + + return action +} + +func expandFilterOperationSelectedFieldsConfiguration(tfList []interface{}) *quicksight.FilterOperationSelectedFieldsConfiguration { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + config := &quicksight.FilterOperationSelectedFieldsConfiguration{} + + if v, ok := tfMap["selected_field_option"].(string); ok && v != "" { + config.SelectedFieldOptions = aws.String(v) + } + if v, ok := tfMap["selected_fields"].([]interface{}); ok { + config.SelectedFields = flex.ExpandStringList(v) + } + + return config +} + +func expandFilterOperationTargetVisualsConfiguration(tfList []interface{}) *quicksight.FilterOperationTargetVisualsConfiguration { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + config := &quicksight.FilterOperationTargetVisualsConfiguration{} + + if v, ok := tfMap["same_sheet_target_visual_configuration"].([]interface{}); ok && len(v) > 0 { + config.SameSheetTargetVisualConfiguration = expandSameSheetTargetVisualConfiguration(v) + } + + return config +} + +func expandSameSheetTargetVisualConfiguration(tfList []interface{}) *quicksight.SameSheetTargetVisualConfiguration { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + config := &quicksight.SameSheetTargetVisualConfiguration{} + + if v, ok := tfMap["target_visual_option"].(string); ok && v != "" { + config.TargetVisualOptions = aws.String(v) + } + if v, ok := tfMap["target_visuals"].(*schema.Set); ok { + config.TargetVisuals = flex.ExpandStringSet(v) + } + + return config +} + +func expandCustomActionNavigationOperation(tfList []interface{}) *quicksight.CustomActionNavigationOperation { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + action := &quicksight.CustomActionNavigationOperation{} + + if v, ok := tfMap["local_navigation_configuration"].([]interface{}); ok && len(v) > 0 { + action.LocalNavigationConfiguration = expandLocalNavigationConfiguration(v) + } + + return action +} + +func expandLocalNavigationConfiguration(tfList []interface{}) *quicksight.LocalNavigationConfiguration { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + config := &quicksight.LocalNavigationConfiguration{} + + if v, ok := tfMap["target_sheet_id"].(string); ok && v != "" { + config.TargetSheetId = aws.String(v) + } + return config +} + +func expandCustomActionSetParametersOperation(tfList []interface{}) (*quicksight.CustomActionSetParametersOperation, error) { + if len(tfList) == 0 || tfList[0] == nil { + return nil, nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil, nil + } + + action := &quicksight.CustomActionSetParametersOperation{} + + if v, ok := tfMap["parameter_value_configurations"].([]interface{}); ok && len(v) > 0 { + value, err := expandSetParameterValueConfigurations(v) + if err != nil { + return nil, err + } + action.ParameterValueConfigurations = value + } + + return action, nil +} + +func expandSetParameterValueConfigurations(tfList []interface{}) ([]*quicksight.SetParameterValueConfiguration, error) { + if len(tfList) == 0 { + return nil, nil + } + + var configs []*quicksight.SetParameterValueConfiguration + for _, tfMapRaw := range tfList { + tfMap, ok := tfMapRaw.(map[string]interface{}) + if !ok { + continue + } + + config, err := expandSetParameterValueConfiguration(tfMap) + if err != nil { + return nil, err + } + if config == nil { + continue + } + + configs = append(configs, config) + } + + return configs, nil +} + +func expandSetParameterValueConfiguration(tfMap map[string]interface{}) (*quicksight.SetParameterValueConfiguration, error) { + if tfMap == nil { + return nil, nil + } + + config := &quicksight.SetParameterValueConfiguration{} + + if v, ok := tfMap["destination_parameter_name"].(string); ok && v != "" { + config.DestinationParameterName = aws.String(v) + } + if v, ok := tfMap["value"].([]interface{}); ok && len(v) > 0 { + value, err := expandDestinationParameterValueConfiguration(v) + if err != nil { + return nil, err + } + config.Value = value + } + + return config, nil +} + +func expandDestinationParameterValueConfiguration(tfList []interface{}) (*quicksight.DestinationParameterValueConfiguration, error) { + if len(tfList) == 0 || tfList[0] == nil { + return nil, nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil, nil + } + + config := &quicksight.DestinationParameterValueConfiguration{} + + if v, ok := tfMap["custom_values_configuration"].([]interface{}); ok && len(v) > 0 { + value, err := expandCustomValuesConfiguration(v) + if err != nil { + return nil, err + } + config.CustomValuesConfiguration = value + } + if v, ok := tfMap["select_all_value_options"].(string); ok && v != "" { + config.SelectAllValueOptions = aws.String(v) + } + if v, ok := tfMap["source_field"].(string); ok && v != "" { + config.SourceField = aws.String(v) + } + if v, ok := tfMap["source_parameter_name"].(string); ok && v != "" { + config.SourceParameterName = aws.String(v) + } + + return config, nil +} + +func expandCustomValuesConfiguration(tfList []interface{}) (*quicksight.CustomValuesConfiguration, error) { + if len(tfList) == 0 || tfList[0] == nil { + return nil, nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil, nil + } + + config := &quicksight.CustomValuesConfiguration{} + + if v, ok := tfMap["custom_values"].([]interface{}); ok && len(v) > 0 { + value, err := expandCustomParameterValues(v) + if err != nil { + return nil, err + } + config.CustomValues = value + } + if v, ok := tfMap["include_null_value"].(bool); ok { + config.IncludeNullValue = aws.Bool(v) + } + + return config, nil +} + +func expandCustomParameterValues(tfList []interface{}) (*quicksight.CustomParameterValues, error) { + if len(tfList) == 0 || tfList[0] == nil { + return nil, nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil, nil + } + + config := &quicksight.CustomParameterValues{} + + if v, ok := tfMap["date_time_values"].([]interface{}); ok { + times, err := flex.ExpandStringTimeList(v, time.RFC3339) + if err != nil { + return nil, err + } + config.DateTimeValues = times + } + if v, ok := tfMap["decimal_values"].([]interface{}); ok { + config.DecimalValues = flex.ExpandFloat64List(v) + } + if v, ok := tfMap["integer_values"].([]interface{}); ok { + config.IntegerValues = flex.ExpandInt64List(v) + } + if v, ok := tfMap["string_values"].([]interface{}); ok { + config.StringValues = flex.ExpandStringList(v) + } + + return config, nil +} + +func expandCustomActionURLOperation(tfList []interface{}) *quicksight.CustomActionURLOperation { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + action := &quicksight.CustomActionURLOperation{} + + if v, ok := tfMap["url_target"].(string); ok && v != "" { + action.URLTarget = aws.String(v) + } + if v, ok := tfMap["url_template"].(string); ok && v != "" { + action.URLTemplate = aws.String(v) + } + + return action +} diff --git a/internal/service/quicksight/visual_bar_chart.go b/internal/service/quicksight/visual_bar_chart.go new file mode 100644 index 000000000000..7f2400cded40 --- /dev/null +++ b/internal/service/quicksight/visual_bar_chart.go @@ -0,0 +1,277 @@ +package quicksight + +import ( + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/quicksight" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" +) + +func barCharVisualSchema() *schema.Schema { + return &schema.Schema{ // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_BarChartVisual.html + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "visual_id": idSchema(), + "actions": visualCustomActionsSchema(customActionsMaxItems), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_VisualCustomAction.html + "chart_configuration": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_BarChartConfiguration.html + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "bars_arrangement": stringSchema(false, validation.StringInSlice(quicksight.BarsArrangement_Values(), false)), + "category_axis": axisDisplayOptionsSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_AxisDisplayOptions.html + "category_label_options": chartAxisLabelOptionsSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_ChartAxisLabelOptions.html + "color_label_options": chartAxisLabelOptionsSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_ChartAxisLabelOptions.html + "contribution_analysis_defaults": contributionAnalysisDefaultsSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_ContributionAnalysisDefault.html + "data_labels": dataLabelOptionsSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_DataLabelOptions.html + "field_wells": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_BarChartFieldWells.html + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "bar_chart_aggregated_field_wells": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_BarChartAggregatedFieldWells.html + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "category": dimensionFieldSchema(dimensionsFieldMaxItems), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_DimensionField.html + "colors": dimensionFieldSchema(dimensionsFieldMaxItems), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_DimensionField.html + "small_multiples": dimensionFieldSchema(1), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_DimensionField.html + "values": measureFieldSchema(200), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_MeasureField.html + }, + }, + }, + }, + }, + }, + "legend": legendOptionsSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_LegendOptions.html + "orientation": stringSchema(false, validation.StringInSlice(quicksight.BarChartOrientation_Values(), false)), + "reference_lines": referenceLineSchema(referenceLinesMaxItems), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_ReferenceLine.html + "small_multiples_options": smallMultiplesOptionsSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_SmallMultiplesOptions.html + "sort_configuration": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_BarChartSortConfiguration.html + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "category_items_limit": itemsLimitConfigurationSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_ItemsLimitConfiguration.html + "category_sort": fieldSortOptionsSchema(100), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_FieldSortOptions.html, + "color_items_limit": itemsLimitConfigurationSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_ItemsLimitConfiguration.html + "color_sort": fieldSortOptionsSchema(100), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_FieldSortOptions.html + "small_multiples_limit_configuration": itemsLimitConfigurationSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_ItemsLimitConfiguration.html + "small_multiples_sort": fieldSortOptionsSchema(100), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_FieldSortOptions.html + }, + }, + }, + "tooltip": tooltipOptionsSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_TooltipOptions.html + "value_axis": axisDisplayOptionsSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_AxisDisplayOptions.html + "value_label_options": chartAxisLabelOptionsSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_ChartAxisLabelOptions.html + "visual_palette": visualPaletteSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_VisualPalette.html + }, + }, + }, + "column_hierarchies": columnHierarchiesSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_ColumnHierarchy.html + "subtitle": visualSubtitleLabelOptionsSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_VisualSubtitleLabelOptions.html + "title": visualTitleLabelOptionsSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_VisualTitleLabelOptions.html + }, + }, + } +} + +func expandBarChartVisual(tfList []interface{}) (*quicksight.BarChartVisual, error) { + if len(tfList) == 0 || tfList[0] == nil { + return nil, nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil, nil + } + + visual := &quicksight.BarChartVisual{} + + if v, ok := tfMap["visual_id"].(string); ok && v != "" { + visual.VisualId = aws.String(v) + } + if v, ok := tfMap["actions"].([]interface{}); ok && len(v) > 0 { + value, err := expandVisualCustomActions(v) + if err != nil { + return nil, err + } + visual.Actions = value + } + if v, ok := tfMap["chart_configuration"].([]interface{}); ok && len(v) > 0 { + visual.ChartConfiguration = expandBarChartConfiguration(v) + } + if v, ok := tfMap["column_hierarchies"].([]interface{}); ok && len(v) > 0 { + value, err := expandColumnHierarchies(v) + if err != nil { + return nil, err + } + visual.ColumnHierarchies = value + } + if v, ok := tfMap["subtitle"].([]interface{}); ok && len(v) > 0 { + visual.Subtitle = expandVisualSubtitleLabelOptions(v) + } + if v, ok := tfMap["title"].([]interface{}); ok && len(v) > 0 { + visual.Title = expandVisualTitleLabelOptions(v) + } + + return visual, nil +} + +func expandBarChartConfiguration(tfList []interface{}) *quicksight.BarChartConfiguration { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + config := &quicksight.BarChartConfiguration{} + + if v, ok := tfMap["bars_arrangement"].(string); ok && v != "" { + config.BarsArrangement = aws.String(v) + } + if v, ok := tfMap["orientation"].(string); ok && v != "" { + config.Orientation = aws.String(v) + } + if v, ok := tfMap["category_axis"].([]interface{}); ok && len(v) > 0 { + config.CategoryAxis = expandAxisDisplayOptions(v) + } + if v, ok := tfMap["category_label_options"].([]interface{}); ok && len(v) > 0 { + config.CategoryLabelOptions = expandChartAxisLabelOptions(v) + } + if v, ok := tfMap["color_label_options"].([]interface{}); ok && len(v) > 0 { + config.ColorLabelOptions = expandChartAxisLabelOptions(v) + } + if v, ok := tfMap["contribution_analysis_defaults"].([]interface{}); ok && len(v) > 0 { + config.ContributionAnalysisDefaults = expandContributionAnalysisDefaults(v) + } + if v, ok := tfMap["data_labels"].([]interface{}); ok && len(v) > 0 { + config.DataLabels = expandDataLabelOptions(v) + } + if v, ok := tfMap["field_wells"].([]interface{}); ok && len(v) > 0 { + config.FieldWells = expandBarChartFieldWells(v) + } + if v, ok := tfMap["legend"].([]interface{}); ok && len(v) > 0 { + config.Legend = expandLegendOptions(v) + } + if v, ok := tfMap["reference_lines"].([]interface{}); ok && len(v) > 0 { + config.ReferenceLines = expandReferenceLines(v) + } + if v, ok := tfMap["small_multiples_options"].([]interface{}); ok && len(v) > 0 { + config.SmallMultiplesOptions = expandSmallMultiplesOptions(v) + } + if v, ok := tfMap["sort_configuration"].([]interface{}); ok && len(v) > 0 { + config.SortConfiguration = expandBarChartSortConfiguration(v) + } + if v, ok := tfMap["tooltip"].([]interface{}); ok && len(v) > 0 { + config.Tooltip = expandTooltipOptions(v) + } + if v, ok := tfMap["value_axis"].([]interface{}); ok && len(v) > 0 { + config.ValueAxis = expandAxisDisplayOptions(v) + } + if v, ok := tfMap["value_label_options"].([]interface{}); ok && len(v) > 0 { + config.ValueLabelOptions = expandChartAxisLabelOptions(v) + } + if v, ok := tfMap["visual_palette"].([]interface{}); ok && len(v) > 0 { + config.VisualPalette = expandVisualPalette(v) + } + + return config +} + +func expandBarChartFieldWells(tfList []interface{}) *quicksight.BarChartFieldWells { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + config := &quicksight.BarChartFieldWells{} + + if v, ok := tfMap["bar_chart_aggregated_field_wells"].([]interface{}); ok && len(v) > 0 { + config.BarChartAggregatedFieldWells = expandBarChartAggregatedFieldWells(v) + } + + return config +} + +func expandBarChartAggregatedFieldWells(tfList []interface{}) *quicksight.BarChartAggregatedFieldWells { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + config := &quicksight.BarChartAggregatedFieldWells{} + + if v, ok := tfMap["category"].([]interface{}); ok && len(v) > 0 { + config.Category = expandDimensionFields(v) + } + if v, ok := tfMap["colors"].([]interface{}); ok && len(v) > 0 { + config.Colors = expandDimensionFields(v) + } + if v, ok := tfMap["small_multiples"].([]interface{}); ok && len(v) > 0 { + config.SmallMultiples = expandDimensionFields(v) + } + if v, ok := tfMap["values"].([]interface{}); ok && len(v) > 0 { + config.Values = expandMeasureFields(v) + } + + return config +} + +func expandBarChartSortConfiguration(tfList []interface{}) *quicksight.BarChartSortConfiguration { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + config := &quicksight.BarChartSortConfiguration{} + + if v, ok := tfMap["category_items_limit"].([]interface{}); ok && len(v) > 0 { + config.CategoryItemsLimit = expandItemsLimitConfiguration(v) + } + if v, ok := tfMap["category_sort"].([]interface{}); ok && len(v) > 0 { + config.CategorySort = expandFieldSortOptionsList(v) + } + if v, ok := tfMap["color_items_limit"].([]interface{}); ok && len(v) > 0 { + config.ColorItemsLimit = expandItemsLimitConfiguration(v) + } + if v, ok := tfMap["color_sort"].([]interface{}); ok && len(v) > 0 { + config.ColorSort = expandFieldSortOptionsList(v) + } + if v, ok := tfMap["small_multiples_limit_configuration"].([]interface{}); ok && len(v) > 0 { + config.SmallMultiplesLimitConfiguration = expandItemsLimitConfiguration(v) + } + if v, ok := tfMap["small_multiples_sort"].([]interface{}); ok && len(v) > 0 { + config.SmallMultiplesSort = expandFieldSortOptionsList(v) + } + + return config +} diff --git a/internal/service/quicksight/visual_box_plot.go b/internal/service/quicksight/visual_box_plot.go new file mode 100644 index 000000000000..ae314372a383 --- /dev/null +++ b/internal/service/quicksight/visual_box_plot.go @@ -0,0 +1,343 @@ +package quicksight + +import ( + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/quicksight" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" +) + +func boxPlotVisualSchema() *schema.Schema { + return &schema.Schema{ // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_BoxPlotVisual.html + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "visual_id": idSchema(), + "actions": visualCustomActionsSchema(customActionsMaxItems), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_VisualCustomAction.html + "chart_configuration": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_BoxPlotChartConfiguration.html + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "box_plot_options": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_BoxPlotOptions.html + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "all_data_points_visibility": stringSchema(false, validation.StringInSlice(quicksight.Visibility_Values(), false)), + "outlier_visibility": stringSchema(false, validation.StringInSlice(quicksight.Visibility_Values(), false)), + "style_options": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_BoxPlotStyleOptions.html + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "fill_style": stringSchema(false, validation.StringInSlice(quicksight.BoxPlotFillStyle_Values(), false)), + }, + }, + }, + }, + }, + }, + "category_axis": axisDisplayOptionsSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_AxisDisplayOptions.html + "category_label_options": chartAxisLabelOptionsSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_ChartAxisLabelOptions.html + "field_wells": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_BoxPlotFieldWells.html + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "box_plot_aggregated_field_wells": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_BoxPlotAggregatedFieldWells.html + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "group_by": dimensionFieldSchema(1), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_DimensionField.html + "values": measureFieldSchema(5), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_MeasureField.html + }, + }, + }, + }, + }, + }, + "legend": legendOptionsSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_LegendOptions.html + "primary_y_axis_display_options": axisDisplayOptionsSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_AxisDisplayOptions.html + "primary_y_axis_label_options": chartAxisLabelOptionsSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_ChartAxisLabelOptions.html + "reference_lines": referenceLineSchema(referenceLinesMaxItems), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_ReferenceLine.html + "sort_configuration": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_BoxPlotSortConfiguration.html + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "category_sort": fieldSortOptionsSchema(100), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_FieldSortOptions.html, + "pagination_configuration": paginationConfigurationSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_PaginationConfiguration.html + }, + }, + }, + "tooltip": tooltipOptionsSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_TooltipOptions.html + "visual_palette": visualPaletteSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_VisualPalette.html + }, + }, + }, + "column_hierarchies": columnHierarchiesSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_ColumnHierarchy.html + "subtitle": visualSubtitleLabelOptionsSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_VisualSubtitleLabelOptions.html + "title": visualTitleLabelOptionsSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_VisualTitleLabelOptions.html + }, + }, + } +} + +func paginationConfigurationSchema() *schema.Schema { + return &schema.Schema{ // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_PaginationConfiguration.html + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "page_number": { + Type: schema.TypeInt, + Required: true, + ValidateFunc: validation.IntAtLeast(0), + }, + "page_size": { + Type: schema.TypeInt, + Required: true, + }, + }, + }, + } +} + +func expandBoxPlotVisual(tfList []interface{}) (*quicksight.BoxPlotVisual, error) { + if len(tfList) == 0 || tfList[0] == nil { + return nil, nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil, nil + } + + visual := &quicksight.BoxPlotVisual{} + + if v, ok := tfMap["visual_id"].(string); ok && v != "" { + visual.VisualId = aws.String(v) + } + if v, ok := tfMap["actions"].([]interface{}); ok && len(v) > 0 { + value, err := expandVisualCustomActions(v) + if err != nil { + return nil, err + } + visual.Actions = value + } + if v, ok := tfMap["chart_configuration"].([]interface{}); ok && len(v) > 0 { + visual.ChartConfiguration = expandBoxPlotChartConfiguration(v) + } + if v, ok := tfMap["column_hierarchies"].([]interface{}); ok && len(v) > 0 { + value, err := expandColumnHierarchies(v) + if err != nil { + return nil, err + } + visual.ColumnHierarchies = value + } + if v, ok := tfMap["subtitle"].([]interface{}); ok && len(v) > 0 { + visual.Subtitle = expandVisualSubtitleLabelOptions(v) + } + if v, ok := tfMap["title"].([]interface{}); ok && len(v) > 0 { + visual.Title = expandVisualTitleLabelOptions(v) + } + + return visual, nil +} + +func expandBoxPlotChartConfiguration(tfList []interface{}) *quicksight.BoxPlotChartConfiguration { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + config := &quicksight.BoxPlotChartConfiguration{} + + if v, ok := tfMap["box_plot_options"].([]interface{}); ok && len(v) > 0 { + config.BoxPlotOptions = expandBoxPlotOptions(v) + } + if v, ok := tfMap["category_axis"].([]interface{}); ok && len(v) > 0 { + config.CategoryAxis = expandAxisDisplayOptions(v) + } + if v, ok := tfMap["category_label_options"].([]interface{}); ok && len(v) > 0 { + config.CategoryLabelOptions = expandChartAxisLabelOptions(v) + } + if v, ok := tfMap["field_wells"].([]interface{}); ok && len(v) > 0 { + config.FieldWells = expandBoxPlotFieldWells(v) + } + if v, ok := tfMap["legend"].([]interface{}); ok && len(v) > 0 { + config.Legend = expandLegendOptions(v) + } + if v, ok := tfMap["primary_y_axis_display_options"].([]interface{}); ok && len(v) > 0 { + config.PrimaryYAxisDisplayOptions = expandAxisDisplayOptions(v) + } + if v, ok := tfMap["primary_y_axis_label_options"].([]interface{}); ok && len(v) > 0 { + config.PrimaryYAxisLabelOptions = expandChartAxisLabelOptions(v) + } + if v, ok := tfMap["reference_lines"].([]interface{}); ok && len(v) > 0 { + config.ReferenceLines = expandReferenceLines(v) + } + if v, ok := tfMap["sort_configuration"].([]interface{}); ok && len(v) > 0 { + config.SortConfiguration = expandBoxPlotSortConfiguration(v) + } + if v, ok := tfMap["tooltip"].([]interface{}); ok && len(v) > 0 { + config.Tooltip = expandTooltipOptions(v) + } + if v, ok := tfMap["visual_palette"].([]interface{}); ok && len(v) > 0 { + config.VisualPalette = expandVisualPalette(v) + } + + return config +} + +func expandBoxPlotOptions(tfList []interface{}) *quicksight.BoxPlotOptions { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + options := &quicksight.BoxPlotOptions{} + + if v, ok := tfMap["all_data_points_visibility"].(string); ok && v != "" { + options.AllDataPointsVisibility = aws.String(v) + } + if v, ok := tfMap["outlier_visibility"].(string); ok && v != "" { + options.OutlierVisibility = aws.String(v) + } + if v, ok := tfMap["style_options"].([]interface{}); ok && len(v) > 0 { + options.StyleOptions = expandBoxPlotStyleOptions(v) + } + + return options +} + +func expandBoxPlotStyleOptions(tfList []interface{}) *quicksight.BoxPlotStyleOptions { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + options := &quicksight.BoxPlotStyleOptions{} + + if v, ok := tfMap["fill_style"].(string); ok && v != "" { + options.FillStyle = aws.String(v) + } + + return options +} + +func expandBoxPlotFieldWells(tfList []interface{}) *quicksight.BoxPlotFieldWells { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + config := &quicksight.BoxPlotFieldWells{} + + if v, ok := tfMap["box_plot_aggregated_field_wells"].([]interface{}); ok && len(v) > 0 { + config.BoxPlotAggregatedFieldWells = expandBoxPlotAggregatedFieldWells(v) + } + + return config +} + +func expandBoxPlotAggregatedFieldWells(tfList []interface{}) *quicksight.BoxPlotAggregatedFieldWells { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + config := &quicksight.BoxPlotAggregatedFieldWells{} + + if v, ok := tfMap["group_by"].([]interface{}); ok && len(v) > 0 { + config.GroupBy = expandDimensionFields(v) + } + if v, ok := tfMap["values"].([]interface{}); ok && len(v) > 0 { + config.Values = expandMeasureFields(v) + } + + return config +} + +func expandBoxPlotSortConfiguration(tfList []interface{}) *quicksight.BoxPlotSortConfiguration { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + config := &quicksight.BoxPlotSortConfiguration{} + + if v, ok := tfMap["category_sort"].([]interface{}); ok && len(v) > 0 { + config.CategorySort = expandFieldSortOptionsList(v) + } + if v, ok := tfMap["pagination_configuration"].([]interface{}); ok && len(v) > 0 { + config.PaginationConfiguration = expandPaginationConfiguration(v) + } + + return config +} + +func expandPaginationConfiguration(tfList []interface{}) *quicksight.PaginationConfiguration { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + config := &quicksight.PaginationConfiguration{} + + if v, ok := tfMap["page_number"].(int64); ok { + config.PageNumber = aws.Int64(v) + } + if v, ok := tfMap["page_size"].(int64); ok { + config.PageSize = aws.Int64(v) + } + + return config +} diff --git a/internal/service/quicksight/visual_chart_configuration.go b/internal/service/quicksight/visual_chart_configuration.go new file mode 100644 index 000000000000..6fe7dfc22475 --- /dev/null +++ b/internal/service/quicksight/visual_chart_configuration.go @@ -0,0 +1,1195 @@ +package quicksight + +import ( + "regexp" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/quicksight" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" +) + +func axisDisplayOptionsSchema() *schema.Schema { + return &schema.Schema{ // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_AxisDisplayOptions.html + Type: schema.TypeList, + MinItems: 1, + MaxItems: 1, + Optional: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "axis_line_visibility": stringSchema(false, validation.StringInSlice(quicksight.Visibility_Values(), false)), + "axis_offset": { + Type: schema.TypeString, + Optional: true, + }, + "data_options": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_AxisDataOptions.html + Type: schema.TypeList, + MinItems: 1, + MaxItems: 1, + Optional: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "date_axis_options": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_DateAxisOptions.html + Type: schema.TypeList, + MinItems: 1, + MaxItems: 1, + Optional: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "missing_date_visibility": stringSchema(false, validation.StringInSlice(quicksight.Visibility_Values(), false)), + }, + }, + }, + "numeric_axis_options": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_NumericAxisOptions.html + Type: schema.TypeList, + MinItems: 1, + MaxItems: 1, + Optional: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "range": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_AxisDisplayRange.html + Type: schema.TypeList, + MinItems: 1, + MaxItems: 1, + Optional: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "data_driven": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_AxisDisplayDataDrivenRange.html + Type: schema.TypeList, + MinItems: 1, + MaxItems: 1, + Optional: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{}, // For future extensions + }, + }, + "min_max": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_AxisDisplayMinMaxRange.html + Type: schema.TypeList, + MinItems: 1, + MaxItems: 1, + Optional: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "maximum": { + Type: schema.TypeFloat, + Optional: true, + }, + "minimum": { + Type: schema.TypeFloat, + Optional: true, + }, + }, + }, + }, + }, + }, + }, + "scale": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_AxisScale.html + Type: schema.TypeList, + MinItems: 1, + MaxItems: 1, + Optional: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "linear": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_AxisLinearScale.html + Type: schema.TypeList, + MinItems: 1, + MaxItems: 1, + Optional: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "step_count": { + Type: schema.TypeInt, + Optional: true, + }, + "step_size": { + Type: schema.TypeFloat, + Optional: true, + }, + }, + }, + }, + "logarithmic": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_AxisLogarithmicScale.html + Type: schema.TypeList, + MinItems: 1, + MaxItems: 1, + Optional: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "base": { + Type: schema.TypeFloat, + Optional: true, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + "grid_line_visibility": stringSchema(false, validation.StringInSlice(quicksight.Visibility_Values(), false)), + "scrollbar_options": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_ScrollBarOptions.html + Type: schema.TypeList, + MinItems: 1, + MaxItems: 1, + Optional: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "visibility": stringSchema(false, validation.StringInSlice(quicksight.Visibility_Values(), false)), + "visible_range": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_VisibleRangeOptions.html + Type: schema.TypeList, + MinItems: 1, + MaxItems: 1, + Optional: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "percent_range": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_PercentVisibleRange.html + Type: schema.TypeList, + MinItems: 1, + MaxItems: 1, + Optional: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "from": floatSchema(false, validation.FloatBetween(0, 100)), + "to": floatSchema(false, validation.FloatBetween(0, 100)), + }, + }, + }, + }, + }, + }, + }, + }, + }, + "tick_label_options": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_AxisTickLabelOptions.html + Type: schema.TypeList, + MinItems: 1, + MaxItems: 1, + Optional: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "label_options": labelOptionsSchema(), + "rotation_angle": { + Type: schema.TypeFloat, + Optional: true, + }, + }, + }, + }, + }, + }, + } +} + +func chartAxisLabelOptionsSchema() *schema.Schema { + return &schema.Schema{ // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_ChartAxisLabelOptions.html + Type: schema.TypeList, + MinItems: 1, + MaxItems: 1, + Optional: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "axis_label_options": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_AxisLabelOptions.html + Type: schema.TypeList, + MinItems: 1, + MaxItems: 1, + Optional: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "apply_to": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_AxisLabelReferenceOptions.html + Type: schema.TypeList, + MinItems: 1, + MaxItems: 1, + Optional: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "column": columnSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_ColumnIdentifier.html + "field_id": stringSchema(true, validation.StringLenBetween(1, 512)), + }, + }, + }, + "custom_label": { + Type: schema.TypeString, + Optional: true, + }, + "font_configuration": fontConfigurationSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_FontConfiguration.html + }, + }, + }, + "sort_icon_visibility": stringSchema(false, validation.StringInSlice(quicksight.Visibility_Values(), false)), + "visibility": stringSchema(false, validation.StringInSlice(quicksight.Visibility_Values(), false)), + }, + }, + } +} + +func itemsLimitConfigurationSchema() *schema.Schema { + return &schema.Schema{ // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_ItemsLimitConfiguration.html + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "items_limit": { + Type: schema.TypeInt, + Optional: true, + }, + "other_categories": stringSchema(true, validation.StringInSlice(quicksight.OtherCategories_Values(), false)), + }, + }, + } +} + +func contributionAnalysisDefaultsSchema() *schema.Schema { + return &schema.Schema{ // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_ContributionAnalysisDefault.html + Type: schema.TypeList, + MinItems: 1, + MaxItems: 200, + Optional: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "contributor_dimensions": { + Type: schema.TypeList, + MinItems: 1, + MaxItems: 4, + Required: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_ColumnIdentifier.html + "column_name": stringSchema(true, validation.StringLenBetween(1, 128)), + "data_set_identifier": stringSchema(true, validation.StringLenBetween(1, 2048)), + }, + }, + }, + "measure_field_id": stringSchema(true, validation.StringLenBetween(1, 512)), + }, + }, + } +} + +func referenceLineSchema(maxItems int) *schema.Schema { + return &schema.Schema{ // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_ReferenceLine.html + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: maxItems, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "data_configuration": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_ReferenceLineDataConfiguration.html + Type: schema.TypeList, + Required: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "axis_binding": stringSchema(false, validation.StringInSlice(quicksight.AxisBinding_Values(), false)), + "dynamic_configuration": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_ReferenceLineDynamicDataConfiguration.html + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "calculation": numericalAggregationFunctionSchema(true), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_NumericalAggregationFunction.html + "column": columnSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_ColumnIdentifier.html + "measure_aggregation_function": aggregationFunctionSchema(true), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_AggregationFunction.html + }, + }, + }, + "static_configuration": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_ReferenceLineStaticDataConfiguration.html + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "value": { + Type: schema.TypeFloat, + Required: true, + }, + }, + }, + }, + }, + }, + }, + "label_configuration": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_ReferenceLineLabelConfiguration.html + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "custom_label_configuration": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_ReferenceLineCustomLabelConfiguration.html + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "custom_label": stringSchema(true, validation.StringMatch(regexp.MustCompile(`.*\S.*`), "")), + }, + }, + }, + "font_color": stringSchema(false, validation.StringMatch(regexp.MustCompile(`^#[A-F0-9]{6}$`), "")), + "font_configuration": fontConfigurationSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_FontConfiguration.html + "horizontal_position": stringSchema(false, validation.StringInSlice(quicksight.ReferenceLineLabelHorizontalPosition_Values(), false)), + "value_label_configuration": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_ReferenceLineValueLabelConfiguration.html + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "format_configuration": numericFormatConfigurationSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_NumericFormatConfiguration.html + "relative_position": stringSchema(false, validation.StringInSlice(quicksight.ReferenceLineValueLabelRelativePosition_Values(), false)), + }, + }, + }, + "vertical_position": stringSchema(false, validation.StringInSlice(quicksight.ReferenceLineLabelVerticalPosition_Values(), false)), + }, + }, + }, + "status": stringSchema(false, validation.StringInSlice(quicksight.Status_Values(), false)), + "style_configuration": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_ReferenceLineStyleConfiguration.html + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "color": stringSchema(false, validation.StringMatch(regexp.MustCompile(`^#[A-F0-9]{6}$`), "")), + "pattern": stringSchema(false, validation.StringInSlice(quicksight.ReferenceLinePatternType_Values(), false)), + }, + }, + }, + }, + }, + } +} + +func smallMultiplesOptionsSchema() *schema.Schema { + return &schema.Schema{ // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_SmallMultiplesOptions.html + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "max_visible_columns": { + Type: schema.TypeInt, + Optional: true, + ValidateFunc: validation.IntBetween(1, 10), + }, + "max_visible_rows": { + Type: schema.TypeInt, + Optional: true, + ValidateFunc: validation.IntBetween(1, 10), + }, + "panel_configuration": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_PanelConfiguration.html + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "background_color": stringSchema(false, validation.StringMatch(regexp.MustCompile(`^#[A-F0-9]{6}(?:[A-F0-9]{2})?$`), "")), + "background_visibility": stringSchema(false, validation.StringInSlice(quicksight.Visibility_Values(), false)), + "border_color": stringSchema(false, validation.StringMatch(regexp.MustCompile(`^#[A-F0-9]{6}(?:[A-F0-9]{2})?$`), "")), + "border_style": stringSchema(false, validation.StringInSlice(quicksight.PanelBorderStyle_Values(), false)), + "border_thickness": { + Type: schema.TypeString, + Optional: true, + }, + "border_visibility": stringSchema(false, validation.StringInSlice(quicksight.Visibility_Values(), false)), + "gutter_spacing": { + Type: schema.TypeString, + Optional: true, + }, + "gutter_visibility": stringSchema(false, validation.StringInSlice(quicksight.Visibility_Values(), false)), + "title": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_PanelTitleOptions.html + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "font_configuration": fontConfigurationSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_FontConfiguration.html + "horizontal_text_alignment": stringSchema(false, validation.StringInSlice(quicksight.HorizontalTextAlignment_Values(), false)), + "visibility": stringSchema(false, validation.StringInSlice(quicksight.Visibility_Values(), false)), + }, + }, + }, + }, + }, + }, + }, + }, + } +} + +func expandAxisDisplayOptions(tfList []interface{}) *quicksight.AxisDisplayOptions { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + options := &quicksight.AxisDisplayOptions{} + + if v, ok := tfMap["axis_line_visibility"].(string); ok && v != "" { + options.AxisLineVisibility = aws.String(v) + } + if v, ok := tfMap["axis_offset"].(string); ok && v != "" { + options.AxisOffset = aws.String(v) + } + if v, ok := tfMap["grid_line_visibility"].(string); ok && v != "" { + options.GridLineVisibility = aws.String(v) + } + if v, ok := tfMap["data_options"].([]interface{}); ok && len(v) > 0 { + options.DataOptions = expandAxisDataOptions(v) + } + if v, ok := tfMap["scrollbar_options"].([]interface{}); ok && len(v) > 0 { + options.ScrollbarOptions = expandScrollBarOptions(v) + } + if v, ok := tfMap["tick_label_options"].([]interface{}); ok && len(v) > 0 { + options.TickLabelOptions = expandAxisTickLabelOptions(v) + } + + return options +} + +func expandAxisDataOptions(tfList []interface{}) *quicksight.AxisDataOptions { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + options := &quicksight.AxisDataOptions{} + + if v, ok := tfMap["date_axis_options"].([]interface{}); ok && len(v) > 0 { + options.DateAxisOptions = expandDateAxisOptions(v) + } + if v, ok := tfMap["numeric_axis_options"].([]interface{}); ok && len(v) > 0 { + options.NumericAxisOptions = expandNumericAxisOptions(v) + } + + return options +} + +func expandDateAxisOptions(tfList []interface{}) *quicksight.DateAxisOptions { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + options := &quicksight.DateAxisOptions{} + + if v, ok := tfMap["missing_date_visibility"].(string); ok && v != "" { + options.MissingDateVisibility = aws.String(v) + } + + return options +} + +func expandNumericAxisOptions(tfList []interface{}) *quicksight.NumericAxisOptions { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + options := &quicksight.NumericAxisOptions{} + + if v, ok := tfMap["range"].([]interface{}); ok && len(v) > 0 { + options.Range = expandAxisDisplayRange(v) + } + if v, ok := tfMap["scale"].([]interface{}); ok && len(v) > 0 { + options.Scale = expandAxisScale(v) + } + + return options +} + +func expandAxisDisplayRange(tfList []interface{}) *quicksight.AxisDisplayRange { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + options := &quicksight.AxisDisplayRange{} + + if v, ok := tfMap["data_driven"].([]interface{}); ok && len(v) > 0 { + options.DataDriven = expandAxisDisplayDataDrivenRange(v) + } + if v, ok := tfMap["min_max"].([]interface{}); ok && len(v) > 0 { + options.MinMax = expandAxisDisplayMinMaxRange(v) + } + + return options +} + +func expandAxisDisplayDataDrivenRange(tfList []interface{}) *quicksight.AxisDisplayDataDrivenRange { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + //tfMap, ok := tfList[0].(map[string]interface{}) + //if !ok { + // return nil + //} + + options := &quicksight.AxisDisplayDataDrivenRange{} + + return options +} + +func expandAxisDisplayMinMaxRange(tfList []interface{}) *quicksight.AxisDisplayMinMaxRange { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + options := &quicksight.AxisDisplayMinMaxRange{} + + if v, ok := tfMap["maximum"].(float64); ok { + options.Maximum = aws.Float64(v) + } + if v, ok := tfMap["minimum"].(float64); ok { + options.Minimum = aws.Float64(v) + } + + return options +} + +func expandAxisScale(tfList []interface{}) *quicksight.AxisScale { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + options := &quicksight.AxisScale{} + + if v, ok := tfMap["linear"].([]interface{}); ok && len(v) > 0 { + options.Linear = expandAxisLinearScale(v) + } + if v, ok := tfMap["logarithmic"].([]interface{}); ok && len(v) > 0 { + options.Logarithmic = expandAxisLogarithmicScale(v) + } + + return options +} + +func expandAxisLinearScale(tfList []interface{}) *quicksight.AxisLinearScale { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + options := &quicksight.AxisLinearScale{} + + if v, ok := tfMap["step_count"].(int64); ok { + options.StepCount = aws.Int64(v) + } + if v, ok := tfMap["step_size"].(float64); ok { + options.StepSize = aws.Float64(v) + } + + return options +} + +func expandAxisLogarithmicScale(tfList []interface{}) *quicksight.AxisLogarithmicScale { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + options := &quicksight.AxisLogarithmicScale{} + + if v, ok := tfMap["base"].(float64); ok { + options.Base = aws.Float64(v) + } + + return options +} + +func expandScrollBarOptions(tfList []interface{}) *quicksight.ScrollBarOptions { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + options := &quicksight.ScrollBarOptions{} + + if v, ok := tfMap["visibility"].(string); ok && v != "" { + options.Visibility = aws.String(v) + } + if v, ok := tfMap["visible_range"].([]interface{}); ok && len(v) > 0 { + options.VisibleRange = expandVisibleRangeOptions(v) + } + + return options +} + +func expandVisibleRangeOptions(tfList []interface{}) *quicksight.VisibleRangeOptions { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + options := &quicksight.VisibleRangeOptions{} + + if v, ok := tfMap["percent_range"].([]interface{}); ok && len(v) > 0 { + options.PercentRange = expandPercentVisibleRange(v) + } + + return options +} + +func expandPercentVisibleRange(tfList []interface{}) *quicksight.PercentVisibleRange { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + options := &quicksight.PercentVisibleRange{} + + if v, ok := tfMap["from"].(float64); ok { + options.From = aws.Float64(v) + } + if v, ok := tfMap["to"].(float64); ok { + options.To = aws.Float64(v) + } + + return options +} + +func expandAxisTickLabelOptions(tfList []interface{}) *quicksight.AxisTickLabelOptions { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + options := &quicksight.AxisTickLabelOptions{} + + if v, ok := tfMap["rotation_angle"].(float64); ok { + options.RotationAngle = aws.Float64(v) + } + if v, ok := tfMap["label_options"].([]interface{}); ok && len(v) > 0 { + options.LabelOptions = expandLabelOptions(v) + } + + return options +} + +func expandChartAxisLabelOptions(tfList []interface{}) *quicksight.ChartAxisLabelOptions { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + options := &quicksight.ChartAxisLabelOptions{} + + if v, ok := tfMap["visibility"].(string); ok && v != "" { + options.Visibility = aws.String(v) + } + if v, ok := tfMap["sort_icon_visibility"].(string); ok && v != "" { + options.SortIconVisibility = aws.String(v) + } + if v, ok := tfMap["axis_label_options"].([]interface{}); ok && len(v) > 0 { + options.AxisLabelOptions = expandAxisLabelOptionsList(v) + } + + return options +} + +func expandAxisLabelOptionsList(tfList []interface{}) []*quicksight.AxisLabelOptions { + if len(tfList) == 0 { + return nil + } + + var options []*quicksight.AxisLabelOptions + for _, tfMapRaw := range tfList { + tfMap, ok := tfMapRaw.(map[string]interface{}) + if !ok { + continue + } + + opts := expandAxisLabelOptions(tfMap) + if opts == nil { + continue + } + + options = append(options, opts) + } + + return options +} + +func expandAxisLabelOptions(tfMap map[string]interface{}) *quicksight.AxisLabelOptions { + if tfMap == nil { + return nil + } + + options := &quicksight.AxisLabelOptions{} + + if v, ok := tfMap["custom_label"].(string); ok && v != "" { + options.CustomLabel = aws.String(v) + } + if v, ok := tfMap["apply_to"].([]interface{}); ok && len(v) > 0 { + options.ApplyTo = expandAxisLabelReferenceOptions(v) + } + if v, ok := tfMap["font_configuration"].([]interface{}); ok && len(v) > 0 { + options.FontConfiguration = expandFontConfiguration(v) + } + + return options +} + +func expandAxisLabelReferenceOptions(tfList []interface{}) *quicksight.AxisLabelReferenceOptions { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + options := &quicksight.AxisLabelReferenceOptions{} + + if v, ok := tfMap["field_id"].(string); ok && v != "" { + options.FieldId = aws.String(v) + } + if v, ok := tfMap["column"].([]interface{}); ok && len(v) > 0 { + options.Column = expandColumnIdentifier(v) + } + + return options +} + +func expandContributionAnalysisDefaults(tfList []interface{}) []*quicksight.ContributionAnalysisDefault { + if len(tfList) == 0 { + return nil + } + + var options []*quicksight.ContributionAnalysisDefault + for _, tfMapRaw := range tfList { + tfMap, ok := tfMapRaw.(map[string]interface{}) + if !ok { + continue + } + + opts := expandContributionAnalysisDefault(tfMap) + if opts == nil { + continue + } + + options = append(options, opts) + } + + return options +} + +func expandContributionAnalysisDefault(tfMap map[string]interface{}) *quicksight.ContributionAnalysisDefault { + if tfMap == nil { + return nil + } + + options := &quicksight.ContributionAnalysisDefault{} + + if v, ok := tfMap["measure_field_id"].(string); ok && v != "" { + options.MeasureFieldId = aws.String(v) + } + if v, ok := tfMap["contributor_dimensions"].([]interface{}); ok && len(v) > 0 { + options.ContributorDimensions = expandColumnIdentifiers(v) + } + + return options +} + +func expandReferenceLines(tfList []interface{}) []*quicksight.ReferenceLine { + if len(tfList) == 0 { + return nil + } + + var lines []*quicksight.ReferenceLine + for _, tfMapRaw := range tfList { + tfMap, ok := tfMapRaw.(map[string]interface{}) + if !ok { + continue + } + + line := expandReferenceLine(tfMap) + if line == nil { + continue + } + + lines = append(lines, line) + } + + return lines +} + +func expandReferenceLine(tfMap map[string]interface{}) *quicksight.ReferenceLine { + if tfMap == nil { + return nil + } + + line := &quicksight.ReferenceLine{} + + if v, ok := tfMap["status"].(string); ok && v != "" { + line.Status = aws.String(v) + } + if v, ok := tfMap["data_configuration"].([]interface{}); ok && len(v) > 0 { + line.DataConfiguration = expandReferenceLineDataConfiguration(v) + } + if v, ok := tfMap["label_configuration"].([]interface{}); ok && len(v) > 0 { + line.LabelConfiguration = expandReferenceLineLabelConfiguration(v) + } + if v, ok := tfMap["style_configuration"].([]interface{}); ok && len(v) > 0 { + line.StyleConfiguration = expandReferenceLineStyleConfiguration(v) + } + + return line +} + +func expandReferenceLineDataConfiguration(tfList []interface{}) *quicksight.ReferenceLineDataConfiguration { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + config := &quicksight.ReferenceLineDataConfiguration{} + + if v, ok := tfMap["axis_binding"].(string); ok && v != "" { + config.AxisBinding = aws.String(v) + } + if v, ok := tfMap["dynamic_configuration"].([]interface{}); ok && len(v) > 0 { + config.DynamicConfiguration = expandReferenceLineDynamicDataConfiguration(v) + } + if v, ok := tfMap["static_configuration"].([]interface{}); ok && len(v) > 0 { + config.StaticConfiguration = expandReferenceLineStaticDataConfiguration(v) + } + + return config +} + +func expandReferenceLineDynamicDataConfiguration(tfList []interface{}) *quicksight.ReferenceLineDynamicDataConfiguration { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + config := &quicksight.ReferenceLineDynamicDataConfiguration{} + + if v, ok := tfMap["calculation"].([]interface{}); ok && len(v) > 0 { + config.Calculation = expandNumericalAggregationFunction(v) + } + if v, ok := tfMap["column"].([]interface{}); ok && len(v) > 0 { + config.Column = expandColumnIdentifier(v) + } + if v, ok := tfMap["measure_aggregation_function"].([]interface{}); ok && len(v) > 0 { + config.MeasureAggregationFunction = expandAggregationFunction(v) + } + + return config +} + +func expandReferenceLineStaticDataConfiguration(tfList []interface{}) *quicksight.ReferenceLineStaticDataConfiguration { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + config := &quicksight.ReferenceLineStaticDataConfiguration{} + + if v, ok := tfMap["value"].(float64); ok { + config.Value = aws.Float64(v) + } + + return config +} + +func expandReferenceLineLabelConfiguration(tfList []interface{}) *quicksight.ReferenceLineLabelConfiguration { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + config := &quicksight.ReferenceLineLabelConfiguration{} + + if v, ok := tfMap["font_color"].(string); ok && v != "" { + config.FontColor = aws.String(v) + } + if v, ok := tfMap["horizontal_position"].(string); ok && v != "" { + config.HorizontalPosition = aws.String(v) + } + if v, ok := tfMap["vertical_position"].(string); ok && v != "" { + config.VerticalPosition = aws.String(v) + } + if v, ok := tfMap["custom_label_configuration"].([]interface{}); ok && len(v) > 0 { + config.CustomLabelConfiguration = expandReferenceLineCustomLabelConfiguration(v) + } + if v, ok := tfMap["font_configuration"].([]interface{}); ok && len(v) > 0 { + config.FontConfiguration = expandFontConfiguration(v) + } + if v, ok := tfMap["value_label_configuration"].([]interface{}); ok && len(v) > 0 { + config.ValueLabelConfiguration = expandReferenceLineValueLabelConfiguration(v) + } + + return config +} + +func expandReferenceLineCustomLabelConfiguration(tfList []interface{}) *quicksight.ReferenceLineCustomLabelConfiguration { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + config := &quicksight.ReferenceLineCustomLabelConfiguration{} + + if v, ok := tfMap["custom_label"].(string); ok && v != "" { + config.CustomLabel = aws.String(v) + } + + return config +} + +func expandReferenceLineValueLabelConfiguration(tfList []interface{}) *quicksight.ReferenceLineValueLabelConfiguration { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + config := &quicksight.ReferenceLineValueLabelConfiguration{} + + if v, ok := tfMap["relative_position"].(string); ok && v != "" { + config.RelativePosition = aws.String(v) + } + if v, ok := tfMap["format_configuration"].([]interface{}); ok && len(v) > 0 { + config.FormatConfiguration = expandNumericFormatConfiguration(v) + } + + return config +} + +func expandReferenceLineStyleConfiguration(tfList []interface{}) *quicksight.ReferenceLineStyleConfiguration { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + config := &quicksight.ReferenceLineStyleConfiguration{} + + if v, ok := tfMap["color"].(string); ok && v != "" { + config.Color = aws.String(v) + } + if v, ok := tfMap["pattern"].(string); ok && v != "" { + config.Pattern = aws.String(v) + } + + return config +} + +func expandSmallMultiplesOptions(tfList []interface{}) *quicksight.SmallMultiplesOptions { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + options := &quicksight.SmallMultiplesOptions{} + + if v, ok := tfMap["max_visible_columns"].(int64); ok { + options.MaxVisibleColumns = aws.Int64(v) + } + if v, ok := tfMap["max_visible_rows"].(int64); ok { + options.MaxVisibleRows = aws.Int64(v) + } + if v, ok := tfMap["panel_configuration"].([]interface{}); ok && len(v) > 0 { + options.PanelConfiguration = expandPanelConfiguration(v) + } + + return options +} + +func expandPanelConfiguration(tfList []interface{}) *quicksight.PanelConfiguration { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + config := &quicksight.PanelConfiguration{} + + if v, ok := tfMap["background_color"].(string); ok && v != "" { + config.BackgroundColor = aws.String(v) + } + if v, ok := tfMap["background_visibility"].(string); ok && v != "" { + config.BackgroundVisibility = aws.String(v) + } + if v, ok := tfMap["border_color"].(string); ok && v != "" { + config.BorderColor = aws.String(v) + } + if v, ok := tfMap["border_style"].(string); ok && v != "" { + config.BorderStyle = aws.String(v) + } + if v, ok := tfMap["border_thickness"].(string); ok && v != "" { + config.BorderThickness = aws.String(v) + } + if v, ok := tfMap["border_visibility"].(string); ok && v != "" { + config.BorderVisibility = aws.String(v) + } + if v, ok := tfMap["gutter_spacing"].(string); ok && v != "" { + config.GutterSpacing = aws.String(v) + } + if v, ok := tfMap["gutter_visibility"].(string); ok && v != "" { + config.GutterVisibility = aws.String(v) + } + if v, ok := tfMap["title"].([]interface{}); ok && len(v) > 0 { + config.Title = expandPanelTitleOptions(v) + } + + return config +} + +func expandPanelTitleOptions(tfList []interface{}) *quicksight.PanelTitleOptions { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + options := &quicksight.PanelTitleOptions{} + + if v, ok := tfMap["horizontal_text_alignment"].(string); ok && v != "" { + options.HorizontalTextAlignment = aws.String(v) + } + if v, ok := tfMap["visibility"].(string); ok && v != "" { + options.Visibility = aws.String(v) + } + if v, ok := tfMap["font_configuration"].([]interface{}); ok && len(v) > 0 { + options.FontConfiguration = expandFontConfiguration(v) + } + + return options +} + +func expandItemsLimitConfiguration(tfList []interface{}) *quicksight.ItemsLimitConfiguration { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + config := &quicksight.ItemsLimitConfiguration{} + + if v, ok := tfMap["items_limit"].(int64); ok { + config.ItemsLimit = aws.Int64(v) + } + if v, ok := tfMap["other_categories"].(string); ok && v != "" { + config.OtherCategories = aws.String(v) + } + + return config +} diff --git a/internal/service/quicksight/visual_combo_chart.go b/internal/service/quicksight/visual_combo_chart.go new file mode 100644 index 000000000000..cc24c1d1c94d --- /dev/null +++ b/internal/service/quicksight/visual_combo_chart.go @@ -0,0 +1,269 @@ +package quicksight + +import ( + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/quicksight" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" +) + +func comboChartVisualSchema() *schema.Schema { + return &schema.Schema{ // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_ComboChartVisual.html + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "visual_id": idSchema(), + "actions": visualCustomActionsSchema(customActionsMaxItems), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_VisualCustomAction.html + "chart_configuration": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_ComboChartConfiguration.html + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "bar_data_labels": dataLabelOptionsSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_DataLabelOptions.html + "bars_arrangement": stringSchema(false, validation.StringInSlice(quicksight.BarsArrangement_Values(), false)), + "category_axis": axisDisplayOptionsSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_AxisDisplayOptions.html + "category_label_options": chartAxisLabelOptionsSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_ChartAxisLabelOptions.html + "color_label_options": chartAxisLabelOptionsSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_ChartAxisLabelOptions.html + "field_wells": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_ComboChartFieldWells.html + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "combo_chart_aggregated_field_wells": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_ComboChartAggregatedFieldWells.html + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "bar_values": measureFieldSchema(200), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_MeasureField.html + "category": dimensionFieldSchema(dimensionsFieldMaxItems), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_DimensionField.html + "colors": dimensionFieldSchema(dimensionsFieldMaxItems), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_DimensionField.html + "line_values": measureFieldSchema(200), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_MeasureField.html + }, + }, + }, + }, + }, + }, + "legend": legendOptionsSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_LegendOptions.html + "line_data_labels": dataLabelOptionsSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_DataLabelOptions.html + "primary_y_axis_display_options": axisDisplayOptionsSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_AxisDisplayOptions.html + "primary_y_axis_label_options": chartAxisLabelOptionsSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_ChartAxisLabelOptions.html + "reference_lines": referenceLineSchema(referenceLinesMaxItems), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_ReferenceLine.html + "secondary_y_axis_display_options": axisDisplayOptionsSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_AxisDisplayOptions.html + "secondary_y_axis_label_options": chartAxisLabelOptionsSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_ChartAxisLabelOptions.html + "sort_configuration": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_ComboChartSortConfiguration.html + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "category_items_limit": itemsLimitConfigurationSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_ItemsLimitConfiguration.html + "category_sort": fieldSortOptionsSchema(100), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_FieldSortOptions.html, + "color_items_limit": itemsLimitConfigurationSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_ItemsLimitConfiguration.html + "color_sort": fieldSortOptionsSchema(100), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_FieldSortOptions.html + }, + }, + }, + "tooltip": tooltipOptionsSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_TooltipOptions.html + "visual_palette": visualPaletteSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_VisualPalette.html + }, + }, + }, + "column_hierarchies": columnHierarchiesSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_ColumnHierarchy.html + "subtitle": visualSubtitleLabelOptionsSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_VisualSubtitleLabelOptions.html + "title": visualTitleLabelOptionsSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_VisualTitleLabelOptions.html + }, + }, + } +} + +func expandComboChartVisual(tfList []interface{}) (*quicksight.ComboChartVisual, error) { + if len(tfList) == 0 || tfList[0] == nil { + return nil, nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil, nil + } + + visual := &quicksight.ComboChartVisual{} + + if v, ok := tfMap["visual_id"].(string); ok && v != "" { + visual.VisualId = aws.String(v) + } + if v, ok := tfMap["actions"].([]interface{}); ok && len(v) > 0 { + value, err := expandVisualCustomActions(v) + if err != nil { + return nil, err + } + visual.Actions = value + } + if v, ok := tfMap["chart_configuration"].([]interface{}); ok && len(v) > 0 { + visual.ChartConfiguration = expandComboChartConfiguration(v) + } + if v, ok := tfMap["column_hierarchies"].([]interface{}); ok && len(v) > 0 { + value, err := expandColumnHierarchies(v) + if err != nil { + return nil, err + } + visual.ColumnHierarchies = value + } + if v, ok := tfMap["subtitle"].([]interface{}); ok && len(v) > 0 { + visual.Subtitle = expandVisualSubtitleLabelOptions(v) + } + if v, ok := tfMap["title"].([]interface{}); ok && len(v) > 0 { + visual.Title = expandVisualTitleLabelOptions(v) + } + + return visual, nil +} + +func expandComboChartConfiguration(tfList []interface{}) *quicksight.ComboChartConfiguration { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + config := &quicksight.ComboChartConfiguration{} + + if v, ok := tfMap["bars_arrangement"].(string); ok && v != "" { + config.BarsArrangement = aws.String(v) + } + if v, ok := tfMap["bar_data_labels"].([]interface{}); ok && len(v) > 0 { + config.BarDataLabels = expandDataLabelOptions(v) + } + if v, ok := tfMap["category_axis"].([]interface{}); ok && len(v) > 0 { + config.CategoryAxis = expandAxisDisplayOptions(v) + } + if v, ok := tfMap["category_label_options"].([]interface{}); ok && len(v) > 0 { + config.CategoryLabelOptions = expandChartAxisLabelOptions(v) + } + if v, ok := tfMap["color_label_options"].([]interface{}); ok && len(v) > 0 { + config.ColorLabelOptions = expandChartAxisLabelOptions(v) + } + if v, ok := tfMap["field_wells"].([]interface{}); ok && len(v) > 0 { + config.FieldWells = expandComboChartFieldWells(v) + } + if v, ok := tfMap["legend"].([]interface{}); ok && len(v) > 0 { + config.Legend = expandLegendOptions(v) + } + if v, ok := tfMap["line_data_labels"].([]interface{}); ok && len(v) > 0 { + config.LineDataLabels = expandDataLabelOptions(v) + } + if v, ok := tfMap["primary_y_axis_display_options"].([]interface{}); ok && len(v) > 0 { + config.PrimaryYAxisDisplayOptions = expandAxisDisplayOptions(v) + } + if v, ok := tfMap["primary_y_axis_label_options"].([]interface{}); ok && len(v) > 0 { + config.PrimaryYAxisLabelOptions = expandChartAxisLabelOptions(v) + } + if v, ok := tfMap["reference_lines"].([]interface{}); ok && len(v) > 0 { + config.ReferenceLines = expandReferenceLines(v) + } + if v, ok := tfMap["secondary_y_axis_display_options"].([]interface{}); ok && len(v) > 0 { + config.SecondaryYAxisDisplayOptions = expandAxisDisplayOptions(v) + } + if v, ok := tfMap["secondary_y_axis_label_options"].([]interface{}); ok && len(v) > 0 { + config.SecondaryYAxisLabelOptions = expandChartAxisLabelOptions(v) + } + if v, ok := tfMap["sort_configuration"].([]interface{}); ok && len(v) > 0 { + config.SortConfiguration = expandComboChartSortConfiguration(v) + } + if v, ok := tfMap["tooltip"].([]interface{}); ok && len(v) > 0 { + config.Tooltip = expandTooltipOptions(v) + } + if v, ok := tfMap["visual_palette"].([]interface{}); ok && len(v) > 0 { + config.VisualPalette = expandVisualPalette(v) + } + + return config +} + +func expandComboChartFieldWells(tfList []interface{}) *quicksight.ComboChartFieldWells { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + config := &quicksight.ComboChartFieldWells{} + + if v, ok := tfMap["combo_chart_aggregated_field_wells"].([]interface{}); ok && len(v) > 0 { + config.ComboChartAggregatedFieldWells = expandComboChartAggregatedFieldWells(v) + } + + return config +} + +func expandComboChartAggregatedFieldWells(tfList []interface{}) *quicksight.ComboChartAggregatedFieldWells { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + config := &quicksight.ComboChartAggregatedFieldWells{} + + if v, ok := tfMap["bar_values"].([]interface{}); ok && len(v) > 0 { + config.BarValues = expandMeasureFields(v) + } + if v, ok := tfMap["category"].([]interface{}); ok && len(v) > 0 { + config.Category = expandDimensionFields(v) + } + if v, ok := tfMap["colors"].([]interface{}); ok && len(v) > 0 { + config.Colors = expandDimensionFields(v) + } + if v, ok := tfMap["line_values"].([]interface{}); ok && len(v) > 0 { + config.LineValues = expandMeasureFields(v) + } + + return config +} + +func expandComboChartSortConfiguration(tfList []interface{}) *quicksight.ComboChartSortConfiguration { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + config := &quicksight.ComboChartSortConfiguration{} + + if v, ok := tfMap["category_items_limit"].([]interface{}); ok && len(v) > 0 { + config.CategoryItemsLimit = expandItemsLimitConfiguration(v) + } + if v, ok := tfMap["category_sort"].([]interface{}); ok && len(v) > 0 { + config.CategorySort = expandFieldSortOptionsList(v) + } + if v, ok := tfMap["color_items_limit"].([]interface{}); ok && len(v) > 0 { + config.ColorItemsLimit = expandItemsLimitConfiguration(v) + } + if v, ok := tfMap["color_sort"].([]interface{}); ok && len(v) > 0 { + config.ColorSort = expandFieldSortOptionsList(v) + } + + return config +} diff --git a/internal/service/quicksight/visual_conditional_formatting.go b/internal/service/quicksight/visual_conditional_formatting.go new file mode 100644 index 000000000000..f1310553b978 --- /dev/null +++ b/internal/service/quicksight/visual_conditional_formatting.go @@ -0,0 +1,400 @@ +package quicksight + +import ( + "regexp" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/quicksight" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" +) + +func conditionalFormattingColorSchema() *schema.Schema { + return &schema.Schema{ // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_ConditionalFormattingColor.html + Type: schema.TypeList, + Required: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "gradient": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_ConditionalFormattingGradientColor.html + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "color": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_GradientColor.html + Type: schema.TypeList, + Required: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "stops": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_GradientStop.html + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 100, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "gradient_offset": { + Type: schema.TypeFloat, + Required: true, + }, + "color": stringSchema(false, validation.StringMatch(regexp.MustCompile(`^#[A-F0-9]{6}$`), "")), + "data_value": { + Type: schema.TypeFloat, + Optional: true, + }, + }, + }, + }, + }, + }, + }, + "expression": stringSchema(true, validation.StringLenBetween(1, 4096)), + }, + }, + }, + "solid": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_ConditionalFormattingSolidColor.html + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "color": stringSchema(false, validation.StringMatch(regexp.MustCompile(`^#[A-F0-9]{6}$`), "")), + "expression": stringSchema(true, validation.StringLenBetween(1, 4096)), + }, + }, + }, + }, + }, + } +} + +func conditionalFormattingIconSchema() *schema.Schema { + return &schema.Schema{ // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_ConditionalFormattingIcon.html + Type: schema.TypeList, + Required: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "custom_condition": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_ConditionalFormattingCustomIconCondition.html + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "color": stringSchema(false, validation.StringMatch(regexp.MustCompile(`^#[A-F0-9]{6}$`), "")), + "expression": stringSchema(true, validation.StringLenBetween(1, 4096)), + "icon_options": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_ConditionalFormattingCustomIconOptions.html + Type: schema.TypeList, + Required: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "icon": stringSchema(false, validation.StringInSlice(quicksight.Icon_Values(), false)), + "unicode_icon": stringSchema(false, validation.StringMatch(regexp.MustCompile(`^[^\\u0000-\\u00FF]$`), "")), + }, + }, + }, + "display_configuration": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_ConditionalFormattingIconDisplayConfiguration.html + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "icon_display_option": stringSchema(false, validation.StringInSlice(quicksight.ConditionalFormattingIconDisplayOption_Values(), false))}, + }, + }, + }, + }, + }, + "icon_set": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_ConditionalFormattingIconSet.html + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "expression": stringSchema(true, validation.StringLenBetween(1, 4096)), + "icon_set_type": stringSchema(false, validation.StringInSlice(quicksight.ConditionalFormattingIconSetType_Values(), false)), + }, + }, + }, + }, + }, + } +} + +func expandConditionalFormattingColor(tfList []interface{}) *quicksight.ConditionalFormattingColor { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + color := &quicksight.ConditionalFormattingColor{} + + if v, ok := tfMap["gradient"].([]interface{}); ok && len(v) > 0 { + color.Gradient = expandConditionalFormattingGradientColor(v) + } + if v, ok := tfMap["solid"].([]interface{}); ok && len(v) > 0 { + color.Solid = expandConditionalFormattingSolidColor(v) + } + + return color +} + +func expandConditionalFormattingGradientColor(tfList []interface{}) *quicksight.ConditionalFormattingGradientColor { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + color := &quicksight.ConditionalFormattingGradientColor{} + + if v, ok := tfMap["expression"].(string); ok && v != "" { + color.Expression = aws.String(v) + } + if v, ok := tfMap["color"].([]interface{}); ok && len(v) > 0 { + color.Color = expandGradientColor(v) + } + + return color +} + +func expandGradientColor(tfList []interface{}) *quicksight.GradientColor { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + color := &quicksight.GradientColor{} + + if v, ok := tfMap["stops"].([]interface{}); ok && len(v) > 0 { + color.Stops = expandGradientStops(v) + } + + return color +} + +func expandGradientStops(tfList []interface{}) []*quicksight.GradientStop { + if len(tfList) == 0 { + return nil + } + + var options []*quicksight.GradientStop + for _, tfMapRaw := range tfList { + tfMap, ok := tfMapRaw.(map[string]interface{}) + if !ok { + continue + } + + opts := expandGradientStop(tfMap) + if opts == nil { + continue + } + + options = append(options, opts) + } + + return options +} + +func expandGradientStop(tfMap map[string]interface{}) *quicksight.GradientStop { + if tfMap == nil { + return nil + } + + options := &quicksight.GradientStop{} + + if v, ok := tfMap["gradient_offset"].(float64); ok { + options.GradientOffset = aws.Float64(v) + } + if v, ok := tfMap["color"].(string); ok && v != "" { + options.Color = aws.String(v) + } + if v, ok := tfMap["data_value"].(float64); ok { + options.DataValue = aws.Float64(v) + } + + return options +} + +func expandConditionalFormattingSolidColor(tfList []interface{}) *quicksight.ConditionalFormattingSolidColor { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + color := &quicksight.ConditionalFormattingSolidColor{} + + if v, ok := tfMap["color"].(string); ok && v != "" { + color.Color = aws.String(v) + } + if v, ok := tfMap["expression"].(string); ok && v != "" { + color.Expression = aws.String(v) + } + + return color +} + +func expandConditionalFormattingIcon(tfList []interface{}) *quicksight.ConditionalFormattingIcon { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + icon := &quicksight.ConditionalFormattingIcon{} + + if v, ok := tfMap["custom_condition"].([]interface{}); ok && len(v) > 0 { + icon.CustomCondition = expandConditionalFormattingCustomIconCondition(v) + } + if v, ok := tfMap["icon_set"].([]interface{}); ok && len(v) > 0 { + icon.IconSet = expandConditionalFormattingIconSet(v) + } + + return icon +} + +func expandConditionalFormattingCustomIconCondition(tfList []interface{}) *quicksight.ConditionalFormattingCustomIconCondition { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + icon := &quicksight.ConditionalFormattingCustomIconCondition{} + + if v, ok := tfMap["color"].(string); ok && v != "" { + icon.Color = aws.String(v) + } + if v, ok := tfMap["expression"].(string); ok && v != "" { + icon.Expression = aws.String(v) + } + if v, ok := tfMap["icon_options"].([]interface{}); ok && len(v) > 0 { + icon.IconOptions = expandConditionalFormattingCustomIconOptions(v) + } + if v, ok := tfMap["display_configuration"].([]interface{}); ok && len(v) > 0 { + icon.DisplayConfiguration = expandConditionalFormattingIconDisplayConfiguration(v) + } + + return icon +} + +func expandConditionalFormattingCustomIconOptions(tfList []interface{}) *quicksight.ConditionalFormattingCustomIconOptions { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + options := &quicksight.ConditionalFormattingCustomIconOptions{} + + if v, ok := tfMap["icon"].(string); ok && v != "" { + options.Icon = aws.String(v) + } + if v, ok := tfMap["unicode_icon"].(string); ok && v != "" { + options.UnicodeIcon = aws.String(v) + } + + return options +} + +func expandConditionalFormattingIconDisplayConfiguration(tfList []interface{}) *quicksight.ConditionalFormattingIconDisplayConfiguration { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + config := &quicksight.ConditionalFormattingIconDisplayConfiguration{} + + if v, ok := tfMap["icon_display_option"].(string); ok && v != "" { + config.IconDisplayOption = aws.String(v) + } + + return config +} + +func expandConditionalFormattingIconSet(tfList []interface{}) *quicksight.ConditionalFormattingIconSet { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + options := &quicksight.ConditionalFormattingIconSet{} + + if v, ok := tfMap["expression"].(string); ok && v != "" { + options.Expression = aws.String(v) + } + if v, ok := tfMap["icon_set_type"].(string); ok && v != "" { + options.IconSetType = aws.String(v) + } + + return options +} + +func expandTextConditionalFormat(tfList []interface{}) *quicksight.TextConditionalFormat { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + options := &quicksight.TextConditionalFormat{} + + if v, ok := tfMap["background_color"].([]interface{}); ok && len(v) > 0 { + options.BackgroundColor = expandConditionalFormattingColor(v) + } + if v, ok := tfMap["icon"].([]interface{}); ok && len(v) > 0 { + options.Icon = expandConditionalFormattingIcon(v) + } + if v, ok := tfMap["text_color"].([]interface{}); ok && len(v) > 0 { + options.TextColor = expandConditionalFormattingColor(v) + } + + return options +} diff --git a/internal/service/quicksight/visual_custom_content.go b/internal/service/quicksight/visual_custom_content.go new file mode 100644 index 000000000000..58f7fbe5d3c9 --- /dev/null +++ b/internal/service/quicksight/visual_custom_content.go @@ -0,0 +1,102 @@ +package quicksight + +import ( + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/quicksight" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" +) + +func customContentVisualSchema() *schema.Schema { + return &schema.Schema{ // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_CustomContentVisual.html + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "data_set_identifier": stringSchema(true, validation.StringLenBetween(1, 2048)), + "visual_id": idSchema(), + "actions": visualCustomActionsSchema(customActionsMaxItems), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_VisualCustomAction.html + "chart_configuration": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_CustomContentConfiguration.html + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "content_type": stringSchema(false, validation.StringInSlice(quicksight.CustomContentType_Values(), false)), + "content_url": stringSchema(false, validation.StringLenBetween(1, 2048)), + "image_scaling": stringSchema(false, validation.StringInSlice(quicksight.CustomContentImageScalingConfiguration_Values(), false)), + }, + }, + }, + "subtitle": visualSubtitleLabelOptionsSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_VisualSubtitleLabelOptions.html + "title": visualTitleLabelOptionsSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_VisualTitleLabelOptions.html + }, + }, + } +} + +func expandCustomContentVisual(tfList []interface{}) (*quicksight.CustomContentVisual, error) { + if len(tfList) == 0 || tfList[0] == nil { + return nil, nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil, nil + } + + visual := &quicksight.CustomContentVisual{} + + if v, ok := tfMap["data_set_identifier"].(string); ok && v != "" { + visual.DataSetIdentifier = aws.String(v) + } + if v, ok := tfMap["visual_id"].(string); ok && v != "" { + visual.VisualId = aws.String(v) + } + if v, ok := tfMap["actions"].([]interface{}); ok && len(v) > 0 { + value, err := expandVisualCustomActions(v) + if err != nil { + return nil, err + } + visual.Actions = value + } + if v, ok := tfMap["chart_configuration"].([]interface{}); ok && len(v) > 0 { + visual.ChartConfiguration = expandCustomContentConfiguration(v) + } + if v, ok := tfMap["subtitle"].([]interface{}); ok && len(v) > 0 { + visual.Subtitle = expandVisualSubtitleLabelOptions(v) + } + if v, ok := tfMap["title"].([]interface{}); ok && len(v) > 0 { + visual.Title = expandVisualTitleLabelOptions(v) + } + + return visual, nil +} + +func expandCustomContentConfiguration(tfList []interface{}) *quicksight.CustomContentConfiguration { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + config := &quicksight.CustomContentConfiguration{} + + if v, ok := tfMap["content_type"].(string); ok && v != "" { + config.ContentType = aws.String(v) + } + if v, ok := tfMap["content_url"].(string); ok && v != "" { + config.ContentUrl = aws.String(v) + } + if v, ok := tfMap["image_scaling"].(string); ok && v != "" { + config.ImageScaling = aws.String(v) + } + + return config +} diff --git a/internal/service/quicksight/visual_empty.go b/internal/service/quicksight/visual_empty.go new file mode 100644 index 000000000000..d021c9948281 --- /dev/null +++ b/internal/service/quicksight/visual_empty.go @@ -0,0 +1,53 @@ +package quicksight + +import ( + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/quicksight" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" +) + +func emptyVisualSchema() *schema.Schema { + return &schema.Schema{ // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_EmptyVisual.html + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "data_set_identifier": stringSchema(true, validation.StringLenBetween(1, 2048)), + "visual_id": idSchema(), + "actions": visualCustomActionsSchema(customActionsMaxItems), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_VisualCustomAction.html + }, + }, + } +} + +func expandEmptyVisual(tfList []interface{}) (*quicksight.EmptyVisual, error) { + if len(tfList) == 0 || tfList[0] == nil { + return nil, nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil, nil + } + + visual := &quicksight.EmptyVisual{} + + if v, ok := tfMap["data_set_identifier"].(string); ok && v != "" { + visual.DataSetIdentifier = aws.String(v) + } + if v, ok := tfMap["visual_id"].(string); ok && v != "" { + visual.VisualId = aws.String(v) + } + if v, ok := tfMap["actions"].([]interface{}); ok && len(v) > 0 { + value, err := expandVisualCustomActions(v) + if err != nil { + return nil, err + } + visual.Actions = value + } + + return visual, nil +} diff --git a/internal/service/quicksight/visual_fields.go b/internal/service/quicksight/visual_fields.go new file mode 100644 index 000000000000..03821d910c93 --- /dev/null +++ b/internal/service/quicksight/visual_fields.go @@ -0,0 +1,443 @@ +package quicksight + +import ( + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/quicksight" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" +) + +func dimensionFieldSchema(maxItems int) *schema.Schema { + return &schema.Schema{ // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_DimensionField.html + Type: schema.TypeList, + MinItems: 1, + MaxItems: maxItems, + Optional: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "categorical_dimension_field": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_CategoricalDimensionField.html + Type: schema.TypeList, + MinItems: 1, + MaxItems: 1, + Optional: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "column": columnSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_ColumnIdentifier.html + "field_id": stringSchema(true, validation.StringLenBetween(1, 512)), + "format_configuration": stringFormatConfigurationSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_StringFormatConfiguration.html + "hierarchy_id": stringSchema(false, validation.StringLenBetween(1, 512)), + }, + }, + }, + "date_dimension_field": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_DateDimensionField.html + Type: schema.TypeList, + MinItems: 1, + MaxItems: 1, + Optional: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "column": columnSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_ColumnIdentifier.html + "field_id": stringSchema(true, validation.StringLenBetween(1, 512)), + "date_granularity": stringSchema(false, validation.StringInSlice(quicksight.TimeGranularity_Values(), false)), + "format_configuration": dateTimeFormatConfigurationSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_DateTimeFormatConfiguration.html + "hierarchy_id": stringSchema(false, validation.StringLenBetween(1, 512)), + }, + }, + }, + "numerical_dimension_field": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_NumericalDimensionField.html + Type: schema.TypeList, + MinItems: 1, + MaxItems: 1, + Optional: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "column": columnSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_ColumnIdentifier.html + "field_id": stringSchema(true, validation.StringLenBetween(1, 512)), + "format_configuration": numberFormatConfigurationSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_NumberFormatConfiguration.html + "hierarchy_id": stringSchema(false, validation.StringLenBetween(1, 512)), + }, + }, + }, + }, + }, + } +} + +func measureFieldSchema(maxItems int) *schema.Schema { + return &schema.Schema{ // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_MeasureField.html + Type: schema.TypeList, + MinItems: 1, + MaxItems: maxItems, + Optional: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "calculated_measure_field": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_CalculatedMeasureField.html + Type: schema.TypeList, + MinItems: 1, + MaxItems: 1, + Optional: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "expression": stringSchema(true, validation.StringLenBetween(1, 4096)), + "field_id": stringSchema(true, validation.StringLenBetween(1, 512)), + }, + }, + }, + "categorical_measure_field": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_CategoricalMeasureField.html + Type: schema.TypeList, + MinItems: 1, + MaxItems: 1, + Optional: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "column": columnSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_ColumnIdentifier.html + "field_id": stringSchema(true, validation.StringLenBetween(1, 512)), + "aggregation_function": stringSchema(false, validation.StringInSlice(quicksight.CategoricalAggregationFunction_Values(), false)), + "format_configuration": stringFormatConfigurationSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_StringFormatConfiguration.html + }, + }, + }, + "date_measure_field": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_DateMeasureField.html + Type: schema.TypeList, + MinItems: 1, + MaxItems: 1, + Optional: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "column": columnSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_ColumnIdentifier.html + "field_id": stringSchema(true, validation.StringLenBetween(1, 512)), + "aggregation_function": stringSchema(false, validation.StringInSlice(quicksight.DateAggregationFunction_Values(), false)), + "format_configuration": dateTimeFormatConfigurationSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_DateTimeFormatConfiguration.html + }, + }, + }, + "numerical_measure_field": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_NumericalMeasureField.html + Type: schema.TypeList, + MinItems: 1, + MaxItems: 1, + Optional: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "column": columnSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_ColumnIdentifier.html + "field_id": stringSchema(true, validation.StringLenBetween(1, 512)), + "aggregation_function": numericalAggregationFunctionSchema(false), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_NumericalAggregationFunction.html + "format_configuration": numberFormatConfigurationSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_NumberFormatConfiguration.html + }, + }, + }, + }, + }, + } +} + +func expandDimensionFields(tfList []interface{}) []*quicksight.DimensionField { + if len(tfList) == 0 { + return nil + } + + var fields []*quicksight.DimensionField + for _, tfMapRaw := range tfList { + tfMap, ok := tfMapRaw.(map[string]interface{}) + if !ok { + continue + } + + field := expandDimensionInternal(tfMap) + if field == nil { + continue + } + + fields = append(fields, field) + } + + return fields +} + +func expandDimensionInternal(tfMap map[string]interface{}) *quicksight.DimensionField { + if tfMap == nil { + return nil + } + + field := &quicksight.DimensionField{} + + if v, ok := tfMap["categorical_dimension_field"].([]interface{}); ok && len(v) > 0 { + field.CategoricalDimensionField = expandCategoricalDimensionField(v) + } + if v, ok := tfMap["date_dimension_field"].([]interface{}); ok && len(v) > 0 { + field.DateDimensionField = expandDateDimensionField(v) + } + if v, ok := tfMap["numerical_dimension_field"].([]interface{}); ok && len(v) > 0 { + field.NumericalDimensionField = expandNumericalDimensionField(v) + } + + return field +} + +func expandDimensionField(tfList []interface{}) *quicksight.DimensionField { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + if tfMap == nil { + return nil + } + + return expandDimensionInternal(tfMap) +} + +func expandCategoricalDimensionField(tfList []interface{}) *quicksight.CategoricalDimensionField { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + field := &quicksight.CategoricalDimensionField{} + + if v, ok := tfMap["field_id"].(string); ok && v != "" { + field.FieldId = aws.String(v) + } + if v, ok := tfMap["hierarchy_id"].(string); ok && v != "" { + field.HierarchyId = aws.String(v) + } + if v, ok := tfMap["column"].([]interface{}); ok && len(v) > 0 { + field.Column = expandColumnIdentifier(v) + } + if v, ok := tfMap["format_configuration"].([]interface{}); ok && len(v) > 0 { + field.FormatConfiguration = expandStringFormatConfiguration(v) + } + + return field +} + +func expandDateDimensionField(tfList []interface{}) *quicksight.DateDimensionField { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + field := &quicksight.DateDimensionField{} + + if v, ok := tfMap["field_id"].(string); ok && v != "" { + field.FieldId = aws.String(v) + } + if v, ok := tfMap["hierarchy_id"].(string); ok && v != "" { + field.HierarchyId = aws.String(v) + } + if v, ok := tfMap["date_granularity"].(string); ok && v != "" { + field.DateGranularity = aws.String(v) + } + if v, ok := tfMap["column"].([]interface{}); ok && len(v) > 0 { + field.Column = expandColumnIdentifier(v) + } + if v, ok := tfMap["format_configuration"].([]interface{}); ok && len(v) > 0 { + field.FormatConfiguration = expandDateTimeFormatConfiguration(v) + } + + return field +} + +func expandNumericalDimensionField(tfList []interface{}) *quicksight.NumericalDimensionField { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + field := &quicksight.NumericalDimensionField{} + + if v, ok := tfMap["field_id"].(string); ok && v != "" { + field.FieldId = aws.String(v) + } + if v, ok := tfMap["hierarchy_id"].(string); ok && v != "" { + field.HierarchyId = aws.String(v) + } + if v, ok := tfMap["column"].([]interface{}); ok && len(v) > 0 { + field.Column = expandColumnIdentifier(v) + } + if v, ok := tfMap["format_configuration"].([]interface{}); ok && len(v) > 0 { + field.FormatConfiguration = expandNumberFormatConfiguration(v) + } + + return field +} + +func expandMeasureFields(tfList []interface{}) []*quicksight.MeasureField { + if len(tfList) == 0 { + return nil + } + + var fields []*quicksight.MeasureField + for _, tfMapRaw := range tfList { + tfMap, ok := tfMapRaw.(map[string]interface{}) + if !ok { + continue + } + + field := expandMeasureFieldInternal(tfMap) + if field == nil { + continue + } + + fields = append(fields, field) + } + + return fields +} + +func expandMeasureFieldInternal(tfMap map[string]interface{}) *quicksight.MeasureField { + if tfMap == nil { + return nil + } + + field := &quicksight.MeasureField{} + + if v, ok := tfMap["calculated_measure_field"].([]interface{}); ok && len(v) > 0 { + field.CalculatedMeasureField = expandCalculatedMeasureField(v) + } + if v, ok := tfMap["categorical_measure_field"].([]interface{}); ok && len(v) > 0 { + field.CategoricalMeasureField = expandCategoricalMeasureField(v) + } + if v, ok := tfMap["date_measure_field"].([]interface{}); ok && len(v) > 0 { + field.DateMeasureField = expandDateMeasureField(v) + } + if v, ok := tfMap["numerical_measure_field"].([]interface{}); ok && len(v) > 0 { + field.NumericalMeasureField = expandNumericalMeasureField(v) + } + + return field +} + +func expandMeasureField(tfList []interface{}) *quicksight.MeasureField { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + return expandMeasureFieldInternal(tfMap) +} + +func expandCalculatedMeasureField(tfList []interface{}) *quicksight.CalculatedMeasureField { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + field := &quicksight.CalculatedMeasureField{} + + if v, ok := tfMap["field_id"].(string); ok && v != "" { + field.FieldId = aws.String(v) + } + if v, ok := tfMap["expression"].(string); ok && v != "" { + field.Expression = aws.String(v) + } + + return field +} + +func expandCategoricalMeasureField(tfList []interface{}) *quicksight.CategoricalMeasureField { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + field := &quicksight.CategoricalMeasureField{} + + if v, ok := tfMap["field_id"].(string); ok && v != "" { + field.FieldId = aws.String(v) + } + if v, ok := tfMap["aggregation_function"].(string); ok && v != "" { + field.AggregationFunction = aws.String(v) + } + if v, ok := tfMap["column"].([]interface{}); ok && len(v) > 0 { + field.Column = expandColumnIdentifier(v) + } + if v, ok := tfMap["format_configuration"].([]interface{}); ok && len(v) > 0 { + field.FormatConfiguration = expandStringFormatConfiguration(v) + } + + return field +} + +func expandDateMeasureField(tfList []interface{}) *quicksight.DateMeasureField { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + field := &quicksight.DateMeasureField{} + + if v, ok := tfMap["field_id"].(string); ok && v != "" { + field.FieldId = aws.String(v) + } + if v, ok := tfMap["aggregation_function"].(string); ok && v != "" { + field.AggregationFunction = aws.String(v) + } + if v, ok := tfMap["column"].([]interface{}); ok && len(v) > 0 { + field.Column = expandColumnIdentifier(v) + } + if v, ok := tfMap["format_configuration"].([]interface{}); ok && len(v) > 0 { + field.FormatConfiguration = expandDateTimeFormatConfiguration(v) + } + + return field +} + +func expandNumericalMeasureField(tfList []interface{}) *quicksight.NumericalMeasureField { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + field := &quicksight.NumericalMeasureField{} + + if v, ok := tfMap["field_id"].(string); ok && v != "" { + field.FieldId = aws.String(v) + } + if v, ok := tfMap["column"].([]interface{}); ok && len(v) > 0 { + field.Column = expandColumnIdentifier(v) + } + if v, ok := tfMap["aggregation_function"].([]interface{}); ok && len(v) > 0 { + field.AggregationFunction = expandNumericalAggregationFunction(v) + } + if v, ok := tfMap["format_configuration"].([]interface{}); ok && len(v) > 0 { + field.FormatConfiguration = expandNumberFormatConfiguration(v) + } + + return field +} diff --git a/internal/service/quicksight/visual_filled_map.go b/internal/service/quicksight/visual_filled_map.go new file mode 100644 index 000000000000..ad1680cba250 --- /dev/null +++ b/internal/service/quicksight/visual_filled_map.go @@ -0,0 +1,351 @@ +package quicksight + +import ( + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/quicksight" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" +) + +func filledMapVisualSchema() *schema.Schema { + return &schema.Schema{ // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_FilledMapVisual.html + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "visual_id": idSchema(), + "actions": visualCustomActionsSchema(customActionsMaxItems), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_VisualCustomAction.html + "chart_configuration": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_FilledMapConfiguration.html + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "field_wells": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_FilledMapFieldWells.html + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "filled_map_aggregated_field_wells": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_FilledMapAggregatedFieldWells.html + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "geospatial": dimensionFieldSchema(1), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_DimensionField.html + "values": measureFieldSchema(1), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_MeasureField.html + }, + }, + }, + }, + }, + }, + "legend": legendOptionsSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_LegendOptions.html + "map_style_options": geospatialMapStyleOptionsSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_GeospatialMapStyleOptions.html + "sort_configuration": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_FilledMapSortConfiguration.html + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "category_sort": fieldSortOptionsSchema(100), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_FieldSortOptions.html, + }, + }, + }, + "tooltip": tooltipOptionsSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_TooltipOptions.html + "window_options": geospatialWindowOptionsSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_GeospatialWindowOptions.html + }, + }, + }, + "column_hierarchies": columnHierarchiesSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_ColumnHierarchy.html + "conditional_formatting": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_FilledMapConditionalFormatting.html + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "conditional_formatting_options": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_FilledMapConditionalFormattingOption.html + Type: schema.TypeList, + Required: true, + MinItems: 1, + MaxItems: 200, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "shape": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_FilledMapShapeConditionalFormatting.html + Type: schema.TypeList, + Required: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "field_id": stringSchema(true, validation.StringLenBetween(1, 512)), + "format": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_ShapeConditionalFormat.html + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "background_color": conditionalFormattingColorSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_ConditionalFormattingColor.html + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + "subtitle": visualSubtitleLabelOptionsSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_VisualSubtitleLabelOptions.html + "title": visualTitleLabelOptionsSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_VisualTitleLabelOptions.html + }, + }, + } +} + +func expandFilledMapVisual(tfList []interface{}) (*quicksight.FilledMapVisual, error) { + if len(tfList) == 0 || tfList[0] == nil { + return nil, nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil, nil + } + + visual := &quicksight.FilledMapVisual{} + + if v, ok := tfMap["visual_id"].(string); ok && v != "" { + visual.VisualId = aws.String(v) + } + if v, ok := tfMap["actions"].([]interface{}); ok && len(v) > 0 { + value, err := expandVisualCustomActions(v) + if err != nil { + return nil, err + } + visual.Actions = value + } + if v, ok := tfMap["chart_configuration"].([]interface{}); ok && len(v) > 0 { + visual.ChartConfiguration = expandFilledMapConfiguration(v) + } + if v, ok := tfMap["conditional_formatting"].([]interface{}); ok && len(v) > 0 { + visual.ConditionalFormatting = expandFilledMapConditionalFormatting(v) + } + if v, ok := tfMap["column_hierarchies"].([]interface{}); ok && len(v) > 0 { + value, err := expandColumnHierarchies(v) + if err != nil { + return nil, err + } + visual.ColumnHierarchies = value + } + if v, ok := tfMap["subtitle"].([]interface{}); ok && len(v) > 0 { + visual.Subtitle = expandVisualSubtitleLabelOptions(v) + } + if v, ok := tfMap["title"].([]interface{}); ok && len(v) > 0 { + visual.Title = expandVisualTitleLabelOptions(v) + } + + return visual, nil +} + +func expandFilledMapConfiguration(tfList []interface{}) *quicksight.FilledMapConfiguration { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + config := &quicksight.FilledMapConfiguration{} + + if v, ok := tfMap["field_wells"].([]interface{}); ok && len(v) > 0 { + config.FieldWells = expandFilledMapFieldWells(v) + } + if v, ok := tfMap["legend"].([]interface{}); ok && len(v) > 0 { + config.Legend = expandLegendOptions(v) + } + if v, ok := tfMap["map_style_options"].([]interface{}); ok && len(v) > 0 { + config.MapStyleOptions = expandGeospatialMapStyleOptions(v) + } + if v, ok := tfMap["sort_configuration"].([]interface{}); ok && len(v) > 0 { + config.SortConfiguration = expandFilledMapSortConfiguration(v) + } + if v, ok := tfMap["tooltip"].([]interface{}); ok && len(v) > 0 { + config.Tooltip = expandTooltipOptions(v) + } + if v, ok := tfMap["value_axis"].([]interface{}); ok && len(v) > 0 { + config.WindowOptions = expandGeospatialWindowOptions(v) + } + + return config +} + +func expandFilledMapFieldWells(tfList []interface{}) *quicksight.FilledMapFieldWells { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + config := &quicksight.FilledMapFieldWells{} + + if v, ok := tfMap["filled_map_aggregated_field_wells"].([]interface{}); ok && len(v) > 0 { + config.FilledMapAggregatedFieldWells = expandFilledMapAggregatedFieldWells(v) + } + + return config +} + +func expandFilledMapAggregatedFieldWells(tfList []interface{}) *quicksight.FilledMapAggregatedFieldWells { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + config := &quicksight.FilledMapAggregatedFieldWells{} + + if v, ok := tfMap["geospatial"].([]interface{}); ok && len(v) > 0 { + config.Geospatial = expandDimensionFields(v) + } + if v, ok := tfMap["values"].([]interface{}); ok && len(v) > 0 { + config.Values = expandMeasureFields(v) + } + + return config +} + +func expandFilledMapSortConfiguration(tfList []interface{}) *quicksight.FilledMapSortConfiguration { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + config := &quicksight.FilledMapSortConfiguration{} + + if v, ok := tfMap["category_sort"].([]interface{}); ok && len(v) > 0 { + config.CategorySort = expandFieldSortOptionsList(v) + } + + return config +} + +func expandFilledMapConditionalFormatting(tfList []interface{}) *quicksight.FilledMapConditionalFormatting { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + config := &quicksight.FilledMapConditionalFormatting{} + + if v, ok := tfMap["conditional_formatting_options"].([]interface{}); ok && len(v) > 0 { + config.ConditionalFormattingOptions = expandFilledMapConditionalFormattingOptions(v) + } + + return config +} + +func expandFilledMapConditionalFormattingOptions(tfList []interface{}) []*quicksight.FilledMapConditionalFormattingOption { + if len(tfList) == 0 { + return nil + } + + var options []*quicksight.FilledMapConditionalFormattingOption + for _, tfMapRaw := range tfList { + tfMap, ok := tfMapRaw.(map[string]interface{}) + if !ok { + continue + } + + opts := expandFilledMapConditionalFormattingOption(tfMap) + if opts == nil { + continue + } + + options = append(options, opts) + } + + return options +} + +func expandFilledMapConditionalFormattingOption(tfMap map[string]interface{}) *quicksight.FilledMapConditionalFormattingOption { + if tfMap == nil { + return nil + } + + options := &quicksight.FilledMapConditionalFormattingOption{} + + if v, ok := tfMap["shape"].([]interface{}); ok && len(v) > 0 { + options.Shape = expandFilledMapShapeConditionalFormatting(v) + } + + return options +} + +func expandFilledMapShapeConditionalFormatting(tfList []interface{}) *quicksight.FilledMapShapeConditionalFormatting { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + options := &quicksight.FilledMapShapeConditionalFormatting{} + + if v, ok := tfMap["field_id"].(string); ok && v != "" { + options.FieldId = aws.String(v) + } + if v, ok := tfMap["format"].([]interface{}); ok && len(v) > 0 { + options.Format = expandShapeConditionalFormat(v) + } + + return options +} + +func expandShapeConditionalFormat(tfList []interface{}) *quicksight.ShapeConditionalFormat { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + options := &quicksight.ShapeConditionalFormat{} + + if v, ok := tfMap["background_color"].([]interface{}); ok && len(v) > 0 { + options.BackgroundColor = expandConditionalFormattingColor(v) + } + + return options +} diff --git a/internal/service/quicksight/visual_funnel_chart.go b/internal/service/quicksight/visual_funnel_chart.go new file mode 100644 index 000000000000..c2ef78b44664 --- /dev/null +++ b/internal/service/quicksight/visual_funnel_chart.go @@ -0,0 +1,272 @@ +package quicksight + +import ( + "regexp" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/quicksight" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" +) + +func funnelChartVisualSchema() *schema.Schema { + return &schema.Schema{ // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_FunnelChartVisual.html + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "visual_id": idSchema(), + "actions": visualCustomActionsSchema(customActionsMaxItems), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_VisualCustomAction.html + "chart_configuration": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_FunnelChartConfiguration.html + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "category_label_options": chartAxisLabelOptionsSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_ChartAxisLabelOptions.html + "data_label_options": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_FunnelChartDataLabelOptions.html + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "category_label_visibility": stringSchema(false, validation.StringInSlice(quicksight.Visibility_Values(), false)), + "label_color": stringSchema(false, validation.StringMatch(regexp.MustCompile(`^#[A-F0-9]{6}$`), "")), + "label_font_configuration": fontConfigurationSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_FontConfiguration.html + "measure_data_label_style": stringSchema(false, validation.StringInSlice(quicksight.FunnelChartMeasureDataLabelStyle_Values(), false)), + "measure_label_visibility": stringSchema(false, validation.StringInSlice(quicksight.Visibility_Values(), false)), + "position": stringSchema(false, validation.StringInSlice(quicksight.DataLabelPosition_Values(), false)), + "visibility": stringSchema(false, validation.StringInSlice(quicksight.Visibility_Values(), false)), + }, + }, + }, + "field_wells": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_FunnelChartFieldWells.html + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "funnel_chart_aggregated_field_wells": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_FunnelChartAggregatedFieldWells.html + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "category": dimensionFieldSchema(1), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_DimensionField.html + "values": measureFieldSchema(1), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_MeasureField.html + }, + }, + }, + }, + }, + }, + "sort_configuration": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_FunnelChartSortConfiguration.html + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "category_items_limit": itemsLimitConfigurationSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_ItemsLimitConfiguration.html + "category_sort": fieldSortOptionsSchema(100), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_FieldSortOptions.html, + }, + }, + }, + "tooltip": tooltipOptionsSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_TooltipOptions.html + "value_label_options": chartAxisLabelOptionsSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_ChartAxisLabelOptions.html + "visual_palette": visualPaletteSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_VisualPalette.html + }, + }, + }, + "column_hierarchies": columnHierarchiesSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_ColumnHierarchy.html + "subtitle": visualSubtitleLabelOptionsSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_VisualSubtitleLabelOptions.html + "title": visualTitleLabelOptionsSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_VisualTitleLabelOptions.html + }, + }, + } +} + +func expandFunnelChartVisual(tfList []interface{}) (*quicksight.FunnelChartVisual, error) { + if len(tfList) == 0 || tfList[0] == nil { + return nil, nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil, nil + } + + visual := &quicksight.FunnelChartVisual{} + + if v, ok := tfMap["visual_id"].(string); ok && v != "" { + visual.VisualId = aws.String(v) + } + if v, ok := tfMap["actions"].([]interface{}); ok && len(v) > 0 { + value, err := expandVisualCustomActions(v) + if err != nil { + return nil, err + } + visual.Actions = value + } + if v, ok := tfMap["chart_configuration"].([]interface{}); ok && len(v) > 0 { + visual.ChartConfiguration = expandFunnelChartConfiguration(v) + } + if v, ok := tfMap["column_hierarchies"].([]interface{}); ok && len(v) > 0 { + value, err := expandColumnHierarchies(v) + if err != nil { + return nil, err + } + visual.ColumnHierarchies = value + } + if v, ok := tfMap["subtitle"].([]interface{}); ok && len(v) > 0 { + visual.Subtitle = expandVisualSubtitleLabelOptions(v) + } + if v, ok := tfMap["title"].([]interface{}); ok && len(v) > 0 { + visual.Title = expandVisualTitleLabelOptions(v) + } + + return visual, nil +} + +func expandFunnelChartConfiguration(tfList []interface{}) *quicksight.FunnelChartConfiguration { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + config := &quicksight.FunnelChartConfiguration{} + + if v, ok := tfMap["category_label_options"].([]interface{}); ok && len(v) > 0 { + config.CategoryLabelOptions = expandChartAxisLabelOptions(v) + } + if v, ok := tfMap["data_label_options"].([]interface{}); ok && len(v) > 0 { + config.DataLabelOptions = expandFunnelChartDataLabelOptions(v) + } + if v, ok := tfMap["field_wells"].([]interface{}); ok && len(v) > 0 { + config.FieldWells = expandFunnelChartFieldWells(v) + } + if v, ok := tfMap["sort_configuration"].([]interface{}); ok && len(v) > 0 { + config.SortConfiguration = expandFunnelChartSortConfiguration(v) + } + if v, ok := tfMap["tooltip"].([]interface{}); ok && len(v) > 0 { + config.Tooltip = expandTooltipOptions(v) + } + if v, ok := tfMap["value_label_options"].([]interface{}); ok && len(v) > 0 { + config.ValueLabelOptions = expandChartAxisLabelOptions(v) + } + if v, ok := tfMap["visual_palette"].([]interface{}); ok && len(v) > 0 { + config.VisualPalette = expandVisualPalette(v) + } + + return config +} + +func expandFunnelChartFieldWells(tfList []interface{}) *quicksight.FunnelChartFieldWells { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + config := &quicksight.FunnelChartFieldWells{} + + if v, ok := tfMap["funnel_chart_aggregated_field_wells"].([]interface{}); ok && len(v) > 0 { + config.FunnelChartAggregatedFieldWells = expandFunnelChartAggregatedFieldWells(v) + } + + return config +} + +func expandFunnelChartAggregatedFieldWells(tfList []interface{}) *quicksight.FunnelChartAggregatedFieldWells { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + config := &quicksight.FunnelChartAggregatedFieldWells{} + + if v, ok := tfMap["category"].([]interface{}); ok && len(v) > 0 { + config.Category = expandDimensionFields(v) + } + if v, ok := tfMap["values"].([]interface{}); ok && len(v) > 0 { + config.Values = expandMeasureFields(v) + } + + return config +} + +func expandFunnelChartSortConfiguration(tfList []interface{}) *quicksight.FunnelChartSortConfiguration { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + config := &quicksight.FunnelChartSortConfiguration{} + + if v, ok := tfMap["category_items_limit"].([]interface{}); ok && len(v) > 0 { + config.CategoryItemsLimit = expandItemsLimitConfiguration(v) + } + if v, ok := tfMap["category_sort"].([]interface{}); ok && len(v) > 0 { + config.CategorySort = expandFieldSortOptionsList(v) + } + + return config +} + +func expandFunnelChartDataLabelOptions(tfList []interface{}) *quicksight.FunnelChartDataLabelOptions { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + options := &quicksight.FunnelChartDataLabelOptions{} + + if v, ok := tfMap["category_label_visibility"].(string); ok && v != "" { + options.CategoryLabelVisibility = aws.String(v) + } + if v, ok := tfMap["label_color"].(string); ok && v != "" { + options.LabelColor = aws.String(v) + } + if v, ok := tfMap["measure_data_label_style"].(string); ok && v != "" { + options.MeasureDataLabelStyle = aws.String(v) + } + if v, ok := tfMap["measure_label_visibility"].(string); ok && v != "" { + options.MeasureLabelVisibility = aws.String(v) + } + if v, ok := tfMap["position"].(string); ok && v != "" { + options.Position = aws.String(v) + } + if v, ok := tfMap["visibility"].(string); ok && v != "" { + options.Visibility = aws.String(v) + } + if v, ok := tfMap["label_font_configuration"].([]interface{}); ok && len(v) > 0 { + options.LabelFontConfiguration = expandFontConfiguration(v) + } + + return options +} diff --git a/internal/service/quicksight/visual_gauge_chart.go b/internal/service/quicksight/visual_gauge_chart.go new file mode 100644 index 000000000000..a0d404ad8f30 --- /dev/null +++ b/internal/service/quicksight/visual_gauge_chart.go @@ -0,0 +1,441 @@ +package quicksight + +import ( + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/quicksight" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" +) + +func gaugeChartVisualSchema() *schema.Schema { + return &schema.Schema{ // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_GaugeChartVisual.html + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "visual_id": idSchema(), + "actions": visualCustomActionsSchema(customActionsMaxItems), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_VisualCustomAction.html + "chart_configuration": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_GaugeChartConfiguration.html + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "data_labels": dataLabelOptionsSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_DataLabelOptions.html + "field_wells": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_GaugeChartFieldWells.html + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "target_values": measureFieldSchema(200), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_MeasureField.html + "values": measureFieldSchema(200), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_MeasureField.html + }, + }, + }, + "gauge_chart_options": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_GaugeChartOptions.html + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "arc": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_ArcConfiguration.html + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "arc_angle": { + Type: schema.TypeFloat, + Optional: true, + }, + "arc_thickness": stringSchema(false, validation.StringInSlice(quicksight.ArcThicknessOptions_Values(), false)), + }, + }, + }, + "arc_axis": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_ArcAxisConfiguration.html + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "range": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_ArcAxisDisplayRange.html + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "max": { + Type: schema.TypeFloat, + Optional: true, + }, + "min": { + Type: schema.TypeFloat, + Optional: true, + }, + }, + }, + }, + "reserve_angle": { + Type: schema.TypeInt, + Optional: true, + }, + }, + }, + }, + "comparison": comparisonConfigurationSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_ComparisonConfiguration.html + "primary_value_display_type": stringSchema(false, validation.StringInSlice(quicksight.PrimaryValueDisplayType_Values(), false)), + "primary_value_font_configuration": fontConfigurationSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_FontConfiguration.html + }, + }, + }, + "tooltip": tooltipOptionsSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_TooltipOptions.html + "visual_palette": visualPaletteSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_VisualPalette.html + }, + }, + }, + "conditional_formatting": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_GaugeChartConditionalFormatting.html + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "conditional_formatting_options": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_GaugeChartConditionalFormattingOption.html + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 100, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "arc": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_GaugeChartArcConditionalFormatting.html + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "foreground_color": conditionalFormattingColorSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_ConditionalFormattingColor.html + }, + }, + }, + "primary_value": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_GaugeChartPrimaryValueConditionalFormatting.html + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "icon": conditionalFormattingIconSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_ConditionalFormattingIcon.html + "text_color": conditionalFormattingColorSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_ConditionalFormattingColor.html + }, + }, + }, + }, + }, + }, + }, + }, + }, + "subtitle": visualSubtitleLabelOptionsSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_VisualSubtitleLabelOptions.html + "title": visualTitleLabelOptionsSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_VisualTitleLabelOptions.html + }, + }, + } +} + +func expandGaugeChartVisual(tfList []interface{}) (*quicksight.GaugeChartVisual, error) { + if len(tfList) == 0 || tfList[0] == nil { + return nil, nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil, nil + } + + visual := &quicksight.GaugeChartVisual{} + + if v, ok := tfMap["visual_id"].(string); ok && v != "" { + visual.VisualId = aws.String(v) + } + if v, ok := tfMap["actions"].([]interface{}); ok && len(v) > 0 { + value, err := expandVisualCustomActions(v) + if err != nil { + return nil, err + } + visual.Actions = value + } + if v, ok := tfMap["chart_configuration"].([]interface{}); ok && len(v) > 0 { + visual.ChartConfiguration = expandGaugeChartConfiguration(v) + } + if v, ok := tfMap["conditional_formatting"].([]interface{}); ok && len(v) > 0 { + visual.ConditionalFormatting = expandGaugeChartConditionalFormatting(v) + } + if v, ok := tfMap["subtitle"].([]interface{}); ok && len(v) > 0 { + visual.Subtitle = expandVisualSubtitleLabelOptions(v) + } + if v, ok := tfMap["title"].([]interface{}); ok && len(v) > 0 { + visual.Title = expandVisualTitleLabelOptions(v) + } + + return visual, nil +} + +func expandGaugeChartConfiguration(tfList []interface{}) *quicksight.GaugeChartConfiguration { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + config := &quicksight.GaugeChartConfiguration{} + + if v, ok := tfMap["data_labels"].([]interface{}); ok && len(v) > 0 { + config.DataLabels = expandDataLabelOptions(v) + } + if v, ok := tfMap["field_wells"].([]interface{}); ok && len(v) > 0 { + config.FieldWells = expandGaugeChartFieldWells(v) + } + if v, ok := tfMap["gauge_chart_options"].([]interface{}); ok && len(v) > 0 { + config.GaugeChartOptions = expandGaugeChartOptions(v) + } + if v, ok := tfMap["tooltip"].([]interface{}); ok && len(v) > 0 { + config.TooltipOptions = expandTooltipOptions(v) + } + if v, ok := tfMap["visual_palette"].([]interface{}); ok && len(v) > 0 { + config.VisualPalette = expandVisualPalette(v) + } + + return config +} + +func expandGaugeChartFieldWells(tfList []interface{}) *quicksight.GaugeChartFieldWells { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + config := &quicksight.GaugeChartFieldWells{} + + if v, ok := tfMap["target_values"].([]interface{}); ok && len(v) > 0 { + config.TargetValues = expandMeasureFields(v) + } + if v, ok := tfMap["values"].([]interface{}); ok && len(v) > 0 { + config.Values = expandMeasureFields(v) + } + + return config +} + +func expandGaugeChartOptions(tfList []interface{}) *quicksight.GaugeChartOptions { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + options := &quicksight.GaugeChartOptions{} + + if v, ok := tfMap["primary_value_display_type"].(string); ok && v != "" { + options.PrimaryValueDisplayType = aws.String(v) + } + if v, ok := tfMap["primary_value_font_configuration"].([]interface{}); ok && len(v) > 0 { + options.PrimaryValueFontConfiguration = expandFontConfiguration(v) + } + if v, ok := tfMap["arc"].([]interface{}); ok && len(v) > 0 { + options.Arc = expandArcConfiguration(v) + } + if v, ok := tfMap["arc_axis"].([]interface{}); ok && len(v) > 0 { + options.ArcAxis = expandArcAxisConfiguration(v) + } + if v, ok := tfMap["comparison"].([]interface{}); ok && len(v) > 0 { + options.Comparison = expandComparisonConfiguration(v) + } + + return options +} + +func expandGaugeChartConditionalFormatting(tfList []interface{}) *quicksight.GaugeChartConditionalFormatting { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + config := &quicksight.GaugeChartConditionalFormatting{} + + if v, ok := tfMap["conditional_formatting_options"].([]interface{}); ok && len(v) > 0 { + config.ConditionalFormattingOptions = expandGaugeChartConditionalFormattingOptions(v) + } + + return config +} + +func expandGaugeChartConditionalFormattingOptions(tfList []interface{}) []*quicksight.GaugeChartConditionalFormattingOption { + if len(tfList) == 0 { + return nil + } + + var options []*quicksight.GaugeChartConditionalFormattingOption + for _, tfMapRaw := range tfList { + tfMap, ok := tfMapRaw.(map[string]interface{}) + if !ok { + continue + } + + opts := expandGaugeChartConditionalFormattingOption(tfMap) + if opts == nil { + continue + } + + options = append(options, opts) + } + + return options +} + +func expandGaugeChartConditionalFormattingOption(tfMap map[string]interface{}) *quicksight.GaugeChartConditionalFormattingOption { + if tfMap == nil { + return nil + } + + options := &quicksight.GaugeChartConditionalFormattingOption{} + + if v, ok := tfMap["arc"].([]interface{}); ok && len(v) > 0 { + options.Arc = expandGaugeChartArcConditionalFormatting(v) + } + if v, ok := tfMap["primary_value"].([]interface{}); ok && len(v) > 0 { + options.PrimaryValue = expandGaugeChartPrimaryValueConditionalFormatting(v) + } + + return options +} + +func expandGaugeChartArcConditionalFormatting(tfList []interface{}) *quicksight.GaugeChartArcConditionalFormatting { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + config := &quicksight.GaugeChartArcConditionalFormatting{} + + if v, ok := tfMap["foreground_color"].([]interface{}); ok && len(v) > 0 { + config.ForegroundColor = expandConditionalFormattingColor(v) + } + + return config +} + +func expandGaugeChartPrimaryValueConditionalFormatting(tfList []interface{}) *quicksight.GaugeChartPrimaryValueConditionalFormatting { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + config := &quicksight.GaugeChartPrimaryValueConditionalFormatting{} + + if v, ok := tfMap["icon"].([]interface{}); ok && len(v) > 0 { + config.Icon = expandConditionalFormattingIcon(v) + } + if v, ok := tfMap["text_color"].([]interface{}); ok && len(v) > 0 { + config.TextColor = expandConditionalFormattingColor(v) + } + + return config +} + +func expandArcConfiguration(tfList []interface{}) *quicksight.ArcConfiguration { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + config := &quicksight.ArcConfiguration{} + + if v, ok := tfMap["arc_angle"].(float64); ok { + config.ArcAngle = aws.Float64(v) + } + if v, ok := tfMap["arc_thickness"].(string); ok && v != "" { + config.ArcThickness = aws.String(v) + } + + return config +} + +func expandArcAxisConfiguration(tfList []interface{}) *quicksight.ArcAxisConfiguration { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + config := &quicksight.ArcAxisConfiguration{} + + if v, ok := tfMap["range"].([]interface{}); ok && len(v) > 0 { + config.Range = expandArcAxisDisplayRange(v) + } + if v, ok := tfMap["reserve_angle"].(int64); ok { + config.ReserveRange = aws.Int64(v) + } + + return config +} + +func expandArcAxisDisplayRange(tfList []interface{}) *quicksight.ArcAxisDisplayRange { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + config := &quicksight.ArcAxisDisplayRange{} + + if v, ok := tfMap["max"].(float64); ok { + config.Max = aws.Float64(v) + } + if v, ok := tfMap["min"].(float64); ok { + config.Min = aws.Float64(v) + } + + return config +} diff --git a/internal/service/quicksight/visual_geospatial_map.go b/internal/service/quicksight/visual_geospatial_map.go new file mode 100644 index 000000000000..537b5bdda2d8 --- /dev/null +++ b/internal/service/quicksight/visual_geospatial_map.go @@ -0,0 +1,306 @@ +package quicksight + +import ( + "regexp" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/quicksight" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" +) + +func geospatialMapVisualSchema() *schema.Schema { + return &schema.Schema{ // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_GeospatialMapVisual.html + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "visual_id": idSchema(), + "actions": visualCustomActionsSchema(customActionsMaxItems), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_VisualCustomAction.html + "chart_configuration": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_GeospatialMapConfiguration.html + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "field_wells": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_GeospatialMapFieldWells.html + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "geospatial_map_aggregated_field_wells": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_GeospatialMapAggregatedFieldWells.html + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "colors": dimensionFieldSchema(dimensionsFieldMaxItems), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_DimensionField.html + "geospatial": dimensionFieldSchema(dimensionsFieldMaxItems), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_DimensionField.html + "values": measureFieldSchema(200), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_MeasureField.html + }, + }, + }, + }, + }, + }, + "legend": legendOptionsSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_LegendOptions.html + "map_style_options": geospatialMapStyleOptionsSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_GeospatialMapStyleOptions.html + "point_style_options": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_GeospatialPointStyleOptions.html + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "cluster_marker_configuration": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_ClusterMarkerConfiguration.html + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "cluster_marker": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_ClusterMarker.html + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "simple_cluster_marker": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_SimpleClusterMarker.html + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "color": stringSchema(false, validation.StringMatch(regexp.MustCompile(`^#[A-F0-9]{6}$`), "")), + }, + }, + }, + }, + }, + }, + }, + }, + }, + "selected_point_style": stringSchema(false, validation.StringInSlice(quicksight.GeospatialSelectedPointStyle_Values(), false)), + }, + }, + }, + "tooltip": tooltipOptionsSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_TooltipOptions.html + + "window_options": geospatialWindowOptionsSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_GeospatialWindowOptions.html + }, + }, + }, + "column_hierarchies": columnHierarchiesSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_ColumnHierarchy.html + "subtitle": visualSubtitleLabelOptionsSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_VisualSubtitleLabelOptions.html + "title": visualTitleLabelOptionsSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_VisualTitleLabelOptions.html + }, + }, + } +} + +func expandGeospatialMapVisual(tfList []interface{}) (*quicksight.GeospatialMapVisual, error) { + if len(tfList) == 0 || tfList[0] == nil { + return nil, nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil, nil + } + + visual := &quicksight.GeospatialMapVisual{} + + if v, ok := tfMap["visual_id"].(string); ok && v != "" { + visual.VisualId = aws.String(v) + } + if v, ok := tfMap["actions"].([]interface{}); ok && len(v) > 0 { + value, err := expandVisualCustomActions(v) + if err != nil { + return nil, err + } + visual.Actions = value + } + if v, ok := tfMap["chart_configuration"].([]interface{}); ok && len(v) > 0 { + visual.ChartConfiguration = expandGeospatialMapConfiguration(v) + } + if v, ok := tfMap["column_hierarchies"].([]interface{}); ok && len(v) > 0 { + value, err := expandColumnHierarchies(v) + if err != nil { + return nil, err + } + visual.ColumnHierarchies = value + } + if v, ok := tfMap["subtitle"].([]interface{}); ok && len(v) > 0 { + visual.Subtitle = expandVisualSubtitleLabelOptions(v) + } + if v, ok := tfMap["title"].([]interface{}); ok && len(v) > 0 { + visual.Title = expandVisualTitleLabelOptions(v) + } + + return visual, nil +} + +func expandGeospatialMapConfiguration(tfList []interface{}) *quicksight.GeospatialMapConfiguration { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + config := &quicksight.GeospatialMapConfiguration{} + + if v, ok := tfMap["field_wells"].([]interface{}); ok && len(v) > 0 { + config.FieldWells = expandGeospatialMapFieldWells(v) + } + if v, ok := tfMap["legend"].([]interface{}); ok && len(v) > 0 { + config.Legend = expandLegendOptions(v) + } + if v, ok := tfMap["map_style_options"].([]interface{}); ok && len(v) > 0 { + config.MapStyleOptions = expandGeospatialMapStyleOptions(v) + } + if v, ok := tfMap["point_style_options"].([]interface{}); ok && len(v) > 0 { + config.PointStyleOptions = expandGeospatialPointStyleOptions(v) + } + if v, ok := tfMap["tooltip"].([]interface{}); ok && len(v) > 0 { + config.Tooltip = expandTooltipOptions(v) + } + if v, ok := tfMap["value_axis"].([]interface{}); ok && len(v) > 0 { + config.WindowOptions = expandGeospatialWindowOptions(v) + } + + return config +} + +func expandGeospatialMapFieldWells(tfList []interface{}) *quicksight.GeospatialMapFieldWells { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + config := &quicksight.GeospatialMapFieldWells{} + + if v, ok := tfMap["geospatial_map_aggregated_field_wells"].([]interface{}); ok && len(v) > 0 { + config.GeospatialMapAggregatedFieldWells = expandGeospatialMapAggregatedFieldWells(v) + } + + return config +} + +func expandGeospatialMapAggregatedFieldWells(tfList []interface{}) *quicksight.GeospatialMapAggregatedFieldWells { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + config := &quicksight.GeospatialMapAggregatedFieldWells{} + + if v, ok := tfMap["colors"].([]interface{}); ok && len(v) > 0 { + config.Colors = expandDimensionFields(v) + } + if v, ok := tfMap["geospatial"].([]interface{}); ok && len(v) > 0 { + config.Geospatial = expandDimensionFields(v) + } + if v, ok := tfMap["values"].([]interface{}); ok && len(v) > 0 { + config.Values = expandMeasureFields(v) + } + + return config +} + +func expandGeospatialPointStyleOptions(tfList []interface{}) *quicksight.GeospatialPointStyleOptions { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + config := &quicksight.GeospatialPointStyleOptions{} + + if v, ok := tfMap["selected_point_style"].(string); ok && v != "" { + config.SelectedPointStyle = aws.String(v) + } + if v, ok := tfMap["cluster_marker_configuration"].([]interface{}); ok && len(v) > 0 { + config.ClusterMarkerConfiguration = expandClusterMarkerConfiguration(v) + } + + return config +} + +func expandClusterMarkerConfiguration(tfList []interface{}) *quicksight.ClusterMarkerConfiguration { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + config := &quicksight.ClusterMarkerConfiguration{} + + if v, ok := tfMap["cluster_marker"].([]interface{}); ok && len(v) > 0 { + config.ClusterMarker = expandClusterMarker(v) + } + + return config +} + +func expandClusterMarker(tfList []interface{}) *quicksight.ClusterMarker { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + config := &quicksight.ClusterMarker{} + + if v, ok := tfMap["simple_cluster_marker"].([]interface{}); ok && len(v) > 0 { + config.SimpleClusterMarker = expandSimpleClusterMarker(v) + } + + return config +} + +func expandSimpleClusterMarker(tfList []interface{}) *quicksight.SimpleClusterMarker { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + config := &quicksight.SimpleClusterMarker{} + + if v, ok := tfMap["color"].(string); ok && v != "" { + config.Color = aws.String(v) + } + return config +} diff --git a/internal/service/quicksight/visual_heat_map.go b/internal/service/quicksight/visual_heat_map.go new file mode 100644 index 000000000000..19178571031c --- /dev/null +++ b/internal/service/quicksight/visual_heat_map.go @@ -0,0 +1,232 @@ +package quicksight + +import ( + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/quicksight" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" +) + +func heatMapVisualSchema() *schema.Schema { + return &schema.Schema{ // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_HeatMapVisual.html + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "visual_id": idSchema(), + "actions": visualCustomActionsSchema(customActionsMaxItems), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_VisualCustomAction.html + "chart_configuration": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_HeatMapConfiguration.html + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "color_scale": colorScaleSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_ColorScale.html + "column_label_options": chartAxisLabelOptionsSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_ChartAxisLabelOptions.html + "data_labels": dataLabelOptionsSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_DataLabelOptions.html + "field_wells": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_HeatMapFieldWells.html + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "heat_map_aggregated_field_wells": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_HeatMapAggregatedFieldWells.html + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "columns": dimensionFieldSchema(1), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_DimensionField.html + "rows": dimensionFieldSchema(1), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_DimensionField.html + "values": measureFieldSchema(1), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_MeasureField.html + }, + }, + }, + }, + }, + }, + "legend": legendOptionsSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_LegendOptions.html + "row_label_options": chartAxisLabelOptionsSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_ChartAxisLabelOptions.html + "sort_configuration": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_HeatMapSortConfiguration.html + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "heat_map_column_items_limit_configuration": itemsLimitConfigurationSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_ItemsLimitConfiguration.html + "heat_map_column_sort": fieldSortOptionsSchema(100), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_FieldSortOptions.html, + "heat_map_row_items_limit_configuration": itemsLimitConfigurationSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_ItemsLimitConfiguration.html + "heat_map_row_sort": fieldSortOptionsSchema(100), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_FieldSortOptions.html + }, + }, + }, + "tooltip": tooltipOptionsSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_TooltipOptions.html + }, + }, + }, + "column_hierarchies": columnHierarchiesSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_ColumnHierarchy.html + "subtitle": visualSubtitleLabelOptionsSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_VisualSubtitleLabelOptions.html + "title": visualTitleLabelOptionsSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_VisualTitleLabelOptions.html + }, + }, + } +} + +func expandHeatMapVisual(tfList []interface{}) (*quicksight.HeatMapVisual, error) { + if len(tfList) == 0 || tfList[0] == nil { + return nil, nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil, nil + } + + visual := &quicksight.HeatMapVisual{} + + if v, ok := tfMap["visual_id"].(string); ok && v != "" { + visual.VisualId = aws.String(v) + } + if v, ok := tfMap["actions"].([]interface{}); ok && len(v) > 0 { + value, err := expandVisualCustomActions(v) + if err != nil { + return nil, err + } + visual.Actions = value + } + if v, ok := tfMap["chart_configuration"].([]interface{}); ok && len(v) > 0 { + visual.ChartConfiguration = expandHeatMapConfiguration(v) + } + if v, ok := tfMap["column_hierarchies"].([]interface{}); ok && len(v) > 0 { + value, err := expandColumnHierarchies(v) + if err != nil { + return nil, err + } + visual.ColumnHierarchies = value + } + if v, ok := tfMap["subtitle"].([]interface{}); ok && len(v) > 0 { + visual.Subtitle = expandVisualSubtitleLabelOptions(v) + } + if v, ok := tfMap["title"].([]interface{}); ok && len(v) > 0 { + visual.Title = expandVisualTitleLabelOptions(v) + } + + return visual, nil +} + +func expandHeatMapConfiguration(tfList []interface{}) *quicksight.HeatMapConfiguration { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + config := &quicksight.HeatMapConfiguration{} + + if v, ok := tfMap["color_scale"].([]interface{}); ok && len(v) > 0 { + config.ColorScale = expandColorScale(v) + } + if v, ok := tfMap["column_label_options"].([]interface{}); ok && len(v) > 0 { + config.ColumnLabelOptions = expandChartAxisLabelOptions(v) + } + if v, ok := tfMap["data_labels"].([]interface{}); ok && len(v) > 0 { + config.DataLabels = expandDataLabelOptions(v) + } + if v, ok := tfMap["field_wells"].([]interface{}); ok && len(v) > 0 { + config.FieldWells = expandHeatMapFieldWells(v) + } + if v, ok := tfMap["legend"].([]interface{}); ok && len(v) > 0 { + config.Legend = expandLegendOptions(v) + } + if v, ok := tfMap["row_label_options"].([]interface{}); ok && len(v) > 0 { + config.RowLabelOptions = expandChartAxisLabelOptions(v) + } + if v, ok := tfMap["sort_configuration"].([]interface{}); ok && len(v) > 0 { + config.SortConfiguration = expandHeatMapSortConfiguration(v) + } + if v, ok := tfMap["tooltip"].([]interface{}); ok && len(v) > 0 { + config.Tooltip = expandTooltipOptions(v) + } + + return config +} + +func expandHeatMapFieldWells(tfList []interface{}) *quicksight.HeatMapFieldWells { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + config := &quicksight.HeatMapFieldWells{} + + if v, ok := tfMap["heat_map_aggregated_field_wells"].([]interface{}); ok && len(v) > 0 { + config.HeatMapAggregatedFieldWells = expandHeatMapAggregatedFieldWells(v) + } + + return config +} + +func expandHeatMapAggregatedFieldWells(tfList []interface{}) *quicksight.HeatMapAggregatedFieldWells { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + config := &quicksight.HeatMapAggregatedFieldWells{} + + if v, ok := tfMap["columns"].([]interface{}); ok && len(v) > 0 { + config.Columns = expandDimensionFields(v) + } + if v, ok := tfMap["rows"].([]interface{}); ok && len(v) > 0 { + config.Rows = expandDimensionFields(v) + } + if v, ok := tfMap["values"].([]interface{}); ok && len(v) > 0 { + config.Values = expandMeasureFields(v) + } + + return config +} + +func expandHeatMapSortConfiguration(tfList []interface{}) *quicksight.HeatMapSortConfiguration { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + config := &quicksight.HeatMapSortConfiguration{} + + if v, ok := tfMap["heat_map_column_items_limit_configuration"].([]interface{}); ok && len(v) > 0 { + config.HeatMapColumnItemsLimitConfiguration = expandItemsLimitConfiguration(v) + } + if v, ok := tfMap["heat_map_column_sort"].([]interface{}); ok && len(v) > 0 { + config.HeatMapColumnSort = expandFieldSortOptionsList(v) + } + if v, ok := tfMap["heat_map_row_items_limit_configuration"].([]interface{}); ok && len(v) > 0 { + config.HeatMapRowItemsLimitConfiguration = expandItemsLimitConfiguration(v) + } + if v, ok := tfMap["heat_map_row_sort"].([]interface{}); ok && len(v) > 0 { + config.HeatMapRowSort = expandFieldSortOptionsList(v) + } + + return config +} diff --git a/internal/service/quicksight/visual_histogram.go b/internal/service/quicksight/visual_histogram.go new file mode 100644 index 000000000000..d195e6ded501 --- /dev/null +++ b/internal/service/quicksight/visual_histogram.go @@ -0,0 +1,294 @@ +package quicksight + +import ( + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/quicksight" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" +) + +func histogramVisualSchema() *schema.Schema { + return &schema.Schema{ // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_HistogramVisual.html + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "visual_id": idSchema(), + "actions": visualCustomActionsSchema(customActionsMaxItems), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_VisualCustomAction.html + "chart_configuration": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_HistogramConfiguration.html + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "bin_options": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_HistogramBinOptions.html + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "bin_count": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_BinCountOptions.html + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "value": { + Type: schema.TypeInt, + Optional: true, + ValidateFunc: validation.IntAtLeast(0), + }, + }, + }, + }, + "bin_width": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_BinWidthOptions.html + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "bin_count_limit": { + Type: schema.TypeInt, + Optional: true, + ValidateFunc: validation.IntBetween(0, 1000), + }, + "value": { + Type: schema.TypeFloat, + Optional: true, + ValidateFunc: validation.IntAtLeast(0), + }, + }, + }, + }, + "selected_bin_type": stringSchema(false, validation.StringInSlice(quicksight.HistogramBinType_Values(), false)), + "start_value": { + Type: schema.TypeFloat, + Optional: true, + }, + }, + }, + }, + "data_labels": dataLabelOptionsSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_DataLabelOptions.html + "field_wells": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_HistogramFieldWells.html + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "histogram_aggregated_field_wells": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_HistogramAggregatedFieldWells.html + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "values": measureFieldSchema(1), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_MeasureField.html + }, + }, + }, + }, + }, + }, + "tooltip": tooltipOptionsSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_TooltipOptions.html + "visual_palette": visualPaletteSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_VisualPalette.html + "x_axis_display_options": axisDisplayOptionsSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_AxisDisplayOptions.html + "x_axis_label_options": chartAxisLabelOptionsSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_ChartAxisLabelOptions.html + "y_axis_display_options": axisDisplayOptionsSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_AxisDisplayOptions.html + }, + }, + }, + "subtitle": visualSubtitleLabelOptionsSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_VisualSubtitleLabelOptions.html + "title": visualTitleLabelOptionsSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_VisualTitleLabelOptions.html + }, + }, + } +} + +func expandHistogramVisual(tfList []interface{}) (*quicksight.HistogramVisual, error) { + if len(tfList) == 0 || tfList[0] == nil { + return nil, nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil, nil + } + + visual := &quicksight.HistogramVisual{} + + if v, ok := tfMap["visual_id"].(string); ok && v != "" { + visual.VisualId = aws.String(v) + } + if v, ok := tfMap["actions"].([]interface{}); ok && len(v) > 0 { + value, err := expandVisualCustomActions(v) + if err != nil { + return nil, err + } + visual.Actions = value + } + if v, ok := tfMap["chart_configuration"].([]interface{}); ok && len(v) > 0 { + visual.ChartConfiguration = expandHistogramConfiguration(v) + } + if v, ok := tfMap["subtitle"].([]interface{}); ok && len(v) > 0 { + visual.Subtitle = expandVisualSubtitleLabelOptions(v) + } + if v, ok := tfMap["title"].([]interface{}); ok && len(v) > 0 { + visual.Title = expandVisualTitleLabelOptions(v) + } + + return visual, nil +} + +func expandHistogramConfiguration(tfList []interface{}) *quicksight.HistogramConfiguration { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + config := &quicksight.HistogramConfiguration{} + + if v, ok := tfMap["bin_options"].([]interface{}); ok && len(v) > 0 { + config.BinOptions = expandHistogramBinOptions(v) + } + if v, ok := tfMap["data_labels"].([]interface{}); ok && len(v) > 0 { + config.DataLabels = expandDataLabelOptions(v) + } + if v, ok := tfMap["field_wells"].([]interface{}); ok && len(v) > 0 { + config.FieldWells = expandHistogramFieldWells(v) + } + if v, ok := tfMap["tooltip"].([]interface{}); ok && len(v) > 0 { + config.Tooltip = expandTooltipOptions(v) + } + if v, ok := tfMap["visual_palette"].([]interface{}); ok && len(v) > 0 { + config.VisualPalette = expandVisualPalette(v) + } + if v, ok := tfMap["x_axis_display_options"].([]interface{}); ok && len(v) > 0 { + config.XAxisDisplayOptions = expandAxisDisplayOptions(v) + } + if v, ok := tfMap["x_axis_label_options"].([]interface{}); ok && len(v) > 0 { + config.XAxisLabelOptions = expandChartAxisLabelOptions(v) + } + if v, ok := tfMap["y_axis_display_options"].([]interface{}); ok && len(v) > 0 { + config.YAxisDisplayOptions = expandAxisDisplayOptions(v) + } + + return config +} + +func expandHistogramFieldWells(tfList []interface{}) *quicksight.HistogramFieldWells { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + config := &quicksight.HistogramFieldWells{} + + if v, ok := tfMap["histogram_aggregated_field_wells"].([]interface{}); ok && len(v) > 0 { + config.HistogramAggregatedFieldWells = expandHistogramAggregatedFieldWells(v) + } + + return config +} + +func expandHistogramAggregatedFieldWells(tfList []interface{}) *quicksight.HistogramAggregatedFieldWells { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + config := &quicksight.HistogramAggregatedFieldWells{} + + if v, ok := tfMap["values"].([]interface{}); ok && len(v) > 0 { + config.Values = expandMeasureFields(v) + } + + return config +} + +func expandHistogramBinOptions(tfList []interface{}) *quicksight.HistogramBinOptions { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + options := &quicksight.HistogramBinOptions{} + + if v, ok := tfMap["selected_bin_type"].(string); ok && v != "" { + options.SelectedBinType = aws.String(v) + } + if v, ok := tfMap["start_value"].(float64); ok { + options.StartValue = aws.Float64(v) + } + if v, ok := tfMap["bin_count"].([]interface{}); ok && len(v) > 0 { + options.BinCount = expandBinCountOptions(v) + } + if v, ok := tfMap["bin_width"].([]interface{}); ok && len(v) > 0 { + options.BinWidth = expandBinWidthOptions(v) + } + + return options +} + +func expandBinCountOptions(tfList []interface{}) *quicksight.BinCountOptions { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + options := &quicksight.BinCountOptions{} + + if v, ok := tfMap["value"].(int64); ok { + options.Value = aws.Int64(v) + } + + return options +} + +func expandBinWidthOptions(tfList []interface{}) *quicksight.BinWidthOptions { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + options := &quicksight.BinWidthOptions{} + + if v, ok := tfMap["bin_count_limit"].(int64); ok { + options.BinCountLimit = aws.Int64(v) + } + if v, ok := tfMap["value"].(float64); ok { + options.Value = aws.Float64(v) + } + + return options +} diff --git a/internal/service/quicksight/visual_insight.go b/internal/service/quicksight/visual_insight.go new file mode 100644 index 000000000000..6b48cd6581a8 --- /dev/null +++ b/internal/service/quicksight/visual_insight.go @@ -0,0 +1,716 @@ +package quicksight + +import ( + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/quicksight" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" +) + +func insightVisualSchema() *schema.Schema { + return &schema.Schema{ // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_InsightVisual.html + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "data_set_identifier": stringSchema(true, validation.StringLenBetween(1, 2048)), + "visual_id": idSchema(), + "actions": visualCustomActionsSchema(customActionsMaxItems), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_VisualCustomAction.html + "insight_configuration": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_InsightConfiguration.html + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "computation": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_Computation.html + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 100, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "forecast": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_ForecastComputation.html + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "computation_id": idSchema(), + "time": dimensionFieldSchema(1), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_DimensionField.html + "custom_seasonality_value": intSchema(false, validation.IntBetween(1, 180)), + "lower_boundary": { + Type: schema.TypeFloat, + Optional: true, + }, + "name": { + Type: schema.TypeString, + Optional: true, + }, + "periods_backward": intSchema(false, validation.IntBetween(0, 1000)), + "periods_forward": intSchema(false, validation.IntBetween(1, 1000)), + "prediction_interval": intSchema(false, validation.IntBetween(50, 95)), + "seasonality": stringSchema(true, validation.StringInSlice(quicksight.ForecastComputationSeasonality_Values(), false)), + "upper_boundary": { + Type: schema.TypeFloat, + Optional: true, + }, + "value": measureFieldSchema(1), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_MeasureField.html + }, + }, + }, + "growth_rate": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_GrowthRateComputation.html + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "computation_id": idSchema(), + "time": dimensionFieldSchema(1), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_DimensionField.html + "name": { + Type: schema.TypeString, + Optional: true, + }, + "period_size": intSchema(false, validation.IntBetween(2, 52)), + "value": measureFieldSchema(1), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_MeasureField.html + }, + }, + }, + "maximum_minimum": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_MaximumMinimumComputation.html + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "computation_id": idSchema(), + "time": dimensionFieldSchema(1), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_DimensionField.html + "type": stringSchema(true, validation.StringInSlice(quicksight.MaximumMinimumComputationType_Values(), false)), + "name": { + Type: schema.TypeString, + Optional: true, + }, + "value": measureFieldSchema(1), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_MeasureField.html + }, + }, + }, + "metric_comparison": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_MetricComparisonComputation.html + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "computation_id": idSchema(), + "time": dimensionFieldSchema(1), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_DimensionField.html + "from_value": measureFieldSchema(1), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_MeasureField.html + "target_value": measureFieldSchema(1), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_MeasureField.html + "name": { + Type: schema.TypeString, + Optional: true, + }, + }, + }, + }, + "period_over_period": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_PeriodOverPeriodComputation.html + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "computation_id": idSchema(), + "time": dimensionFieldSchema(1), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_DimensionField.html + "name": { + Type: schema.TypeString, + Optional: true, + }, + "value": measureFieldSchema(1), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_MeasureField.html + }, + }, + }, + "period_to_date": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_PeriodToDateComputation.html + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "computation_id": idSchema(), + "time": dimensionFieldSchema(1), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_DimensionField.html + "name": { + Type: schema.TypeString, + Optional: true, + }, + "period_time_granularity": stringSchema(true, validation.StringInSlice(quicksight.TimeGranularity_Values(), false)), + "value": measureFieldSchema(1), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_MeasureField.html + }, + }, + }, + "top_bottom_movers": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_TopBottomMoversComputation.html + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "computation_id": idSchema(), + "category": dimensionFieldSchema(1), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_DimensionField.html + "time": dimensionFieldSchema(1), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_DimensionField.html + "type": stringSchema(true, validation.StringInSlice(quicksight.TopBottomComputationType_Values(), false)), + "mover_size": intSchema(false, validation.IntBetween(1, 20)), + "sort_order": stringSchema(true, validation.StringInSlice(quicksight.TopBottomSortOrder_Values(), false)), + "name": { + Type: schema.TypeString, + Optional: true, + }, + "value": measureFieldSchema(1), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_MeasureField.html + }, + }, + }, + "top_bottom_ranked": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_TopBottomRankedComputation.html + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "computation_id": idSchema(), + "category": dimensionFieldSchema(1), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_DimensionField.html + "name": { + Type: schema.TypeString, + Optional: true, + }, + "result_size": intSchema(false, validation.IntBetween(1, 20)), + "type": stringSchema(true, validation.StringInSlice(quicksight.TopBottomComputationType_Values(), false)), + "value": measureFieldSchema(1), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_MeasureField.html + }, + }, + }, + "total_aggregation": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_TotalAggregationComputation.html + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "computation_id": idSchema(), + "name": { + Type: schema.TypeString, + Optional: true, + }, + "value": measureFieldSchema(1), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_MeasureField.html + }, + }, + }, + "unique_values": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_UniqueValuesComputation.html + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "category": dimensionFieldSchema(1), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_DimensionField.html + "computation_id": idSchema(), + "name": { + Type: schema.TypeString, + Optional: true, + }, + }, + }, + }, + }, + }, + }, + "custom_narrative": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_CustomNarrativeOptions.html + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "narrative": stringSchema(true, validation.StringLenBetween(1, 150000)), + }, + }, + }, + }, + }, + }, + "subtitle": visualSubtitleLabelOptionsSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_VisualSubtitleLabelOptions.html + "title": visualTitleLabelOptionsSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_VisualTitleLabelOptions.html + }, + }, + } +} + +func expandInsightVisual(tfList []interface{}) (*quicksight.InsightVisual, error) { + if len(tfList) == 0 || tfList[0] == nil { + return nil, nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil, nil + } + + visual := &quicksight.InsightVisual{} + + if v, ok := tfMap["data_set_identifier"].(string); ok && v != "" { + visual.DataSetIdentifier = aws.String(v) + } + if v, ok := tfMap["visual_id"].(string); ok && v != "" { + visual.VisualId = aws.String(v) + } + if v, ok := tfMap["actions"].([]interface{}); ok && len(v) > 0 { + value, err := expandVisualCustomActions(v) + if err != nil { + return nil, err + } + visual.Actions = value + } + if v, ok := tfMap["insight_configuration"].([]interface{}); ok && len(v) > 0 { + visual.InsightConfiguration = expandInsightConfiguration(v) + } + if v, ok := tfMap["subtitle"].([]interface{}); ok && len(v) > 0 { + visual.Subtitle = expandVisualSubtitleLabelOptions(v) + } + if v, ok := tfMap["title"].([]interface{}); ok && len(v) > 0 { + visual.Title = expandVisualTitleLabelOptions(v) + } + + return visual, nil +} + +func expandInsightConfiguration(tfList []interface{}) *quicksight.InsightConfiguration { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + config := &quicksight.InsightConfiguration{} + + if v, ok := tfMap["computation"].([]interface{}); ok && len(v) > 0 { + config.Computations = expandComputations(v) + } + if v, ok := tfMap["custom_narrative"].([]interface{}); ok && len(v) > 0 { + config.CustomNarrative = expandCustomNarrativeOptions(v) + } + + return config +} + +func expandComputations(tfList []interface{}) []*quicksight.Computation { + if len(tfList) == 0 { + return nil + } + + var computations []*quicksight.Computation + for _, tfMapRaw := range tfList { + tfMap, ok := tfMapRaw.(map[string]interface{}) + if !ok { + continue + } + + computation := expandComputation(tfMap) + if computation == nil { + continue + } + + computations = append(computations, computation) + } + + return computations +} + +func expandComputation(tfMap map[string]interface{}) *quicksight.Computation { + if tfMap == nil { + return nil + } + + computation := &quicksight.Computation{} + + if v, ok := tfMap["forecast"].([]interface{}); ok && len(v) > 0 { + computation.Forecast = expandForecastComputation(v) + } + if v, ok := tfMap["growth_rate"].([]interface{}); ok && len(v) > 0 { + computation.GrowthRate = expandGrowthRateComputation(v) + } + if v, ok := tfMap["maximum_minimum"].([]interface{}); ok && len(v) > 0 { + computation.MaximumMinimum = expandMaximumMinimumComputation(v) + } + if v, ok := tfMap["metric_comparison"].([]interface{}); ok && len(v) > 0 { + computation.MetricComparison = expandMetricComparisonComputation(v) + } + if v, ok := tfMap["period_over_period"].([]interface{}); ok && len(v) > 0 { + computation.PeriodOverPeriod = expandPeriodOverPeriodComputation(v) + } + if v, ok := tfMap["period_to_date"].([]interface{}); ok && len(v) > 0 { + computation.PeriodToDate = expandPeriodToDateComputation(v) + } + if v, ok := tfMap["top_bottom_movers"].([]interface{}); ok && len(v) > 0 { + computation.TopBottomMovers = expandTopBottomMoversComputation(v) + } + if v, ok := tfMap["top_bottom_ranked"].([]interface{}); ok && len(v) > 0 { + computation.TopBottomRanked = expandTopBottomRankedComputation(v) + } + if v, ok := tfMap["total_aggregation"].([]interface{}); ok && len(v) > 0 { + computation.TotalAggregation = expandTotalAggregationComputation(v) + } + if v, ok := tfMap["unique_values"].([]interface{}); ok && len(v) > 0 { + computation.UniqueValues = expandUniqueValuesComputation(v) + } + + return computation +} + +func expandForecastComputation(tfList []interface{}) *quicksight.ForecastComputation { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + computation := &quicksight.ForecastComputation{} + + if v, ok := tfMap["computation_id"].(string); ok && v != "" { + computation.ComputationId = aws.String(v) + } + if v, ok := tfMap["custom_seasonality_value"].(int64); ok { + computation.CustomSeasonalityValue = aws.Int64(v) + } + if v, ok := tfMap["lower_boundary"].(float64); ok { + computation.LowerBoundary = aws.Float64(v) + } + if v, ok := tfMap["name"].(string); ok && v != "" { + computation.Name = aws.String(v) + } + if v, ok := tfMap["periods_backward"].(int64); ok { + computation.PeriodsBackward = aws.Int64(v) + } + if v, ok := tfMap["periods_forward"].(int64); ok { + computation.PeriodsForward = aws.Int64(v) + } + if v, ok := tfMap["prediction_interval"].(int64); ok { + computation.PredictionInterval = aws.Int64(v) + } + if v, ok := tfMap["seasonality"].(string); ok && v != "" { + computation.Seasonality = aws.String(v) + } + if v, ok := tfMap["upper_boundary"].(float64); ok { + computation.UpperBoundary = aws.Float64(v) + } + if v, ok := tfMap["time"].([]interface{}); ok && len(v) > 0 { + computation.Time = expandDimensionField(v) + } + if v, ok := tfMap["value"].([]interface{}); ok && len(v) > 0 { + computation.Value = expandMeasureField(v) + } + + return computation +} + +func expandGrowthRateComputation(tfList []interface{}) *quicksight.GrowthRateComputation { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + computation := &quicksight.GrowthRateComputation{} + + if v, ok := tfMap["computation_id"].(string); ok && v != "" { + computation.ComputationId = aws.String(v) + } + if v, ok := tfMap["name"].(string); ok && v != "" { + computation.Name = aws.String(v) + } + if v, ok := tfMap["period_size"].(int64); ok { + computation.PeriodSize = aws.Int64(v) + } + if v, ok := tfMap["time"].([]interface{}); ok && len(v) > 0 { + computation.Time = expandDimensionField(v) + } + if v, ok := tfMap["value"].([]interface{}); ok && len(v) > 0 { + computation.Value = expandMeasureField(v) + } + + return computation +} + +func expandMaximumMinimumComputation(tfList []interface{}) *quicksight.MaximumMinimumComputation { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + computation := &quicksight.MaximumMinimumComputation{} + + if v, ok := tfMap["computation_id"].(string); ok && v != "" { + computation.ComputationId = aws.String(v) + } + if v, ok := tfMap["name"].(string); ok && v != "" { + computation.Name = aws.String(v) + } + if v, ok := tfMap["type"].(string); ok && v != "" { + computation.Type = aws.String(v) + } + if v, ok := tfMap["time"].([]interface{}); ok && len(v) > 0 { + computation.Time = expandDimensionField(v) + } + if v, ok := tfMap["value"].([]interface{}); ok && len(v) > 0 { + computation.Value = expandMeasureField(v) + } + + return computation +} + +func expandMetricComparisonComputation(tfList []interface{}) *quicksight.MetricComparisonComputation { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + computation := &quicksight.MetricComparisonComputation{} + + if v, ok := tfMap["computation_id"].(string); ok && v != "" { + computation.ComputationId = aws.String(v) + } + if v, ok := tfMap["name"].(string); ok && v != "" { + computation.Name = aws.String(v) + } + if v, ok := tfMap["time"].([]interface{}); ok && len(v) > 0 { + computation.Time = expandDimensionField(v) + } + if v, ok := tfMap["from_value"].([]interface{}); ok && len(v) > 0 { + computation.FromValue = expandMeasureField(v) + } + if v, ok := tfMap["target_value"].([]interface{}); ok && len(v) > 0 { + computation.TargetValue = expandMeasureField(v) + } + + return computation +} + +func expandPeriodOverPeriodComputation(tfList []interface{}) *quicksight.PeriodOverPeriodComputation { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + computation := &quicksight.PeriodOverPeriodComputation{} + + if v, ok := tfMap["computation_id"].(string); ok && v != "" { + computation.ComputationId = aws.String(v) + } + if v, ok := tfMap["name"].(string); ok && v != "" { + computation.Name = aws.String(v) + } + if v, ok := tfMap["time"].([]interface{}); ok && len(v) > 0 { + computation.Time = expandDimensionField(v) + } + if v, ok := tfMap["value"].([]interface{}); ok && len(v) > 0 { + computation.Value = expandMeasureField(v) + } + + return computation +} + +func expandPeriodToDateComputation(tfList []interface{}) *quicksight.PeriodToDateComputation { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + computation := &quicksight.PeriodToDateComputation{} + + if v, ok := tfMap["computation_id"].(string); ok && v != "" { + computation.ComputationId = aws.String(v) + } + if v, ok := tfMap["name"].(string); ok && v != "" { + computation.Name = aws.String(v) + } + if v, ok := tfMap["period_time_granularity"].(string); ok && v != "" { + computation.PeriodTimeGranularity = aws.String(v) + } + if v, ok := tfMap["time"].([]interface{}); ok && len(v) > 0 { + computation.Time = expandDimensionField(v) + } + if v, ok := tfMap["value"].([]interface{}); ok && len(v) > 0 { + computation.Value = expandMeasureField(v) + } + + return computation +} + +func expandTopBottomMoversComputation(tfList []interface{}) *quicksight.TopBottomMoversComputation { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + computation := &quicksight.TopBottomMoversComputation{} + + if v, ok := tfMap["computation_id"].(string); ok && v != "" { + computation.ComputationId = aws.String(v) + } + if v, ok := tfMap["name"].(string); ok && v != "" { + computation.Name = aws.String(v) + } + if v, ok := tfMap["sort_order"].(string); ok && v != "" { + computation.SortOrder = aws.String(v) + } + if v, ok := tfMap["type"].(string); ok && v != "" { + computation.Type = aws.String(v) + } + if v, ok := tfMap["mover_size"].(int64); ok { + computation.MoverSize = aws.Int64(v) + } + if v, ok := tfMap["category"].([]interface{}); ok && len(v) > 0 { + computation.Category = expandDimensionField(v) + } + if v, ok := tfMap["time"].([]interface{}); ok && len(v) > 0 { + computation.Time = expandDimensionField(v) + } + if v, ok := tfMap["value"].([]interface{}); ok && len(v) > 0 { + computation.Value = expandMeasureField(v) + } + + return computation +} + +func expandTopBottomRankedComputation(tfList []interface{}) *quicksight.TopBottomRankedComputation { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + computation := &quicksight.TopBottomRankedComputation{} + + if v, ok := tfMap["computation_id"].(string); ok && v != "" { + computation.ComputationId = aws.String(v) + } + if v, ok := tfMap["name"].(string); ok && v != "" { + computation.Name = aws.String(v) + } + if v, ok := tfMap["type"].(string); ok && v != "" { + computation.Type = aws.String(v) + } + if v, ok := tfMap["result_size"].(int64); ok { + computation.ResultSize = aws.Int64(v) + } + if v, ok := tfMap["category"].([]interface{}); ok && len(v) > 0 { + computation.Category = expandDimensionField(v) + } + if v, ok := tfMap["value"].([]interface{}); ok && len(v) > 0 { + computation.Value = expandMeasureField(v) + } + + return computation +} + +func expandTotalAggregationComputation(tfList []interface{}) *quicksight.TotalAggregationComputation { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + computation := &quicksight.TotalAggregationComputation{} + + if v, ok := tfMap["computation_id"].(string); ok && v != "" { + computation.ComputationId = aws.String(v) + } + if v, ok := tfMap["name"].(string); ok && v != "" { + computation.Name = aws.String(v) + } + if v, ok := tfMap["value"].([]interface{}); ok && len(v) > 0 { + computation.Value = expandMeasureField(v) + } + + return computation +} + +func expandUniqueValuesComputation(tfList []interface{}) *quicksight.UniqueValuesComputation { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + computation := &quicksight.UniqueValuesComputation{} + + if v, ok := tfMap["computation_id"].(string); ok && v != "" { + computation.ComputationId = aws.String(v) + } + if v, ok := tfMap["name"].(string); ok && v != "" { + computation.Name = aws.String(v) + } + if v, ok := tfMap["category"].([]interface{}); ok && len(v) > 0 { + computation.Category = expandDimensionField(v) + } + + return computation +} + +func expandCustomNarrativeOptions(tfList []interface{}) *quicksight.CustomNarrativeOptions { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + options := &quicksight.CustomNarrativeOptions{} + + if v, ok := tfMap["narrative"].(string); ok && v != "" { + options.Narrative = aws.String(v) + } + + return options +} diff --git a/internal/service/quicksight/visual_kpi.go b/internal/service/quicksight/visual_kpi.go new file mode 100644 index 000000000000..3a9affe64153 --- /dev/null +++ b/internal/service/quicksight/visual_kpi.go @@ -0,0 +1,457 @@ +package quicksight + +import ( + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/quicksight" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" +) + +func kpiVisualSchema() *schema.Schema { + return &schema.Schema{ // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_KPIVisual.html + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "visual_id": idSchema(), + "actions": visualCustomActionsSchema(customActionsMaxItems), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_VisualCustomAction.html + "chart_configuration": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_KPIConfiguration.html + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "field_wells": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_KPIFieldWells.html + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "target_values": measureFieldSchema(200), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_MeasureField.html + "trend_groups": dimensionFieldSchema(dimensionsFieldMaxItems), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_DimensionField.html + "values": measureFieldSchema(200), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_MeasureField.html + }, + }, + }, + "kpi_options": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_KPIOptions.html + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "comparison": comparisonConfigurationSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_ComparisonConfiguration.html + "primary_value_display_type": stringSchema(false, validation.StringInSlice(quicksight.PrimaryValueDisplayType_Values(), false)), + "primary_value_font_configuration": fontConfigurationSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_FontConfiguration.html + "progress_bar": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_ProgressBarOptions.html + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "visibility": stringSchema(false, validation.StringInSlice(quicksight.Visibility_Values(), false)), + }, + }, + }, + "secondary_value": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_SecondaryValueOptions.html + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "visibility": stringSchema(false, validation.StringInSlice(quicksight.Visibility_Values(), false)), + }, + }, + }, + "secondary_value_font_configuration": fontConfigurationSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_FontConfiguration.html + "trend_arrows": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_TrendArrowOptions.html + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "visibility": stringSchema(false, validation.StringInSlice(quicksight.Visibility_Values(), false)), + }, + }, + }, + }, + }, + }, + "sort_configuration": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_KPISortConfiguration.html + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "trend_group_sort": fieldSortOptionsSchema(100), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_FieldSortOptions.html, + }, + }, + }, + }, + }, + }, + "column_hierarchies": columnHierarchiesSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_ColumnHierarchy.html + "conditional_formatting": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_KPIConditionalFormatting.html + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "conditional_formatting_options": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_KPIConditionalFormattingOption.html + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 100, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "primary_value": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_KPIPrimaryValueConditionalFormatting.html + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "icon": conditionalFormattingIconSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_ConditionalFormattingIcon.html + "text_color": conditionalFormattingColorSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_ConditionalFormattingColor.html + }, + }, + }, + "progress_bar": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_KPIProgressBarConditionalFormatting.html + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "foreground_color": conditionalFormattingColorSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_ConditionalFormattingColor.html + }, + }, + }, + }, + }, + }, + }, + }, + }, + "subtitle": visualSubtitleLabelOptionsSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_VisualSubtitleLabelOptions.html + "title": visualTitleLabelOptionsSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_VisualTitleLabelOptions.html + }, + }, + } +} + +func expandKPIVisual(tfList []interface{}) (*quicksight.KPIVisual, error) { + if len(tfList) == 0 || tfList[0] == nil { + return nil, nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil, nil + } + + visual := &quicksight.KPIVisual{} + + if v, ok := tfMap["visual_id"].(string); ok && v != "" { + visual.VisualId = aws.String(v) + } + if v, ok := tfMap["actions"].([]interface{}); ok && len(v) > 0 { + value, err := expandVisualCustomActions(v) + if err != nil { + return nil, err + } + visual.Actions = value + } + if v, ok := tfMap["chart_configuration"].([]interface{}); ok && len(v) > 0 { + visual.ChartConfiguration = expandKPIConfiguration(v) + } + if v, ok := tfMap["conditional_formatting"].([]interface{}); ok && len(v) > 0 { + visual.ConditionalFormatting = expandKPIConditionalFormatting(v) + } + if v, ok := tfMap["column_hierarchies"].([]interface{}); ok && len(v) > 0 { + value, err := expandColumnHierarchies(v) + if err != nil { + return nil, err + } + visual.ColumnHierarchies = value + } + if v, ok := tfMap["subtitle"].([]interface{}); ok && len(v) > 0 { + visual.Subtitle = expandVisualSubtitleLabelOptions(v) + } + if v, ok := tfMap["title"].([]interface{}); ok && len(v) > 0 { + visual.Title = expandVisualTitleLabelOptions(v) + } + + return visual, nil +} + +func expandKPIConfiguration(tfList []interface{}) *quicksight.KPIConfiguration { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + config := &quicksight.KPIConfiguration{} + + if v, ok := tfMap["field_wells"].([]interface{}); ok && len(v) > 0 { + config.FieldWells = expandKPIFieldWells(v) + } + if v, ok := tfMap["kpi_options"].([]interface{}); ok && len(v) > 0 { + config.KPIOptions = expandKPIOptions(v) + } + if v, ok := tfMap["sort_configuration"].([]interface{}); ok && len(v) > 0 { + config.SortConfiguration = expandKPISortConfiguration(v) + } + + return config +} + +func expandKPIFieldWells(tfList []interface{}) *quicksight.KPIFieldWells { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + config := &quicksight.KPIFieldWells{} + + if v, ok := tfMap["trend_groups"].([]interface{}); ok && len(v) > 0 { + config.TrendGroups = expandDimensionFields(v) + } + if v, ok := tfMap["target_values"].([]interface{}); ok && len(v) > 0 { + config.Values = expandMeasureFields(v) + } + if v, ok := tfMap["values"].([]interface{}); ok && len(v) > 0 { + config.Values = expandMeasureFields(v) + } + return config +} + +func expandKPIOptions(tfList []interface{}) *quicksight.KPIOptions { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + options := &quicksight.KPIOptions{} + + if v, ok := tfMap["primary_value_display_type"].(string); ok && v != "" { + options.PrimaryValueDisplayType = aws.String(v) + } + if v, ok := tfMap["comparison"].([]interface{}); ok && len(v) > 0 { + options.Comparison = expandComparisonConfiguration(v) + } + if v, ok := tfMap["primary_value_font_configuration"].([]interface{}); ok && len(v) > 0 { + options.PrimaryValueFontConfiguration = expandFontConfiguration(v) + } + if v, ok := tfMap["progress_bar"].([]interface{}); ok && len(v) > 0 { + options.ProgressBar = expandProgressBarOptions(v) + } + if v, ok := tfMap["secondary_value"].([]interface{}); ok && len(v) > 0 { + options.SecondaryValue = expandSecondaryValueOptions(v) + } + if v, ok := tfMap["secondary_value_font_configuration"].([]interface{}); ok && len(v) > 0 { + options.SecondaryValueFontConfiguration = expandFontConfiguration(v) + } + if v, ok := tfMap["trend_arrows"].([]interface{}); ok && len(v) > 0 { + options.TrendArrows = expandTrendArrowOptions(v) + } + + return options +} + +func expandProgressBarOptions(tfList []interface{}) *quicksight.ProgressBarOptions { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + options := &quicksight.ProgressBarOptions{} + + if v, ok := tfMap["visibility"].(string); ok && v != "" { + options.Visibility = aws.String(v) + } + + return options +} + +func expandSecondaryValueOptions(tfList []interface{}) *quicksight.SecondaryValueOptions { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + options := &quicksight.SecondaryValueOptions{} + + if v, ok := tfMap["visibility"].(string); ok && v != "" { + options.Visibility = aws.String(v) + } + + return options +} + +func expandTrendArrowOptions(tfList []interface{}) *quicksight.TrendArrowOptions { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + options := &quicksight.TrendArrowOptions{} + + if v, ok := tfMap["visibility"].(string); ok && v != "" { + options.Visibility = aws.String(v) + } + + return options +} + +func expandKPISortConfiguration(tfList []interface{}) *quicksight.KPISortConfiguration { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + config := &quicksight.KPISortConfiguration{} + + if v, ok := tfMap["trend_group_sort"].([]interface{}); ok && len(v) > 0 { + config.TrendGroupSort = expandFieldSortOptionsList(v) + } + + return config +} + +func expandKPIConditionalFormatting(tfList []interface{}) *quicksight.KPIConditionalFormatting { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + config := &quicksight.KPIConditionalFormatting{} + + if v, ok := tfMap["conditional_formatting_options"].([]interface{}); ok && len(v) > 0 { + config.ConditionalFormattingOptions = expandKPIConditionalFormattingOptions(v) + } + + return config +} + +func expandKPIConditionalFormattingOptions(tfList []interface{}) []*quicksight.KPIConditionalFormattingOption { + if len(tfList) == 0 { + return nil + } + + var options []*quicksight.KPIConditionalFormattingOption + for _, tfMapRaw := range tfList { + tfMap, ok := tfMapRaw.(map[string]interface{}) + if !ok { + continue + } + + opts := expandKPIConditionalFormattingOption(tfMap) + if opts == nil { + continue + } + + options = append(options, opts) + } + + return options +} + +func expandKPIConditionalFormattingOption(tfMap map[string]interface{}) *quicksight.KPIConditionalFormattingOption { + if tfMap == nil { + return nil + } + + options := &quicksight.KPIConditionalFormattingOption{} + + if v, ok := tfMap["primary_value"].([]interface{}); ok && len(v) > 0 { + options.PrimaryValue = expandKPIPrimaryValueConditionalFormatting(v) + } + if v, ok := tfMap["progress_bar"].([]interface{}); ok && len(v) > 0 { + options.ProgressBar = expandKPIProgressBarConditionalFormatting(v) + } + + return options +} + +func expandKPIPrimaryValueConditionalFormatting(tfList []interface{}) *quicksight.KPIPrimaryValueConditionalFormatting { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + options := &quicksight.KPIPrimaryValueConditionalFormatting{} + + if v, ok := tfMap["icon"].([]interface{}); ok && len(v) > 0 { + options.Icon = expandConditionalFormattingIcon(v) + } + if v, ok := tfMap["text_color"].([]interface{}); ok && len(v) > 0 { + options.TextColor = expandConditionalFormattingColor(v) + } + + return options +} + +func expandKPIProgressBarConditionalFormatting(tfList []interface{}) *quicksight.KPIProgressBarConditionalFormatting { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + options := &quicksight.KPIProgressBarConditionalFormatting{} + + if v, ok := tfMap["foreground_color"].([]interface{}); ok && len(v) > 0 { + options.ForegroundColor = expandConditionalFormattingColor(v) + } + + return options +} diff --git a/internal/service/quicksight/visual_line_chart.go b/internal/service/quicksight/visual_line_chart.go new file mode 100644 index 000000000000..a0b74126ce36 --- /dev/null +++ b/internal/service/quicksight/visual_line_chart.go @@ -0,0 +1,951 @@ +package quicksight + +import ( + "fmt" + "regexp" + "time" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/quicksight" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" + "github.com/hashicorp/terraform-provider-aws/internal/verify" +) + +func lineChartVisualSchema() *schema.Schema { + return &schema.Schema{ // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_LineChartVisual.html + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "visual_id": idSchema(), + "actions": visualCustomActionsSchema(customActionsMaxItems), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_VisualCustomAction.html + "chart_configuration": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_LineChartConfiguration.html + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "contribution_analysis_defaults": contributionAnalysisDefaultsSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_ContributionAnalysisDefault.html + "data_labels": dataLabelOptionsSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_DataLabelOptions.html + "default_series_settings": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_LineChartDefaultSeriesSettings.html + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "axis_binding": stringSchema(false, validation.StringInSlice(quicksight.AxisBinding_Values(), false)), + "line_style_settings": lineChartLineStyleSettingsSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_LineChartLineStyleSettings.html + "marker_style_settings": lineChartMarkerStyleSettingsSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_LineChartMarkerStyleSettings.html + }, + }, + }, + "field_wells": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_LineChartFieldWells.html + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "line_chart_aggregated_field_wells": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_LineChartAggregatedFieldWells.html + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "category": dimensionFieldSchema(dimensionsFieldMaxItems), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_DimensionField.html + "colors": dimensionFieldSchema(dimensionsFieldMaxItems), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_DimensionField.html + "small_multiples": dimensionFieldSchema(1), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_DimensionField.html + "values": measureFieldSchema(200), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_MeasureField.html + }, + }, + }, + }, + }, + }, + "forecast_configurations": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_ForecastConfiguration.html + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 10, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "forecast_properties": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_TimeBasedForecastProperties.html + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "lower_boundary": { + Type: schema.TypeFloat, + Optional: true, + }, + "periods_backward": intSchema(false, validation.IntBetween(0, 1000)), + "periods_forward": intSchema(false, validation.IntBetween(1, 1000)), + "prediction_interval": intSchema(false, validation.IntBetween(50, 95)), + "seasonality": intSchema(false, validation.IntBetween(1, 180)), + "upper_boundary": { + Type: schema.TypeFloat, + Optional: true, + }, + }, + }, + }, + "scenario": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_ForecastScenario.html + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "what_if_point_scenario": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_WhatIfPointScenario.html + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "date": stringSchema(true, verify.ValidUTCTimestamp), + "value": { + Type: schema.TypeFloat, + Required: true, + }, + }, + }, + }, + "what_if_range_scenario": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_WhatIfRangeScenario.html + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "end_date": stringSchema(true, verify.ValidUTCTimestamp), + "start_date": stringSchema(true, verify.ValidUTCTimestamp), + "value": { + Type: schema.TypeFloat, + Required: true, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + "legend": legendOptionsSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_LegendOptions.html + "primary_y_axis_display_options": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_LineSeriesAxisDisplayOptions.html + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "axis_options": axisDisplayOptionsSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_AxisDisplayOptions.html + "missing_data_configuration": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_MissingDataConfiguration.html + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 100, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "treatment_option": stringSchema(false, validation.StringInSlice(quicksight.MissingDataTreatmentOption_Values(), false)), + }, + }, + }, + }, + }, + }, + "primary_y_axis_label_options": chartAxisLabelOptionsSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_ChartAxisLabelOptions.html + "reference_lines": referenceLineSchema(referenceLinesMaxItems), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_ReferenceLine.html + "secondary_y_axis_display_options": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_LineSeriesAxisDisplayOptions.html + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "axis_options": axisDisplayOptionsSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_AxisDisplayOptions.html + "missing_data_configuration": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_MissingDataConfiguration.html + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 100, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "treatment_option": stringSchema(false, validation.StringInSlice(quicksight.MissingDataTreatmentOption_Values(), false)), + }, + }, + }, + }, + }, + }, + "secondary_y_axis_label_options": chartAxisLabelOptionsSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_ChartAxisLabelOptions.html + "series": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_SeriesItem.html + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 10, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "data_field_series_item": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_DataFieldSeriesItem.html + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "axis_binding": stringSchema(true, validation.StringInSlice(quicksight.AxisBinding_Values(), false)), + "field_id": stringSchema(true, validation.StringLenBetween(1, 512)), + "field_value": { + Type: schema.TypeString, + Optional: true, + }, + "settings": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_LineChartSeriesSettings.html + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "line_style_settings": lineChartLineStyleSettingsSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_LineChartLineStyleSettings.html + "marker_style_settings": lineChartMarkerStyleSettingsSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_LineChartMarkerStyleSettings.html + }, + }, + }, + }, + }, + }, + "field_series_item": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_FieldSeriesItem.html + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "axis_binding": stringSchema(true, validation.StringInSlice(quicksight.AxisBinding_Values(), false)), + "field_id": stringSchema(true, validation.StringLenBetween(1, 512)), + "settings": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_LineChartSeriesSettings.html + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "line_style_settings": lineChartLineStyleSettingsSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_LineChartLineStyleSettings.html + "marker_style_settings": lineChartMarkerStyleSettingsSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_LineChartMarkerStyleSettings.html + }, + }, + }, + }, + }, + }, + }, + }, + }, + "small_multiples_options": smallMultiplesOptionsSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_SmallMultiplesOptions.html + "sort_configuration": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_LineChartSortConfiguration.html + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "category_items_limit_configuration": itemsLimitConfigurationSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_ItemsLimitConfiguration.html + "category_sort": fieldSortOptionsSchema(100), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_FieldSortOptions.html, + "color_items_limit_configuration": itemsLimitConfigurationSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_ItemsLimitConfiguration.html + "small_multiples_limit_configuration": itemsLimitConfigurationSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_ItemsLimitConfiguration.html + "small_multiples_sort": fieldSortOptionsSchema(100), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_FieldSortOptions.html + }, + }, + }, + "tooltip": tooltipOptionsSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_TooltipOptions.html + "type": stringSchema(false, validation.StringInSlice(quicksight.LineChartType_Values(), false)), + "visual_palette": visualPaletteSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_VisualPalette.html + "x_axis_display_options": axisDisplayOptionsSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_AxisDisplayOptions.html + "x_axis_label_options": chartAxisLabelOptionsSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_ChartAxisLabelOptions.html + }, + }, + }, + "column_hierarchies": columnHierarchiesSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_ColumnHierarchy.html + "subtitle": visualSubtitleLabelOptionsSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_VisualSubtitleLabelOptions.html + "title": visualTitleLabelOptionsSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_VisualTitleLabelOptions.html + }, + }, + } +} + +func lineChartLineStyleSettingsSchema() *schema.Schema { + return &schema.Schema{ // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_LineChartLineStyleSettings.html + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "line_interpolation": stringSchema(false, validation.StringInSlice(quicksight.LineInterpolation_Values(), false)), + "line_style": stringSchema(false, validation.StringInSlice(quicksight.LineChartLineStyle_Values(), false)), + "line_visibility": stringSchema(false, validation.StringInSlice(quicksight.Visibility_Values(), false)), + "line_width": { + Type: schema.TypeString, + Optional: true, + }, + }, + }, + } +} + +func lineChartMarkerStyleSettingsSchema() *schema.Schema { + return &schema.Schema{ // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_LineChartMarkerStyleSettings.html + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "marker_color": stringSchema(false, validation.StringMatch(regexp.MustCompile(`^#[A-F0-9]{6}$`), "")), + "marker_shape": stringSchema(false, validation.StringInSlice(quicksight.LineChartMarkerShape_Values(), false)), + "marker_size": { + Type: schema.TypeString, + Optional: true, + }, + "marker_visibility": stringSchema(false, validation.StringInSlice(quicksight.Visibility_Values(), false)), + }, + }, + } +} + +func expandLineChartVisual(tfList []interface{}) (*quicksight.LineChartVisual, error) { + if len(tfList) == 0 || tfList[0] == nil { + return nil, nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil, nil + } + + visual := &quicksight.LineChartVisual{} + + if v, ok := tfMap["visual_id"].(string); ok && v != "" { + visual.VisualId = aws.String(v) + } + if v, ok := tfMap["actions"].([]interface{}); ok && len(v) > 0 { + value, err := expandVisualCustomActions(v) + if err != nil { + return nil, err + } + visual.Actions = value + } + if v, ok := tfMap["chart_configuration"].([]interface{}); ok && len(v) > 0 { + value, err := expandLineChartConfiguration(v) + if err != nil { + return nil, err + } + visual.ChartConfiguration = value + } + if v, ok := tfMap["column_hierarchies"].([]interface{}); ok && len(v) > 0 { + value, err := expandColumnHierarchies(v) + if err != nil { + return nil, err + } + visual.ColumnHierarchies = value + } + if v, ok := tfMap["subtitle"].([]interface{}); ok && len(v) > 0 { + visual.Subtitle = expandVisualSubtitleLabelOptions(v) + } + if v, ok := tfMap["title"].([]interface{}); ok && len(v) > 0 { + visual.Title = expandVisualTitleLabelOptions(v) + } + + return visual, nil +} + +func expandLineChartConfiguration(tfList []interface{}) (*quicksight.LineChartConfiguration, error) { + if len(tfList) == 0 || tfList[0] == nil { + return nil, nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil, nil + } + + config := &quicksight.LineChartConfiguration{} + + if v, ok := tfMap["type"].(string); ok && v != "" { + config.Type = aws.String(v) + } + if v, ok := tfMap["contribution_analysis_defaults"].([]interface{}); ok && len(v) > 0 { + config.ContributionAnalysisDefaults = expandContributionAnalysisDefaults(v) + } + if v, ok := tfMap["data_labels"].([]interface{}); ok && len(v) > 0 { + config.DataLabels = expandDataLabelOptions(v) + } + if v, ok := tfMap["default_series_settings"].([]interface{}); ok && len(v) > 0 { + config.DefaultSeriesSettings = expandLineChartDefaultSeriesSettings(v) + } + if v, ok := tfMap["field_wells"].([]interface{}); ok && len(v) > 0 { + config.FieldWells = expandLineChartFieldWells(v) + } + if v, ok := tfMap["forecast_configurations"].([]interface{}); ok && len(v) > 0 { + value, err := expandForecastConfigurations(v) + if err != nil { + return nil, err + } + config.ForecastConfigurations = value + } + if v, ok := tfMap["legend"].([]interface{}); ok && len(v) > 0 { + config.Legend = expandLegendOptions(v) + } + if v, ok := tfMap["primary_y_axis_display_options"].([]interface{}); ok && len(v) > 0 { + config.PrimaryYAxisDisplayOptions = expandLineSeriesAxisDisplayOptions(v) + } + if v, ok := tfMap["primary_y_axis_label_options"].([]interface{}); ok && len(v) > 0 { + config.PrimaryYAxisLabelOptions = expandChartAxisLabelOptions(v) + } + if v, ok := tfMap["reference_lines"].([]interface{}); ok && len(v) > 0 { + config.ReferenceLines = expandReferenceLines(v) + } + if v, ok := tfMap["secondary_y_axis_display_options"].([]interface{}); ok && len(v) > 0 { + config.SecondaryYAxisDisplayOptions = expandLineSeriesAxisDisplayOptions(v) + } + if v, ok := tfMap["secondary_y_axis_label_options"].([]interface{}); ok && len(v) > 0 { + config.SecondaryYAxisLabelOptions = expandChartAxisLabelOptions(v) + } + if v, ok := tfMap["series"].([]interface{}); ok && len(v) > 0 { + config.Series = expandSeriesItems(v) + } + if v, ok := tfMap["small_multiples_options"].([]interface{}); ok && len(v) > 0 { + config.SmallMultiplesOptions = expandSmallMultiplesOptions(v) + } + if v, ok := tfMap["sort_configuration"].([]interface{}); ok && len(v) > 0 { + config.SortConfiguration = expandLineChartSortConfiguration(v) + } + if v, ok := tfMap["tooltip"].([]interface{}); ok && len(v) > 0 { + config.Tooltip = expandTooltipOptions(v) + } + if v, ok := tfMap["visual_palette"].([]interface{}); ok && len(v) > 0 { + config.VisualPalette = expandVisualPalette(v) + } + if v, ok := tfMap["x_axis_display_options"].([]interface{}); ok && len(v) > 0 { + config.XAxisDisplayOptions = expandAxisDisplayOptions(v) + } + if v, ok := tfMap["x_axis_label_options"].([]interface{}); ok && len(v) > 0 { + config.XAxisLabelOptions = expandChartAxisLabelOptions(v) + } + + return config, nil +} + +func expandLineChartFieldWells(tfList []interface{}) *quicksight.LineChartFieldWells { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + config := &quicksight.LineChartFieldWells{} + + if v, ok := tfMap["line_chart_aggregated_field_wells"].([]interface{}); ok && len(v) > 0 { + config.LineChartAggregatedFieldWells = expandLineChartAggregatedFieldWells(v) + } + + return config +} + +func expandLineChartAggregatedFieldWells(tfList []interface{}) *quicksight.LineChartAggregatedFieldWells { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + config := &quicksight.LineChartAggregatedFieldWells{} + + if v, ok := tfMap["category"].([]interface{}); ok && len(v) > 0 { + config.Category = expandDimensionFields(v) + } + if v, ok := tfMap["colors"].([]interface{}); ok && len(v) > 0 { + config.Colors = expandDimensionFields(v) + } + if v, ok := tfMap["small_multiples"].([]interface{}); ok && len(v) > 0 { + config.SmallMultiples = expandDimensionFields(v) + } + if v, ok := tfMap["values"].([]interface{}); ok && len(v) > 0 { + config.Values = expandMeasureFields(v) + } + + return config +} + +func expandLineChartSortConfiguration(tfList []interface{}) *quicksight.LineChartSortConfiguration { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + config := &quicksight.LineChartSortConfiguration{} + + if v, ok := tfMap["category_items_limit"].([]interface{}); ok && len(v) > 0 { + config.CategoryItemsLimitConfiguration = expandItemsLimitConfiguration(v) + } + if v, ok := tfMap["category_sort"].([]interface{}); ok && len(v) > 0 { + config.CategorySort = expandFieldSortOptionsList(v) + } + if v, ok := tfMap["color_items_limit_configuration"].([]interface{}); ok && len(v) > 0 { + config.ColorItemsLimitConfiguration = expandItemsLimitConfiguration(v) + } + if v, ok := tfMap["small_multiples_limit_configuration"].([]interface{}); ok && len(v) > 0 { + config.SmallMultiplesLimitConfiguration = expandItemsLimitConfiguration(v) + } + if v, ok := tfMap["small_multiples_sort"].([]interface{}); ok && len(v) > 0 { + config.SmallMultiplesSort = expandFieldSortOptionsList(v) + } + + return config +} + +func expandLineChartDefaultSeriesSettings(tfList []interface{}) *quicksight.LineChartDefaultSeriesSettings { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + options := &quicksight.LineChartDefaultSeriesSettings{} + + if v, ok := tfMap["axis_binding"].(string); ok && v != "" { + options.AxisBinding = aws.String(v) + } + if v, ok := tfMap["line_style_settings"].([]interface{}); ok && len(v) > 0 { + options.LineStyleSettings = expandLineChartLineStyleSettings(v) + } + if v, ok := tfMap["marker_style_settings"].([]interface{}); ok && len(v) > 0 { + options.MarkerStyleSettings = expandLineChartMarkerStyleSettings(v) + } + + return options +} + +func expandLineChartLineStyleSettings(tfList []interface{}) *quicksight.LineChartLineStyleSettings { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + options := &quicksight.LineChartLineStyleSettings{} + + if v, ok := tfMap["line_interpolation"].(string); ok && v != "" { + options.LineInterpolation = aws.String(v) + } + if v, ok := tfMap["line_style"].(string); ok && v != "" { + options.LineStyle = aws.String(v) + } + if v, ok := tfMap["line_visibility"].(string); ok && v != "" { + options.LineVisibility = aws.String(v) + } + if v, ok := tfMap["line_width"].(string); ok && v != "" { + options.LineWidth = aws.String(v) + } + + return options +} + +func expandLineChartMarkerStyleSettings(tfList []interface{}) *quicksight.LineChartMarkerStyleSettings { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + options := &quicksight.LineChartMarkerStyleSettings{} + + if v, ok := tfMap["marker_color"].(string); ok && v != "" { + options.MarkerColor = aws.String(v) + } + if v, ok := tfMap["marker_shape"].(string); ok && v != "" { + options.MarkerShape = aws.String(v) + } + if v, ok := tfMap["marker_size"].(string); ok && v != "" { + options.MarkerSize = aws.String(v) + } + if v, ok := tfMap["marker_visibility"].(string); ok && v != "" { + options.MarkerVisibility = aws.String(v) + } + + return options +} + +func expandForecastConfigurations(tfList []interface{}) ([]*quicksight.ForecastConfiguration, error) { + if len(tfList) == 0 { + return nil, nil + } + + var configs []*quicksight.ForecastConfiguration + for _, tfMapRaw := range tfList { + tfMap, ok := tfMapRaw.(map[string]interface{}) + if !ok { + continue + } + + config, err := expandForecastConfiguration(tfMap) + if err != nil { + return nil, err + } + if config == nil { + continue + } + + configs = append(configs, config) + } + + return configs, nil +} + +func expandForecastConfiguration(tfMap map[string]interface{}) (*quicksight.ForecastConfiguration, error) { + if tfMap == nil { + return nil, nil + } + + config := &quicksight.ForecastConfiguration{} + + if v, ok := tfMap["forecast_properties"].([]interface{}); ok && len(v) > 0 { + config.ForecastProperties = expandTimeBasedForecastProperties(v) + } + if v, ok := tfMap["scenario"].([]interface{}); ok && len(v) > 0 { + value, err := expandForecastScenario(v) + if err != nil { + return nil, err + } + config.Scenario = value + } + + return config, nil +} + +func expandTimeBasedForecastProperties(tfList []interface{}) *quicksight.TimeBasedForecastProperties { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + options := &quicksight.TimeBasedForecastProperties{} + + if v, ok := tfMap["lower_boundary"].(float64); ok { + options.LowerBoundary = aws.Float64(v) + } + if v, ok := tfMap["periods_backward"].(int64); ok { + options.PeriodsBackward = aws.Int64(v) + } + if v, ok := tfMap["periods_forward"].(int64); ok { + options.PeriodsForward = aws.Int64(v) + } + if v, ok := tfMap["prediction_interval"].(int64); ok { + options.PredictionInterval = aws.Int64(v) + } + if v, ok := tfMap["seasonality"].(int64); ok { + options.Seasonality = aws.Int64(v) + } + if v, ok := tfMap["upper_boundary"].(float64); ok { + options.UpperBoundary = aws.Float64(v) + } + return options +} + +func expandForecastScenario(tfList []interface{}) (*quicksight.ForecastScenario, error) { + if len(tfList) == 0 || tfList[0] == nil { + return nil, nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil, nil + } + + scenario := &quicksight.ForecastScenario{} + + if v, ok := tfMap["what_if_point_scenario"].([]interface{}); ok && len(v) > 0 { + value, err := expandWhatIfPointScenario(v) + if err != nil { + return nil, err + } + scenario.WhatIfPointScenario = value + } + if v, ok := tfMap["what_if_range_scenario"].([]interface{}); ok && len(v) > 0 { + value, err := expandWhatIfRangeScenario(v) + if err != nil { + return nil, err + } + scenario.WhatIfRangeScenario = value + } + + return scenario, nil +} + +func expandWhatIfPointScenario(tfList []interface{}) (*quicksight.WhatIfPointScenario, error) { + if len(tfList) == 0 || tfList[0] == nil { + return nil, nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil, nil + } + + scenario := &quicksight.WhatIfPointScenario{} + + if v, ok := tfMap["date"].(string); ok && v != "" { + t, err := time.Parse(time.RFC3339, v) + if err != nil { + return nil, fmt.Errorf("error parsing WhatIfPointScenario date: %w", err) + } + scenario.Date = aws.Time(t) + } + + if v, ok := tfMap["value"].(float64); ok { + scenario.Value = aws.Float64(v) + } + + return scenario, nil +} + +func expandWhatIfRangeScenario(tfList []interface{}) (*quicksight.WhatIfRangeScenario, error) { + if len(tfList) == 0 || tfList[0] == nil { + return nil, nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil, nil + } + + scenario := &quicksight.WhatIfRangeScenario{} + + if v, ok := tfMap["end_date"].(string); ok && v != "" { + t, err := time.Parse(time.RFC3339, v) + if err != nil { + return nil, fmt.Errorf("error parsing WhatIfRangeScenario end_date: %w", err) + } + scenario.EndDate = aws.Time(t) + } + if v, ok := tfMap["start_date"].(string); ok && v != "" { + t, err := time.Parse(time.RFC3339, v) + if err != nil { + return nil, fmt.Errorf("error parsing WhatIfRangeScenario start_date: %w", err) + } + scenario.StartDate = aws.Time(t) + } + if v, ok := tfMap["value"].(float64); ok { + scenario.Value = aws.Float64(v) + } + + return scenario, nil +} + +func expandLineSeriesAxisDisplayOptions(tfList []interface{}) *quicksight.LineSeriesAxisDisplayOptions { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + options := &quicksight.LineSeriesAxisDisplayOptions{} + + if v, ok := tfMap["axis_options"].([]interface{}); ok && len(v) > 0 { + options.AxisOptions = expandAxisDisplayOptions(v) + } + if v, ok := tfMap["missing_data_configuration"].([]interface{}); ok && len(v) > 0 { + options.MissingDataConfigurations = expandMissingDataConfigurations(v) + } + + return options +} + +func expandMissingDataConfigurations(tfList []interface{}) []*quicksight.MissingDataConfiguration { + if len(tfList) == 0 { + return nil + } + + var options []*quicksight.MissingDataConfiguration + for _, tfMapRaw := range tfList { + tfMap, ok := tfMapRaw.(map[string]interface{}) + if !ok { + continue + } + + opts := expandMissingDataConfiguration(tfMap) + if opts == nil { + continue + } + + options = append(options, opts) + } + + return options +} + +func expandMissingDataConfiguration(tfMap map[string]interface{}) *quicksight.MissingDataConfiguration { + if tfMap == nil { + return nil + } + + options := &quicksight.MissingDataConfiguration{} + + if v, ok := tfMap["treatment_option"].(string); ok && v != "" { + options.TreatmentOption = aws.String(v) + } + + return options +} + +func expandSeriesItems(tfList []interface{}) []*quicksight.SeriesItem { + if len(tfList) == 0 { + return nil + } + + var options []*quicksight.SeriesItem + for _, tfMapRaw := range tfList { + tfMap, ok := tfMapRaw.(map[string]interface{}) + if !ok { + continue + } + + opts := expandSeriesItem(tfMap) + if opts == nil { + continue + } + + options = append(options, opts) + } + + return options +} + +func expandSeriesItem(tfMap map[string]interface{}) *quicksight.SeriesItem { + if tfMap == nil { + return nil + } + + options := &quicksight.SeriesItem{} + + if v, ok := tfMap["data_field_series_item"].([]interface{}); ok && len(v) > 0 { + options.DataFieldSeriesItem = expandDataFieldSeriesItem(v) + } + if v, ok := tfMap["field_series_item"].([]interface{}); ok && len(v) > 0 { + options.FieldSeriesItem = expandFieldSeriesItem(v) + } + + return options +} + +func expandDataFieldSeriesItem(tfList []interface{}) *quicksight.DataFieldSeriesItem { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + options := &quicksight.DataFieldSeriesItem{} + + if v, ok := tfMap["axis_binding"].(string); ok && v != "" { + options.AxisBinding = aws.String(v) + } + if v, ok := tfMap["field_id"].(string); ok && v != "" { + options.FieldId = aws.String(v) + } + if v, ok := tfMap["field_value"].(string); ok && v != "" { + options.FieldValue = aws.String(v) + } + + if v, ok := tfMap["settings"].([]interface{}); ok && len(v) > 0 { + options.Settings = expandLineChartSeriesSettings(v) + } + + return options +} + +func expandFieldSeriesItem(tfList []interface{}) *quicksight.FieldSeriesItem { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + options := &quicksight.FieldSeriesItem{} + + if v, ok := tfMap["axis_binding"].(string); ok && v != "" { + options.AxisBinding = aws.String(v) + } + if v, ok := tfMap["field_id"].(string); ok && v != "" { + options.FieldId = aws.String(v) + } + if v, ok := tfMap["settings"].([]interface{}); ok && len(v) > 0 { + options.Settings = expandLineChartSeriesSettings(v) + } + + return options +} + +func expandLineChartSeriesSettings(tfList []interface{}) *quicksight.LineChartSeriesSettings { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + options := &quicksight.LineChartSeriesSettings{} + + if v, ok := tfMap["line_style_settings"].([]interface{}); ok && len(v) > 0 { + options.LineStyleSettings = expandLineChartLineStyleSettings(v) + } + if v, ok := tfMap["marker_style_settings"].([]interface{}); ok && len(v) > 0 { + options.MarkerStyleSettings = expandLineChartMarkerStyleSettings(v) + } + + return options +} diff --git a/internal/service/quicksight/visual_map.go b/internal/service/quicksight/visual_map.go new file mode 100644 index 000000000000..04d14034434c --- /dev/null +++ b/internal/service/quicksight/visual_map.go @@ -0,0 +1,135 @@ +package quicksight + +import ( + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/quicksight" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" +) + +func geospatialMapStyleOptionsSchema() *schema.Schema { + return &schema.Schema{ // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_GeospatialMapStyleOptions.html + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "base_map_style": stringSchema(false, validation.StringInSlice(quicksight.BaseMapStyleType_Values(), false)), + }, + }, + } +} + +func geospatialWindowOptionsSchema() *schema.Schema { + return &schema.Schema{ // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_GeospatialWindowOptions.html + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "bounds": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_GeospatialCoordinateBounds.html + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "east": { + Type: schema.TypeFloat, + Required: true, + ValidateFunc: validation.IntBetween(-1800, 1800), + }, + "north": { + Type: schema.TypeFloat, + Required: true, + ValidateFunc: validation.IntBetween(-90, 90), + }, + "south": { + Type: schema.TypeFloat, + Required: true, + ValidateFunc: validation.IntBetween(-90, 90), + }, + "west": { + Type: schema.TypeFloat, + Required: true, + ValidateFunc: validation.IntBetween(-1800, 1800), + }, + }, + }, + }, + "map_zoom_mode": stringSchema(false, validation.StringInSlice(quicksight.MapZoomMode_Values(), false)), + }, + }, + } +} + +func expandGeospatialMapStyleOptions(tfList []interface{}) *quicksight.GeospatialMapStyleOptions { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + options := &quicksight.GeospatialMapStyleOptions{} + + if v, ok := tfMap["base_map_style"].(string); ok && v != "" { + options.BaseMapStyle = aws.String(v) + } + + return options +} + +func expandGeospatialWindowOptions(tfList []interface{}) *quicksight.GeospatialWindowOptions { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + options := &quicksight.GeospatialWindowOptions{} + + if v, ok := tfMap["map_zoom_mode"].(string); ok && v != "" { + options.MapZoomMode = aws.String(v) + } + if v, ok := tfMap["bounds"].([]interface{}); ok && len(v) > 0 { + options.Bounds = expandGeospatialCoordinateBounds(v) + } + + return options +} + +func expandGeospatialCoordinateBounds(tfList []interface{}) *quicksight.GeospatialCoordinateBounds { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + config := &quicksight.GeospatialCoordinateBounds{} + + if v, ok := tfMap["east"].(float64); ok { + config.East = aws.Float64(v) + } + if v, ok := tfMap["north"].(float64); ok { + config.North = aws.Float64(v) + } + if v, ok := tfMap["south"].(float64); ok { + config.South = aws.Float64(v) + } + if v, ok := tfMap["west"].(float64); ok { + config.West = aws.Float64(v) + } + + return config +} diff --git a/internal/service/quicksight/visual_pie_chart.go b/internal/service/quicksight/visual_pie_chart.go new file mode 100644 index 000000000000..05c2b6f849dd --- /dev/null +++ b/internal/service/quicksight/visual_pie_chart.go @@ -0,0 +1,336 @@ +package quicksight + +import ( + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/quicksight" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" +) + +func pieChartVisualSchema() *schema.Schema { + return &schema.Schema{ // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_PieChartVisual.html + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "visual_id": idSchema(), + "actions": visualCustomActionsSchema(customActionsMaxItems), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_VisualCustomAction.html + "chart_configuration": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_PieChartConfiguration.html + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "category_label_options": chartAxisLabelOptionsSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_ChartAxisLabelOptions.html + "contribution_analysis_defaults": contributionAnalysisDefaultsSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_ContributionAnalysisDefault.html + "data_labels": dataLabelOptionsSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_DataLabelOptions.html + "donut_options": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_DonutOptions.html + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "arc_options": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_ArcOptions.html + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "arc_thickness": stringSchema(false, validation.StringInSlice(quicksight.ArcThicknessOptions_Values(), false)), + }, + }, + }, + "donut_center_options": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_DonutCenterOptions.html + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "label_visibility": stringSchema(false, validation.StringInSlice(quicksight.Visibility_Values(), false)), + }, + }, + }, + }, + }, + }, + "field_wells": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_PieChartFieldWells.html + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "pie_chart_aggregated_field_wells": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_PieChartAggregatedFieldWells.html + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "category": dimensionFieldSchema(dimensionsFieldMaxItems), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_DimensionField.html + "small_multiples": dimensionFieldSchema(1), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_DimensionField.html + "values": measureFieldSchema(200), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_MeasureField.html + }, + }, + }, + }, + }, + }, + "legend": legendOptionsSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_LegendOptions.html + "small_multiples_options": smallMultiplesOptionsSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_SmallMultiplesOptions.html + "sort_configuration": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_PieChartSortConfiguration.html + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "category_items_limit": itemsLimitConfigurationSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_ItemsLimitConfiguration.html + "category_sort": fieldSortOptionsSchema(100), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_FieldSortOptions.html, + "small_multiples_limit_configuration": itemsLimitConfigurationSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_ItemsLimitConfiguration.html + "small_multiples_sort": fieldSortOptionsSchema(100), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_FieldSortOptions.html + }, + }, + }, + "tooltip": tooltipOptionsSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_TooltipOptions.html + "value_label_options": chartAxisLabelOptionsSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_ChartAxisLabelOptions.html + "visual_palette": visualPaletteSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_VisualPalette.html + }, + }, + }, + "column_hierarchies": columnHierarchiesSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_ColumnHierarchy.html + "subtitle": visualSubtitleLabelOptionsSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_VisualSubtitleLabelOptions.html + "title": visualTitleLabelOptionsSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_VisualTitleLabelOptions.html + }, + }, + } +} + +func expandPieChartVisual(tfList []interface{}) (*quicksight.PieChartVisual, error) { + if len(tfList) == 0 || tfList[0] == nil { + return nil, nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil, nil + } + + visual := &quicksight.PieChartVisual{} + + if v, ok := tfMap["visual_id"].(string); ok && v != "" { + visual.VisualId = aws.String(v) + } + if v, ok := tfMap["actions"].([]interface{}); ok && len(v) > 0 { + value, err := expandVisualCustomActions(v) + if err != nil { + return nil, err + } + visual.Actions = value + } + if v, ok := tfMap["chart_configuration"].([]interface{}); ok && len(v) > 0 { + visual.ChartConfiguration = expandPieChartConfiguration(v) + } + if v, ok := tfMap["column_hierarchies"].([]interface{}); ok && len(v) > 0 { + value, err := expandColumnHierarchies(v) + if err != nil { + return nil, err + } + visual.ColumnHierarchies = value + } + if v, ok := tfMap["subtitle"].([]interface{}); ok && len(v) > 0 { + visual.Subtitle = expandVisualSubtitleLabelOptions(v) + } + if v, ok := tfMap["title"].([]interface{}); ok && len(v) > 0 { + visual.Title = expandVisualTitleLabelOptions(v) + } + + return visual, nil +} + +func expandPieChartConfiguration(tfList []interface{}) *quicksight.PieChartConfiguration { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + config := &quicksight.PieChartConfiguration{} + + if v, ok := tfMap["category_label_options"].([]interface{}); ok && len(v) > 0 { + config.CategoryLabelOptions = expandChartAxisLabelOptions(v) + } + if v, ok := tfMap["contribution_analysis_defaults"].([]interface{}); ok && len(v) > 0 { + config.ContributionAnalysisDefaults = expandContributionAnalysisDefaults(v) + } + if v, ok := tfMap["data_labels"].([]interface{}); ok && len(v) > 0 { + config.DataLabels = expandDataLabelOptions(v) + } + if v, ok := tfMap["donut_options"].([]interface{}); ok && len(v) > 0 { + config.DonutOptions = expandDonutOptions(v) + } + if v, ok := tfMap["field_wells"].([]interface{}); ok && len(v) > 0 { + config.FieldWells = expandPieChartFieldWells(v) + } + if v, ok := tfMap["legend"].([]interface{}); ok && len(v) > 0 { + config.Legend = expandLegendOptions(v) + } + if v, ok := tfMap["small_multiples_options"].([]interface{}); ok && len(v) > 0 { + config.SmallMultiplesOptions = expandSmallMultiplesOptions(v) + } + if v, ok := tfMap["sort_configuration"].([]interface{}); ok && len(v) > 0 { + config.SortConfiguration = expandPieChartSortConfiguration(v) + } + if v, ok := tfMap["tooltip"].([]interface{}); ok && len(v) > 0 { + config.Tooltip = expandTooltipOptions(v) + } + if v, ok := tfMap["value_label_options"].([]interface{}); ok && len(v) > 0 { + config.ValueLabelOptions = expandChartAxisLabelOptions(v) + } + if v, ok := tfMap["visual_palette"].([]interface{}); ok && len(v) > 0 { + config.VisualPalette = expandVisualPalette(v) + } + + return config +} + +func expandPieChartFieldWells(tfList []interface{}) *quicksight.PieChartFieldWells { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + config := &quicksight.PieChartFieldWells{} + + if v, ok := tfMap["pie_chart_aggregated_field_wells"].([]interface{}); ok && len(v) > 0 { + config.PieChartAggregatedFieldWells = expandPieChartAggregatedFieldWells(v) + } + + return config +} + +func expandPieChartAggregatedFieldWells(tfList []interface{}) *quicksight.PieChartAggregatedFieldWells { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + config := &quicksight.PieChartAggregatedFieldWells{} + + if v, ok := tfMap["category"].([]interface{}); ok && len(v) > 0 { + config.Category = expandDimensionFields(v) + } + if v, ok := tfMap["small_multiples"].([]interface{}); ok && len(v) > 0 { + config.SmallMultiples = expandDimensionFields(v) + } + if v, ok := tfMap["values"].([]interface{}); ok && len(v) > 0 { + config.Values = expandMeasureFields(v) + } + + return config +} + +func expandPieChartSortConfiguration(tfList []interface{}) *quicksight.PieChartSortConfiguration { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + config := &quicksight.PieChartSortConfiguration{} + + if v, ok := tfMap["category_items_limit"].([]interface{}); ok && len(v) > 0 { + config.CategoryItemsLimit = expandItemsLimitConfiguration(v) + } + if v, ok := tfMap["category_sort"].([]interface{}); ok && len(v) > 0 { + config.CategorySort = expandFieldSortOptionsList(v) + } + if v, ok := tfMap["small_multiples_limit_configuration"].([]interface{}); ok && len(v) > 0 { + config.SmallMultiplesLimitConfiguration = expandItemsLimitConfiguration(v) + } + if v, ok := tfMap["small_multiples_sort"].([]interface{}); ok && len(v) > 0 { + config.SmallMultiplesSort = expandFieldSortOptionsList(v) + } + + return config +} + +func expandDonutOptions(tfList []interface{}) *quicksight.DonutOptions { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + options := &quicksight.DonutOptions{} + + if v, ok := tfMap["arc_options"].([]interface{}); ok && len(v) > 0 { + options.ArcOptions = expandArcOptions(v) + } + if v, ok := tfMap["donut_center_options"].([]interface{}); ok && len(v) > 0 { + options.DonutCenterOptions = expandDonutCenterOptions(v) + } + + return options +} + +func expandArcOptions(tfList []interface{}) *quicksight.ArcOptions { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + options := &quicksight.ArcOptions{} + + if v, ok := tfMap["arc_thickness"].(string); ok && v != "" { + options.ArcThickness = aws.String(v) + } + + return options +} + +func expandDonutCenterOptions(tfList []interface{}) *quicksight.DonutCenterOptions { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + options := &quicksight.DonutCenterOptions{} + + if v, ok := tfMap["label_visibility"].(string); ok && v != "" { + options.LabelVisibility = aws.String(v) + } + + return options +} diff --git a/internal/service/quicksight/visual_pivot_table.go b/internal/service/quicksight/visual_pivot_table.go new file mode 100644 index 000000000000..6cc652f3a52c --- /dev/null +++ b/internal/service/quicksight/visual_pivot_table.go @@ -0,0 +1,1176 @@ +package quicksight + +import ( + "regexp" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/quicksight" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" + "github.com/hashicorp/terraform-provider-aws/internal/flex" +) + +func pivotTableVisualSchema() *schema.Schema { + return &schema.Schema{ // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_PivotTableVisual.html + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "visual_id": idSchema(), + "actions": visualCustomActionsSchema(customActionsMaxItems), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_VisualCustomAction.html + "chart_configuration": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_PivotTableConfiguration.html + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "field_options": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_PivotTableFieldOptions.html + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "data_path_options": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_PivotTableDataPathOption.html + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 100, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "data_path_list": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_DataPathValue.html + Type: schema.TypeList, + Required: true, + MinItems: 1, + MaxItems: 20, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "field_id": stringSchema(true, validation.StringLenBetween(1, 512)), + "field_value": stringSchema(true, validation.StringLenBetween(1, 2048)), + }, + }, + }, + "width": { + Type: schema.TypeString, + Optional: true, + }, + }, + }, + }, + "selected_field_options": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_PivotTableFieldOption.html + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 100, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "field_id": stringSchema(true, validation.StringLenBetween(1, 512)), + "custom_label": stringSchema(false, validation.StringLenBetween(1, 2048)), + "visibility": stringSchema(false, validation.StringInSlice(quicksight.Visibility_Values(), false)), + }, + }, + }, + }, + }, + }, + "field_wells": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_PivotTableFieldWells.html + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "pivot_table_aggregated_field_wells": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_PivotTableAggregatedFieldWells.html + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "columns": dimensionFieldSchema(40), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_DimensionField.html + "rows": dimensionFieldSchema(40), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_DimensionField.html + "values": measureFieldSchema(40), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_MeasureField.html + }, + }, + }, + }, + }, + }, + "paginated_report_options": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_PivotTablePaginatedReportOptions.html + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "overflow_column_header_visibility": stringSchema(false, validation.StringInSlice(quicksight.Visibility_Values(), false)), + "vertical_overflow_visibility": stringSchema(false, validation.StringInSlice(quicksight.Visibility_Values(), false)), + }, + }, + }, + "sort_configuration": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_PivotTableSortConfiguration.html + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "field_sort_options": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_PivotFieldSortOptions.html + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 200, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "field_id": stringSchema(true, validation.StringLenBetween(1, 512)), + "sort_by": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_PivotTableSortBy.html + Type: schema.TypeList, + Required: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "column": columnSortSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_ColumnSort.html + "data_path": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_DataPathSort.html + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "direction": stringSchema(true, validation.StringInSlice(quicksight.SortDirection_Values(), false)), + "sort_paths": dataPathValueSchema(20), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_DataPathValue.html + }, + }, + }, + "field": fieldSortSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_FieldSort.html + }, + }, + }, + }, + }, + }, + }, + }, + }, + "table_options": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_PivotTableOptions.html + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "cell_style": tableCellStyleSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_TableCellStyle.html + "collapsed_row_dimensions_visibility": stringSchema(false, validation.StringInSlice(quicksight.Visibility_Values(), false)), + "column_header_style": tableCellStyleSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_TableCellStyle.html + "column_name_visibility": stringSchema(false, validation.StringInSlice(quicksight.Visibility_Values(), false)), + "metric_placement": stringSchema(false, validation.StringInSlice(quicksight.PivotTableMetricPlacement_Values(), false)), + "row_alternate_color_options": rowAlternateColorOptionsSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_RowAlternateColorOptions.html + "row_field_names_style": tableCellStyleSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_TableCellStyle.html + "row_header_style": tableCellStyleSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_TableCellStyle.html + "single_metric_visibility": stringSchema(false, validation.StringInSlice(quicksight.Visibility_Values(), false)), + "toggle_buttons_visibility": stringSchema(false, validation.StringInSlice(quicksight.Visibility_Values(), false)), + }, + }, + }, + "total_options": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_PivotTableTotalOptions.html + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "column_subtotal_options": subtotalOptionsSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_SubtotalOptions.html + "column_total_options": pivotTotalOptionsSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_PivotTotalOptions.html + "row_subtotal_options": subtotalOptionsSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_SubtotalOptions.html + "row_total_options": pivotTotalOptionsSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_PivotTotalOptions.html + }, + }, + }, + }, + }, + }, + "conditional_formatting": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_PivotTableConditionalFormatting.html + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "conditional_formatting_options": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_PivotTableConditionalFormattingOption.html + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 100, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "cell": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_PivotTableCellConditionalFormatting.html + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "field_id": stringSchema(true, validation.StringLenBetween(1, 512)), + "scope": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_PivotTableConditionalFormattingScope.html + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "role": stringSchema(false, validation.StringInSlice(quicksight.PivotTableConditionalFormattingScopeRole_Values(), false)), + }, + }, + }, + "text_format": textConditionalFormatSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_TextConditionalFormat.html + }, + }, + }, + }, + }, + }, + }, + }, + }, + "subtitle": visualSubtitleLabelOptionsSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_VisualSubtitleLabelOptions.html + "title": visualTitleLabelOptionsSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_VisualTitleLabelOptions.html + }, + }, + } +} + +func tableBorderOptionsSchema() *schema.Schema { + return &schema.Schema{ // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_TableBorderOptions.html + Type: schema.TypeList, + Required: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "color": stringSchema(false, validation.StringMatch(regexp.MustCompile(`^#[A-F0-9]{6}$`), "")), + "style": stringSchema(false, validation.StringInSlice(quicksight.TableBorderStyle_Values(), false)), + "thickness": intSchema(false, validation.IntBetween(1, 4)), + }, + }, + } +} + +func tableCellStyleSchema() *schema.Schema { + return &schema.Schema{ // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_TableCellStyle.html + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "background_color": stringSchema(false, validation.StringMatch(regexp.MustCompile(`^#[A-F0-9]{6}$`), "")), + "border": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_GlobalTableBorderOptions.html + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "side_specific_border": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_TableSideBorderOptions.html + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "bottom": tableBorderOptionsSchema(), + "inner_horizontal": tableBorderOptionsSchema(), + "inner_vertical": tableBorderOptionsSchema(), + "left": tableBorderOptionsSchema(), + "right": tableBorderOptionsSchema(), + "top": tableBorderOptionsSchema(), + }, + }, + }, + "uniform_border": tableBorderOptionsSchema(), + }, + }, + }, + "font_configuration": fontConfigurationSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_FontConfiguration.html + "height": intSchema(false, validation.IntBetween(8, 500)), + "horizontal_text_alignment": stringSchema(false, validation.StringInSlice(quicksight.HorizontalTextAlignment_Values(), false)), + "text_wrap": stringSchema(false, validation.StringInSlice(quicksight.TextWrap_Values(), false)), + "vertical_text_alignment": stringSchema(false, validation.StringInSlice(quicksight.VerticalTextAlignment_Values(), false)), + "visibility": stringSchema(false, validation.StringInSlice(quicksight.Visibility_Values(), false)), + }, + }, + } +} + +func subtotalOptionsSchema() *schema.Schema { + return &schema.Schema{ // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_SubtotalOptions.html + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "custom_label": { + Type: schema.TypeString, + Optional: true, + }, + "field_level": stringSchema(false, validation.StringInSlice(quicksight.PivotTableSubtotalLevel_Values(), false)), + "field_level_options": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_PivotTableFieldSubtotalOptions.html + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 100, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "field_id": stringSchema(false, validation.StringLenBetween(1, 512)), + }, + }, + }, + "metric_header_cell_style": tableCellStyleSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_TableCellStyle.html + "total_cell_style": tableCellStyleSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_TableCellStyle.html + "totals_visibility": stringSchema(false, validation.StringInSlice(quicksight.Visibility_Values(), false)), + "value_cell_style": tableCellStyleSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_TableCellStyle.html + }, + }, + } +} + +func pivotTotalOptionsSchema() *schema.Schema { + return &schema.Schema{ // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_PivotTotalOptions.html + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "custom_label": { + Type: schema.TypeString, + Optional: true, + }, + "metric_header_cell_style": tableCellStyleSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_TableCellStyle.html + "placement": stringSchema(false, validation.StringInSlice(quicksight.TableTotalsPlacement_Values(), false)), + "scroll_status": stringSchema(false, validation.StringInSlice(quicksight.TableTotalsScrollStatus_Values(), false)), + "total_cell_style": tableCellStyleSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_TableCellStyle.html + "totals_visibility": stringSchema(false, validation.StringInSlice(quicksight.Visibility_Values(), false)), + "value_cell_style": tableCellStyleSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_TableCellStyle.html + }, + }, + } +} + +func rowAlternateColorOptionsSchema() *schema.Schema { + return &schema.Schema{ // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_RowAlternateColorOptions.html + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "row_alternate_colors": { + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Schema{Type: schema.TypeString, ValidateFunc: validation.StringMatch(regexp.MustCompile(`^#[A-F0-9]{6}$`), "")}, + }, + "status": stringSchema(false, validation.StringInSlice(quicksight.Status_Values(), false)), + }, + }, + } +} + +func textConditionalFormatSchema() *schema.Schema { + return &schema.Schema{ // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_TextConditionalFormat.html + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "background_color": conditionalFormattingColorSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_ConditionalFormattingColor.html + "icon": conditionalFormattingIconSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_ConditionalFormattingIcon.html + "text_color": conditionalFormattingColorSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_ConditionalFormattingColor.html + }, + }, + } +} + +func expandPivotTableVisual(tfList []interface{}) (*quicksight.PivotTableVisual, error) { + if len(tfList) == 0 || tfList[0] == nil { + return nil, nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil, nil + } + + visual := &quicksight.PivotTableVisual{} + + if v, ok := tfMap["visual_id"].(string); ok && v != "" { + visual.VisualId = aws.String(v) + } + if v, ok := tfMap["actions"].([]interface{}); ok && len(v) > 0 { + value, err := expandVisualCustomActions(v) + if err != nil { + return nil, err + } + visual.Actions = value + } + if v, ok := tfMap["chart_configuration"].([]interface{}); ok && len(v) > 0 { + visual.ChartConfiguration = expandPivotTableConfiguration(v) + } + if v, ok := tfMap["conditional_formatting"].([]interface{}); ok && len(v) > 0 { + visual.ConditionalFormatting = expandPivotTableConditionalFormatting(v) + } + if v, ok := tfMap["subtitle"].([]interface{}); ok && len(v) > 0 { + visual.Subtitle = expandVisualSubtitleLabelOptions(v) + } + if v, ok := tfMap["title"].([]interface{}); ok && len(v) > 0 { + visual.Title = expandVisualTitleLabelOptions(v) + } + + return visual, nil +} + +func expandPivotTableConfiguration(tfList []interface{}) *quicksight.PivotTableConfiguration { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + config := &quicksight.PivotTableConfiguration{} + + if v, ok := tfMap["field_options"].([]interface{}); ok && len(v) > 0 { + config.FieldOptions = expandPivotTableFieldOptions(v) + } + if v, ok := tfMap["field_wells"].([]interface{}); ok && len(v) > 0 { + config.FieldWells = expandPivotTableFieldWells(v) + } + if v, ok := tfMap["paginated_report_options"].([]interface{}); ok && len(v) > 0 { + config.PaginatedReportOptions = expandPivotTablePaginatedReportOptions(v) + } + if v, ok := tfMap["sort_configuration"].([]interface{}); ok && len(v) > 0 { + config.SortConfiguration = expandPivotTableSortConfiguration(v) + } + if v, ok := tfMap["table_options"].([]interface{}); ok && len(v) > 0 { + config.TableOptions = expandPivotTableOptions(v) + } + if v, ok := tfMap["total_options"].([]interface{}); ok && len(v) > 0 { + config.TotalOptions = expandPivotTableTotalOptions(v) + } + + return config +} + +func expandPivotTableFieldWells(tfList []interface{}) *quicksight.PivotTableFieldWells { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + config := &quicksight.PivotTableFieldWells{} + + if v, ok := tfMap["pivot_table_aggregated_field_wells"].([]interface{}); ok && len(v) > 0 { + config.PivotTableAggregatedFieldWells = expandPivotTableAggregatedFieldWells(v) + } + + return config +} + +func expandPivotTableAggregatedFieldWells(tfList []interface{}) *quicksight.PivotTableAggregatedFieldWells { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + config := &quicksight.PivotTableAggregatedFieldWells{} + + if v, ok := tfMap["columns"].([]interface{}); ok && len(v) > 0 { + config.Columns = expandDimensionFields(v) + } + if v, ok := tfMap["rows"].([]interface{}); ok && len(v) > 0 { + config.Rows = expandDimensionFields(v) + } + if v, ok := tfMap["values"].([]interface{}); ok && len(v) > 0 { + config.Values = expandMeasureFields(v) + } + + return config +} + +func expandPivotTableSortConfiguration(tfList []interface{}) *quicksight.PivotTableSortConfiguration { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + config := &quicksight.PivotTableSortConfiguration{} + + if v, ok := tfMap["field_sort_options"].([]interface{}); ok && len(v) > 0 { + config.FieldSortOptions = expandPivotFieldSortOptionsList(v) + } + + return config +} + +func expandPivotFieldSortOptionsList(tfList []interface{}) []*quicksight.PivotFieldSortOptions { + if len(tfList) == 0 { + return nil + } + + var options []*quicksight.PivotFieldSortOptions + for _, tfMapRaw := range tfList { + tfMap, ok := tfMapRaw.(map[string]interface{}) + if !ok { + continue + } + + opts := expandPivotFieldSortOptions(tfMap) + if opts == nil { + continue + } + + options = append(options, opts) + } + + return options +} + +func expandPivotFieldSortOptions(tfMap map[string]interface{}) *quicksight.PivotFieldSortOptions { + if tfMap == nil { + return nil + } + + options := &quicksight.PivotFieldSortOptions{} + + if v, ok := tfMap["field_id"].(string); ok && v != "" { + options.FieldId = aws.String(v) + } + if v, ok := tfMap["sort_by"].([]interface{}); ok && len(v) > 0 { + options.SortBy = expandPivotTableSortBy(v) + } + + return options +} + +func expandPivotTableSortBy(tfList []interface{}) *quicksight.PivotTableSortBy { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + config := &quicksight.PivotTableSortBy{} + + if v, ok := tfMap["column"].([]interface{}); ok && len(v) > 0 { + config.Column = expandColumnSort(v) + } + if v, ok := tfMap["data_path"].([]interface{}); ok && len(v) > 0 { + config.DataPath = expandDataPathSort(v) + } + if v, ok := tfMap["field"].([]interface{}); ok && len(v) > 0 { + config.Field = expandFieldSort(v) + } + + return config +} + +func expandDataPathSort(tfList []interface{}) *quicksight.DataPathSort { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + config := &quicksight.DataPathSort{} + + if v, ok := tfMap["direction"].(string); ok && v != "" { + config.Direction = aws.String(v) + } + if v, ok := tfMap["sort_paths"].([]interface{}); ok && len(v) > 0 { + config.SortPaths = expandDataPathValues(v) + } + + return config +} + +func expandPivotTableFieldOptions(tfList []interface{}) *quicksight.PivotTableFieldOptions { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + options := &quicksight.PivotTableFieldOptions{} + + if v, ok := tfMap["data_path_options"].([]interface{}); ok && len(v) > 0 { + options.DataPathOptions = expandPivotTableDataPathOptions(v) + } + if v, ok := tfMap["selected_field_options"].([]interface{}); ok && len(v) > 0 { + options.SelectedFieldOptions = expandPivotTableFieldOptionsList(v) + } + + return options +} + +func expandPivotTableDataPathOptions(tfList []interface{}) []*quicksight.PivotTableDataPathOption { + if len(tfList) == 0 { + return nil + } + + var options []*quicksight.PivotTableDataPathOption + for _, tfMapRaw := range tfList { + tfMap, ok := tfMapRaw.(map[string]interface{}) + if !ok { + continue + } + + opts := expandPivotTableDataPathOption(tfMap) + if opts == nil { + continue + } + + options = append(options, opts) + } + + return options +} + +func expandPivotTableDataPathOption(tfMap map[string]interface{}) *quicksight.PivotTableDataPathOption { + if tfMap == nil { + return nil + } + + options := &quicksight.PivotTableDataPathOption{} + + if v, ok := tfMap["width"].(string); ok && v != "" { + options.Width = aws.String(v) + } + if v, ok := tfMap["data_path_list"].([]interface{}); ok && len(v) > 0 { + options.DataPathList = expandDataPathValues(v) + } + + return options +} + +func expandPivotTableFieldOptionsList(tfList []interface{}) []*quicksight.PivotTableFieldOption { + if len(tfList) == 0 { + return nil + } + + var options []*quicksight.PivotTableFieldOption + for _, tfMapRaw := range tfList { + tfMap, ok := tfMapRaw.(map[string]interface{}) + if !ok { + continue + } + + opts := expandPivotTableFieldOption(tfMap) + if opts == nil { + continue + } + + options = append(options, opts) + } + + return options +} + +func expandPivotTableFieldOption(tfMap map[string]interface{}) *quicksight.PivotTableFieldOption { + if tfMap == nil { + return nil + } + + options := &quicksight.PivotTableFieldOption{} + + if v, ok := tfMap["field_id"].(string); ok && v != "" { + options.FieldId = aws.String(v) + } + if v, ok := tfMap["custom_label"].(string); ok && v != "" { + options.CustomLabel = aws.String(v) + } + if v, ok := tfMap["visibility"].(string); ok && v != "" { + options.Visibility = aws.String(v) + } + + return options +} + +func expandPivotTablePaginatedReportOptions(tfList []interface{}) *quicksight.PivotTablePaginatedReportOptions { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + options := &quicksight.PivotTablePaginatedReportOptions{} + + if v, ok := tfMap["overflow_column_header_visibility"].(string); ok && v != "" { + options.OverflowColumnHeaderVisibility = aws.String(v) + } + if v, ok := tfMap["vertical_overflow_visibility"].(string); ok && v != "" { + options.VerticalOverflowVisibility = aws.String(v) + } + + return options +} + +func expandPivotTableOptions(tfList []interface{}) *quicksight.PivotTableOptions { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + options := &quicksight.PivotTableOptions{} + + if v, ok := tfMap["collapsed_row_dimensions_visibility"].(string); ok && v != "" { + options.CollapsedRowDimensionsVisibility = aws.String(v) + } + if v, ok := tfMap["column_name_visibility"].(string); ok && v != "" { + options.ColumnNamesVisibility = aws.String(v) + } + if v, ok := tfMap["metric_placement"].(string); ok && v != "" { + options.MetricPlacement = aws.String(v) + } + if v, ok := tfMap["single_metric_visibility"].(string); ok && v != "" { + options.SingleMetricVisibility = aws.String(v) + } + if v, ok := tfMap["toggle_buttons_visibility"].(string); ok && v != "" { + options.ToggleButtonsVisibility = aws.String(v) + } + if v, ok := tfMap["cell_style"].([]interface{}); ok && len(v) > 0 { + options.CellStyle = expandTableCellStyle(v) + } + if v, ok := tfMap["column_header_style"].([]interface{}); ok && len(v) > 0 { + options.ColumnHeaderStyle = expandTableCellStyle(v) + } + if v, ok := tfMap["row_alternate_color_options"].([]interface{}); ok && len(v) > 0 { + options.RowAlternateColorOptions = expandRowAlternateColorOptions(v) + } + if v, ok := tfMap["row_field_names_style"].([]interface{}); ok && len(v) > 0 { + options.RowFieldNamesStyle = expandTableCellStyle(v) + } + if v, ok := tfMap["row_header_style"].([]interface{}); ok && len(v) > 0 { + options.RowHeaderStyle = expandTableCellStyle(v) + } + + return options +} + +func expandTableCellStyle(tfList []interface{}) *quicksight.TableCellStyle { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + style := &quicksight.TableCellStyle{} + + if v, ok := tfMap["background_color"].(string); ok && v != "" { + style.BackgroundColor = aws.String(v) + } + if v, ok := tfMap["height"].(int64); ok { + style.Height = aws.Int64(v) + } + if v, ok := tfMap["horizontal_text_alignment"].(string); ok && v != "" { + style.HorizontalTextAlignment = aws.String(v) + } + if v, ok := tfMap["text_wrap"].(string); ok && v != "" { + style.TextWrap = aws.String(v) + } + if v, ok := tfMap["vertical_text_alignment"].(string); ok && v != "" { + style.VerticalTextAlignment = aws.String(v) + } + if v, ok := tfMap["visibility"].(string); ok && v != "" { + style.Visibility = aws.String(v) + } + if v, ok := tfMap["border"].([]interface{}); ok && len(v) > 0 { + style.Border = expandGlobalTableBorderOptions(v) + } + if v, ok := tfMap["font_configuration"].([]interface{}); ok && len(v) > 0 { + style.FontConfiguration = expandFontConfiguration(v) + } + + return style +} + +func expandGlobalTableBorderOptions(tfList []interface{}) *quicksight.GlobalTableBorderOptions { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + options := &quicksight.GlobalTableBorderOptions{} + + if v, ok := tfMap["side_specific_border"].([]interface{}); ok && len(v) > 0 { + options.SideSpecificBorder = expandTableSideBorderOptions(v) + } + if v, ok := tfMap["uniform_border"].([]interface{}); ok && len(v) > 0 { + options.UniformBorder = expandTableBorderOptions(v) + } + + return options +} + +func expandTableSideBorderOptions(tfList []interface{}) *quicksight.TableSideBorderOptions { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + options := &quicksight.TableSideBorderOptions{} + + if v, ok := tfMap["bottom"].([]interface{}); ok && len(v) > 0 { + options.Bottom = expandTableBorderOptions(v) + } + if v, ok := tfMap["inner_horizontal"].([]interface{}); ok && len(v) > 0 { + options.InnerHorizontal = expandTableBorderOptions(v) + } + if v, ok := tfMap["inner_vertical"].([]interface{}); ok && len(v) > 0 { + options.InnerVertical = expandTableBorderOptions(v) + } + if v, ok := tfMap["left"].([]interface{}); ok && len(v) > 0 { + options.Left = expandTableBorderOptions(v) + } + if v, ok := tfMap["right"].([]interface{}); ok && len(v) > 0 { + options.Right = expandTableBorderOptions(v) + } + if v, ok := tfMap["top"].([]interface{}); ok && len(v) > 0 { + options.Top = expandTableBorderOptions(v) + } + + return options +} + +func expandTableBorderOptions(tfList []interface{}) *quicksight.TableBorderOptions { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + options := &quicksight.TableBorderOptions{} + + if v, ok := tfMap["color"].(string); ok && v != "" { + options.Color = aws.String(v) + } + if v, ok := tfMap["style"].(string); ok && v != "" { + options.Style = aws.String(v) + } + if v, ok := tfMap["thickness"].(int64); ok { + options.Thickness = aws.Int64(v) + } + + return options +} + +func expandPivotTableTotalOptions(tfList []interface{}) *quicksight.PivotTableTotalOptions { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + options := &quicksight.PivotTableTotalOptions{} + + if v, ok := tfMap["column_subtotal_options"].([]interface{}); ok && len(v) > 0 { + options.ColumnSubtotalOptions = expandSubtotalOptions(v) + } + if v, ok := tfMap["column_total_options"].([]interface{}); ok && len(v) > 0 { + options.ColumnTotalOptions = expandPivotTotalOptions(v) + } + if v, ok := tfMap["row_subtotal_options"].([]interface{}); ok && len(v) > 0 { + options.RowSubtotalOptions = expandSubtotalOptions(v) + } + if v, ok := tfMap["row_total_options"].([]interface{}); ok && len(v) > 0 { + options.RowTotalOptions = expandPivotTotalOptions(v) + } + + return options +} + +func expandSubtotalOptions(tfList []interface{}) *quicksight.SubtotalOptions { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + options := &quicksight.SubtotalOptions{} + + if v, ok := tfMap["custom_label"].(string); ok && v != "" { + options.CustomLabel = aws.String(v) + } + if v, ok := tfMap["field_level"].(string); ok && v != "" { + options.FieldLevel = aws.String(v) + } + if v, ok := tfMap["totals_visibility"].(string); ok && v != "" { + options.TotalsVisibility = aws.String(v) + } + if v, ok := tfMap["field_level_options"].([]interface{}); ok && len(v) > 0 { + options.FieldLevelOptions = expandPivotTableFieldSubtotalOptionsList(v) + } + if v, ok := tfMap["metric_header_cell_style"].([]interface{}); ok && len(v) > 0 { + options.MetricHeaderCellStyle = expandTableCellStyle(v) + } + if v, ok := tfMap["total_cell_style"].([]interface{}); ok && len(v) > 0 { + options.TotalCellStyle = expandTableCellStyle(v) + } + if v, ok := tfMap["value_cell_style"].([]interface{}); ok && len(v) > 0 { + options.ValueCellStyle = expandTableCellStyle(v) + } + + return options +} + +func expandPivotTableFieldSubtotalOptionsList(tfList []interface{}) []*quicksight.PivotTableFieldSubtotalOptions { + if len(tfList) == 0 { + return nil + } + + var options []*quicksight.PivotTableFieldSubtotalOptions + for _, tfMapRaw := range tfList { + tfMap, ok := tfMapRaw.(map[string]interface{}) + if !ok { + continue + } + + opts := expandPivotTableFieldSubtotalOptions(tfMap) + if opts == nil { + continue + } + + options = append(options, opts) + } + + return options +} + +func expandPivotTableFieldSubtotalOptions(tfMap map[string]interface{}) *quicksight.PivotTableFieldSubtotalOptions { + if tfMap == nil { + return nil + } + + options := &quicksight.PivotTableFieldSubtotalOptions{} + + if v, ok := tfMap["field_id"].(string); ok && v != "" { + options.FieldId = aws.String(v) + } + return options +} + +func expandPivotTotalOptions(tfList []interface{}) *quicksight.PivotTotalOptions { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + options := &quicksight.PivotTotalOptions{} + + if v, ok := tfMap["custom_label"].(string); ok && v != "" { + options.CustomLabel = aws.String(v) + } + if v, ok := tfMap["placement"].(string); ok && v != "" { + options.Placement = aws.String(v) + } + if v, ok := tfMap["scroll_status"].(string); ok && v != "" { + options.ScrollStatus = aws.String(v) + } + if v, ok := tfMap["totals_visibility"].(string); ok && v != "" { + options.TotalsVisibility = aws.String(v) + } + if v, ok := tfMap["metric_header_cell_style"].([]interface{}); ok && len(v) > 0 { + options.MetricHeaderCellStyle = expandTableCellStyle(v) + } + if v, ok := tfMap["total_cell_style"].([]interface{}); ok && len(v) > 0 { + options.TotalCellStyle = expandTableCellStyle(v) + } + if v, ok := tfMap["value_cell_style"].([]interface{}); ok && len(v) > 0 { + options.ValueCellStyle = expandTableCellStyle(v) + } + + return options +} + +func expandRowAlternateColorOptions(tfList []interface{}) *quicksight.RowAlternateColorOptions { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + options := &quicksight.RowAlternateColorOptions{} + + if v, ok := tfMap["status"].(string); ok && v != "" { + options.Status = aws.String(v) + } + if v, ok := tfMap["row_alternate_colors"].([]interface{}); ok && len(v) > 0 { + options.RowAlternateColors = flex.ExpandStringList(v) + } + + return options +} + +func expandPivotTableConditionalFormatting(tfList []interface{}) *quicksight.PivotTableConditionalFormatting { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + options := &quicksight.PivotTableConditionalFormatting{} + + if v, ok := tfMap["conditional_formatting_options"].([]interface{}); ok && len(v) > 0 { + options.ConditionalFormattingOptions = expandPivotTableConditionalFormattingOptions(v) + } + + return options +} + +func expandPivotTableConditionalFormattingOptions(tfList []interface{}) []*quicksight.PivotTableConditionalFormattingOption { + if len(tfList) == 0 { + return nil + } + + var options []*quicksight.PivotTableConditionalFormattingOption + for _, tfMapRaw := range tfList { + tfMap, ok := tfMapRaw.(map[string]interface{}) + if !ok { + continue + } + + opts := expandPivotTableConditionalFormattingOption(tfMap) + if opts == nil { + continue + } + + options = append(options, opts) + } + + return options +} + +func expandPivotTableConditionalFormattingOption(tfMap map[string]interface{}) *quicksight.PivotTableConditionalFormattingOption { + if tfMap == nil { + return nil + } + + options := &quicksight.PivotTableConditionalFormattingOption{} + + if v, ok := tfMap["cell"].([]interface{}); ok && len(v) > 0 { + options.Cell = expandPivotTableCellConditionalFormatting(v) + } + + return options +} + +func expandPivotTableCellConditionalFormatting(tfList []interface{}) *quicksight.PivotTableCellConditionalFormatting { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + options := &quicksight.PivotTableCellConditionalFormatting{} + + if v, ok := tfMap["field_id"].(string); ok && v != "" { + options.FieldId = aws.String(v) + } + if v, ok := tfMap["scope"].([]interface{}); ok && len(v) > 0 { + options.Scope = expandPivotTableConditionalFormattingScope(v) + } + if v, ok := tfMap["text_format"].([]interface{}); ok && len(v) > 0 { + options.TextFormat = expandTextConditionalFormat(v) + } + + return options +} + +func expandPivotTableConditionalFormattingScope(tfList []interface{}) *quicksight.PivotTableConditionalFormattingScope { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + options := &quicksight.PivotTableConditionalFormattingScope{} + + if v, ok := tfMap["role"].(string); ok && v != "" { + options.Role = aws.String(v) + } + + return options +} diff --git a/internal/service/quicksight/visual_radar_chart.go b/internal/service/quicksight/visual_radar_chart.go new file mode 100644 index 000000000000..0677ceb6d6e2 --- /dev/null +++ b/internal/service/quicksight/visual_radar_chart.go @@ -0,0 +1,317 @@ +package quicksight + +import ( + "regexp" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/quicksight" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" +) + +func radarChartVisualSchema() *schema.Schema { + return &schema.Schema{ // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_RadarChartVisual.html + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "visual_id": idSchema(), + "actions": visualCustomActionsSchema(customActionsMaxItems), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_VisualCustomAction.html + "chart_configuration": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_RadarChartConfiguration.html + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "alternate_band_colors_visibility": stringSchema(false, validation.StringInSlice(quicksight.Visibility_Values(), false)), + "alternate_band_even_color": stringSchema(false, validation.StringMatch(regexp.MustCompile(`^#[A-F0-9]{6}$`), "")), + "alternate_band_odd_color": stringSchema(false, validation.StringMatch(regexp.MustCompile(`^#[A-F0-9]{6}$`), "")), + "base_series_settings": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_RadarChartSeriesSettings.html + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "area_style_settings": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_RadarChartAreaStyleSettings.html + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "visibility": stringSchema(false, validation.StringInSlice(quicksight.Visibility_Values(), false)), + }, + }, + }, + }, + }, + }, + "category_axis": axisDisplayOptionsSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_AxisDisplayOptions.html + "category_label_options": chartAxisLabelOptionsSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_ChartAxisLabelOptions.html + "color_axis": axisDisplayOptionsSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_AxisDisplayOptions.html + "color_label_options": chartAxisLabelOptionsSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_ChartAxisLabelOptions.html + "field_wells": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_RadarChartFieldWells.html + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "radar_chart_aggregated_field_wells": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_RadarChartAggregatedFieldWells.html + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "category": dimensionFieldSchema(1), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_DimensionField.html + "color": dimensionFieldSchema(1), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_DimensionField.html + "values": measureFieldSchema(20), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_MeasureField.html + }, + }, + }, + }, + }, + }, + "legend": legendOptionsSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_LegendOptions.html + "shape": stringSchema(false, validation.StringInSlice(quicksight.RadarChartShape_Values(), false)), + "sort_configuration": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_RadarChartSortConfiguration.html + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "category_items_limit": itemsLimitConfigurationSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_ItemsLimitConfiguration.html + "category_sort": fieldSortOptionsSchema(100), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_FieldSortOptions.html, + "color_items_limit": itemsLimitConfigurationSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_ItemsLimitConfiguration.html + "color_sort": fieldSortOptionsSchema(100), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_FieldSortOptions.html + }, + }, + }, + "start_angle": floatSchema(false, validation.FloatBetween(-360, 360)), + "visual_palette": visualPaletteSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_VisualPalette.html + }, + }, + }, + "column_hierarchies": columnHierarchiesSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_ColumnHierarchy.html + "subtitle": visualSubtitleLabelOptionsSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_VisualSubtitleLabelOptions.html + "title": visualTitleLabelOptionsSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_VisualTitleLabelOptions.html + }, + }, + } +} + +func expandRadarChartVisual(tfList []interface{}) (*quicksight.RadarChartVisual, error) { + if len(tfList) == 0 || tfList[0] == nil { + return nil, nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil, nil + } + + visual := &quicksight.RadarChartVisual{} + + if v, ok := tfMap["visual_id"].(string); ok && v != "" { + visual.VisualId = aws.String(v) + } + if v, ok := tfMap["actions"].([]interface{}); ok && len(v) > 0 { + value, err := expandVisualCustomActions(v) + if err != nil { + return nil, err + } + visual.Actions = value + } + if v, ok := tfMap["chart_configuration"].([]interface{}); ok && len(v) > 0 { + visual.ChartConfiguration = expandRadarChartConfiguration(v) + } + if v, ok := tfMap["column_hierarchies"].([]interface{}); ok && len(v) > 0 { + value, err := expandColumnHierarchies(v) + if err != nil { + return nil, err + } + visual.ColumnHierarchies = value + } + if v, ok := tfMap["subtitle"].([]interface{}); ok && len(v) > 0 { + visual.Subtitle = expandVisualSubtitleLabelOptions(v) + } + if v, ok := tfMap["title"].([]interface{}); ok && len(v) > 0 { + visual.Title = expandVisualTitleLabelOptions(v) + } + + return visual, nil +} + +func expandRadarChartConfiguration(tfList []interface{}) *quicksight.RadarChartConfiguration { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + config := &quicksight.RadarChartConfiguration{} + + if v, ok := tfMap["alternate_band_colors_visibility"].(string); ok && v != "" { + config.AlternateBandColorsVisibility = aws.String(v) + } + if v, ok := tfMap["alternate_band_even_color"].(string); ok && v != "" { + config.AlternateBandEvenColor = aws.String(v) + } + if v, ok := tfMap["alternate_band_odd_color"].(string); ok && v != "" { + config.AlternateBandOddColor = aws.String(v) + } + if v, ok := tfMap["shape"].(string); ok && v != "" { + config.Shape = aws.String(v) + } + if v, ok := tfMap["start_angle"].(float64); ok { + config.StartAngle = aws.Float64(v) + } + if v, ok := tfMap["base_series_settings"].([]interface{}); ok && len(v) > 0 { + config.BaseSeriesSettings = expandRadarChartSeriesSettings(v) + } + if v, ok := tfMap["category_axis"].([]interface{}); ok && len(v) > 0 { + config.CategoryAxis = expandAxisDisplayOptions(v) + } + if v, ok := tfMap["category_label_options"].([]interface{}); ok && len(v) > 0 { + config.CategoryLabelOptions = expandChartAxisLabelOptions(v) + } + if v, ok := tfMap["color_axis"].([]interface{}); ok && len(v) > 0 { + config.ColorAxis = expandAxisDisplayOptions(v) + } + if v, ok := tfMap["color_label_options"].([]interface{}); ok && len(v) > 0 { + config.ColorLabelOptions = expandChartAxisLabelOptions(v) + } + if v, ok := tfMap["field_wells"].([]interface{}); ok && len(v) > 0 { + config.FieldWells = expandRadarChartFieldWells(v) + } + if v, ok := tfMap["legend"].([]interface{}); ok && len(v) > 0 { + config.Legend = expandLegendOptions(v) + } + if v, ok := tfMap["sort_configuration"].([]interface{}); ok && len(v) > 0 { + config.SortConfiguration = expandRadarChartSortConfiguration(v) + } + if v, ok := tfMap["visual_palette"].([]interface{}); ok && len(v) > 0 { + config.VisualPalette = expandVisualPalette(v) + } + + return config +} + +func expandRadarChartFieldWells(tfList []interface{}) *quicksight.RadarChartFieldWells { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + config := &quicksight.RadarChartFieldWells{} + + if v, ok := tfMap["radar_chart_aggregated_field_wells"].([]interface{}); ok && len(v) > 0 { + config.RadarChartAggregatedFieldWells = expandRadarChartAggregatedFieldWells(v) + } + + return config +} + +func expandRadarChartAggregatedFieldWells(tfList []interface{}) *quicksight.RadarChartAggregatedFieldWells { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + config := &quicksight.RadarChartAggregatedFieldWells{} + + if v, ok := tfMap["category"].([]interface{}); ok && len(v) > 0 { + config.Category = expandDimensionFields(v) + } + if v, ok := tfMap["colors"].([]interface{}); ok && len(v) > 0 { + config.Color = expandDimensionFields(v) + } + if v, ok := tfMap["values"].([]interface{}); ok && len(v) > 0 { + config.Values = expandMeasureFields(v) + } + + return config +} + +func expandRadarChartSortConfiguration(tfList []interface{}) *quicksight.RadarChartSortConfiguration { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + config := &quicksight.RadarChartSortConfiguration{} + + if v, ok := tfMap["category_items_limit"].([]interface{}); ok && len(v) > 0 { + config.CategoryItemsLimit = expandItemsLimitConfiguration(v) + } + if v, ok := tfMap["category_sort"].([]interface{}); ok && len(v) > 0 { + config.CategorySort = expandFieldSortOptionsList(v) + } + if v, ok := tfMap["color_items_limit"].([]interface{}); ok && len(v) > 0 { + config.ColorItemsLimit = expandItemsLimitConfiguration(v) + } + if v, ok := tfMap["color_sort"].([]interface{}); ok && len(v) > 0 { + config.ColorSort = expandFieldSortOptionsList(v) + } + + return config +} + +func expandRadarChartSeriesSettings(tfList []interface{}) *quicksight.RadarChartSeriesSettings { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + config := &quicksight.RadarChartSeriesSettings{} + + if v, ok := tfMap["area_style_settings"].([]interface{}); ok && len(v) > 0 { + config.AreaStyleSettings = expandRadarChartAreaStyleSettings(v) + } + + return config +} + +func expandRadarChartAreaStyleSettings(tfList []interface{}) *quicksight.RadarChartAreaStyleSettings { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + config := &quicksight.RadarChartAreaStyleSettings{} + + if v, ok := tfMap["visibility"].(string); ok && v != "" { + config.Visibility = aws.String(v) + } + + return config +} diff --git a/internal/service/quicksight/visual_sankey_diagram.go b/internal/service/quicksight/visual_sankey_diagram.go new file mode 100644 index 000000000000..41c085a77fc6 --- /dev/null +++ b/internal/service/quicksight/visual_sankey_diagram.go @@ -0,0 +1,200 @@ +package quicksight + +import ( + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/quicksight" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" +) + +func sankeyDiagramVisualSchema() *schema.Schema { + return &schema.Schema{ // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_SankeyDiagramVisual.html + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "visual_id": idSchema(), + "actions": visualCustomActionsSchema(customActionsMaxItems), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_VisualCustomAction.html + "chart_configuration": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_SankeyDiagramChartConfiguration.html + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "data_labels": dataLabelOptionsSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_DataLabelOptions.html + "field_wells": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_SankeyDiagramFieldWells.html + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "sankey_diagram_aggregated_field_wells": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_SankeyDiagramAggregatedFieldWells.html + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "destination": dimensionFieldSchema(dimensionsFieldMaxItems), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_DimensionField.html + "source": dimensionFieldSchema(dimensionsFieldMaxItems), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_DimensionField.html + "weight": measureFieldSchema(200), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_MeasureField.html + }, + }, + }, + }, + }, + }, + "sort_configuration": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_SankeyDiagramSortConfiguration.html + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "destination_items_limit": itemsLimitConfigurationSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_ItemsLimitConfiguration.html + "source_items_limit": itemsLimitConfigurationSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_ItemsLimitConfiguration.html + "weight_sort": fieldSortOptionsSchema(100), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_FieldSortOptions.html + }, + }, + }, + }, + }, + }, + "subtitle": visualSubtitleLabelOptionsSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_VisualSubtitleLabelOptions.html + "title": visualTitleLabelOptionsSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_VisualTitleLabelOptions.html + }, + }, + } +} + +func expandSankeyDiagramVisual(tfList []interface{}) (*quicksight.SankeyDiagramVisual, error) { + if len(tfList) == 0 || tfList[0] == nil { + return nil, nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil, nil + } + + visual := &quicksight.SankeyDiagramVisual{} + + if v, ok := tfMap["visual_id"].(string); ok && v != "" { + visual.VisualId = aws.String(v) + } + if v, ok := tfMap["actions"].([]interface{}); ok && len(v) > 0 { + value, err := expandVisualCustomActions(v) + if err != nil { + return nil, err + } + visual.Actions = value + } + if v, ok := tfMap["chart_configuration"].([]interface{}); ok && len(v) > 0 { + visual.ChartConfiguration = expandSankeyDiagramConfiguration(v) + } + if v, ok := tfMap["subtitle"].([]interface{}); ok && len(v) > 0 { + visual.Subtitle = expandVisualSubtitleLabelOptions(v) + } + if v, ok := tfMap["title"].([]interface{}); ok && len(v) > 0 { + visual.Title = expandVisualTitleLabelOptions(v) + } + + return visual, nil +} + +func expandSankeyDiagramConfiguration(tfList []interface{}) *quicksight.SankeyDiagramChartConfiguration { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + config := &quicksight.SankeyDiagramChartConfiguration{} + + if v, ok := tfMap["data_labels"].([]interface{}); ok && len(v) > 0 { + config.DataLabels = expandDataLabelOptions(v) + } + if v, ok := tfMap["field_wells"].([]interface{}); ok && len(v) > 0 { + config.FieldWells = expandSankeyDiagramFieldWells(v) + } + if v, ok := tfMap["sort_configuration"].([]interface{}); ok && len(v) > 0 { + config.SortConfiguration = expandSankeyDiagramSortConfiguration(v) + } + + return config +} + +func expandSankeyDiagramFieldWells(tfList []interface{}) *quicksight.SankeyDiagramFieldWells { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + config := &quicksight.SankeyDiagramFieldWells{} + + if v, ok := tfMap["sankey_diagram_aggregated_field_wells"].([]interface{}); ok && len(v) > 0 { + config.SankeyDiagramAggregatedFieldWells = expandSankeyDiagramAggregatedFieldWells(v) + } + + return config +} + +func expandSankeyDiagramAggregatedFieldWells(tfList []interface{}) *quicksight.SankeyDiagramAggregatedFieldWells { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + config := &quicksight.SankeyDiagramAggregatedFieldWells{} + + if v, ok := tfMap["destination"].([]interface{}); ok && len(v) > 0 { + config.Destination = expandDimensionFields(v) + } + if v, ok := tfMap["source"].([]interface{}); ok && len(v) > 0 { + config.Source = expandDimensionFields(v) + } + if v, ok := tfMap["weight"].([]interface{}); ok && len(v) > 0 { + config.Weight = expandMeasureFields(v) + } + + return config +} + +func expandSankeyDiagramSortConfiguration(tfList []interface{}) *quicksight.SankeyDiagramSortConfiguration { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + config := &quicksight.SankeyDiagramSortConfiguration{} + + if v, ok := tfMap["destination_items_limit"].([]interface{}); ok && len(v) > 0 { + config.DestinationItemsLimit = expandItemsLimitConfiguration(v) + } + if v, ok := tfMap["source_items_limit"].([]interface{}); ok && len(v) > 0 { + config.SourceItemsLimit = expandItemsLimitConfiguration(v) + } + if v, ok := tfMap["weight_sort"].([]interface{}); ok && len(v) > 0 { + config.WeightSort = expandFieldSortOptionsList(v) + } + + return config +} diff --git a/internal/service/quicksight/visual_scatter_plot.go b/internal/service/quicksight/visual_scatter_plot.go new file mode 100644 index 000000000000..9115c39fd1ec --- /dev/null +++ b/internal/service/quicksight/visual_scatter_plot.go @@ -0,0 +1,244 @@ +package quicksight + +import ( + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/quicksight" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" +) + +func scatterPlotVisualSchema() *schema.Schema { + return &schema.Schema{ // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_ScatterPlotVisual.html + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "visual_id": idSchema(), + "actions": visualCustomActionsSchema(customActionsMaxItems), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_VisualCustomAction.html + "chart_configuration": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_ScatterPlotConfiguration.html + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "data_labels": dataLabelOptionsSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_DataLabelOptions.html + "field_wells": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_ScatterPlotFieldWells.html + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "scatter_plot_categorically_aggregated_field_wells": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_ScatterPlotCategoricallyAggregatedFieldWells.html + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "category": dimensionFieldSchema(dimensionsFieldMaxItems), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_DimensionField.html + "size": measureFieldSchema(200), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_MeasureField.html + "x_axis": measureFieldSchema(200), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_MeasureField.html + "y_axis": measureFieldSchema(200), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_MeasureField.html + }, + }, + }, + "scatter_plot_unaggregated_field_wells": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_ScatterPlotUnaggregatedFieldWells.html + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "size": measureFieldSchema(200), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_MeasureField.html + "x_axis": dimensionFieldSchema(dimensionsFieldMaxItems), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_DimensionField.html + "y_axis": dimensionFieldSchema(dimensionsFieldMaxItems), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_DimensionField.html + }, + }, + }, + }, + }, + }, + "legend": legendOptionsSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_LegendOptions.html + "tooltip": tooltipOptionsSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_TooltipOptions.html + "visual_palette": visualPaletteSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_VisualPalette.html + "x_axis_display_options": axisDisplayOptionsSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_AxisDisplayOptions.html + "x_axis_label_options": chartAxisLabelOptionsSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_ChartAxisLabelOptions.html + "y_axis_display_options": axisDisplayOptionsSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_AxisDisplayOptions.html + "y_axis_label_options": chartAxisLabelOptionsSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_ChartAxisLabelOptions.html + }, + }, + }, + "column_hierarchies": columnHierarchiesSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_ColumnHierarchy.html + "subtitle": visualSubtitleLabelOptionsSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_VisualSubtitleLabelOptions.html + "title": visualTitleLabelOptionsSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_VisualTitleLabelOptions.html + }, + }, + } +} + +func expandScatterPlotVisual(tfList []interface{}) (*quicksight.ScatterPlotVisual, error) { + if len(tfList) == 0 || tfList[0] == nil { + return nil, nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil, nil + } + + visual := &quicksight.ScatterPlotVisual{} + + if v, ok := tfMap["visual_id"].(string); ok && v != "" { + visual.VisualId = aws.String(v) + } + if v, ok := tfMap["actions"].([]interface{}); ok && len(v) > 0 { + value, err := expandVisualCustomActions(v) + if err != nil { + return nil, err + } + visual.Actions = value + } + if v, ok := tfMap["chart_configuration"].([]interface{}); ok && len(v) > 0 { + value, err := expandScatterPlotConfiguration(v) + if err != nil { + return nil, err + } + visual.ChartConfiguration = value + } + if v, ok := tfMap["column_hierarchies"].([]interface{}); ok && len(v) > 0 { + value, err := expandColumnHierarchies(v) + if err != nil { + return nil, err + } + visual.ColumnHierarchies = value + } + if v, ok := tfMap["subtitle"].([]interface{}); ok && len(v) > 0 { + visual.Subtitle = expandVisualSubtitleLabelOptions(v) + } + if v, ok := tfMap["title"].([]interface{}); ok && len(v) > 0 { + visual.Title = expandVisualTitleLabelOptions(v) + } + + return visual, nil +} + +func expandScatterPlotConfiguration(tfList []interface{}) (*quicksight.ScatterPlotConfiguration, error) { + if len(tfList) == 0 || tfList[0] == nil { + return nil, nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil, nil + } + + config := &quicksight.ScatterPlotConfiguration{} + + if v, ok := tfMap["data_labels"].([]interface{}); ok && len(v) > 0 { + config.DataLabels = expandDataLabelOptions(v) + } + if v, ok := tfMap["field_wells"].([]interface{}); ok && len(v) > 0 { + config.FieldWells = expandScatterPlotFieldWells(v) + } + if v, ok := tfMap["legend"].([]interface{}); ok && len(v) > 0 { + config.Legend = expandLegendOptions(v) + } + if v, ok := tfMap["tooltip"].([]interface{}); ok && len(v) > 0 { + config.Tooltip = expandTooltipOptions(v) + } + if v, ok := tfMap["visual_palette"].([]interface{}); ok && len(v) > 0 { + config.VisualPalette = expandVisualPalette(v) + } + if v, ok := tfMap["x_axis_display_options"].([]interface{}); ok && len(v) > 0 { + config.XAxisDisplayOptions = expandAxisDisplayOptions(v) + } + if v, ok := tfMap["x_axis_label_options"].([]interface{}); ok && len(v) > 0 { + config.XAxisLabelOptions = expandChartAxisLabelOptions(v) + } + if v, ok := tfMap["y_axis_display_options"].([]interface{}); ok && len(v) > 0 { + config.YAxisDisplayOptions = expandAxisDisplayOptions(v) + } + if v, ok := tfMap["y_axis_label_options"].([]interface{}); ok && len(v) > 0 { + config.YAxisLabelOptions = expandChartAxisLabelOptions(v) + } + + return config, nil +} + +func expandScatterPlotFieldWells(tfList []interface{}) *quicksight.ScatterPlotFieldWells { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + config := &quicksight.ScatterPlotFieldWells{} + + if v, ok := tfMap["scatter_plot_categorically_aggregated_field_wells"].([]interface{}); ok && len(v) > 0 { + config.ScatterPlotCategoricallyAggregatedFieldWells = expandScatterPlotCategoricallyAggregatedFieldWells(v) + } + if v, ok := tfMap["scatter_plot_unaggregated_field_wells"].([]interface{}); ok && len(v) > 0 { + config.ScatterPlotUnaggregatedFieldWells = expandScatterPlotUnaggregatedFieldWells(v) + } + + return config +} + +func expandScatterPlotCategoricallyAggregatedFieldWells(tfList []interface{}) *quicksight.ScatterPlotCategoricallyAggregatedFieldWells { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + config := &quicksight.ScatterPlotCategoricallyAggregatedFieldWells{} + + if v, ok := tfMap["category"].([]interface{}); ok && len(v) > 0 { + config.Category = expandDimensionFields(v) + } + if v, ok := tfMap["size"].([]interface{}); ok && len(v) > 0 { + config.Size = expandMeasureFields(v) + } + if v, ok := tfMap["x_axis"].([]interface{}); ok && len(v) > 0 { + config.XAxis = expandMeasureFields(v) + } + if v, ok := tfMap["y_axis"].([]interface{}); ok && len(v) > 0 { + config.YAxis = expandMeasureFields(v) + } + + return config +} + +func expandScatterPlotUnaggregatedFieldWells(tfList []interface{}) *quicksight.ScatterPlotUnaggregatedFieldWells { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + config := &quicksight.ScatterPlotUnaggregatedFieldWells{} + + if v, ok := tfMap["size"].([]interface{}); ok && len(v) > 0 { + config.Size = expandMeasureFields(v) + } + if v, ok := tfMap["x_axis"].([]interface{}); ok && len(v) > 0 { + config.XAxis = expandDimensionFields(v) + } + if v, ok := tfMap["y_axis"].([]interface{}); ok && len(v) > 0 { + config.YAxis = expandDimensionFields(v) + } + + return config +} diff --git a/internal/service/quicksight/visual_sort.go b/internal/service/quicksight/visual_sort.go new file mode 100644 index 000000000000..a8044242dc4a --- /dev/null +++ b/internal/service/quicksight/visual_sort.go @@ -0,0 +1,141 @@ +package quicksight + +import ( + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/quicksight" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" +) + +func fieldSortOptionsSchema(maxItems int) *schema.Schema { + return &schema.Schema{ // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_FieldSortOptions.html + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: maxItems, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "column_sort": columnSortSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_ColumnSort.html + "field_sort": fieldSortSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_FieldSort.html + }, + }, + } +} + +func columnSortSchema() *schema.Schema { + return &schema.Schema{ // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_ColumnSort.html + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "direction": stringSchema(true, validation.StringInSlice(quicksight.SortDirection_Values(), false)), + "sort_by": columnSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_ColumnIdentifier.html + "aggregation_function": aggregationFunctionSchema(false), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_AggregationFunction.html + }, + }, + } +} + +func fieldSortSchema() *schema.Schema { + return &schema.Schema{ // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_FieldSort.html + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "direction": stringSchema(true, validation.StringInSlice(quicksight.SortDirection_Values(), false)), + "field_id": stringSchema(true, validation.StringLenBetween(1, 512)), + }, + }, + } +} + +func expandFieldSortOptionsList(tfList []interface{}) []*quicksight.FieldSortOptions { + if len(tfList) == 0 { + return nil + } + + var options []*quicksight.FieldSortOptions + for _, tfMapRaw := range tfList { + tfMap, ok := tfMapRaw.(map[string]interface{}) + if !ok { + continue + } + + opts := expandFieldSortOptions(tfMap) + if opts == nil { + continue + } + + options = append(options, opts) + } + + return options +} + +func expandFieldSortOptions(tfMap map[string]interface{}) *quicksight.FieldSortOptions { + if tfMap == nil { + return nil + } + + options := &quicksight.FieldSortOptions{} + + if v, ok := tfMap["column_sort"].([]interface{}); ok && len(v) > 0 { + options.ColumnSort = expandColumnSort(v) + } + if v, ok := tfMap["field_sort"].([]interface{}); ok && len(v) > 0 { + options.FieldSort = expandFieldSort(v) + } + + return options +} + +func expandColumnSort(tfList []interface{}) *quicksight.ColumnSort { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + config := &quicksight.ColumnSort{} + + if v, ok := tfMap["direction"].(string); ok && v != "" { + config.Direction = aws.String(v) + } + if v, ok := tfMap["sort_by"].([]interface{}); ok && len(v) > 0 { + config.SortBy = expandColumnIdentifier(v) + } + if v, ok := tfMap["aggregation_function"].([]interface{}); ok && len(v) > 0 { + config.AggregationFunction = expandAggregationFunction(v) + } + + return config +} + +func expandFieldSort(tfList []interface{}) *quicksight.FieldSort { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + config := &quicksight.FieldSort{} + + if v, ok := tfMap["direction"].(string); ok && v != "" { + config.Direction = aws.String(v) + } + if v, ok := tfMap["field_id"].(string); ok && v != "" { + config.FieldId = aws.String(v) + } + + return config +} diff --git a/internal/service/quicksight/visual_table.go b/internal/service/quicksight/visual_table.go new file mode 100644 index 000000000000..450d0a46c442 --- /dev/null +++ b/internal/service/quicksight/visual_table.go @@ -0,0 +1,996 @@ +package quicksight + +import ( + "regexp" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/quicksight" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" + "github.com/hashicorp/terraform-provider-aws/internal/flex" +) + +func tableVisualSchema() *schema.Schema { + return &schema.Schema{ // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_TableVisual.html + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "visual_id": idSchema(), + "actions": visualCustomActionsSchema(customActionsMaxItems), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_VisualCustomAction.html + "chart_configuration": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_TableConfiguration.html + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "field_options": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_TableFieldOptions.html + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "order": { + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 100, + Elem: &schema.Schema{Type: schema.TypeString, ValidateFunc: validation.StringLenBetween(1, 512)}, + }, + "selected_field_options": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_TableFieldOption.html + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 100, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "field_id": stringSchema(true, validation.StringLenBetween(1, 512)), + "custom_label": stringSchema(false, validation.StringLenBetween(1, 2048)), + "url_styling": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_TableFieldURLConfiguration.html + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "image_configuration": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_TableFieldImageConfiguration.html + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "sizing_options": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_TableCellImageSizingConfiguration.html + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "table_cell_image_scaling_configuration": stringSchema(false, validation.StringInSlice(quicksight.TableCellImageScalingConfiguration_Values(), false)), + }, + }, + }, + }, + }, + }, + "link_configuration": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_TableFieldLinkConfiguration.html + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "content": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_TableFieldLinkContentConfiguration.html + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "custom_icon_content": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_TableFieldCustomIconContent.html + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "icon": stringSchema(false, validation.StringInSlice(quicksight.TableFieldIconSetType_Values(), false)), + }, + }, + }, + "custom_text_content": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_TableFieldCustomTextContent.html + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "font_configuration": fontConfigurationSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_FontConfiguration.html + "value": { + Type: schema.TypeString, + Optional: true, + }, + }, + }, + }, + }, + }, + }, + "target": stringSchema(false, validation.StringInSlice(quicksight.URLTargetConfiguration_Values(), false)), + }, + }, + }, + }, + }, + }, + "visibility": stringSchema(false, validation.StringInSlice(quicksight.Visibility_Values(), false)), + "width": { + Type: schema.TypeString, + Optional: true, + }, + }, + }, + }, + }, + }, + }, + "field_wells": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_TableFieldWells.html + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "table_aggregated_field_wells": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_TableAggregatedFieldWells.html + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "group_by": dimensionFieldSchema(dimensionsFieldMaxItems), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_DimensionField.html + "values": measureFieldSchema(200), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_MeasureField.html + }, + }, + }, + "table_unaggregated_field_wells": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_TableUnaggregatedFieldWells.html + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "values": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_UnaggregatedField.html + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 200, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "column": columnSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_ColumnIdentifier.html + "field_id": stringSchema(true, validation.StringLenBetween(1, 512)), + "format_configuration": formatConfigurationSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_FormatConfiguration.html + }, + }, + }, + }, + }, + }, + }, + }, + }, + "paginated_report_options": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_TablePaginatedReportOptions.html + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "overflow_column_header_visibility": stringSchema(false, validation.StringInSlice(quicksight.Visibility_Values(), false)), + "vertical_overflow_visibility": stringSchema(false, validation.StringInSlice(quicksight.Visibility_Values(), false)), + }, + }, + }, + "sort_configuration": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_TableSortConfiguration.html + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "pagination_configuration": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_PaginationConfiguration.html + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "page_number": intSchema(true, validation.IntAtLeast(1)), + "page_size": { + Type: schema.TypeInt, + Required: true, + }, + }, + }, + }, + "row_sort": fieldSortOptionsSchema(100), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_FieldSortOptions.html + }, + }, + }, + "table_inline_visualizations": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_TableInlineVisualization.html + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 200, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "data_bars": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_DataBarsOptions.html + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "field_id": stringSchema(true, validation.StringLenBetween(1, 512)), + "negative_color": stringSchema(false, validation.StringMatch(regexp.MustCompile(`^#[A-F0-9]{6}$`), "")), + "positive_color": stringSchema(false, validation.StringMatch(regexp.MustCompile(`^#[A-F0-9]{6}$`), "")), + }, + }, + }, + }, + }, + }, + "table_options": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_TableOptions.html + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "cell_style": tableCellStyleSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_TableCellStyle.html + "header_style": tableCellStyleSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_TableCellStyle.html + "orientation": stringSchema(false, validation.StringInSlice(quicksight.TableOrientation_Values(), false)), + "row_alternate_color_options": rowAlternateColorOptionsSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_RowAlternateColorOptions.html + }, + }, + }, + "total_options": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_TotalOptions.html + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "custom_label": { + Type: schema.TypeString, + Optional: true, + }, + "placement": stringSchema(false, validation.StringInSlice(quicksight.TableTotalsPlacement_Values(), false)), + "scroll_status": stringSchema(false, validation.StringInSlice(quicksight.TableTotalsScrollStatus_Values(), false)), + "total_cell_style": tableCellStyleSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_TableCellStyle.html + "totals_visibility": stringSchema(false, validation.StringInSlice(quicksight.Visibility_Values(), false)), + }, + }, + }, + }, + }, + }, + "conditional_formatting": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_TableConditionalFormatting.html + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "conditional_formatting_options": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_TableConditionalFormattingOption.html + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 100, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "cell": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_TableCellConditionalFormatting.html + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "field_id": stringSchema(true, validation.StringLenBetween(1, 512)), + "text_format": textConditionalFormatSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_TextConditionalFormat.html + }, + }, + }, + "row": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_TableRowConditionalFormatting.html + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "background_color": conditionalFormattingColorSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_ConditionalFormattingColor.html + "text_color": conditionalFormattingColorSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_ConditionalFormattingColor.html + }, + }, + }, + }, + }, + }, + }, + }, + }, + "subtitle": visualSubtitleLabelOptionsSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_VisualSubtitleLabelOptions.html + "title": visualTitleLabelOptionsSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_VisualTitleLabelOptions.html + }, + }, + } +} + +func expandTableVisual(tfList []interface{}) (*quicksight.TableVisual, error) { + if len(tfList) == 0 || tfList[0] == nil { + return nil, nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil, nil + } + + visual := &quicksight.TableVisual{} + + if v, ok := tfMap["visual_id"].(string); ok && v != "" { + visual.VisualId = aws.String(v) + } + if v, ok := tfMap["actions"].([]interface{}); ok && len(v) > 0 { + value, err := expandVisualCustomActions(v) + if err != nil { + return nil, err + } + visual.Actions = value + } + if v, ok := tfMap["chart_configuration"].([]interface{}); ok && len(v) > 0 { + visual.ChartConfiguration = expandTableConfiguration(v) + } + if v, ok := tfMap["conditional_formatting"].([]interface{}); ok && len(v) > 0 { + visual.ConditionalFormatting = expandTableConditionalFormatting(v) + } + if v, ok := tfMap["subtitle"].([]interface{}); ok && len(v) > 0 { + visual.Subtitle = expandVisualSubtitleLabelOptions(v) + } + if v, ok := tfMap["title"].([]interface{}); ok && len(v) > 0 { + visual.Title = expandVisualTitleLabelOptions(v) + } + + return visual, nil +} + +func expandTableConfiguration(tfList []interface{}) *quicksight.TableConfiguration { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + config := &quicksight.TableConfiguration{} + + if v, ok := tfMap["field_options"].([]interface{}); ok && len(v) > 0 { + config.FieldOptions = expandTableFieldOptions(v) + } + if v, ok := tfMap["field_wells"].([]interface{}); ok && len(v) > 0 { + config.FieldWells = expandTableFieldWells(v) + } + if v, ok := tfMap["paginated_report_options"].([]interface{}); ok && len(v) > 0 { + config.PaginatedReportOptions = expandTablePaginatedReportOptions(v) + } + if v, ok := tfMap["sort_configuration"].([]interface{}); ok && len(v) > 0 { + config.SortConfiguration = expandTableSortConfiguration(v) + } + if v, ok := tfMap["table_inline_visualizations"].([]interface{}); ok && len(v) > 0 { + config.TableInlineVisualizations = expandTableInlineVisualizations(v) + } + if v, ok := tfMap["table_options"].([]interface{}); ok && len(v) > 0 { + config.TableOptions = expandTableOptions(v) + } + if v, ok := tfMap["total_options"].([]interface{}); ok && len(v) > 0 { + config.TotalOptions = expandTableTotalOptions(v) + } + + return config +} + +func expandTableFieldWells(tfList []interface{}) *quicksight.TableFieldWells { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + config := &quicksight.TableFieldWells{} + + if v, ok := tfMap["table_aggregated_field_wells"].([]interface{}); ok && len(v) > 0 { + config.TableAggregatedFieldWells = expandTableAggregatedFieldWells(v) + } + if v, ok := tfMap["table_unaggregated_field_wells"].([]interface{}); ok && len(v) > 0 { + config.TableUnaggregatedFieldWells = expandTableUnaggregatedFieldWells(v) + } + + return config +} + +func expandTableAggregatedFieldWells(tfList []interface{}) *quicksight.TableAggregatedFieldWells { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + config := &quicksight.TableAggregatedFieldWells{} + + if v, ok := tfMap["group_by"].([]interface{}); ok && len(v) > 0 { + config.GroupBy = expandDimensionFields(v) + } + if v, ok := tfMap["values"].([]interface{}); ok && len(v) > 0 { + config.Values = expandMeasureFields(v) + } + + return config +} + +func expandTableUnaggregatedFieldWells(tfList []interface{}) *quicksight.TableUnaggregatedFieldWells { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + config := &quicksight.TableUnaggregatedFieldWells{} + + if v, ok := tfMap["values"].([]interface{}); ok && len(v) > 0 { + config.Values = expandUnaggregatedFields(v) + } + + return config +} + +func expandUnaggregatedFields(tfList []interface{}) []*quicksight.UnaggregatedField { + if len(tfList) == 0 { + return nil + } + + var options []*quicksight.UnaggregatedField + for _, tfMapRaw := range tfList { + tfMap, ok := tfMapRaw.(map[string]interface{}) + if !ok { + continue + } + + opts := expandUnaggregatedField(tfMap) + if opts == nil { + continue + } + + options = append(options, opts) + } + + return options +} + +func expandUnaggregatedField(tfMap map[string]interface{}) *quicksight.UnaggregatedField { + if tfMap == nil { + return nil + } + + options := &quicksight.UnaggregatedField{} + + if v, ok := tfMap["field_id"].(string); ok && v != "" { + options.FieldId = aws.String(v) + } + if v, ok := tfMap["column"].([]interface{}); ok && len(v) > 0 { + options.Column = expandColumnIdentifier(v) + } + if v, ok := tfMap["format_configuration"].([]interface{}); ok && len(v) > 0 { + options.FormatConfiguration = expandFormatConfiguration(v) + } + + return options +} + +func expandTableSortConfiguration(tfList []interface{}) *quicksight.TableSortConfiguration { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + config := &quicksight.TableSortConfiguration{} + + if v, ok := tfMap["pagination_configuration"].([]interface{}); ok && len(v) > 0 { + config.PaginationConfiguration = expandPaginationConfiguration(v) + } + if v, ok := tfMap["row_sort"].([]interface{}); ok && len(v) > 0 { + config.RowSort = expandFieldSortOptionsList(v) + } + + return config +} + +func expandTableFieldOptions(tfList []interface{}) *quicksight.TableFieldOptions { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + options := &quicksight.TableFieldOptions{} + + if v, ok := tfMap["order"].([]interface{}); ok && len(v) > 0 { + options.Order = flex.ExpandStringList(v) + } + if v, ok := tfMap["selected_field_options"].([]interface{}); ok && len(v) > 0 { + options.SelectedFieldOptions = expandTableFieldOptionsList(v) + } + + return options +} + +func expandTableFieldOptionsList(tfList []interface{}) []*quicksight.TableFieldOption { + if len(tfList) == 0 { + return nil + } + + var options []*quicksight.TableFieldOption + for _, tfMapRaw := range tfList { + tfMap, ok := tfMapRaw.(map[string]interface{}) + if !ok { + continue + } + + opts := expandTableFieldOption(tfMap) + if opts == nil { + continue + } + + options = append(options, opts) + } + + return options +} + +func expandTableFieldOption(tfMap map[string]interface{}) *quicksight.TableFieldOption { + if tfMap == nil { + return nil + } + + options := &quicksight.TableFieldOption{} + + if v, ok := tfMap["field_id"].(string); ok && v != "" { + options.FieldId = aws.String(v) + } + if v, ok := tfMap["custom_label"].(string); ok && v != "" { + options.CustomLabel = aws.String(v) + } + if v, ok := tfMap["visibility"].(string); ok && v != "" { + options.Visibility = aws.String(v) + } + if v, ok := tfMap["width"].(string); ok && v != "" { + options.Width = aws.String(v) + } + if v, ok := tfMap["url_styling"].([]interface{}); ok && len(v) > 0 { + options.URLStyling = expandTableFieldURLConfiguration(v) + } + + return options +} + +func expandTableFieldURLConfiguration(tfList []interface{}) *quicksight.TableFieldURLConfiguration { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + options := &quicksight.TableFieldURLConfiguration{} + + if v, ok := tfMap["image_configuration"].([]interface{}); ok && len(v) > 0 { + options.ImageConfiguration = expandTableFieldImageConfiguration(v) + } + if v, ok := tfMap["link_configuration"].([]interface{}); ok && len(v) > 0 { + options.LinkConfiguration = expandTableFieldLinkConfiguration(v) + } + + return options +} + +func expandTableFieldImageConfiguration(tfList []interface{}) *quicksight.TableFieldImageConfiguration { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + options := &quicksight.TableFieldImageConfiguration{} + + if v, ok := tfMap["sizing_options"].([]interface{}); ok && len(v) > 0 { + options.SizingOptions = expandTableCellImageSizingConfiguration(v) + } + + return options +} + +func expandTableCellImageSizingConfiguration(tfList []interface{}) *quicksight.TableCellImageSizingConfiguration { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + options := &quicksight.TableCellImageSizingConfiguration{} + + if v, ok := tfMap["table_cell_image_scaling_configuration"].(string); ok && v != "" { + options.TableCellImageScalingConfiguration = aws.String(v) + } + + return options +} + +func expandTableFieldLinkConfiguration(tfList []interface{}) *quicksight.TableFieldLinkConfiguration { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + options := &quicksight.TableFieldLinkConfiguration{} + + if v, ok := tfMap["target"].(string); ok && v != "" { + options.Target = aws.String(v) + } + if v, ok := tfMap["content"].([]interface{}); ok && len(v) > 0 { + options.Content = expandTableFieldLinkContentConfiguration(v) + } + + return options +} + +func expandTableFieldLinkContentConfiguration(tfList []interface{}) *quicksight.TableFieldLinkContentConfiguration { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + options := &quicksight.TableFieldLinkContentConfiguration{} + + if v, ok := tfMap["custom_icon_content"].([]interface{}); ok && len(v) > 0 { + options.CustomIconContent = expandTableFieldCustomIconContent(v) + } + if v, ok := tfMap["custom_text_content"].([]interface{}); ok && len(v) > 0 { + options.CustomTextContent = expandTableFieldCustomTextContent(v) + } + + return options +} + +func expandTableFieldCustomIconContent(tfList []interface{}) *quicksight.TableFieldCustomIconContent { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + options := &quicksight.TableFieldCustomIconContent{} + + if v, ok := tfMap["icon"].(string); ok && v != "" { + options.Icon = aws.String(v) + } + + return options +} + +func expandTableFieldCustomTextContent(tfList []interface{}) *quicksight.TableFieldCustomTextContent { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + options := &quicksight.TableFieldCustomTextContent{} + + if v, ok := tfMap["value"].(string); ok && v != "" { + options.Value = aws.String(v) + } + if v, ok := tfMap["custom_text_content"].([]interface{}); ok && len(v) > 0 { + options.FontConfiguration = expandFontConfiguration(v) + } + + return options +} + +func expandTablePaginatedReportOptions(tfList []interface{}) *quicksight.TablePaginatedReportOptions { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + options := &quicksight.TablePaginatedReportOptions{} + + if v, ok := tfMap["overflow_column_header_visibility"].(string); ok && v != "" { + options.OverflowColumnHeaderVisibility = aws.String(v) + } + if v, ok := tfMap["vertical_overflow_visibility"].(string); ok && v != "" { + options.VerticalOverflowVisibility = aws.String(v) + } + + return options +} + +func expandTableOptions(tfList []interface{}) *quicksight.TableOptions { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + options := &quicksight.TableOptions{} + + if v, ok := tfMap["orientation"].(string); ok && v != "" { + options.Orientation = aws.String(v) + } + if v, ok := tfMap["cell_style"].([]interface{}); ok && len(v) > 0 { + options.CellStyle = expandTableCellStyle(v) + } + if v, ok := tfMap["header_style"].([]interface{}); ok && len(v) > 0 { + options.HeaderStyle = expandTableCellStyle(v) + } + if v, ok := tfMap["row_alternate_color_options"].([]interface{}); ok && len(v) > 0 { + options.RowAlternateColorOptions = expandRowAlternateColorOptions(v) + } + + return options +} + +func expandTableTotalOptions(tfList []interface{}) *quicksight.TotalOptions { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + options := &quicksight.TotalOptions{} + + if v, ok := tfMap["custom_label"].(string); ok && v != "" { + options.CustomLabel = aws.String(v) + } + if v, ok := tfMap["placement"].(string); ok && v != "" { + options.Placement = aws.String(v) + } + if v, ok := tfMap["scroll_status"].(string); ok && v != "" { + options.ScrollStatus = aws.String(v) + } + if v, ok := tfMap["totals_visibility"].(string); ok && v != "" { + options.TotalsVisibility = aws.String(v) + } + if v, ok := tfMap["total_cell_style"].([]interface{}); ok && len(v) > 0 { + options.TotalCellStyle = expandTableCellStyle(v) + } + + return options +} + +func expandTableConditionalFormatting(tfList []interface{}) *quicksight.TableConditionalFormatting { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + options := &quicksight.TableConditionalFormatting{} + + if v, ok := tfMap["conditional_formatting_options"].([]interface{}); ok && len(v) > 0 { + options.ConditionalFormattingOptions = expandTableConditionalFormattingOptions(v) + } + + return options +} + +func expandTableConditionalFormattingOptions(tfList []interface{}) []*quicksight.TableConditionalFormattingOption { + if len(tfList) == 0 { + return nil + } + + var options []*quicksight.TableConditionalFormattingOption + for _, tfMapRaw := range tfList { + tfMap, ok := tfMapRaw.(map[string]interface{}) + if !ok { + continue + } + + opts := expandTableConditionalFormattingOption(tfMap) + if opts == nil { + continue + } + + options = append(options, opts) + } + + return options +} + +func expandTableConditionalFormattingOption(tfMap map[string]interface{}) *quicksight.TableConditionalFormattingOption { + if tfMap == nil { + return nil + } + + options := &quicksight.TableConditionalFormattingOption{} + + if v, ok := tfMap["cell"].([]interface{}); ok && len(v) > 0 { + options.Cell = expandTableCellConditionalFormatting(v) + } + if v, ok := tfMap["row"].([]interface{}); ok && len(v) > 0 { + options.Row = expandTableRowConditionalFormatting(v) + } + + return options +} + +func expandTableCellConditionalFormatting(tfList []interface{}) *quicksight.TableCellConditionalFormatting { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + options := &quicksight.TableCellConditionalFormatting{} + + if v, ok := tfMap["field_id"].(string); ok && v != "" { + options.FieldId = aws.String(v) + } + if v, ok := tfMap["text_format"].([]interface{}); ok && len(v) > 0 { + options.TextFormat = expandTextConditionalFormat(v) + } + + return options +} + +func expandTableRowConditionalFormatting(tfList []interface{}) *quicksight.TableRowConditionalFormatting { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + options := &quicksight.TableRowConditionalFormatting{} + + if v, ok := tfMap["background_color"].([]interface{}); ok && len(v) > 0 { + options.BackgroundColor = expandConditionalFormattingColor(v) + } + if v, ok := tfMap["text_color"].([]interface{}); ok && len(v) > 0 { + options.TextColor = expandConditionalFormattingColor(v) + } + + return options +} + +func expandTableInlineVisualizations(tfList []interface{}) []*quicksight.TableInlineVisualization { + if len(tfList) == 0 { + return nil + } + + var options []*quicksight.TableInlineVisualization + for _, tfMapRaw := range tfList { + tfMap, ok := tfMapRaw.(map[string]interface{}) + if !ok { + continue + } + + opts := expandTableInlineVisualization(tfMap) + if opts == nil { + continue + } + + options = append(options, opts) + } + + return options +} + +func expandTableInlineVisualization(tfMap map[string]interface{}) *quicksight.TableInlineVisualization { + if tfMap == nil { + return nil + } + + options := &quicksight.TableInlineVisualization{} + + if v, ok := tfMap["data_bars"].([]interface{}); ok && len(v) > 0 { + options.DataBars = expandDataBarsOptions(v) + } + + return options +} + +func expandDataBarsOptions(tfList []interface{}) *quicksight.DataBarsOptions { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + options := &quicksight.DataBarsOptions{} + + if v, ok := tfMap["field_id"].(string); ok && v != "" { + options.FieldId = aws.String(v) + } + if v, ok := tfMap["negative_color"].(string); ok && v != "" { + options.NegativeColor = aws.String(v) + } + if v, ok := tfMap["positive_color"].(string); ok && v != "" { + options.PositiveColor = aws.String(v) + } + return options +} diff --git a/internal/service/quicksight/visual_tree_map.go b/internal/service/quicksight/visual_tree_map.go new file mode 100644 index 000000000000..761c423cc6d5 --- /dev/null +++ b/internal/service/quicksight/visual_tree_map.go @@ -0,0 +1,228 @@ +package quicksight + +import ( + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/quicksight" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" +) + +func treeMapVisualSchema() *schema.Schema { + return &schema.Schema{ // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_TreeMapVisual.html + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "visual_id": idSchema(), + "actions": visualCustomActionsSchema(customActionsMaxItems), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_VisualCustomAction.html + "chart_configuration": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_TreeMapConfiguration.html + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "color_label_options": chartAxisLabelOptionsSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_ChartAxisLabelOptions.html + "color_scale": colorScaleSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_ColorScale.html + "data_labels": dataLabelOptionsSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_DataLabelOptions.html + "field_wells": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_TreeMapFieldWells.html + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "tree_map_aggregated_field_wells": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_TreeMapAggregatedFieldWells.html + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "colors": measureFieldSchema(1), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_MeasureField.html + "groups": dimensionFieldSchema(1), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_DimensionField.html + "sizes": measureFieldSchema(1), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_MeasureField.html + }, + }, + }, + }, + }, + }, + "group_label_options": chartAxisLabelOptionsSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_ChartAxisLabelOptions.html + "legend": legendOptionsSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_LegendOptions.html + "size_label_options": chartAxisLabelOptionsSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_ChartAxisLabelOptions.html + "sort_configuration": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_TreeMapSortConfiguration.html + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "tree_map_group_items_limit_configuration": itemsLimitConfigurationSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_ItemsLimitConfiguration.html + "tree_map_sort": fieldSortOptionsSchema(100), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_FieldSortOptions.html + }, + }, + }, + "tooltip": tooltipOptionsSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_TooltipOptions.html + }, + }, + }, + "column_hierarchies": columnHierarchiesSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_ColumnHierarchy.html + "subtitle": visualSubtitleLabelOptionsSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_VisualSubtitleLabelOptions.html + "title": visualTitleLabelOptionsSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_VisualTitleLabelOptions.html + }, + }, + } +} + +func expandTreeMapVisual(tfList []interface{}) (*quicksight.TreeMapVisual, error) { + if len(tfList) == 0 || tfList[0] == nil { + return nil, nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil, nil + } + + visual := &quicksight.TreeMapVisual{} + + if v, ok := tfMap["visual_id"].(string); ok && v != "" { + visual.VisualId = aws.String(v) + } + if v, ok := tfMap["actions"].([]interface{}); ok && len(v) > 0 { + value, err := expandVisualCustomActions(v) + if err != nil { + return nil, err + } + visual.Actions = value + } + if v, ok := tfMap["chart_configuration"].([]interface{}); ok && len(v) > 0 { + visual.ChartConfiguration = expandTreeMapConfiguration(v) + } + if v, ok := tfMap["column_hierarchies"].([]interface{}); ok && len(v) > 0 { + value, err := expandColumnHierarchies(v) + if err != nil { + return nil, err + } + visual.ColumnHierarchies = value + } + if v, ok := tfMap["subtitle"].([]interface{}); ok && len(v) > 0 { + visual.Subtitle = expandVisualSubtitleLabelOptions(v) + } + if v, ok := tfMap["title"].([]interface{}); ok && len(v) > 0 { + visual.Title = expandVisualTitleLabelOptions(v) + } + + return visual, nil +} + +func expandTreeMapConfiguration(tfList []interface{}) *quicksight.TreeMapConfiguration { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + config := &quicksight.TreeMapConfiguration{} + + if v, ok := tfMap["color_label_options"].([]interface{}); ok && len(v) > 0 { + config.ColorLabelOptions = expandChartAxisLabelOptions(v) + } + if v, ok := tfMap["color_scale"].([]interface{}); ok && len(v) > 0 { + config.ColorScale = expandColorScale(v) + } + if v, ok := tfMap["data_labels"].([]interface{}); ok && len(v) > 0 { + config.DataLabels = expandDataLabelOptions(v) + } + if v, ok := tfMap["field_wells"].([]interface{}); ok && len(v) > 0 { + config.FieldWells = expandTreeMapFieldWells(v) + } + if v, ok := tfMap["group_label_options"].([]interface{}); ok && len(v) > 0 { + config.GroupLabelOptions = expandChartAxisLabelOptions(v) + } + if v, ok := tfMap["legend"].([]interface{}); ok && len(v) > 0 { + config.Legend = expandLegendOptions(v) + } + if v, ok := tfMap["size_label_options"].([]interface{}); ok && len(v) > 0 { + config.SizeLabelOptions = expandChartAxisLabelOptions(v) + } + if v, ok := tfMap["sort_configuration"].([]interface{}); ok && len(v) > 0 { + config.SortConfiguration = expandTreeMapSortConfiguration(v) + } + if v, ok := tfMap["tooltip"].([]interface{}); ok && len(v) > 0 { + config.Tooltip = expandTooltipOptions(v) + } + + return config +} + +func expandTreeMapFieldWells(tfList []interface{}) *quicksight.TreeMapFieldWells { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + config := &quicksight.TreeMapFieldWells{} + + if v, ok := tfMap["tree_map_aggregated_field_wells"].([]interface{}); ok && len(v) > 0 { + config.TreeMapAggregatedFieldWells = expandTreeMapAggregatedFieldWells(v) + } + + return config +} + +func expandTreeMapAggregatedFieldWells(tfList []interface{}) *quicksight.TreeMapAggregatedFieldWells { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + config := &quicksight.TreeMapAggregatedFieldWells{} + + if v, ok := tfMap["colors"].([]interface{}); ok && len(v) > 0 { + config.Colors = expandMeasureFields(v) + } + if v, ok := tfMap["groups"].([]interface{}); ok && len(v) > 0 { + config.Groups = expandDimensionFields(v) + } + if v, ok := tfMap["sizes"].([]interface{}); ok && len(v) > 0 { + config.Sizes = expandMeasureFields(v) + } + + return config +} + +func expandTreeMapSortConfiguration(tfList []interface{}) *quicksight.TreeMapSortConfiguration { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + config := &quicksight.TreeMapSortConfiguration{} + + if v, ok := tfMap["tree_map_group_items_limit_configuration"].([]interface{}); ok && len(v) > 0 { + config.TreeMapGroupItemsLimitConfiguration = expandItemsLimitConfiguration(v) + } + if v, ok := tfMap["tree_map_sort"].([]interface{}); ok && len(v) > 0 { + config.TreeMapSort = expandFieldSortOptionsList(v) + } + + return config +} diff --git a/internal/service/quicksight/visual_waterfall.go b/internal/service/quicksight/visual_waterfall.go new file mode 100644 index 000000000000..17d2ff920fa9 --- /dev/null +++ b/internal/service/quicksight/visual_waterfall.go @@ -0,0 +1,264 @@ +package quicksight + +import ( + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/quicksight" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" +) + +func waterfallVisualSchema() *schema.Schema { + return &schema.Schema{ // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_WaterfallVisual.html + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "visual_id": idSchema(), + "actions": visualCustomActionsSchema(customActionsMaxItems), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_VisualCustomAction.html + "chart_configuration": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_WaterfallChartConfiguration.html + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "category_axis_display_options": axisDisplayOptionsSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_AxisDisplayOptions.html + "category_axis_label_options": chartAxisLabelOptionsSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_ChartAxisLabelOptions.html + "data_labels": dataLabelOptionsSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_DataLabelOptions.html + "field_wells": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_WaterfallChartFieldWells.html + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "waterfall_chart_aggregated_field_wells": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_WaterfallChartAggregatedFieldWells.html + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "breakdowns": dimensionFieldSchema(dimensionsFieldMaxItems), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_DimensionField.html + "categories": dimensionFieldSchema(dimensionsFieldMaxItems), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_DimensionField.html + "values": measureFieldSchema(200), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_MeasureField.html + }, + }, + }, + }, + }, + }, + "legend": legendOptionsSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_LegendOptions.html + "primary_y_axis_display_options": axisDisplayOptionsSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_AxisDisplayOptions.html + "primary_y_axis_label_options": chartAxisLabelOptionsSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_ChartAxisLabelOptions.html + "sort_configuration": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_WaterfallChartSortConfiguration.html + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "breakdown_items_limit": itemsLimitConfigurationSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_ItemsLimitConfiguration.html + "category_sort": fieldSortOptionsSchema(100), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_FieldSortOptions.html + }, + }, + }, + "visual_palette": visualPaletteSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_VisualPalette.html + "waterfall_chart_options": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_WaterfallChartOptions.html + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "total_bar_label": { + Type: schema.TypeString, + Optional: true, + }, + }, + }, + }, + }, + }, + }, + "column_hierarchies": columnHierarchiesSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_ColumnHierarchy.html + "subtitle": visualSubtitleLabelOptionsSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_VisualSubtitleLabelOptions.html + "title": visualTitleLabelOptionsSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_VisualTitleLabelOptions.html + }, + }, + } +} + +func expandWaterfallVisual(tfList []interface{}) (*quicksight.WaterfallVisual, error) { + if len(tfList) == 0 || tfList[0] == nil { + return nil, nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil, nil + } + + visual := &quicksight.WaterfallVisual{} + + if v, ok := tfMap["visual_id"].(string); ok && v != "" { + visual.VisualId = aws.String(v) + } + if v, ok := tfMap["actions"].([]interface{}); ok && len(v) > 0 { + value, err := expandVisualCustomActions(v) + if err != nil { + return nil, err + } + visual.Actions = value + } + if v, ok := tfMap["chart_configuration"].([]interface{}); ok && len(v) > 0 { + visual.ChartConfiguration = expandWaterfallChartConfiguration(v) + } + if v, ok := tfMap["column_hierarchies"].([]interface{}); ok && len(v) > 0 { + value, err := expandColumnHierarchies(v) + if err != nil { + return nil, err + } + visual.ColumnHierarchies = value + } + if v, ok := tfMap["subtitle"].([]interface{}); ok && len(v) > 0 { + visual.Subtitle = expandVisualSubtitleLabelOptions(v) + } + if v, ok := tfMap["title"].([]interface{}); ok && len(v) > 0 { + visual.Title = expandVisualTitleLabelOptions(v) + } + + return visual, nil +} + +func expandWaterfallChartConfiguration(tfList []interface{}) *quicksight.WaterfallChartConfiguration { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + config := &quicksight.WaterfallChartConfiguration{} + + if v, ok := tfMap["category_axis_display_options"].([]interface{}); ok && len(v) > 0 { + config.CategoryAxisDisplayOptions = expandAxisDisplayOptions(v) + } + if v, ok := tfMap["category_axis_label_options"].([]interface{}); ok && len(v) > 0 { + config.CategoryAxisLabelOptions = expandChartAxisLabelOptions(v) + } + if v, ok := tfMap["data_labels"].([]interface{}); ok && len(v) > 0 { + config.DataLabels = expandDataLabelOptions(v) + } + if v, ok := tfMap["field_wells"].([]interface{}); ok && len(v) > 0 { + config.FieldWells = expandWaterfallChartFieldWells(v) + } + if v, ok := tfMap["legend"].([]interface{}); ok && len(v) > 0 { + config.Legend = expandLegendOptions(v) + } + if v, ok := tfMap["primary_y_axis_display_options"].([]interface{}); ok && len(v) > 0 { + config.PrimaryYAxisDisplayOptions = expandAxisDisplayOptions(v) + } + if v, ok := tfMap["primary_y_axis_label_options"].([]interface{}); ok && len(v) > 0 { + config.PrimaryYAxisLabelOptions = expandChartAxisLabelOptions(v) + } + if v, ok := tfMap["sort_configuration"].([]interface{}); ok && len(v) > 0 { + config.SortConfiguration = expandWaterfallChartSortConfiguration(v) + } + if v, ok := tfMap["visual_palette"].([]interface{}); ok && len(v) > 0 { + config.VisualPalette = expandVisualPalette(v) + } + if v, ok := tfMap["waterfall_chart_options"].([]interface{}); ok && len(v) > 0 { + config.WaterfallChartOptions = expandWaterfallChartOptions(v) + } + + return config +} + +func expandWaterfallChartFieldWells(tfList []interface{}) *quicksight.WaterfallChartFieldWells { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + config := &quicksight.WaterfallChartFieldWells{} + + if v, ok := tfMap["waterfall_chart_aggregated_field_wells"].([]interface{}); ok && len(v) > 0 { + config.WaterfallChartAggregatedFieldWells = expandWaterfallChartAggregatedFieldWells(v) + } + + return config +} + +func expandWaterfallChartAggregatedFieldWells(tfList []interface{}) *quicksight.WaterfallChartAggregatedFieldWells { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + config := &quicksight.WaterfallChartAggregatedFieldWells{} + + if v, ok := tfMap["breakdowns"].([]interface{}); ok && len(v) > 0 { + config.Breakdowns = expandDimensionFields(v) + } + if v, ok := tfMap["categories"].([]interface{}); ok && len(v) > 0 { + config.Categories = expandDimensionFields(v) + } + if v, ok := tfMap["values"].([]interface{}); ok && len(v) > 0 { + config.Values = expandMeasureFields(v) + } + + return config +} + +func expandWaterfallChartSortConfiguration(tfList []interface{}) *quicksight.WaterfallChartSortConfiguration { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + config := &quicksight.WaterfallChartSortConfiguration{} + + if v, ok := tfMap["breakdown_items_limit"].([]interface{}); ok && len(v) > 0 { + config.BreakdownItemsLimit = expandItemsLimitConfiguration(v) + } + if v, ok := tfMap["category_sort"].([]interface{}); ok && len(v) > 0 { + config.CategorySort = expandFieldSortOptionsList(v) + } + + return config +} + +func expandWaterfallChartOptions(tfList []interface{}) *quicksight.WaterfallChartOptions { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + options := &quicksight.WaterfallChartOptions{} + + if v, ok := tfMap["total_bar_label"].(string); ok && v != "" { + options.TotalBarLabel = aws.String(v) + } + + return options +} diff --git a/internal/service/quicksight/visual_word_cloud.go b/internal/service/quicksight/visual_word_cloud.go new file mode 100644 index 000000000000..cef2de713616 --- /dev/null +++ b/internal/service/quicksight/visual_word_cloud.go @@ -0,0 +1,254 @@ +package quicksight + +import ( + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/quicksight" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" +) + +func wordCloudVisualSchema() *schema.Schema { + return &schema.Schema{ // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_WordCloudVisual.html + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "visual_id": idSchema(), + "actions": visualCustomActionsSchema(customActionsMaxItems), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_VisualCustomAction.html + "chart_configuration": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_WordCloudChartConfiguration.html + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "category_label_options": chartAxisLabelOptionsSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_ChartAxisLabelOptions.html + "field_wells": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_WordCloudFieldWells.html + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "word_cloud_aggregated_field_wells": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_WordCloudAggregatedFieldWells.html + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "group_by": dimensionFieldSchema(10), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_DimensionField.html + "size": measureFieldSchema(1), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_MeasureField.html + }, + }, + }, + }, + }, + }, + "sort_configuration": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_WordCloudSortConfiguration.html + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "category_items_limit": itemsLimitConfigurationSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_ItemsLimitConfiguration.html + "category_sort": fieldSortOptionsSchema(100), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_FieldSortOptions.html + }, + }, + }, + "word_cloud_options": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_WordCloudOptions.html + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "cloud_layout": stringSchema(false, validation.StringInSlice(quicksight.WordCloudCloudLayout_Values(), false)), + "maximum_string_length": intSchema(false, validation.IntBetween(1, 100)), + "word_casing": stringSchema(false, validation.StringInSlice(quicksight.WordCloudWordCasing_Values(), false)), + "word_orientation": stringSchema(false, validation.StringInSlice(quicksight.WordCloudWordOrientation_Values(), false)), + "word_padding": stringSchema(false, validation.StringInSlice(quicksight.WordCloudWordPadding_Values(), false)), + "word_scaling": stringSchema(false, validation.StringInSlice(quicksight.WordCloudWordScaling_Values(), false)), + }, + }, + }, + }, + }, + }, + "column_hierarchies": columnHierarchiesSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_ColumnHierarchy.html + "subtitle": visualSubtitleLabelOptionsSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_VisualSubtitleLabelOptions.html + "title": visualTitleLabelOptionsSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_VisualTitleLabelOptions.html + }, + }, + } +} + +func expandWordCloudVisual(tfList []interface{}) (*quicksight.WordCloudVisual, error) { + if len(tfList) == 0 || tfList[0] == nil { + return nil, nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil, nil + } + + visual := &quicksight.WordCloudVisual{} + + if v, ok := tfMap["visual_id"].(string); ok && v != "" { + visual.VisualId = aws.String(v) + } + if v, ok := tfMap["actions"].([]interface{}); ok && len(v) > 0 { + value, err := expandVisualCustomActions(v) + if err != nil { + return nil, err + } + visual.Actions = value + } + if v, ok := tfMap["chart_configuration"].([]interface{}); ok && len(v) > 0 { + visual.ChartConfiguration = expandWordCloudChartConfiguration(v) + } + if v, ok := tfMap["column_hierarchies"].([]interface{}); ok && len(v) > 0 { + value, err := expandColumnHierarchies(v) + if err != nil { + return nil, err + } + visual.ColumnHierarchies = value + } + if v, ok := tfMap["subtitle"].([]interface{}); ok && len(v) > 0 { + visual.Subtitle = expandVisualSubtitleLabelOptions(v) + } + if v, ok := tfMap["title"].([]interface{}); ok && len(v) > 0 { + visual.Title = expandVisualTitleLabelOptions(v) + } + + return visual, nil +} + +func expandWordCloudChartConfiguration(tfList []interface{}) *quicksight.WordCloudChartConfiguration { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + config := &quicksight.WordCloudChartConfiguration{} + + if v, ok := tfMap["category_label_options"].([]interface{}); ok && len(v) > 0 { + config.CategoryLabelOptions = expandChartAxisLabelOptions(v) + } + if v, ok := tfMap["field_wells"].([]interface{}); ok && len(v) > 0 { + config.FieldWells = expandWordCloudFieldWells(v) + } + if v, ok := tfMap["sort_configuration"].([]interface{}); ok && len(v) > 0 { + config.SortConfiguration = expandWordCloudSortConfiguration(v) + } + if v, ok := tfMap["word_cloud_options"].([]interface{}); ok && len(v) > 0 { + config.WordCloudOptions = expandWordCloudOptions(v) + } + + return config +} + +func expandWordCloudFieldWells(tfList []interface{}) *quicksight.WordCloudFieldWells { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + config := &quicksight.WordCloudFieldWells{} + + if v, ok := tfMap["word_cloud_aggregated_field_wells"].([]interface{}); ok && len(v) > 0 { + config.WordCloudAggregatedFieldWells = expandWordCloudAggregatedFieldWells(v) + } + + return config +} + +func expandWordCloudAggregatedFieldWells(tfList []interface{}) *quicksight.WordCloudAggregatedFieldWells { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + config := &quicksight.WordCloudAggregatedFieldWells{} + + if v, ok := tfMap["group_by"].([]interface{}); ok && len(v) > 0 { + config.GroupBy = expandDimensionFields(v) + } + if v, ok := tfMap["size"].([]interface{}); ok && len(v) > 0 { + config.Size = expandMeasureFields(v) + } + + return config +} + +func expandWordCloudSortConfiguration(tfList []interface{}) *quicksight.WordCloudSortConfiguration { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + config := &quicksight.WordCloudSortConfiguration{} + + if v, ok := tfMap["category_items_limit"].([]interface{}); ok && len(v) > 0 { + config.CategoryItemsLimit = expandItemsLimitConfiguration(v) + } + if v, ok := tfMap["category_sort"].([]interface{}); ok && len(v) > 0 { + config.CategorySort = expandFieldSortOptionsList(v) + } + + return config +} + +func expandWordCloudOptions(tfList []interface{}) *quicksight.WordCloudOptions { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + options := &quicksight.WordCloudOptions{} + + if v, ok := tfMap["cloud_layout"].(string); ok && v != "" { + options.CloudLayout = aws.String(v) + } + if v, ok := tfMap["maximum_string_length"].(int64); ok { + options.MaximumStringLength = aws.Int64(v) + } + if v, ok := tfMap["word_casing"].(string); ok && v != "" { + options.WordCasing = aws.String(v) + } + if v, ok := tfMap["word_orientation"].(string); ok && v != "" { + options.WordOrientation = aws.String(v) + } + if v, ok := tfMap["word_padding"].(string); ok && v != "" { + options.WordPadding = aws.String(v) + } + if v, ok := tfMap["word_padding"].(string); ok && v != "" { + options.WordScaling = aws.String(v) + } + + return options +} diff --git a/internal/service/quicksight/wait.go b/internal/service/quicksight/wait.go index 11fd360cb2b9..dd60fa4d0058 100644 --- a/internal/service/quicksight/wait.go +++ b/internal/service/quicksight/wait.go @@ -6,7 +6,9 @@ import ( "time" "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/aws/awserr" "github.com/aws/aws-sdk-go/service/quicksight" + "github.com/hashicorp/go-multierror" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/retry" "github.com/hashicorp/terraform-provider-aws/internal/tfresource" ) @@ -59,3 +61,63 @@ func waitUpdated(ctx context.Context, conn *quicksight.QuickSight, accountId, da return nil, err } + +func waitTemplateCreated(ctx context.Context, conn *quicksight.QuickSight, id string, timeout time.Duration) (*quicksight.Template, error) { + stateConf := &retry.StateChangeConf{ + Pending: []string{quicksight.ResourceStatusCreationInProgress}, + Target: []string{quicksight.ResourceStatusCreationSuccessful}, + Refresh: statusTemplate(ctx, conn, id), + Timeout: timeout, + NotFoundChecks: 20, + ContinuousTargetOccurence: 2, + } + + outputRaw, err := stateConf.WaitForStateContext(ctx) + if out, ok := outputRaw.(*quicksight.Template); ok { + if status, apiErrors := aws.StringValue(out.Version.Status), out.Version.Errors; status == quicksight.ResourceStatusCreationFailed && apiErrors != nil { + var errors *multierror.Error + + for _, apiError := range apiErrors { + if apiError == nil { + continue + } + errors = multierror.Append(errors, awserr.New(aws.StringValue(apiError.Type), aws.StringValue(apiError.Message), nil)) + } + tfresource.SetLastError(err, errors) + } + + return out, err + } + + return nil, err +} + +func waitTemplateUpdated(ctx context.Context, conn *quicksight.QuickSight, id string, timeout time.Duration) (*quicksight.Template, error) { + stateConf := &retry.StateChangeConf{ + Pending: []string{quicksight.ResourceStatusUpdateInProgress}, + Target: []string{quicksight.ResourceStatusUpdateSuccessful, quicksight.ResourceStatusCreationSuccessful}, + Refresh: statusTemplate(ctx, conn, id), + Timeout: timeout, + NotFoundChecks: 20, + ContinuousTargetOccurence: 2, + } + + outputRaw, err := stateConf.WaitForStateContext(ctx) + if out, ok := outputRaw.(*quicksight.Template); ok { + if status, apiErrors := aws.StringValue(out.Version.Status), out.Version.Errors; status == quicksight.ResourceStatusCreationFailed && apiErrors != nil { + var errors *multierror.Error + + for _, apiError := range apiErrors { + if apiError == nil { + continue + } + errors = multierror.Append(errors, awserr.New(aws.StringValue(apiError.Type), aws.StringValue(apiError.Message), nil)) + } + tfresource.SetLastError(err, errors) + } + + return out, err + } + + return nil, err +} diff --git a/website/docs/r/quicksight_template.html.markdown b/website/docs/r/quicksight_template.html.markdown new file mode 100644 index 000000000000..cad8343eeea4 --- /dev/null +++ b/website/docs/r/quicksight_template.html.markdown @@ -0,0 +1,53 @@ +--- +subcategory: "QuickSight" +layout: "aws" +page_title: "AWS: aws_quicksight_template" +description: |- + Terraform resource for managing an AWS QuickSight Template. +--- + +# Resource: aws_quicksight_template + +Terraform resource for managing an AWS QuickSight Template. + +## Example Usage + +### Basic Usage + +```terraform +resource "aws_quicksight_template" "example" { +} +``` + +## Argument Reference + +The following arguments are required: + +* `example_arg` - (Required) Concise argument description. Do not begin the description with "An", "The", "Defines", "Indicates", or "Specifies," as these are verbose. In other words, "Indicates the amount of storage," can be rewritten as "Amount of storage," without losing any information. + +The following arguments are optional: + +* `optional_arg` - (Optional) Concise argument description. Do not begin the description with "An", "The", "Defines", "Indicates", or "Specifies," as these are verbose. In other words, "Indicates the amount of storage," can be rewritten as "Amount of storage," without losing any information. + +## Attributes Reference + +In addition to all arguments above, the following attributes are exported: + +* `arn` - ARN of the Template. Do not begin the description with "An", "The", "Defines", "Indicates", or "Specifies," as these are verbose. In other words, "Indicates the amount of storage," can be rewritten as "Amount of storage," without losing any information. +* `example_attribute` - Concise description. Do not begin the description with "An", "The", "Defines", "Indicates", or "Specifies," as these are verbose. In other words, "Indicates the amount of storage," can be rewritten as "Amount of storage," without losing any information. + +## Timeouts + +[Configuration options](https://developer.hashicorp.com/terraform/language/resources/syntax#operation-timeouts): + +* `create` - (Default `60m`) +* `update` - (Default `180m`) +* `delete` - (Default `90m`) + +## Import + +QuickSight Template can be imported using the `example_id_arg`, e.g., + +``` +$ terraform import aws_quicksight_template.example rft-8012925589 +```