diff --git a/.changelog/31448.txt b/.changelog/31448.txt new file mode 100644 index 00000000000..ecf330a5089 --- /dev/null +++ b/.changelog/31448.txt @@ -0,0 +1,3 @@ +```release-note:new-resource +aws_quicksight_dashboard +``` \ No newline at end of file diff --git a/internal/service/quicksight/dashboard.go b/internal/service/quicksight/dashboard.go new file mode 100644 index 00000000000..112b59b9d03 --- /dev/null +++ b/internal/service/quicksight/dashboard.go @@ -0,0 +1,424 @@ +package quicksight + +import ( + "context" + "fmt" + "log" + "strconv" + "strings" + "time" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/quicksight" + "github.com/hashicorp/aws-sdk-go-base/v2/awsv1shim/v2/tfawserr" + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/customdiff" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/retry" + "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/conns" + "github.com/hashicorp/terraform-provider-aws/internal/create" + quicksightschema "github.com/hashicorp/terraform-provider-aws/internal/service/quicksight/schema" + tftags "github.com/hashicorp/terraform-provider-aws/internal/tags" + "github.com/hashicorp/terraform-provider-aws/internal/tfresource" + "github.com/hashicorp/terraform-provider-aws/internal/verify" + "github.com/hashicorp/terraform-provider-aws/names" +) + +// @SDKResource("aws_quicksight_dashboard", name="Dashboard") +// @Tags(identifierAttribute="arn") +func ResourceDashboard() *schema.Resource { + return &schema.Resource{ + CreateWithoutTimeout: resourceDashboardCreate, + ReadWithoutTimeout: resourceDashboardRead, + UpdateWithoutTimeout: resourceDashboardUpdate, + DeleteWithoutTimeout: resourceDashboardDelete, + + Importer: &schema.ResourceImporter{ + StateContext: schema.ImportStatePassthroughContext, + }, + + Timeouts: &schema.ResourceTimeout{ + Create: schema.DefaultTimeout(5 * time.Minute), + Update: schema.DefaultTimeout(5 * time.Minute), + Delete: schema.DefaultTimeout(5 * time.Minute), + }, + + Schema: map[string]*schema.Schema{ + "arn": { + Type: schema.TypeString, + Computed: true, + }, + "aws_account_id": { + Type: schema.TypeString, + Optional: true, + Computed: true, + ForceNew: true, + ValidateFunc: verify.ValidAccountID, + }, + "created_time": { + Type: schema.TypeString, + Computed: true, + }, + "dashboard_id": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + "dashboard_publish_options": quicksightschema.DashboardPublishOptionsSchema(), + "definition": quicksightschema.DashboardDefinitionSchema(), + "last_updated_time": { + Type: schema.TypeString, + Computed: true, + }, + "last_published_time": { + Type: schema.TypeString, + Computed: true, + }, + "name": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.StringLenBetween(1, 2048), + }, + "parameters": quicksightschema.ParametersSchema(), + "permissions": { + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 64, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "actions": { + Type: schema.TypeSet, + Required: true, + MinItems: 1, + MaxItems: 16, + Elem: &schema.Schema{Type: schema.TypeString}, + }, + "principal": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.StringLenBetween(1, 256), + }, + }, + }, + }, + "source_entity": quicksightschema.DashboardSourceEntitySchema(), + "source_entity_arn": { + Type: schema.TypeString, + Computed: true, + }, + "status": { + Type: schema.TypeString, + Computed: true, + }, + names.AttrTags: tftags.TagsSchema(), + names.AttrTagsAll: tftags.TagsSchemaComputed(), + "theme_arn": { + Type: schema.TypeString, + Optional: true, + }, + "version_description": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.StringLenBetween(1, 512), + }, + "version_number": { + Type: schema.TypeInt, + Computed: true, + }, + }, + CustomizeDiff: customdiff.All( + refreshOutputsDiff, + verify.SetTagsDiff, + ), + } +} + +const ( + ResNameDashboard = "Dashboard" +) + +func resourceDashboardCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + conn := meta.(*conns.AWSClient).QuickSightConn() + + awsAccountId := meta.(*conns.AWSClient).AccountID + if v, ok := d.GetOk("aws_account_id"); ok { + awsAccountId = v.(string) + } + dashboardId := d.Get("dashboard_id").(string) + + d.SetId(createDashboardId(awsAccountId, dashboardId)) + + input := &quicksight.CreateDashboardInput{ + AwsAccountId: aws.String(awsAccountId), + DashboardId: aws.String(dashboardId), + Name: aws.String(d.Get("name").(string)), + Tags: GetTagsIn(ctx), + } + + if v, ok := d.GetOk("version_description"); ok { + input.VersionDescription = aws.String(v.(string)) + } + + if v, ok := d.GetOk("source_entity"); ok && len(v.([]interface{})) > 0 && v.([]interface{})[0] != nil { + input.SourceEntity = quicksightschema.ExpandDashboardSourceEntity(v.([]interface{})) + } + + if v, ok := d.GetOk("definition"); ok && len(v.([]interface{})) > 0 && v.([]interface{})[0] != nil { + input.Definition = quicksightschema.ExpandDashboardDefinition(d.Get("definition").([]interface{})) + } + + if v, ok := d.GetOk("dashboard_publish_options"); ok && len(v.([]interface{})) > 0 && v.([]interface{})[0] != nil { + input.DashboardPublishOptions = quicksightschema.ExpandDashboardPublishOptions(d.Get("dashboard_publish_options").([]interface{})) + } + + if v, ok := d.GetOk("parameters"); ok && len(v.([]interface{})) > 0 && v.([]interface{})[0] != nil { + input.Parameters = quicksightschema.ExpandParameters(d.Get("parameters").([]interface{})) + } + + if v, ok := d.GetOk("permissions"); ok && len(v.([]interface{})) > 0 && v.([]interface{})[0] != nil { + input.Permissions = expandResourcePermissions(v.([]interface{})) + } + + _, err := conn.CreateDashboardWithContext(ctx, input) + if err != nil { + return create.DiagError(names.QuickSight, create.ErrActionCreating, ResNameDashboard, d.Get("name").(string), err) + } + + if _, err := waitDashboardCreated(ctx, conn, d.Id(), d.Timeout(schema.TimeoutCreate)); err != nil { + return create.DiagError(names.QuickSight, create.ErrActionWaitingForCreation, ResNameDashboard, d.Id(), err) + } + + return resourceDashboardRead(ctx, d, meta) +} + +func resourceDashboardRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + conn := meta.(*conns.AWSClient).QuickSightConn() + + awsAccountId, dashboardId, err := ParseDashboardId(d.Id()) + if err != nil { + return diag.FromErr(err) + } + + out, err := FindDashboardByID(ctx, conn, d.Id()) + + if !d.IsNewResource() && tfresource.NotFound(err) { + log.Printf("[WARN] QuickSight Dashboard (%s) not found, removing from state", d.Id()) + d.SetId("") + return nil + } + + if err != nil { + return create.DiagError(names.QuickSight, create.ErrActionReading, ResNameDashboard, 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("source_entity_arn", out.Version.SourceEntityArn) + d.Set("dashboard_id", out.DashboardId) + d.Set("version_description", out.Version.Description) + d.Set("version_number", out.Version.VersionNumber) + + descResp, err := conn.DescribeDashboardDefinitionWithContext(ctx, &quicksight.DescribeDashboardDefinitionInput{ + AwsAccountId: aws.String(awsAccountId), + DashboardId: aws.String(dashboardId), + VersionNumber: out.Version.VersionNumber, + }) + + if err != nil { + return diag.Errorf("describing QuickSight Dashboard (%s) Definition: %s", d.Id(), err) + } + + if err := d.Set("definition", quicksightschema.FlattenDashboardDefinition(descResp.Definition)); err != nil { + return diag.Errorf("setting definition: %s", err) + } + + if err := d.Set("dashboard_publish_options", quicksightschema.FlattenDashboardPublishOptions(descResp.DashboardPublishOptions)); err != nil { + return diag.Errorf("setting dashboard_publish_options: %s", err) + } + + permsResp, err := conn.DescribeDashboardPermissionsWithContext(ctx, &quicksight.DescribeDashboardPermissionsInput{ + AwsAccountId: aws.String(awsAccountId), + DashboardId: aws.String(dashboardId), + }) + + if err != nil { + return diag.Errorf("describing QuickSight Dashboard (%s) Permissions: %s", d.Id(), err) + } + + if err := d.Set("permissions", flattenPermissions(permsResp.Permissions)); err != nil { + return diag.Errorf("setting permissions: %s", err) + } + + return nil +} + +func resourceDashboardUpdate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + conn := meta.(*conns.AWSClient).QuickSightConn() + + awsAccountId, dashboardId, err := ParseDashboardId(d.Id()) + if err != nil { + return diag.FromErr(err) + } + + if d.HasChangesExcept("permissions", "tags", "tags_all") { + in := &quicksight.UpdateDashboardInput{ + AwsAccountId: aws.String(awsAccountId), + DashboardId: aws.String(dashboardId), + Name: aws.String(d.Get("name").(string)), + VersionDescription: aws.String(d.Get("version_description").(string)), + } + + _, createdFromEntity := d.GetOk("source_entity") + if createdFromEntity { + in.SourceEntity = quicksightschema.ExpandDashboardSourceEntity(d.Get("source_entity").([]interface{})) + } else { + in.Definition = quicksightschema.ExpandDashboardDefinition(d.Get("definition").([]interface{})) + } + + if v, ok := d.GetOk("parameters"); ok && len(v.([]interface{})) > 0 && v.([]interface{})[0] != nil { + in.Parameters = quicksightschema.ExpandParameters(d.Get("parameters").([]interface{})) + } + + if v, ok := d.GetOk("dashboard_publish_options"); ok && len(v.([]interface{})) > 0 && v.([]interface{})[0] != nil { + in.DashboardPublishOptions = quicksightschema.ExpandDashboardPublishOptions(d.Get("dashboard_publish_options").([]interface{})) + } + + log.Printf("[DEBUG] Updating QuickSight Dashboard (%s): %#v", d.Id(), in) + out, err := conn.UpdateDashboardWithContext(ctx, in) + if err != nil { + return create.DiagError(names.QuickSight, create.ErrActionUpdating, ResNameDashboard, d.Id(), err) + } + + if _, err := waitDashboardUpdated(ctx, conn, d.Id(), d.Timeout(schema.TimeoutUpdate)); err != nil { + return create.DiagError(names.QuickSight, create.ErrActionWaitingForUpdate, ResNameDashboard, d.Id(), err) + } + + publishVersion := &quicksight.UpdateDashboardPublishedVersionInput{ + AwsAccountId: aws.String(awsAccountId), + DashboardId: aws.String(dashboardId), + VersionNumber: extractVersionFromARN(aws.StringValue(out.VersionArn)), + } + _, err = conn.UpdateDashboardPublishedVersionWithContext(ctx, publishVersion) + if err != nil { + return create.DiagError(names.QuickSight, create.ErrActionUpdating, ResNameDashboard, 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.UpdateDashboardPermissionsInput{ + AwsAccountId: aws.String(awsAccountId), + DashboardId: aws.String(dashboardId), + } + + if len(toGrant) > 0 { + params.GrantPermissions = toGrant + } + + if len(toRevoke) > 0 { + params.RevokePermissions = toRevoke + } + + _, err = conn.UpdateDashboardPermissionsWithContext(ctx, params) + + if err != nil { + return diag.Errorf("updating QuickSight Dashboard (%s) permissions: %s", dashboardId, err) + } + } + + return resourceDashboardRead(ctx, d, meta) +} + +func resourceDashboardDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + conn := meta.(*conns.AWSClient).QuickSightConn() + + awsAccountId, dashboardId, err := ParseDashboardId(d.Id()) + if err != nil { + return diag.FromErr(err) + } + + log.Printf("[INFO] Deleting QuickSight Dashboard %s", d.Id()) + _, err = conn.DeleteDashboardWithContext(ctx, &quicksight.DeleteDashboardInput{ + AwsAccountId: aws.String(awsAccountId), + DashboardId: aws.String(dashboardId), + }) + + if tfawserr.ErrCodeEquals(err, quicksight.ErrCodeResourceNotFoundException) { + return nil + } + + if err != nil { + return create.DiagError(names.QuickSight, create.ErrActionDeleting, ResNameDashboard, d.Id(), err) + } + + return nil +} + +func FindDashboardByID(ctx context.Context, conn *quicksight.QuickSight, id string) (*quicksight.Dashboard, error) { + awsAccountId, dashboardId, err := ParseDashboardId(id) + if err != nil { + return nil, err + } + + descOpts := &quicksight.DescribeDashboardInput{ + AwsAccountId: aws.String(awsAccountId), + DashboardId: aws.String(dashboardId), + } + + out, err := conn.DescribeDashboardWithContext(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.Dashboard == nil { + return nil, tfresource.NewEmptyResultError(descOpts) + } + + return out.Dashboard, nil +} + +func ParseDashboardId(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,DASHBOARD_ID", id) + } + return parts[0], parts[1], nil +} + +func createDashboardId(awsAccountID, dashboardId string) string { + return fmt.Sprintf("%s,%s", awsAccountID, dashboardId) +} + +func extractVersionFromARN(arn string) *int64 { + version, _ := strconv.Atoi(arn[strings.LastIndex(arn, "/")+1:]) + return aws.Int64(int64(version)) +} + +func refreshOutputsDiff(_ context.Context, diff *schema.ResourceDiff, meta interface{}) error { + if diff.HasChanges("name", "definition", "source_entity", "theme_arn", "version_description", "parameters", "dashboard_publish_options") { + if err := diff.SetNewComputed("version_number"); err != nil { + return err + } + } + + return nil +} diff --git a/internal/service/quicksight/dashboard_test.go b/internal/service/quicksight/dashboard_test.go new file mode 100644 index 00000000000..d6d5d110a5c --- /dev/null +++ b/internal/service/quicksight/dashboard_test.go @@ -0,0 +1,559 @@ +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-testing/helper/resource" + "github.com/hashicorp/terraform-plugin-testing/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 TestAccQuickSightDashboard_basic(t *testing.T) { + ctx := acctest.Context(t) + + var dashboard quicksight.Dashboard + resourceName := "aws_quicksight_dashboard.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: testAccCheckDashboardDestroy(ctx), + Steps: []resource.TestStep{ + { + Config: testAccDashboardConfig_basic(rId, rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckDashboardExists(ctx, resourceName, &dashboard), + resource.TestCheckResourceAttr(resourceName, "dashboard_id", rId), + resource.TestCheckResourceAttr(resourceName, "name", rName), + resource.TestCheckResourceAttr(resourceName, "status", quicksight.ResourceStatusCreationSuccessful), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccQuickSightDashboard_disappears(t *testing.T) { + ctx := acctest.Context(t) + + var dashboard quicksight.Dashboard + resourceName := "aws_quicksight_dashboard.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: testAccCheckDashboardDestroy(ctx), + Steps: []resource.TestStep{ + { + Config: testAccDashboardConfig_basic(rId, rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckDashboardExists(ctx, resourceName, &dashboard), + acctest.CheckResourceDisappears(ctx, acctest.Provider, tfquicksight.ResourceDashboard(), resourceName), + ), + ExpectNonEmptyPlan: true, + }, + }, + }) +} + +func TestAccQuickSightDashboard_sourceEntity(t *testing.T) { + ctx := acctest.Context(t) + + var dashboard quicksight.Dashboard + resourceName := "aws_quicksight_dashboard.test" + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + rId := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + sourceName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + sourceId := 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: testAccCheckDashboardDestroy(ctx), + Steps: []resource.TestStep{ + { + Config: testAccDashboardConfig_TemplateSourceEntity(rId, rName, sourceId, sourceName), + Check: resource.ComposeTestCheckFunc( + testAccCheckDashboardExists(ctx, resourceName, &dashboard), + resource.TestCheckResourceAttr(resourceName, "dashboard_id", rId), + resource.TestCheckResourceAttr(resourceName, "name", rName), + resource.TestCheckResourceAttr(resourceName, "status", quicksight.ResourceStatusCreationSuccessful), + acctest.CheckResourceAttrRegionalARN(resourceName, "source_entity.0.source_template.0.arn", "quicksight", fmt.Sprintf("template/%s", sourceId)), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"source_entity"}, + }, + }, + }) +} + +func TestAccQuickSightDashboard_updateVersionNumber(t *testing.T) { + ctx := acctest.Context(t) + + var dashboard quicksight.Dashboard + resourceName := "aws_quicksight_dashboard.test" + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + rNameUpdated := 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: testAccCheckDashboardDestroy(ctx), + Steps: []resource.TestStep{ + { + Config: testAccDashboardConfig_basic(rId, rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckDashboardExists(ctx, resourceName, &dashboard), + resource.TestCheckResourceAttr(resourceName, "dashboard_id", rId), + resource.TestCheckResourceAttr(resourceName, "name", rName), + resource.TestCheckResourceAttr(resourceName, "status", quicksight.ResourceStatusCreationSuccessful), + resource.TestCheckResourceAttr(resourceName, "version_number", "1"), + ), + }, + { + Config: testAccDashboardConfig_basic(rId, rNameUpdated), + Check: resource.ComposeTestCheckFunc( + testAccCheckDashboardExists(ctx, resourceName, &dashboard), + resource.TestCheckResourceAttr(resourceName, "dashboard_id", rId), + resource.TestCheckResourceAttr(resourceName, "name", rNameUpdated), + resource.TestCheckResourceAttr(resourceName, "status", quicksight.ResourceStatusCreationSuccessful), + resource.TestCheckResourceAttr(resourceName, "version_number", "2"), + ), + }, + }, + }) +} + +func TestAccQuickSightDashboard_dashboardSpecificConfig(t *testing.T) { + ctx := acctest.Context(t) + + var dashboard quicksight.Dashboard + resourceName := "aws_quicksight_dashboard.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: testAccCheckDashboardDestroy(ctx), + Steps: []resource.TestStep{ + { + Config: testAccDashboardConfig_DashboardSpecificConfig(rId, rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckDashboardExists(ctx, resourceName, &dashboard), + resource.TestCheckResourceAttr(resourceName, "dashboard_id", rId), + resource.TestCheckResourceAttr(resourceName, "name", rName), + resource.TestCheckResourceAttr(resourceName, "dashboard_publish_options.0.ad_hoc_filtering_option.0.availability_status", quicksight.StatusDisabled), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"parameters"}, + }, + }, + }) +} + +func testAccCheckDashboardDestroy(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_dashboard" { + continue + } + + output, err := tfquicksight.FindDashboardByID(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 Dashboard (%s) still exists", rs.Primary.ID) + } + } + + return nil + } +} + +func testAccCheckDashboardExists(ctx context.Context, name string, dashboard *quicksight.Dashboard) resource.TestCheckFunc { + return func(s *terraform.State) error { + rs, ok := s.RootModule().Resources[name] + if !ok { + return create.Error(names.QuickSight, create.ErrActionCheckingExistence, tfquicksight.ResNameDashboard, name, errors.New("not found")) + } + + if rs.Primary.ID == "" { + return create.Error(names.QuickSight, create.ErrActionCheckingExistence, tfquicksight.ResNameDashboard, name, errors.New("not set")) + } + + conn := acctest.Provider.Meta().(*conns.AWSClient).QuickSightConn() + output, err := tfquicksight.FindDashboardByID(ctx, conn, rs.Primary.ID) + + if err != nil { + return create.Error(names.QuickSight, create.ErrActionCheckingExistence, tfquicksight.ResNameDashboard, rs.Primary.ID, err) + } + + *dashboard = *output + + return nil + } +} + +func testAccDashboardConfigBase(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 {} + } + } + 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 testAccDashboardConfig_basic(rId, rName string) string { + return acctest.ConfigCompose( + testAccDashboardConfigBase(rId, rName), + fmt.Sprintf(` +resource "aws_quicksight_dashboard" "test" { + dashboard_id = %[1]q + name = %[2]q + version_description = "test" + definition { + data_set_identifiers_declarations { + data_set_arn = aws_quicksight_data_set.test.arn + identifier = "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)) +} + +func testAccDashboardConfig_TemplateSourceEntity(rId, rName, sourceId, sourceName string) string { + return acctest.ConfigCompose( + testAccDashboardConfigBase(rId, rName), + fmt.Sprintf(` +resource "aws_quicksight_template" "test" { + template_id = %[3]q + name = %[4]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" + } + } + } + } + } + } + } + } + } +} + +resource "aws_quicksight_dashboard" "test" { + dashboard_id = %[1]q + name = %[2]q + version_description = "test" + source_entity { + source_template { + arn = aws_quicksight_template.test.arn + data_set_references { + data_set_arn = aws_quicksight_data_set.test.arn + data_set_placeholder = "1" + } + } + } +} +`, rId, rName, sourceId, sourceName)) +} + +func testAccDashboardConfig_DashboardSpecificConfig(rId, rName string) string { + return acctest.ConfigCompose( + testAccDashboardConfigBase(rId, rName), + fmt.Sprintf(` +resource "aws_quicksight_dashboard" "test" { + dashboard_id = %[1]q + name = %[2]q + version_description = "test" + parameters { + string_parameters { + name = "test" + values = ["value"] + } + } + dashboard_publish_options { + ad_hoc_filtering_option { + availability_status = "DISABLED" + } + data_point_drill_up_down_option { + availability_status = "ENABLED" + } + data_point_menu_label_option { + availability_status = "ENABLED" + } + data_point_tooltip_option { + availability_status = "ENABLED" + } + export_to_csv_option { + availability_status = "ENABLED" + } + export_with_hidden_fields_option { + availability_status = "DISABLED" + } + sheet_controls_option { + visibility_state = "COLLAPSED" + } + sheet_layout_element_maximization_option { + availability_status = "ENABLED" + } + visual_axis_sort_option { + availability_status = "ENABLED" + } + visual_menu_option { + availability_status = "ENABLED" + } + } + definition { + data_set_identifiers_declarations { + data_set_arn = aws_quicksight_data_set.test.arn + identifier = "1" + } + parameter_declarations { + string_parameter_declaration { + name = "test" + parameter_value_type = "SINGLE_VALUED" + default_values { + static_values = ["value"] + } + values_when_unset { + value_when_unset_option = "NULL" + } + } + } + sheets { + title = "Example" + sheet_id = "Example1" + visuals { + line_chart_visual { + visual_id = "LineChart" + title { + format_text { + plain_text = "Line Chart Example" + } + } + 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/schema/dashboard.go b/internal/service/quicksight/schema/dashboard.go new file mode 100644 index 00000000000..97a6e0a5270 --- /dev/null +++ b/internal/service/quicksight/schema/dashboard.go @@ -0,0 +1,755 @@ +package schema + +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/verify" +) + +func DashboardDefinitionSchema() *schema.Schema { + return &schema.Schema{ // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_DashboardVersionDefinition.html + Type: schema.TypeList, + MaxItems: 1, + Optional: true, + Computed: true, + ExactlyOneOf: []string{ + "definition", + "source_entity", + }, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "data_set_identifiers_declarations": dataSetIdentifierDeclarationsSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_DataSetIdentifierDeclaration.html + "analysis_defaults": analysisDefaultSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_AnalysisDefaults.html + "calculated_fields": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_CalculatedField.html + Type: schema.TypeList, + MinItems: 1, + MaxItems: 500, + Optional: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "data_set_identifier": stringSchema(true, validation.StringLenBetween(1, 2048)), + "expression": stringSchema(true, validation.StringLenBetween(1, 4096)), + "name": stringSchema(true, validation.StringLenBetween(1, 128)), + }, + }, + }, + "column_configurations": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_ColumnConfiguration.html + Type: schema.TypeList, + MinItems: 1, + MaxItems: 200, + Optional: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "column": columnSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_ColumnIdentifier.html + "format_configuration": formatConfigurationSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_FormatConfiguration.html + "role": stringSchema(false, validation.StringInSlice(quicksight.ColumnRole_Values(), false)), + }, + }, + }, + "filter_groups": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_FilterGroup.html + Type: schema.TypeList, + MinItems: 1, + MaxItems: 2000, + Optional: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "cross_dataset": stringSchema(true, validation.StringInSlice(quicksight.CrossDatasetTypes_Values(), false)), + "filter_group_id": idSchema(), + "filters": filtersSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_Filter.html + "scope_configuration": filterScopeConfigurationSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_FilterScopeConfiguration.html + "status": stringSchema(false, validation.StringInSlice(quicksight.Status_Values(), false)), + }, + }, + }, + "parameter_declarations": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_ParameterDeclaration.html + Type: schema.TypeList, + MinItems: 1, + MaxItems: 200, + Optional: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "date_time_parameter_declaration": dateTimeParameterDeclarationSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_DateTimeParameterDeclaration.html + "decimal_parameter_declaration": decimalParameterDeclarationSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_DecimalParameterDeclaration.html + "integer_parameter_declaration": integerParameterDeclarationSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_IntegerParameterDeclaration.html + "string_parameter_declaration": stringParameterDeclarationSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_StringParameterDeclaration.html + }, + }, + }, + "sheets": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_SheetDefinition.html + Type: schema.TypeList, + MinItems: 1, + MaxItems: 20, + Optional: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "sheet_id": idSchema(), + "content_type": { + Type: schema.TypeString, + Optional: true, + Computed: true, + ValidateFunc: validation.StringInSlice(quicksight.SheetContentType_Values(), false), + }, + "description": stringSchema(false, validation.StringLenBetween(1, 1024)), + "filter_controls": filterControlsSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_FilterControl.html + "layouts": layoutSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_Layout.html + "name": stringSchema(false, validation.StringLenBetween(1, 2048)), + "parameter_controls": parameterControlsSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_ParameterControl.html + "sheet_control_layouts": sheetControlLayoutsSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_SheetControlLayout.html + "text_boxes": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_SheetTextBox.html + Type: schema.TypeList, + MinItems: 1, + MaxItems: 100, + Optional: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "sheet_text_box_id": idSchema(), + "content": stringSchema(false, validation.StringLenBetween(1, 150000)), + }, + }, + }, + "title": stringSchema(false, validation.StringLenBetween(1, 1024)), + "visuals": visualsSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_Visual.html + }, + }, + }, + }, + }, + } +} + +func DashboardPublishOptionsSchema() *schema.Schema { + return &schema.Schema{ // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_DashboardPublishOptions.html + Type: schema.TypeList, + MaxItems: 1, + Optional: true, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "ad_hoc_filtering_option": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_AdHocFilteringOption.html + Type: schema.TypeList, + MaxItems: 1, + Optional: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "availability_status": { + Type: schema.TypeString, + Optional: true, + Default: quicksight.StatusEnabled, + ValidateFunc: validation.StringInSlice(quicksight.Status_Values(), false), + }, + }, + }, + }, + "data_point_drill_up_down_option": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_DataPointDrillUpDownOption.html + Type: schema.TypeList, + MaxItems: 1, + Optional: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "availability_status": { + Type: schema.TypeString, + Optional: true, + Default: quicksight.StatusEnabled, + ValidateFunc: validation.StringInSlice(quicksight.Status_Values(), false), + }}, + }, + }, + "data_point_menu_label_option": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_DataPointMenuLabelOption.html + Type: schema.TypeList, + MaxItems: 1, + Optional: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "availability_status": { + Type: schema.TypeString, + Optional: true, + Default: quicksight.StatusEnabled, + ValidateFunc: validation.StringInSlice(quicksight.Status_Values(), false), + }}, + }, + }, + "data_point_tooltip_option": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_DataPointTooltipOption.html + Type: schema.TypeList, + MaxItems: 1, + Optional: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "availability_status": { + Type: schema.TypeString, + Optional: true, + Default: quicksight.StatusEnabled, + ValidateFunc: validation.StringInSlice(quicksight.Status_Values(), false), + }}, + }, + }, + "export_to_csv_option": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_ExportToCSVOption.html + Type: schema.TypeList, + MaxItems: 1, + Optional: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "availability_status": { + Type: schema.TypeString, + Optional: true, + Default: quicksight.StatusEnabled, + ValidateFunc: validation.StringInSlice(quicksight.Status_Values(), false), + }}, + }, + }, + "export_with_hidden_fields_option": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_ExportWithHiddenFieldsOption.html + Type: schema.TypeList, + MaxItems: 1, + Optional: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "availability_status": { + Type: schema.TypeString, + Optional: true, + Default: quicksight.StatusDisabled, + ValidateFunc: validation.StringInSlice(quicksight.Status_Values(), false), + }}, + }, + }, + "sheet_controls_option": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_SheetControlsOption.html + Type: schema.TypeList, + MaxItems: 1, + Optional: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "visibility_state": { + Type: schema.TypeString, + Optional: true, + Default: quicksight.DashboardUIStateCollapsed, + ValidateFunc: validation.StringInSlice(quicksight.DashboardUIState_Values(), false), + }, + }, + }, + }, + "sheet_layout_element_maximization_option": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_SheetLayoutElementMaximizationOption.html + Type: schema.TypeList, + MaxItems: 1, + Optional: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "availability_status": { + Type: schema.TypeString, + Optional: true, + Default: quicksight.StatusEnabled, + ValidateFunc: validation.StringInSlice(quicksight.Status_Values(), false), + }}, + }, + }, + "visual_axis_sort_option": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_VisualAxisSortOption.html + Type: schema.TypeList, + MaxItems: 1, + Optional: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "availability_status": { + Type: schema.TypeString, + Optional: true, + Default: quicksight.StatusEnabled, + ValidateFunc: validation.StringInSlice(quicksight.Status_Values(), false), + }}, + }, + }, + "visual_menu_option": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_VisualMenuOption.html + Type: schema.TypeList, + MaxItems: 1, + Optional: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "availability_status": { + Type: schema.TypeString, + Optional: true, + Default: quicksight.StatusEnabled, + ValidateFunc: validation.StringInSlice(quicksight.Status_Values(), false), + }}, + }, + }, + }, + }, + } +} + +func DashboardSourceEntitySchema() *schema.Schema { + return &schema.Schema{ + Type: schema.TypeList, + MaxItems: 1, + Optional: true, + ExactlyOneOf: []string{ + "definition", + "source_entity", + }, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "source_template": { + Type: schema.TypeList, + MaxItems: 1, + Optional: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "arn": { + Type: schema.TypeString, + Required: true, + ValidateFunc: verify.ValidARN, + }, + "data_set_references": dataSetReferencesSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_DataSetReference.html + }, + }, + }, + }, + }, + } +} + +func ExpandDashboardSourceEntity(tfList []interface{}) *quicksight.DashboardSourceEntity { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + sourceEntity := &quicksight.DashboardSourceEntity{} + + if v, ok := tfMap["source_template"].([]interface{}); ok && len(v) > 0 { + sourceEntity.SourceTemplate = expandDashboardSourceTemplate(v[0].(map[string]interface{})) + } + + return sourceEntity +} + +func expandDashboardSourceTemplate(tfMap map[string]interface{}) *quicksight.DashboardSourceTemplate { + if tfMap == nil { + return nil + } + + sourceTemplate := &quicksight.DashboardSourceTemplate{} + if v, ok := tfMap["arn"].(string); ok && v != "" { + sourceTemplate.Arn = aws.String(v) + } + if v, ok := tfMap["data_set_references"].([]interface{}); ok && len(v) > 0 { + sourceTemplate.DataSetReferences = expandDataSetReferences(v) + } + + return sourceTemplate +} + +func ExpandDashboardDefinition(tfList []interface{}) *quicksight.DashboardVersionDefinition { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + definition := &quicksight.DashboardVersionDefinition{} + + 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_identifiers_declarations"].([]interface{}); ok && len(v) > 0 { + definition.DataSetIdentifierDeclarations = expandDataSetIdentifierDeclarations(v) + } + if v, ok := tfMap["filter_groups"].([]interface{}); ok && len(v) > 0 { + definition.FilterGroups = expandFilterGroups(v) + } + if v, ok := tfMap["parameter_declarations"].([]interface{}); ok && len(v) > 0 { + definition.ParameterDeclarations = expandParameterDeclarations(v) + } + if v, ok := tfMap["sheets"].([]interface{}); ok && len(v) > 0 { + definition.Sheets = expandSheetDefinitions(v) + } + + return definition +} + +func ExpandDashboardPublishOptions(tfList []interface{}) *quicksight.DashboardPublishOptions { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + options := &quicksight.DashboardPublishOptions{} + + if v, ok := tfMap["ad_hoc_filtering_option"].([]interface{}); ok && len(v) > 0 { + options.AdHocFilteringOption = expandAdHocFilteringOption(v[0].(map[string]interface{})) + } + if v, ok := tfMap["data_point_drill_up_down_option"].([]interface{}); ok && len(v) > 0 { + options.DataPointDrillUpDownOption = expandDataPointDrillUpDownOption(v[0].(map[string]interface{})) + } + if v, ok := tfMap["data_point_menu_label_option"].([]interface{}); ok && len(v) > 0 { + options.DataPointMenuLabelOption = expandDataPointMenuLabelOption(v[0].(map[string]interface{})) + } + if v, ok := tfMap["data_point_tooltip_option"].([]interface{}); ok && len(v) > 0 { + options.DataPointTooltipOption = expandDataPointTooltipOption(v[0].(map[string]interface{})) + } + if v, ok := tfMap["export_to_csv_option"].([]interface{}); ok && len(v) > 0 { + options.ExportToCSVOption = expandExportToCSVOption(v[0].(map[string]interface{})) + } + if v, ok := tfMap["export_with_hidden_fields_option"].([]interface{}); ok && len(v) > 0 { + options.ExportWithHiddenFieldsOption = expandExportWithHiddenFieldsOption(v[0].(map[string]interface{})) + } + if v, ok := tfMap["sheet_controls_option"].([]interface{}); ok && len(v) > 0 { + options.SheetControlsOption = expandSheetControlsOption(v[0].(map[string]interface{})) + } + if v, ok := tfMap["sheet_layout_element_maximization_option"].([]interface{}); ok && len(v) > 0 { + options.SheetLayoutElementMaximizationOption = expandSheetLayoutElementMaximizationOption(v[0].(map[string]interface{})) + } + if v, ok := tfMap["visual_axis_sort_option"].([]interface{}); ok && len(v) > 0 { + options.VisualAxisSortOption = expandVisualAxisSortOption(v[0].(map[string]interface{})) + } + if v, ok := tfMap["visual_menu_option"].([]interface{}); ok && len(v) > 0 { + options.VisualMenuOption = expandVisualMenuOption(v[0].(map[string]interface{})) + } + + return options +} + +func expandAdHocFilteringOption(tfMap map[string]interface{}) *quicksight.AdHocFilteringOption { + if tfMap == nil { + return nil + } + + options := &quicksight.AdHocFilteringOption{} + if v, ok := tfMap["availability_status"].(string); ok && v != "" { + options.AvailabilityStatus = aws.String(v) + } + + return options +} + +func expandDataPointDrillUpDownOption(tfMap map[string]interface{}) *quicksight.DataPointDrillUpDownOption { + if tfMap == nil { + return nil + } + + options := &quicksight.DataPointDrillUpDownOption{} + if v, ok := tfMap["availability_status"].(string); ok && v != "" { + options.AvailabilityStatus = aws.String(v) + } + + return options +} + +func expandDataPointMenuLabelOption(tfMap map[string]interface{}) *quicksight.DataPointMenuLabelOption { + if tfMap == nil { + return nil + } + + options := &quicksight.DataPointMenuLabelOption{} + if v, ok := tfMap["availability_status"].(string); ok && v != "" { + options.AvailabilityStatus = aws.String(v) + } + + return options +} + +func expandDataPointTooltipOption(tfMap map[string]interface{}) *quicksight.DataPointTooltipOption { + if tfMap == nil { + return nil + } + + options := &quicksight.DataPointTooltipOption{} + if v, ok := tfMap["availability_status"].(string); ok && v != "" { + options.AvailabilityStatus = aws.String(v) + } + + return options +} + +func expandExportToCSVOption(tfMap map[string]interface{}) *quicksight.ExportToCSVOption { + if tfMap == nil { + return nil + } + + options := &quicksight.ExportToCSVOption{} + if v, ok := tfMap["availability_status"].(string); ok && v != "" { + options.AvailabilityStatus = aws.String(v) + } + + return options +} + +func expandExportWithHiddenFieldsOption(tfMap map[string]interface{}) *quicksight.ExportWithHiddenFieldsOption { + if tfMap == nil { + return nil + } + + options := &quicksight.ExportWithHiddenFieldsOption{} + if v, ok := tfMap["availability_status"].(string); ok && v != "" { + options.AvailabilityStatus = aws.String(v) + } + + return options +} + +func expandSheetLayoutElementMaximizationOption(tfMap map[string]interface{}) *quicksight.SheetLayoutElementMaximizationOption { + if tfMap == nil { + return nil + } + + options := &quicksight.SheetLayoutElementMaximizationOption{} + if v, ok := tfMap["availability_status"].(string); ok && v != "" { + options.AvailabilityStatus = aws.String(v) + } + + return options +} + +func expandSheetControlsOption(tfMap map[string]interface{}) *quicksight.SheetControlsOption { + if tfMap == nil { + return nil + } + + options := &quicksight.SheetControlsOption{} + if v, ok := tfMap["visibility_state"].(string); ok && v != "" { + options.VisibilityState = aws.String(v) + } + + return options +} + +func expandVisualAxisSortOption(tfMap map[string]interface{}) *quicksight.VisualAxisSortOption { + if tfMap == nil { + return nil + } + + options := &quicksight.VisualAxisSortOption{} + if v, ok := tfMap["availability_status"].(string); ok && v != "" { + options.AvailabilityStatus = aws.String(v) + } + + return options +} + +func expandVisualMenuOption(tfMap map[string]interface{}) *quicksight.VisualMenuOption { + if tfMap == nil { + return nil + } + + options := &quicksight.VisualMenuOption{} + if v, ok := tfMap["availability_status"].(string); ok && v != "" { + options.AvailabilityStatus = aws.String(v) + } + + return options +} + +func FlattenDashboardDefinition(apiObject *quicksight.DashboardVersionDefinition) []interface{} { + if apiObject == nil { + return nil + } + + tfMap := map[string]interface{}{} + if apiObject.AnalysisDefaults != nil { + tfMap["analysis_defaults"] = flattenAnalysisDefaults(apiObject.AnalysisDefaults) + } + if apiObject.CalculatedFields != nil { + tfMap["calculated_fields"] = flattenCalculatedFields(apiObject.CalculatedFields) + } + if apiObject.ColumnConfigurations != nil { + tfMap["column_configurations"] = flattenColumnConfigurations(apiObject.ColumnConfigurations) + } + if apiObject.DataSetIdentifierDeclarations != nil { + tfMap["data_set_identifiers_declarations"] = flattenDataSetIdentifierDeclarations(apiObject.DataSetIdentifierDeclarations) + } + if apiObject.FilterGroups != nil { + tfMap["filter_groups"] = flattenFilterGroups(apiObject.FilterGroups) + } + if apiObject.ParameterDeclarations != nil { + tfMap["parameter_declarations"] = flattenParameterDeclarations(apiObject.ParameterDeclarations) + } + if apiObject.Sheets != nil { + tfMap["sheets"] = flattenSheetDefinitions(apiObject.Sheets) + } + + return []interface{}{tfMap} +} + +func FlattenDashboardPublishOptions(apiObject *quicksight.DashboardPublishOptions) []interface{} { + if apiObject == nil { + return nil + } + + tfMap := map[string]interface{}{} + if apiObject.AdHocFilteringOption != nil { + tfMap["ad_hoc_filtering_option"] = flattenAdHocFilteringOption(apiObject.AdHocFilteringOption) + } + if apiObject.DataPointDrillUpDownOption != nil { + tfMap["data_point_drill_up_down_option"] = flattenDataPointDrillUpDownOption(apiObject.DataPointDrillUpDownOption) + } + if apiObject.DataPointMenuLabelOption != nil { + tfMap["data_point_menu_label_option"] = flattenDataPointMenuLabelOption(apiObject.DataPointMenuLabelOption) + } + if apiObject.DataPointTooltipOption != nil { + tfMap["data_point_tooltip_option"] = flattenDataPointTooltipOption(apiObject.DataPointTooltipOption) + } + if apiObject.ExportToCSVOption != nil { + tfMap["export_to_csv_option"] = flattenExportToCSVOption(apiObject.ExportToCSVOption) + } + if apiObject.ExportWithHiddenFieldsOption != nil { + tfMap["export_with_hidden_fields_option"] = flattenExportWithHiddenFieldsOption(apiObject.ExportWithHiddenFieldsOption) + } + if apiObject.SheetControlsOption != nil { + tfMap["sheet_controls_option"] = flattenSheetControlsOption(apiObject.SheetControlsOption) + } + if apiObject.SheetLayoutElementMaximizationOption != nil { + tfMap["sheet_layout_element_maximization_option"] = flattenSheetLayoutElementMaximizationOption(apiObject.SheetLayoutElementMaximizationOption) + } + if apiObject.VisualAxisSortOption != nil { + tfMap["visual_axis_sort_option"] = flattenVisualAxisSortOption(apiObject.VisualAxisSortOption) + } + if apiObject.VisualMenuOption != nil { + tfMap["visual_menu_option"] = flattenVisualMenuOption(apiObject.VisualMenuOption) + } + + return []interface{}{tfMap} +} + +func flattenAdHocFilteringOption(apiObject *quicksight.AdHocFilteringOption) []interface{} { + if apiObject == nil { + return nil + } + + tfMap := map[string]interface{}{} + if apiObject.AvailabilityStatus != nil { + tfMap["availability_status"] = aws.StringValue(apiObject.AvailabilityStatus) + } + + return []interface{}{tfMap} +} + +func flattenDataPointDrillUpDownOption(apiObject *quicksight.DataPointDrillUpDownOption) []interface{} { + if apiObject == nil { + return nil + } + + tfMap := map[string]interface{}{} + if apiObject.AvailabilityStatus != nil { + tfMap["availability_status"] = aws.StringValue(apiObject.AvailabilityStatus) + } + + return []interface{}{tfMap} +} + +func flattenDataPointMenuLabelOption(apiObject *quicksight.DataPointMenuLabelOption) []interface{} { + if apiObject == nil { + return nil + } + + tfMap := map[string]interface{}{} + if apiObject.AvailabilityStatus != nil { + tfMap["availability_status"] = aws.StringValue(apiObject.AvailabilityStatus) + } + + return []interface{}{tfMap} +} + +func flattenDataPointTooltipOption(apiObject *quicksight.DataPointTooltipOption) []interface{} { + if apiObject == nil { + return nil + } + + tfMap := map[string]interface{}{} + if apiObject.AvailabilityStatus != nil { + tfMap["availability_status"] = aws.StringValue(apiObject.AvailabilityStatus) + } + + return []interface{}{tfMap} +} + +func flattenExportToCSVOption(apiObject *quicksight.ExportToCSVOption) []interface{} { + if apiObject == nil { + return nil + } + + tfMap := map[string]interface{}{} + if apiObject.AvailabilityStatus != nil { + tfMap["availability_status"] = aws.StringValue(apiObject.AvailabilityStatus) + } + + return []interface{}{tfMap} +} + +func flattenExportWithHiddenFieldsOption(apiObject *quicksight.ExportWithHiddenFieldsOption) []interface{} { + if apiObject == nil { + return nil + } + + tfMap := map[string]interface{}{} + if apiObject.AvailabilityStatus != nil { + tfMap["availability_status"] = aws.StringValue(apiObject.AvailabilityStatus) + } + + return []interface{}{tfMap} +} + +func flattenSheetControlsOption(apiObject *quicksight.SheetControlsOption) []interface{} { + if apiObject == nil { + return nil + } + + tfMap := map[string]interface{}{} + if apiObject.VisibilityState != nil { + tfMap["visibility_state"] = aws.StringValue(apiObject.VisibilityState) + } + + return []interface{}{tfMap} +} + +func flattenSheetLayoutElementMaximizationOption(apiObject *quicksight.SheetLayoutElementMaximizationOption) []interface{} { + if apiObject == nil { + return nil + } + + tfMap := map[string]interface{}{} + if apiObject.AvailabilityStatus != nil { + tfMap["availability_status"] = aws.StringValue(apiObject.AvailabilityStatus) + } + + return []interface{}{tfMap} +} + +func flattenVisualAxisSortOption(apiObject *quicksight.VisualAxisSortOption) []interface{} { + if apiObject == nil { + return nil + } + + tfMap := map[string]interface{}{} + if apiObject.AvailabilityStatus != nil { + tfMap["availability_status"] = aws.StringValue(apiObject.AvailabilityStatus) + } + + return []interface{}{tfMap} +} + +func flattenVisualMenuOption(apiObject *quicksight.VisualMenuOption) []interface{} { + if apiObject == nil { + return nil + } + + tfMap := map[string]interface{}{} + if apiObject.AvailabilityStatus != nil { + tfMap["availability_status"] = aws.StringValue(apiObject.AvailabilityStatus) + } + + return []interface{}{tfMap} +} diff --git a/internal/service/quicksight/schema/dataset.go b/internal/service/quicksight/schema/dataset.go new file mode 100644 index 00000000000..6ef5737cbeb --- /dev/null +++ b/internal/service/quicksight/schema/dataset.go @@ -0,0 +1,109 @@ +package schema + +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/verify" +) + +func dataSetIdentifierDeclarationsSchema() *schema.Schema { + return &schema.Schema{ // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_DataSetIdentifierDeclaration.html + Type: schema.TypeList, + MinItems: 1, + MaxItems: 50, + Required: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "data_set_arn": stringSchema(false, verify.ValidARN), + "identifier": stringSchema(false, validation.StringLenBetween(1, 2048)), + }, + }, + } +} + +func dataSetReferencesSchema() *schema.Schema { + return &schema.Schema{ // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_DataSetReference.html + Type: schema.TypeList, + Required: true, + MinItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "data_set_arn": { + Type: schema.TypeString, + Required: true, + ValidateFunc: verify.ValidARN, + }, + "data_set_placeholder": { + Type: schema.TypeString, + Required: true, + }, + }, + }, + } +} + +func expandDataSetIdentifierDeclarations(tfList []interface{}) []*quicksight.DataSetIdentifierDeclaration { + if len(tfList) == 0 { + return nil + } + + var identifiers []*quicksight.DataSetIdentifierDeclaration + for _, tfMapRaw := range tfList { + tfMap, ok := tfMapRaw.(map[string]interface{}) + if !ok { + continue + } + + identifier := expandDataSetIdentifierDeclaration(tfMap) + if identifier == nil { + continue + } + + identifiers = append(identifiers, identifier) + } + + return identifiers +} + +func expandDataSetIdentifierDeclaration(tfMap map[string]interface{}) *quicksight.DataSetIdentifierDeclaration { + if tfMap == nil { + return nil + } + + identifier := &quicksight.DataSetIdentifierDeclaration{} + + if v, ok := tfMap["data_set_arn"].(string); ok && v != "" { + identifier.DataSetArn = aws.String(v) + } + if v, ok := tfMap["identifier"].(string); ok && v != "" { + identifier.Identifier = aws.String(v) + } + + return identifier +} + +func flattenDataSetIdentifierDeclarations(apiObject []*quicksight.DataSetIdentifierDeclaration) []interface{} { + if len(apiObject) == 0 { + return nil + } + + var tfList []interface{} + for _, identifier := range apiObject { + if identifier == nil { + continue + } + + tfMap := map[string]interface{}{} + if identifier.DataSetArn != nil { + tfMap["data_set_arn"] = aws.StringValue(identifier.DataSetArn) + } + if identifier.Identifier != nil { + tfMap["identifier"] = aws.StringValue(identifier.Identifier) + } + tfList = append(tfList, tfMap) + } + + return tfList +} diff --git a/internal/service/quicksight/schema/parameters.go b/internal/service/quicksight/schema/parameters.go new file mode 100644 index 00000000000..2c5f90363f2 --- /dev/null +++ b/internal/service/quicksight/schema/parameters.go @@ -0,0 +1,291 @@ +package schema + +import ( + "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 ParametersSchema() *schema.Schema { + return &schema.Schema{ // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_Parameters.html + Type: schema.TypeList, + MaxItems: 1, + Optional: true, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "date_time_parameters": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_DateTimeParameter.html + Type: schema.TypeList, + MinItems: 1, + MaxItems: 100, + Optional: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "name": stringSchema(true, validation.StringMatch(regexp.MustCompile(`.*\S.*`), "")), + "values": { + Type: schema.TypeList, + MinItems: 1, + Required: true, + Elem: &schema.Schema{ + Type: schema.TypeString, + ValidateFunc: verify.ValidUTCTimestamp, + }, + }, + }, + }, + }, + "decimal_parameters": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_DecimalParameter.html + Type: schema.TypeList, + MinItems: 1, + MaxItems: 100, + Optional: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "name": stringSchema(true, validation.StringMatch(regexp.MustCompile(`.*\S.*`), "")), + "values": { + Type: schema.TypeList, + MinItems: 1, + Required: true, + Elem: &schema.Schema{ + Type: schema.TypeFloat, + }, + }, + }, + }, + }, + "integer_parameters": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_IntegerParameter.html + Type: schema.TypeList, + MinItems: 1, + MaxItems: 100, + Optional: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "name": stringSchema(true, validation.StringMatch(regexp.MustCompile(`.*\S.*`), "")), + "values": { + Type: schema.TypeList, + MinItems: 1, + Required: true, + Elem: &schema.Schema{ + Type: schema.TypeInt, + }, + }, + }, + }, + }, + "string_parameters": { // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_StringParameter.html + Type: schema.TypeList, + MinItems: 1, + MaxItems: 100, + Optional: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "name": stringSchema(true, validation.StringMatch(regexp.MustCompile(`.*\S.*`), "")), + "values": { + Type: schema.TypeList, + MinItems: 1, + Required: true, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + }, + }, + }, + }, + }, + }, + } +} + +func ExpandParameters(tfList []interface{}) *quicksight.Parameters { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + parameters := &quicksight.Parameters{} + + if v, ok := tfMap["date_time_parameters"].([]interface{}); ok && len(v) > 0 { + parameters.DateTimeParameters = expandDateTimeParameters(v) + } + if v, ok := tfMap["decimal_parameters"].([]interface{}); ok && len(v) > 0 { + parameters.DecimalParameters = expandDecimalParameters(v) + } + if v, ok := tfMap["integer_parameters"].([]interface{}); ok && len(v) > 0 { + parameters.IntegerParameters = expandIntegerParameters(v) + } + if v, ok := tfMap["string_parameters"].([]interface{}); ok && len(v) > 0 { + parameters.StringParameters = expandStringParameters(v) + } + + return parameters +} + +func expandDateTimeParameters(tfList []interface{}) []*quicksight.DateTimeParameter { + if len(tfList) == 0 { + return nil + } + + var parameters []*quicksight.DateTimeParameter + for _, tfMapRaw := range tfList { + tfMap, ok := tfMapRaw.(map[string]interface{}) + if !ok { + continue + } + + parameter := expandDateTimeParameter(tfMap) + if parameter == nil { + continue + } + + parameters = append(parameters, parameter) + } + + return parameters +} + +func expandDateTimeParameter(tfMap map[string]interface{}) *quicksight.DateTimeParameter { + if tfMap == nil { + return nil + } + + parameter := &quicksight.DateTimeParameter{} + + if v, ok := tfMap["name"].(string); ok && v != "" { + parameter.Name = aws.String(v) + } + if v, ok := tfMap["values"].([]interface{}); ok && len(v) > 0 { + parameter.Values = flex.ExpandStringTimeList(v, time.RFC3339) + } + + return parameter +} + +func expandDecimalParameters(tfList []interface{}) []*quicksight.DecimalParameter { + if len(tfList) == 0 { + return nil + } + + var parameters []*quicksight.DecimalParameter + for _, tfMapRaw := range tfList { + tfMap, ok := tfMapRaw.(map[string]interface{}) + if !ok { + continue + } + + parameter := expandDecimalParameter(tfMap) + if parameter == nil { + continue + } + + parameters = append(parameters, parameter) + } + + return parameters +} + +func expandDecimalParameter(tfMap map[string]interface{}) *quicksight.DecimalParameter { + if tfMap == nil { + return nil + } + + parameter := &quicksight.DecimalParameter{} + + if v, ok := tfMap["name"].(string); ok && v != "" { + parameter.Name = aws.String(v) + } + if v, ok := tfMap["values"].([]interface{}); ok && len(v) > 0 { + parameter.Values = flex.ExpandFloat64List(v) + } + + return parameter +} + +func expandIntegerParameters(tfList []interface{}) []*quicksight.IntegerParameter { + if len(tfList) == 0 { + return nil + } + + var parameters []*quicksight.IntegerParameter + for _, tfMapRaw := range tfList { + tfMap, ok := tfMapRaw.(map[string]interface{}) + if !ok { + continue + } + + parameter := expandIntegerParameter(tfMap) + if parameter == nil { + continue + } + + parameters = append(parameters, parameter) + } + + return parameters +} + +func expandIntegerParameter(tfMap map[string]interface{}) *quicksight.IntegerParameter { + if tfMap == nil { + return nil + } + + parameter := &quicksight.IntegerParameter{} + + if v, ok := tfMap["name"].(string); ok && v != "" { + parameter.Name = aws.String(v) + } + if v, ok := tfMap["values"].([]interface{}); ok && len(v) > 0 { + parameter.Values = flex.ExpandInt64List(v) + } + + return parameter +} + +func expandStringParameters(tfList []interface{}) []*quicksight.StringParameter { + if len(tfList) == 0 { + return nil + } + + var parameters []*quicksight.StringParameter + for _, tfMapRaw := range tfList { + tfMap, ok := tfMapRaw.(map[string]interface{}) + if !ok { + continue + } + + parameter := expandStringParameter(tfMap) + if parameter == nil { + continue + } + + parameters = append(parameters, parameter) + } + + return parameters +} + +func expandStringParameter(tfMap map[string]interface{}) *quicksight.StringParameter { + if tfMap == nil { + return nil + } + + parameter := &quicksight.StringParameter{} + + if v, ok := tfMap["name"].(string); ok && v != "" { + parameter.Name = aws.String(v) + } + if v, ok := tfMap["values"].([]interface{}); ok && len(v) > 0 { + parameter.Values = flex.ExpandStringList(v) + } + + return parameter +} diff --git a/internal/service/quicksight/schema/template.go b/internal/service/quicksight/schema/template.go index ed00d3ba7d3..eabdca532a4 100644 --- a/internal/service/quicksight/schema/template.go +++ b/internal/service/quicksight/schema/template.go @@ -10,7 +10,7 @@ import ( "github.com/hashicorp/terraform-provider-aws/internal/verify" ) -func DefinitionSchema() *schema.Schema { +func TemplateDefinitionSchema() *schema.Schema { return &schema.Schema{ // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_TemplateVersionDefinition.html Type: schema.TypeList, MaxItems: 1, @@ -321,7 +321,7 @@ func rollingDateConfigurationSchema() *schema.Schema { } } -func SourceEntitySchema() *schema.Schema { +func TemplateSourceEntitySchema() *schema.Schema { return &schema.Schema{ Type: schema.TypeList, MaxItems: 1, @@ -344,24 +344,7 @@ func SourceEntitySchema() *schema.Schema { Required: true, ValidateFunc: verify.ValidARN, }, - "data_set_references": { - Type: schema.TypeList, - Required: true, - MinItems: 1, - Elem: &schema.Resource{ - Schema: map[string]*schema.Schema{ - "data_set_arn": { - Type: schema.TypeString, - Required: true, - ValidateFunc: verify.ValidARN, - }, - "data_set_placeholder": { - Type: schema.TypeString, - Required: true, - }, - }, - }, - }, + "data_set_references": dataSetReferencesSchema(), // https://docs.aws.amazon.com/quicksight/latest/APIReference/API_DataSetReference.html }, }, }, @@ -385,7 +368,7 @@ func SourceEntitySchema() *schema.Schema { } } -func ExpandSourceEntity(tfList []interface{}) *quicksight.TemplateSourceEntity { +func ExpandTemplateSourceEntity(tfList []interface{}) *quicksight.TemplateSourceEntity { if len(tfList) == 0 || tfList[0] == nil { return nil } @@ -400,7 +383,7 @@ func ExpandSourceEntity(tfList []interface{}) *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{})) + sourceEntity.SourceTemplate = expandTemplateSourceTemplate(v[0].(map[string]interface{})) } return sourceEntity @@ -461,7 +444,7 @@ func expandDataSetReference(tfMap map[string]interface{}) *quicksight.DataSetRef return dataSetReference } -func expandSourceTemplate(tfMap map[string]interface{}) *quicksight.TemplateSourceTemplate { +func expandTemplateSourceTemplate(tfMap map[string]interface{}) *quicksight.TemplateSourceTemplate { if tfMap == nil { return nil } @@ -474,7 +457,7 @@ func expandSourceTemplate(tfMap map[string]interface{}) *quicksight.TemplateSour return sourceTemplate } -func ExpandDefinition(tfList []interface{}) *quicksight.TemplateVersionDefinition { +func ExpandTemplateDefinition(tfList []interface{}) *quicksight.TemplateVersionDefinition { if len(tfList) == 0 || tfList[0] == nil { return nil } diff --git a/internal/service/quicksight/schema/template_parameter.go b/internal/service/quicksight/schema/template_parameter.go index fd5d2713515..1e002469c95 100644 --- a/internal/service/quicksight/schema/template_parameter.go +++ b/internal/service/quicksight/schema/template_parameter.go @@ -551,7 +551,7 @@ func expandDecimalValueWhenUnsetConfiguration(tfList []interface{}) *quicksight. config := &quicksight.DecimalValueWhenUnsetConfiguration{} - if v, ok := tfMap["custom_value"].(float64); ok { + if v, ok := tfMap["custom_value"].(float64); ok && v != 0.0 { config.CustomValue = aws.Float64(v) } if v, ok := tfMap["value_when_unset_option"].(string); ok && v != "" { @@ -623,7 +623,7 @@ func expandIntegerValueWhenUnsetConfiguration(tfList []interface{}) *quicksight. config := &quicksight.IntegerValueWhenUnsetConfiguration{} - if v, ok := tfMap["custom_value"].(int); ok { + if v, ok := tfMap["custom_value"].(int); ok && v != 0 { config.CustomValue = aws.Int64(int64(v)) } if v, ok := tfMap["value_when_unset_option"].(string); ok && v != "" { @@ -695,7 +695,7 @@ func expandStringValueWhenUnsetConfiguration(tfList []interface{}) *quicksight.S config := &quicksight.StringValueWhenUnsetConfiguration{} - if v, ok := tfMap["custom_value"].(string); ok { + if v, ok := tfMap["custom_value"].(string); ok && v != "" { config.CustomValue = aws.String(v) } if v, ok := tfMap["value_when_unset_option"].(string); ok && v != "" { diff --git a/internal/service/quicksight/schema/template_sheet.go b/internal/service/quicksight/schema/template_sheet.go index ddd8309b0af..3c10c89a000 100644 --- a/internal/service/quicksight/schema/template_sheet.go +++ b/internal/service/quicksight/schema/template_sheet.go @@ -756,6 +756,9 @@ func expandGridLayoutScreenCanvasSizeOptions(tfList []interface{}) *quicksight.G if v, ok := tfMap["optimized_view_port_width"].(string); ok && v != "" { options.OptimizedViewPortWidth = aws.String(v) } + if v, ok := tfMap["resize_option"].(string); ok && v != "" { + options.ResizeOption = aws.String(v) + } return options } @@ -1270,16 +1273,16 @@ func expandGridLayoutElement(tfMap map[string]interface{}) *quicksight.GridLayou if v, ok := tfMap["element_type"].(string); ok && v != "" { layout.ElementType = aws.String(v) } - if v, ok := tfMap["column_span"].(int); ok { + if v, ok := tfMap["column_span"].(int); ok && v != 0 { layout.ColumnSpan = aws.Int64(int64(v)) } - if v, ok := tfMap["row_span"].(int); ok { + if v, ok := tfMap["row_span"].(int); ok && v != 0 { layout.RowSpan = aws.Int64(int64(v)) } - if v, ok := tfMap["column_index"].(int); ok { + if v, ok := tfMap["column_index"].(int); ok && v != 0 { layout.ColumnIndex = aws.Int64(int64(v)) } - if v, ok := tfMap["row_index"].(int); ok { + if v, ok := tfMap["row_index"].(int); ok && v != 0 { layout.RowIndex = aws.Int64(int64(v)) } diff --git a/internal/service/quicksight/service_package_gen.go b/internal/service/quicksight/service_package_gen.go index d5b164541a5..c72f9b593b5 100644 --- a/internal/service/quicksight/service_package_gen.go +++ b/internal/service/quicksight/service_package_gen.go @@ -81,6 +81,14 @@ func (p *servicePackage) SDKResources(ctx context.Context) []*types.ServicePacka TypeName: "aws_quicksight_account_subscription", Name: "Account Subscription", }, + { + Factory: ResourceDashboard, + TypeName: "aws_quicksight_dashboard", + Name: "Dashboard", + Tags: &types.ServicePackageResourceTags{ + IdentifierAttribute: "arn", + }, + }, { Factory: ResourceDataSet, TypeName: "aws_quicksight_data_set", diff --git a/internal/service/quicksight/status.go b/internal/service/quicksight/status.go index 7e8ec69e492..45082f1577f 100644 --- a/internal/service/quicksight/status.go +++ b/internal/service/quicksight/status.go @@ -46,3 +46,19 @@ func statusTemplate(ctx context.Context, conn *quicksight.QuickSight, id string) return out, *out.Version.Status, nil } } + +// Fetch Dashboard status +func statusDashboard(ctx context.Context, conn *quicksight.QuickSight, id string) retry.StateRefreshFunc { + return func() (interface{}, string, error) { + out, err := FindDashboardByID(ctx, conn, id) + if tfresource.NotFound(err) { + return nil, "", nil + } + + if err != nil { + return nil, "", err + } + + return out, *out.Version.Status, nil + } +} diff --git a/internal/service/quicksight/template.go b/internal/service/quicksight/template.go index 36a0583478b..83d033dfcb4 100644 --- a/internal/service/quicksight/template.go +++ b/internal/service/quicksight/template.go @@ -58,7 +58,7 @@ func ResourceTemplate() *schema.Resource { Type: schema.TypeString, Computed: true, }, - "definition": quicksightschema.DefinitionSchema(), + "definition": quicksightschema.TemplateDefinitionSchema(), "last_updated_time": { Type: schema.TypeString, Computed: true, @@ -90,7 +90,7 @@ func ResourceTemplate() *schema.Resource { }, }, }, - "source_entity": quicksightschema.SourceEntitySchema(), + "source_entity": quicksightschema.TemplateSourceEntitySchema(), "source_entity_arn": { Type: schema.TypeString, Computed: true, @@ -147,11 +147,11 @@ func resourceTemplateCreate(ctx context.Context, d *schema.ResourceData, meta in } if v, ok := d.GetOk("source_entity"); ok && len(v.([]interface{})) > 0 && v.([]interface{})[0] != nil { - input.SourceEntity = quicksightschema.ExpandSourceEntity(v.([]interface{})) + input.SourceEntity = quicksightschema.ExpandTemplateSourceEntity(v.([]interface{})) } if v, ok := d.GetOk("definition"); ok && len(v.([]interface{})) > 0 && v.([]interface{})[0] != nil { - input.Definition = quicksightschema.ExpandDefinition(d.Get("definition").([]interface{})) + input.Definition = quicksightschema.ExpandTemplateDefinition(d.Get("definition").([]interface{})) } if v, ok := d.GetOk("permissions"); ok && len(v.([]interface{})) > 0 && v.([]interface{})[0] != nil { @@ -247,12 +247,11 @@ func resourceTemplateUpdate(ctx context.Context, d *schema.ResourceData, meta in VersionDescription: aws.String(d.Get("version_description").(string)), } - if d.HasChange("source_entity") { - in.SourceEntity = quicksightschema.ExpandSourceEntity(d.Get("source_entity").([]interface{})) - } - - if d.HasChange("definition") { - in.Definition = quicksightschema.ExpandDefinition(d.Get("definition").([]interface{})) + // One of source_entity or definition is required for update + if _, ok := d.GetOk("source_entity"); ok { + in.SourceEntity = quicksightschema.ExpandTemplateSourceEntity(d.Get("source_entity").([]interface{})) + } else { + in.Definition = quicksightschema.ExpandTemplateDefinition(d.Get("definition").([]interface{})) } log.Printf("[DEBUG] Updating QuickSight Template (%s): %#v", d.Id(), in) diff --git a/internal/service/quicksight/template_test.go b/internal/service/quicksight/template_test.go index 67a53e56dcb..fde9a87fdda 100644 --- a/internal/service/quicksight/template_test.go +++ b/internal/service/quicksight/template_test.go @@ -260,6 +260,58 @@ func TestAccQuickSightTemplate_tags(t *testing.T) { }) } +func TestAccQuickSightTemplate_update(t *testing.T) { + ctx := acctest.Context(t) + + var template quicksight.Template + resourceName := "aws_quicksight_template.copy" + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + rNameUpdated := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + rId := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + sourceName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + sourceId := 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_TemplateSourceEntity(rId, rName, sourceId, sourceName), + Check: resource.ComposeTestCheckFunc( + testAccCheckTemplateExists(ctx, resourceName, &template), + resource.TestCheckResourceAttr(resourceName, "template_id", rId), + resource.TestCheckResourceAttr(resourceName, "name", rName), + resource.TestCheckResourceAttr(resourceName, "status", quicksight.ResourceStatusCreationSuccessful), + acctest.CheckResourceAttrRegionalARN(resourceName, "source_entity.0.source_template.0.arn", "quicksight", fmt.Sprintf("template/%s", sourceId)), + resource.TestCheckResourceAttr(resourceName, "version_number", "1"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"source_entity"}, + }, + + { + Config: testAccTemplateConfig_TemplateSourceEntity(rId, rNameUpdated, sourceId, sourceName), + Check: resource.ComposeTestCheckFunc( + testAccCheckTemplateExists(ctx, resourceName, &template), + resource.TestCheckResourceAttr(resourceName, "template_id", rId), + resource.TestCheckResourceAttr(resourceName, "name", rNameUpdated), + resource.TestCheckResourceAttr(resourceName, "status", quicksight.ResourceStatusCreationSuccessful), + acctest.CheckResourceAttrRegionalARN(resourceName, "source_entity.0.source_template.0.arn", "quicksight", fmt.Sprintf("template/%s", sourceId)), + resource.TestCheckResourceAttr(resourceName, "version_number", "2"), + ), + }, + }, + }) +} + func testAccCheckTemplateDestroy(ctx context.Context) resource.TestCheckFunc { return func(s *terraform.State) error { conn := acctest.Provider.Meta().(*conns.AWSClient).QuickSightConn() diff --git a/internal/service/quicksight/wait.go b/internal/service/quicksight/wait.go index 609ee1f1133..e582d00262f 100644 --- a/internal/service/quicksight/wait.go +++ b/internal/service/quicksight/wait.go @@ -95,7 +95,7 @@ func waitTemplateCreated(ctx context.Context, conn *quicksight.QuickSight, id st func waitTemplateUpdated(ctx context.Context, conn *quicksight.QuickSight, id string, timeout time.Duration) (*quicksight.Template, error) { stateConf := &retry.StateChangeConf{ - Pending: []string{quicksight.ResourceStatusUpdateInProgress}, + Pending: []string{quicksight.ResourceStatusUpdateInProgress, quicksight.ResourceStatusCreationInProgress}, Target: []string{quicksight.ResourceStatusUpdateSuccessful, quicksight.ResourceStatusCreationSuccessful}, Refresh: statusTemplate(ctx, conn, id), Timeout: timeout, @@ -122,3 +122,63 @@ func waitTemplateUpdated(ctx context.Context, conn *quicksight.QuickSight, id st return nil, err } + +func waitDashboardCreated(ctx context.Context, conn *quicksight.QuickSight, id string, timeout time.Duration) (*quicksight.Dashboard, error) { + stateConf := &retry.StateChangeConf{ + Pending: []string{quicksight.ResourceStatusCreationInProgress}, + Target: []string{quicksight.ResourceStatusCreationSuccessful}, + Refresh: statusDashboard(ctx, conn, id), + Timeout: timeout, + NotFoundChecks: 20, + ContinuousTargetOccurence: 2, + } + + outputRaw, err := stateConf.WaitForStateContext(ctx) + if out, ok := outputRaw.(*quicksight.Dashboard); 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 waitDashboardUpdated(ctx context.Context, conn *quicksight.QuickSight, id string, timeout time.Duration) (*quicksight.Dashboard, error) { + stateConf := &retry.StateChangeConf{ + Pending: []string{quicksight.ResourceStatusUpdateInProgress, quicksight.ResourceStatusCreationInProgress}, + Target: []string{quicksight.ResourceStatusUpdateSuccessful, quicksight.ResourceStatusCreationSuccessful}, + Refresh: statusDashboard(ctx, conn, id), + Timeout: timeout, + NotFoundChecks: 20, + ContinuousTargetOccurence: 2, + } + + outputRaw, err := stateConf.WaitForStateContext(ctx) + if out, ok := outputRaw.(*quicksight.Dashboard); 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_dashboard.html.markdown b/website/docs/r/quicksight_dashboard.html.markdown new file mode 100644 index 00000000000..ebe1826e478 --- /dev/null +++ b/website/docs/r/quicksight_dashboard.html.markdown @@ -0,0 +1,224 @@ +--- +subcategory: "QuickSight" +layout: "aws" +page_title: "AWS: aws_quicksight_dashboard" +description: |- + Manages a QuickSight Dashboard. +--- + +# Resource: aws_quicksight_dashboard + +Resource for managing a QuickSight Dashboard. + +## Example Usage + +### From Source Template + +```terraform +resource "aws_quicksight_dashboard" "example" { + dashboard_id = "example-id" + name = "example-name" + version_description = "version" + source_entity { + source_template { + arn = aws_quicksight_template.source.arn + data_set_references { + data_set_arn = aws_quicksight_data_set.dataset.arn + data_set_placeholder = "1" + } + } + } +} +``` + +### With Definition + +```terraform +resource "aws_quicksight_dashboard" "example" { + dashboard_id = "example-id" + name = "example-name" + version_description = "version" + definition { + data_set_identifiers_declarations { + data_set_arn = aws_quicksight_data_set.dataset.arn + identifier = "1" + } + sheets { + title = "Example" + sheet_id = "Example1" + visuals { + line_chart_visual { + visual_id = "LineChart" + title { + format_text { + plain_text = "Line Chart Example" + } + } + 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" + } + } + } + } + } + } + } + } + } +} +``` + +## Argument Reference + +The following arguments are required: + +* `dashboard_id` - (Required, Forces new resource) Identifier for the dashboard. +* `name` - (Required) Display name for the dashboard. +* `version_description` - (Required) A description of the current dashboard version being created/updated. + +The following arguments are optional: + +* `aws_account_id` - (Optional, Forces new resource) AWS account ID. +* `dashboard_publish_options` - (Optional) Options for publishing the dashboard. See [dashboard_publish_options](#dashboard_publish_options). +* `definition` - (Optional) A detailed dashboard definition. Only one of `definition` or `source_entity` should be configured. See [definition](#definition). +* `parameters` - (Optional) The parameters for the creation of the dashboard, which you want to use to override the default settings. A dashboard can have any type of parameters, and some parameters might accept multiple values. See [parameters](#parameters). +* `permissions` - (Optional) A set of resource permissions on the dashboard. Maximum of 64 items. See [permissions](#permissions). +* `source_entity` - (Optional) The entity that you are using as a source when you create the dashboard (template). Only one of `definition` or `source_entity` should be configured. See [source_entity](#source_entity). +* `tags` - (Optional) Key-value map of resource tags. If configured with a provider [`default_tags` configuration block](/docs/providers/aws/index.html#default_tags-configuration-block) present, tags with matching keys will overwrite those defined at the provider-level. +* `theme_arn` - (Optional) The Amazon Resource Name (ARN) of the theme that is being used for this dashboard. The theme ARN must exist in the same AWS account where you create the dashboard. + +### permissions + +* `actions` - (Required) List of IAM actions to grant or revoke permissions on. +* `principal` - (Required) ARN of the principal. See the [ResourcePermission documentation](https://docs.aws.amazon.com/quicksight/latest/APIReference/API_ResourcePermission.html) for the applicable ARN values. + +### source_entity + +* `source_template` - (Optional) The source template. See [source_template](#source_template). + +### source_template + +* `arn` - (Required) The Amazon Resource Name (ARN) of the resource. +* `data_set_references` - (Required) List of dataset references. See [data_set_references](#data_set_references). + +### data_set_references + +* `data_set_arn` - (Required) Dataset Amazon Resource Name (ARN). +* `data_set_placeholder` - (Required) Dataset placeholder. + +### dashboard_publish_options + +* `ad_hoc_filtering_option` - (Optional) Ad hoc (one-time) filtering option. See [ad_hoc_filtering_option](#ad_hoc_filtering_option). +* `data_point_drill_up_down_option` - (Optional) The drill-down options of data points in a dashboard. See [data_point_drill_up_down_option](#data_point_drill_up_down_option). +* `data_point_menu_label_option` - (Optional) The data point menu label options of a dashboard. See [data_point_menu_label_option](#data_point_menu_label_option). +* `data_point_tooltip_option` - (Optional) The data point tool tip options of a dashboard. See [data_point_tooltip_option](#data_point_tooltip_option). +* `export_to_csv_option` - (Optional) Export to .csv option. See [export_to_csv_option](#export_to_csv_option). +* `export_with_hidden_fields_option` - (Optional) Determines if hidden fields are exported with a dashboard. See [export_with_hidden_fields_option](#export_with_hidden_fields_option). +* `sheet_controls_option` - (Optional) Sheet controls option. See [sheet_controls_option](#sheet_controls_option). +* `sheet_layout_element_maximization_option` - (Optional) The sheet layout maximization options of a dashboard. See [sheet_layout_element_maximization_option](#sheet_layout_element_maximization_option). +* `visual_axis_sort_option` - (Optional) The axis sort options of a dashboard. See [visual_axis_sort_option](#visual_axis_sort_option). +* `visual_menu_option` - (Optional) The menu options of a visual in a dashboard. See [visual_menu_option](#visual_menu_option). + +### ad_hoc_filtering_option + +* `availability_status` - (Optional) Availability status. Possibles values: ENABLED, DISABLED. + +### data_point_drill_up_down_option + +* `availability_status` - (Optional) Availability status. Possibles values: ENABLED, DISABLED. + +### data_point_menu_label_option + +* `availability_status` - (Optional) Availability status. Possibles values: ENABLED, DISABLED. + +### data_point_tooltip_option + +* `availability_status` - (Optional) Availability status. Possibles values: ENABLED, DISABLED. + +### export_to_csv_option + +* `availability_status` - (Optional) Availability status. Possibles values: ENABLED, DISABLED. + +### export_with_hidden_fields_option + +* `availability_status` - (Optional) Availability status. Possibles values: ENABLED, DISABLED. + +### sheet_controls_option + +* `visibility_state` - (Optional) Visibility state. Possibles values: EXPANDED, COLLAPSED. + +### sheet_layout_element_maximization_option + +* `availability_status` - (Optional) Availability status. Possibles values: ENABLED, DISABLED. + +### visual_axis_sort_option + +* `availability_status` - (Optional) Availability status. Possibles values: ENABLED, DISABLED. + +### visual_menu_option + +* `availability_status` - (Optional) Availability status. Possibles values: ENABLED, DISABLED. + +### parameters + +* `date_time_parameters` - (Optional) A list of parameters that have a data type of date-time. See [AWS API Documentation for complete description](https://docs.aws.amazon.com/quicksight/latest/APIReference/API_DateTimeParameter.html). +* `decimal_parameters` - (Optional) A list of parameters that have a data type of decimal. See [AWS API Documentation for complete description](https://docs.aws.amazon.com/quicksight/latest/APIReference/API_DecimalParameter.html). +* `integer_parameters` - (Optional) A list of parameters that have a data type of integer. See [AWS API Documentation for complete description](https://docs.aws.amazon.com/quicksight/latest/APIReference/API_IntegerParameter.html). +* `string_parameters` - (Optional) A list of parameters that have a data type of string. See [AWS API Documentation for complete description](https://docs.aws.amazon.com/quicksight/latest/APIReference/API_StringParameter.html). + +### definition + +* `data_set_identifiers_declarations` - (Required) A list dataset identifier declarations. With this mapping,you can use dataset identifiers instead of dataset Amazon Resource Names (ARNs) throughout the dashboard's sub-structures. See [AWS API Documentation for complete description](https://docs.aws.amazon.com/quicksight/latest/APIReference/API_DataSetIdentifierDeclaration.html). +* `analysis_defaults` - (Optional) The configuration for default analysis settings. See [AWS API Documentation for complete description](https://docs.aws.amazon.com/quicksight/latest/APIReference/API_AnalysisDefaults.html). +* `calculated_fields` - (Optional) A list of calculated field definitions for the dashboard. See [AWS API Documentation for complete description](https://docs.aws.amazon.com/quicksight/latest/APIReference/API_CalculatedField.html). +* `column_configurations` - (Optional) A list of dashboard-level column configurations. Column configurations are used to set default formatting for a column that's used throughout a dashboard. See [AWS API Documentation for complete description](ttps://docs.aws.amazon.com/quicksight/latest/APIReference/API_ColumnConfiguration.html). +* `filter_groups` - (Optional) A list of filter definitions for a dashboard. See [AWS API Documentation for complete description](https://docs.aws.amazon.com/quicksight/latest/APIReference/API_FilterGroup.html). For more information, see [Filtering Data](https://docs.aws.amazon.com/quicksight/latest/user/filtering-visual-data.html) in Amazon QuickSight User Guide. +* `parameters_declarations` - (Optional) A list of parameter declarations for a dashboard. Parameters are named variables that can transfer a value for use by an action or an object. See [AWS API Documentation for complete description](https://docs.aws.amazon.com/quicksight/latest/APIReference/API_ParameterDeclaration.html). For more information, see [Parameters in Amazon QuickSight](https://docs.aws.amazon.com/quicksight/latest/user/parameters-in-quicksight.html) in the Amazon QuickSight User Guide. +* `sheets` - (Optional) A list of sheet definitions for a dashboard. See [AWS API Documentation for complete description](https://docs.aws.amazon.com/quicksight/latest/APIReference/API_SheetDefinition.html). + +## Attributes Reference + +In addition to all arguments above, the following attributes are exported: + +* `arn` - ARN of the dashboard. +* `created_time` - The time that the dashboard was created. +* `id` - A comma-delimited string joining AWS account ID and dashboard ID. +* `last_updated_time` - The time that the dashboard was last updated. +* `source_entity_arn` - Amazon Resource Name (ARN) of a template that was used to create this dashboard. +* `status` - The dashboard creation status. +* `tags_all` - A map of tags assigned to the resource, including those inherited from the provider [`default_tags` configuration block](/docs/providers/aws/index.html#default_tags-configuration-block). +* `version_number` - The version number of the dashboard version. + +## Timeouts + +[Configuration options](https://developer.hashicorp.com/terraform/language/resources/syntax#operation-timeouts): + +* `create` - (Default `5m`) +* `update` - (Default `5m`) +* `delete` - (Default `5m`) + +## Import + +A QuickSight Dashboard can be imported using the AWS account ID and dashboard ID separated by a comma (`,`) e.g., + +``` +$ terraform import aws_quicksight_dashboard.example 123456789012,example-id +```