Skip to content

Commit

Permalink
aws_quicksight_data_set: Add support for configuring refresh properties
Browse files Browse the repository at this point in the history
  • Loading branch information
g-dx committed Apr 17, 2023
1 parent cea654b commit ccf9587
Show file tree
Hide file tree
Showing 4 changed files with 266 additions and 5 deletions.
3 changes: 3 additions & 0 deletions .changelog/30744.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
```release-note:enhancement
aws_quicksight_data_set: Add support for configuring refresh properties
```
127 changes: 122 additions & 5 deletions internal/service/quicksight/data_set.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (
"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/schema"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation"
"github.com/hashicorp/terraform-provider-aws/internal/conns"
Expand Down Expand Up @@ -279,10 +280,68 @@ func ResourceDataSet() *schema.Resource {
},
},
},
"refresh_properties": {
Type: schema.TypeList,
Optional: true,
MaxItems: 1,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"refresh_configuration": {
Type: schema.TypeList,
Required: true,
MaxItems: 1,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"incremental_refresh": {
Type: schema.TypeList,
Required: true,
MaxItems: 1,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"lookback_window": {
Type: schema.TypeList,
Required: true,
MaxItems: 1,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"column_name": {
Type: schema.TypeString,
Required: true,
},
"size": {
Type: schema.TypeInt,
Required: true,
},
"size_unit": {
Type: schema.TypeString,
Required: true,
ValidateFunc: validation.StringInSlice(quicksight.LookbackWindowSizeUnit_Values(), false),
},
},
},
},
},
},
},
},
},
},
},
},
},
names.AttrTags: tftags.TagsSchema(),
names.AttrTagsAll: tftags.TagsSchemaComputed(),
},
CustomizeDiff: verify.SetTagsDiff,
CustomizeDiff: customdiff.All(
func(_ context.Context, diff *schema.ResourceDiff, _ interface{}) error {
mode := diff.Get("import_mode").(string)
if v, ok := diff.Get("refresh_properties").([]interface{}); ok && v != nil && len(v) > 0 && mode == "DIRECT_QUERY" {
return fmt.Errorf("refresh_properties cannot be set when import_mode is 'DIRECT_QUERY'")
}
return nil
},
verify.SetTagsDiff,
),
}
}

Expand Down Expand Up @@ -809,6 +868,19 @@ func resourceDataSetCreate(ctx context.Context, d *schema.ResourceData, meta int
return diag.Errorf("error creating QuickSight Data Set: %s", err)
}

if v, ok := d.GetOk("refresh_properties"); ok && len(v.([]interface{})) > 0 && v.([]interface{})[0] != nil {
input := &quicksight.PutDataSetRefreshPropertiesInput{
AwsAccountId: aws.String(awsAccountId),
DataSetId: aws.String(dataSetID),
DataSetRefreshProperties: expandDataSetRefreshProperties(v.([]interface{})),
}

_, err := conn.PutDataSetRefreshPropertiesWithContext(ctx, input)
if err != nil {
return diag.Errorf("error putting QuickSight Data Set Refresh Properties: %s", err)
}
}

return resourceDataSetRead(ctx, d, meta)
}

Expand Down Expand Up @@ -893,13 +965,29 @@ func resourceDataSetRead(ctx context.Context, d *schema.ResourceData, meta inter
if err := d.Set("permissions", flattenPermissions(permsResp.Permissions)); err != nil {
return diag.Errorf("error setting permissions: %s", err)
}

propsResp, err := conn.DescribeDataSetRefreshPropertiesWithContext(ctx, &quicksight.DescribeDataSetRefreshPropertiesInput{
AwsAccountId: aws.String(awsAccountId),
DataSetId: aws.String(dataSetId),
})

if !tfawserr.ErrCodeEquals(err, quicksight.ErrCodeResourceNotFoundException) {
return diag.Errorf("error describing QuickSight Data Set Refresh Properties (%s): %s", d.Id(), err)
}

if err == nil {
if err := d.Set("refresh_properties", flattenRefreshProperties(propsResp.DataSetRefreshProperties)); err != nil {
return diag.Errorf("error setting refresh properties: %s", err)
}
}

return nil
}

func resourceDataSetUpdate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
conn := meta.(*conns.AWSClient).QuickSightConn()

