diff --git a/aws/data_source_aws_lex_slot_type.go b/aws/data_source_aws_lex_slot_type.go new file mode 100644 index 000000000000..046aa36ab9e3 --- /dev/null +++ b/aws/data_source_aws_lex_slot_type.go @@ -0,0 +1,104 @@ +package aws + +import ( + "fmt" + "regexp" + "time" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/lexmodelbuildingservice" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" +) + +func dataSourceAwsLexSlotType() *schema.Resource { + return &schema.Resource{ + Read: dataSourceAwsLexSlotTypeRead, + + Schema: map[string]*schema.Schema{ + "checksum": { + Type: schema.TypeString, + Computed: true, + }, + "created_date": { + Type: schema.TypeString, + Computed: true, + }, + "description": { + Type: schema.TypeString, + Computed: true, + }, + "enumeration_value": { + Type: schema.TypeSet, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "synonyms": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + }, + "value": { + Type: schema.TypeString, + Computed: true, + }, + }, + }, + }, + "last_updated_date": { + Type: schema.TypeString, + Computed: true, + }, + "name": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.All( + validation.StringLenBetween(1, 100), + validation.StringMatch(regexp.MustCompile(`^([A-Za-z]_?)+$`), ""), + ), + }, + "value_selection_strategy": { + Type: schema.TypeString, + Computed: true, + }, + "version": { + Type: schema.TypeString, + Optional: true, + Default: LexSlotTypeVersionLatest, + ValidateFunc: validation.All( + validation.StringLenBetween(1, 64), + validation.StringMatch(regexp.MustCompile(`\$LATEST|[0-9]+`), ""), + ), + }, + }, + } +} + +func dataSourceAwsLexSlotTypeRead(d *schema.ResourceData, meta interface{}) error { + slotTypeName := d.Get("name").(string) + + conn := meta.(*AWSClient).lexmodelconn + + resp, err := conn.GetSlotType(&lexmodelbuildingservice.GetSlotTypeInput{ + Name: aws.String(slotTypeName), + Version: aws.String(d.Get("version").(string)), + }) + if err != nil { + return fmt.Errorf("error getting slot type %s: %w", slotTypeName, err) + } + + d.Set("checksum", resp.Checksum) + d.Set("created_date", resp.CreatedDate.Format(time.RFC3339)) + d.Set("description", resp.Description) + d.Set("enumeration_value", flattenLexEnumerationValues(resp.EnumerationValues)) + d.Set("last_updated_date", resp.LastUpdatedDate.Format(time.RFC3339)) + d.Set("name", resp.Name) + d.Set("value_selection_strategy", resp.ValueSelectionStrategy) + d.Set("version", resp.Version) + + d.SetId(slotTypeName) + + return nil +} diff --git a/aws/data_source_aws_lex_slot_type_test.go b/aws/data_source_aws_lex_slot_type_test.go new file mode 100644 index 000000000000..eb20426cd5ed --- /dev/null +++ b/aws/data_source_aws_lex_slot_type_test.go @@ -0,0 +1,84 @@ +package aws + +import ( + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" +) + +func TestAccDataSourceAwsLexSlotType_basic(t *testing.T) { + rName := acctest.RandStringFromCharSet(8, acctest.CharSetAlpha) + dataSourceName := "data.aws_lex_slot_type.test" + resourceName := "aws_lex_slot_type.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + Config: composeConfig( + testAccAwsLexSlotTypeConfig_basic(rName), + testAccDataSourceAwsLexSlotTypeConfig_basic(), + ), + Check: resource.ComposeAggregateTestCheckFunc( + resource.TestCheckResourceAttrPair(dataSourceName, "checksum", resourceName, "checksum"), + resource.TestCheckResourceAttrPair(dataSourceName, "description", resourceName, "description"), + resource.TestCheckResourceAttrPair(dataSourceName, "enumeration_value.#", resourceName, "enumeration_value.#"), + resource.TestCheckResourceAttrPair(dataSourceName, "name", resourceName, "name"), + resource.TestCheckResourceAttrPair(dataSourceName, "value_selection_strategy", resourceName, "value_selection_strategy"), + resource.TestCheckResourceAttrPair(dataSourceName, "version", resourceName, "version"), + resource.TestCheckResourceAttrPair(dataSourceName, "created_date", resourceName, "created_date"), + testAccCheckResourceAttrRfc3339(dataSourceName, "created_date"), + resource.TestCheckResourceAttrPair(dataSourceName, "last_updated_date", resourceName, "last_updated_date"), + testAccCheckResourceAttrRfc3339(dataSourceName, "last_updated_date"), + ), + }, + }, + }) +} + +func TestAccDataSourceAwsLexSlotType_withVersion(t *testing.T) { + rName := acctest.RandStringFromCharSet(8, acctest.CharSetAlpha) + dataSourceName := "data.aws_lex_slot_type.test" + resourceName := "aws_lex_slot_type.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + Config: composeConfig( + testAccAwsLexSlotTypeConfig_withVersion(rName), + testAccDataSourceAwsLexSlotTypeConfig_withVersion(), + ), + Check: resource.ComposeAggregateTestCheckFunc( + resource.TestCheckResourceAttrPair(dataSourceName, "checksum", resourceName, "checksum"), + resource.TestCheckResourceAttrPair(dataSourceName, "description", resourceName, "description"), + resource.TestCheckResourceAttrPair(dataSourceName, "name", resourceName, "name"), + resource.TestCheckResourceAttrPair(dataSourceName, "value_selection_strategy", resourceName, "value_selection_strategy"), + resource.TestCheckResourceAttrPair(dataSourceName, "version", resourceName, "version"), + testAccCheckResourceAttrRfc3339(dataSourceName, "created_date"), + testAccCheckResourceAttrRfc3339(dataSourceName, "last_updated_date"), + ), + }, + }, + }) +} + +func testAccDataSourceAwsLexSlotTypeConfig_basic() string { + return ` +data "aws_lex_slot_type" "test" { + name = "${aws_lex_slot_type.test.name}" +} +` +} + +func testAccDataSourceAwsLexSlotTypeConfig_withVersion() string { + return ` +data "aws_lex_slot_type" "test" { + name = "${aws_lex_slot_type.test.name}" + version = "1" +} +` +} diff --git a/aws/internal/service/lex/waiter/status.go b/aws/internal/service/lex/waiter/status.go new file mode 100644 index 000000000000..7edbfb6a3198 --- /dev/null +++ b/aws/internal/service/lex/waiter/status.go @@ -0,0 +1,34 @@ +package waiter + +import ( + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/lexmodelbuildingservice" + "github.com/hashicorp/aws-sdk-go-base/tfawserr" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" +) + +const ( + LexModelBuildingServiceStatusCreated = "Created" + LexModelBuildingServiceStatusNotFound = "NotFound" + LexModelBuildingServiceStatusUnknown = "Unknown" +) + +func LexSlotTypeStatus(conn *lexmodelbuildingservice.LexModelBuildingService, id string) resource.StateRefreshFunc { + return func() (interface{}, string, error) { + output, err := conn.GetSlotTypeVersions(&lexmodelbuildingservice.GetSlotTypeVersionsInput{ + Name: aws.String(id), + }) + if tfawserr.ErrCodeEquals(err, lexmodelbuildingservice.ErrCodeNotFoundException) { + return nil, LexModelBuildingServiceStatusNotFound, nil + } + if err != nil { + return nil, LexModelBuildingServiceStatusUnknown, err + } + + if output == nil || len(output.SlotTypes) == 0 { + return nil, LexModelBuildingServiceStatusNotFound, nil + } + + return output, LexModelBuildingServiceStatusCreated, nil + } +} diff --git a/aws/internal/service/lex/waiter/waiter.go b/aws/internal/service/lex/waiter/waiter.go new file mode 100644 index 000000000000..6cec69c7c208 --- /dev/null +++ b/aws/internal/service/lex/waiter/waiter.go @@ -0,0 +1,28 @@ +package waiter + +import ( + "time" + + "github.com/aws/aws-sdk-go/service/lexmodelbuildingservice" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" +) + +const ( + LexSlotTypeDeleteTimeout = 5 * time.Minute +) + +func LexSlotTypeDeleted(conn *lexmodelbuildingservice.LexModelBuildingService, slotTypeId string) (*lexmodelbuildingservice.GetSlotTypeVersionsOutput, error) { + stateChangeConf := &resource.StateChangeConf{ + Pending: []string{LexModelBuildingServiceStatusCreated}, + Target: []string{}, // An empty slice indicates that the resource is gone + Refresh: LexSlotTypeStatus(conn, slotTypeId), + Timeout: LexSlotTypeDeleteTimeout, + } + outputRaw, err := stateChangeConf.WaitForState() + + if v, ok := outputRaw.(*lexmodelbuildingservice.GetSlotTypeVersionsOutput); ok { + return v, err + } + + return nil, err +} diff --git a/aws/provider.go b/aws/provider.go index bc49427d3250..8bc6f07a5b48 100644 --- a/aws/provider.go +++ b/aws/provider.go @@ -280,6 +280,7 @@ func Provider() *schema.Provider { "aws_lambda_layer_version": dataSourceAwsLambdaLayerVersion(), "aws_launch_configuration": dataSourceAwsLaunchConfiguration(), "aws_launch_template": dataSourceAwsLaunchTemplate(), + "aws_lex_slot_type": dataSourceAwsLexSlotType(), "aws_mq_broker": dataSourceAwsMqBroker(), "aws_msk_cluster": dataSourceAwsMskCluster(), "aws_msk_configuration": dataSourceAwsMskConfiguration(), @@ -690,6 +691,7 @@ func Provider() *schema.Provider { "aws_lambda_provisioned_concurrency_config": resourceAwsLambdaProvisionedConcurrencyConfig(), "aws_launch_configuration": resourceAwsLaunchConfiguration(), "aws_launch_template": resourceAwsLaunchTemplate(), + "aws_lex_slot_type": resourceAwsLexSlotType(), "aws_licensemanager_association": resourceAwsLicenseManagerAssociation(), "aws_licensemanager_license_configuration": resourceAwsLicenseManagerLicenseConfiguration(), "aws_lightsail_domain": resourceAwsLightsailDomain(), diff --git a/aws/resource_aws_lex_slot_type.go b/aws/resource_aws_lex_slot_type.go new file mode 100644 index 000000000000..8affb6daad65 --- /dev/null +++ b/aws/resource_aws_lex_slot_type.go @@ -0,0 +1,305 @@ +package aws + +import ( + "fmt" + "regexp" + "time" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/lexmodelbuildingservice" + "github.com/hashicorp/aws-sdk-go-base/tfawserr" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" + "github.com/terraform-providers/terraform-provider-aws/aws/internal/service/lex/waiter" +) + +const ( + LexSlotTypeCreateTimeout = 1 * time.Minute + LexSlotTypeUpdateTimeout = 1 * time.Minute + LexSlotTypeDeleteTimeout = 5 * time.Minute + LexSlotTypeVersionLatest = "$LATEST" +) + +func resourceAwsLexSlotType() *schema.Resource { + return &schema.Resource{ + Create: resourceAwsLexSlotTypeCreate, + Read: resourceAwsLexSlotTypeRead, + Update: resourceAwsLexSlotTypeUpdate, + Delete: resourceAwsLexSlotTypeDelete, + Importer: &schema.ResourceImporter{ + State: schema.ImportStatePassthrough, + }, + + Timeouts: &schema.ResourceTimeout{ + Create: schema.DefaultTimeout(LexSlotTypeCreateTimeout), + Update: schema.DefaultTimeout(LexSlotTypeUpdateTimeout), + Delete: schema.DefaultTimeout(LexSlotTypeDeleteTimeout), + }, + + Schema: map[string]*schema.Schema{ + "checksum": { + Type: schema.TypeString, + Computed: true, + }, + "create_version": { + Type: schema.TypeBool, + Optional: true, + Default: false, + }, + "created_date": { + Type: schema.TypeString, + Computed: true, + }, + "description": { + Type: schema.TypeString, + Optional: true, + Default: "", + ValidateFunc: validation.StringLenBetween(0, 200), + }, + "enumeration_value": { + Type: schema.TypeSet, + Required: true, + MinItems: 1, + MaxItems: 10000, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "synonyms": { + Type: schema.TypeSet, + Optional: true, + MinItems: 1, + Elem: &schema.Schema{ + Type: schema.TypeString, + ValidateFunc: validation.StringLenBetween(1, 140), + }, + }, + "value": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.StringLenBetween(1, 140), + }, + }, + }, + }, + "last_updated_date": { + Type: schema.TypeString, + Computed: true, + }, + "name": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validation.All( + validation.StringLenBetween(1, 100), + validation.StringMatch(regexp.MustCompile(`^((AMAZON\.)_?|[A-Za-z]_?)+`), ""), + ), + }, + "value_selection_strategy": { + Type: schema.TypeString, + Optional: true, + Default: lexmodelbuildingservice.SlotValueSelectionStrategyOriginalValue, + ValidateFunc: validation.StringInSlice([]string{ + lexmodelbuildingservice.SlotValueSelectionStrategyOriginalValue, + lexmodelbuildingservice.SlotValueSelectionStrategyTopResolution, + }, false), + }, + "version": { + Type: schema.TypeString, + Computed: true, + }, + }, + } +} + +func resourceAwsLexSlotTypeCreate(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).lexmodelconn + name := d.Get("name").(string) + + input := &lexmodelbuildingservice.PutSlotTypeInput{ + CreateVersion: aws.Bool(d.Get("create_version").(bool)), + Description: aws.String(d.Get("description").(string)), + Name: aws.String(name), + ValueSelectionStrategy: aws.String(d.Get("value_selection_strategy").(string)), + } + + if v, ok := d.GetOk("enumeration_value"); ok { + input.EnumerationValues = expandLexEnumerationValues(v.(*schema.Set).List()) + } + + err := resource.Retry(d.Timeout(schema.TimeoutCreate), func() *resource.RetryError { + output, err := conn.PutSlotType(input) + + if tfawserr.ErrCodeEquals(err, lexmodelbuildingservice.ErrCodeConflictException) { + input.Checksum = output.Checksum + return resource.RetryableError(fmt.Errorf("%q slot type still creating, another operation is pending: %s", d.Id(), err)) + } + if err != nil { + return resource.NonRetryableError(err) + } + + return nil + }) + if err != nil { + return fmt.Errorf("error creating slot type %s: %w", name, err) + } + + d.SetId(name) + + return resourceAwsLexSlotTypeRead(d, meta) +} + +func resourceAwsLexSlotTypeRead(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).lexmodelconn + + resp, err := conn.GetSlotType(&lexmodelbuildingservice.GetSlotTypeInput{ + Name: aws.String(d.Id()), + Version: aws.String(LexSlotTypeVersionLatest), + }) + if tfawserr.ErrCodeEquals(err, lexmodelbuildingservice.ErrCodeNotFoundException) { + d.SetId("") + return nil + } + if err != nil { + return fmt.Errorf("error getting slot type %s: %w", d.Id(), err) + } + + d.Set("checksum", resp.Checksum) + d.Set("created_date", resp.CreatedDate.Format(time.RFC3339)) + d.Set("description", resp.Description) + d.Set("last_updated_date", resp.LastUpdatedDate.Format(time.RFC3339)) + d.Set("name", resp.Name) + d.Set("value_selection_strategy", resp.ValueSelectionStrategy) + + if resp.EnumerationValues != nil { + d.Set("enumeration_value", flattenLexEnumerationValues(resp.EnumerationValues)) + } + + version, err := getLatestLexSlotTypeVersion(conn, &lexmodelbuildingservice.GetSlotTypeVersionsInput{ + Name: aws.String(d.Id()), + }) + if err != nil { + return err + } + d.Set("version", version) + + return nil +} + +func getLatestLexSlotTypeVersion(conn *lexmodelbuildingservice.LexModelBuildingService, input *lexmodelbuildingservice.GetSlotTypeVersionsInput) (string, error) { + version := "$LATEST" + + for { + page, err := conn.GetSlotTypeVersions(input) + if err != nil { + return "", err + } + + // At least 1 version will always be returned. + if len(page.SlotTypes) == 1 { + break + } + + for _, slotType := range page.SlotTypes { + if *slotType.Version == LexSlotTypeVersionLatest { + continue + } + if *slotType.Version > version { + version = *slotType.Version + } + } + + if page.NextToken == nil { + break + } + input.NextToken = page.NextToken + } + + return version, nil +} + +func resourceAwsLexSlotTypeUpdate(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).lexmodelconn + + input := &lexmodelbuildingservice.PutSlotTypeInput{ + Checksum: aws.String(d.Get("checksum").(string)), + CreateVersion: aws.Bool(d.Get("create_version").(bool)), + Description: aws.String(d.Get("description").(string)), + Name: aws.String(d.Id()), + ValueSelectionStrategy: aws.String(d.Get("value_selection_strategy").(string)), + } + + if v, ok := d.GetOk("enumeration_value"); ok { + input.EnumerationValues = expandLexEnumerationValues(v.(*schema.Set).List()) + } + + err := resource.Retry(d.Timeout(schema.TimeoutUpdate), func() *resource.RetryError { + _, err := conn.PutSlotType(input) + + if tfawserr.ErrCodeEquals(err, lexmodelbuildingservice.ErrCodeConflictException) { + return resource.RetryableError(fmt.Errorf("%q: slot type still updating", d.Id())) + } + if err != nil { + return resource.NonRetryableError(err) + } + + return nil + }) + if err != nil { + return fmt.Errorf("error updating slot type %s: %w", d.Id(), err) + } + + return resourceAwsLexSlotTypeRead(d, meta) +} + +func resourceAwsLexSlotTypeDelete(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).lexmodelconn + + err := resource.Retry(d.Timeout(schema.TimeoutDelete), func() *resource.RetryError { + _, err := conn.DeleteSlotType(&lexmodelbuildingservice.DeleteSlotTypeInput{ + Name: aws.String(d.Id()), + }) + + if tfawserr.ErrCodeEquals(err, lexmodelbuildingservice.ErrCodeConflictException) { + return resource.RetryableError(fmt.Errorf("%q: there is a pending operation, slot type still deleting", d.Id())) + } + if err != nil { + return resource.NonRetryableError(err) + } + + return nil + }) + if err != nil { + return fmt.Errorf("error deleting slot type %s: %w", d.Id(), err) + } + + _, err = waiter.LexSlotTypeDeleted(conn, d.Id()) + + return err +} + +func flattenLexEnumerationValues(values []*lexmodelbuildingservice.EnumerationValue) (flattened []map[string]interface{}) { + for _, value := range values { + flattened = append(flattened, map[string]interface{}{ + "synonyms": flattenStringList(value.Synonyms), + "value": aws.StringValue(value.Value), + }) + } + + return +} + +func expandLexEnumerationValues(rawValues []interface{}) []*lexmodelbuildingservice.EnumerationValue { + enums := make([]*lexmodelbuildingservice.EnumerationValue, 0, len(rawValues)) + for _, rawValue := range rawValues { + value, ok := rawValue.(map[string]interface{}) + if !ok { + continue + } + + enums = append(enums, &lexmodelbuildingservice.EnumerationValue{ + Synonyms: expandStringSet(value["synonyms"].(*schema.Set)), + Value: aws.String(value["value"].(string)), + }) + } + return enums +} diff --git a/aws/resource_aws_lex_slot_type_test.go b/aws/resource_aws_lex_slot_type_test.go new file mode 100644 index 000000000000..d65b01e31b4b --- /dev/null +++ b/aws/resource_aws_lex_slot_type_test.go @@ -0,0 +1,454 @@ +package aws + +import ( + "fmt" + "testing" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/lexmodelbuildingservice" + "github.com/hashicorp/aws-sdk-go-base/tfawserr" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" + "github.com/terraform-providers/terraform-provider-aws/aws/internal/tfawsresource" +) + +func TestAccAwsLexSlotType_basic(t *testing.T) { + var v lexmodelbuildingservice.GetSlotTypeOutput + rName := "aws_lex_slot_type.test" + testSlotTypeID := "test_slot_type_" + acctest.RandStringFromCharSet(8, acctest.CharSetAlpha) + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAwsLexSlotTypeDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAwsLexSlotTypeConfig_basic(testSlotTypeID), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckAwsLexSlotTypeExists(rName, &v), + testAccCheckAwsLexSlotTypeNotExists(testSlotTypeID, "1"), + resource.TestCheckResourceAttr(rName, "create_version", "false"), + resource.TestCheckResourceAttr(rName, "description", ""), + resource.TestCheckResourceAttr(rName, "enumeration_value.#", "1"), + tfawsresource.TestCheckTypeSetElemNestedAttrs(rName, "enumeration_value.*", map[string]string{ + "value": "lilies", + }), + tfawsresource.TestCheckTypeSetElemAttr(rName, "enumeration_value.*.synonyms.*", "Lirium"), + tfawsresource.TestCheckTypeSetElemAttr(rName, "enumeration_value.*.synonyms.*", "Martagon"), + resource.TestCheckResourceAttr(rName, "name", testSlotTypeID), + resource.TestCheckResourceAttr(rName, "value_selection_strategy", lexmodelbuildingservice.SlotValueSelectionStrategyOriginalValue), + resource.TestCheckResourceAttrSet(rName, "checksum"), + resource.TestCheckResourceAttr(rName, "version", LexSlotTypeVersionLatest), + testAccCheckResourceAttrRfc3339(rName, "created_date"), + testAccCheckResourceAttrRfc3339(rName, "last_updated_date"), + ), + }, + { + ResourceName: rName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"create_version"}, + }, + }, + }) +} + +func TestAccAwsLexSlotType_createVersion(t *testing.T) { + var v lexmodelbuildingservice.GetSlotTypeOutput + rName := "aws_lex_slot_type.test" + testSlotTypeID := "test_slot_type_" + acctest.RandStringFromCharSet(8, acctest.CharSetAlpha) + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAwsLexSlotTypeDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAwsLexSlotTypeConfig_basic(testSlotTypeID), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckAwsLexSlotTypeExists(rName, &v), + testAccCheckAwsLexSlotTypeNotExists(testSlotTypeID, "1"), + resource.TestCheckResourceAttr(rName, "version", LexSlotTypeVersionLatest), + ), + }, + { + ResourceName: rName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"create_version"}, + }, + { + Config: testAccAwsLexSlotTypeConfig_withVersion(testSlotTypeID), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckAwsLexSlotTypeExists(rName, &v), + testAccCheckAwsLexSlotTypeExistsWithVersion(rName, "1", &v), + resource.TestCheckResourceAttr(rName, "version", "1"), + ), + }, + { + ResourceName: rName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"create_version"}, + }, + }, + }) +} + +func TestAccAwsLexSlotType_description(t *testing.T) { + var v lexmodelbuildingservice.GetSlotTypeOutput + rName := "aws_lex_slot_type.test" + testSlotTypeID := "test_slot_type_" + acctest.RandStringFromCharSet(8, acctest.CharSetAlpha) + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAwsLexSlotTypeDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAwsLexSlotTypeConfig_basic(testSlotTypeID), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckAwsLexSlotTypeExists(rName, &v), + resource.TestCheckResourceAttr(rName, "description", ""), + ), + }, + { + ResourceName: rName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"create_version"}, + }, + { + Config: testAccAwsLexSlotTypeUpdateConfig_description(testSlotTypeID), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckAwsLexSlotTypeExists(rName, &v), + resource.TestCheckResourceAttr(rName, "description", "Types of flowers to pick up"), + ), + }, + { + ResourceName: rName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"create_version"}, + }, + }, + }) +} + +func TestAccAwsLexSlotType_enumerationValues(t *testing.T) { + var v lexmodelbuildingservice.GetSlotTypeOutput + rName := "aws_lex_slot_type.test" + testSlotTypeID := "test_slot_type_" + acctest.RandStringFromCharSet(8, acctest.CharSetAlpha) + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAwsLexSlotTypeDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAwsLexSlotTypeConfig_basic(testSlotTypeID), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckAwsLexSlotTypeExists(rName, &v), + resource.TestCheckResourceAttr(rName, "enumeration_value.#", "1"), + ), + }, + { + ResourceName: rName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"create_version"}, + }, + { + Config: testAccAwsLexSlotTypeConfig_enumerationValues(testSlotTypeID), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckAwsLexSlotTypeExists(rName, &v), + resource.TestCheckResourceAttr(rName, "enumeration_value.#", "2"), + tfawsresource.TestCheckTypeSetElemNestedAttrs(rName, "enumeration_value.*", map[string]string{ + "value": "tulips", + }), + tfawsresource.TestCheckTypeSetElemAttr(rName, "enumeration_value.*.synonyms.*", "Eduardoregelia"), + tfawsresource.TestCheckTypeSetElemAttr(rName, "enumeration_value.*.synonyms.*", "Podonix"), + ), + }, + { + ResourceName: rName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"create_version"}, + }, + }, + }) +} + +func TestAccAwsLexSlotType_name(t *testing.T) { + var v lexmodelbuildingservice.GetSlotTypeOutput + rName := "aws_lex_slot_type.test" + testSlotTypeID1 := "test_slot_type_" + acctest.RandStringFromCharSet(8, acctest.CharSetAlpha) + testSlotTypeID2 := "test_slot_type_" + acctest.RandStringFromCharSet(8, acctest.CharSetAlpha) + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAwsLexSlotTypeDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAwsLexSlotTypeConfig_basic(testSlotTypeID1), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckAwsLexSlotTypeExists(rName, &v), + resource.TestCheckResourceAttr(rName, "name", testSlotTypeID1), + ), + }, + { + ResourceName: rName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"create_version"}, + }, + { + Config: testAccAwsLexSlotTypeConfig_basic(testSlotTypeID2), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckAwsLexSlotTypeExists(rName, &v), + resource.TestCheckResourceAttr(rName, "name", testSlotTypeID2), + ), + }, + { + ResourceName: rName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"create_version"}, + }, + }, + }) +} + +func TestAccAwsLexSlotType_valueSelectionStrategy(t *testing.T) { + var v lexmodelbuildingservice.GetSlotTypeOutput + rName := "aws_lex_slot_type.test" + testSlotTypeID := "test_slot_type_" + acctest.RandStringFromCharSet(8, acctest.CharSetAlpha) + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAwsLexSlotTypeDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAwsLexSlotTypeConfig_basic(testSlotTypeID), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckAwsLexSlotTypeExists(rName, &v), + resource.TestCheckResourceAttr(rName, "value_selection_strategy", lexmodelbuildingservice.SlotValueSelectionStrategyOriginalValue), + ), + }, + { + ResourceName: rName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"create_version"}, + }, + { + Config: testAccAwsLexSlotTypeConfig_valueSelectionStrategy(testSlotTypeID), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckAwsLexSlotTypeExists(rName, &v), + resource.TestCheckResourceAttr(rName, "value_selection_strategy", lexmodelbuildingservice.SlotValueSelectionStrategyTopResolution), + ), + }, + { + ResourceName: rName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"create_version"}, + }, + }, + }) +} + +func TestAccAwsLexSlotType_disappears(t *testing.T) { + var v lexmodelbuildingservice.GetSlotTypeOutput + rName := "aws_lex_slot_type.test" + testSlotTypeID := "test_slot_type_" + acctest.RandStringFromCharSet(8, acctest.CharSetAlpha) + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAwsLexSlotTypeDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAwsLexSlotTypeConfig_basic(testSlotTypeID), + Check: resource.ComposeTestCheckFunc( + testAccCheckAwsLexSlotTypeExists(rName, &v), + testAccCheckResourceDisappears(testAccProvider, resourceAwsLexSlotType(), rName), + ), + ExpectNonEmptyPlan: true, + }, + }, + }) +} + +func testAccCheckAwsLexSlotTypeExistsWithVersion(rName, slotTypeVersion string, output *lexmodelbuildingservice.GetSlotTypeOutput) resource.TestCheckFunc { + return func(s *terraform.State) error { + rs, ok := s.RootModule().Resources[rName] + if !ok { + return fmt.Errorf("Not found: %s", rName) + } + + if rs.Primary.ID == "" { + return fmt.Errorf("No Lex slot type ID is set") + } + + var err error + conn := testAccProvider.Meta().(*AWSClient).lexmodelconn + + output, err = conn.GetSlotType(&lexmodelbuildingservice.GetSlotTypeInput{ + Name: aws.String(rs.Primary.ID), + Version: aws.String(slotTypeVersion), + }) + if tfawserr.ErrCodeEquals(err, lexmodelbuildingservice.ErrCodeNotFoundException) { + return fmt.Errorf("error slot type %q version %s not found", rs.Primary.ID, slotTypeVersion) + } + if err != nil { + return fmt.Errorf("error getting slot type %q version %s: %w", rs.Primary.ID, slotTypeVersion, err) + } + + return nil + } +} + +func testAccCheckAwsLexSlotTypeExists(rName string, output *lexmodelbuildingservice.GetSlotTypeOutput) resource.TestCheckFunc { + return testAccCheckAwsLexSlotTypeExistsWithVersion(rName, LexSlotTypeVersionLatest, output) +} + +func testAccCheckAwsLexSlotTypeNotExists(slotTypeName, slotTypeVersion string) resource.TestCheckFunc { + return func(s *terraform.State) error { + conn := testAccProvider.Meta().(*AWSClient).lexmodelconn + + _, err := conn.GetSlotType(&lexmodelbuildingservice.GetSlotTypeInput{ + Name: aws.String(slotTypeName), + Version: aws.String(slotTypeVersion), + }) + if tfawserr.ErrCodeEquals(err, lexmodelbuildingservice.ErrCodeNotFoundException) { + return nil + } + if err != nil { + return fmt.Errorf("error getting slot type %s version %s: %s", slotTypeName, slotTypeVersion, err) + } + + return fmt.Errorf("error slot type %s version %s exists", slotTypeName, slotTypeVersion) + } +} + +func testAccCheckAwsLexSlotTypeDestroy(s *terraform.State) error { + conn := testAccProvider.Meta().(*AWSClient).lexmodelconn + + for _, rs := range s.RootModule().Resources { + if rs.Type != "aws_lex_slot_type" { + continue + } + + output, err := conn.GetSlotTypeVersions(&lexmodelbuildingservice.GetSlotTypeVersionsInput{ + Name: aws.String(rs.Primary.ID), + }) + if tfawserr.ErrCodeEquals(err, lexmodelbuildingservice.ErrCodeNotFoundException) { + continue + } + if err != nil { + return err + } + + if output == nil || len(output.SlotTypes) == 0 { + return nil + } + + return fmt.Errorf("Lex slot type %q still exists", rs.Primary.ID) + } + + return nil +} + +func testAccAwsLexSlotTypeConfig_basic(rName string) string { + return fmt.Sprintf(` +resource "aws_lex_slot_type" "test" { + name = "%s" + enumeration_value { + synonyms = [ + "Lirium", + "Martagon", + ] + value = "lilies" + } +} +`, rName) +} + +func testAccAwsLexSlotTypeConfig_withVersion(rName string) string { + return fmt.Sprintf(` +resource "aws_lex_slot_type" "test" { + name = "%s" + enumeration_value { + synonyms = [ + "Lirium", + "Martagon", + ] + value = "lilies" + } + create_version = true +} +`, rName) +} + +func testAccAwsLexSlotTypeUpdateConfig_description(rName string) string { + return fmt.Sprintf(` +resource "aws_lex_slot_type" "test" { + description = "Types of flowers to pick up" + name = "%s" + enumeration_value { + synonyms = [ + "Lirium", + "Martagon", + ] + value = "lilies" + } +} +`, rName) +} + +func testAccAwsLexSlotTypeConfig_enumerationValues(rName string) string { + return fmt.Sprintf(` +resource "aws_lex_slot_type" "test" { + enumeration_value { + synonyms = [ + "Lirium", + "Martagon", + ] + + value = "lilies" + } + + enumeration_value { + synonyms = [ + "Eduardoregelia", + "Podonix", + ] + + value = "tulips" + } + + name = "%s" +} +`, rName) +} + +func testAccAwsLexSlotTypeConfig_valueSelectionStrategy(rName string) string { + return fmt.Sprintf(` +resource "aws_lex_slot_type" "test" { + name = "%s" + value_selection_strategy = "TOP_RESOLUTION" + enumeration_value { + synonyms = [ + "Lirium", + "Martagon", + ] + value = "lilies" + } +} +`, rName) +} diff --git a/website/allowed-subcategories.txt b/website/allowed-subcategories.txt index 15d8db21d040..7a46e947c4c5 100644 --- a/website/allowed-subcategories.txt +++ b/website/allowed-subcategories.txt @@ -65,6 +65,7 @@ Kinesis Data Analytics v2 (SQL and Java Applications) Kinesis Firehose Kinesis Video Lambda +Lex License Manager Lightsail MQ diff --git a/website/docs/d/lex_slot_type.html.markdown b/website/docs/d/lex_slot_type.html.markdown new file mode 100644 index 000000000000..6d799d4d5645 --- /dev/null +++ b/website/docs/d/lex_slot_type.html.markdown @@ -0,0 +1,46 @@ +--- +subcategory: "Lex" +layout: "aws" +page_title: "AWS: aws_lex_slot_type" +description: |- + Provides details about a specific Amazon Lex Slot Type +--- + +# Data Source: aws_lex_slot_type + +Provides details about a specific Amazon Lex Slot Type. + +## Example Usage + +```hcl +data "aws_lex_slot_type" "flower_types" { + name = "FlowerTypes" + version = "1" +} +``` + +## Argument Reference + +The following arguments are supported: + +* `name` - (Required) The name of the slot type. The name is case sensitive. +* `version` - (Optional) The version of the slot type. + +## Attributes Reference + +The following attributes are exported. + +* `checksum` - Checksum identifying the version of the slot type that was created. The checksum is +not included as an argument because the resource will add it automatically when updating the slot type. +* `created_date` - The date when the slot type version was created. +* `description` - A description of the slot type. +* `enumeration_value` - A set of EnumerationValue objects that defines the values that +the slot type can take. Each value can have a set of synonyms, which are additional values that help +train the machine learning model about the values that it resolves for a slot. +* `last_updated_date` - The date when the $LATEST version of this slot type was updated. +* `name` - The name of the slot type. The name is not case sensitive. +* `value_selection_strategy` - Determines the slot resolution strategy that Amazon Lex +uses to return slot type values. `ORIGINAL_VALUE` returns the value entered by the user if the user +value is similar to the slot value. `TOP_RESOLUTION` returns the first value in the resolution list +if there is a resolution list for the slot, otherwise null is returned. +* `version` - The version of the slot type. diff --git a/website/docs/r/lex_slot_type.html.markdown b/website/docs/r/lex_slot_type.html.markdown new file mode 100644 index 000000000000..09b68dbe4c26 --- /dev/null +++ b/website/docs/r/lex_slot_type.html.markdown @@ -0,0 +1,97 @@ +--- +subcategory: "Lex" +layout: "aws" +page_title: "AWS: aws_lex_slot_type" +description: |- + Provides details about a specific Amazon Lex Slot Type +--- + +# Resource: aws_lex_slot_type + +Provides an Amazon Lex Slot Type resource. For more information see +[Amazon Lex: How It Works](https://docs.aws.amazon.com/lex/latest/dg/how-it-works.html) + +## Example Usage + +```hcl +resource "aws_lex_slot_type" "flower_types" { + create_version = false + description = "Types of flowers to order" + + enumeration_value { + synonyms = [ + "Lirium", + "Martagon", + ] + + value = "lilies" + } + + enumeration_value { + synonyms = [ + "Eduardoregelia", + "Podonix", + ] + + value = "tulips" + } + + name = "FlowerTypes" + value_selection_strategy = "ORIGINAL_VALUE" +} +``` + +## Argument Reference + +The following arguments are supported: + +* `enumeration_value` - (Required) A list of EnumerationValue objects that defines the values that +the slot type can take. Each value can have a list of synonyms, which are additional values that help +train the machine learning model about the values that it resolves for a slot. Attributes are +documented under [enumeration_value](#enumeration_value-1). +* `name` - (Required) The name of the slot type. The name is not case sensitive. +* `create_version` - (Optional) +Determines if a new slot type version is created when the initial resource is created and on each +update. Defaults to true. +* `description` - (Optional) A description of the slot type. +* `value_selection_strategy` - (Optional) Determines the slot resolution strategy that Amazon Lex +uses to return slot type values. `ORIGINAL_VALUE` returns the value entered by the user if the user +value is similar to the slot value. `TOP_RESOLUTION` returns the first value in the resolution list +if there is a resolution list for the slot, otherwise null is returned. + +### enumeration_value + +Each slot type can have a set of values. Each enumeration value represents a value the slot type +can take. + +For example, a pizza ordering bot could have a slot type that specifies the type of crust that the +pizza should have. The slot type could include the values: thick, thin, stuffed. + +* `synonyms` - (Optional) Additional values related to the slot type value. +* `value` - (Required) The value of the slot type. + +### Timeouts + +The `timeouts` block allows you to specify [timeouts](https://www.terraform.io/docs/configuration/resources.html#timeouts) for certain actions: + +* `create` - (Defaults to 1 min) Used when creating the slot type +* `update` - (Defaults to 1 min) Used when updating the slot type +* `delete` - (Defaults to 5 mins) Used when deleting the slot type + +## Attributes Reference + +The following attributes are exported in addition to the arguments listed above: + +* `checksum` - Checksum identifying the version of the slot type that was created. The checksum is +not included as an argument because the resource will add it automatically when updating the slot type. +* `created_date` - The date when the slot type version was created. +* `last_updated_date` - The date when the `$LATEST` version of this slot type was updated. +* `version` - The version of the slot type. + +## Import + +Slot types can be imported using their name. + +``` +$ terraform import aws_lex_slot_type.flower_types FlowerTypes +```