if d.HasChangesExcept("permissions", "tags", "tags_all") {
if d.HasChangesExcept("permissions", "tags", "tags_all", "refresh_properties") {
awsAccountId, dataSetId, err := ParseDataSetID(d.Id())
if err != nil {
return diag.FromErr(err)
Expand Down Expand Up @@ -979,6 +1067,35 @@ func resourceDataSetUpdate(ctx context.Context, d *schema.ResourceData, meta int
}
}

if d.HasChange("refresh_properties") {
awsAccountId, dataSetId, err := ParseDataSetID(d.Id())
if err != nil {
return diag.FromErr(err)
}

oldraw, newraw := d.GetChange("refresh_properties")
old := oldraw.([]interface{})
new := newraw.([]interface{})
if len(old) == 1 && len(new) == 0 {
_, err := conn.DeleteDataSetRefreshPropertiesWithContext(ctx, &quicksight.DeleteDataSetRefreshPropertiesInput{
AwsAccountId: aws.String(awsAccountId),
DataSetId: aws.String(dataSetId),
})
if err != nil {
return diag.Errorf("error deleting QuickSight Data Set Refresh Properties (%s): %s", d.Id(), err)
}
} else {
_, err = conn.PutDataSetRefreshPropertiesWithContext(ctx, &quicksight.PutDataSetRefreshPropertiesInput{
AwsAccountId: aws.String(awsAccountId),
DataSetId: aws.String(dataSetId),
DataSetRefreshProperties: expandDataSetRefreshProperties(d.Get("refresh_properties").([]interface{})),
})
if err != nil {
return diag.Errorf("error updating QuickSight Data Set Refresh Properties (%s): %s", d.Id(), err)
}
}
}

return resourceDataSetRead(ctx, d, meta)
}

Expand Down Expand Up @@ -1396,8 +1513,8 @@ func expandDataSetProjectOperation(tfList []interface{}) *quicksight.ProjectOper
}

projectOperation := &quicksight.ProjectOperation{}
if v, ok := tfMap["projected_columns"].([]string); ok && len(v) > 0 {
projectOperation.ProjectedColumns = aws.StringSlice(v)
if v, ok := tfMap["projected_columns"].([]interface{}); ok && len(v) > 0 {
projectOperation.ProjectedColumns = flex.ExpandStringList(v)
}

return projectOperation
Expand Down Expand Up @@ -1976,7 +2093,7 @@ func flattenCastColumnTypeOperation(apiObject *quicksight.CastColumnTypeOperatio
tfMap["column_name"] = aws.StringValue(apiObject.ColumnName)
}
if apiObject.Format != nil {
tfMap["cast_column_type_operation"] = aws.StringValue(apiObject.ColumnName)
tfMap["format"] = aws.StringValue(apiObject.Format)
}
if apiObject.NewColumnType != nil {
tfMap["new_column_type"] = aws.StringValue(apiObject.NewColumnType)
Expand Down
121 changes: 121 additions & 0 deletions internal/service/quicksight/data_set_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -340,6 +340,41 @@ func TestAccQuickSightDataSet_rowLevelPermissionTagConfiguration(t *testing.T) {
})
}

func TestAccQuickSightDataSet_refreshProperties(t *testing.T) {
ctx := acctest.Context(t)
var dataSet quicksight.DataSet
resourceName := "aws_quicksight_data_set.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: testAccCheckDataSetDestroy(ctx),
Steps: []resource.TestStep{
{
Config: testAccDataSetConfigRefreshProperties(rId, rName),
Check: resource.ComposeTestCheckFunc(
testAccCheckDataSetExists(ctx, resourceName, &dataSet),
resource.TestCheckResourceAttr(resourceName, "refresh_properties.#", "1"),
resource.TestCheckResourceAttr(resourceName, "refresh_properties.0.refresh_configuration.#", "1"),
resource.TestCheckResourceAttr(resourceName, "refresh_properties.0.refresh_configuration.0.incremental_refresh.#", "1"),
resource.TestCheckResourceAttr(resourceName, "refresh_properties.0.refresh_configuration.0.incremental_refresh.0.lookback_window.#", "1"),
resource.TestCheckResourceAttr(resourceName, "refresh_properties.0.refresh_configuration.0.incremental_refresh.0.lookback_window.0.column_name", "column1"),
resource.TestCheckResourceAttr(resourceName, "refresh_properties.0.refresh_configuration.0.incremental_refresh.0.lookback_window.0.size", "1"),
resource.TestCheckResourceAttr(resourceName, "refresh_properties.0.refresh_configuration.0.incremental_refresh.0.lookback_window.0.size_unit", "DAY"),
),
},
{
ResourceName: resourceName,
ImportState: true,
ImportStateVerify: true,
},
},
})
}

func TestAccQuickSightDataSet_tags(t *testing.T) {
ctx := acctest.Context(t)
var dataSet quicksight.DataSet
Expand Down Expand Up @@ -787,6 +822,92 @@ resource "aws_quicksight_data_set" "test" {
`, rId, rName))
}

func testAccDataSetConfigRefreshProperties(rId, rName string) string {
// NOTE: Must use Athena data source here as incremental refresh is not supported by S3
return acctest.ConfigCompose(
testAccBaseDataSourceConfig(rName),
fmt.Sprintf(`
resource "aws_glue_catalog_database" "test" {
name = %[2]q
}
resource "aws_glue_catalog_table" "test" {
name = %[2]q
database_name = aws_glue_catalog_database.test.name
table_type = "EXTERNAL_TABLE"
parameters = {
EXTERNAL = "TRUE"
classification = "json"
}
storage_descriptor {
location = "s3://${aws_s3_bucket.test.id}/data/"
input_format = "org.apache.hadoop.mapred.TextInputFormat"
output_format = "org.apache.hadoop.hive.ql.io.HiveIgnoreKeyTextOutputFormat"
ser_de_info {
name = "jsonserde"
serialization_library = "org.openx.data.jsonserde.JsonSerDe"
parameters = {
"serialization.format" = "1"
}
}
columns {
name = "column1"
type = "date"
}
}
}
resource "aws_quicksight_data_source" "test" {
data_source_id = %[1]q
name = %[2]q
type = "ATHENA"
parameters {
athena {
work_group = "primary"
}
}
ssl_properties {
disable_ssl = false
}
}
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
relational_table {
data_source_arn = aws_quicksight_data_source.test.arn
catalog = "AwsDataCatalog"
schema = aws_glue_catalog_database.test.name
name = aws_glue_catalog_table.test.name
input_columns {
name = "column1"
type = "DATETIME"
}
}
}
refresh_properties {
refresh_configuration {
incremental_refresh {
lookback_window {
column_name = "column1"
size = 1
size_unit = "DAY"
}
}
}
}
}
`, rId, rName))
}

func testAccDataSetConfigTags1(rId, rName, key1, value1 string) string {
return acctest.ConfigCompose(
testAccDataSetConfigBase(rId, rName),
Expand Down
20 changes: 20 additions & 0 deletions website/docs/r/quicksight_data_set.html.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,7 @@ The following arguments are optional:
* `permissions` - (Optional) A set of resource permissions on the data source. Maximum of 64 items. See [permissions](#permissions).
* `row_level_permission_data_set` - (Optional) The row-level security configuration for the data that you want to create. See [row_level_permission_data_set](#row_level_permission_data_set).
* `row_level_permission_tag_configuration` - (Optional) The configuration of tags on a dataset to set row-level security. Row-level security tags are currently supported for anonymous embedding only. See [row_level_permission_tag_configuration](#row_level_permission_tag_configuration).
* `refresh_properties` - (Optional) The refresh properties for the data set. **NOTE**: Only valid when `import_mode` is set to `SPICE`. See [refresh_properties](#refresh_properties).
* `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.

### physical_table_map
Expand Down Expand Up @@ -361,6 +362,25 @@ For a `physical_table_map` item to be valid, only one of `custom_sql`, `relation
* `tag_rules` - (Required) A set of rules associated with row-level security, such as the tag names and columns that they are assigned to. See [tag_rules](#tag_rules).
* `status` - (Optional) The status of row-level security tags. If enabled, the status is `ENABLED`. If disabled, the status is `DISABLED`.

### refresh_properties

* `refresh_configuration` - (Required) The refresh configuration for the data set. See [refresh_configuration](#refresh_configuration).

### refresh_configuration

* `incremental_refresh` - (Required) The incremental refresh for the data set. See [incremental_refresh](#incremental_refresh).

### incremental_refresh

* `lookback_window` - (Required) The lookback window setup for an incremental refresh configuration. See [lookback_window](#lookback_window).

### lookback_window

* `column_name` - (Required) The name of the lookback window column.
* `size` - (Required) The lookback window column size.
* `size_unit` - (Required) The size unit that is used for the lookback window column. Valid values for this structure are `HOUR`, `DAY`, and `WEEK`.


### tag_rules

* `column_name` - (Required) Column name that a tag key is assigned to.
Expand Down

0 comments on commit ccf9587

Please sign in to comment.