From 255be14331deb1c10d78dc7a84d76435f8c0fa03 Mon Sep 17 00:00:00 2001 From: Sharon Nam Date: Tue, 28 Nov 2023 14:50:52 -0800 Subject: [PATCH 01/42] initial commit --- internal/service/lexv2models/slot.go | 1193 +++++++++++++++++ internal/service/lexv2models/slot_test.go | 334 +++++ website/docs/r/lexv2models_slot.html.markdown | 69 + 3 files changed, 1596 insertions(+) create mode 100644 internal/service/lexv2models/slot.go create mode 100644 internal/service/lexv2models/slot_test.go create mode 100644 website/docs/r/lexv2models_slot.html.markdown diff --git a/internal/service/lexv2models/slot.go b/internal/service/lexv2models/slot.go new file mode 100644 index 00000000000..272068a9b45 --- /dev/null +++ b/internal/service/lexv2models/slot.go @@ -0,0 +1,1193 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package lexv2models + +import ( + "context" + "errors" + "time" + + "github.com/aws/aws-sdk-go-v2/aws" + awstypes "github.com/aws/aws-sdk-go-v2/service/lexmodelsv2/types" + "github.com/hashicorp/terraform-plugin-framework-timeouts/resource/timeouts" + "github.com/hashicorp/terraform-plugin-framework-validators/listvalidator" + "github.com/hashicorp/terraform-plugin-framework/attr" + "github.com/hashicorp/terraform-plugin-framework/datasource/schema" + "github.com/hashicorp/terraform-plugin-framework/diag" + "github.com/hashicorp/terraform-plugin-framework/path" + "github.com/hashicorp/terraform-plugin-framework/provider/schema" + "github.com/hashicorp/terraform-plugin-framework/resource" + "github.com/hashicorp/terraform-plugin-framework/resource/schema" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" + "github.com/hashicorp/terraform-plugin-framework/schema/validator" + "github.com/hashicorp/terraform-plugin-framework/types" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/retry" + "github.com/hashicorp/terraform-provider-aws/internal/create" + "github.com/hashicorp/terraform-provider-aws/internal/enum" + "github.com/hashicorp/terraform-provider-aws/internal/framework" + "github.com/hashicorp/terraform-provider-aws/internal/framework/flex" + "github.com/hashicorp/terraform-provider-aws/internal/tfresource" + "github.com/hashicorp/terraform-provider-aws/names" +) + +// @FrameworkResource(name="Slot") +func newResourceSlot(_ context.Context) (resource.ResourceWithConfigure, error) { + r := &resourceSlot{} + + r.SetDefaultCreateTimeout(30 * time.Minute) + r.SetDefaultUpdateTimeout(30 * time.Minute) + r.SetDefaultDeleteTimeout(30 * time.Minute) + + return r, nil +} + +const ( + ResNameSlot = "Slot" +) + +type resourceSlot struct { + framework.ResourceWithConfigure + framework.WithTimeouts +} + +func (r *resourceSlot) Metadata(_ context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { + resp.TypeName = "aws_lexv2models_slot" +} + +// TIP: ==== SCHEMA ==== +// In the schema, add each of the attributes in snake case (e.g., +// delete_automated_backups). +// +// Formatting rules: +// * Alphabetize attributes to make them easier to find. +// * Do not add a blank line between attributes. +// +// Attribute basics: +// - If a user can provide a value ("configure a value") for an +// attribute (e.g., instances = 5), we call the attribute an +// "argument." +// - You change the way users interact with attributes using: +// - Required +// - Optional +// - Computed +// - There are only four valid combinations: +// +// 1. Required only - the user must provide a value +// Required: true, +// +// 2. Optional only - the user can configure or omit a value; do not +// use Default or DefaultFunc +// +// Optional: true, +// +// 3. Computed only - the provider can provide a value but the user +// cannot, i.e., read-only +// +// Computed: true, +// +// 4. Optional AND Computed - the provider or user can provide a value; +// use this combination if you are using Default +// +// Optional: true, +// Computed: true, +// +// You will typically find arguments in the input struct +// (e.g., CreateDBInstanceInput) for the create operation. Sometimes +// they are only in the input struct (e.g., ModifyDBInstanceInput) for +// the modify operation. +// +// For more about schema options, visit +// https://developer.hashicorp.com/terraform/plugin/framework/handling-data/schemas?page=schemas +func (r *resourceSlot) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { + slotValueOverrideO := types.ObjectType{ + AttrTypes: map[string]attr.Type{ + "shape": types.StringType, + "value": types.ObjectType{ + AttrTypes: map[string]attr.Types{ + "interpreted_value" : types.StringType, + }, + }, + }, + } + slotValueOverrideO.AttrTypes["values"] = slotValueOverrideO + + messageNBO := stypes.Object{ + Blocks: map[string]schema.Block{ + "custom_playload": schema.ListedNestedBlock{ + Validators: []validator.List{ + listvalidator.SizeAtMost(1), + }, + NestedObject: schema.NestedBlockObject{ + Attributes: map[string]schema.Attribute{ + "value": schema.StringAttribute{ + Required: true, + }, + }, + }, + }, + "image_response_card": schema.ListNestedBlock{ + Validators: []validator.List{ + listvalidator.SizeAtMost(1), + }, + NestedObject: schema.NestedBlockObject{ + Attributes: map[string]schema.Attribute{ + "image_url": schema.StringAttribute{ + Optional: true, + }, + "subtitle": schema.StringAttribute{ + Optional: true, + }, + "title": schema.StringAttribute{ + Required: true, + }, + }, + Blocks: map[string]schema.Block{ + "button": schema.ListNestedBlock{ + Nestedobject: schema.NestedBlockObject{ + Attributes: map[string]schema.Attribute{ + + } + } + } + } + }, + } + + } + "message": schema.NestedBlockObject{ + Required: true, + Blocks: map[string]schema.Block{ + + + Blocks: map[string]schema.Block{ + "buttons": schema.ListNestedBlock{ + Validators: []validator.List{ + listvalidator.SizeAtMost(1), + }, + NestedObject: schema.NestedBlockObject{ + Attributes: map[strings]schema.Attribute{ + "text": schema.StringAttribute{ + Required: true, + }, + "value": schema.StringAttribute{ + Required: true, + }, + }, + }, + }, + }, + }, + "plain_text_message": schema.SingleNestedBlock{ + Attributes: map[strings]schema.Attribute{ + "value": schema.StringAttribute{ + Required: true, + }, + }, + }, + "ssml_message": schema.SingleNestedBlock{ + Attributes: map[strings]schema.Attribute{ + "value": schema.StringAttribute{ + Required: true, + }, + }, + }, + }, + }, + } + + resp.Schema = schema.Schema{ + Attributes: map[string]schema.Attribute{ + // "arn": framework.ARNAttributeComputedOnly(), + "bot_id": schema.StringAttribute{ + Required: true, + PlanModifiers: []planmodifier.String{ + stringplanmodifier.RequiresReplace(), + }, + }, + "bot_version": schema.StringAttribute{ + Required: true, + PlanModifiers: []planmodifier.String{ + stringplanmodifier.RequiresReplace(), + }, + }, + "description": schema.StringAttribute{ + Optional: true, + }, + "intent_id": schema.StringAttribute{ + Required: true, + PlanModifiers: []planmodifier.String{ + stringplanmodifier.RequiresReplace(), + }, + }, + "locale_id": schema.StringAttribute{ + Required: true, + PlanModifiers: []planmodifier.String{ + stringplanmodifier.RequiresReplace(), + }, + }, + // "id": framework.IDAttribute(), + "name": schema.StringAttribute{ + Required: true, + PlanModifiers: []planmodifier.String{ + stringplanmodifier.RequiresReplace(), + }, + }, + "slot_type_id": schema.StringAttribute{ + Optional: true, + }, + }, + Blocks: map[string]schema.Block{ + "value_elicitation_setting": schema.SingleNestedBlock{ + Attributes: map[string]schema.Attribute{ + "slot_constraint": schema.StringAttribute{ + Required: true, + Validators: []validator.String{ + enum.FrameworkValidate[awstypes.SlotConstraint](), + }, + }, + }, + Blocks: map[string]schema.Block{ + "default_value_specification": schema.SingleNestedBlock{ + Blocks: map[string]schema.Block{ + "default_value_list": schema.ListNestedBlock{ + Validators: []validator.List{ + listvalidator.SizeAtLeast(1), + }, + NestedObject: schema.NestedBlockObject{ + Attributes: map[string]schema.Attribute{ + "default_value": schema.StringAttribute{ + Required: true, + }, + }, + }, + }, + }, + }, + "prompt_specification": schema.SingleNestedBlock{ + Attributes: map[string]schema.Attribute{ + "max_retries": schema.Int32Attribute{ + Required: true, + }, + "allow_interrupt": schema.BoolAttribute{ + Optional: true, + }, + "message_selection_strategy": schema.StringAttribute{ + Optional: true, + Validators: []validator.String{ + enum.FrameworkValidate[awstypes.MessageSelectionStrategy](), + }, + }, + }, + Blocks: map[string]schema.Block{ + "message_groups": schema.ListNestedBlock{ + Validators: []validator.List{ + listvalidator.SizeAtLeast(1), + }, + Blocks: map[string]schema.Block{ + "message": schema.SingleNestedBlock{ + Required: true, + Blocks: map[string]schema.Block{ + "custom_payload": schema.SingleNestedBlock{ + NestedObject: schema.NestedBlockObject{ + Attributes: map[string]schema.Attribute{ + "value": schema.StringAttribute{ + Required: true, + }, + }, + }, + }, + "image_response_card": schema.SingleNestedBlock{ + Attributes: map[strings]schema.Attribute{ + "title": schema.StringAttribute{ + Required: true, + }, + "image_url": schema.StringAttribute{ + Optional: true, + }, + "subtitle": schema.StringAttribute{ + Optional: true, + }, + }, + Blocks: map[string]schema.Block{ + "buttons": schema.ListNestedBlock{ + Validators: []validator.List{ + listvalidator.SizeAtMost(1), + }, + NestedObject: schema.NestedBlockObject{ + Attributes: map[strings]schema.Attribute{ + "text": schema.StringAttribute{ + Required: true, + }, + "value": schema.StringAttribute{ + Required: true, + }, + }, + }, + }, + }, + }, + "plain_text_message": schema.SingleNestedBlock{ + Attributes: map[strings]schema.Attribute{ + "value": schema.StringAttribute{ + Required: true, + }, + }, + }, + "ssml_message": schema.SingleNestedBlock{ + Attributes: map[strings]schema.Attribute{ + "value": schema.StringAttribute{ + Required: true, + }, + }, + }, + }, + "variations": schema.ListNestedBlock{ + Blocks: map[string]schema.Block{ + "custom_payload": schema.SingleNestedBlock{ + Attributes: map[string]schema.Attribute{ + "value": schema.StringAttribute{ + Required: true, + }, + }, + }, + "image_response_card": schema.SingleNestedBlock{ + Attributes: map[strings]schema.Attribute{ + "title": schema.StringAttribute{ + Required: true, + }, + "image_url": schema.StringAttribute{ + Optional: true, + }, + "subtitle": schema.StringAttribute{ + Optional: true, + }, + }, + Blocks: map[string]schema.Block{ + "buttons": schema.ListNestedBlock{ + Validators: []validator.List{ + listvalidator.SizeAtMost(1), + }, + NestedObject: schema.NestedBlockObject{ + Attributes: map[strings]schema.Attribute{ + "text": schema.StringAttribute{ + Required: true, + }, + "value": schema.StringAttribute{ + Required: true, + }, + }, + }, + }, + }, + }, + "plain_text_message": schema.SingleNestedBlock{ + Attributes: map[strings]schema.Attribute{ + "value": schema.StringAttribute{ + Required: true, + }, + }, + }, + "ssml_message": schema.SingleNestedBlock{ + Attributes: map[strings]schema.Attribute{ + "value": schema.StringAttribute{ + Required: true, + }, + }, + }, + }, + }, + }, + }, + "prompt_attempts_specification": schema.MapAttribute{ + Optional: true, + Attributes: map[strings]schema.Attribute{ + "allow_interrupts": schema.BoolAttribute{ + Optional: true, + }, + }, + Blocks: map[strings]schema.Block{ + "allow_input_types": schema.SingleNestedBlock{ + Attributes: map[strings]schema.Attribute{ + "allow_audio_input": schema.BoolAttribute{ + Required: true, + }, + "allow_dtmf_input": schema.BoolAttribute{ + Required: true, + }, + }, + }, + "audio_and_dtmf_input_specification": schema.SingleNestedBlock{ + Optional: true, + Attributes: map[strings]schema.Attribute{ + "start_timeout_ms": schema.Int32Attribute{ + Required: true, + }, + }, + "audio_specification": schema.SingleNestedBlock{ + Optional: true, + Attributes: map[strings]schema.Attribute{ + "end_timeout_ms": schema.Int32Attribute{ + Required: true, + }, + "max_length_ms": schema.Int32Attribute{ + Required: true, + }, + }, + }, + "dtmf_specification": schema.SingleNestedBlock{ + Optional: true, + Attributes: map[strings]schema.Attribute{ + "deletion_character": schema.StringAttribute{ + Required: true, + }, + "end_character": schema.StringAttribute{ + Required: true, + }, + "end_timeout_ms": schema.StringAttribute{ + Required: true, + }, + "max_length": schema.Int32Attribute{ + Required: true, + }, + }, + }, + }, + "text_input_specification": schema.SingleNestedBlock{ + Optional: true, + Attributes: map[strings]schema.Attribute{ + "start_timeout_ms": schema.Int32Attribute{ + Required: true, + }, + }, + }, + }, + }, + }, + }, + "sample_utterances": schema.ListNestedBlock{ + Optional: true, + Attributes: map[strings]schema.Attribute{ + "utterance": schema.StringAttribute{ + Required: true, + }, + }, + }, + "slot_capture_setting": schema.SingleNestedBlock{ + Optional: true, + Blocks: map[string]schema.Block{ + "capture_conditional": schema.SingleNestedBlock{ + Optional: true, + Blocks: map[string]schema.Block{ + "condition_specification": schema.SingleNestedBlock{ + Optional: true, + Attributes: map[string]schema.Attribute{ + "active": schema.BoolAttribute{ + Required: true, + }, + }, + Blocks: map[string]schema.Block{ + "conditional_branch": schema.ListNestedBlock{ + Required: true, + NestedObject: schema.NestedBlockObject{ + Attributes: map[string]schema.Attribute{ + "name": schema.StringAttribute{ + Required: true, + }, + }, + Blocks: map[string]schema.Block{ + "condition": schema.SingleNestedBlock{ + Validators: []validator.Object{ + objectvalidator.IsRequired(), + }, + Attributes: map[string]schema.Attribute{ + "expression_string": schema.StringAttribute{ + Required: true, + }, + }, + }, + "next_step": schema.SingleNestedBlock{ + Validators: []validator.Object{ + objectvalidator.IsRequired(), + }, + NestedObject: schema.NestedBlockObject{ + Blocks: map[string]schema.SingleNestedBlock{ + "dialog_state": schema.SingleNestedBlock{ + Optional: true, + Blocks: map[string]SingleNestedBlock{ + "dialog_action": schema.SingleNestedBlock{ + Optional: true, + Attributes: map[string]schema.Attribute{ + "slot_to_elicit": schema.StringAttribute{ + Optional: true, + }, + "supress_next_message": schema.BoolAttribute{ + Optional: true, + }, + }, + Blocks: map[string]schema.Block{ + "type": schema.StringAttribute{ + Required: true, + Validators: []validator.String{ + enum.FrameworkValidate[awstypes.DialogActionType](), + }, + }, + }, + }, + "intent": schema.SingleNestedBlock{ + Optional: true, + Attributes: map[string]schema.Attribute{ + "intent_overide": schema.NestedBlockObject{ + Attributes: map[string]schema.Attribute{ + "name": schema.StringAttribute{ + Optional: true, + }, + NestedObject: schema.NestedBlockObject{ + Blocks: map[string]schema.Block{ + "slots": schema.MapAttribute{ + NestedObject: slotValueOverrideO, + }, + }, + }, + }, + }, + }, + "session_attributes": schema.MapAttribute{ + ElementType: types.StringType, + }, + }, + }, + }, + "response" + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, +} + +func (r *resourceSlot) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { + // TIP: ==== RESOURCE CREATE ==== + // Generally, the Create function should do the following things. Make + // sure there is a good reason if you don't do one of these. + // + // 1. Get a client connection to the relevant service + // 2. Fetch the plan + // 3. Populate a create input structure + // 4. Call the AWS create/put function + // 5. Using the output from the create function, set the minimum arguments + // and attributes for the Read function to work, as well as any computed + // only attributes. + // 6. Use a waiter to wait for create to complete + // 7. Save the request plan to response state + + // TIP: -- 1. Get a client connection to the relevant service + conn := r.Meta().LexV2ModelsClient(ctx) + + // TIP: -- 2. Fetch the plan + var plan resourceSlotData + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + if resp.Diagnostics.HasError() { + return + } + + // TIP: -- 3. Populate a create input structure + in := &lexv2models.CreateSlotInput{ + // TIP: Mandatory or fields that will always be present can be set when + // you create the Input structure. (Replace these with real fields.) + SlotName: aws.String(plan.Name.ValueString()), + SlotType: aws.String(plan.Type.ValueString()), + } + + if !plan.Description.IsNull() { + // TIP: Optional fields should be set based on whether or not they are + // used. + in.Description = aws.String(plan.Description.ValueString()) + } + if !plan.ComplexArgument.IsNull() { + // TIP: Use an expander to assign a complex argument. The elements must be + // deserialized into the appropriate struct before being passed to the expander. + var tfList []complexArgumentData + resp.Diagnostics.Append(plan.ComplexArgument.ElementsAs(ctx, &tfList, false)...) + if resp.Diagnostics.HasError() { + return + } + + in.ComplexArgument = expandComplexArgument(tfList) + } + + // TIP: -- 4. Call the AWS create function + out, err := conn.CreateSlot(ctx, in) + if err != nil { + // TIP: Since ID has not been set yet, you cannot use plan.ID.String() + // in error messages at this point. + resp.Diagnostics.AddError( + create.ProblemStandardMessage(names.LexV2Models, create.ErrActionCreating, ResNameSlot, plan.Name.String(), err), + err.Error(), + ) + return + } + if out == nil || out.Slot == nil { + resp.Diagnostics.AddError( + create.ProblemStandardMessage(names.LexV2Models, create.ErrActionCreating, ResNameSlot, plan.Name.String(), nil), + errors.New("empty output").Error(), + ) + return + } + + // TIP: -- 5. Using the output from the create function, set the minimum attributes + plan.ARN = flex.StringToFramework(ctx, out.Slot.Arn) + plan.ID = flex.StringToFramework(ctx, out.Slot.SlotId) + + // TIP: -- 6. Use a waiter to wait for create to complete + createTimeout := r.CreateTimeout(ctx, plan.Timeouts) + _, err = waitSlotCreated(ctx, conn, plan.ID.ValueString(), createTimeout) + if err != nil { + resp.Diagnostics.AddError( + create.ProblemStandardMessage(names.LexV2Models, create.ErrActionWaitingForCreation, ResNameSlot, plan.Name.String(), err), + err.Error(), + ) + return + } + + // TIP: -- 7. Save the request plan to response state + resp.Diagnostics.Append(resp.State.Set(ctx, plan)...) +} + +func (r *resourceSlot) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { + // TIP: ==== RESOURCE READ ==== + // Generally, the Read function should do the following things. Make + // sure there is a good reason if you don't do one of these. + // + // 1. Get a client connection to the relevant service + // 2. Fetch the state + // 3. Get the resource from AWS + // 4. Remove resource from state if it is not found + // 5. Set the arguments and attributes + // 6. Set the state + + // TIP: -- 1. Get a client connection to the relevant service + conn := r.Meta().LexV2ModelsClient(ctx) + + // TIP: -- 2. Fetch the state + var state resourceSlotData + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + + // TIP: -- 3. Get the resource from AWS using an API Get, List, or Describe- + // type function, or, better yet, using a finder. + out, err := findSlotByID(ctx, conn, state.ID.ValueString()) + // TIP: -- 4. Remove resource from state if it is not found + if tfresource.NotFound(err) { + resp.State.RemoveResource(ctx) + return + } + if err != nil { + resp.Diagnostics.AddError( + create.ProblemStandardMessage(names.LexV2Models, create.ErrActionSetting, ResNameSlot, state.ID.String(), err), + err.Error(), + ) + return + } + + // TIP: -- 5. Set the arguments and attributes + // + // For simple data types (i.e., schema.StringAttribute, schema.BoolAttribute, + // schema.Int64Attribute, and schema.Float64Attribue), simply setting the + // appropriate data struct field is sufficient. The flex package implements + // helpers for converting between Go and Plugin-Framework types seamlessly. No + // error or nil checking is necessary. + // + // However, there are some situations where more handling is needed such as + // complex data types (e.g., schema.ListAttribute, schema.SetAttribute). In + // these cases the flatten function may have a diagnostics return value, which + // should be appended to resp.Diagnostics. + state.ARN = flex.StringToFramework(ctx, out.Arn) + state.ID = flex.StringToFramework(ctx, out.SlotId) + state.Name = flex.StringToFramework(ctx, out.SlotName) + state.Type = flex.StringToFramework(ctx, out.SlotType) + + // TIP: Setting a complex type. + complexArgument, d := flattenComplexArgument(ctx, out.ComplexArgument) + resp.Diagnostics.Append(d...) + state.ComplexArgument = complexArgument + + // TIP: -- 6. Set the state + resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) +} + +func (r *resourceSlot) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { + // TIP: ==== RESOURCE UPDATE ==== + // Not all resources have Update functions. There are a few reasons: + // a. The AWS API does not support changing a resource + // b. All arguments have RequiresReplace() plan modifiers + // c. The AWS API uses a create call to modify an existing resource + // + // In the cases of a. and b., the resource will not have an update method + // defined. In the case of c., Update and Create can be refactored to call + // the same underlying function. + // + // The rest of the time, there should be an Update function and it should + // do the following things. Make sure there is a good reason if you don't + // do one of these. + // + // 1. Get a client connection to the relevant service + // 2. Fetch the plan and state + // 3. Populate a modify input structure and check for changes + // 4. Call the AWS modify/update function + // 5. Use a waiter to wait for update to complete + // 6. Save the request plan to response state + // TIP: -- 1. Get a client connection to the relevant service + conn := r.Meta().LexV2ModelsClient(ctx) + + // TIP: -- 2. Fetch the plan + var plan, state resourceSlotData + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + + // TIP: -- 3. Populate a modify input structure and check for changes + if !plan.Name.Equal(state.Name) || + !plan.Description.Equal(state.Description) || + !plan.ComplexArgument.Equal(state.ComplexArgument) || + !plan.Type.Equal(state.Type) { + + in := &lexv2models.UpdateSlotInput{ + // TIP: Mandatory or fields that will always be present can be set when + // you create the Input structure. (Replace these with real fields.) + SlotId: aws.String(plan.ID.ValueString()), + SlotName: aws.String(plan.Name.ValueString()), + SlotType: aws.String(plan.Type.ValueString()), + } + + if !plan.Description.IsNull() { + // TIP: Optional fields should be set based on whether or not they are + // used. + in.Description = aws.String(plan.Description.ValueString()) + } + if !plan.ComplexArgument.IsNull() { + // TIP: Use an expander to assign a complex argument. The elements must be + // deserialized into the appropriate struct before being passed to the expander. + var tfList []complexArgumentData + resp.Diagnostics.Append(plan.ComplexArgument.ElementsAs(ctx, &tfList, false)...) + if resp.Diagnostics.HasError() { + return + } + + in.ComplexArgument = expandComplexArgument(tfList) + } + + // TIP: -- 4. Call the AWS modify/update function + out, err := conn.UpdateSlot(ctx, in) + if err != nil { + resp.Diagnostics.AddError( + create.ProblemStandardMessage(names.LexV2Models, create.ErrActionUpdating, ResNameSlot, plan.ID.String(), err), + err.Error(), + ) + return + } + if out == nil || out.Slot == nil { + resp.Diagnostics.AddError( + create.ProblemStandardMessage(names.LexV2Models, create.ErrActionUpdating, ResNameSlot, plan.ID.String(), nil), + errors.New("empty output").Error(), + ) + return + } + + // TIP: Using the output from the update function, re-set any computed attributes + plan.ARN = flex.StringToFramework(ctx, out.Slot.Arn) + plan.ID = flex.StringToFramework(ctx, out.Slot.SlotId) + } + + // TIP: -- 5. Use a waiter to wait for update to complete + updateTimeout := r.UpdateTimeout(ctx, plan.Timeouts) + _, err := waitSlotUpdated(ctx, conn, plan.ID.ValueString(), updateTimeout) + if err != nil { + resp.Diagnostics.AddError( + create.ProblemStandardMessage(names.LexV2Models, create.ErrActionWaitingForUpdate, ResNameSlot, plan.ID.String(), err), + err.Error(), + ) + return + } + + // TIP: -- 6. Save the request plan to response state + resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) +} + +func (r *resourceSlot) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { + // TIP: ==== RESOURCE DELETE ==== + // Most resources have Delete functions. There are rare situations + // where you might not need a delete: + // a. The AWS API does not provide a way to delete the resource + // b. The point of your resource is to perform an action (e.g., reboot a + // server) and deleting serves no purpose. + // + // The Delete function should do the following things. Make sure there + // is a good reason if you don't do one of these. + // + // 1. Get a client connection to the relevant service + // 2. Fetch the state + // 3. Populate a delete input structure + // 4. Call the AWS delete function + // 5. Use a waiter to wait for delete to complete + // TIP: -- 1. Get a client connection to the relevant service + conn := r.Meta().LexV2ModelsClient(ctx) + + // TIP: -- 2. Fetch the state + var state resourceSlotData + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + + // TIP: -- 3. Populate a delete input structure + in := &lexv2models.DeleteSlotInput{ + SlotId: aws.String(state.ID.ValueString()), + } + + // TIP: -- 4. Call the AWS delete function + _, err := conn.DeleteSlot(ctx, in) + // TIP: On rare occassions, the API returns a not found error after deleting a + // resource. If that happens, we don't want it to show up as an error. + if err != nil { + var nfe *awstypes.ResourceNotFoundException + if errors.As(err, &nfe) { + return + } + resp.Diagnostics.AddError( + create.ProblemStandardMessage(names.LexV2Models, create.ErrActionDeleting, ResNameSlot, state.ID.String(), err), + err.Error(), + ) + return + } + + // TIP: -- 5. Use a waiter to wait for delete to complete + deleteTimeout := r.DeleteTimeout(ctx, state.Timeouts) + _, err = waitSlotDeleted(ctx, conn, state.ID.ValueString(), deleteTimeout) + if err != nil { + resp.Diagnostics.AddError( + create.ProblemStandardMessage(names.LexV2Models, create.ErrActionWaitingForDeletion, ResNameSlot, state.ID.String(), err), + err.Error(), + ) + return + } +} + +// TIP: ==== TERRAFORM IMPORTING ==== +// If Read can get all the information it needs from the Identifier +// (i.e., path.Root("id")), you can use the PassthroughID importer. Otherwise, +// you'll need a custom import function. +// +// See more: +// https://developer.hashicorp.com/terraform/plugin/framework/resources/import +func (r *resourceSlot) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { + resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) +} + +// TIP: ==== STATUS CONSTANTS ==== +// Create constants for states and statuses if the service does not +// already have suitable constants. We prefer that you use the constants +// provided in the service if available (e.g., amp.WorkspaceStatusCodeActive). +const ( + statusChangePending = "Pending" + statusDeleting = "Deleting" + statusNormal = "Normal" + statusUpdated = "Updated" +) + +// TIP: ==== WAITERS ==== +// Some resources of some services have waiters provided by the AWS API. +// Unless they do not work properly, use them rather than defining new ones +// here. +// +// Sometimes we define the wait, status, and find functions in separate +// files, wait.go, status.go, and find.go. Follow the pattern set out in the +// service and define these where it makes the most sense. +// +// If these functions are used in the _test.go file, they will need to be +// exported (i.e., capitalized). +// +// You will need to adjust the parameters and names to fit the service. +func waitSlotCreated(ctx context.Context, conn *lexv2models.Client, id string, timeout time.Duration) (*lexv2models.Slot, error) { + stateConf := &retry.StateChangeConf{ + Pending: []string{}, + Target: []string{statusNormal}, + Refresh: statusSlot(ctx, conn, id), + Timeout: timeout, + NotFoundChecks: 20, + ContinuousTargetOccurence: 2, + } + + outputRaw, err := stateConf.WaitForStateContext(ctx) + if out, ok := outputRaw.(*lexv2models.Slot); ok { + return out, err + } + + return nil, err +} + +// TIP: It is easier to determine whether a resource is updated for some +// resources than others. The best case is a status flag that tells you when +// the update has been fully realized. Other times, you can check to see if a +// key resource argument is updated to a new value or not. +func waitSlotUpdated(ctx context.Context, conn *lexv2models.Client, id string, timeout time.Duration) (*lexv2models.Slot, error) { + stateConf := &retry.StateChangeConf{ + Pending: []string{statusChangePending}, + Target: []string{statusUpdated}, + Refresh: statusSlot(ctx, conn, id), + Timeout: timeout, + NotFoundChecks: 20, + ContinuousTargetOccurence: 2, + } + + outputRaw, err := stateConf.WaitForStateContext(ctx) + if out, ok := outputRaw.(*lexv2models.Slot); ok { + return out, err + } + + return nil, err +} + +// TIP: A deleted waiter is almost like a backwards created waiter. There may +// be additional pending states, however. +func waitSlotDeleted(ctx context.Context, conn *lexv2models.Client, id string, timeout time.Duration) (*lexv2models.Slot, error) { + stateConf := &retry.StateChangeConf{ + Pending: []string{statusDeleting, statusNormal}, + Target: []string{}, + Refresh: statusSlot(ctx, conn, id), + Timeout: timeout, + } + + outputRaw, err := stateConf.WaitForStateContext(ctx) + if out, ok := outputRaw.(*lexv2models.Slot); ok { + return out, err + } + + return nil, err +} + +// TIP: ==== STATUS ==== +// The status function can return an actual status when that field is +// available from the API (e.g., out.Status). Otherwise, you can use custom +// statuses to communicate the states of the resource. +// +// Waiters consume the values returned by status functions. Design status so +// that it can be reused by a create, update, and delete waiter, if possible. +func statusSlot(ctx context.Context, conn *lexv2models.Client, id string) retry.StateRefreshFunc { + return func() (interface{}, string, error) { + out, err := findSlotByID(ctx, conn, id) + if tfresource.NotFound(err) { + return nil, "", nil + } + + if err != nil { + return nil, "", err + } + + return out, aws.ToString(out.Status), nil + } +} + +// TIP: ==== FINDERS ==== +// The find function is not strictly necessary. You could do the API +// request from the status function. However, we have found that find often +// comes in handy in other places besides the status function. As a result, it +// is good practice to define it separately. +func findSlotByID(ctx context.Context, conn *lexv2models.Client, id string) (*lexv2models.Slot, error) { + in := &lexv2models.GetSlotInput{ + Id: aws.String(id), + } + + out, err := conn.GetSlot(ctx, in) + if err != nil { + var nfe *awstypes.ResourceNotFoundException + if errors.As(err, &nfe) { + return nil, &retry.NotFoundError{ + LastError: err, + LastRequest: in, + } + } + + return nil, err + } + + if out == nil || out.Slot == nil { + return nil, tfresource.NewEmptyResultError(in) + } + + return out.Slot, nil +} + +// TIP: ==== FLEX ==== +// Flatteners and expanders ("flex" functions) help handle complex data +// types. Flatteners take an API data type and return the equivalent Plugin-Framework +// type. In other words, flatteners translate from AWS -> Terraform. +// +// On the other hand, expanders take a Terraform data structure and return +// something that you can send to the AWS API. In other words, expanders +// translate from Terraform -> AWS. +// +// See more: +// https://hashicorp.github.io/terraform-provider-aws/data-handling-and-conversion/ +func flattenComplexArgument(ctx context.Context, apiObject *lexv2models.ComplexArgument) (types.List, diag.Diagnostics) { + var diags diag.Diagnostics + elemType := types.ObjectType{AttrTypes: complexArgumentAttrTypes} + + if apiObject == nil { + return types.ListNull(elemType), diags + } + + obj := map[string]attr.Value{ + "nested_required": flex.StringValueToFramework(ctx, apiObject.NestedRequired), + "nested_optional": flex.StringValueToFramework(ctx, apiObject.NestedOptional), + } + objVal, d := types.ObjectValue(complexArgumentAttrTypes, obj) + diags.Append(d...) + + listVal, d := types.ListValue(elemType, []attr.Value{objVal}) + diags.Append(d...) + + return listVal, diags +} + +// TIP: Often the AWS API will return a slice of structures in response to a +// request for information. Sometimes you will have set criteria (e.g., the ID) +// that means you'll get back a one-length slice. This plural function works +// brilliantly for that situation too. +func flattenComplexArguments(ctx context.Context, apiObjects []*lexv2models.ComplexArgument) (types.List, diag.Diagnostics) { + var diags diag.Diagnostics + elemType := types.ObjectType{AttrTypes: complexArgumentAttrTypes} + + if len(apiObjects) == 0 { + return types.ListNull(elemType), diags + } + + elems := []attr.Value{} + for _, apiObject := range apiObjects { + if apiObject == nil { + continue + } + + obj := map[string]attr.Value{ + "nested_required": flex.StringValueToFramework(ctx, apiObject.NestedRequired), + "nested_optional": flex.StringValueToFramework(ctx, apiObject.NestedOptional), + } + objVal, d := types.ObjectValue(complexArgumentAttrTypes, obj) + diags.Append(d...) + + elems = append(elems, objVal) + } + + listVal, d := types.ListValue(elemType, elems) + diags.Append(d...) + + return listVal, diags +} + +// TIP: Remember, as mentioned above, expanders take a Terraform data structure +// and return something that you can send to the AWS API. In other words, +// expanders translate from Terraform -> AWS. +// +// See more: +// https://hashicorp.github.io/terraform-provider-aws/data-handling-and-conversion/ +func expandComplexArgument(tfList []complexArgumentData) *lexv2models.ComplexArgument { + if len(tfList) == 0 { + return nil + } + + tfObj := tfList[0] + apiObject := &lexv2models.ComplexArgument{ + NestedRequired: aws.String(tfObj.NestedRequired.ValueString()), + } + if !tfObj.NestedOptional.IsNull() { + apiObject.NestedOptional = aws.String(tfObj.NestedOptional.ValueString()) + } + + return apiObject +} + +// TIP: Even when you have a list with max length of 1, this plural function +// works brilliantly. However, if the AWS API takes a structure rather than a +// slice of structures, you will not need it. +func expandComplexArguments(tfList []complexArgumentData) []*lexv2models.ComplexArgument { + // TIP: The AWS API can be picky about whether you send a nil or zero- + // length for an argument that should be cleared. For example, in some + // cases, if you send a nil value, the AWS API interprets that as "make no + // changes" when what you want to say is "remove everything." Sometimes + // using a zero-length list will cause an error. + // + // As a result, here are two options. Usually, option 1, nil, will work as + // expected, clearing the field. But, test going from something to nothing + // to make sure it works. If not, try the second option. + // TIP: Option 1: Returning nil for zero-length list + if len(tfList) == 0 { + return nil + } + + var apiObject []*lexv2models.ComplexArgument + // TIP: Option 2: Return zero-length list for zero-length list. If option 1 does + // not work, after testing going from something to nothing (if that is + // possible), uncomment out the next line and remove option 1. + // + // apiObject := make([]*lexv2models.ComplexArgument, 0) + + for _, tfObj := range tfList { + item := &lexv2models.ComplexArgument{ + NestedRequired: aws.String(tfObj.NestedRequired.ValueString()), + } + if !tfObj.NestedOptional.IsNull() { + item.NestedOptional = aws.String(tfObj.NestedOptional.ValueString()) + } + + apiObject = append(apiObject, item) + } + + return apiObject +} + +// TIP: ==== DATA STRUCTURES ==== +// With Terraform Plugin-Framework configurations are deserialized into +// Go types, providing type safety without the need for type assertions. +// These structs should match the schema definition exactly, and the `tfsdk` +// tag value should match the attribute name. +// +// Nested objects are represented in their own data struct. These will +// also have a corresponding attribute type mapping for use inside flex +// functions. +// +// See more: +// https://developer.hashicorp.com/terraform/plugin/framework/handling-data/accessing-values +type resourceSlotData struct { + ARN types.String `tfsdk:"arn"` + ComplexArgument types.List `tfsdk:"complex_argument"` + Description types.String `tfsdk:"description"` + ID types.String `tfsdk:"id"` + Name types.String `tfsdk:"name"` + Timeouts timeouts.Value `tfsdk:"timeouts"` + Type types.String `tfsdk:"type"` +} + +type complexArgumentData struct { + NestedRequired types.String `tfsdk:"nested_required"` + NestedOptional types.String `tfsdk:"nested_optional"` +} + +var complexArgumentAttrTypes = map[string]attr.Type{ + "nested_required": types.StringType, + "nested_optional": types.StringType, +} diff --git a/internal/service/lexv2models/slot_test.go b/internal/service/lexv2models/slot_test.go new file mode 100644 index 00000000000..8a273063670 --- /dev/null +++ b/internal/service/lexv2models/slot_test.go @@ -0,0 +1,334 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package lexv2models_test +// **PLEASE DELETE THIS AND ALL TIP COMMENTS BEFORE SUBMITTING A PR FOR REVIEW!** +// +// TIP: ==== INTRODUCTION ==== +// Thank you for trying the skaff tool! +// +// You have opted to include these helpful comments. They all include "TIP:" +// to help you find and remove them when you're done with them. +// +// While some aspects of this file are customized to your input, the +// scaffold tool does *not* look at the AWS API and ensure it has correct +// function, structure, and variable names. It makes guesses based on +// commonalities. You will need to make significant adjustments. +// +// In other words, as generated, this is a rough outline of the work you will +// need to do. If something doesn't make sense for your situation, get rid of +// it. + +import ( + // TIP: ==== IMPORTS ==== + // This is a common set of imports but not customized to your code since + // your code hasn't been written yet. Make sure you, your IDE, or + // goimports -w fixes these imports. + // + // The provider linter wants your imports to be in two groups: first, + // standard library (i.e., "fmt" or "strings"), second, everything else. + // + // Also, AWS Go SDK v2 may handle nested structures differently than v1, + // using the services/lexv2models/types package. If so, you'll + // need to import types and reference the nested types, e.g., as + // types.. + "context" + "fmt" + "regexp" + "strings" + "testing" + + "github.com/YakDriver/regexache" + + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/service/lexv2models" + "github.com/aws/aws-sdk-go-v2/service/lexv2models/types" + sdkacctest "github.com/hashicorp/terraform-plugin-testing/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" + "github.com/hashicorp/terraform-provider-aws/internal/errs" + "github.com/hashicorp/terraform-provider-aws/names" + + // TIP: You will often need to import the package that this test file lives + // in. Since it is in the "test" context, it must import the package to use + // any normal context constants, variables, or functions. + tflexv2models "github.com/hashicorp/terraform-provider-aws/internal/service/lexv2models" +) + +// TIP: File Structure. The basic outline for all test files should be as +// follows. Improve this resource's maintainability by following this +// outline. +// +// 1. Package declaration (add "_test" since this is a test file) +// 2. Imports +// 3. Unit tests +// 4. Basic test +// 5. Disappears test +// 6. All the other tests +// 7. Helper functions (exists, destroy, check, etc.) +// 8. Functions that return Terraform configurations + +// TIP: ==== UNIT TESTS ==== +// This is an example of a unit test. Its name is not prefixed with +// "TestAcc" like an acceptance test. +// +// Unlike acceptance tests, unit tests do not access AWS and are focused on a +// function (or method). Because of this, they are quick and cheap to run. +// +// In designing a resource's implementation, isolate complex bits from AWS bits +// so that they can be tested through a unit test. We encourage more unit tests +// in the provider. +// +// Cut and dry functions using well-used patterns, like typical flatteners and +// expanders, don't need unit testing. However, if they are complex or +// intricate, they should be unit tested. +func TestSlotExampleUnitTest(t *testing.T) { + t.Parallel() + + testCases := []struct { + TestName string + Input string + Expected string + Error bool + }{ + { + TestName: "empty", + Input: "", + Expected: "", + Error: true, + }, + { + TestName: "descriptive name", + Input: "some input", + Expected: "some output", + Error: false, + }, + { + TestName: "another descriptive name", + Input: "more input", + Expected: "more output", + Error: false, + }, + } + + for _, testCase := range testCases { + testCase := testCase + t.Run(testCase.TestName, func(t *testing.T) { + t.Parallel() + got, err := tflexv2models.FunctionFromResource(testCase.Input) + + if err != nil && !testCase.Error { + t.Errorf("got error (%s), expected no error", err) + } + + if err == nil && testCase.Error { + t.Errorf("got (%s) and no error, expected error", got) + } + + if got != testCase.Expected { + t.Errorf("got %s, expected %s", got, testCase.Expected) + } + }) + } +} + +// TIP: ==== ACCEPTANCE TESTS ==== +// This is an example of a basic acceptance test. This should test as much of +// standard functionality of the resource as possible, and test importing, if +// applicable. We prefix its name with "TestAcc", the service, and the +// resource name. +// +// Acceptance test access AWS and cost money to run. +func TestAccLexV2ModelsSlot_basic(t *testing.T) { + ctx := acctest.Context(t) + // TIP: This is a long-running test guard for tests that run longer than + // 300s (5 min) generally. + if testing.Short() { + t.Skip("skipping long-running test in short mode") + } + + var slot lexv2models.DescribeSlotResponse + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + resourceName := "aws_lexv2models_slot.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { + acctest.PreCheck(ctx, t) + acctest.PreCheckPartitionHasService(t, names.LexV2ModelsEndpointID) + testAccPreCheck(ctx, t) + }, + ErrorCheck: acctest.ErrorCheck(t, names.LexV2ModelsEndpointID), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + CheckDestroy: testAccCheckSlotDestroy(ctx), + Steps: []resource.TestStep{ + { + Config: testAccSlotConfig_basic(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckSlotExists(ctx, resourceName, &slot), + resource.TestCheckResourceAttr(resourceName, "auto_minor_version_upgrade", "false"), + resource.TestCheckResourceAttrSet(resourceName, "maintenance_window_start_time.0.day_of_week"), + resource.TestCheckTypeSetElemNestedAttrs(resourceName, "user.*", map[string]string{ + "console_access": "false", + "groups.#": "0", + "username": "Test", + "password": "TestTest1234", + }), + acctest.MatchResourceAttrRegionalARN(resourceName, "arn", "lexv2models", regexache.MustCompile(`slot:+.`)), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"apply_immediately", "user"}, + }, + }, + }) +} + +func TestAccLexV2ModelsSlot_disappears(t *testing.T) { + ctx := acctest.Context(t) + if testing.Short() { + t.Skip("skipping long-running test in short mode") + } + + var slot lexv2models.DescribeSlotResponse + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + resourceName := "aws_lexv2models_slot.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { + acctest.PreCheck(ctx, t) + acctest.PreCheckPartitionHasService(t, names.LexV2ModelsEndpointID) + testAccPreCheck(t) + }, + ErrorCheck: acctest.ErrorCheck(t, names.LexV2ModelsEndpointID), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + CheckDestroy: testAccCheckSlotDestroy(ctx), + Steps: []resource.TestStep{ + { + Config: testAccSlotConfig_basic(rName, testAccSlotVersionNewer), + Check: resource.ComposeTestCheckFunc( + testAccCheckSlotExists(ctx, resourceName, &slot), + // TIP: The Plugin-Framework disappears helper is similar to the Plugin-SDK version, + // but expects a new resource factory function as the third argument. To expose this + // private function to the testing package, you may need to add a line like the following + // to exports_test.go: + // + // var ResourceSlot = newResourceSlot + acctest.CheckFrameworkResourceDisappears(ctx, acctest.Provider, tflexv2models.ResourceSlot, resourceName), + ), + ExpectNonEmptyPlan: true, + }, + }, + }) +} + +func testAccCheckSlotDestroy(ctx context.Context) resource.TestCheckFunc { + return func(s *terraform.State) error { + conn := acctest.Provider.Meta().(*conns.AWSClient).LexV2ModelsClient(ctx) + + for _, rs := range s.RootModule().Resources { + if rs.Type != "aws_lexv2models_slot" { + continue + } + + input := &lexv2models.DescribeSlotInput{ + SlotId: aws.String(rs.Primary.ID), + } + _, err := conn.DescribeSlot(ctx, &lexv2models.DescribeSlotInput{ + SlotId: aws.String(rs.Primary.ID), + }) + if errs.IsA[*types.ResourceNotFoundException](err){ + return nil + } + if err != nil { + return nil + } + + return create.Error(names.LexV2Models, create.ErrActionCheckingDestroyed, tflexv2models.ResNameSlot, rs.Primary.ID, errors.New("not destroyed")) + } + + return nil + } +} + +func testAccCheckSlotExists(ctx context.Context, name string, slot *lexv2models.DescribeSlotResponse) resource.TestCheckFunc { + return func(s *terraform.State) error { + rs, ok := s.RootModule().Resources[name] + if !ok { + return create.Error(names.LexV2Models, create.ErrActionCheckingExistence, tflexv2models.ResNameSlot, name, errors.New("not found")) + } + + if rs.Primary.ID == "" { + return create.Error(names.LexV2Models, create.ErrActionCheckingExistence, tflexv2models.ResNameSlot, name, errors.New("not set")) + } + + conn := acctest.Provider.Meta().(*conns.AWSClient).LexV2ModelsClient(ctx) + resp, err := conn.DescribeSlot(ctx, &lexv2models.DescribeSlotInput{ + SlotId: aws.String(rs.Primary.ID), + }) + + if err != nil { + return create.Error(names.LexV2Models, create.ErrActionCheckingExistence, tflexv2models.ResNameSlot, rs.Primary.ID, err) + } + + *slot = *resp + + return nil + } +} + +func testAccPreCheck(ctx context.Context, t *testing.T) { + conn := acctest.Provider.Meta().(*conns.AWSClient).LexV2ModelsClient(ctx) + + input := &lexv2models.ListSlotsInput{} + _, err := conn.ListSlots(ctx, input) + + if acctest.PreCheckSkipError(err) { + t.Skipf("skipping acceptance testing: %s", err) + } + if err != nil { + t.Fatalf("unexpected PreCheck error: %s", err) + } +} + +func testAccCheckSlotNotRecreated(before, after *lexv2models.DescribeSlotResponse) resource.TestCheckFunc { + return func(s *terraform.State) error { + if before, after := aws.ToString(before.SlotId), aws.ToString(after.SlotId); before != after { + return create.Error(names.LexV2Models, create.ErrActionCheckingNotRecreated, tflexv2models.ResNameSlot, aws.ToString(before.SlotId), errors.New("recreated")) + } + + return nil + } +} + +func testAccSlotConfig_basic(rName, version string) string { + return fmt.Sprintf(` +resource "aws_security_group" "test" { + name = %[1]q +} + +resource "aws_lexv2models_slot" "test" { + slot_name = %[1]q + engine_type = "ActiveLexV2Models" + engine_version = %[2]q + host_instance_type = "lexv2models.t2.micro" + security_groups = [aws_security_group.test.id] + authentication_strategy = "simple" + storage_type = "efs" + + logs { + general = true + } + + user { + username = "Test" + password = "TestTest1234" + } +} +`, rName, version) +} diff --git a/website/docs/r/lexv2models_slot.html.markdown b/website/docs/r/lexv2models_slot.html.markdown new file mode 100644 index 00000000000..0d421b07052 --- /dev/null +++ b/website/docs/r/lexv2models_slot.html.markdown @@ -0,0 +1,69 @@ +--- +subcategory: "Lex V2 Models" +layout: "aws" +page_title: "AWS: aws_lexv2models_slot" +description: |- + Terraform resource for managing an AWS Lex V2 Models Slot. +--- +` +# Resource: aws_lexv2models_slot + +Terraform resource for managing an AWS Lex V2 Models Slot. + +## Example Usage + +### Basic Usage + +```terraform +resource "aws_lexv2models_slot" "example" { +} +``` + +## Argument Reference + +The following arguments are required: + +* `example_arg` - (Required) Concise argument description. Do not begin the description with "An", "The", "Defines", "Indicates", or "Specifies," as these are verbose. In other words, "Indicates the amount of storage," can be rewritten as "Amount of storage," without losing any information. + +The following arguments are optional: + +* `optional_arg` - (Optional) Concise argument description. Do not begin the description with "An", "The", "Defines", "Indicates", or "Specifies," as these are verbose. In other words, "Indicates the amount of storage," can be rewritten as "Amount of storage," without losing any information. + +## Attribute Reference + +This resource exports the following attributes in addition to the arguments above: + +* `arn` - ARN of the Slot. Do not begin the description with "An", "The", "Defines", "Indicates", or "Specifies," as these are verbose. In other words, "Indicates the amount of storage," can be rewritten as "Amount of storage," without losing any information. +* `example_attribute` - Concise description. Do not begin the description with "An", "The", "Defines", "Indicates", or "Specifies," as these are verbose. In other words, "Indicates the amount of storage," can be rewritten as "Amount of storage," without losing any information. + +## Timeouts + +[Configuration options](https://developer.hashicorp.com/terraform/language/resources/syntax#operation-timeouts): + +* `create` - (Default `60m`) +* `update` - (Default `180m`) +* `delete` - (Default `90m`) + +## Import + +In Terraform v1.5.0 and later, use an [`import` block](https://developer.hashicorp.com/terraform/language/import) to import Lex V2 Models Slot using the `example_id_arg`. For example: + +```terraform +import { + to = aws_lexv2models_slot.example + id = "slot-id-12345678" +} +``` + +Using `terraform import`, import Lex V2 Models Slot using the `example_id_arg`. For example: + +```console +% terraform import aws_lexv2models_slot.example slot-id-12345678 +``` From c076e34940f2a67e8527d01bb3bd807c21e64f76 Mon Sep 17 00:00:00 2001 From: Sharon Nam Date: Thu, 7 Dec 2023 00:16:41 -0800 Subject: [PATCH 02/42] fix schema --- internal/service/lexv2models/slot.go | 553 +++++++++++---------------- 1 file changed, 222 insertions(+), 331 deletions(-) diff --git a/internal/service/lexv2models/slot.go b/internal/service/lexv2models/slot.go index 272068a9b45..47844890f64 100644 --- a/internal/service/lexv2models/slot.go +++ b/internal/service/lexv2models/slot.go @@ -56,50 +56,6 @@ func (r *resourceSlot) Metadata(_ context.Context, req resource.MetadataRequest, resp.TypeName = "aws_lexv2models_slot" } -// TIP: ==== SCHEMA ==== -// In the schema, add each of the attributes in snake case (e.g., -// delete_automated_backups). -// -// Formatting rules: -// * Alphabetize attributes to make them easier to find. -// * Do not add a blank line between attributes. -// -// Attribute basics: -// - If a user can provide a value ("configure a value") for an -// attribute (e.g., instances = 5), we call the attribute an -// "argument." -// - You change the way users interact with attributes using: -// - Required -// - Optional -// - Computed -// - There are only four valid combinations: -// -// 1. Required only - the user must provide a value -// Required: true, -// -// 2. Optional only - the user can configure or omit a value; do not -// use Default or DefaultFunc -// -// Optional: true, -// -// 3. Computed only - the provider can provide a value but the user -// cannot, i.e., read-only -// -// Computed: true, -// -// 4. Optional AND Computed - the provider or user can provide a value; -// use this combination if you are using Default -// -// Optional: true, -// Computed: true, -// -// You will typically find arguments in the input struct -// (e.g., CreateDBInstanceInput) for the create operation. Sometimes -// they are only in the input struct (e.g., ModifyDBInstanceInput) for -// the modify operation. -// -// For more about schema options, visit -// https://developer.hashicorp.com/terraform/plugin/framework/handling-data/schemas?page=schemas func (r *resourceSlot) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { slotValueOverrideO := types.ObjectType{ AttrTypes: map[string]attr.Type{ @@ -147,27 +103,6 @@ func (r *resourceSlot) Schema(ctx context.Context, req resource.SchemaRequest, r "button": schema.ListNestedBlock{ Nestedobject: schema.NestedBlockObject{ Attributes: map[string]schema.Attribute{ - - } - } - } - } - }, - } - - } - "message": schema.NestedBlockObject{ - Required: true, - Blocks: map[string]schema.Block{ - - - Blocks: map[string]schema.Block{ - "buttons": schema.ListNestedBlock{ - Validators: []validator.List{ - listvalidator.SizeAtMost(1), - }, - NestedObject: schema.NestedBlockObject{ - Attributes: map[strings]schema.Attribute{ "text": schema.StringAttribute{ Required: true, }, @@ -179,15 +114,25 @@ func (r *resourceSlot) Schema(ctx context.Context, req resource.SchemaRequest, r }, }, }, - "plain_text_message": schema.SingleNestedBlock{ - Attributes: map[strings]schema.Attribute{ + }, + "plain_text_message": schema.ListNestedBlock{ + Validators: []validator.List{ + listvalidator.SizeAtMost(1), + }, + NestedObject: schema.NestedBlockObject{ + Attributes: map[string]schema.Attribute{ "value": schema.StringAttribute{ Required: true, }, }, }, - "ssml_message": schema.SingleNestedBlock{ - Attributes: map[strings]schema.Attribute{ + }, + "ssml_message": schema.ListNestedBlock{ + Validators: []validator.List{ + listvalidator.SizeAtMost(1), + }, + NestedObject: schema.NestedBlockObject{ + Attributes: map[string]schema.Attribute{ "value": schema.StringAttribute{ Required: true, }, @@ -196,7 +141,151 @@ func (r *resourceSlot) Schema(ctx context.Context, req resource.SchemaRequest, r }, }, } + messageGroupLNB := schema.ListNestedAttributeBlock{ + Validators: []validator.List{ + listvalidator.SizeAtLeast(1), + }, + NestedObject: schema.NestedBlockObject{ + Blocks: map[string]schema.Block{ + "message": schema.ListNestedBlock{ + Validators: []validator.List{ + listvalidator.SizeBetween(1,1), + }, + NestedObject: messageNBO, + }, + "variations": schema.ListNestedBlock{ + NestedObject: messageNBO, + }, + }, + }, + } + + dialogStateNBO := schema.NestedBlockObject{ + Attributes: map[string]schema.Attribute{ + "session_attributes": schema.MapAttribute{ + ElementType: types.StringType, + Optional: true, + }, + }, + Blocks: map[string]schema.Block{ + "dialog_action": schema.ListNestedBlock{ + Validators: []validator.List{ + listvalidator.SizeAtMost(1), + }, + NesredObject: schema.NestedBlockObject{ + Attributes: map[string]schema.Attribute{ + "type": schema.StringAttribute{ + Required: true, + }, + "slot_to_elicit": schema.StringAttribute{ + Optional: true, + }, + "suppress_next_message": schema.StringAttribute{ + Optional: true, + }, + }, + }, + }, + "intent": schema.ListNestedBlock{ + Validators: []validator.List{ + listvalidator.SizeAtMost(1), + }, + NestedObject: schema.NestedBlockObject{ + Attributes: map[string]schema.Attribute{ + "name": schema.StringAttribute{ + Optional: true, + }, + "slots": schema.MapAttribute{ + Optional: true, + ElementType: slotValueOverrideO, + }, + }, + }, + }, + }, + } + + responseSpecificationLNB := schema.ListNestedBlock{ + Validators: []validator.List{ + listvalidator.SizeAtMost(1), + }, + NestedObject: schema.NestedBlockObject{ + Attributes: map[string]schema.Attribute{ + "allow_interrupt": schema.BoolAttribute{ + Optional: true, + }, + }, + Blocks: map[string]schema.Block{ + "message_group": messageGroupLNB, + }, + }, + } + + conditionalSpecificationLNB := schema.ListNestedBlock{ + Validators: []validator.List{ + listvalidator.SizeAtMost(1), + }, + NestedObject: schema.NestedBlockObject{ + Attributes: map[string]schema.Attribute{ + "active": schema.BoolAttribute{ + Required: true, + }, + }, + Blocks: map[string]schema.Block{ + "conditional_branch": schema.ListNestedBlock{ + Validators: []validator.List{ + listvalidator.SizeAtLeast(1), + }, + NestedObject: schema.NestedBlockObject{ + Attributes: map[string]schema.Attribute{ + "name": schema.StringAttribute{ + Required: true, + }, + }, + Blocks: map[string]schema.Block{ + "condition": schema.ListNestedBlock{ + Validators: []validator.List{ + listvalidator.SizeBetween(1, 1), + }, + NestedObject: schema.NestedBlockObject{ + Attributes: map[string]schema.Attribute{ + "expression_string": schema.StringAttribute{ + Required: true, + }, + }, + }, + }, + "next_step": schema.ListNestedBlock{ + Validators: []validator.List{ + listvalidator.SizeBetween(1, 1), + }, + NestedObject: dialogStateNBO, + }, + "response": responseSpecificationLNB, + }, + }, + }, + "default_branch": schema.ListNestedBlock{ + Validators: []validator.List{ + listvalidator.SizeBetween(1, 1), + }, + NestedObject: schema.NestedBlockObject{ + Blocks: map[string]schema.Block{ + "next_step": schema.ListNestedBlock{ + Validators: []validator.List{ + listvalidator.SizeBetween(1, 1), + }, + NestedObject: dialogStateNBO, + }, + "response": responseSpecificationLNB, + }, + }, + }, + }, + }, + } + ////////// resp.Schema = schema.Schema{ Attributes: map[string]schema.Attribute{ // "arn": framework.ARNAttributeComputedOnly(), @@ -265,212 +354,97 @@ func (r *resourceSlot) Schema(ctx context.Context, req resource.SchemaRequest, r }, }, }, - "prompt_specification": schema.SingleNestedBlock{ - Attributes: map[string]schema.Attribute{ - "max_retries": schema.Int32Attribute{ - Required: true, - }, - "allow_interrupt": schema.BoolAttribute{ - Optional: true, - }, - "message_selection_strategy": schema.StringAttribute{ - Optional: true, - Validators: []validator.String{ - enum.FrameworkValidate[awstypes.MessageSelectionStrategy](), - }, - }, + "prompt_specification": schema.ListNestedBlock{ + Validators: []validator.List{ + listvalidator.SizeBetween(1, 1), }, - Blocks: map[string]schema.Block{ - "message_groups": schema.ListNestedBlock{ - Validators: []validator.List{ - listvalidator.SizeAtLeast(1), + NestedObject: schema.NestedBlockObject{ + Attributes: map[string]schema.Attribute{ + "max_retries": schema.Int32Attribute{ + Required: true, }, - Blocks: map[string]schema.Block{ - "message": schema.SingleNestedBlock{ - Required: true, - Blocks: map[string]schema.Block{ - "custom_payload": schema.SingleNestedBlock{ - NestedObject: schema.NestedBlockObject{ - Attributes: map[string]schema.Attribute{ - "value": schema.StringAttribute{ - Required: true, - }, - }, - }, - }, - "image_response_card": schema.SingleNestedBlock{ - Attributes: map[strings]schema.Attribute{ - "title": schema.StringAttribute{ - Required: true, - }, - "image_url": schema.StringAttribute{ - Optional: true, - }, - "subtitle": schema.StringAttribute{ - Optional: true, - }, - }, - Blocks: map[string]schema.Block{ - "buttons": schema.ListNestedBlock{ - Validators: []validator.List{ - listvalidator.SizeAtMost(1), - }, - NestedObject: schema.NestedBlockObject{ - Attributes: map[strings]schema.Attribute{ - "text": schema.StringAttribute{ - Required: true, - }, - "value": schema.StringAttribute{ - Required: true, - }, - }, - }, - }, - }, - }, - "plain_text_message": schema.SingleNestedBlock{ - Attributes: map[strings]schema.Attribute{ - "value": schema.StringAttribute{ - Required: true, - }, - }, - }, - "ssml_message": schema.SingleNestedBlock{ - Attributes: map[strings]schema.Attribute{ - "value": schema.StringAttribute{ + "allow_interrupt": schema.BoolAttribute{ + Optional: true, + }, + "message_selection_strategy": schema.StringAttribute{ + Optional: true, + Validators: []validator.String{ + enum.FrameworkValidate[awstypes.MessageSelectionStrategy](), + }, + }, + "message_groups": messageGroupLNB, + "prompt_attempts_specification": schema.MapAttribute{ + Optional: true, + ElementType: types.ObjectType{ + AttrTypes: map[string]attr.Type{ + "allow_input_types": schema.ObjectType{ + AttrTypes: map[string]attr.Type{ + "allow_audio_input": schema.BoolAttribute{ Required: true, }, - }, - }, - }, - "variations": schema.ListNestedBlock{ - Blocks: map[string]schema.Block{ - "custom_payload": schema.SingleNestedBlock{ - Attributes: map[string]schema.Attribute{ - "value": schema.StringAttribute{ + "allow_dtmf_input": schema.BoolAttribute{ Required: true, }, }, + "allow_interrupts": schema.BoolAttribute{ + Optional: true, }, - "image_response_card": schema.SingleNestedBlock{ - Attributes: map[strings]schema.Attribute{ - "title": schema.StringAttribute{ + "audio_and_dtmf_input_specification": types.ObjectType{ + AttrTypes: map[string]attr.Type{ + "start_timeout_ms": schema.Int32Attribute{ Required: true, }, - "image_url": schema.StringAttribute{ + "audio_specification": types.ObjectType{ Optional: true, + AttrTypes: map[strings]attr.Type{ + "end_timeout_ms": schema.Int32Attribute{ + Required: true, + }, + "max_length_ms": schema.Int32Attribute{ + Required: true, + }, + }, }, - "subtitle": schema.StringAttribute{ + "dtmf_specification": schema.SingleNestedBlock{ Optional: true, - }, - }, - Blocks: map[string]schema.Block{ - "buttons": schema.ListNestedBlock{ - Validators: []validator.List{ - listvalidator.SizeAtMost(1), - }, - NestedObject: schema.NestedBlockObject{ - Attributes: map[strings]schema.Attribute{ - "text": schema.StringAttribute{ - Required: true, - }, - "value": schema.StringAttribute{ - Required: true, - }, + Attributes: map[strings]schema.Attribute{ + "deletion_character": schema.StringAttribute{ + Required: true, + }, + "end_character": schema.StringAttribute{ + Required: true, + }, + "end_timeout_ms": schema.StringAttribute{ + Required: true, + }, + "max_length": schema.Int32Attribute{ + Required: true, }, }, }, }, }, - "plain_text_message": schema.SingleNestedBlock{ + "text_input_specification": schema.SingleNestedBlock{ + Optional: true, Attributes: map[strings]schema.Attribute{ - "value": schema.StringAttribute{ + "start_timeout_ms": schema.Int32Attribute{ Required: true, }, }, }, - "ssml_message": schema.SingleNestedBlock{ - Attributes: map[strings]schema.Attribute{ - "value": schema.StringAttribute{ - Required: true, - }, - }, - }, - }, - }, - }, - }, - "prompt_attempts_specification": schema.MapAttribute{ - Optional: true, - Attributes: map[strings]schema.Attribute{ - "allow_interrupts": schema.BoolAttribute{ - Optional: true, - }, - }, - Blocks: map[strings]schema.Block{ - "allow_input_types": schema.SingleNestedBlock{ - Attributes: map[strings]schema.Attribute{ - "allow_audio_input": schema.BoolAttribute{ - Required: true, - }, - "allow_dtmf_input": schema.BoolAttribute{ - Required: true, - }, - }, - }, - "audio_and_dtmf_input_specification": schema.SingleNestedBlock{ - Optional: true, - Attributes: map[strings]schema.Attribute{ - "start_timeout_ms": schema.Int32Attribute{ - Required: true, - }, - }, - "audio_specification": schema.SingleNestedBlock{ - Optional: true, - Attributes: map[strings]schema.Attribute{ - "end_timeout_ms": schema.Int32Attribute{ - Required: true, - }, - "max_length_ms": schema.Int32Attribute{ - Required: true, - }, - }, - }, - "dtmf_specification": schema.SingleNestedBlock{ - Optional: true, - Attributes: map[strings]schema.Attribute{ - "deletion_character": schema.StringAttribute{ - Required: true, - }, - "end_character": schema.StringAttribute{ - Required: true, - }, - "end_timeout_ms": schema.StringAttribute{ - Required: true, - }, - "max_length": schema.Int32Attribute{ - Required: true, - }, - }, }, }, - "text_input_specification": schema.SingleNestedBlock{ - Optional: true, - Attributes: map[strings]schema.Attribute{ - "start_timeout_ms": schema.Int32Attribute{ - Required: true, - }, - }, - }, - }, + }, //end of prompt specification }, }, }, "sample_utterances": schema.ListNestedBlock{ Optional: true, - Attributes: map[strings]schema.Attribute{ - "utterance": schema.StringAttribute{ - Required: true, + NestedObject: schema.NestedBlockObject{ + Attributes: map[strings]schema.Attribute{ + "utterance": schema.StringAttribute{ + Required: true, + }, }, }, }, @@ -480,92 +454,9 @@ func (r *resourceSlot) Schema(ctx context.Context, req resource.SchemaRequest, r "capture_conditional": schema.SingleNestedBlock{ Optional: true, Blocks: map[string]schema.Block{ - "condition_specification": schema.SingleNestedBlock{ - Optional: true, - Attributes: map[string]schema.Attribute{ - "active": schema.BoolAttribute{ - Required: true, - }, - }, - Blocks: map[string]schema.Block{ - "conditional_branch": schema.ListNestedBlock{ - Required: true, - NestedObject: schema.NestedBlockObject{ - Attributes: map[string]schema.Attribute{ - "name": schema.StringAttribute{ - Required: true, - }, - }, - Blocks: map[string]schema.Block{ - "condition": schema.SingleNestedBlock{ - Validators: []validator.Object{ - objectvalidator.IsRequired(), - }, - Attributes: map[string]schema.Attribute{ - "expression_string": schema.StringAttribute{ - Required: true, - }, - }, - }, - "next_step": schema.SingleNestedBlock{ - Validators: []validator.Object{ - objectvalidator.IsRequired(), - }, - NestedObject: schema.NestedBlockObject{ - Blocks: map[string]schema.SingleNestedBlock{ - "dialog_state": schema.SingleNestedBlock{ - Optional: true, - Blocks: map[string]SingleNestedBlock{ - "dialog_action": schema.SingleNestedBlock{ - Optional: true, - Attributes: map[string]schema.Attribute{ - "slot_to_elicit": schema.StringAttribute{ - Optional: true, - }, - "supress_next_message": schema.BoolAttribute{ - Optional: true, - }, - }, - Blocks: map[string]schema.Block{ - "type": schema.StringAttribute{ - Required: true, - Validators: []validator.String{ - enum.FrameworkValidate[awstypes.DialogActionType](), - }, - }, - }, - }, - "intent": schema.SingleNestedBlock{ - Optional: true, - Attributes: map[string]schema.Attribute{ - "intent_overide": schema.NestedBlockObject{ - Attributes: map[string]schema.Attribute{ - "name": schema.StringAttribute{ - Optional: true, - }, - NestedObject: schema.NestedBlockObject{ - Blocks: map[string]schema.Block{ - "slots": schema.MapAttribute{ - NestedObject: slotValueOverrideO, - }, - }, - }, - }, - }, - }, - "session_attributes": schema.MapAttribute{ - ElementType: types.StringType, - }, - }, - }, - }, - "response" - }, - }, - }, - }, - }, - }, + "condition_specification": conditionalSpecificationLNB, + "capture_next_step": dialogStateNBO, + "capture_resonse": responseSpecificationLNB, }, }, }, From df7bc4b8c0ca22757643e003177f774df8a89c0f Mon Sep 17 00:00:00 2001 From: Sharon Nam Date: Tue, 28 Nov 2023 14:50:52 -0800 Subject: [PATCH 03/42] initial commit --- internal/service/lexv2models/slot.go | 1193 +++++++++++++++++ internal/service/lexv2models/slot_test.go | 334 +++++ website/docs/r/lexv2models_slot.html.markdown | 69 + 3 files changed, 1596 insertions(+) create mode 100644 internal/service/lexv2models/slot.go create mode 100644 internal/service/lexv2models/slot_test.go create mode 100644 website/docs/r/lexv2models_slot.html.markdown diff --git a/internal/service/lexv2models/slot.go b/internal/service/lexv2models/slot.go new file mode 100644 index 00000000000..272068a9b45 --- /dev/null +++ b/internal/service/lexv2models/slot.go @@ -0,0 +1,1193 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package lexv2models + +import ( + "context" + "errors" + "time" + + "github.com/aws/aws-sdk-go-v2/aws" + awstypes "github.com/aws/aws-sdk-go-v2/service/lexmodelsv2/types" + "github.com/hashicorp/terraform-plugin-framework-timeouts/resource/timeouts" + "github.com/hashicorp/terraform-plugin-framework-validators/listvalidator" + "github.com/hashicorp/terraform-plugin-framework/attr" + "github.com/hashicorp/terraform-plugin-framework/datasource/schema" + "github.com/hashicorp/terraform-plugin-framework/diag" + "github.com/hashicorp/terraform-plugin-framework/path" + "github.com/hashicorp/terraform-plugin-framework/provider/schema" + "github.com/hashicorp/terraform-plugin-framework/resource" + "github.com/hashicorp/terraform-plugin-framework/resource/schema" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" + "github.com/hashicorp/terraform-plugin-framework/schema/validator" + "github.com/hashicorp/terraform-plugin-framework/types" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/retry" + "github.com/hashicorp/terraform-provider-aws/internal/create" + "github.com/hashicorp/terraform-provider-aws/internal/enum" + "github.com/hashicorp/terraform-provider-aws/internal/framework" + "github.com/hashicorp/terraform-provider-aws/internal/framework/flex" + "github.com/hashicorp/terraform-provider-aws/internal/tfresource" + "github.com/hashicorp/terraform-provider-aws/names" +) + +// @FrameworkResource(name="Slot") +func newResourceSlot(_ context.Context) (resource.ResourceWithConfigure, error) { + r := &resourceSlot{} + + r.SetDefaultCreateTimeout(30 * time.Minute) + r.SetDefaultUpdateTimeout(30 * time.Minute) + r.SetDefaultDeleteTimeout(30 * time.Minute) + + return r, nil +} + +const ( + ResNameSlot = "Slot" +) + +type resourceSlot struct { + framework.ResourceWithConfigure + framework.WithTimeouts +} + +func (r *resourceSlot) Metadata(_ context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { + resp.TypeName = "aws_lexv2models_slot" +} + +// TIP: ==== SCHEMA ==== +// In the schema, add each of the attributes in snake case (e.g., +// delete_automated_backups). +// +// Formatting rules: +// * Alphabetize attributes to make them easier to find. +// * Do not add a blank line between attributes. +// +// Attribute basics: +// - If a user can provide a value ("configure a value") for an +// attribute (e.g., instances = 5), we call the attribute an +// "argument." +// - You change the way users interact with attributes using: +// - Required +// - Optional +// - Computed +// - There are only four valid combinations: +// +// 1. Required only - the user must provide a value +// Required: true, +// +// 2. Optional only - the user can configure or omit a value; do not +// use Default or DefaultFunc +// +// Optional: true, +// +// 3. Computed only - the provider can provide a value but the user +// cannot, i.e., read-only +// +// Computed: true, +// +// 4. Optional AND Computed - the provider or user can provide a value; +// use this combination if you are using Default +// +// Optional: true, +// Computed: true, +// +// You will typically find arguments in the input struct +// (e.g., CreateDBInstanceInput) for the create operation. Sometimes +// they are only in the input struct (e.g., ModifyDBInstanceInput) for +// the modify operation. +// +// For more about schema options, visit +// https://developer.hashicorp.com/terraform/plugin/framework/handling-data/schemas?page=schemas +func (r *resourceSlot) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { + slotValueOverrideO := types.ObjectType{ + AttrTypes: map[string]attr.Type{ + "shape": types.StringType, + "value": types.ObjectType{ + AttrTypes: map[string]attr.Types{ + "interpreted_value" : types.StringType, + }, + }, + }, + } + slotValueOverrideO.AttrTypes["values"] = slotValueOverrideO + + messageNBO := stypes.Object{ + Blocks: map[string]schema.Block{ + "custom_playload": schema.ListedNestedBlock{ + Validators: []validator.List{ + listvalidator.SizeAtMost(1), + }, + NestedObject: schema.NestedBlockObject{ + Attributes: map[string]schema.Attribute{ + "value": schema.StringAttribute{ + Required: true, + }, + }, + }, + }, + "image_response_card": schema.ListNestedBlock{ + Validators: []validator.List{ + listvalidator.SizeAtMost(1), + }, + NestedObject: schema.NestedBlockObject{ + Attributes: map[string]schema.Attribute{ + "image_url": schema.StringAttribute{ + Optional: true, + }, + "subtitle": schema.StringAttribute{ + Optional: true, + }, + "title": schema.StringAttribute{ + Required: true, + }, + }, + Blocks: map[string]schema.Block{ + "button": schema.ListNestedBlock{ + Nestedobject: schema.NestedBlockObject{ + Attributes: map[string]schema.Attribute{ + + } + } + } + } + }, + } + + } + "message": schema.NestedBlockObject{ + Required: true, + Blocks: map[string]schema.Block{ + + + Blocks: map[string]schema.Block{ + "buttons": schema.ListNestedBlock{ + Validators: []validator.List{ + listvalidator.SizeAtMost(1), + }, + NestedObject: schema.NestedBlockObject{ + Attributes: map[strings]schema.Attribute{ + "text": schema.StringAttribute{ + Required: true, + }, + "value": schema.StringAttribute{ + Required: true, + }, + }, + }, + }, + }, + }, + "plain_text_message": schema.SingleNestedBlock{ + Attributes: map[strings]schema.Attribute{ + "value": schema.StringAttribute{ + Required: true, + }, + }, + }, + "ssml_message": schema.SingleNestedBlock{ + Attributes: map[strings]schema.Attribute{ + "value": schema.StringAttribute{ + Required: true, + }, + }, + }, + }, + }, + } + + resp.Schema = schema.Schema{ + Attributes: map[string]schema.Attribute{ + // "arn": framework.ARNAttributeComputedOnly(), + "bot_id": schema.StringAttribute{ + Required: true, + PlanModifiers: []planmodifier.String{ + stringplanmodifier.RequiresReplace(), + }, + }, + "bot_version": schema.StringAttribute{ + Required: true, + PlanModifiers: []planmodifier.String{ + stringplanmodifier.RequiresReplace(), + }, + }, + "description": schema.StringAttribute{ + Optional: true, + }, + "intent_id": schema.StringAttribute{ + Required: true, + PlanModifiers: []planmodifier.String{ + stringplanmodifier.RequiresReplace(), + }, + }, + "locale_id": schema.StringAttribute{ + Required: true, + PlanModifiers: []planmodifier.String{ + stringplanmodifier.RequiresReplace(), + }, + }, + // "id": framework.IDAttribute(), + "name": schema.StringAttribute{ + Required: true, + PlanModifiers: []planmodifier.String{ + stringplanmodifier.RequiresReplace(), + }, + }, + "slot_type_id": schema.StringAttribute{ + Optional: true, + }, + }, + Blocks: map[string]schema.Block{ + "value_elicitation_setting": schema.SingleNestedBlock{ + Attributes: map[string]schema.Attribute{ + "slot_constraint": schema.StringAttribute{ + Required: true, + Validators: []validator.String{ + enum.FrameworkValidate[awstypes.SlotConstraint](), + }, + }, + }, + Blocks: map[string]schema.Block{ + "default_value_specification": schema.SingleNestedBlock{ + Blocks: map[string]schema.Block{ + "default_value_list": schema.ListNestedBlock{ + Validators: []validator.List{ + listvalidator.SizeAtLeast(1), + }, + NestedObject: schema.NestedBlockObject{ + Attributes: map[string]schema.Attribute{ + "default_value": schema.StringAttribute{ + Required: true, + }, + }, + }, + }, + }, + }, + "prompt_specification": schema.SingleNestedBlock{ + Attributes: map[string]schema.Attribute{ + "max_retries": schema.Int32Attribute{ + Required: true, + }, + "allow_interrupt": schema.BoolAttribute{ + Optional: true, + }, + "message_selection_strategy": schema.StringAttribute{ + Optional: true, + Validators: []validator.String{ + enum.FrameworkValidate[awstypes.MessageSelectionStrategy](), + }, + }, + }, + Blocks: map[string]schema.Block{ + "message_groups": schema.ListNestedBlock{ + Validators: []validator.List{ + listvalidator.SizeAtLeast(1), + }, + Blocks: map[string]schema.Block{ + "message": schema.SingleNestedBlock{ + Required: true, + Blocks: map[string]schema.Block{ + "custom_payload": schema.SingleNestedBlock{ + NestedObject: schema.NestedBlockObject{ + Attributes: map[string]schema.Attribute{ + "value": schema.StringAttribute{ + Required: true, + }, + }, + }, + }, + "image_response_card": schema.SingleNestedBlock{ + Attributes: map[strings]schema.Attribute{ + "title": schema.StringAttribute{ + Required: true, + }, + "image_url": schema.StringAttribute{ + Optional: true, + }, + "subtitle": schema.StringAttribute{ + Optional: true, + }, + }, + Blocks: map[string]schema.Block{ + "buttons": schema.ListNestedBlock{ + Validators: []validator.List{ + listvalidator.SizeAtMost(1), + }, + NestedObject: schema.NestedBlockObject{ + Attributes: map[strings]schema.Attribute{ + "text": schema.StringAttribute{ + Required: true, + }, + "value": schema.StringAttribute{ + Required: true, + }, + }, + }, + }, + }, + }, + "plain_text_message": schema.SingleNestedBlock{ + Attributes: map[strings]schema.Attribute{ + "value": schema.StringAttribute{ + Required: true, + }, + }, + }, + "ssml_message": schema.SingleNestedBlock{ + Attributes: map[strings]schema.Attribute{ + "value": schema.StringAttribute{ + Required: true, + }, + }, + }, + }, + "variations": schema.ListNestedBlock{ + Blocks: map[string]schema.Block{ + "custom_payload": schema.SingleNestedBlock{ + Attributes: map[string]schema.Attribute{ + "value": schema.StringAttribute{ + Required: true, + }, + }, + }, + "image_response_card": schema.SingleNestedBlock{ + Attributes: map[strings]schema.Attribute{ + "title": schema.StringAttribute{ + Required: true, + }, + "image_url": schema.StringAttribute{ + Optional: true, + }, + "subtitle": schema.StringAttribute{ + Optional: true, + }, + }, + Blocks: map[string]schema.Block{ + "buttons": schema.ListNestedBlock{ + Validators: []validator.List{ + listvalidator.SizeAtMost(1), + }, + NestedObject: schema.NestedBlockObject{ + Attributes: map[strings]schema.Attribute{ + "text": schema.StringAttribute{ + Required: true, + }, + "value": schema.StringAttribute{ + Required: true, + }, + }, + }, + }, + }, + }, + "plain_text_message": schema.SingleNestedBlock{ + Attributes: map[strings]schema.Attribute{ + "value": schema.StringAttribute{ + Required: true, + }, + }, + }, + "ssml_message": schema.SingleNestedBlock{ + Attributes: map[strings]schema.Attribute{ + "value": schema.StringAttribute{ + Required: true, + }, + }, + }, + }, + }, + }, + }, + "prompt_attempts_specification": schema.MapAttribute{ + Optional: true, + Attributes: map[strings]schema.Attribute{ + "allow_interrupts": schema.BoolAttribute{ + Optional: true, + }, + }, + Blocks: map[strings]schema.Block{ + "allow_input_types": schema.SingleNestedBlock{ + Attributes: map[strings]schema.Attribute{ + "allow_audio_input": schema.BoolAttribute{ + Required: true, + }, + "allow_dtmf_input": schema.BoolAttribute{ + Required: true, + }, + }, + }, + "audio_and_dtmf_input_specification": schema.SingleNestedBlock{ + Optional: true, + Attributes: map[strings]schema.Attribute{ + "start_timeout_ms": schema.Int32Attribute{ + Required: true, + }, + }, + "audio_specification": schema.SingleNestedBlock{ + Optional: true, + Attributes: map[strings]schema.Attribute{ + "end_timeout_ms": schema.Int32Attribute{ + Required: true, + }, + "max_length_ms": schema.Int32Attribute{ + Required: true, + }, + }, + }, + "dtmf_specification": schema.SingleNestedBlock{ + Optional: true, + Attributes: map[strings]schema.Attribute{ + "deletion_character": schema.StringAttribute{ + Required: true, + }, + "end_character": schema.StringAttribute{ + Required: true, + }, + "end_timeout_ms": schema.StringAttribute{ + Required: true, + }, + "max_length": schema.Int32Attribute{ + Required: true, + }, + }, + }, + }, + "text_input_specification": schema.SingleNestedBlock{ + Optional: true, + Attributes: map[strings]schema.Attribute{ + "start_timeout_ms": schema.Int32Attribute{ + Required: true, + }, + }, + }, + }, + }, + }, + }, + "sample_utterances": schema.ListNestedBlock{ + Optional: true, + Attributes: map[strings]schema.Attribute{ + "utterance": schema.StringAttribute{ + Required: true, + }, + }, + }, + "slot_capture_setting": schema.SingleNestedBlock{ + Optional: true, + Blocks: map[string]schema.Block{ + "capture_conditional": schema.SingleNestedBlock{ + Optional: true, + Blocks: map[string]schema.Block{ + "condition_specification": schema.SingleNestedBlock{ + Optional: true, + Attributes: map[string]schema.Attribute{ + "active": schema.BoolAttribute{ + Required: true, + }, + }, + Blocks: map[string]schema.Block{ + "conditional_branch": schema.ListNestedBlock{ + Required: true, + NestedObject: schema.NestedBlockObject{ + Attributes: map[string]schema.Attribute{ + "name": schema.StringAttribute{ + Required: true, + }, + }, + Blocks: map[string]schema.Block{ + "condition": schema.SingleNestedBlock{ + Validators: []validator.Object{ + objectvalidator.IsRequired(), + }, + Attributes: map[string]schema.Attribute{ + "expression_string": schema.StringAttribute{ + Required: true, + }, + }, + }, + "next_step": schema.SingleNestedBlock{ + Validators: []validator.Object{ + objectvalidator.IsRequired(), + }, + NestedObject: schema.NestedBlockObject{ + Blocks: map[string]schema.SingleNestedBlock{ + "dialog_state": schema.SingleNestedBlock{ + Optional: true, + Blocks: map[string]SingleNestedBlock{ + "dialog_action": schema.SingleNestedBlock{ + Optional: true, + Attributes: map[string]schema.Attribute{ + "slot_to_elicit": schema.StringAttribute{ + Optional: true, + }, + "supress_next_message": schema.BoolAttribute{ + Optional: true, + }, + }, + Blocks: map[string]schema.Block{ + "type": schema.StringAttribute{ + Required: true, + Validators: []validator.String{ + enum.FrameworkValidate[awstypes.DialogActionType](), + }, + }, + }, + }, + "intent": schema.SingleNestedBlock{ + Optional: true, + Attributes: map[string]schema.Attribute{ + "intent_overide": schema.NestedBlockObject{ + Attributes: map[string]schema.Attribute{ + "name": schema.StringAttribute{ + Optional: true, + }, + NestedObject: schema.NestedBlockObject{ + Blocks: map[string]schema.Block{ + "slots": schema.MapAttribute{ + NestedObject: slotValueOverrideO, + }, + }, + }, + }, + }, + }, + "session_attributes": schema.MapAttribute{ + ElementType: types.StringType, + }, + }, + }, + }, + "response" + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, +} + +func (r *resourceSlot) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { + // TIP: ==== RESOURCE CREATE ==== + // Generally, the Create function should do the following things. Make + // sure there is a good reason if you don't do one of these. + // + // 1. Get a client connection to the relevant service + // 2. Fetch the plan + // 3. Populate a create input structure + // 4. Call the AWS create/put function + // 5. Using the output from the create function, set the minimum arguments + // and attributes for the Read function to work, as well as any computed + // only attributes. + // 6. Use a waiter to wait for create to complete + // 7. Save the request plan to response state + + // TIP: -- 1. Get a client connection to the relevant service + conn := r.Meta().LexV2ModelsClient(ctx) + + // TIP: -- 2. Fetch the plan + var plan resourceSlotData + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + if resp.Diagnostics.HasError() { + return + } + + // TIP: -- 3. Populate a create input structure + in := &lexv2models.CreateSlotInput{ + // TIP: Mandatory or fields that will always be present can be set when + // you create the Input structure. (Replace these with real fields.) + SlotName: aws.String(plan.Name.ValueString()), + SlotType: aws.String(plan.Type.ValueString()), + } + + if !plan.Description.IsNull() { + // TIP: Optional fields should be set based on whether or not they are + // used. + in.Description = aws.String(plan.Description.ValueString()) + } + if !plan.ComplexArgument.IsNull() { + // TIP: Use an expander to assign a complex argument. The elements must be + // deserialized into the appropriate struct before being passed to the expander. + var tfList []complexArgumentData + resp.Diagnostics.Append(plan.ComplexArgument.ElementsAs(ctx, &tfList, false)...) + if resp.Diagnostics.HasError() { + return + } + + in.ComplexArgument = expandComplexArgument(tfList) + } + + // TIP: -- 4. Call the AWS create function + out, err := conn.CreateSlot(ctx, in) + if err != nil { + // TIP: Since ID has not been set yet, you cannot use plan.ID.String() + // in error messages at this point. + resp.Diagnostics.AddError( + create.ProblemStandardMessage(names.LexV2Models, create.ErrActionCreating, ResNameSlot, plan.Name.String(), err), + err.Error(), + ) + return + } + if out == nil || out.Slot == nil { + resp.Diagnostics.AddError( + create.ProblemStandardMessage(names.LexV2Models, create.ErrActionCreating, ResNameSlot, plan.Name.String(), nil), + errors.New("empty output").Error(), + ) + return + } + + // TIP: -- 5. Using the output from the create function, set the minimum attributes + plan.ARN = flex.StringToFramework(ctx, out.Slot.Arn) + plan.ID = flex.StringToFramework(ctx, out.Slot.SlotId) + + // TIP: -- 6. Use a waiter to wait for create to complete + createTimeout := r.CreateTimeout(ctx, plan.Timeouts) + _, err = waitSlotCreated(ctx, conn, plan.ID.ValueString(), createTimeout) + if err != nil { + resp.Diagnostics.AddError( + create.ProblemStandardMessage(names.LexV2Models, create.ErrActionWaitingForCreation, ResNameSlot, plan.Name.String(), err), + err.Error(), + ) + return + } + + // TIP: -- 7. Save the request plan to response state + resp.Diagnostics.Append(resp.State.Set(ctx, plan)...) +} + +func (r *resourceSlot) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { + // TIP: ==== RESOURCE READ ==== + // Generally, the Read function should do the following things. Make + // sure there is a good reason if you don't do one of these. + // + // 1. Get a client connection to the relevant service + // 2. Fetch the state + // 3. Get the resource from AWS + // 4. Remove resource from state if it is not found + // 5. Set the arguments and attributes + // 6. Set the state + + // TIP: -- 1. Get a client connection to the relevant service + conn := r.Meta().LexV2ModelsClient(ctx) + + // TIP: -- 2. Fetch the state + var state resourceSlotData + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + + // TIP: -- 3. Get the resource from AWS using an API Get, List, or Describe- + // type function, or, better yet, using a finder. + out, err := findSlotByID(ctx, conn, state.ID.ValueString()) + // TIP: -- 4. Remove resource from state if it is not found + if tfresource.NotFound(err) { + resp.State.RemoveResource(ctx) + return + } + if err != nil { + resp.Diagnostics.AddError( + create.ProblemStandardMessage(names.LexV2Models, create.ErrActionSetting, ResNameSlot, state.ID.String(), err), + err.Error(), + ) + return + } + + // TIP: -- 5. Set the arguments and attributes + // + // For simple data types (i.e., schema.StringAttribute, schema.BoolAttribute, + // schema.Int64Attribute, and schema.Float64Attribue), simply setting the + // appropriate data struct field is sufficient. The flex package implements + // helpers for converting between Go and Plugin-Framework types seamlessly. No + // error or nil checking is necessary. + // + // However, there are some situations where more handling is needed such as + // complex data types (e.g., schema.ListAttribute, schema.SetAttribute). In + // these cases the flatten function may have a diagnostics return value, which + // should be appended to resp.Diagnostics. + state.ARN = flex.StringToFramework(ctx, out.Arn) + state.ID = flex.StringToFramework(ctx, out.SlotId) + state.Name = flex.StringToFramework(ctx, out.SlotName) + state.Type = flex.StringToFramework(ctx, out.SlotType) + + // TIP: Setting a complex type. + complexArgument, d := flattenComplexArgument(ctx, out.ComplexArgument) + resp.Diagnostics.Append(d...) + state.ComplexArgument = complexArgument + + // TIP: -- 6. Set the state + resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) +} + +func (r *resourceSlot) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { + // TIP: ==== RESOURCE UPDATE ==== + // Not all resources have Update functions. There are a few reasons: + // a. The AWS API does not support changing a resource + // b. All arguments have RequiresReplace() plan modifiers + // c. The AWS API uses a create call to modify an existing resource + // + // In the cases of a. and b., the resource will not have an update method + // defined. In the case of c., Update and Create can be refactored to call + // the same underlying function. + // + // The rest of the time, there should be an Update function and it should + // do the following things. Make sure there is a good reason if you don't + // do one of these. + // + // 1. Get a client connection to the relevant service + // 2. Fetch the plan and state + // 3. Populate a modify input structure and check for changes + // 4. Call the AWS modify/update function + // 5. Use a waiter to wait for update to complete + // 6. Save the request plan to response state + // TIP: -- 1. Get a client connection to the relevant service + conn := r.Meta().LexV2ModelsClient(ctx) + + // TIP: -- 2. Fetch the plan + var plan, state resourceSlotData + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + + // TIP: -- 3. Populate a modify input structure and check for changes + if !plan.Name.Equal(state.Name) || + !plan.Description.Equal(state.Description) || + !plan.ComplexArgument.Equal(state.ComplexArgument) || + !plan.Type.Equal(state.Type) { + + in := &lexv2models.UpdateSlotInput{ + // TIP: Mandatory or fields that will always be present can be set when + // you create the Input structure. (Replace these with real fields.) + SlotId: aws.String(plan.ID.ValueString()), + SlotName: aws.String(plan.Name.ValueString()), + SlotType: aws.String(plan.Type.ValueString()), + } + + if !plan.Description.IsNull() { + // TIP: Optional fields should be set based on whether or not they are + // used. + in.Description = aws.String(plan.Description.ValueString()) + } + if !plan.ComplexArgument.IsNull() { + // TIP: Use an expander to assign a complex argument. The elements must be + // deserialized into the appropriate struct before being passed to the expander. + var tfList []complexArgumentData + resp.Diagnostics.Append(plan.ComplexArgument.ElementsAs(ctx, &tfList, false)...) + if resp.Diagnostics.HasError() { + return + } + + in.ComplexArgument = expandComplexArgument(tfList) + } + + // TIP: -- 4. Call the AWS modify/update function + out, err := conn.UpdateSlot(ctx, in) + if err != nil { + resp.Diagnostics.AddError( + create.ProblemStandardMessage(names.LexV2Models, create.ErrActionUpdating, ResNameSlot, plan.ID.String(), err), + err.Error(), + ) + return + } + if out == nil || out.Slot == nil { + resp.Diagnostics.AddError( + create.ProblemStandardMessage(names.LexV2Models, create.ErrActionUpdating, ResNameSlot, plan.ID.String(), nil), + errors.New("empty output").Error(), + ) + return + } + + // TIP: Using the output from the update function, re-set any computed attributes + plan.ARN = flex.StringToFramework(ctx, out.Slot.Arn) + plan.ID = flex.StringToFramework(ctx, out.Slot.SlotId) + } + + // TIP: -- 5. Use a waiter to wait for update to complete + updateTimeout := r.UpdateTimeout(ctx, plan.Timeouts) + _, err := waitSlotUpdated(ctx, conn, plan.ID.ValueString(), updateTimeout) + if err != nil { + resp.Diagnostics.AddError( + create.ProblemStandardMessage(names.LexV2Models, create.ErrActionWaitingForUpdate, ResNameSlot, plan.ID.String(), err), + err.Error(), + ) + return + } + + // TIP: -- 6. Save the request plan to response state + resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) +} + +func (r *resourceSlot) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { + // TIP: ==== RESOURCE DELETE ==== + // Most resources have Delete functions. There are rare situations + // where you might not need a delete: + // a. The AWS API does not provide a way to delete the resource + // b. The point of your resource is to perform an action (e.g., reboot a + // server) and deleting serves no purpose. + // + // The Delete function should do the following things. Make sure there + // is a good reason if you don't do one of these. + // + // 1. Get a client connection to the relevant service + // 2. Fetch the state + // 3. Populate a delete input structure + // 4. Call the AWS delete function + // 5. Use a waiter to wait for delete to complete + // TIP: -- 1. Get a client connection to the relevant service + conn := r.Meta().LexV2ModelsClient(ctx) + + // TIP: -- 2. Fetch the state + var state resourceSlotData + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + + // TIP: -- 3. Populate a delete input structure + in := &lexv2models.DeleteSlotInput{ + SlotId: aws.String(state.ID.ValueString()), + } + + // TIP: -- 4. Call the AWS delete function + _, err := conn.DeleteSlot(ctx, in) + // TIP: On rare occassions, the API returns a not found error after deleting a + // resource. If that happens, we don't want it to show up as an error. + if err != nil { + var nfe *awstypes.ResourceNotFoundException + if errors.As(err, &nfe) { + return + } + resp.Diagnostics.AddError( + create.ProblemStandardMessage(names.LexV2Models, create.ErrActionDeleting, ResNameSlot, state.ID.String(), err), + err.Error(), + ) + return + } + + // TIP: -- 5. Use a waiter to wait for delete to complete + deleteTimeout := r.DeleteTimeout(ctx, state.Timeouts) + _, err = waitSlotDeleted(ctx, conn, state.ID.ValueString(), deleteTimeout) + if err != nil { + resp.Diagnostics.AddError( + create.ProblemStandardMessage(names.LexV2Models, create.ErrActionWaitingForDeletion, ResNameSlot, state.ID.String(), err), + err.Error(), + ) + return + } +} + +// TIP: ==== TERRAFORM IMPORTING ==== +// If Read can get all the information it needs from the Identifier +// (i.e., path.Root("id")), you can use the PassthroughID importer. Otherwise, +// you'll need a custom import function. +// +// See more: +// https://developer.hashicorp.com/terraform/plugin/framework/resources/import +func (r *resourceSlot) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { + resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) +} + +// TIP: ==== STATUS CONSTANTS ==== +// Create constants for states and statuses if the service does not +// already have suitable constants. We prefer that you use the constants +// provided in the service if available (e.g., amp.WorkspaceStatusCodeActive). +const ( + statusChangePending = "Pending" + statusDeleting = "Deleting" + statusNormal = "Normal" + statusUpdated = "Updated" +) + +// TIP: ==== WAITERS ==== +// Some resources of some services have waiters provided by the AWS API. +// Unless they do not work properly, use them rather than defining new ones +// here. +// +// Sometimes we define the wait, status, and find functions in separate +// files, wait.go, status.go, and find.go. Follow the pattern set out in the +// service and define these where it makes the most sense. +// +// If these functions are used in the _test.go file, they will need to be +// exported (i.e., capitalized). +// +// You will need to adjust the parameters and names to fit the service. +func waitSlotCreated(ctx context.Context, conn *lexv2models.Client, id string, timeout time.Duration) (*lexv2models.Slot, error) { + stateConf := &retry.StateChangeConf{ + Pending: []string{}, + Target: []string{statusNormal}, + Refresh: statusSlot(ctx, conn, id), + Timeout: timeout, + NotFoundChecks: 20, + ContinuousTargetOccurence: 2, + } + + outputRaw, err := stateConf.WaitForStateContext(ctx) + if out, ok := outputRaw.(*lexv2models.Slot); ok { + return out, err + } + + return nil, err +} + +// TIP: It is easier to determine whether a resource is updated for some +// resources than others. The best case is a status flag that tells you when +// the update has been fully realized. Other times, you can check to see if a +// key resource argument is updated to a new value or not. +func waitSlotUpdated(ctx context.Context, conn *lexv2models.Client, id string, timeout time.Duration) (*lexv2models.Slot, error) { + stateConf := &retry.StateChangeConf{ + Pending: []string{statusChangePending}, + Target: []string{statusUpdated}, + Refresh: statusSlot(ctx, conn, id), + Timeout: timeout, + NotFoundChecks: 20, + ContinuousTargetOccurence: 2, + } + + outputRaw, err := stateConf.WaitForStateContext(ctx) + if out, ok := outputRaw.(*lexv2models.Slot); ok { + return out, err + } + + return nil, err +} + +// TIP: A deleted waiter is almost like a backwards created waiter. There may +// be additional pending states, however. +func waitSlotDeleted(ctx context.Context, conn *lexv2models.Client, id string, timeout time.Duration) (*lexv2models.Slot, error) { + stateConf := &retry.StateChangeConf{ + Pending: []string{statusDeleting, statusNormal}, + Target: []string{}, + Refresh: statusSlot(ctx, conn, id), + Timeout: timeout, + } + + outputRaw, err := stateConf.WaitForStateContext(ctx) + if out, ok := outputRaw.(*lexv2models.Slot); ok { + return out, err + } + + return nil, err +} + +// TIP: ==== STATUS ==== +// The status function can return an actual status when that field is +// available from the API (e.g., out.Status). Otherwise, you can use custom +// statuses to communicate the states of the resource. +// +// Waiters consume the values returned by status functions. Design status so +// that it can be reused by a create, update, and delete waiter, if possible. +func statusSlot(ctx context.Context, conn *lexv2models.Client, id string) retry.StateRefreshFunc { + return func() (interface{}, string, error) { + out, err := findSlotByID(ctx, conn, id) + if tfresource.NotFound(err) { + return nil, "", nil + } + + if err != nil { + return nil, "", err + } + + return out, aws.ToString(out.Status), nil + } +} + +// TIP: ==== FINDERS ==== +// The find function is not strictly necessary. You could do the API +// request from the status function. However, we have found that find often +// comes in handy in other places besides the status function. As a result, it +// is good practice to define it separately. +func findSlotByID(ctx context.Context, conn *lexv2models.Client, id string) (*lexv2models.Slot, error) { + in := &lexv2models.GetSlotInput{ + Id: aws.String(id), + } + + out, err := conn.GetSlot(ctx, in) + if err != nil { + var nfe *awstypes.ResourceNotFoundException + if errors.As(err, &nfe) { + return nil, &retry.NotFoundError{ + LastError: err, + LastRequest: in, + } + } + + return nil, err + } + + if out == nil || out.Slot == nil { + return nil, tfresource.NewEmptyResultError(in) + } + + return out.Slot, nil +} + +// TIP: ==== FLEX ==== +// Flatteners and expanders ("flex" functions) help handle complex data +// types. Flatteners take an API data type and return the equivalent Plugin-Framework +// type. In other words, flatteners translate from AWS -> Terraform. +// +// On the other hand, expanders take a Terraform data structure and return +// something that you can send to the AWS API. In other words, expanders +// translate from Terraform -> AWS. +// +// See more: +// https://hashicorp.github.io/terraform-provider-aws/data-handling-and-conversion/ +func flattenComplexArgument(ctx context.Context, apiObject *lexv2models.ComplexArgument) (types.List, diag.Diagnostics) { + var diags diag.Diagnostics + elemType := types.ObjectType{AttrTypes: complexArgumentAttrTypes} + + if apiObject == nil { + return types.ListNull(elemType), diags + } + + obj := map[string]attr.Value{ + "nested_required": flex.StringValueToFramework(ctx, apiObject.NestedRequired), + "nested_optional": flex.StringValueToFramework(ctx, apiObject.NestedOptional), + } + objVal, d := types.ObjectValue(complexArgumentAttrTypes, obj) + diags.Append(d...) + + listVal, d := types.ListValue(elemType, []attr.Value{objVal}) + diags.Append(d...) + + return listVal, diags +} + +// TIP: Often the AWS API will return a slice of structures in response to a +// request for information. Sometimes you will have set criteria (e.g., the ID) +// that means you'll get back a one-length slice. This plural function works +// brilliantly for that situation too. +func flattenComplexArguments(ctx context.Context, apiObjects []*lexv2models.ComplexArgument) (types.List, diag.Diagnostics) { + var diags diag.Diagnostics + elemType := types.ObjectType{AttrTypes: complexArgumentAttrTypes} + + if len(apiObjects) == 0 { + return types.ListNull(elemType), diags + } + + elems := []attr.Value{} + for _, apiObject := range apiObjects { + if apiObject == nil { + continue + } + + obj := map[string]attr.Value{ + "nested_required": flex.StringValueToFramework(ctx, apiObject.NestedRequired), + "nested_optional": flex.StringValueToFramework(ctx, apiObject.NestedOptional), + } + objVal, d := types.ObjectValue(complexArgumentAttrTypes, obj) + diags.Append(d...) + + elems = append(elems, objVal) + } + + listVal, d := types.ListValue(elemType, elems) + diags.Append(d...) + + return listVal, diags +} + +// TIP: Remember, as mentioned above, expanders take a Terraform data structure +// and return something that you can send to the AWS API. In other words, +// expanders translate from Terraform -> AWS. +// +// See more: +// https://hashicorp.github.io/terraform-provider-aws/data-handling-and-conversion/ +func expandComplexArgument(tfList []complexArgumentData) *lexv2models.ComplexArgument { + if len(tfList) == 0 { + return nil + } + + tfObj := tfList[0] + apiObject := &lexv2models.ComplexArgument{ + NestedRequired: aws.String(tfObj.NestedRequired.ValueString()), + } + if !tfObj.NestedOptional.IsNull() { + apiObject.NestedOptional = aws.String(tfObj.NestedOptional.ValueString()) + } + + return apiObject +} + +// TIP: Even when you have a list with max length of 1, this plural function +// works brilliantly. However, if the AWS API takes a structure rather than a +// slice of structures, you will not need it. +func expandComplexArguments(tfList []complexArgumentData) []*lexv2models.ComplexArgument { + // TIP: The AWS API can be picky about whether you send a nil or zero- + // length for an argument that should be cleared. For example, in some + // cases, if you send a nil value, the AWS API interprets that as "make no + // changes" when what you want to say is "remove everything." Sometimes + // using a zero-length list will cause an error. + // + // As a result, here are two options. Usually, option 1, nil, will work as + // expected, clearing the field. But, test going from something to nothing + // to make sure it works. If not, try the second option. + // TIP: Option 1: Returning nil for zero-length list + if len(tfList) == 0 { + return nil + } + + var apiObject []*lexv2models.ComplexArgument + // TIP: Option 2: Return zero-length list for zero-length list. If option 1 does + // not work, after testing going from something to nothing (if that is + // possible), uncomment out the next line and remove option 1. + // + // apiObject := make([]*lexv2models.ComplexArgument, 0) + + for _, tfObj := range tfList { + item := &lexv2models.ComplexArgument{ + NestedRequired: aws.String(tfObj.NestedRequired.ValueString()), + } + if !tfObj.NestedOptional.IsNull() { + item.NestedOptional = aws.String(tfObj.NestedOptional.ValueString()) + } + + apiObject = append(apiObject, item) + } + + return apiObject +} + +// TIP: ==== DATA STRUCTURES ==== +// With Terraform Plugin-Framework configurations are deserialized into +// Go types, providing type safety without the need for type assertions. +// These structs should match the schema definition exactly, and the `tfsdk` +// tag value should match the attribute name. +// +// Nested objects are represented in their own data struct. These will +// also have a corresponding attribute type mapping for use inside flex +// functions. +// +// See more: +// https://developer.hashicorp.com/terraform/plugin/framework/handling-data/accessing-values +type resourceSlotData struct { + ARN types.String `tfsdk:"arn"` + ComplexArgument types.List `tfsdk:"complex_argument"` + Description types.String `tfsdk:"description"` + ID types.String `tfsdk:"id"` + Name types.String `tfsdk:"name"` + Timeouts timeouts.Value `tfsdk:"timeouts"` + Type types.String `tfsdk:"type"` +} + +type complexArgumentData struct { + NestedRequired types.String `tfsdk:"nested_required"` + NestedOptional types.String `tfsdk:"nested_optional"` +} + +var complexArgumentAttrTypes = map[string]attr.Type{ + "nested_required": types.StringType, + "nested_optional": types.StringType, +} diff --git a/internal/service/lexv2models/slot_test.go b/internal/service/lexv2models/slot_test.go new file mode 100644 index 00000000000..8a273063670 --- /dev/null +++ b/internal/service/lexv2models/slot_test.go @@ -0,0 +1,334 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package lexv2models_test +// **PLEASE DELETE THIS AND ALL TIP COMMENTS BEFORE SUBMITTING A PR FOR REVIEW!** +// +// TIP: ==== INTRODUCTION ==== +// Thank you for trying the skaff tool! +// +// You have opted to include these helpful comments. They all include "TIP:" +// to help you find and remove them when you're done with them. +// +// While some aspects of this file are customized to your input, the +// scaffold tool does *not* look at the AWS API and ensure it has correct +// function, structure, and variable names. It makes guesses based on +// commonalities. You will need to make significant adjustments. +// +// In other words, as generated, this is a rough outline of the work you will +// need to do. If something doesn't make sense for your situation, get rid of +// it. + +import ( + // TIP: ==== IMPORTS ==== + // This is a common set of imports but not customized to your code since + // your code hasn't been written yet. Make sure you, your IDE, or + // goimports -w fixes these imports. + // + // The provider linter wants your imports to be in two groups: first, + // standard library (i.e., "fmt" or "strings"), second, everything else. + // + // Also, AWS Go SDK v2 may handle nested structures differently than v1, + // using the services/lexv2models/types package. If so, you'll + // need to import types and reference the nested types, e.g., as + // types.. + "context" + "fmt" + "regexp" + "strings" + "testing" + + "github.com/YakDriver/regexache" + + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/service/lexv2models" + "github.com/aws/aws-sdk-go-v2/service/lexv2models/types" + sdkacctest "github.com/hashicorp/terraform-plugin-testing/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" + "github.com/hashicorp/terraform-provider-aws/internal/errs" + "github.com/hashicorp/terraform-provider-aws/names" + + // TIP: You will often need to import the package that this test file lives + // in. Since it is in the "test" context, it must import the package to use + // any normal context constants, variables, or functions. + tflexv2models "github.com/hashicorp/terraform-provider-aws/internal/service/lexv2models" +) + +// TIP: File Structure. The basic outline for all test files should be as +// follows. Improve this resource's maintainability by following this +// outline. +// +// 1. Package declaration (add "_test" since this is a test file) +// 2. Imports +// 3. Unit tests +// 4. Basic test +// 5. Disappears test +// 6. All the other tests +// 7. Helper functions (exists, destroy, check, etc.) +// 8. Functions that return Terraform configurations + +// TIP: ==== UNIT TESTS ==== +// This is an example of a unit test. Its name is not prefixed with +// "TestAcc" like an acceptance test. +// +// Unlike acceptance tests, unit tests do not access AWS and are focused on a +// function (or method). Because of this, they are quick and cheap to run. +// +// In designing a resource's implementation, isolate complex bits from AWS bits +// so that they can be tested through a unit test. We encourage more unit tests +// in the provider. +// +// Cut and dry functions using well-used patterns, like typical flatteners and +// expanders, don't need unit testing. However, if they are complex or +// intricate, they should be unit tested. +func TestSlotExampleUnitTest(t *testing.T) { + t.Parallel() + + testCases := []struct { + TestName string + Input string + Expected string + Error bool + }{ + { + TestName: "empty", + Input: "", + Expected: "", + Error: true, + }, + { + TestName: "descriptive name", + Input: "some input", + Expected: "some output", + Error: false, + }, + { + TestName: "another descriptive name", + Input: "more input", + Expected: "more output", + Error: false, + }, + } + + for _, testCase := range testCases { + testCase := testCase + t.Run(testCase.TestName, func(t *testing.T) { + t.Parallel() + got, err := tflexv2models.FunctionFromResource(testCase.Input) + + if err != nil && !testCase.Error { + t.Errorf("got error (%s), expected no error", err) + } + + if err == nil && testCase.Error { + t.Errorf("got (%s) and no error, expected error", got) + } + + if got != testCase.Expected { + t.Errorf("got %s, expected %s", got, testCase.Expected) + } + }) + } +} + +// TIP: ==== ACCEPTANCE TESTS ==== +// This is an example of a basic acceptance test. This should test as much of +// standard functionality of the resource as possible, and test importing, if +// applicable. We prefix its name with "TestAcc", the service, and the +// resource name. +// +// Acceptance test access AWS and cost money to run. +func TestAccLexV2ModelsSlot_basic(t *testing.T) { + ctx := acctest.Context(t) + // TIP: This is a long-running test guard for tests that run longer than + // 300s (5 min) generally. + if testing.Short() { + t.Skip("skipping long-running test in short mode") + } + + var slot lexv2models.DescribeSlotResponse + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + resourceName := "aws_lexv2models_slot.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { + acctest.PreCheck(ctx, t) + acctest.PreCheckPartitionHasService(t, names.LexV2ModelsEndpointID) + testAccPreCheck(ctx, t) + }, + ErrorCheck: acctest.ErrorCheck(t, names.LexV2ModelsEndpointID), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + CheckDestroy: testAccCheckSlotDestroy(ctx), + Steps: []resource.TestStep{ + { + Config: testAccSlotConfig_basic(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckSlotExists(ctx, resourceName, &slot), + resource.TestCheckResourceAttr(resourceName, "auto_minor_version_upgrade", "false"), + resource.TestCheckResourceAttrSet(resourceName, "maintenance_window_start_time.0.day_of_week"), + resource.TestCheckTypeSetElemNestedAttrs(resourceName, "user.*", map[string]string{ + "console_access": "false", + "groups.#": "0", + "username": "Test", + "password": "TestTest1234", + }), + acctest.MatchResourceAttrRegionalARN(resourceName, "arn", "lexv2models", regexache.MustCompile(`slot:+.`)), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"apply_immediately", "user"}, + }, + }, + }) +} + +func TestAccLexV2ModelsSlot_disappears(t *testing.T) { + ctx := acctest.Context(t) + if testing.Short() { + t.Skip("skipping long-running test in short mode") + } + + var slot lexv2models.DescribeSlotResponse + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + resourceName := "aws_lexv2models_slot.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { + acctest.PreCheck(ctx, t) + acctest.PreCheckPartitionHasService(t, names.LexV2ModelsEndpointID) + testAccPreCheck(t) + }, + ErrorCheck: acctest.ErrorCheck(t, names.LexV2ModelsEndpointID), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + CheckDestroy: testAccCheckSlotDestroy(ctx), + Steps: []resource.TestStep{ + { + Config: testAccSlotConfig_basic(rName, testAccSlotVersionNewer), + Check: resource.ComposeTestCheckFunc( + testAccCheckSlotExists(ctx, resourceName, &slot), + // TIP: The Plugin-Framework disappears helper is similar to the Plugin-SDK version, + // but expects a new resource factory function as the third argument. To expose this + // private function to the testing package, you may need to add a line like the following + // to exports_test.go: + // + // var ResourceSlot = newResourceSlot + acctest.CheckFrameworkResourceDisappears(ctx, acctest.Provider, tflexv2models.ResourceSlot, resourceName), + ), + ExpectNonEmptyPlan: true, + }, + }, + }) +} + +func testAccCheckSlotDestroy(ctx context.Context) resource.TestCheckFunc { + return func(s *terraform.State) error { + conn := acctest.Provider.Meta().(*conns.AWSClient).LexV2ModelsClient(ctx) + + for _, rs := range s.RootModule().Resources { + if rs.Type != "aws_lexv2models_slot" { + continue + } + + input := &lexv2models.DescribeSlotInput{ + SlotId: aws.String(rs.Primary.ID), + } + _, err := conn.DescribeSlot(ctx, &lexv2models.DescribeSlotInput{ + SlotId: aws.String(rs.Primary.ID), + }) + if errs.IsA[*types.ResourceNotFoundException](err){ + return nil + } + if err != nil { + return nil + } + + return create.Error(names.LexV2Models, create.ErrActionCheckingDestroyed, tflexv2models.ResNameSlot, rs.Primary.ID, errors.New("not destroyed")) + } + + return nil + } +} + +func testAccCheckSlotExists(ctx context.Context, name string, slot *lexv2models.DescribeSlotResponse) resource.TestCheckFunc { + return func(s *terraform.State) error { + rs, ok := s.RootModule().Resources[name] + if !ok { + return create.Error(names.LexV2Models, create.ErrActionCheckingExistence, tflexv2models.ResNameSlot, name, errors.New("not found")) + } + + if rs.Primary.ID == "" { + return create.Error(names.LexV2Models, create.ErrActionCheckingExistence, tflexv2models.ResNameSlot, name, errors.New("not set")) + } + + conn := acctest.Provider.Meta().(*conns.AWSClient).LexV2ModelsClient(ctx) + resp, err := conn.DescribeSlot(ctx, &lexv2models.DescribeSlotInput{ + SlotId: aws.String(rs.Primary.ID), + }) + + if err != nil { + return create.Error(names.LexV2Models, create.ErrActionCheckingExistence, tflexv2models.ResNameSlot, rs.Primary.ID, err) + } + + *slot = *resp + + return nil + } +} + +func testAccPreCheck(ctx context.Context, t *testing.T) { + conn := acctest.Provider.Meta().(*conns.AWSClient).LexV2ModelsClient(ctx) + + input := &lexv2models.ListSlotsInput{} + _, err := conn.ListSlots(ctx, input) + + if acctest.PreCheckSkipError(err) { + t.Skipf("skipping acceptance testing: %s", err) + } + if err != nil { + t.Fatalf("unexpected PreCheck error: %s", err) + } +} + +func testAccCheckSlotNotRecreated(before, after *lexv2models.DescribeSlotResponse) resource.TestCheckFunc { + return func(s *terraform.State) error { + if before, after := aws.ToString(before.SlotId), aws.ToString(after.SlotId); before != after { + return create.Error(names.LexV2Models, create.ErrActionCheckingNotRecreated, tflexv2models.ResNameSlot, aws.ToString(before.SlotId), errors.New("recreated")) + } + + return nil + } +} + +func testAccSlotConfig_basic(rName, version string) string { + return fmt.Sprintf(` +resource "aws_security_group" "test" { + name = %[1]q +} + +resource "aws_lexv2models_slot" "test" { + slot_name = %[1]q + engine_type = "ActiveLexV2Models" + engine_version = %[2]q + host_instance_type = "lexv2models.t2.micro" + security_groups = [aws_security_group.test.id] + authentication_strategy = "simple" + storage_type = "efs" + + logs { + general = true + } + + user { + username = "Test" + password = "TestTest1234" + } +} +`, rName, version) +} diff --git a/website/docs/r/lexv2models_slot.html.markdown b/website/docs/r/lexv2models_slot.html.markdown new file mode 100644 index 00000000000..0d421b07052 --- /dev/null +++ b/website/docs/r/lexv2models_slot.html.markdown @@ -0,0 +1,69 @@ +--- +subcategory: "Lex V2 Models" +layout: "aws" +page_title: "AWS: aws_lexv2models_slot" +description: |- + Terraform resource for managing an AWS Lex V2 Models Slot. +--- +` +# Resource: aws_lexv2models_slot + +Terraform resource for managing an AWS Lex V2 Models Slot. + +## Example Usage + +### Basic Usage + +```terraform +resource "aws_lexv2models_slot" "example" { +} +``` + +## Argument Reference + +The following arguments are required: + +* `example_arg` - (Required) Concise argument description. Do not begin the description with "An", "The", "Defines", "Indicates", or "Specifies," as these are verbose. In other words, "Indicates the amount of storage," can be rewritten as "Amount of storage," without losing any information. + +The following arguments are optional: + +* `optional_arg` - (Optional) Concise argument description. Do not begin the description with "An", "The", "Defines", "Indicates", or "Specifies," as these are verbose. In other words, "Indicates the amount of storage," can be rewritten as "Amount of storage," without losing any information. + +## Attribute Reference + +This resource exports the following attributes in addition to the arguments above: + +* `arn` - ARN of the Slot. Do not begin the description with "An", "The", "Defines", "Indicates", or "Specifies," as these are verbose. In other words, "Indicates the amount of storage," can be rewritten as "Amount of storage," without losing any information. +* `example_attribute` - Concise description. Do not begin the description with "An", "The", "Defines", "Indicates", or "Specifies," as these are verbose. In other words, "Indicates the amount of storage," can be rewritten as "Amount of storage," without losing any information. + +## Timeouts + +[Configuration options](https://developer.hashicorp.com/terraform/language/resources/syntax#operation-timeouts): + +* `create` - (Default `60m`) +* `update` - (Default `180m`) +* `delete` - (Default `90m`) + +## Import + +In Terraform v1.5.0 and later, use an [`import` block](https://developer.hashicorp.com/terraform/language/import) to import Lex V2 Models Slot using the `example_id_arg`. For example: + +```terraform +import { + to = aws_lexv2models_slot.example + id = "slot-id-12345678" +} +``` + +Using `terraform import`, import Lex V2 Models Slot using the `example_id_arg`. For example: + +```console +% terraform import aws_lexv2models_slot.example slot-id-12345678 +``` From 259c98fa9985b09df17790f9d7376e95f1f1fff6 Mon Sep 17 00:00:00 2001 From: Sharon Nam Date: Thu, 7 Dec 2023 00:16:41 -0800 Subject: [PATCH 04/42] fix schema --- internal/service/lexv2models/slot.go | 553 +++++++++++---------------- 1 file changed, 222 insertions(+), 331 deletions(-) diff --git a/internal/service/lexv2models/slot.go b/internal/service/lexv2models/slot.go index 272068a9b45..47844890f64 100644 --- a/internal/service/lexv2models/slot.go +++ b/internal/service/lexv2models/slot.go @@ -56,50 +56,6 @@ func (r *resourceSlot) Metadata(_ context.Context, req resource.MetadataRequest, resp.TypeName = "aws_lexv2models_slot" } -// TIP: ==== SCHEMA ==== -// In the schema, add each of the attributes in snake case (e.g., -// delete_automated_backups). -// -// Formatting rules: -// * Alphabetize attributes to make them easier to find. -// * Do not add a blank line between attributes. -// -// Attribute basics: -// - If a user can provide a value ("configure a value") for an -// attribute (e.g., instances = 5), we call the attribute an -// "argument." -// - You change the way users interact with attributes using: -// - Required -// - Optional -// - Computed -// - There are only four valid combinations: -// -// 1. Required only - the user must provide a value -// Required: true, -// -// 2. Optional only - the user can configure or omit a value; do not -// use Default or DefaultFunc -// -// Optional: true, -// -// 3. Computed only - the provider can provide a value but the user -// cannot, i.e., read-only -// -// Computed: true, -// -// 4. Optional AND Computed - the provider or user can provide a value; -// use this combination if you are using Default -// -// Optional: true, -// Computed: true, -// -// You will typically find arguments in the input struct -// (e.g., CreateDBInstanceInput) for the create operation. Sometimes -// they are only in the input struct (e.g., ModifyDBInstanceInput) for -// the modify operation. -// -// For more about schema options, visit -// https://developer.hashicorp.com/terraform/plugin/framework/handling-data/schemas?page=schemas func (r *resourceSlot) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { slotValueOverrideO := types.ObjectType{ AttrTypes: map[string]attr.Type{ @@ -147,27 +103,6 @@ func (r *resourceSlot) Schema(ctx context.Context, req resource.SchemaRequest, r "button": schema.ListNestedBlock{ Nestedobject: schema.NestedBlockObject{ Attributes: map[string]schema.Attribute{ - - } - } - } - } - }, - } - - } - "message": schema.NestedBlockObject{ - Required: true, - Blocks: map[string]schema.Block{ - - - Blocks: map[string]schema.Block{ - "buttons": schema.ListNestedBlock{ - Validators: []validator.List{ - listvalidator.SizeAtMost(1), - }, - NestedObject: schema.NestedBlockObject{ - Attributes: map[strings]schema.Attribute{ "text": schema.StringAttribute{ Required: true, }, @@ -179,15 +114,25 @@ func (r *resourceSlot) Schema(ctx context.Context, req resource.SchemaRequest, r }, }, }, - "plain_text_message": schema.SingleNestedBlock{ - Attributes: map[strings]schema.Attribute{ + }, + "plain_text_message": schema.ListNestedBlock{ + Validators: []validator.List{ + listvalidator.SizeAtMost(1), + }, + NestedObject: schema.NestedBlockObject{ + Attributes: map[string]schema.Attribute{ "value": schema.StringAttribute{ Required: true, }, }, }, - "ssml_message": schema.SingleNestedBlock{ - Attributes: map[strings]schema.Attribute{ + }, + "ssml_message": schema.ListNestedBlock{ + Validators: []validator.List{ + listvalidator.SizeAtMost(1), + }, + NestedObject: schema.NestedBlockObject{ + Attributes: map[string]schema.Attribute{ "value": schema.StringAttribute{ Required: true, }, @@ -196,7 +141,151 @@ func (r *resourceSlot) Schema(ctx context.Context, req resource.SchemaRequest, r }, }, } + messageGroupLNB := schema.ListNestedAttributeBlock{ + Validators: []validator.List{ + listvalidator.SizeAtLeast(1), + }, + NestedObject: schema.NestedBlockObject{ + Blocks: map[string]schema.Block{ + "message": schema.ListNestedBlock{ + Validators: []validator.List{ + listvalidator.SizeBetween(1,1), + }, + NestedObject: messageNBO, + }, + "variations": schema.ListNestedBlock{ + NestedObject: messageNBO, + }, + }, + }, + } + + dialogStateNBO := schema.NestedBlockObject{ + Attributes: map[string]schema.Attribute{ + "session_attributes": schema.MapAttribute{ + ElementType: types.StringType, + Optional: true, + }, + }, + Blocks: map[string]schema.Block{ + "dialog_action": schema.ListNestedBlock{ + Validators: []validator.List{ + listvalidator.SizeAtMost(1), + }, + NesredObject: schema.NestedBlockObject{ + Attributes: map[string]schema.Attribute{ + "type": schema.StringAttribute{ + Required: true, + }, + "slot_to_elicit": schema.StringAttribute{ + Optional: true, + }, + "suppress_next_message": schema.StringAttribute{ + Optional: true, + }, + }, + }, + }, + "intent": schema.ListNestedBlock{ + Validators: []validator.List{ + listvalidator.SizeAtMost(1), + }, + NestedObject: schema.NestedBlockObject{ + Attributes: map[string]schema.Attribute{ + "name": schema.StringAttribute{ + Optional: true, + }, + "slots": schema.MapAttribute{ + Optional: true, + ElementType: slotValueOverrideO, + }, + }, + }, + }, + }, + } + + responseSpecificationLNB := schema.ListNestedBlock{ + Validators: []validator.List{ + listvalidator.SizeAtMost(1), + }, + NestedObject: schema.NestedBlockObject{ + Attributes: map[string]schema.Attribute{ + "allow_interrupt": schema.BoolAttribute{ + Optional: true, + }, + }, + Blocks: map[string]schema.Block{ + "message_group": messageGroupLNB, + }, + }, + } + + conditionalSpecificationLNB := schema.ListNestedBlock{ + Validators: []validator.List{ + listvalidator.SizeAtMost(1), + }, + NestedObject: schema.NestedBlockObject{ + Attributes: map[string]schema.Attribute{ + "active": schema.BoolAttribute{ + Required: true, + }, + }, + Blocks: map[string]schema.Block{ + "conditional_branch": schema.ListNestedBlock{ + Validators: []validator.List{ + listvalidator.SizeAtLeast(1), + }, + NestedObject: schema.NestedBlockObject{ + Attributes: map[string]schema.Attribute{ + "name": schema.StringAttribute{ + Required: true, + }, + }, + Blocks: map[string]schema.Block{ + "condition": schema.ListNestedBlock{ + Validators: []validator.List{ + listvalidator.SizeBetween(1, 1), + }, + NestedObject: schema.NestedBlockObject{ + Attributes: map[string]schema.Attribute{ + "expression_string": schema.StringAttribute{ + Required: true, + }, + }, + }, + }, + "next_step": schema.ListNestedBlock{ + Validators: []validator.List{ + listvalidator.SizeBetween(1, 1), + }, + NestedObject: dialogStateNBO, + }, + "response": responseSpecificationLNB, + }, + }, + }, + "default_branch": schema.ListNestedBlock{ + Validators: []validator.List{ + listvalidator.SizeBetween(1, 1), + }, + NestedObject: schema.NestedBlockObject{ + Blocks: map[string]schema.Block{ + "next_step": schema.ListNestedBlock{ + Validators: []validator.List{ + listvalidator.SizeBetween(1, 1), + }, + NestedObject: dialogStateNBO, + }, + "response": responseSpecificationLNB, + }, + }, + }, + }, + }, + } + ////////// resp.Schema = schema.Schema{ Attributes: map[string]schema.Attribute{ // "arn": framework.ARNAttributeComputedOnly(), @@ -265,212 +354,97 @@ func (r *resourceSlot) Schema(ctx context.Context, req resource.SchemaRequest, r }, }, }, - "prompt_specification": schema.SingleNestedBlock{ - Attributes: map[string]schema.Attribute{ - "max_retries": schema.Int32Attribute{ - Required: true, - }, - "allow_interrupt": schema.BoolAttribute{ - Optional: true, - }, - "message_selection_strategy": schema.StringAttribute{ - Optional: true, - Validators: []validator.String{ - enum.FrameworkValidate[awstypes.MessageSelectionStrategy](), - }, - }, + "prompt_specification": schema.ListNestedBlock{ + Validators: []validator.List{ + listvalidator.SizeBetween(1, 1), }, - Blocks: map[string]schema.Block{ - "message_groups": schema.ListNestedBlock{ - Validators: []validator.List{ - listvalidator.SizeAtLeast(1), + NestedObject: schema.NestedBlockObject{ + Attributes: map[string]schema.Attribute{ + "max_retries": schema.Int32Attribute{ + Required: true, }, - Blocks: map[string]schema.Block{ - "message": schema.SingleNestedBlock{ - Required: true, - Blocks: map[string]schema.Block{ - "custom_payload": schema.SingleNestedBlock{ - NestedObject: schema.NestedBlockObject{ - Attributes: map[string]schema.Attribute{ - "value": schema.StringAttribute{ - Required: true, - }, - }, - }, - }, - "image_response_card": schema.SingleNestedBlock{ - Attributes: map[strings]schema.Attribute{ - "title": schema.StringAttribute{ - Required: true, - }, - "image_url": schema.StringAttribute{ - Optional: true, - }, - "subtitle": schema.StringAttribute{ - Optional: true, - }, - }, - Blocks: map[string]schema.Block{ - "buttons": schema.ListNestedBlock{ - Validators: []validator.List{ - listvalidator.SizeAtMost(1), - }, - NestedObject: schema.NestedBlockObject{ - Attributes: map[strings]schema.Attribute{ - "text": schema.StringAttribute{ - Required: true, - }, - "value": schema.StringAttribute{ - Required: true, - }, - }, - }, - }, - }, - }, - "plain_text_message": schema.SingleNestedBlock{ - Attributes: map[strings]schema.Attribute{ - "value": schema.StringAttribute{ - Required: true, - }, - }, - }, - "ssml_message": schema.SingleNestedBlock{ - Attributes: map[strings]schema.Attribute{ - "value": schema.StringAttribute{ + "allow_interrupt": schema.BoolAttribute{ + Optional: true, + }, + "message_selection_strategy": schema.StringAttribute{ + Optional: true, + Validators: []validator.String{ + enum.FrameworkValidate[awstypes.MessageSelectionStrategy](), + }, + }, + "message_groups": messageGroupLNB, + "prompt_attempts_specification": schema.MapAttribute{ + Optional: true, + ElementType: types.ObjectType{ + AttrTypes: map[string]attr.Type{ + "allow_input_types": schema.ObjectType{ + AttrTypes: map[string]attr.Type{ + "allow_audio_input": schema.BoolAttribute{ Required: true, }, - }, - }, - }, - "variations": schema.ListNestedBlock{ - Blocks: map[string]schema.Block{ - "custom_payload": schema.SingleNestedBlock{ - Attributes: map[string]schema.Attribute{ - "value": schema.StringAttribute{ + "allow_dtmf_input": schema.BoolAttribute{ Required: true, }, }, + "allow_interrupts": schema.BoolAttribute{ + Optional: true, }, - "image_response_card": schema.SingleNestedBlock{ - Attributes: map[strings]schema.Attribute{ - "title": schema.StringAttribute{ + "audio_and_dtmf_input_specification": types.ObjectType{ + AttrTypes: map[string]attr.Type{ + "start_timeout_ms": schema.Int32Attribute{ Required: true, }, - "image_url": schema.StringAttribute{ + "audio_specification": types.ObjectType{ Optional: true, + AttrTypes: map[strings]attr.Type{ + "end_timeout_ms": schema.Int32Attribute{ + Required: true, + }, + "max_length_ms": schema.Int32Attribute{ + Required: true, + }, + }, }, - "subtitle": schema.StringAttribute{ + "dtmf_specification": schema.SingleNestedBlock{ Optional: true, - }, - }, - Blocks: map[string]schema.Block{ - "buttons": schema.ListNestedBlock{ - Validators: []validator.List{ - listvalidator.SizeAtMost(1), - }, - NestedObject: schema.NestedBlockObject{ - Attributes: map[strings]schema.Attribute{ - "text": schema.StringAttribute{ - Required: true, - }, - "value": schema.StringAttribute{ - Required: true, - }, + Attributes: map[strings]schema.Attribute{ + "deletion_character": schema.StringAttribute{ + Required: true, + }, + "end_character": schema.StringAttribute{ + Required: true, + }, + "end_timeout_ms": schema.StringAttribute{ + Required: true, + }, + "max_length": schema.Int32Attribute{ + Required: true, }, }, }, }, }, - "plain_text_message": schema.SingleNestedBlock{ + "text_input_specification": schema.SingleNestedBlock{ + Optional: true, Attributes: map[strings]schema.Attribute{ - "value": schema.StringAttribute{ + "start_timeout_ms": schema.Int32Attribute{ Required: true, }, }, }, - "ssml_message": schema.SingleNestedBlock{ - Attributes: map[strings]schema.Attribute{ - "value": schema.StringAttribute{ - Required: true, - }, - }, - }, - }, - }, - }, - }, - "prompt_attempts_specification": schema.MapAttribute{ - Optional: true, - Attributes: map[strings]schema.Attribute{ - "allow_interrupts": schema.BoolAttribute{ - Optional: true, - }, - }, - Blocks: map[strings]schema.Block{ - "allow_input_types": schema.SingleNestedBlock{ - Attributes: map[strings]schema.Attribute{ - "allow_audio_input": schema.BoolAttribute{ - Required: true, - }, - "allow_dtmf_input": schema.BoolAttribute{ - Required: true, - }, - }, - }, - "audio_and_dtmf_input_specification": schema.SingleNestedBlock{ - Optional: true, - Attributes: map[strings]schema.Attribute{ - "start_timeout_ms": schema.Int32Attribute{ - Required: true, - }, - }, - "audio_specification": schema.SingleNestedBlock{ - Optional: true, - Attributes: map[strings]schema.Attribute{ - "end_timeout_ms": schema.Int32Attribute{ - Required: true, - }, - "max_length_ms": schema.Int32Attribute{ - Required: true, - }, - }, - }, - "dtmf_specification": schema.SingleNestedBlock{ - Optional: true, - Attributes: map[strings]schema.Attribute{ - "deletion_character": schema.StringAttribute{ - Required: true, - }, - "end_character": schema.StringAttribute{ - Required: true, - }, - "end_timeout_ms": schema.StringAttribute{ - Required: true, - }, - "max_length": schema.Int32Attribute{ - Required: true, - }, - }, }, }, - "text_input_specification": schema.SingleNestedBlock{ - Optional: true, - Attributes: map[strings]schema.Attribute{ - "start_timeout_ms": schema.Int32Attribute{ - Required: true, - }, - }, - }, - }, + }, //end of prompt specification }, }, }, "sample_utterances": schema.ListNestedBlock{ Optional: true, - Attributes: map[strings]schema.Attribute{ - "utterance": schema.StringAttribute{ - Required: true, + NestedObject: schema.NestedBlockObject{ + Attributes: map[strings]schema.Attribute{ + "utterance": schema.StringAttribute{ + Required: true, + }, }, }, }, @@ -480,92 +454,9 @@ func (r *resourceSlot) Schema(ctx context.Context, req resource.SchemaRequest, r "capture_conditional": schema.SingleNestedBlock{ Optional: true, Blocks: map[string]schema.Block{ - "condition_specification": schema.SingleNestedBlock{ - Optional: true, - Attributes: map[string]schema.Attribute{ - "active": schema.BoolAttribute{ - Required: true, - }, - }, - Blocks: map[string]schema.Block{ - "conditional_branch": schema.ListNestedBlock{ - Required: true, - NestedObject: schema.NestedBlockObject{ - Attributes: map[string]schema.Attribute{ - "name": schema.StringAttribute{ - Required: true, - }, - }, - Blocks: map[string]schema.Block{ - "condition": schema.SingleNestedBlock{ - Validators: []validator.Object{ - objectvalidator.IsRequired(), - }, - Attributes: map[string]schema.Attribute{ - "expression_string": schema.StringAttribute{ - Required: true, - }, - }, - }, - "next_step": schema.SingleNestedBlock{ - Validators: []validator.Object{ - objectvalidator.IsRequired(), - }, - NestedObject: schema.NestedBlockObject{ - Blocks: map[string]schema.SingleNestedBlock{ - "dialog_state": schema.SingleNestedBlock{ - Optional: true, - Blocks: map[string]SingleNestedBlock{ - "dialog_action": schema.SingleNestedBlock{ - Optional: true, - Attributes: map[string]schema.Attribute{ - "slot_to_elicit": schema.StringAttribute{ - Optional: true, - }, - "supress_next_message": schema.BoolAttribute{ - Optional: true, - }, - }, - Blocks: map[string]schema.Block{ - "type": schema.StringAttribute{ - Required: true, - Validators: []validator.String{ - enum.FrameworkValidate[awstypes.DialogActionType](), - }, - }, - }, - }, - "intent": schema.SingleNestedBlock{ - Optional: true, - Attributes: map[string]schema.Attribute{ - "intent_overide": schema.NestedBlockObject{ - Attributes: map[string]schema.Attribute{ - "name": schema.StringAttribute{ - Optional: true, - }, - NestedObject: schema.NestedBlockObject{ - Blocks: map[string]schema.Block{ - "slots": schema.MapAttribute{ - NestedObject: slotValueOverrideO, - }, - }, - }, - }, - }, - }, - "session_attributes": schema.MapAttribute{ - ElementType: types.StringType, - }, - }, - }, - }, - "response" - }, - }, - }, - }, - }, - }, + "condition_specification": conditionalSpecificationLNB, + "capture_next_step": dialogStateNBO, + "capture_resonse": responseSpecificationLNB, }, }, }, From 4a1a182f3b3a4cd22f21f562543197e0682b5412 Mon Sep 17 00:00:00 2001 From: Jared Baker Date: Thu, 7 Dec 2023 16:52:56 -0500 Subject: [PATCH 05/42] r/aws_lexv2models_slot: reduce to minimal schema --- internal/service/lexv2models/exports_test.go | 3 + .../slot_value_elicitivation_settings.go | 71 ++ .../lexv2models/service_package_gen.go | 4 + internal/service/lexv2models/slot.go | 883 +----------------- internal/service/lexv2models/slot_test.go | 224 +---- 5 files changed, 141 insertions(+), 1044 deletions(-) create mode 100644 internal/service/lexv2models/schema/slot_value_elicitivation_settings.go diff --git a/internal/service/lexv2models/exports_test.go b/internal/service/lexv2models/exports_test.go index c78da004858..b94fb34ee95 100644 --- a/internal/service/lexv2models/exports_test.go +++ b/internal/service/lexv2models/exports_test.go @@ -8,4 +8,7 @@ var ( ResourceBot = newResourceBot ResourceBotLocale = newResourceBotLocale ResourceBotVersion = newResourceBotVersion + ResourceSlot = newResourceSlot + + FindSlotByID = findSlotByID ) diff --git a/internal/service/lexv2models/schema/slot_value_elicitivation_settings.go b/internal/service/lexv2models/schema/slot_value_elicitivation_settings.go new file mode 100644 index 00000000000..864f22a8bbe --- /dev/null +++ b/internal/service/lexv2models/schema/slot_value_elicitivation_settings.go @@ -0,0 +1,71 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package schema + +import ( + "context" + + awstypes "github.com/aws/aws-sdk-go-v2/service/lexmodelsv2/types" + "github.com/hashicorp/terraform-plugin-framework-validators/listvalidator" + "github.com/hashicorp/terraform-plugin-framework/provider/schema" + "github.com/hashicorp/terraform-plugin-framework/schema/validator" + "github.com/hashicorp/terraform-plugin-framework/types" + "github.com/hashicorp/terraform-provider-aws/internal/enum" + fwtypes "github.com/hashicorp/terraform-provider-aws/internal/framework/types" +) + +func ValueElicitationSettingBlock(ctx context.Context) schema.ListNestedBlock { + return schema.ListNestedBlock{ + CustomType: fwtypes.NewListNestedObjectTypeOf[ValueElicitationSettingData](ctx), + Validators: []validator.List{ + listvalidator.IsRequired(), + listvalidator.SizeAtMost(1), + }, + NestedObject: schema.NestedBlockObject{ + Attributes: map[string]schema.Attribute{ + "slot_constraint": schema.StringAttribute{ + Required: true, + Validators: []validator.String{ + enum.FrameworkValidate[awstypes.SlotConstraint](), + }, + }, + }, + Blocks: map[string]schema.Block{ + "default_value_specification": schema.ListNestedBlock{ + CustomType: fwtypes.NewListNestedObjectTypeOf[DefaultValueSpecificationData](ctx), + NestedObject: schema.NestedBlockObject{ + Blocks: map[string]schema.Block{ + "default_value_list": schema.ListNestedBlock{ + CustomType: fwtypes.NewListNestedObjectTypeOf[DefaultValueData](ctx), + Validators: []validator.List{ + listvalidator.IsRequired(), + }, + NestedObject: schema.NestedBlockObject{ + Attributes: map[string]schema.Attribute{ + "default_value": schema.StringAttribute{ + Required: true, + }, + }, + }, + }, + }, + }, + }, + }, + }, + } +} + +type ValueElicitationSettingData struct { + SlotConstraint fwtypes.StringEnum[awstypes.SlotConstraint] `tfsdk:"slot_constraint"` + DefaultValueSpecification fwtypes.ListNestedObjectValueOf[DefaultValueSpecificationData] `tfsdk:"default_value_specification"` +} + +type DefaultValueSpecificationData struct { + DefaultValueList fwtypes.ListNestedObjectValueOf[DefaultValueData] `tfsdk:"default_value_list"` +} + +type DefaultValueData struct { + DefaultValue types.String `tfsdk:"default_value"` +} diff --git a/internal/service/lexv2models/service_package_gen.go b/internal/service/lexv2models/service_package_gen.go index f29fc27ec46..8cbcf84b1fc 100644 --- a/internal/service/lexv2models/service_package_gen.go +++ b/internal/service/lexv2models/service_package_gen.go @@ -35,6 +35,10 @@ func (p *servicePackage) FrameworkResources(ctx context.Context) []*types.Servic Factory: newResourceBotVersion, Name: "Bot Version", }, + { + Factory: newResourceSlot, + Name: "Slot", + }, } } diff --git a/internal/service/lexv2models/slot.go b/internal/service/lexv2models/slot.go index 47844890f64..dc9b6f059fa 100644 --- a/internal/service/lexv2models/slot.go +++ b/internal/service/lexv2models/slot.go @@ -9,25 +9,21 @@ import ( "time" "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/service/lexmodelsv2" awstypes "github.com/aws/aws-sdk-go-v2/service/lexmodelsv2/types" "github.com/hashicorp/terraform-plugin-framework-timeouts/resource/timeouts" - "github.com/hashicorp/terraform-plugin-framework-validators/listvalidator" - "github.com/hashicorp/terraform-plugin-framework/attr" - "github.com/hashicorp/terraform-plugin-framework/datasource/schema" - "github.com/hashicorp/terraform-plugin-framework/diag" "github.com/hashicorp/terraform-plugin-framework/path" - "github.com/hashicorp/terraform-plugin-framework/provider/schema" "github.com/hashicorp/terraform-plugin-framework/resource" "github.com/hashicorp/terraform-plugin-framework/resource/schema" "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" - "github.com/hashicorp/terraform-plugin-framework/schema/validator" "github.com/hashicorp/terraform-plugin-framework/types" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/retry" "github.com/hashicorp/terraform-provider-aws/internal/create" - "github.com/hashicorp/terraform-provider-aws/internal/enum" "github.com/hashicorp/terraform-provider-aws/internal/framework" "github.com/hashicorp/terraform-provider-aws/internal/framework/flex" + fwtypes "github.com/hashicorp/terraform-provider-aws/internal/framework/types" + lexschema "github.com/hashicorp/terraform-provider-aws/internal/service/lexv2models/schema" "github.com/hashicorp/terraform-provider-aws/internal/tfresource" "github.com/hashicorp/terraform-provider-aws/names" ) @@ -57,238 +53,8 @@ func (r *resourceSlot) Metadata(_ context.Context, req resource.MetadataRequest, } func (r *resourceSlot) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { - slotValueOverrideO := types.ObjectType{ - AttrTypes: map[string]attr.Type{ - "shape": types.StringType, - "value": types.ObjectType{ - AttrTypes: map[string]attr.Types{ - "interpreted_value" : types.StringType, - }, - }, - }, - } - slotValueOverrideO.AttrTypes["values"] = slotValueOverrideO - - messageNBO := stypes.Object{ - Blocks: map[string]schema.Block{ - "custom_playload": schema.ListedNestedBlock{ - Validators: []validator.List{ - listvalidator.SizeAtMost(1), - }, - NestedObject: schema.NestedBlockObject{ - Attributes: map[string]schema.Attribute{ - "value": schema.StringAttribute{ - Required: true, - }, - }, - }, - }, - "image_response_card": schema.ListNestedBlock{ - Validators: []validator.List{ - listvalidator.SizeAtMost(1), - }, - NestedObject: schema.NestedBlockObject{ - Attributes: map[string]schema.Attribute{ - "image_url": schema.StringAttribute{ - Optional: true, - }, - "subtitle": schema.StringAttribute{ - Optional: true, - }, - "title": schema.StringAttribute{ - Required: true, - }, - }, - Blocks: map[string]schema.Block{ - "button": schema.ListNestedBlock{ - Nestedobject: schema.NestedBlockObject{ - Attributes: map[string]schema.Attribute{ - "text": schema.StringAttribute{ - Required: true, - }, - "value": schema.StringAttribute{ - Required: true, - }, - }, - }, - }, - }, - }, - }, - "plain_text_message": schema.ListNestedBlock{ - Validators: []validator.List{ - listvalidator.SizeAtMost(1), - }, - NestedObject: schema.NestedBlockObject{ - Attributes: map[string]schema.Attribute{ - "value": schema.StringAttribute{ - Required: true, - }, - }, - }, - }, - "ssml_message": schema.ListNestedBlock{ - Validators: []validator.List{ - listvalidator.SizeAtMost(1), - }, - NestedObject: schema.NestedBlockObject{ - Attributes: map[string]schema.Attribute{ - "value": schema.StringAttribute{ - Required: true, - }, - }, - }, - }, - }, - } - messageGroupLNB := schema.ListNestedAttributeBlock{ - Validators: []validator.List{ - listvalidator.SizeAtLeast(1), - }, - NestedObject: schema.NestedBlockObject{ - Blocks: map[string]schema.Block{ - "message": schema.ListNestedBlock{ - Validators: []validator.List{ - listvalidator.SizeBetween(1,1), - }, - NestedObject: messageNBO, - }, - "variations": schema.ListNestedBlock{ - NestedObject: messageNBO, - }, - }, - }, - } - - dialogStateNBO := schema.NestedBlockObject{ - Attributes: map[string]schema.Attribute{ - "session_attributes": schema.MapAttribute{ - ElementType: types.StringType, - Optional: true, - }, - }, - Blocks: map[string]schema.Block{ - "dialog_action": schema.ListNestedBlock{ - Validators: []validator.List{ - listvalidator.SizeAtMost(1), - }, - NesredObject: schema.NestedBlockObject{ - Attributes: map[string]schema.Attribute{ - "type": schema.StringAttribute{ - Required: true, - }, - "slot_to_elicit": schema.StringAttribute{ - Optional: true, - }, - "suppress_next_message": schema.StringAttribute{ - Optional: true, - }, - }, - }, - }, - "intent": schema.ListNestedBlock{ - Validators: []validator.List{ - listvalidator.SizeAtMost(1), - }, - NestedObject: schema.NestedBlockObject{ - Attributes: map[string]schema.Attribute{ - "name": schema.StringAttribute{ - Optional: true, - }, - "slots": schema.MapAttribute{ - Optional: true, - ElementType: slotValueOverrideO, - }, - }, - }, - }, - }, - } - - responseSpecificationLNB := schema.ListNestedBlock{ - Validators: []validator.List{ - listvalidator.SizeAtMost(1), - }, - NestedObject: schema.NestedBlockObject{ - Attributes: map[string]schema.Attribute{ - "allow_interrupt": schema.BoolAttribute{ - Optional: true, - }, - }, - Blocks: map[string]schema.Block{ - "message_group": messageGroupLNB, - }, - }, - } - - conditionalSpecificationLNB := schema.ListNestedBlock{ - Validators: []validator.List{ - listvalidator.SizeAtMost(1), - }, - NestedObject: schema.NestedBlockObject{ - Attributes: map[string]schema.Attribute{ - "active": schema.BoolAttribute{ - Required: true, - }, - }, - Blocks: map[string]schema.Block{ - "conditional_branch": schema.ListNestedBlock{ - Validators: []validator.List{ - listvalidator.SizeAtLeast(1), - }, - NestedObject: schema.NestedBlockObject{ - Attributes: map[string]schema.Attribute{ - "name": schema.StringAttribute{ - Required: true, - }, - }, - Blocks: map[string]schema.Block{ - "condition": schema.ListNestedBlock{ - Validators: []validator.List{ - listvalidator.SizeBetween(1, 1), - }, - NestedObject: schema.NestedBlockObject{ - Attributes: map[string]schema.Attribute{ - "expression_string": schema.StringAttribute{ - Required: true, - }, - }, - }, - }, - "next_step": schema.ListNestedBlock{ - Validators: []validator.List{ - listvalidator.SizeBetween(1, 1), - }, - NestedObject: dialogStateNBO, - }, - "response": responseSpecificationLNB, - }, - }, - }, - "default_branch": schema.ListNestedBlock{ - Validators: []validator.List{ - listvalidator.SizeBetween(1, 1), - }, - NestedObject: schema.NestedBlockObject{ - Blocks: map[string]schema.Block{ - "next_step": schema.ListNestedBlock{ - Validators: []validator.List{ - listvalidator.SizeBetween(1, 1), - }, - NestedObject: dialogStateNBO, - }, - "response": responseSpecificationLNB, - }, - }, - }, - }, - }, - } - - ////////// resp.Schema = schema.Schema{ Attributes: map[string]schema.Attribute{ - // "arn": framework.ARNAttributeComputedOnly(), "bot_id": schema.StringAttribute{ Required: true, PlanModifiers: []planmodifier.String{ @@ -304,6 +70,7 @@ func (r *resourceSlot) Schema(ctx context.Context, req resource.SchemaRequest, r "description": schema.StringAttribute{ Optional: true, }, + "id": framework.IDAttribute(), "intent_id": schema.StringAttribute{ Required: true, PlanModifiers: []planmodifier.String{ @@ -316,7 +83,6 @@ func (r *resourceSlot) Schema(ctx context.Context, req resource.SchemaRequest, r stringplanmodifier.RequiresReplace(), }, }, - // "id": framework.IDAttribute(), "name": schema.StringAttribute{ Required: true, PlanModifiers: []planmodifier.String{ @@ -328,208 +94,38 @@ func (r *resourceSlot) Schema(ctx context.Context, req resource.SchemaRequest, r }, }, Blocks: map[string]schema.Block{ - "value_elicitation_setting": schema.SingleNestedBlock{ - Attributes: map[string]schema.Attribute{ - "slot_constraint": schema.StringAttribute{ - Required: true, - Validators: []validator.String{ - enum.FrameworkValidate[awstypes.SlotConstraint](), - }, - }, - }, - Blocks: map[string]schema.Block{ - "default_value_specification": schema.SingleNestedBlock{ - Blocks: map[string]schema.Block{ - "default_value_list": schema.ListNestedBlock{ - Validators: []validator.List{ - listvalidator.SizeAtLeast(1), - }, - NestedObject: schema.NestedBlockObject{ - Attributes: map[string]schema.Attribute{ - "default_value": schema.StringAttribute{ - Required: true, - }, - }, - }, - }, - }, - }, - "prompt_specification": schema.ListNestedBlock{ - Validators: []validator.List{ - listvalidator.SizeBetween(1, 1), - }, - NestedObject: schema.NestedBlockObject{ - Attributes: map[string]schema.Attribute{ - "max_retries": schema.Int32Attribute{ - Required: true, - }, - "allow_interrupt": schema.BoolAttribute{ - Optional: true, - }, - "message_selection_strategy": schema.StringAttribute{ - Optional: true, - Validators: []validator.String{ - enum.FrameworkValidate[awstypes.MessageSelectionStrategy](), - }, - }, - "message_groups": messageGroupLNB, - "prompt_attempts_specification": schema.MapAttribute{ - Optional: true, - ElementType: types.ObjectType{ - AttrTypes: map[string]attr.Type{ - "allow_input_types": schema.ObjectType{ - AttrTypes: map[string]attr.Type{ - "allow_audio_input": schema.BoolAttribute{ - Required: true, - }, - "allow_dtmf_input": schema.BoolAttribute{ - Required: true, - }, - }, - "allow_interrupts": schema.BoolAttribute{ - Optional: true, - }, - "audio_and_dtmf_input_specification": types.ObjectType{ - AttrTypes: map[string]attr.Type{ - "start_timeout_ms": schema.Int32Attribute{ - Required: true, - }, - "audio_specification": types.ObjectType{ - Optional: true, - AttrTypes: map[strings]attr.Type{ - "end_timeout_ms": schema.Int32Attribute{ - Required: true, - }, - "max_length_ms": schema.Int32Attribute{ - Required: true, - }, - }, - }, - "dtmf_specification": schema.SingleNestedBlock{ - Optional: true, - Attributes: map[strings]schema.Attribute{ - "deletion_character": schema.StringAttribute{ - Required: true, - }, - "end_character": schema.StringAttribute{ - Required: true, - }, - "end_timeout_ms": schema.StringAttribute{ - Required: true, - }, - "max_length": schema.Int32Attribute{ - Required: true, - }, - }, - }, - }, - }, - "text_input_specification": schema.SingleNestedBlock{ - Optional: true, - Attributes: map[strings]schema.Attribute{ - "start_timeout_ms": schema.Int32Attribute{ - Required: true, - }, - }, - }, - }, - }, - }, //end of prompt specification - }, - }, - }, - "sample_utterances": schema.ListNestedBlock{ - Optional: true, - NestedObject: schema.NestedBlockObject{ - Attributes: map[strings]schema.Attribute{ - "utterance": schema.StringAttribute{ - Required: true, - }, - }, - }, - }, - "slot_capture_setting": schema.SingleNestedBlock{ - Optional: true, - Blocks: map[string]schema.Block{ - "capture_conditional": schema.SingleNestedBlock{ - Optional: true, - Blocks: map[string]schema.Block{ - "condition_specification": conditionalSpecificationLNB, - "capture_next_step": dialogStateNBO, - "capture_resonse": responseSpecificationLNB, - }, - }, - }, - }, - }, - }, - }, + "value_elicitation_setting": lexschema.ValueElicitationSettingBlock(ctx), }, - }, + } } func (r *resourceSlot) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { - // TIP: ==== RESOURCE CREATE ==== - // Generally, the Create function should do the following things. Make - // sure there is a good reason if you don't do one of these. - // - // 1. Get a client connection to the relevant service - // 2. Fetch the plan - // 3. Populate a create input structure - // 4. Call the AWS create/put function - // 5. Using the output from the create function, set the minimum arguments - // and attributes for the Read function to work, as well as any computed - // only attributes. - // 6. Use a waiter to wait for create to complete - // 7. Save the request plan to response state - - // TIP: -- 1. Get a client connection to the relevant service conn := r.Meta().LexV2ModelsClient(ctx) - // TIP: -- 2. Fetch the plan var plan resourceSlotData resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) if resp.Diagnostics.HasError() { return } - // TIP: -- 3. Populate a create input structure - in := &lexv2models.CreateSlotInput{ - // TIP: Mandatory or fields that will always be present can be set when - // you create the Input structure. (Replace these with real fields.) + in := &lexmodelsv2.CreateSlotInput{ SlotName: aws.String(plan.Name.ValueString()), - SlotType: aws.String(plan.Type.ValueString()), } - if !plan.Description.IsNull() { - // TIP: Optional fields should be set based on whether or not they are - // used. - in.Description = aws.String(plan.Description.ValueString()) - } - if !plan.ComplexArgument.IsNull() { - // TIP: Use an expander to assign a complex argument. The elements must be - // deserialized into the appropriate struct before being passed to the expander. - var tfList []complexArgumentData - resp.Diagnostics.Append(plan.ComplexArgument.ElementsAs(ctx, &tfList, false)...) - if resp.Diagnostics.HasError() { - return - } - - in.ComplexArgument = expandComplexArgument(tfList) + resp.Diagnostics.Append(flex.Expand(ctx, plan, &in)...) + if resp.Diagnostics.HasError() { + return } - // TIP: -- 4. Call the AWS create function out, err := conn.CreateSlot(ctx, in) if err != nil { - // TIP: Since ID has not been set yet, you cannot use plan.ID.String() - // in error messages at this point. resp.Diagnostics.AddError( create.ProblemStandardMessage(names.LexV2Models, create.ErrActionCreating, ResNameSlot, plan.Name.String(), err), err.Error(), ) return } - if out == nil || out.Slot == nil { + if out == nil { resp.Diagnostics.AddError( create.ProblemStandardMessage(names.LexV2Models, create.ErrActionCreating, ResNameSlot, plan.Name.String(), nil), errors.New("empty output").Error(), @@ -537,51 +133,24 @@ func (r *resourceSlot) Create(ctx context.Context, req resource.CreateRequest, r return } - // TIP: -- 5. Using the output from the create function, set the minimum attributes - plan.ARN = flex.StringToFramework(ctx, out.Slot.Arn) - plan.ID = flex.StringToFramework(ctx, out.Slot.SlotId) - - // TIP: -- 6. Use a waiter to wait for create to complete - createTimeout := r.CreateTimeout(ctx, plan.Timeouts) - _, err = waitSlotCreated(ctx, conn, plan.ID.ValueString(), createTimeout) - if err != nil { - resp.Diagnostics.AddError( - create.ProblemStandardMessage(names.LexV2Models, create.ErrActionWaitingForCreation, ResNameSlot, plan.Name.String(), err), - err.Error(), - ) + resp.Diagnostics.Append(flex.Flatten(ctx, out, &plan)...) + if resp.Diagnostics.HasError() { return } - // TIP: -- 7. Save the request plan to response state resp.Diagnostics.Append(resp.State.Set(ctx, plan)...) } func (r *resourceSlot) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - // TIP: ==== RESOURCE READ ==== - // Generally, the Read function should do the following things. Make - // sure there is a good reason if you don't do one of these. - // - // 1. Get a client connection to the relevant service - // 2. Fetch the state - // 3. Get the resource from AWS - // 4. Remove resource from state if it is not found - // 5. Set the arguments and attributes - // 6. Set the state - - // TIP: -- 1. Get a client connection to the relevant service conn := r.Meta().LexV2ModelsClient(ctx) - // TIP: -- 2. Fetch the state var state resourceSlotData resp.Diagnostics.Append(req.State.Get(ctx, &state)...) if resp.Diagnostics.HasError() { return } - // TIP: -- 3. Get the resource from AWS using an API Get, List, or Describe- - // type function, or, better yet, using a finder. out, err := findSlotByID(ctx, conn, state.ID.ValueString()) - // TIP: -- 4. Remove resource from state if it is not found if tfresource.NotFound(err) { resp.State.RemoveResource(ctx) return @@ -594,57 +163,17 @@ func (r *resourceSlot) Read(ctx context.Context, req resource.ReadRequest, resp return } - // TIP: -- 5. Set the arguments and attributes - // - // For simple data types (i.e., schema.StringAttribute, schema.BoolAttribute, - // schema.Int64Attribute, and schema.Float64Attribue), simply setting the - // appropriate data struct field is sufficient. The flex package implements - // helpers for converting between Go and Plugin-Framework types seamlessly. No - // error or nil checking is necessary. - // - // However, there are some situations where more handling is needed such as - // complex data types (e.g., schema.ListAttribute, schema.SetAttribute). In - // these cases the flatten function may have a diagnostics return value, which - // should be appended to resp.Diagnostics. - state.ARN = flex.StringToFramework(ctx, out.Arn) - state.ID = flex.StringToFramework(ctx, out.SlotId) - state.Name = flex.StringToFramework(ctx, out.SlotName) - state.Type = flex.StringToFramework(ctx, out.SlotType) - - // TIP: Setting a complex type. - complexArgument, d := flattenComplexArgument(ctx, out.ComplexArgument) - resp.Diagnostics.Append(d...) - state.ComplexArgument = complexArgument + resp.Diagnostics.Append(flex.Flatten(ctx, out, &state)...) + if resp.Diagnostics.HasError() { + return + } - // TIP: -- 6. Set the state resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) } func (r *resourceSlot) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - // TIP: ==== RESOURCE UPDATE ==== - // Not all resources have Update functions. There are a few reasons: - // a. The AWS API does not support changing a resource - // b. All arguments have RequiresReplace() plan modifiers - // c. The AWS API uses a create call to modify an existing resource - // - // In the cases of a. and b., the resource will not have an update method - // defined. In the case of c., Update and Create can be refactored to call - // the same underlying function. - // - // The rest of the time, there should be an Update function and it should - // do the following things. Make sure there is a good reason if you don't - // do one of these. - // - // 1. Get a client connection to the relevant service - // 2. Fetch the plan and state - // 3. Populate a modify input structure and check for changes - // 4. Call the AWS modify/update function - // 5. Use a waiter to wait for update to complete - // 6. Save the request plan to response state - // TIP: -- 1. Get a client connection to the relevant service conn := r.Meta().LexV2ModelsClient(ctx) - // TIP: -- 2. Fetch the plan var plan, state resourceSlotData resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) resp.Diagnostics.Append(req.State.Get(ctx, &state)...) @@ -652,38 +181,21 @@ func (r *resourceSlot) Update(ctx context.Context, req resource.UpdateRequest, r return } - // TIP: -- 3. Populate a modify input structure and check for changes if !plan.Name.Equal(state.Name) || !plan.Description.Equal(state.Description) || - !plan.ComplexArgument.Equal(state.ComplexArgument) || - !plan.Type.Equal(state.Type) { + !plan.SlotTypeID.Equal(state.SlotTypeID) { - in := &lexv2models.UpdateSlotInput{ - // TIP: Mandatory or fields that will always be present can be set when - // you create the Input structure. (Replace these with real fields.) + in := &lexmodelsv2.UpdateSlotInput{ SlotId: aws.String(plan.ID.ValueString()), SlotName: aws.String(plan.Name.ValueString()), - SlotType: aws.String(plan.Type.ValueString()), - } - - if !plan.Description.IsNull() { - // TIP: Optional fields should be set based on whether or not they are - // used. - in.Description = aws.String(plan.Description.ValueString()) } - if !plan.ComplexArgument.IsNull() { - // TIP: Use an expander to assign a complex argument. The elements must be - // deserialized into the appropriate struct before being passed to the expander. - var tfList []complexArgumentData - resp.Diagnostics.Append(plan.ComplexArgument.ElementsAs(ctx, &tfList, false)...) - if resp.Diagnostics.HasError() { - return - } - in.ComplexArgument = expandComplexArgument(tfList) + // TODO: expand here, or check for updatable arguments individually? + resp.Diagnostics.Append(flex.Expand(ctx, plan, &in)...) + if resp.Diagnostics.HasError() { + return } - // TIP: -- 4. Call the AWS modify/update function out, err := conn.UpdateSlot(ctx, in) if err != nil { resp.Diagnostics.AddError( @@ -692,7 +204,7 @@ func (r *resourceSlot) Update(ctx context.Context, req resource.UpdateRequest, r ) return } - if out == nil || out.Slot == nil { + if out == nil { resp.Diagnostics.AddError( create.ProblemStandardMessage(names.LexV2Models, create.ErrActionUpdating, ResNameSlot, plan.ID.String(), nil), errors.New("empty output").Error(), @@ -700,61 +212,29 @@ func (r *resourceSlot) Update(ctx context.Context, req resource.UpdateRequest, r return } - // TIP: Using the output from the update function, re-set any computed attributes - plan.ARN = flex.StringToFramework(ctx, out.Slot.Arn) - plan.ID = flex.StringToFramework(ctx, out.Slot.SlotId) - } - - // TIP: -- 5. Use a waiter to wait for update to complete - updateTimeout := r.UpdateTimeout(ctx, plan.Timeouts) - _, err := waitSlotUpdated(ctx, conn, plan.ID.ValueString(), updateTimeout) - if err != nil { - resp.Diagnostics.AddError( - create.ProblemStandardMessage(names.LexV2Models, create.ErrActionWaitingForUpdate, ResNameSlot, plan.ID.String(), err), - err.Error(), - ) - return + resp.Diagnostics.Append(flex.Flatten(ctx, out, &plan)...) + if resp.Diagnostics.HasError() { + return + } } - // TIP: -- 6. Save the request plan to response state resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) } func (r *resourceSlot) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { - // TIP: ==== RESOURCE DELETE ==== - // Most resources have Delete functions. There are rare situations - // where you might not need a delete: - // a. The AWS API does not provide a way to delete the resource - // b. The point of your resource is to perform an action (e.g., reboot a - // server) and deleting serves no purpose. - // - // The Delete function should do the following things. Make sure there - // is a good reason if you don't do one of these. - // - // 1. Get a client connection to the relevant service - // 2. Fetch the state - // 3. Populate a delete input structure - // 4. Call the AWS delete function - // 5. Use a waiter to wait for delete to complete - // TIP: -- 1. Get a client connection to the relevant service conn := r.Meta().LexV2ModelsClient(ctx) - // TIP: -- 2. Fetch the state var state resourceSlotData resp.Diagnostics.Append(req.State.Get(ctx, &state)...) if resp.Diagnostics.HasError() { return } - // TIP: -- 3. Populate a delete input structure - in := &lexv2models.DeleteSlotInput{ + in := &lexmodelsv2.DeleteSlotInput{ SlotId: aws.String(state.ID.ValueString()), } - // TIP: -- 4. Call the AWS delete function _, err := conn.DeleteSlot(ctx, in) - // TIP: On rare occassions, the API returns a not found error after deleting a - // resource. If that happens, we don't want it to show up as an error. if err != nil { var nfe *awstypes.ResourceNotFoundException if errors.As(err, &nfe) { @@ -766,145 +246,18 @@ func (r *resourceSlot) Delete(ctx context.Context, req resource.DeleteRequest, r ) return } - - // TIP: -- 5. Use a waiter to wait for delete to complete - deleteTimeout := r.DeleteTimeout(ctx, state.Timeouts) - _, err = waitSlotDeleted(ctx, conn, state.ID.ValueString(), deleteTimeout) - if err != nil { - resp.Diagnostics.AddError( - create.ProblemStandardMessage(names.LexV2Models, create.ErrActionWaitingForDeletion, ResNameSlot, state.ID.String(), err), - err.Error(), - ) - return - } } -// TIP: ==== TERRAFORM IMPORTING ==== -// If Read can get all the information it needs from the Identifier -// (i.e., path.Root("id")), you can use the PassthroughID importer. Otherwise, -// you'll need a custom import function. -// -// See more: -// https://developer.hashicorp.com/terraform/plugin/framework/resources/import func (r *resourceSlot) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) } -// TIP: ==== STATUS CONSTANTS ==== -// Create constants for states and statuses if the service does not -// already have suitable constants. We prefer that you use the constants -// provided in the service if available (e.g., amp.WorkspaceStatusCodeActive). -const ( - statusChangePending = "Pending" - statusDeleting = "Deleting" - statusNormal = "Normal" - statusUpdated = "Updated" -) - -// TIP: ==== WAITERS ==== -// Some resources of some services have waiters provided by the AWS API. -// Unless they do not work properly, use them rather than defining new ones -// here. -// -// Sometimes we define the wait, status, and find functions in separate -// files, wait.go, status.go, and find.go. Follow the pattern set out in the -// service and define these where it makes the most sense. -// -// If these functions are used in the _test.go file, they will need to be -// exported (i.e., capitalized). -// -// You will need to adjust the parameters and names to fit the service. -func waitSlotCreated(ctx context.Context, conn *lexv2models.Client, id string, timeout time.Duration) (*lexv2models.Slot, error) { - stateConf := &retry.StateChangeConf{ - Pending: []string{}, - Target: []string{statusNormal}, - Refresh: statusSlot(ctx, conn, id), - Timeout: timeout, - NotFoundChecks: 20, - ContinuousTargetOccurence: 2, - } - - outputRaw, err := stateConf.WaitForStateContext(ctx) - if out, ok := outputRaw.(*lexv2models.Slot); ok { - return out, err - } - - return nil, err -} - -// TIP: It is easier to determine whether a resource is updated for some -// resources than others. The best case is a status flag that tells you when -// the update has been fully realized. Other times, you can check to see if a -// key resource argument is updated to a new value or not. -func waitSlotUpdated(ctx context.Context, conn *lexv2models.Client, id string, timeout time.Duration) (*lexv2models.Slot, error) { - stateConf := &retry.StateChangeConf{ - Pending: []string{statusChangePending}, - Target: []string{statusUpdated}, - Refresh: statusSlot(ctx, conn, id), - Timeout: timeout, - NotFoundChecks: 20, - ContinuousTargetOccurence: 2, - } - - outputRaw, err := stateConf.WaitForStateContext(ctx) - if out, ok := outputRaw.(*lexv2models.Slot); ok { - return out, err - } - - return nil, err -} - -// TIP: A deleted waiter is almost like a backwards created waiter. There may -// be additional pending states, however. -func waitSlotDeleted(ctx context.Context, conn *lexv2models.Client, id string, timeout time.Duration) (*lexv2models.Slot, error) { - stateConf := &retry.StateChangeConf{ - Pending: []string{statusDeleting, statusNormal}, - Target: []string{}, - Refresh: statusSlot(ctx, conn, id), - Timeout: timeout, - } - - outputRaw, err := stateConf.WaitForStateContext(ctx) - if out, ok := outputRaw.(*lexv2models.Slot); ok { - return out, err - } - - return nil, err -} - -// TIP: ==== STATUS ==== -// The status function can return an actual status when that field is -// available from the API (e.g., out.Status). Otherwise, you can use custom -// statuses to communicate the states of the resource. -// -// Waiters consume the values returned by status functions. Design status so -// that it can be reused by a create, update, and delete waiter, if possible. -func statusSlot(ctx context.Context, conn *lexv2models.Client, id string) retry.StateRefreshFunc { - return func() (interface{}, string, error) { - out, err := findSlotByID(ctx, conn, id) - if tfresource.NotFound(err) { - return nil, "", nil - } - - if err != nil { - return nil, "", err - } - - return out, aws.ToString(out.Status), nil - } -} - -// TIP: ==== FINDERS ==== -// The find function is not strictly necessary. You could do the API -// request from the status function. However, we have found that find often -// comes in handy in other places besides the status function. As a result, it -// is good practice to define it separately. -func findSlotByID(ctx context.Context, conn *lexv2models.Client, id string) (*lexv2models.Slot, error) { - in := &lexv2models.GetSlotInput{ - Id: aws.String(id), +func findSlotByID(ctx context.Context, conn *lexmodelsv2.Client, id string) (*lexmodelsv2.DescribeSlotOutput, error) { + in := &lexmodelsv2.DescribeSlotInput{ + SlotId: aws.String(id), } - out, err := conn.GetSlot(ctx, in) + out, err := conn.DescribeSlot(ctx, in) if err != nil { var nfe *awstypes.ResourceNotFoundException if errors.As(err, &nfe) { @@ -917,168 +270,22 @@ func findSlotByID(ctx context.Context, conn *lexv2models.Client, id string) (*le return nil, err } - if out == nil || out.Slot == nil { + if out == nil { return nil, tfresource.NewEmptyResultError(in) } - return out.Slot, nil -} - -// TIP: ==== FLEX ==== -// Flatteners and expanders ("flex" functions) help handle complex data -// types. Flatteners take an API data type and return the equivalent Plugin-Framework -// type. In other words, flatteners translate from AWS -> Terraform. -// -// On the other hand, expanders take a Terraform data structure and return -// something that you can send to the AWS API. In other words, expanders -// translate from Terraform -> AWS. -// -// See more: -// https://hashicorp.github.io/terraform-provider-aws/data-handling-and-conversion/ -func flattenComplexArgument(ctx context.Context, apiObject *lexv2models.ComplexArgument) (types.List, diag.Diagnostics) { - var diags diag.Diagnostics - elemType := types.ObjectType{AttrTypes: complexArgumentAttrTypes} - - if apiObject == nil { - return types.ListNull(elemType), diags - } - - obj := map[string]attr.Value{ - "nested_required": flex.StringValueToFramework(ctx, apiObject.NestedRequired), - "nested_optional": flex.StringValueToFramework(ctx, apiObject.NestedOptional), - } - objVal, d := types.ObjectValue(complexArgumentAttrTypes, obj) - diags.Append(d...) - - listVal, d := types.ListValue(elemType, []attr.Value{objVal}) - diags.Append(d...) - - return listVal, diags -} - -// TIP: Often the AWS API will return a slice of structures in response to a -// request for information. Sometimes you will have set criteria (e.g., the ID) -// that means you'll get back a one-length slice. This plural function works -// brilliantly for that situation too. -func flattenComplexArguments(ctx context.Context, apiObjects []*lexv2models.ComplexArgument) (types.List, diag.Diagnostics) { - var diags diag.Diagnostics - elemType := types.ObjectType{AttrTypes: complexArgumentAttrTypes} - - if len(apiObjects) == 0 { - return types.ListNull(elemType), diags - } - - elems := []attr.Value{} - for _, apiObject := range apiObjects { - if apiObject == nil { - continue - } - - obj := map[string]attr.Value{ - "nested_required": flex.StringValueToFramework(ctx, apiObject.NestedRequired), - "nested_optional": flex.StringValueToFramework(ctx, apiObject.NestedOptional), - } - objVal, d := types.ObjectValue(complexArgumentAttrTypes, obj) - diags.Append(d...) - - elems = append(elems, objVal) - } - - listVal, d := types.ListValue(elemType, elems) - diags.Append(d...) - - return listVal, diags + return out, nil } -// TIP: Remember, as mentioned above, expanders take a Terraform data structure -// and return something that you can send to the AWS API. In other words, -// expanders translate from Terraform -> AWS. -// -// See more: -// https://hashicorp.github.io/terraform-provider-aws/data-handling-and-conversion/ -func expandComplexArgument(tfList []complexArgumentData) *lexv2models.ComplexArgument { - if len(tfList) == 0 { - return nil - } - - tfObj := tfList[0] - apiObject := &lexv2models.ComplexArgument{ - NestedRequired: aws.String(tfObj.NestedRequired.ValueString()), - } - if !tfObj.NestedOptional.IsNull() { - apiObject.NestedOptional = aws.String(tfObj.NestedOptional.ValueString()) - } - - return apiObject -} - -// TIP: Even when you have a list with max length of 1, this plural function -// works brilliantly. However, if the AWS API takes a structure rather than a -// slice of structures, you will not need it. -func expandComplexArguments(tfList []complexArgumentData) []*lexv2models.ComplexArgument { - // TIP: The AWS API can be picky about whether you send a nil or zero- - // length for an argument that should be cleared. For example, in some - // cases, if you send a nil value, the AWS API interprets that as "make no - // changes" when what you want to say is "remove everything." Sometimes - // using a zero-length list will cause an error. - // - // As a result, here are two options. Usually, option 1, nil, will work as - // expected, clearing the field. But, test going from something to nothing - // to make sure it works. If not, try the second option. - // TIP: Option 1: Returning nil for zero-length list - if len(tfList) == 0 { - return nil - } - - var apiObject []*lexv2models.ComplexArgument - // TIP: Option 2: Return zero-length list for zero-length list. If option 1 does - // not work, after testing going from something to nothing (if that is - // possible), uncomment out the next line and remove option 1. - // - // apiObject := make([]*lexv2models.ComplexArgument, 0) - - for _, tfObj := range tfList { - item := &lexv2models.ComplexArgument{ - NestedRequired: aws.String(tfObj.NestedRequired.ValueString()), - } - if !tfObj.NestedOptional.IsNull() { - item.NestedOptional = aws.String(tfObj.NestedOptional.ValueString()) - } - - apiObject = append(apiObject, item) - } - - return apiObject -} - -// TIP: ==== DATA STRUCTURES ==== -// With Terraform Plugin-Framework configurations are deserialized into -// Go types, providing type safety without the need for type assertions. -// These structs should match the schema definition exactly, and the `tfsdk` -// tag value should match the attribute name. -// -// Nested objects are represented in their own data struct. These will -// also have a corresponding attribute type mapping for use inside flex -// functions. -// -// See more: -// https://developer.hashicorp.com/terraform/plugin/framework/handling-data/accessing-values type resourceSlotData struct { - ARN types.String `tfsdk:"arn"` - ComplexArgument types.List `tfsdk:"complex_argument"` - Description types.String `tfsdk:"description"` - ID types.String `tfsdk:"id"` - Name types.String `tfsdk:"name"` - Timeouts timeouts.Value `tfsdk:"timeouts"` - Type types.String `tfsdk:"type"` -} - -type complexArgumentData struct { - NestedRequired types.String `tfsdk:"nested_required"` - NestedOptional types.String `tfsdk:"nested_optional"` -} - -var complexArgumentAttrTypes = map[string]attr.Type{ - "nested_required": types.StringType, - "nested_optional": types.StringType, + BotID types.String `tfsdk:"bot_id"` + BotVersion types.String `tfsdk:"bot_version"` + Description types.String `tfsdk:"description"` + ID types.String `tfsdk:"id"` + IntentID types.String `tfsdk:"intent_id"` + LocaleID types.String `tfsdk:"locale_id"` + Name types.String `tfsdk:"name"` + Timeouts timeouts.Value `tfsdk:"timeouts"` + SlotTypeID types.String `tfsdk:"slot_type_id"` + ValueElicitationSettings fwtypes.ListNestedObjectValueOf[lexschema.ValueElicitationSettingData] `tfsdk:"value_elicitation_settings"` } diff --git a/internal/service/lexv2models/slot_test.go b/internal/service/lexv2models/slot_test.go index 8a273063670..78622530848 100644 --- a/internal/service/lexv2models/slot_test.go +++ b/internal/service/lexv2models/slot_test.go @@ -2,47 +2,15 @@ // SPDX-License-Identifier: MPL-2.0 package lexv2models_test -// **PLEASE DELETE THIS AND ALL TIP COMMENTS BEFORE SUBMITTING A PR FOR REVIEW!** -// -// TIP: ==== INTRODUCTION ==== -// Thank you for trying the skaff tool! -// -// You have opted to include these helpful comments. They all include "TIP:" -// to help you find and remove them when you're done with them. -// -// While some aspects of this file are customized to your input, the -// scaffold tool does *not* look at the AWS API and ensure it has correct -// function, structure, and variable names. It makes guesses based on -// commonalities. You will need to make significant adjustments. -// -// In other words, as generated, this is a rough outline of the work you will -// need to do. If something doesn't make sense for your situation, get rid of -// it. import ( - // TIP: ==== IMPORTS ==== - // This is a common set of imports but not customized to your code since - // your code hasn't been written yet. Make sure you, your IDE, or - // goimports -w fixes these imports. - // - // The provider linter wants your imports to be in two groups: first, - // standard library (i.e., "fmt" or "strings"), second, everything else. - // - // Also, AWS Go SDK v2 may handle nested structures differently than v1, - // using the services/lexv2models/types package. If so, you'll - // need to import types and reference the nested types, e.g., as - // types.. "context" + "errors" "fmt" - "regexp" - "strings" "testing" - "github.com/YakDriver/regexache" - - "github.com/aws/aws-sdk-go-v2/aws" - "github.com/aws/aws-sdk-go-v2/service/lexv2models" - "github.com/aws/aws-sdk-go-v2/service/lexv2models/types" + "github.com/aws/aws-sdk-go-v2/service/lexmodelsv2" + "github.com/aws/aws-sdk-go-v2/service/lexmodelsv2/types" sdkacctest "github.com/hashicorp/terraform-plugin-testing/helper/acctest" "github.com/hashicorp/terraform-plugin-testing/helper/resource" "github.com/hashicorp/terraform-plugin-testing/terraform" @@ -52,105 +20,16 @@ import ( "github.com/hashicorp/terraform-provider-aws/internal/errs" "github.com/hashicorp/terraform-provider-aws/names" - // TIP: You will often need to import the package that this test file lives - // in. Since it is in the "test" context, it must import the package to use - // any normal context constants, variables, or functions. tflexv2models "github.com/hashicorp/terraform-provider-aws/internal/service/lexv2models" ) -// TIP: File Structure. The basic outline for all test files should be as -// follows. Improve this resource's maintainability by following this -// outline. -// -// 1. Package declaration (add "_test" since this is a test file) -// 2. Imports -// 3. Unit tests -// 4. Basic test -// 5. Disappears test -// 6. All the other tests -// 7. Helper functions (exists, destroy, check, etc.) -// 8. Functions that return Terraform configurations - -// TIP: ==== UNIT TESTS ==== -// This is an example of a unit test. Its name is not prefixed with -// "TestAcc" like an acceptance test. -// -// Unlike acceptance tests, unit tests do not access AWS and are focused on a -// function (or method). Because of this, they are quick and cheap to run. -// -// In designing a resource's implementation, isolate complex bits from AWS bits -// so that they can be tested through a unit test. We encourage more unit tests -// in the provider. -// -// Cut and dry functions using well-used patterns, like typical flatteners and -// expanders, don't need unit testing. However, if they are complex or -// intricate, they should be unit tested. -func TestSlotExampleUnitTest(t *testing.T) { - t.Parallel() - - testCases := []struct { - TestName string - Input string - Expected string - Error bool - }{ - { - TestName: "empty", - Input: "", - Expected: "", - Error: true, - }, - { - TestName: "descriptive name", - Input: "some input", - Expected: "some output", - Error: false, - }, - { - TestName: "another descriptive name", - Input: "more input", - Expected: "more output", - Error: false, - }, - } - - for _, testCase := range testCases { - testCase := testCase - t.Run(testCase.TestName, func(t *testing.T) { - t.Parallel() - got, err := tflexv2models.FunctionFromResource(testCase.Input) - - if err != nil && !testCase.Error { - t.Errorf("got error (%s), expected no error", err) - } - - if err == nil && testCase.Error { - t.Errorf("got (%s) and no error, expected error", got) - } - - if got != testCase.Expected { - t.Errorf("got %s, expected %s", got, testCase.Expected) - } - }) - } -} - -// TIP: ==== ACCEPTANCE TESTS ==== -// This is an example of a basic acceptance test. This should test as much of -// standard functionality of the resource as possible, and test importing, if -// applicable. We prefix its name with "TestAcc", the service, and the -// resource name. -// -// Acceptance test access AWS and cost money to run. func TestAccLexV2ModelsSlot_basic(t *testing.T) { ctx := acctest.Context(t) - // TIP: This is a long-running test guard for tests that run longer than - // 300s (5 min) generally. if testing.Short() { t.Skip("skipping long-running test in short mode") } - var slot lexv2models.DescribeSlotResponse + var slot lexmodelsv2.DescribeSlotOutput rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resourceName := "aws_lexv2models_slot.test" @@ -158,7 +37,6 @@ func TestAccLexV2ModelsSlot_basic(t *testing.T) { PreCheck: func() { acctest.PreCheck(ctx, t) acctest.PreCheckPartitionHasService(t, names.LexV2ModelsEndpointID) - testAccPreCheck(ctx, t) }, ErrorCheck: acctest.ErrorCheck(t, names.LexV2ModelsEndpointID), ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, @@ -168,22 +46,13 @@ func TestAccLexV2ModelsSlot_basic(t *testing.T) { Config: testAccSlotConfig_basic(rName), Check: resource.ComposeTestCheckFunc( testAccCheckSlotExists(ctx, resourceName, &slot), - resource.TestCheckResourceAttr(resourceName, "auto_minor_version_upgrade", "false"), - resource.TestCheckResourceAttrSet(resourceName, "maintenance_window_start_time.0.day_of_week"), - resource.TestCheckTypeSetElemNestedAttrs(resourceName, "user.*", map[string]string{ - "console_access": "false", - "groups.#": "0", - "username": "Test", - "password": "TestTest1234", - }), - acctest.MatchResourceAttrRegionalARN(resourceName, "arn", "lexv2models", regexache.MustCompile(`slot:+.`)), + resource.TestCheckResourceAttr(resourceName, "name", rName), ), }, { - ResourceName: resourceName, - ImportState: true, - ImportStateVerify: true, - ImportStateVerifyIgnore: []string{"apply_immediately", "user"}, + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, }, }, }) @@ -195,7 +64,7 @@ func TestAccLexV2ModelsSlot_disappears(t *testing.T) { t.Skip("skipping long-running test in short mode") } - var slot lexv2models.DescribeSlotResponse + var slot lexmodelsv2.DescribeSlotOutput rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resourceName := "aws_lexv2models_slot.test" @@ -203,22 +72,15 @@ func TestAccLexV2ModelsSlot_disappears(t *testing.T) { PreCheck: func() { acctest.PreCheck(ctx, t) acctest.PreCheckPartitionHasService(t, names.LexV2ModelsEndpointID) - testAccPreCheck(t) }, ErrorCheck: acctest.ErrorCheck(t, names.LexV2ModelsEndpointID), ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, CheckDestroy: testAccCheckSlotDestroy(ctx), Steps: []resource.TestStep{ { - Config: testAccSlotConfig_basic(rName, testAccSlotVersionNewer), + Config: testAccSlotConfig_basic(rName), Check: resource.ComposeTestCheckFunc( testAccCheckSlotExists(ctx, resourceName, &slot), - // TIP: The Plugin-Framework disappears helper is similar to the Plugin-SDK version, - // but expects a new resource factory function as the third argument. To expose this - // private function to the testing package, you may need to add a line like the following - // to exports_test.go: - // - // var ResourceSlot = newResourceSlot acctest.CheckFrameworkResourceDisappears(ctx, acctest.Provider, tflexv2models.ResourceSlot, resourceName), ), ExpectNonEmptyPlan: true, @@ -236,13 +98,8 @@ func testAccCheckSlotDestroy(ctx context.Context) resource.TestCheckFunc { continue } - input := &lexv2models.DescribeSlotInput{ - SlotId: aws.String(rs.Primary.ID), - } - _, err := conn.DescribeSlot(ctx, &lexv2models.DescribeSlotInput{ - SlotId: aws.String(rs.Primary.ID), - }) - if errs.IsA[*types.ResourceNotFoundException](err){ + _, err := tflexv2models.FindSlotByID(ctx, conn, rs.Primary.ID) + if errs.IsA[*types.ResourceNotFoundException](err) { return nil } if err != nil { @@ -256,7 +113,7 @@ func testAccCheckSlotDestroy(ctx context.Context) resource.TestCheckFunc { } } -func testAccCheckSlotExists(ctx context.Context, name string, slot *lexv2models.DescribeSlotResponse) resource.TestCheckFunc { +func testAccCheckSlotExists(ctx context.Context, name string, slot *lexmodelsv2.DescribeSlotOutput) resource.TestCheckFunc { return func(s *terraform.State) error { rs, ok := s.RootModule().Resources[name] if !ok { @@ -268,67 +125,22 @@ func testAccCheckSlotExists(ctx context.Context, name string, slot *lexv2models. } conn := acctest.Provider.Meta().(*conns.AWSClient).LexV2ModelsClient(ctx) - resp, err := conn.DescribeSlot(ctx, &lexv2models.DescribeSlotInput{ - SlotId: aws.String(rs.Primary.ID), - }) + out, err := tflexv2models.FindSlotByID(ctx, conn, rs.Primary.ID) if err != nil { return create.Error(names.LexV2Models, create.ErrActionCheckingExistence, tflexv2models.ResNameSlot, rs.Primary.ID, err) } - *slot = *resp + *slot = *out return nil } } -func testAccPreCheck(ctx context.Context, t *testing.T) { - conn := acctest.Provider.Meta().(*conns.AWSClient).LexV2ModelsClient(ctx) - - input := &lexv2models.ListSlotsInput{} - _, err := conn.ListSlots(ctx, input) - - if acctest.PreCheckSkipError(err) { - t.Skipf("skipping acceptance testing: %s", err) - } - if err != nil { - t.Fatalf("unexpected PreCheck error: %s", err) - } -} - -func testAccCheckSlotNotRecreated(before, after *lexv2models.DescribeSlotResponse) resource.TestCheckFunc { - return func(s *terraform.State) error { - if before, after := aws.ToString(before.SlotId), aws.ToString(after.SlotId); before != after { - return create.Error(names.LexV2Models, create.ErrActionCheckingNotRecreated, tflexv2models.ResNameSlot, aws.ToString(before.SlotId), errors.New("recreated")) - } - - return nil - } -} - -func testAccSlotConfig_basic(rName, version string) string { +func testAccSlotConfig_basic(rName string) string { return fmt.Sprintf(` -resource "aws_security_group" "test" { - name = %[1]q -} - resource "aws_lexv2models_slot" "test" { - slot_name = %[1]q - engine_type = "ActiveLexV2Models" - engine_version = %[2]q - host_instance_type = "lexv2models.t2.micro" - security_groups = [aws_security_group.test.id] - authentication_strategy = "simple" - storage_type = "efs" - - logs { - general = true - } - - user { - username = "Test" - password = "TestTest1234" - } + name = %[1]q } -`, rName, version) +`, rName) } From dd32276ef7a30ef18d8a298a79e3311aabd0e30d Mon Sep 17 00:00:00 2001 From: Jared Baker Date: Thu, 7 Dec 2023 20:27:41 -0500 Subject: [PATCH 06/42] r/aws_lexv2models_slot: reorg schema directory --- .../schema/default_value_specification.go | 45 +++++++++++++++++++ ...ngs.go => value_elicitatation_settings.go} | 30 +------------ 2 files changed, 46 insertions(+), 29 deletions(-) create mode 100644 internal/service/lexv2models/schema/default_value_specification.go rename internal/service/lexv2models/schema/{slot_value_elicitivation_settings.go => value_elicitatation_settings.go} (59%) diff --git a/internal/service/lexv2models/schema/default_value_specification.go b/internal/service/lexv2models/schema/default_value_specification.go new file mode 100644 index 00000000000..3efbbf55318 --- /dev/null +++ b/internal/service/lexv2models/schema/default_value_specification.go @@ -0,0 +1,45 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package schema + +import ( + "context" + + "github.com/hashicorp/terraform-plugin-framework-validators/listvalidator" + "github.com/hashicorp/terraform-plugin-framework/provider/schema" + "github.com/hashicorp/terraform-plugin-framework/schema/validator" + "github.com/hashicorp/terraform-plugin-framework/types" + fwtypes "github.com/hashicorp/terraform-provider-aws/internal/framework/types" +) + +func DefaultValueSpecificationBlock(ctx context.Context) schema.ListNestedBlock { + return schema.ListNestedBlock{ + CustomType: fwtypes.NewListNestedObjectTypeOf[DefaultValueSpecificationData](ctx), + NestedObject: schema.NestedBlockObject{ + Blocks: map[string]schema.Block{ + "default_value_list": schema.ListNestedBlock{ + CustomType: fwtypes.NewListNestedObjectTypeOf[DefaultValueData](ctx), + Validators: []validator.List{ + listvalidator.IsRequired(), + }, + NestedObject: schema.NestedBlockObject{ + Attributes: map[string]schema.Attribute{ + "default_value": schema.StringAttribute{ + Required: true, + }, + }, + }, + }, + }, + }, + } +} + +type DefaultValueSpecificationData struct { + DefaultValueList fwtypes.ListNestedObjectValueOf[DefaultValueData] `tfsdk:"default_value_list"` +} + +type DefaultValueData struct { + DefaultValue types.String `tfsdk:"default_value"` +} diff --git a/internal/service/lexv2models/schema/slot_value_elicitivation_settings.go b/internal/service/lexv2models/schema/value_elicitatation_settings.go similarity index 59% rename from internal/service/lexv2models/schema/slot_value_elicitivation_settings.go rename to internal/service/lexv2models/schema/value_elicitatation_settings.go index 864f22a8bbe..1a17be380f4 100644 --- a/internal/service/lexv2models/schema/slot_value_elicitivation_settings.go +++ b/internal/service/lexv2models/schema/value_elicitatation_settings.go @@ -10,7 +10,6 @@ import ( "github.com/hashicorp/terraform-plugin-framework-validators/listvalidator" "github.com/hashicorp/terraform-plugin-framework/provider/schema" "github.com/hashicorp/terraform-plugin-framework/schema/validator" - "github.com/hashicorp/terraform-plugin-framework/types" "github.com/hashicorp/terraform-provider-aws/internal/enum" fwtypes "github.com/hashicorp/terraform-provider-aws/internal/framework/types" ) @@ -32,26 +31,7 @@ func ValueElicitationSettingBlock(ctx context.Context) schema.ListNestedBlock { }, }, Blocks: map[string]schema.Block{ - "default_value_specification": schema.ListNestedBlock{ - CustomType: fwtypes.NewListNestedObjectTypeOf[DefaultValueSpecificationData](ctx), - NestedObject: schema.NestedBlockObject{ - Blocks: map[string]schema.Block{ - "default_value_list": schema.ListNestedBlock{ - CustomType: fwtypes.NewListNestedObjectTypeOf[DefaultValueData](ctx), - Validators: []validator.List{ - listvalidator.IsRequired(), - }, - NestedObject: schema.NestedBlockObject{ - Attributes: map[string]schema.Attribute{ - "default_value": schema.StringAttribute{ - Required: true, - }, - }, - }, - }, - }, - }, - }, + "default_value_specification": DefaultValueSpecificationBlock(ctx), }, }, } @@ -61,11 +41,3 @@ type ValueElicitationSettingData struct { SlotConstraint fwtypes.StringEnum[awstypes.SlotConstraint] `tfsdk:"slot_constraint"` DefaultValueSpecification fwtypes.ListNestedObjectValueOf[DefaultValueSpecificationData] `tfsdk:"default_value_specification"` } - -type DefaultValueSpecificationData struct { - DefaultValueList fwtypes.ListNestedObjectValueOf[DefaultValueData] `tfsdk:"default_value_list"` -} - -type DefaultValueData struct { - DefaultValue types.String `tfsdk:"default_value"` -} From e8bc0ba2db8402dbd5a37d44756467b8852d5d71 Mon Sep 17 00:00:00 2001 From: Jared Baker Date: Thu, 7 Dec 2023 20:47:00 -0500 Subject: [PATCH 07/42] r/aws_lexv2models_slot: slot_resolution_setting schema --- .../schema/slot_resolution_setting.go | 30 +++++++++++++++++++ .../schema/value_elicitatation_settings.go | 2 ++ 2 files changed, 32 insertions(+) create mode 100644 internal/service/lexv2models/schema/slot_resolution_setting.go diff --git a/internal/service/lexv2models/schema/slot_resolution_setting.go b/internal/service/lexv2models/schema/slot_resolution_setting.go new file mode 100644 index 00000000000..3b6022bea12 --- /dev/null +++ b/internal/service/lexv2models/schema/slot_resolution_setting.go @@ -0,0 +1,30 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package schema + +import ( + "context" + + awstypes "github.com/aws/aws-sdk-go-v2/service/lexmodelsv2/types" + "github.com/hashicorp/terraform-plugin-framework/provider/schema" + fwtypes "github.com/hashicorp/terraform-provider-aws/internal/framework/types" +) + +type SlotResolutionSettingData struct { + SlotResolutionStrategy fwtypes.StringEnum[awstypes.SlotResolutionStrategy] `tfsdk:"slot_resolution_strategy"` +} + +func SlotResolutionSettingBlock(ctx context.Context) schema.ListNestedBlock { + return schema.ListNestedBlock{ + CustomType: fwtypes.NewListNestedObjectTypeOf[SlotResolutionSettingData](ctx), + NestedObject: schema.NestedBlockObject{ + Attributes: map[string]schema.Attribute{ + "slot_resolution_strategy": schema.StringAttribute{ + CustomType: fwtypes.StringEnumType[awstypes.SlotResolutionStrategy](), + Required: true, + }, + }, + }, + } +} diff --git a/internal/service/lexv2models/schema/value_elicitatation_settings.go b/internal/service/lexv2models/schema/value_elicitatation_settings.go index 1a17be380f4..2f1643035e3 100644 --- a/internal/service/lexv2models/schema/value_elicitatation_settings.go +++ b/internal/service/lexv2models/schema/value_elicitatation_settings.go @@ -32,6 +32,7 @@ func ValueElicitationSettingBlock(ctx context.Context) schema.ListNestedBlock { }, Blocks: map[string]schema.Block{ "default_value_specification": DefaultValueSpecificationBlock(ctx), + "slot_resolution_setting": SlotResolutionSettingBlock(ctx), }, }, } @@ -40,4 +41,5 @@ func ValueElicitationSettingBlock(ctx context.Context) schema.ListNestedBlock { type ValueElicitationSettingData struct { SlotConstraint fwtypes.StringEnum[awstypes.SlotConstraint] `tfsdk:"slot_constraint"` DefaultValueSpecification fwtypes.ListNestedObjectValueOf[DefaultValueSpecificationData] `tfsdk:"default_value_specification"` + SlotResolutionSetting fwtypes.ListNestedObjectValueOf[SlotResolutionSettingData] `tfsdk:"slot_resolution_setting"` } From 7243b74ccf323629ec50fc1bd543e6f4a258ba31 Mon Sep 17 00:00:00 2001 From: Sharon Nam Date: Mon, 11 Dec 2023 20:30:01 -0800 Subject: [PATCH 08/42] work on prompt_specification schema --- .../lexv2models/schema/message_groups.go | 150 ++++++++++++++++++ .../schema/prompt_specification.go | 124 +++++++++++++++ internal/service/lexv2models/slot.go | 1 + 3 files changed, 275 insertions(+) create mode 100644 internal/service/lexv2models/schema/message_groups.go create mode 100644 internal/service/lexv2models/schema/prompt_specification.go diff --git a/internal/service/lexv2models/schema/message_groups.go b/internal/service/lexv2models/schema/message_groups.go new file mode 100644 index 00000000000..79fed05621f --- /dev/null +++ b/internal/service/lexv2models/schema/message_groups.go @@ -0,0 +1,150 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package schema + +import ( + "context" + + "github.com/hashicorp/terraform-plugin-framework-validators/listvalidator" + "github.com/hashicorp/terraform-plugin-framework/provider/schema" + "github.com/hashicorp/terraform-plugin-framework/schema/validator" + "github.com/hashicorp/terraform-plugin-framework/types" + fwtypes "github.com/hashicorp/terraform-provider-aws/internal/framework/types" +) + +func MessageGroupsBlock(ctx context.Context) schema.ListNestedBlock { + messageNBO := schema.NestedBlockObject{ + Blocks: map[string]schema.Block{ + "custom_playload": schema.ListNestedBlock{ + Validators: []validator.List{ + listvalidator.SizeAtMost(1), + }, + CustomType: fwtypes.NewListNestedObjectTypeOf[CustomPayload](ctx), + NestedObject: schema.NestedBlockObject{ + Attributes: map[string]schema.Attribute{ + "value": schema.StringAttribute{ + Required: true, + }, + }, + }, + }, + "image_response_card": schema.ListNestedBlock{ + Validators: []validator.List{ + listvalidator.SizeAtMost(1), + }, + CustomType: fwtypes.NewListNestedObjectTypeOf[ImageResponseCard](ctx), + NestedObject: schema.NestedBlockObject{ + Attributes: map[string]schema.Attribute{ + "image_url": schema.StringAttribute{ + Optional: true, + }, + "subtitle": schema.StringAttribute{ + Optional: true, + }, + "title": schema.StringAttribute{ + Required: true, + }, + }, + Blocks: map[string]schema.Block{ + "button": schema.ListNestedBlock{ + CustomType: fwtypes.NewListNestedObjectTypeOf[Button](ctx), + NestedObject: schema.NestedBlockObject{ + Attributes: map[string]schema.Attribute{ + "text": schema.StringAttribute{ + Required: true, + }, + "value": schema.StringAttribute{ + Required: true, + }, + }, + }, + }, + }, + }, + }, + "plain_text_message": schema.ListNestedBlock{ + Validators: []validator.List{ + listvalidator.SizeAtMost(1), + }, + CustomType: fwtypes.NewListNestedObjectTypeOf[PlainTextMessage](ctx), + NestedObject: schema.NestedBlockObject{ + Attributes: map[string]schema.Attribute{ + "value": schema.StringAttribute{ + Required: true, + }, + }, + }, + }, + "ssml_message": schema.ListNestedBlock{ + Validators: []validator.List{ + listvalidator.SizeAtMost(1), + }, + CustomType: fwtypes.NewListNestedObjectTypeOf[SSMLMessage](ctx), + NestedObject: schema.NestedBlockObject{ + Attributes: map[string]schema.Attribute{ + "value": schema.StringAttribute{ + Required: true, + }, + }, + }, + }, + }, + } + return schema.ListNestedBlock{ + Validators: []validator.List{ + listvalidator.SizeAtLeast(1), + }, + CustomType: fwtypes.NewListNestedObjectTypeOf[MessageGroup](ctx), + NestedObject: schema.NestedBlockObject{ + Blocks: map[string]schema.Block{ + "message": schema.ListNestedBlock{ + Validators: []validator.List{ + listvalidator.SizeBetween(1, 1), + }, + CustomType: fwtypes.NewListNestedObjectTypeOf[Message](ctx), + NestedObject: messageNBO, + }, + "variations": schema.ListNestedBlock{ + CustomType: fwtypes.NewListNestedObjectTypeOf[Message](ctx), + NestedObject: messageNBO, + }, + }, + }, + } +} + +type CustomPayload struct { + Value types.String `tfsdk:"value"` +} + +type ImageResponseCard struct { + Title types.String `tfsdk:"title"` + Button fwtypes.ListNestedObjectValueOf[Button] `tfsdk:"buttons"` + ImageURL types.String `tfsdk:"image_url"` + Subtitle types.String `tfsdk:"subtitle"` +} + +type Button struct { + Text types.String `tfsdk:"text"` + Value types.String `tfsdk:"value"` +} + +type PlainTextMessage struct { + Value types.String `tfsdk:"value"` +} + +type SSMLMessage struct { + Value types.String `tfsdk:"value"` +} +type MessageGroup struct { + Message fwtypes.ListNestedObjectValueOf[Message] `tfsdk:"message"` + Variations fwtypes.ListNestedObjectValueOf[Message] `tfsdk:"variations"` +} + +type Message struct { + CustomPayload fwtypes.ListNestedObjectValueOf[CustomPayload] `tfsdk:"custom_payload"` + ImageResponseCard fwtypes.ListNestedObjectValueOf[ImageResponseCard] `tfsdk:"image_response_card"` + PlainTextMessage fwtypes.ListNestedObjectValueOf[PlainTextMessage] `tfsdk:"plain_text_message"` + SSMLMessage fwtypes.ListNestedObjectValueOf[SSMLMessage] `tfsdk:"ssml_message"` +} diff --git a/internal/service/lexv2models/schema/prompt_specification.go b/internal/service/lexv2models/schema/prompt_specification.go new file mode 100644 index 00000000000..b37a1d3f9b6 --- /dev/null +++ b/internal/service/lexv2models/schema/prompt_specification.go @@ -0,0 +1,124 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package schema + +import ( + "context" + + awstypes "github.com/aws/aws-sdk-go-v2/service/lexmodelsv2/types" + "github.com/hashicorp/terraform-plugin-framework-validators/listvalidator" + "github.com/hashicorp/terraform-plugin-framework/attr" + "github.com/hashicorp/terraform-plugin-framework/provider/schema" + "github.com/hashicorp/terraform-plugin-framework/schema/validator" + "github.com/hashicorp/terraform-plugin-framework/types" + "github.com/hashicorp/terraform-provider-aws/internal/enum" + fwtypes "github.com/hashicorp/terraform-provider-aws/internal/framework/types" +) + +func PromptSpecificationBlock(ctx context.Context) schema.ListNestedBlock { + return schema.ListNestedBlock{ + Validators: []validator.List{ + listvalidator.SizeBetween(1, 1), + }, + NestedObject: schema.NestedBlockObject{ + Attributes: map[string]schema.Attribute{ + "max_retries": schema.Int64Attribute{ + Required: true, + }, + "allow_interrupt": schema.BoolAttribute{ + Optional: true, + }, + "message_selection_strategy": schema.StringAttribute{ + Optional: true, + Validators: []validator.String{ + enum.FrameworkValidate[awstypes.MessageSelectionStrategy](), + }, + }, + "prompt_attempts_specification": schema.MapAttribute{ + Optional: true, + ElementType: types.ObjectType{ + AttrTypes: map[string]attr.Type{ + "allow_input_types": types.ObjectType{ + AttrTypes: map[string]attr.Type{ + "allow_audio_input": types.BoolType, + "allow_dtmf_input": types.BoolType, + }, + }, + "allow_interrupts": types.BoolType, + "audio_and_dtmf_input_specification": types.ObjectType{ + AttrTypes: map[string]attr.Type{ + "start_timeout_ms": types.Int64Type, + "audio_specification": types.ObjectType{ + AttrTypes: map[string]attr.Type{ + "end_timeout_ms": types.Int64Type, + "max_length_ms": types.Int64Type, + }, + }, + "dtmf_specification": types.ObjectType{ + AttrTypes: map[string]attr.Type{ + "deletion_character": types.StringType, + "end_character": types.StringType, + "end_timeout_ms": types.Int64Type, + "max_length": types.Int64Type, + }, + }, + }, + }, + "text_input_specification": types.ObjectType{ + AttrTypes: map[string]attr.Type{ + "start_timeout_ms": types.Int64Type, + }, + }, + }, + }, + }, + }, + Blocks: map[string]schema.Block{ + "message_groups": MessageGroupsBlock(ctx), + }, + }, + } +} + +type PromptSpecification struct { + MaxRetries types.Int64 `tfsdk:"max_retries"` + MessageGroup fwtypes.ListNestedObjectValueOf[MessageGroup] `tfsdk:"message_groups"` + AllowInterrupt types.Bool `tfsdk:"allow_interrupt"` + MessageSelectionStrategy fwtypes.StringEnum[awstypes.MessageSelectionStrategy] `tfsdk:"message_selection_strategy"` + PromptAttemptsSpecification fwtypes.ObjectMapValueOf[PromptAttemptSpecification] `tfsdk:"prompt_attempts_specification"` +} + +type PromptAttemptSpecification struct { + AllowedInputTypes fwtypes.ListNestedObjectValueOf[AllowedInputTypes] `tfsdk:"allowed_input_types"` + AllowInterrupt types.Bool `tfsdk:"allow_interrupt"` + AudioAndDTMFInputSpecification fwtypes.ListNestedObjectValueOf[AudioAndDTMFInputSpecification] `tfsdk:"audio_and_dtmf_input_specification"` + TextInputSpecification fwtypes.ListNestedObjectValueOf[TextInputSpecification] `tfsdk:"text_input_specification"` +} + +type DTMFSpecification struct { + DeletionCharacter types.String `tfsdk:"deletion_character"` + EndCharacter types.String `tfsdk:"end_character"` + EndTimeoutMs types.Int64 `tfsdk:"end_timeout_ms"` + MaxLength types.Int64 `tfsdk:"max_length"` +} + +type TextInputSpecification struct { + StartTimeoutMs types.Int64 `tfsdk:"start_timeout_ms"` +} + +type AllowedInputTypes struct { + AllowAudioInput types.Bool `tfsdk:"allow_audio_input"` + AllowDTMFInput types.Bool `tfsdk:"allow_dtmf_input"` +} + +type AudioAndDTMFInputSpecification struct { + StartTimeoutMs types.Int64 `tfsdk:"start_timeout_ms"` + AudioSpecification fwtypes.ListNestedObjectValueOf[AudioSpecification] `tfsdk:"audio_specification"` + DTMFSpecification fwtypes.ListNestedObjectValueOf[DTMFSpecification] `tfsdk:"dtmf_specification"` +} + +type AudioSpecification struct { + EndTimeoutMs types.Int64 `tfsdk:"end_timeout_ms"` + MaxLengthMs types.Int64 `tfsdk:"max_length_ms"` +} diff --git a/internal/service/lexv2models/slot.go b/internal/service/lexv2models/slot.go index dc9b6f059fa..1770088645a 100644 --- a/internal/service/lexv2models/slot.go +++ b/internal/service/lexv2models/slot.go @@ -95,6 +95,7 @@ func (r *resourceSlot) Schema(ctx context.Context, req resource.SchemaRequest, r }, Blocks: map[string]schema.Block{ "value_elicitation_setting": lexschema.ValueElicitationSettingBlock(ctx), + "prompt_specification": lexschema.PromptSpecificationBlock(ctx), }, } } From d1158adaade0b02efd8515665cf1115277d435de Mon Sep 17 00:00:00 2001 From: Sharon Nam Date: Tue, 12 Dec 2023 19:14:31 -0800 Subject: [PATCH 09/42] Fill out prompt_specification more and add sample_utterance --- .../lexv2models/schema/message_groups.go | 50 +++++++++---------- .../schema/prompt_specification.go | 40 +++++++-------- .../lexv2models/schema/sample_utterance..go | 29 +++++++++++ .../schema/value_elicitatation_settings.go | 4 ++ internal/service/lexv2models/slot.go | 1 - 5 files changed, 78 insertions(+), 46 deletions(-) create mode 100644 internal/service/lexv2models/schema/sample_utterance..go diff --git a/internal/service/lexv2models/schema/message_groups.go b/internal/service/lexv2models/schema/message_groups.go index 79fed05621f..df0b9a2ad82 100644 --- a/internal/service/lexv2models/schema/message_groups.go +++ b/internal/service/lexv2models/schema/message_groups.go @@ -20,7 +20,7 @@ func MessageGroupsBlock(ctx context.Context) schema.ListNestedBlock { Validators: []validator.List{ listvalidator.SizeAtMost(1), }, - CustomType: fwtypes.NewListNestedObjectTypeOf[CustomPayload](ctx), + CustomType: fwtypes.NewListNestedObjectTypeOf[CustomPayloadData](ctx), NestedObject: schema.NestedBlockObject{ Attributes: map[string]schema.Attribute{ "value": schema.StringAttribute{ @@ -33,7 +33,7 @@ func MessageGroupsBlock(ctx context.Context) schema.ListNestedBlock { Validators: []validator.List{ listvalidator.SizeAtMost(1), }, - CustomType: fwtypes.NewListNestedObjectTypeOf[ImageResponseCard](ctx), + CustomType: fwtypes.NewListNestedObjectTypeOf[ImageResponseCardData](ctx), NestedObject: schema.NestedBlockObject{ Attributes: map[string]schema.Attribute{ "image_url": schema.StringAttribute{ @@ -48,7 +48,7 @@ func MessageGroupsBlock(ctx context.Context) schema.ListNestedBlock { }, Blocks: map[string]schema.Block{ "button": schema.ListNestedBlock{ - CustomType: fwtypes.NewListNestedObjectTypeOf[Button](ctx), + CustomType: fwtypes.NewListNestedObjectTypeOf[ButtonData](ctx), NestedObject: schema.NestedBlockObject{ Attributes: map[string]schema.Attribute{ "text": schema.StringAttribute{ @@ -67,7 +67,7 @@ func MessageGroupsBlock(ctx context.Context) schema.ListNestedBlock { Validators: []validator.List{ listvalidator.SizeAtMost(1), }, - CustomType: fwtypes.NewListNestedObjectTypeOf[PlainTextMessage](ctx), + CustomType: fwtypes.NewListNestedObjectTypeOf[PlainTextMessageData](ctx), NestedObject: schema.NestedBlockObject{ Attributes: map[string]schema.Attribute{ "value": schema.StringAttribute{ @@ -80,7 +80,7 @@ func MessageGroupsBlock(ctx context.Context) schema.ListNestedBlock { Validators: []validator.List{ listvalidator.SizeAtMost(1), }, - CustomType: fwtypes.NewListNestedObjectTypeOf[SSMLMessage](ctx), + CustomType: fwtypes.NewListNestedObjectTypeOf[SSMLMessageData](ctx), NestedObject: schema.NestedBlockObject{ Attributes: map[string]schema.Attribute{ "value": schema.StringAttribute{ @@ -95,18 +95,18 @@ func MessageGroupsBlock(ctx context.Context) schema.ListNestedBlock { Validators: []validator.List{ listvalidator.SizeAtLeast(1), }, - CustomType: fwtypes.NewListNestedObjectTypeOf[MessageGroup](ctx), + CustomType: fwtypes.NewListNestedObjectTypeOf[MessageGroupData](ctx), NestedObject: schema.NestedBlockObject{ Blocks: map[string]schema.Block{ "message": schema.ListNestedBlock{ Validators: []validator.List{ listvalidator.SizeBetween(1, 1), }, - CustomType: fwtypes.NewListNestedObjectTypeOf[Message](ctx), + CustomType: fwtypes.NewListNestedObjectTypeOf[MessageData](ctx), NestedObject: messageNBO, }, "variations": schema.ListNestedBlock{ - CustomType: fwtypes.NewListNestedObjectTypeOf[Message](ctx), + CustomType: fwtypes.NewListNestedObjectTypeOf[MessageData](ctx), NestedObject: messageNBO, }, }, @@ -114,37 +114,37 @@ func MessageGroupsBlock(ctx context.Context) schema.ListNestedBlock { } } -type CustomPayload struct { +type CustomPayloadData struct { Value types.String `tfsdk:"value"` } -type ImageResponseCard struct { - Title types.String `tfsdk:"title"` - Button fwtypes.ListNestedObjectValueOf[Button] `tfsdk:"buttons"` - ImageURL types.String `tfsdk:"image_url"` - Subtitle types.String `tfsdk:"subtitle"` +type ImageResponseCardData struct { + Title types.String `tfsdk:"title"` + Button fwtypes.ListNestedObjectValueOf[ButtonData] `tfsdk:"buttons"` + ImageURL types.String `tfsdk:"image_url"` + Subtitle types.String `tfsdk:"subtitle"` } -type Button struct { +type ButtonData struct { Text types.String `tfsdk:"text"` Value types.String `tfsdk:"value"` } -type PlainTextMessage struct { +type PlainTextMessageData struct { Value types.String `tfsdk:"value"` } -type SSMLMessage struct { +type SSMLMessageData struct { Value types.String `tfsdk:"value"` } -type MessageGroup struct { - Message fwtypes.ListNestedObjectValueOf[Message] `tfsdk:"message"` - Variations fwtypes.ListNestedObjectValueOf[Message] `tfsdk:"variations"` +type MessageGroupData struct { + Message fwtypes.ListNestedObjectValueOf[MessageData] `tfsdk:"message"` + Variations fwtypes.ListNestedObjectValueOf[MessageData] `tfsdk:"variations"` } -type Message struct { - CustomPayload fwtypes.ListNestedObjectValueOf[CustomPayload] `tfsdk:"custom_payload"` - ImageResponseCard fwtypes.ListNestedObjectValueOf[ImageResponseCard] `tfsdk:"image_response_card"` - PlainTextMessage fwtypes.ListNestedObjectValueOf[PlainTextMessage] `tfsdk:"plain_text_message"` - SSMLMessage fwtypes.ListNestedObjectValueOf[SSMLMessage] `tfsdk:"ssml_message"` +type MessageData struct { + CustomPayload fwtypes.ListNestedObjectValueOf[CustomPayloadData] `tfsdk:"custom_payload"` + ImageResponseCard fwtypes.ListNestedObjectValueOf[ImageResponseCardData] `tfsdk:"image_response_card"` + PlainTextMessage fwtypes.ListNestedObjectValueOf[PlainTextMessageData] `tfsdk:"plain_text_message"` + SSMLMessage fwtypes.ListNestedObjectValueOf[SSMLMessageData] `tfsdk:"ssml_message"` } diff --git a/internal/service/lexv2models/schema/prompt_specification.go b/internal/service/lexv2models/schema/prompt_specification.go index b37a1d3f9b6..b3401e0eda4 100644 --- a/internal/service/lexv2models/schema/prompt_specification.go +++ b/internal/service/lexv2models/schema/prompt_specification.go @@ -81,44 +81,44 @@ func PromptSpecificationBlock(ctx context.Context) schema.ListNestedBlock { } } -type PromptSpecification struct { - MaxRetries types.Int64 `tfsdk:"max_retries"` - MessageGroup fwtypes.ListNestedObjectValueOf[MessageGroup] `tfsdk:"message_groups"` - AllowInterrupt types.Bool `tfsdk:"allow_interrupt"` - MessageSelectionStrategy fwtypes.StringEnum[awstypes.MessageSelectionStrategy] `tfsdk:"message_selection_strategy"` - PromptAttemptsSpecification fwtypes.ObjectMapValueOf[PromptAttemptSpecification] `tfsdk:"prompt_attempts_specification"` +type PromptSpecificationData struct { + AllowInterrupt types.Bool `tfsdk:"allow_interrupt"` + MaxRetries types.Int64 `tfsdk:"max_retries"` + MessageGroup fwtypes.ListNestedObjectValueOf[MessageGroupData] `tfsdk:"message_groups"` + MessageSelectionStrategy fwtypes.StringEnum[awstypes.MessageSelectionStrategy] `tfsdk:"message_selection_strategy"` + PromptAttemptsSpecification fwtypes.ObjectMapValueOf[PromptAttemptSpecificationData] `tfsdk:"prompt_attempts_specification"` } -type PromptAttemptSpecification struct { - AllowedInputTypes fwtypes.ListNestedObjectValueOf[AllowedInputTypes] `tfsdk:"allowed_input_types"` - AllowInterrupt types.Bool `tfsdk:"allow_interrupt"` - AudioAndDTMFInputSpecification fwtypes.ListNestedObjectValueOf[AudioAndDTMFInputSpecification] `tfsdk:"audio_and_dtmf_input_specification"` - TextInputSpecification fwtypes.ListNestedObjectValueOf[TextInputSpecification] `tfsdk:"text_input_specification"` +type PromptAttemptSpecificationData struct { + AllowedInputTypes fwtypes.ListNestedObjectValueOf[AllowedInputTypesData] `tfsdk:"allowed_input_types"` + AllowInterrupt types.Bool `tfsdk:"allow_interrupt"` + AudioAndDTMFInputSpecification fwtypes.ListNestedObjectValueOf[AudioAndDTMFInputSpecificationData] `tfsdk:"audio_and_dtmf_input_specification"` + TextInputSpecification fwtypes.ListNestedObjectValueOf[TextInputSpecificationData] `tfsdk:"text_input_specification"` } -type DTMFSpecification struct { - DeletionCharacter types.String `tfsdk:"deletion_character"` +type DTMFSpecificationData struct { EndCharacter types.String `tfsdk:"end_character"` EndTimeoutMs types.Int64 `tfsdk:"end_timeout_ms"` + DeletionCharacter types.String `tfsdk:"deletion_character"` MaxLength types.Int64 `tfsdk:"max_length"` } -type TextInputSpecification struct { +type TextInputSpecificationData struct { StartTimeoutMs types.Int64 `tfsdk:"start_timeout_ms"` } -type AllowedInputTypes struct { +type AllowedInputTypesData struct { AllowAudioInput types.Bool `tfsdk:"allow_audio_input"` AllowDTMFInput types.Bool `tfsdk:"allow_dtmf_input"` } -type AudioAndDTMFInputSpecification struct { - StartTimeoutMs types.Int64 `tfsdk:"start_timeout_ms"` - AudioSpecification fwtypes.ListNestedObjectValueOf[AudioSpecification] `tfsdk:"audio_specification"` - DTMFSpecification fwtypes.ListNestedObjectValueOf[DTMFSpecification] `tfsdk:"dtmf_specification"` +type AudioAndDTMFInputSpecificationData struct { + AudioSpecification fwtypes.ListNestedObjectValueOf[AudioSpecificationData] `tfsdk:"audio_specification"` + StartTimeoutMs types.Int64 `tfsdk:"start_timeout_ms"` + DTMFSpecification fwtypes.ListNestedObjectValueOf[DTMFSpecificationData] `tfsdk:"dtmf_specification"` } -type AudioSpecification struct { +type AudioSpecificationData struct { EndTimeoutMs types.Int64 `tfsdk:"end_timeout_ms"` MaxLengthMs types.Int64 `tfsdk:"max_length_ms"` } diff --git a/internal/service/lexv2models/schema/sample_utterance..go b/internal/service/lexv2models/schema/sample_utterance..go new file mode 100644 index 00000000000..8d55fb1e246 --- /dev/null +++ b/internal/service/lexv2models/schema/sample_utterance..go @@ -0,0 +1,29 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package schema + +import ( + "context" + + "github.com/hashicorp/terraform-plugin-framework/provider/schema" + "github.com/hashicorp/terraform-plugin-framework/types" + fwtypes "github.com/hashicorp/terraform-provider-aws/internal/framework/types" +) + +func SampleUtteranceBlock(ctx context.Context) schema.ListNestedBlock { + return schema.ListNestedBlock{ + CustomType: fwtypes.NewListNestedObjectTypeOf[SampleUtteranceData](ctx), + NestedObject: schema.NestedBlockObject{ + Attributes: map[string]schema.Attribute{ + "utterance": schema.StringAttribute{ + Required: true, + }, + }, + }, + } +} + +type SampleUtteranceData struct { + Utterance types.String `tfsdk:"utterance"` +} diff --git a/internal/service/lexv2models/schema/value_elicitatation_settings.go b/internal/service/lexv2models/schema/value_elicitatation_settings.go index 2f1643035e3..809507de600 100644 --- a/internal/service/lexv2models/schema/value_elicitatation_settings.go +++ b/internal/service/lexv2models/schema/value_elicitatation_settings.go @@ -33,6 +33,8 @@ func ValueElicitationSettingBlock(ctx context.Context) schema.ListNestedBlock { Blocks: map[string]schema.Block{ "default_value_specification": DefaultValueSpecificationBlock(ctx), "slot_resolution_setting": SlotResolutionSettingBlock(ctx), + "prompt_specification": PromptSpecificationBlock(ctx), + "sample_utterance": SampleUtteranceBlock(ctx), }, }, } @@ -42,4 +44,6 @@ type ValueElicitationSettingData struct { SlotConstraint fwtypes.StringEnum[awstypes.SlotConstraint] `tfsdk:"slot_constraint"` DefaultValueSpecification fwtypes.ListNestedObjectValueOf[DefaultValueSpecificationData] `tfsdk:"default_value_specification"` SlotResolutionSetting fwtypes.ListNestedObjectValueOf[SlotResolutionSettingData] `tfsdk:"slot_resolution_setting"` + PromptSpecification fwtypes.ListNestedObjectValueOf[PromptSpecificationData] `tfsdk:"prompt_specification"` + SampleUtterance fwtypes.ListNestedObjectValueOf[SampleUtteranceData] `tfsdk:"sample_utterance"` } diff --git a/internal/service/lexv2models/slot.go b/internal/service/lexv2models/slot.go index 1770088645a..dc9b6f059fa 100644 --- a/internal/service/lexv2models/slot.go +++ b/internal/service/lexv2models/slot.go @@ -95,7 +95,6 @@ func (r *resourceSlot) Schema(ctx context.Context, req resource.SchemaRequest, r }, Blocks: map[string]schema.Block{ "value_elicitation_setting": lexschema.ValueElicitationSettingBlock(ctx), - "prompt_specification": lexschema.PromptSpecificationBlock(ctx), }, } } From a54fbd6a256b0f8d04a1b3c45c1aa818b2123a0f Mon Sep 17 00:00:00 2001 From: Jared Baker Date: Thu, 14 Dec 2023 16:37:51 -0500 Subject: [PATCH 10/42] r/aws_lexv2models_slot(test): fix importlint --- internal/service/lexv2models/slot_test.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/internal/service/lexv2models/slot_test.go b/internal/service/lexv2models/slot_test.go index 78622530848..8a7bc17262b 100644 --- a/internal/service/lexv2models/slot_test.go +++ b/internal/service/lexv2models/slot_test.go @@ -18,9 +18,8 @@ import ( "github.com/hashicorp/terraform-provider-aws/internal/conns" "github.com/hashicorp/terraform-provider-aws/internal/create" "github.com/hashicorp/terraform-provider-aws/internal/errs" - "github.com/hashicorp/terraform-provider-aws/names" - tflexv2models "github.com/hashicorp/terraform-provider-aws/internal/service/lexv2models" + "github.com/hashicorp/terraform-provider-aws/names" ) func TestAccLexV2ModelsSlot_basic(t *testing.T) { From 69d7e9c484a8aa0162cc236e2ed504d52280e0e4 Mon Sep 17 00:00:00 2001 From: Jared Baker Date: Thu, 14 Dec 2023 16:45:51 -0500 Subject: [PATCH 11/42] r/aws_lexv2models_slot: fix linter findings --- internal/service/lexv2models/slot.go | 1 - internal/service/lexv2models/slot_test.go | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/internal/service/lexv2models/slot.go b/internal/service/lexv2models/slot.go index dc9b6f059fa..4fcf14c3fe3 100644 --- a/internal/service/lexv2models/slot.go +++ b/internal/service/lexv2models/slot.go @@ -184,7 +184,6 @@ func (r *resourceSlot) Update(ctx context.Context, req resource.UpdateRequest, r if !plan.Name.Equal(state.Name) || !plan.Description.Equal(state.Description) || !plan.SlotTypeID.Equal(state.SlotTypeID) { - in := &lexmodelsv2.UpdateSlotInput{ SlotId: aws.String(plan.ID.ValueString()), SlotName: aws.String(plan.Name.ValueString()), diff --git a/internal/service/lexv2models/slot_test.go b/internal/service/lexv2models/slot_test.go index 8a7bc17262b..0752499407a 100644 --- a/internal/service/lexv2models/slot_test.go +++ b/internal/service/lexv2models/slot_test.go @@ -102,7 +102,7 @@ func testAccCheckSlotDestroy(ctx context.Context) resource.TestCheckFunc { return nil } if err != nil { - return nil + return err } return create.Error(names.LexV2Models, create.ErrActionCheckingDestroyed, tflexv2models.ResNameSlot, rs.Primary.ID, errors.New("not destroyed")) From 8d95afb6dc9a8e7a6db71d6169208c39c24ff145 Mon Sep 17 00:00:00 2001 From: Jared Baker Date: Thu, 14 Dec 2023 16:52:53 -0500 Subject: [PATCH 12/42] r/aws_lexv2models_slot(doc): frame out root arguments --- website/docs/r/lexv2models_slot.html.markdown | 41 +++++++++++-------- 1 file changed, 24 insertions(+), 17 deletions(-) diff --git a/website/docs/r/lexv2models_slot.html.markdown b/website/docs/r/lexv2models_slot.html.markdown index 0d421b07052..d606388a93c 100644 --- a/website/docs/r/lexv2models_slot.html.markdown +++ b/website/docs/r/lexv2models_slot.html.markdown @@ -5,14 +5,7 @@ page_title: "AWS: aws_lexv2models_slot" description: |- Terraform resource for managing an AWS Lex V2 Models Slot. --- -` + # Resource: aws_lexv2models_slot Terraform resource for managing an AWS Lex V2 Models Slot. @@ -23,6 +16,11 @@ Terraform resource for managing an AWS Lex V2 Models Slot. ```terraform resource "aws_lexv2models_slot" "example" { + bot_id = aws_lexv2models_bot.example.id + bot_version = aws_lexv2models_bot_version.example.bot_version + intent_id = aws_lexv2models_intent.example.id + locale_id = aws_lexv2models_locale.example.locale_id + name = "example" } ``` @@ -30,30 +28,39 @@ resource "aws_lexv2models_slot" "example" { The following arguments are required: -* `example_arg` - (Required) Concise argument description. Do not begin the description with "An", "The", "Defines", "Indicates", or "Specifies," as these are verbose. In other words, "Indicates the amount of storage," can be rewritten as "Amount of storage," without losing any information. +* `bot_id` - (Required) +* `bot_version` - (Required) +* `intent_id` - (Required) +* `locale_id` - (Required) +* `name` - (Required) +* `value_elicitation_setting` - (Required) The following arguments are optional: -* `optional_arg` - (Optional) Concise argument description. Do not begin the description with "An", "The", "Defines", "Indicates", or "Specifies," as these are verbose. In other words, "Indicates the amount of storage," can be rewritten as "Amount of storage," without losing any information. +* `description` - (Optional) +* `multiple_values_setting` - (Optional) +* `obfuscation_setting` - (Optional) +* `slot_type_id` - (Optional) +* `sub_slot_setting` - (Optional) ## Attribute Reference This resource exports the following attributes in addition to the arguments above: -* `arn` - ARN of the Slot. Do not begin the description with "An", "The", "Defines", "Indicates", or "Specifies," as these are verbose. In other words, "Indicates the amount of storage," can be rewritten as "Amount of storage," without losing any information. -* `example_attribute` - Concise description. Do not begin the description with "An", "The", "Defines", "Indicates", or "Specifies," as these are verbose. In other words, "Indicates the amount of storage," can be rewritten as "Amount of storage," without losing any information. +* `id` - +* `slot_id` - ## Timeouts [Configuration options](https://developer.hashicorp.com/terraform/language/resources/syntax#operation-timeouts): -* `create` - (Default `60m`) -* `update` - (Default `180m`) -* `delete` - (Default `90m`) +* `create` - (Default `30m`) +* `update` - (Default `30m`) +* `delete` - (Default `30m`) ## Import -In Terraform v1.5.0 and later, use an [`import` block](https://developer.hashicorp.com/terraform/language/import) to import Lex V2 Models Slot using the `example_id_arg`. For example: +In Terraform v1.5.0 and later, use an [`import` block](https://developer.hashicorp.com/terraform/language/import) to import Lex V2 Models Slot using the `id`. For example: ```terraform import { @@ -62,7 +69,7 @@ import { } ``` -Using `terraform import`, import Lex V2 Models Slot using the `example_id_arg`. For example: +Using `terraform import`, import Lex V2 Models Slot using the `id`. For example: ```console % terraform import aws_lexv2models_slot.example slot-id-12345678 From d57508317055c636389051c276f0b13ed23bd17a Mon Sep 17 00:00:00 2001 From: Jared Baker Date: Mon, 18 Dec 2023 11:15:22 -0500 Subject: [PATCH 13/42] r/aws_lexv2models_slot: multipart id finder --- internal/service/lexv2models/slot.go | 32 ++++++++++++++++++- website/docs/r/lexv2models_slot.html.markdown | 4 +-- 2 files changed, 33 insertions(+), 3 deletions(-) diff --git a/internal/service/lexv2models/slot.go b/internal/service/lexv2models/slot.go index 4fcf14c3fe3..a2f260191a0 100644 --- a/internal/service/lexv2models/slot.go +++ b/internal/service/lexv2models/slot.go @@ -20,6 +20,7 @@ import ( "github.com/hashicorp/terraform-plugin-framework/types" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/retry" "github.com/hashicorp/terraform-provider-aws/internal/create" + intflex "github.com/hashicorp/terraform-provider-aws/internal/flex" "github.com/hashicorp/terraform-provider-aws/internal/framework" "github.com/hashicorp/terraform-provider-aws/internal/framework/flex" fwtypes "github.com/hashicorp/terraform-provider-aws/internal/framework/types" @@ -41,6 +42,8 @@ func newResourceSlot(_ context.Context) (resource.ResourceWithConfigure, error) const ( ResNameSlot = "Slot" + + slotIDPartCount = 5 ) type resourceSlot struct { @@ -133,6 +136,24 @@ func (r *resourceSlot) Create(ctx context.Context, req resource.CreateRequest, r return } + idParts := []string{ + aws.ToString(out.BotId), + aws.ToString(out.BotVersion), + aws.ToString(out.IntentId), + aws.ToString(out.LocaleId), + aws.ToString(out.SlotId), + } + id, err := intflex.FlattenResourceId(idParts, slotIDPartCount, false) + if err != nil { + resp.Diagnostics.AddError( + create.ProblemStandardMessage(names.LexV2Models, create.ErrActionCreating, ResNameSlot, plan.Name.String(), err), + err.Error(), + ) + return + } + + plan.ID = types.StringValue(id) + resp.Diagnostics.Append(flex.Flatten(ctx, out, &plan)...) if resp.Diagnostics.HasError() { return @@ -252,8 +273,17 @@ func (r *resourceSlot) ImportState(ctx context.Context, req resource.ImportState } func findSlotByID(ctx context.Context, conn *lexmodelsv2.Client, id string) (*lexmodelsv2.DescribeSlotOutput, error) { + parts, err := intflex.ExpandResourceId(id, slotIDPartCount, false) + if err != nil { + return nil, err + } + in := &lexmodelsv2.DescribeSlotInput{ - SlotId: aws.String(id), + BotId: aws.String(parts[0]), + BotVersion: aws.String(parts[1]), + IntentId: aws.String(parts[2]), + LocaleId: aws.String(parts[3]), + SlotId: aws.String(parts[4]), } out, err := conn.DescribeSlot(ctx, in) diff --git a/website/docs/r/lexv2models_slot.html.markdown b/website/docs/r/lexv2models_slot.html.markdown index d606388a93c..98fdea2139d 100644 --- a/website/docs/r/lexv2models_slot.html.markdown +++ b/website/docs/r/lexv2models_slot.html.markdown @@ -47,8 +47,8 @@ The following arguments are optional: This resource exports the following attributes in addition to the arguments above: -* `id` - -* `slot_id` - +* `id` - A comma-delimited string concatenating `bot_id`, `bot_version`, `intent_id`, `locale_id`, and `slot_id`. +* `slot_id` - Unique identifier associated with the slot. ## Timeouts From 67007b4f010b0359db878181621b6279b6a39e22 Mon Sep 17 00:00:00 2001 From: Jared Baker Date: Mon, 18 Dec 2023 15:59:57 -0500 Subject: [PATCH 14/42] r/aws_lexv2models_slot: wait_and_continue_specification schema --- .../lexv2models/schema/message_groups.go | 11 ++- .../schema/value_elicitatation_settings.go | 20 +++-- .../schema/wait_and_continue_specification.go | 87 +++++++++++++++++++ 3 files changed, 106 insertions(+), 12 deletions(-) create mode 100644 internal/service/lexv2models/schema/wait_and_continue_specification.go diff --git a/internal/service/lexv2models/schema/message_groups.go b/internal/service/lexv2models/schema/message_groups.go index df0b9a2ad82..9b98e252e35 100644 --- a/internal/service/lexv2models/schema/message_groups.go +++ b/internal/service/lexv2models/schema/message_groups.go @@ -91,22 +91,27 @@ func MessageGroupsBlock(ctx context.Context) schema.ListNestedBlock { }, }, } + return schema.ListNestedBlock{ Validators: []validator.List{ - listvalidator.SizeAtLeast(1), + listvalidator.IsRequired(), }, CustomType: fwtypes.NewListNestedObjectTypeOf[MessageGroupData](ctx), NestedObject: schema.NestedBlockObject{ Blocks: map[string]schema.Block{ "message": schema.ListNestedBlock{ + CustomType: fwtypes.NewListNestedObjectTypeOf[MessageData](ctx), Validators: []validator.List{ + listvalidator.IsRequired(), listvalidator.SizeBetween(1, 1), }, - CustomType: fwtypes.NewListNestedObjectTypeOf[MessageData](ctx), NestedObject: messageNBO, }, "variations": schema.ListNestedBlock{ - CustomType: fwtypes.NewListNestedObjectTypeOf[MessageData](ctx), + CustomType: fwtypes.NewListNestedObjectTypeOf[MessageData](ctx), + Validators: []validator.List{ + listvalidator.SizeBetween(0, 2), + }, NestedObject: messageNBO, }, }, diff --git a/internal/service/lexv2models/schema/value_elicitatation_settings.go b/internal/service/lexv2models/schema/value_elicitatation_settings.go index 809507de600..b28af104932 100644 --- a/internal/service/lexv2models/schema/value_elicitatation_settings.go +++ b/internal/service/lexv2models/schema/value_elicitatation_settings.go @@ -31,19 +31,21 @@ func ValueElicitationSettingBlock(ctx context.Context) schema.ListNestedBlock { }, }, Blocks: map[string]schema.Block{ - "default_value_specification": DefaultValueSpecificationBlock(ctx), - "slot_resolution_setting": SlotResolutionSettingBlock(ctx), - "prompt_specification": PromptSpecificationBlock(ctx), - "sample_utterance": SampleUtteranceBlock(ctx), + "default_value_specification": DefaultValueSpecificationBlock(ctx), + "prompt_specification": PromptSpecificationBlock(ctx), + "sample_utterance": SampleUtteranceBlock(ctx), + "slot_resolution_setting": SlotResolutionSettingBlock(ctx), + "wait_and_continue_specification": WaitAndContinueSpecificationBlock(ctx), }, }, } } type ValueElicitationSettingData struct { - SlotConstraint fwtypes.StringEnum[awstypes.SlotConstraint] `tfsdk:"slot_constraint"` - DefaultValueSpecification fwtypes.ListNestedObjectValueOf[DefaultValueSpecificationData] `tfsdk:"default_value_specification"` - SlotResolutionSetting fwtypes.ListNestedObjectValueOf[SlotResolutionSettingData] `tfsdk:"slot_resolution_setting"` - PromptSpecification fwtypes.ListNestedObjectValueOf[PromptSpecificationData] `tfsdk:"prompt_specification"` - SampleUtterance fwtypes.ListNestedObjectValueOf[SampleUtteranceData] `tfsdk:"sample_utterance"` + SlotConstraint fwtypes.StringEnum[awstypes.SlotConstraint] `tfsdk:"slot_constraint"` + DefaultValueSpecification fwtypes.ListNestedObjectValueOf[DefaultValueSpecificationData] `tfsdk:"default_value_specification"` + PromptSpecification fwtypes.ListNestedObjectValueOf[PromptSpecificationData] `tfsdk:"prompt_specification"` + SampleUtterance fwtypes.ListNestedObjectValueOf[SampleUtteranceData] `tfsdk:"sample_utterance"` + SlotResolutionSetting fwtypes.ListNestedObjectValueOf[SlotResolutionSettingData] `tfsdk:"slot_resolution_setting"` + WaitAndContinueSpecification fwtypes.ListNestedObjectValueOf[WaitAndContinueSpecificationData] `tfsdk:"wait_and_continue_specification"` } diff --git a/internal/service/lexv2models/schema/wait_and_continue_specification.go b/internal/service/lexv2models/schema/wait_and_continue_specification.go new file mode 100644 index 00000000000..b6ee82c9f38 --- /dev/null +++ b/internal/service/lexv2models/schema/wait_and_continue_specification.go @@ -0,0 +1,87 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package schema + +import ( + "context" + + "github.com/hashicorp/terraform-plugin-framework/provider/schema" + "github.com/hashicorp/terraform-plugin-framework/types" + fwtypes "github.com/hashicorp/terraform-provider-aws/internal/framework/types" +) + +type WaitAndContinueSpecificationData struct { + Active types.Bool `tfsdk:"active"` + ContinueResponse fwtypes.ListNestedObjectValueOf[ResponseSpecificationData] `tfsdk:"continue_response"` + StillWaitingResponse fwtypes.ListNestedObjectValueOf[StillWaitingResponseSpecificationData] `tfsdk:"still_waiting_response"` + WaitingResponse fwtypes.ListNestedObjectValueOf[ResponseSpecificationData] `tfsdk:"waiting_response"` +} + +func WaitAndContinueSpecificationBlock(ctx context.Context) schema.ListNestedBlock { + return schema.ListNestedBlock{ + CustomType: fwtypes.NewListNestedObjectTypeOf[WaitAndContinueSpecificationData](ctx), + NestedObject: schema.NestedBlockObject{ + Attributes: map[string]schema.Attribute{ + "active": schema.BoolAttribute{ + Optional: true, + }, + }, + Blocks: map[string]schema.Block{ + "continue_response": ResponseSpecificationBlock(ctx), + "still_waiting_response": StillWaitingResponseSpecificationBlock(ctx), + "waiting_response": ResponseSpecificationBlock(ctx), + }, + }, + } +} + +type ResponseSpecificationData struct { + AllowInterrupt types.Bool `tfsdk:"allow_interrupt"` + MessageGroups fwtypes.ListNestedObjectValueOf[MessageGroupData] `tfsdk:"message_groups"` +} + +func ResponseSpecificationBlock(ctx context.Context) schema.ListNestedBlock { + return schema.ListNestedBlock{ + CustomType: fwtypes.NewListNestedObjectTypeOf[ResponseSpecificationData](ctx), + NestedObject: schema.NestedBlockObject{ + Attributes: map[string]schema.Attribute{ + "allow_interrupt": schema.BoolAttribute{ + Optional: true, + }, + }, + Blocks: map[string]schema.Block{ + "message_groups": MessageGroupsBlock(ctx), + }, + }, + } +} + +type StillWaitingResponseSpecificationData struct { + AllowInterrupt types.Bool `tfsdk:"allow_interrupt"` + FrequencyInSeconds types.Int64 `tfsdk:"frequency_in_seconds"` + MessageGroups fwtypes.ListNestedObjectValueOf[MessageGroupData] `tfsdk:"message_groups"` + TimeoutInSeconds types.Int64 `tfsdk:"timeout_in_seconds"` +} + +func StillWaitingResponseSpecificationBlock(ctx context.Context) schema.ListNestedBlock { + return schema.ListNestedBlock{ + CustomType: fwtypes.NewListNestedObjectTypeOf[StillWaitingResponseSpecificationData](ctx), + NestedObject: schema.NestedBlockObject{ + Attributes: map[string]schema.Attribute{ + "allow_interrupt": schema.BoolAttribute{ + Optional: true, + }, + "frequency_in_seconds": schema.Int64Attribute{ + Required: true, + }, + "timeout_in_seconds": schema.Int64Attribute{ + Required: true, + }, + }, + Blocks: map[string]schema.Block{ + "message_groups": MessageGroupsBlock(ctx), + }, + }, + } +} From 6d23c94b41fce7fed0630c3365c430c15b3a04dc Mon Sep 17 00:00:00 2001 From: Jared Baker Date: Mon, 18 Dec 2023 16:12:16 -0500 Subject: [PATCH 15/42] r/aws_lexv2models_slot: obfuscation_setting schema --- .../lexv2models/schema/obfuscation_setting.go | 30 +++++++++++++++++++ internal/service/lexv2models/slot.go | 2 ++ 2 files changed, 32 insertions(+) create mode 100644 internal/service/lexv2models/schema/obfuscation_setting.go diff --git a/internal/service/lexv2models/schema/obfuscation_setting.go b/internal/service/lexv2models/schema/obfuscation_setting.go new file mode 100644 index 00000000000..eadf254caeb --- /dev/null +++ b/internal/service/lexv2models/schema/obfuscation_setting.go @@ -0,0 +1,30 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package schema + +import ( + "context" + + awstypes "github.com/aws/aws-sdk-go-v2/service/lexmodelsv2/types" + "github.com/hashicorp/terraform-plugin-framework/provider/schema" + fwtypes "github.com/hashicorp/terraform-provider-aws/internal/framework/types" +) + +func ObfuscationSettingBlock(ctx context.Context) schema.ListNestedBlock { + return schema.ListNestedBlock{ + CustomType: fwtypes.NewListNestedObjectTypeOf[ObfuscationSettingData](ctx), + NestedObject: schema.NestedBlockObject{ + Attributes: map[string]schema.Attribute{ + "obfuscation_setting_type": schema.StringAttribute{ + CustomType: fwtypes.StringEnumType[awstypes.ObfuscationSettingType](), + Required: true, + }, + }, + }, + } +} + +type ObfuscationSettingData struct { + ObfuscationSettingType fwtypes.StringEnum[awstypes.ObfuscationSettingType] `tfsdk:"obfuscation_setting_type"` +} diff --git a/internal/service/lexv2models/slot.go b/internal/service/lexv2models/slot.go index a2f260191a0..bcc269f33eb 100644 --- a/internal/service/lexv2models/slot.go +++ b/internal/service/lexv2models/slot.go @@ -97,6 +97,7 @@ func (r *resourceSlot) Schema(ctx context.Context, req resource.SchemaRequest, r }, }, Blocks: map[string]schema.Block{ + "obfuscation_setting": lexschema.ObfuscationSettingBlock(ctx), "value_elicitation_setting": lexschema.ValueElicitationSettingBlock(ctx), }, } @@ -314,6 +315,7 @@ type resourceSlotData struct { IntentID types.String `tfsdk:"intent_id"` LocaleID types.String `tfsdk:"locale_id"` Name types.String `tfsdk:"name"` + ObfuscationSetting fwtypes.ListNestedObjectValueOf[lexschema.ObfuscationSettingData] `tfsdk:"obfuscation_setting"` Timeouts timeouts.Value `tfsdk:"timeouts"` SlotTypeID types.String `tfsdk:"slot_type_id"` ValueElicitationSettings fwtypes.ListNestedObjectValueOf[lexschema.ValueElicitationSettingData] `tfsdk:"value_elicitation_settings"` From 02e93acd5f4919cd4782c980d621996b4ae42ecd Mon Sep 17 00:00:00 2001 From: Jared Baker Date: Mon, 18 Dec 2023 16:20:18 -0500 Subject: [PATCH 16/42] r/aws_lexv2models_slot: multiple_values_setting schema --- .../schema/multiple_values_setting.go | 29 +++++++++++++++++++ internal/service/lexv2models/slot.go | 2 ++ 2 files changed, 31 insertions(+) create mode 100644 internal/service/lexv2models/schema/multiple_values_setting.go diff --git a/internal/service/lexv2models/schema/multiple_values_setting.go b/internal/service/lexv2models/schema/multiple_values_setting.go new file mode 100644 index 00000000000..1e592f32275 --- /dev/null +++ b/internal/service/lexv2models/schema/multiple_values_setting.go @@ -0,0 +1,29 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package schema + +import ( + "context" + + "github.com/hashicorp/terraform-plugin-framework/provider/schema" + "github.com/hashicorp/terraform-plugin-framework/types" + fwtypes "github.com/hashicorp/terraform-provider-aws/internal/framework/types" +) + +func MultipleValuesSettingBlock(ctx context.Context) schema.ListNestedBlock { + return schema.ListNestedBlock{ + CustomType: fwtypes.NewListNestedObjectTypeOf[ObfuscationSettingData](ctx), + NestedObject: schema.NestedBlockObject{ + Attributes: map[string]schema.Attribute{ + "allow_multiple_values": schema.BoolAttribute{ + Optional: true, + }, + }, + }, + } +} + +type MultipleValuesSettingData struct { + AllowMultipleValues types.Bool `tfsdk:"allow_multiple_values"` +} diff --git a/internal/service/lexv2models/slot.go b/internal/service/lexv2models/slot.go index bcc269f33eb..4ee755aa97a 100644 --- a/internal/service/lexv2models/slot.go +++ b/internal/service/lexv2models/slot.go @@ -97,6 +97,7 @@ func (r *resourceSlot) Schema(ctx context.Context, req resource.SchemaRequest, r }, }, Blocks: map[string]schema.Block{ + "multiple_values_setting": lexschema.MultipleValuesSettingBlock(ctx), "obfuscation_setting": lexschema.ObfuscationSettingBlock(ctx), "value_elicitation_setting": lexschema.ValueElicitationSettingBlock(ctx), }, @@ -314,6 +315,7 @@ type resourceSlotData struct { ID types.String `tfsdk:"id"` IntentID types.String `tfsdk:"intent_id"` LocaleID types.String `tfsdk:"locale_id"` + MultipleValuesSetting fwtypes.ListNestedObjectValueOf[lexschema.MultipleValuesSettingData] `tfsdk:"multiple_values_setting"` Name types.String `tfsdk:"name"` ObfuscationSetting fwtypes.ListNestedObjectValueOf[lexschema.ObfuscationSettingData] `tfsdk:"obfuscation_setting"` Timeouts timeouts.Value `tfsdk:"timeouts"` From c6c15ccf199eedf3597452a0c524bb8421197408 Mon Sep 17 00:00:00 2001 From: Jared Baker Date: Mon, 18 Dec 2023 16:37:36 -0500 Subject: [PATCH 17/42] r/aws_lexv2models_slot(doc): add root argument descriptions, proper import example --- website/docs/r/lexv2models_slot.html.markdown | 34 ++++++++++++------- 1 file changed, 21 insertions(+), 13 deletions(-) diff --git a/website/docs/r/lexv2models_slot.html.markdown b/website/docs/r/lexv2models_slot.html.markdown index 98fdea2139d..4b2638c5814 100644 --- a/website/docs/r/lexv2models_slot.html.markdown +++ b/website/docs/r/lexv2models_slot.html.markdown @@ -28,20 +28,28 @@ resource "aws_lexv2models_slot" "example" { The following arguments are required: -* `bot_id` - (Required) -* `bot_version` - (Required) -* `intent_id` - (Required) -* `locale_id` - (Required) -* `name` - (Required) -* `value_elicitation_setting` - (Required) +* `bot_id` - (Required) Identifier of the bot associated with the slot. +* `bot_version` - (Required) Version of the bot associated with the slot. +* `intent_id` - (Required) Identifier of the intent that contains the slot. +* `locale_id` - (Required) Identifier of the language and locale that the slot will be used in. +* `name` - (Required) Name of the slot. +* `value_elicitation_setting` - (Required) Specifies prompts that Amazon Lex sends to the user to elicit a response that provides the value for the slot. The following arguments are optional: -* `description` - (Optional) -* `multiple_values_setting` - (Optional) -* `obfuscation_setting` - (Optional) -* `slot_type_id` - (Optional) -* `sub_slot_setting` - (Optional) +* `description` - (Optional) Description of the slot. +* `multiple_values_setting` - (Optional) Indicates whether the slot returns multiple values in one response. See the [`multiple_values_setting` argument reference](#multiple_values_setting-argument-reference) below. +* `obfuscation_setting` - (Optional) Determines how slot values are used in Amazon CloudWatch logs. See the [`obfuscation_setting` argument reference](#obfuscation_setting-argument-reference) below. +* `slot_type_id` - (Optional) Unique identifier for the slot type associated with this slot. +* `sub_slot_setting` - (Optional) Specifications for the constituent sub slots and the expression for the composite slot. + +### `multiple_values_setting` Argument Reference + +* `allow_multiple_values` - (Optional) Indicates whether a slot can return multiple values. When `true`, the slot may return more than one value in a response. When `false`, the slot returns only a single value. Multi-value slots are only available in the `en-US` locale. + +### `obfuscation_setting` Argument Reference + +* `obfuscation_setting_type` - (Required) Whether Amazon Lex obscures slot values in conversation logs. Valid values are `DefaultObfuscation` and `None`. ## Attribute Reference @@ -65,12 +73,12 @@ In Terraform v1.5.0 and later, use an [`import` block](https://developer.hashico ```terraform import { to = aws_lexv2models_slot.example - id = "slot-id-12345678" + id = "bot-1234,1,intent-5678,en-US,slot-9012" } ``` Using `terraform import`, import Lex V2 Models Slot using the `id`. For example: ```console -% terraform import aws_lexv2models_slot.example slot-id-12345678 +% terraform import aws_lexv2models_slot.example bot-1234,1,intent-5678,en-US,slot-9012 ``` From dd63b937e3af31756595b869e975ffa5b8ee04a9 Mon Sep 17 00:00:00 2001 From: Sharon Nam Date: Wed, 17 Jan 2024 11:16:51 -0800 Subject: [PATCH 18/42] go mod tidy --- go.mod | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/go.mod b/go.mod index a87ea8fd3d5..bbb7ff99758 100644 --- a/go.mod +++ b/go.mod @@ -115,6 +115,7 @@ require ( github.com/aws/aws-sdk-go-v2/service/wellarchitected v1.27.6 github.com/aws/aws-sdk-go-v2/service/workspaces v1.35.8 github.com/aws/aws-sdk-go-v2/service/xray v1.23.7 + github.com/aws/smithy-go v1.19.0 github.com/beevik/etree v1.3.0 github.com/davecgh/go-spew v1.1.1 github.com/gertd/go-pluralize v0.2.1 @@ -178,7 +179,6 @@ require ( github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.16.10 // indirect github.com/aws/aws-sdk-go-v2/service/sso v1.18.6 // indirect github.com/aws/aws-sdk-go-v2/service/ssooidc v1.21.6 // indirect - github.com/aws/smithy-go v1.19.0 // indirect github.com/bgentry/speakeasy v0.1.0 // indirect github.com/boombuler/barcode v1.0.1 // indirect github.com/bufbuild/protocompile v0.6.0 // indirect From c427e88c9e94e2208e2369db986eaec4a516f9ee Mon Sep 17 00:00:00 2001 From: Sharon Nam Date: Mon, 22 Jan 2024 20:29:08 -0800 Subject: [PATCH 19/42] Add rest of basic schema and test --- .../lexv2models/schema/allowed_input_types.go | 38 ++++++++ .../schema/audio_dtmf_input_specification.go | 44 +++++++++ .../lexv2models/schema/audio_specification.go | 45 ++++++++++ .../schema/default_value_specification.go | 4 + .../lexv2models/schema/dtmf_specification.go | 67 ++++++++++++++ .../schema/prompt_attempts_specification.go | 55 ++++++++++++ .../schema/prompt_specification.go | 48 ++-------- .../schema/text_input_specification.go | 38 ++++++++ internal/service/lexv2models/slot.go | 35 +++++--- internal/service/lexv2models/slot_test.go | 89 +++++++++++++++++-- 10 files changed, 403 insertions(+), 60 deletions(-) create mode 100644 internal/service/lexv2models/schema/allowed_input_types.go create mode 100644 internal/service/lexv2models/schema/audio_dtmf_input_specification.go create mode 100644 internal/service/lexv2models/schema/audio_specification.go create mode 100644 internal/service/lexv2models/schema/dtmf_specification.go create mode 100644 internal/service/lexv2models/schema/prompt_attempts_specification.go create mode 100644 internal/service/lexv2models/schema/text_input_specification.go diff --git a/internal/service/lexv2models/schema/allowed_input_types.go b/internal/service/lexv2models/schema/allowed_input_types.go new file mode 100644 index 00000000000..1f0700d520f --- /dev/null +++ b/internal/service/lexv2models/schema/allowed_input_types.go @@ -0,0 +1,38 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package schema + +import ( + "context" + + "github.com/hashicorp/terraform-plugin-framework-validators/listvalidator" + "github.com/hashicorp/terraform-plugin-framework/provider/schema" + "github.com/hashicorp/terraform-plugin-framework/schema/validator" + "github.com/hashicorp/terraform-plugin-framework/types" + fwtypes "github.com/hashicorp/terraform-provider-aws/internal/framework/types" +) + +func AllowedInputTypesBlock(ctx context.Context) schema.ListNestedBlock { + return schema.ListNestedBlock{ + Validators: []validator.List{ + listvalidator.SizeBetween(1, 1), + }, + CustomType: fwtypes.NewListNestedObjectTypeOf[AllowedInputTypes](ctx), + NestedObject: schema.NestedBlockObject{ + Attributes: map[string]schema.Attribute{ + "allow_audio_input": schema.BoolAttribute{ + Required: true, + }, + "allow_dtmf_input": schema.BoolAttribute{ + Required: true, + }, + }, + }, + } +} + +type AllowedInputTypes struct { + AllowAudioInput types.Bool `tfsdk:"allow_audio_input"` + AllowDTMFInput types.Bool `tfsdk:"allow_dtmf_input"` +} diff --git a/internal/service/lexv2models/schema/audio_dtmf_input_specification.go b/internal/service/lexv2models/schema/audio_dtmf_input_specification.go new file mode 100644 index 00000000000..4e927eafd45 --- /dev/null +++ b/internal/service/lexv2models/schema/audio_dtmf_input_specification.go @@ -0,0 +1,44 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package schema + +import ( + "context" + + "github.com/hashicorp/terraform-plugin-framework-validators/int64validator" + "github.com/hashicorp/terraform-plugin-framework-validators/listvalidator" + "github.com/hashicorp/terraform-plugin-framework/provider/schema" + "github.com/hashicorp/terraform-plugin-framework/schema/validator" + "github.com/hashicorp/terraform-plugin-framework/types" + fwtypes "github.com/hashicorp/terraform-provider-aws/internal/framework/types" +) + +func AudioAndDTMFInputSpecificationBlock(ctx context.Context) schema.ListNestedBlock { + return schema.ListNestedBlock{ + Validators: []validator.List{ + listvalidator.SizeAtMost(1), + }, + CustomType: fwtypes.NewListNestedObjectTypeOf[AudioAndDTMFInputSpecification](ctx), + NestedObject: schema.NestedBlockObject{ + Attributes: map[string]schema.Attribute{ + "start_timeout_ms": schema.Int64Attribute{ + Required: true, + Validators: []validator.Int64{ + int64validator.AtLeast(1), + }, + }, + }, + Blocks: map[string]schema.Block{ + "audio_specification": AudioAndDTMFInputSpecificationBlock(ctx), + "dtmf_specification": DtmfSpecificationBlock(ctx), + }, + }, + } +} + +type AudioAndDTMFInputSpecification struct { + StartTimeoutMs types.Int64 `tfsdk:"start_timeout_ms"` + AudioSpecification fwtypes.ListNestedObjectValueOf[AudioSpecification] `tfsdk:"audio_specification"` + DTMFSpecification fwtypes.ListNestedObjectValueOf[DTMFSpecification] `tfsdk:"dtmf_specification"` +} diff --git a/internal/service/lexv2models/schema/audio_specification.go b/internal/service/lexv2models/schema/audio_specification.go new file mode 100644 index 00000000000..d0981fa5586 --- /dev/null +++ b/internal/service/lexv2models/schema/audio_specification.go @@ -0,0 +1,45 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package schema + +import ( + "context" + + "github.com/hashicorp/terraform-plugin-framework-validators/int64validator" + "github.com/hashicorp/terraform-plugin-framework-validators/listvalidator" + "github.com/hashicorp/terraform-plugin-framework/provider/schema" + "github.com/hashicorp/terraform-plugin-framework/schema/validator" + "github.com/hashicorp/terraform-plugin-framework/types" + fwtypes "github.com/hashicorp/terraform-provider-aws/internal/framework/types" +) + +func AudioSpecificationnBlock(ctx context.Context) schema.ListNestedBlock { + return schema.ListNestedBlock{ + Validators: []validator.List{ + listvalidator.SizeAtMost(1), + }, + CustomType: fwtypes.NewListNestedObjectTypeOf[AudioSpecification](ctx), + NestedObject: schema.NestedBlockObject{ + Attributes: map[string]schema.Attribute{ + "end_timeout_ms": schema.Int64Attribute{ + Required: true, + Validators: []validator.Int64{ + int64validator.AtLeast(1), + }, + }, + "max_length_ms": schema.Int64Attribute{ + Required: true, + Validators: []validator.Int64{ + int64validator.AtLeast(1), + }, + }, + }, + }, + } +} + +type AudioSpecification struct { + EndTimeoutMs types.Int64 `tfsdk:"end_timeout_ms"` + MaxLengthMs types.Int64 `tfsdk:"max_length_ms"` +} diff --git a/internal/service/lexv2models/schema/default_value_specification.go b/internal/service/lexv2models/schema/default_value_specification.go index 3efbbf55318..61e3b88fe56 100644 --- a/internal/service/lexv2models/schema/default_value_specification.go +++ b/internal/service/lexv2models/schema/default_value_specification.go @@ -7,6 +7,7 @@ import ( "context" "github.com/hashicorp/terraform-plugin-framework-validators/listvalidator" + "github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator" "github.com/hashicorp/terraform-plugin-framework/provider/schema" "github.com/hashicorp/terraform-plugin-framework/schema/validator" "github.com/hashicorp/terraform-plugin-framework/types" @@ -27,6 +28,9 @@ func DefaultValueSpecificationBlock(ctx context.Context) schema.ListNestedBlock Attributes: map[string]schema.Attribute{ "default_value": schema.StringAttribute{ Required: true, + Validators: []validator.String{ + stringvalidator.LengthBetween(1, 202), + }, }, }, }, diff --git a/internal/service/lexv2models/schema/dtmf_specification.go b/internal/service/lexv2models/schema/dtmf_specification.go new file mode 100644 index 00000000000..2b974b3150f --- /dev/null +++ b/internal/service/lexv2models/schema/dtmf_specification.go @@ -0,0 +1,67 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package schema + +import ( + "context" + + "github.com/YakDriver/regexache" + "github.com/hashicorp/terraform-plugin-framework-validators/int64validator" + "github.com/hashicorp/terraform-plugin-framework-validators/listvalidator" + "github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator" + "github.com/hashicorp/terraform-plugin-framework/provider/schema" + "github.com/hashicorp/terraform-plugin-framework/schema/validator" + "github.com/hashicorp/terraform-plugin-framework/types" + fwtypes "github.com/hashicorp/terraform-provider-aws/internal/framework/types" +) + +func DtmfSpecificationBlock(ctx context.Context) schema.ListNestedBlock { + return schema.ListNestedBlock{ + Validators: []validator.List{ + listvalidator.SizeAtMost(1), + }, + CustomType: fwtypes.NewListNestedObjectTypeOf[DTMFSpecification](ctx), + NestedObject: schema.NestedBlockObject{ + Attributes: map[string]schema.Attribute{ + "deletion_character": schema.StringAttribute{ + Required: true, + Validators: []validator.String{ + stringvalidator.RegexMatches( + regexache.MustCompile(`^[A-D0-9#*]{1}$`), + "alphanumeric characters", + ), + }, + }, + "end_character": schema.StringAttribute{ + Required: true, + Validators: []validator.String{ + stringvalidator.RegexMatches( + regexache.MustCompile(`^[A-D0-9#*]{1}$`), + "alphanumeric characters", + ), + }, + }, + "end_timeout_ms": schema.Int64Attribute{ + Required: true, + Validators: []validator.Int64{ + int64validator.AtLeast(1), + }, + }, + "max_length": schema.Int64Attribute{ + Required: true, + Validators: []validator.Int64{ + int64validator.Between(1, 1024), + }, + }, + }, + }, + } +} + +type DTMFSpecification struct { + DeletionCharacter types.String `tfsdk:"deletion_character"` + EndCharacter types.String `tfsdk:"end_character"` + EndTimeoutMs types.Int64 `tfsdk:"end_timeout_ms"` + MaxLength types.Int64 `tfsdk:"max_length"` +} diff --git a/internal/service/lexv2models/schema/prompt_attempts_specification.go b/internal/service/lexv2models/schema/prompt_attempts_specification.go new file mode 100644 index 00000000000..10d4f97c479 --- /dev/null +++ b/internal/service/lexv2models/schema/prompt_attempts_specification.go @@ -0,0 +1,55 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package schema + +import ( + "context" + + "github.com/hashicorp/terraform-plugin-framework/provider/schema" + "github.com/hashicorp/terraform-plugin-framework/types" + fwtypes "github.com/hashicorp/terraform-provider-aws/internal/framework/types" +) + +func PromptAttemptsSpecificationBlock(ctx context.Context) schema.ListNestedBlock { + return schema.ListNestedBlock{ + CustomType: fwtypes.NewListNestedObjectTypeOf[PromptAttemptsSpecificationData](ctx), + NestedObject: schema.NestedBlockObject{ + Attributes: map[string]schema.Attribute{ + "map_block_key": schema.StringAttribute{ + Required: true, + CustomType: fwtypes.StringEnumType[PromptAttemptsType](), + }, + "allow_interrupt": schema.BoolAttribute{ + Optional: true, + }, + }, + Blocks: map[string]schema.Block{ + "allowed_input_types": AllowedInputTypesBlock(ctx), + "audio_and_dtmf_input_specification": AudioAndDTMFInputSpecificationBlock(ctx), + "text_input_specification": TextInputSpecificationBlock(ctx), + }, + }, + } +} + +type PromptAttemptsSpecificationData struct { + AllowedInputTypes fwtypes.ListNestedObjectValueOf[AllowedInputTypes] `tfsdk:"allowed_input_types"` + AllowInterrupt types.Bool `tfsdk:"allow_interrupt"` + AudioAndDTMFInputSpecification fwtypes.ListNestedObjectValueOf[AudioAndDTMFInputSpecification] `tfsdk:"audio_and_dtmf_input_specification"` + MapBlockKey fwtypes.StringEnum[PromptAttemptsType] `tfsdk:"map_block_key"` + TextInputSpecification fwtypes.ListNestedObjectValueOf[TextInputSpecification] `tfsdk:"text_input_specification"` +} + +type PromptAttemptsType string + +func (PromptAttemptsType) Values() []PromptAttemptsType { + return []PromptAttemptsType{ + "Initial", + "Retry1", + "Retry2", + "Retry3", + "Retry4", + "Retry5", + } +} diff --git a/internal/service/lexv2models/schema/prompt_specification.go b/internal/service/lexv2models/schema/prompt_specification.go index b3401e0eda4..6d85b866be7 100644 --- a/internal/service/lexv2models/schema/prompt_specification.go +++ b/internal/service/lexv2models/schema/prompt_specification.go @@ -8,7 +8,6 @@ import ( awstypes "github.com/aws/aws-sdk-go-v2/service/lexmodelsv2/types" "github.com/hashicorp/terraform-plugin-framework-validators/listvalidator" - "github.com/hashicorp/terraform-plugin-framework/attr" "github.com/hashicorp/terraform-plugin-framework/provider/schema" "github.com/hashicorp/terraform-plugin-framework/schema/validator" "github.com/hashicorp/terraform-plugin-framework/types" @@ -23,59 +22,22 @@ func PromptSpecificationBlock(ctx context.Context) schema.ListNestedBlock { }, NestedObject: schema.NestedBlockObject{ Attributes: map[string]schema.Attribute{ - "max_retries": schema.Int64Attribute{ - Required: true, - }, "allow_interrupt": schema.BoolAttribute{ Optional: true, }, + "max_retries": schema.Int64Attribute{ + Required: true, + }, "message_selection_strategy": schema.StringAttribute{ Optional: true, Validators: []validator.String{ enum.FrameworkValidate[awstypes.MessageSelectionStrategy](), }, }, - "prompt_attempts_specification": schema.MapAttribute{ - Optional: true, - ElementType: types.ObjectType{ - AttrTypes: map[string]attr.Type{ - "allow_input_types": types.ObjectType{ - AttrTypes: map[string]attr.Type{ - "allow_audio_input": types.BoolType, - "allow_dtmf_input": types.BoolType, - }, - }, - "allow_interrupts": types.BoolType, - "audio_and_dtmf_input_specification": types.ObjectType{ - AttrTypes: map[string]attr.Type{ - "start_timeout_ms": types.Int64Type, - "audio_specification": types.ObjectType{ - AttrTypes: map[string]attr.Type{ - "end_timeout_ms": types.Int64Type, - "max_length_ms": types.Int64Type, - }, - }, - "dtmf_specification": types.ObjectType{ - AttrTypes: map[string]attr.Type{ - "deletion_character": types.StringType, - "end_character": types.StringType, - "end_timeout_ms": types.Int64Type, - "max_length": types.Int64Type, - }, - }, - }, - }, - "text_input_specification": types.ObjectType{ - AttrTypes: map[string]attr.Type{ - "start_timeout_ms": types.Int64Type, - }, - }, - }, - }, - }, }, Blocks: map[string]schema.Block{ - "message_groups": MessageGroupsBlock(ctx), + "message_groups": MessageGroupsBlock(ctx), + "prompt_attempts_specification": PromptAttemptsSpecificationBlock(ctx), }, }, } diff --git a/internal/service/lexv2models/schema/text_input_specification.go b/internal/service/lexv2models/schema/text_input_specification.go new file mode 100644 index 00000000000..01a94ea014e --- /dev/null +++ b/internal/service/lexv2models/schema/text_input_specification.go @@ -0,0 +1,38 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package schema + +import ( + "context" + + "github.com/hashicorp/terraform-plugin-framework-validators/int64validator" + "github.com/hashicorp/terraform-plugin-framework-validators/listvalidator" + "github.com/hashicorp/terraform-plugin-framework/provider/schema" + "github.com/hashicorp/terraform-plugin-framework/schema/validator" + "github.com/hashicorp/terraform-plugin-framework/types" + fwtypes "github.com/hashicorp/terraform-provider-aws/internal/framework/types" +) + +func TextInputSpecificationBlock(ctx context.Context) schema.ListNestedBlock { + return schema.ListNestedBlock{ + Validators: []validator.List{ + listvalidator.SizeAtMost(1), + }, + CustomType: fwtypes.NewListNestedObjectTypeOf[TextInputSpecification](ctx), + NestedObject: schema.NestedBlockObject{ + Attributes: map[string]schema.Attribute{ + "start_timeout_ms": schema.Int64Attribute{ + Required: true, + Validators: []validator.Int64{ + int64validator.AtLeast(1), + }, + }, + }, + }, + } +} + +type TextInputSpecification struct { + StartTimeoutMs types.Int64 `tfsdk:"start_timeout_ms"` +} diff --git a/internal/service/lexv2models/slot.go b/internal/service/lexv2models/slot.go index 4ee755aa97a..c2a672020fc 100644 --- a/internal/service/lexv2models/slot.go +++ b/internal/service/lexv2models/slot.go @@ -100,6 +100,12 @@ func (r *resourceSlot) Schema(ctx context.Context, req resource.SchemaRequest, r "multiple_values_setting": lexschema.MultipleValuesSettingBlock(ctx), "obfuscation_setting": lexschema.ObfuscationSettingBlock(ctx), "value_elicitation_setting": lexschema.ValueElicitationSettingBlock(ctx), + //sub_slot_setting + "timeouts": timeouts.Block(ctx, timeouts.Opts{ + Create: true, + Update: true, + Delete: true, + }), }, } } @@ -204,21 +210,17 @@ func (r *resourceSlot) Update(ctx context.Context, req resource.UpdateRequest, r return } - if !plan.Name.Equal(state.Name) || - !plan.Description.Equal(state.Description) || - !plan.SlotTypeID.Equal(state.SlotTypeID) { - in := &lexmodelsv2.UpdateSlotInput{ - SlotId: aws.String(plan.ID.ValueString()), - SlotName: aws.String(plan.Name.ValueString()), - } + if slotHasChanges(ctx, plan, state) { + input := &lexmodelsv2.UpdateSlotInput{} // TODO: expand here, or check for updatable arguments individually? - resp.Diagnostics.Append(flex.Expand(ctx, plan, &in)...) + + resp.Diagnostics.Append(flex.Expand(context.WithValue(ctx, flex.ResourcePrefix, ResNameSlot), &plan, input)...) if resp.Diagnostics.HasError() { return } - out, err := conn.UpdateSlot(ctx, in) + out, err := conn.UpdateSlot(ctx, input) if err != nil { resp.Diagnostics.AddError( create.ProblemStandardMessage(names.LexV2Models, create.ErrActionUpdating, ResNameSlot, plan.ID.String(), err), @@ -234,7 +236,7 @@ func (r *resourceSlot) Update(ctx context.Context, req resource.UpdateRequest, r return } - resp.Diagnostics.Append(flex.Flatten(ctx, out, &plan)...) + // resp.Diagnostics.Append(flex.Flatten(ctx, out, &plan)...) if resp.Diagnostics.HasError() { return } @@ -253,7 +255,11 @@ func (r *resourceSlot) Delete(ctx context.Context, req resource.DeleteRequest, r } in := &lexmodelsv2.DeleteSlotInput{ - SlotId: aws.String(state.ID.ValueString()), + BotId: aws.String(state.ID.ValueString()), + BotVersion: aws.String(state.ID.ValueString()), + IntentId: aws.String(state.ID.ValueString()), + LocaleId: aws.String(state.ID.ValueString()), + SlotId: aws.String(state.ID.ValueString()), } _, err := conn.DeleteSlot(ctx, in) @@ -322,3 +328,10 @@ type resourceSlotData struct { SlotTypeID types.String `tfsdk:"slot_type_id"` ValueElicitationSettings fwtypes.ListNestedObjectValueOf[lexschema.ValueElicitationSettingData] `tfsdk:"value_elicitation_settings"` } + +func slotHasChanges(_ context.Context, plan, state resourceSlotData) bool { + return !plan.Description.Equal(state.Description) || + !plan.Name.Equal(state.Name) || + !plan.Description.Equal(state.Description) || + !plan.SlotTypeID.Equal(state.SlotTypeID) +} diff --git a/internal/service/lexv2models/slot_test.go b/internal/service/lexv2models/slot_test.go index 0752499407a..14c02024c6e 100644 --- a/internal/service/lexv2models/slot_test.go +++ b/internal/service/lexv2models/slot_test.go @@ -24,18 +24,17 @@ import ( func TestAccLexV2ModelsSlot_basic(t *testing.T) { ctx := acctest.Context(t) - if testing.Short() { - t.Skip("skipping long-running test in short mode") - } var slot lexmodelsv2.DescribeSlotOutput rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resourceName := "aws_lexv2models_slot.test" + botLocaleName := "aws_lexv2models_bot_locale.test" resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { acctest.PreCheck(ctx, t) acctest.PreCheckPartitionHasService(t, names.LexV2ModelsEndpointID) + testAccPreCheck(ctx, t) }, ErrorCheck: acctest.ErrorCheck(t, names.LexV2ModelsEndpointID), ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, @@ -46,6 +45,9 @@ func TestAccLexV2ModelsSlot_basic(t *testing.T) { Check: resource.ComposeTestCheckFunc( testAccCheckSlotExists(ctx, resourceName, &slot), resource.TestCheckResourceAttr(resourceName, "name", rName), + resource.TestCheckResourceAttrPair(resourceName, "bot_id", botLocaleName, "bot_id"), + resource.TestCheckResourceAttrPair(resourceName, "bot_version", botLocaleName, "bot_version"), + resource.TestCheckResourceAttrPair(resourceName, "locale_id", botLocaleName, "locale_id"), ), }, { @@ -136,10 +138,85 @@ func testAccCheckSlotExists(ctx context.Context, name string, slot *lexmodelsv2. } } -func testAccSlotConfig_basic(rName string) string { +func testAccSlotConfig_base(rName string, ttl int, dp bool) string { return fmt.Sprintf(` -resource "aws_lexv2models_slot" "test" { +data "aws_partition" "current" {} + +resource "aws_iam_role" "test" { name = %[1]q + assume_role_policy = jsonencode({ + Version = "2012-10-17" + Statement = [ + { + Action = "sts:AssumeRole" + Effect = "Allow" + Sid = "" + Principal = { + Service = "lexv2.amazonaws.com" + } + }, + ] + }) +} + +resource "aws_iam_role_policy_attachment" "test" { + role = aws_iam_role.test.name + policy_arn = "arn:${data.aws_partition.current.partition}:iam::aws:policy/AmazonLexFullAccess" +} + +resource "aws_lexv2models_bot" "test" { + name = %[1]q + idle_session_ttl_in_seconds = %[2]d + role_arn = aws_iam_role.test.arn + + data_privacy { + child_directed = %[3]t + } +} + +resource "aws_lexv2models_bot_locale" "test" { + locale_id = "en_US" + bot_id = aws_lexv2models_bot.test.id + bot_version = "DRAFT" + n_lu_intent_confidence_threshold = 0.7 +} + +resource "aws_lexv2models_bot_version" "test" { + bot_id = aws_lexv2models_bot.test.id + locale_specification = { + (aws_lexv2models_bot_locale.test.locale_id) = { + source_bot_version = "DRAFT" + } + } +} + +resource "aws_lexv2models_intent" "test" { + bot_id = aws_lexv2models_bot.test.id + bot_version = aws_lexv2models_bot_locale.test.bot_version + name = %[1]q + locale_id = aws_lexv2models_bot_locale.test.locale_id + } +`, rName, ttl, dp) +} + +func testAccSlotConfig_basic(rName string) string { + return acctest.ConfigCompose( + testAccSlotConfig_base(rName, 60, true), + fmt.Sprintf(` +resource "aws_lexv2models_slot" "test" { + bot_id = aws_lexv2models_bot.test.id + bot_version = aws_lexv2models_bot_locale.test.bot_version + intent_id = aws_lexv2models_intent.test.id + name = %[1]q + locale_id = aws_lexv2models_bot_locale.test.locale_id + + value_elicitation_setting { + default_value_specification { + default_value_list { + default_value = "default" + } + } + } } -`, rName) +`, rName)) } From 508f86b46309d54e40e2baa3aafa1425d39d8110 Mon Sep 17 00:00:00 2001 From: Sharon Nam Date: Tue, 23 Jan 2024 16:02:19 -0800 Subject: [PATCH 20/42] Put separate schema files into slot --- .../lexv2models/schema/allowed_input_types.go | 38 -- .../schema/audio_dtmf_input_specification.go | 44 -- .../lexv2models/schema/audio_specification.go | 45 -- .../schema/default_value_specification.go | 49 -- .../lexv2models/schema/dtmf_specification.go | 67 --- .../lexv2models/schema/message_groups.go | 155 ----- .../schema/multiple_values_setting.go | 29 - .../lexv2models/schema/obfuscation_setting.go | 30 - .../schema/prompt_attempts_specification.go | 55 -- .../schema/prompt_specification.go | 86 --- .../lexv2models/schema/sample_utterance..go | 29 - .../schema/slot_resolution_setting.go | 30 - .../schema/text_input_specification.go | 38 -- .../schema/value_elicitatation_settings.go | 51 -- .../schema/wait_and_continue_specification.go | 87 --- internal/service/lexv2models/slot.go | 561 +++++++++++++++++- 16 files changed, 557 insertions(+), 837 deletions(-) delete mode 100644 internal/service/lexv2models/schema/allowed_input_types.go delete mode 100644 internal/service/lexv2models/schema/audio_dtmf_input_specification.go delete mode 100644 internal/service/lexv2models/schema/audio_specification.go delete mode 100644 internal/service/lexv2models/schema/default_value_specification.go delete mode 100644 internal/service/lexv2models/schema/dtmf_specification.go delete mode 100644 internal/service/lexv2models/schema/message_groups.go delete mode 100644 internal/service/lexv2models/schema/multiple_values_setting.go delete mode 100644 internal/service/lexv2models/schema/obfuscation_setting.go delete mode 100644 internal/service/lexv2models/schema/prompt_attempts_specification.go delete mode 100644 internal/service/lexv2models/schema/prompt_specification.go delete mode 100644 internal/service/lexv2models/schema/sample_utterance..go delete mode 100644 internal/service/lexv2models/schema/slot_resolution_setting.go delete mode 100644 internal/service/lexv2models/schema/text_input_specification.go delete mode 100644 internal/service/lexv2models/schema/value_elicitatation_settings.go delete mode 100644 internal/service/lexv2models/schema/wait_and_continue_specification.go diff --git a/internal/service/lexv2models/schema/allowed_input_types.go b/internal/service/lexv2models/schema/allowed_input_types.go deleted file mode 100644 index 1f0700d520f..00000000000 --- a/internal/service/lexv2models/schema/allowed_input_types.go +++ /dev/null @@ -1,38 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - -package schema - -import ( - "context" - - "github.com/hashicorp/terraform-plugin-framework-validators/listvalidator" - "github.com/hashicorp/terraform-plugin-framework/provider/schema" - "github.com/hashicorp/terraform-plugin-framework/schema/validator" - "github.com/hashicorp/terraform-plugin-framework/types" - fwtypes "github.com/hashicorp/terraform-provider-aws/internal/framework/types" -) - -func AllowedInputTypesBlock(ctx context.Context) schema.ListNestedBlock { - return schema.ListNestedBlock{ - Validators: []validator.List{ - listvalidator.SizeBetween(1, 1), - }, - CustomType: fwtypes.NewListNestedObjectTypeOf[AllowedInputTypes](ctx), - NestedObject: schema.NestedBlockObject{ - Attributes: map[string]schema.Attribute{ - "allow_audio_input": schema.BoolAttribute{ - Required: true, - }, - "allow_dtmf_input": schema.BoolAttribute{ - Required: true, - }, - }, - }, - } -} - -type AllowedInputTypes struct { - AllowAudioInput types.Bool `tfsdk:"allow_audio_input"` - AllowDTMFInput types.Bool `tfsdk:"allow_dtmf_input"` -} diff --git a/internal/service/lexv2models/schema/audio_dtmf_input_specification.go b/internal/service/lexv2models/schema/audio_dtmf_input_specification.go deleted file mode 100644 index 4e927eafd45..00000000000 --- a/internal/service/lexv2models/schema/audio_dtmf_input_specification.go +++ /dev/null @@ -1,44 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - -package schema - -import ( - "context" - - "github.com/hashicorp/terraform-plugin-framework-validators/int64validator" - "github.com/hashicorp/terraform-plugin-framework-validators/listvalidator" - "github.com/hashicorp/terraform-plugin-framework/provider/schema" - "github.com/hashicorp/terraform-plugin-framework/schema/validator" - "github.com/hashicorp/terraform-plugin-framework/types" - fwtypes "github.com/hashicorp/terraform-provider-aws/internal/framework/types" -) - -func AudioAndDTMFInputSpecificationBlock(ctx context.Context) schema.ListNestedBlock { - return schema.ListNestedBlock{ - Validators: []validator.List{ - listvalidator.SizeAtMost(1), - }, - CustomType: fwtypes.NewListNestedObjectTypeOf[AudioAndDTMFInputSpecification](ctx), - NestedObject: schema.NestedBlockObject{ - Attributes: map[string]schema.Attribute{ - "start_timeout_ms": schema.Int64Attribute{ - Required: true, - Validators: []validator.Int64{ - int64validator.AtLeast(1), - }, - }, - }, - Blocks: map[string]schema.Block{ - "audio_specification": AudioAndDTMFInputSpecificationBlock(ctx), - "dtmf_specification": DtmfSpecificationBlock(ctx), - }, - }, - } -} - -type AudioAndDTMFInputSpecification struct { - StartTimeoutMs types.Int64 `tfsdk:"start_timeout_ms"` - AudioSpecification fwtypes.ListNestedObjectValueOf[AudioSpecification] `tfsdk:"audio_specification"` - DTMFSpecification fwtypes.ListNestedObjectValueOf[DTMFSpecification] `tfsdk:"dtmf_specification"` -} diff --git a/internal/service/lexv2models/schema/audio_specification.go b/internal/service/lexv2models/schema/audio_specification.go deleted file mode 100644 index d0981fa5586..00000000000 --- a/internal/service/lexv2models/schema/audio_specification.go +++ /dev/null @@ -1,45 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - -package schema - -import ( - "context" - - "github.com/hashicorp/terraform-plugin-framework-validators/int64validator" - "github.com/hashicorp/terraform-plugin-framework-validators/listvalidator" - "github.com/hashicorp/terraform-plugin-framework/provider/schema" - "github.com/hashicorp/terraform-plugin-framework/schema/validator" - "github.com/hashicorp/terraform-plugin-framework/types" - fwtypes "github.com/hashicorp/terraform-provider-aws/internal/framework/types" -) - -func AudioSpecificationnBlock(ctx context.Context) schema.ListNestedBlock { - return schema.ListNestedBlock{ - Validators: []validator.List{ - listvalidator.SizeAtMost(1), - }, - CustomType: fwtypes.NewListNestedObjectTypeOf[AudioSpecification](ctx), - NestedObject: schema.NestedBlockObject{ - Attributes: map[string]schema.Attribute{ - "end_timeout_ms": schema.Int64Attribute{ - Required: true, - Validators: []validator.Int64{ - int64validator.AtLeast(1), - }, - }, - "max_length_ms": schema.Int64Attribute{ - Required: true, - Validators: []validator.Int64{ - int64validator.AtLeast(1), - }, - }, - }, - }, - } -} - -type AudioSpecification struct { - EndTimeoutMs types.Int64 `tfsdk:"end_timeout_ms"` - MaxLengthMs types.Int64 `tfsdk:"max_length_ms"` -} diff --git a/internal/service/lexv2models/schema/default_value_specification.go b/internal/service/lexv2models/schema/default_value_specification.go deleted file mode 100644 index 61e3b88fe56..00000000000 --- a/internal/service/lexv2models/schema/default_value_specification.go +++ /dev/null @@ -1,49 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - -package schema - -import ( - "context" - - "github.com/hashicorp/terraform-plugin-framework-validators/listvalidator" - "github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator" - "github.com/hashicorp/terraform-plugin-framework/provider/schema" - "github.com/hashicorp/terraform-plugin-framework/schema/validator" - "github.com/hashicorp/terraform-plugin-framework/types" - fwtypes "github.com/hashicorp/terraform-provider-aws/internal/framework/types" -) - -func DefaultValueSpecificationBlock(ctx context.Context) schema.ListNestedBlock { - return schema.ListNestedBlock{ - CustomType: fwtypes.NewListNestedObjectTypeOf[DefaultValueSpecificationData](ctx), - NestedObject: schema.NestedBlockObject{ - Blocks: map[string]schema.Block{ - "default_value_list": schema.ListNestedBlock{ - CustomType: fwtypes.NewListNestedObjectTypeOf[DefaultValueData](ctx), - Validators: []validator.List{ - listvalidator.IsRequired(), - }, - NestedObject: schema.NestedBlockObject{ - Attributes: map[string]schema.Attribute{ - "default_value": schema.StringAttribute{ - Required: true, - Validators: []validator.String{ - stringvalidator.LengthBetween(1, 202), - }, - }, - }, - }, - }, - }, - }, - } -} - -type DefaultValueSpecificationData struct { - DefaultValueList fwtypes.ListNestedObjectValueOf[DefaultValueData] `tfsdk:"default_value_list"` -} - -type DefaultValueData struct { - DefaultValue types.String `tfsdk:"default_value"` -} diff --git a/internal/service/lexv2models/schema/dtmf_specification.go b/internal/service/lexv2models/schema/dtmf_specification.go deleted file mode 100644 index 2b974b3150f..00000000000 --- a/internal/service/lexv2models/schema/dtmf_specification.go +++ /dev/null @@ -1,67 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - -package schema - -import ( - "context" - - "github.com/YakDriver/regexache" - "github.com/hashicorp/terraform-plugin-framework-validators/int64validator" - "github.com/hashicorp/terraform-plugin-framework-validators/listvalidator" - "github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator" - "github.com/hashicorp/terraform-plugin-framework/provider/schema" - "github.com/hashicorp/terraform-plugin-framework/schema/validator" - "github.com/hashicorp/terraform-plugin-framework/types" - fwtypes "github.com/hashicorp/terraform-provider-aws/internal/framework/types" -) - -func DtmfSpecificationBlock(ctx context.Context) schema.ListNestedBlock { - return schema.ListNestedBlock{ - Validators: []validator.List{ - listvalidator.SizeAtMost(1), - }, - CustomType: fwtypes.NewListNestedObjectTypeOf[DTMFSpecification](ctx), - NestedObject: schema.NestedBlockObject{ - Attributes: map[string]schema.Attribute{ - "deletion_character": schema.StringAttribute{ - Required: true, - Validators: []validator.String{ - stringvalidator.RegexMatches( - regexache.MustCompile(`^[A-D0-9#*]{1}$`), - "alphanumeric characters", - ), - }, - }, - "end_character": schema.StringAttribute{ - Required: true, - Validators: []validator.String{ - stringvalidator.RegexMatches( - regexache.MustCompile(`^[A-D0-9#*]{1}$`), - "alphanumeric characters", - ), - }, - }, - "end_timeout_ms": schema.Int64Attribute{ - Required: true, - Validators: []validator.Int64{ - int64validator.AtLeast(1), - }, - }, - "max_length": schema.Int64Attribute{ - Required: true, - Validators: []validator.Int64{ - int64validator.Between(1, 1024), - }, - }, - }, - }, - } -} - -type DTMFSpecification struct { - DeletionCharacter types.String `tfsdk:"deletion_character"` - EndCharacter types.String `tfsdk:"end_character"` - EndTimeoutMs types.Int64 `tfsdk:"end_timeout_ms"` - MaxLength types.Int64 `tfsdk:"max_length"` -} diff --git a/internal/service/lexv2models/schema/message_groups.go b/internal/service/lexv2models/schema/message_groups.go deleted file mode 100644 index 9b98e252e35..00000000000 --- a/internal/service/lexv2models/schema/message_groups.go +++ /dev/null @@ -1,155 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - -package schema - -import ( - "context" - - "github.com/hashicorp/terraform-plugin-framework-validators/listvalidator" - "github.com/hashicorp/terraform-plugin-framework/provider/schema" - "github.com/hashicorp/terraform-plugin-framework/schema/validator" - "github.com/hashicorp/terraform-plugin-framework/types" - fwtypes "github.com/hashicorp/terraform-provider-aws/internal/framework/types" -) - -func MessageGroupsBlock(ctx context.Context) schema.ListNestedBlock { - messageNBO := schema.NestedBlockObject{ - Blocks: map[string]schema.Block{ - "custom_playload": schema.ListNestedBlock{ - Validators: []validator.List{ - listvalidator.SizeAtMost(1), - }, - CustomType: fwtypes.NewListNestedObjectTypeOf[CustomPayloadData](ctx), - NestedObject: schema.NestedBlockObject{ - Attributes: map[string]schema.Attribute{ - "value": schema.StringAttribute{ - Required: true, - }, - }, - }, - }, - "image_response_card": schema.ListNestedBlock{ - Validators: []validator.List{ - listvalidator.SizeAtMost(1), - }, - CustomType: fwtypes.NewListNestedObjectTypeOf[ImageResponseCardData](ctx), - NestedObject: schema.NestedBlockObject{ - Attributes: map[string]schema.Attribute{ - "image_url": schema.StringAttribute{ - Optional: true, - }, - "subtitle": schema.StringAttribute{ - Optional: true, - }, - "title": schema.StringAttribute{ - Required: true, - }, - }, - Blocks: map[string]schema.Block{ - "button": schema.ListNestedBlock{ - CustomType: fwtypes.NewListNestedObjectTypeOf[ButtonData](ctx), - NestedObject: schema.NestedBlockObject{ - Attributes: map[string]schema.Attribute{ - "text": schema.StringAttribute{ - Required: true, - }, - "value": schema.StringAttribute{ - Required: true, - }, - }, - }, - }, - }, - }, - }, - "plain_text_message": schema.ListNestedBlock{ - Validators: []validator.List{ - listvalidator.SizeAtMost(1), - }, - CustomType: fwtypes.NewListNestedObjectTypeOf[PlainTextMessageData](ctx), - NestedObject: schema.NestedBlockObject{ - Attributes: map[string]schema.Attribute{ - "value": schema.StringAttribute{ - Required: true, - }, - }, - }, - }, - "ssml_message": schema.ListNestedBlock{ - Validators: []validator.List{ - listvalidator.SizeAtMost(1), - }, - CustomType: fwtypes.NewListNestedObjectTypeOf[SSMLMessageData](ctx), - NestedObject: schema.NestedBlockObject{ - Attributes: map[string]schema.Attribute{ - "value": schema.StringAttribute{ - Required: true, - }, - }, - }, - }, - }, - } - - return schema.ListNestedBlock{ - Validators: []validator.List{ - listvalidator.IsRequired(), - }, - CustomType: fwtypes.NewListNestedObjectTypeOf[MessageGroupData](ctx), - NestedObject: schema.NestedBlockObject{ - Blocks: map[string]schema.Block{ - "message": schema.ListNestedBlock{ - CustomType: fwtypes.NewListNestedObjectTypeOf[MessageData](ctx), - Validators: []validator.List{ - listvalidator.IsRequired(), - listvalidator.SizeBetween(1, 1), - }, - NestedObject: messageNBO, - }, - "variations": schema.ListNestedBlock{ - CustomType: fwtypes.NewListNestedObjectTypeOf[MessageData](ctx), - Validators: []validator.List{ - listvalidator.SizeBetween(0, 2), - }, - NestedObject: messageNBO, - }, - }, - }, - } -} - -type CustomPayloadData struct { - Value types.String `tfsdk:"value"` -} - -type ImageResponseCardData struct { - Title types.String `tfsdk:"title"` - Button fwtypes.ListNestedObjectValueOf[ButtonData] `tfsdk:"buttons"` - ImageURL types.String `tfsdk:"image_url"` - Subtitle types.String `tfsdk:"subtitle"` -} - -type ButtonData struct { - Text types.String `tfsdk:"text"` - Value types.String `tfsdk:"value"` -} - -type PlainTextMessageData struct { - Value types.String `tfsdk:"value"` -} - -type SSMLMessageData struct { - Value types.String `tfsdk:"value"` -} -type MessageGroupData struct { - Message fwtypes.ListNestedObjectValueOf[MessageData] `tfsdk:"message"` - Variations fwtypes.ListNestedObjectValueOf[MessageData] `tfsdk:"variations"` -} - -type MessageData struct { - CustomPayload fwtypes.ListNestedObjectValueOf[CustomPayloadData] `tfsdk:"custom_payload"` - ImageResponseCard fwtypes.ListNestedObjectValueOf[ImageResponseCardData] `tfsdk:"image_response_card"` - PlainTextMessage fwtypes.ListNestedObjectValueOf[PlainTextMessageData] `tfsdk:"plain_text_message"` - SSMLMessage fwtypes.ListNestedObjectValueOf[SSMLMessageData] `tfsdk:"ssml_message"` -} diff --git a/internal/service/lexv2models/schema/multiple_values_setting.go b/internal/service/lexv2models/schema/multiple_values_setting.go deleted file mode 100644 index 1e592f32275..00000000000 --- a/internal/service/lexv2models/schema/multiple_values_setting.go +++ /dev/null @@ -1,29 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - -package schema - -import ( - "context" - - "github.com/hashicorp/terraform-plugin-framework/provider/schema" - "github.com/hashicorp/terraform-plugin-framework/types" - fwtypes "github.com/hashicorp/terraform-provider-aws/internal/framework/types" -) - -func MultipleValuesSettingBlock(ctx context.Context) schema.ListNestedBlock { - return schema.ListNestedBlock{ - CustomType: fwtypes.NewListNestedObjectTypeOf[ObfuscationSettingData](ctx), - NestedObject: schema.NestedBlockObject{ - Attributes: map[string]schema.Attribute{ - "allow_multiple_values": schema.BoolAttribute{ - Optional: true, - }, - }, - }, - } -} - -type MultipleValuesSettingData struct { - AllowMultipleValues types.Bool `tfsdk:"allow_multiple_values"` -} diff --git a/internal/service/lexv2models/schema/obfuscation_setting.go b/internal/service/lexv2models/schema/obfuscation_setting.go deleted file mode 100644 index eadf254caeb..00000000000 --- a/internal/service/lexv2models/schema/obfuscation_setting.go +++ /dev/null @@ -1,30 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - -package schema - -import ( - "context" - - awstypes "github.com/aws/aws-sdk-go-v2/service/lexmodelsv2/types" - "github.com/hashicorp/terraform-plugin-framework/provider/schema" - fwtypes "github.com/hashicorp/terraform-provider-aws/internal/framework/types" -) - -func ObfuscationSettingBlock(ctx context.Context) schema.ListNestedBlock { - return schema.ListNestedBlock{ - CustomType: fwtypes.NewListNestedObjectTypeOf[ObfuscationSettingData](ctx), - NestedObject: schema.NestedBlockObject{ - Attributes: map[string]schema.Attribute{ - "obfuscation_setting_type": schema.StringAttribute{ - CustomType: fwtypes.StringEnumType[awstypes.ObfuscationSettingType](), - Required: true, - }, - }, - }, - } -} - -type ObfuscationSettingData struct { - ObfuscationSettingType fwtypes.StringEnum[awstypes.ObfuscationSettingType] `tfsdk:"obfuscation_setting_type"` -} diff --git a/internal/service/lexv2models/schema/prompt_attempts_specification.go b/internal/service/lexv2models/schema/prompt_attempts_specification.go deleted file mode 100644 index 10d4f97c479..00000000000 --- a/internal/service/lexv2models/schema/prompt_attempts_specification.go +++ /dev/null @@ -1,55 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - -package schema - -import ( - "context" - - "github.com/hashicorp/terraform-plugin-framework/provider/schema" - "github.com/hashicorp/terraform-plugin-framework/types" - fwtypes "github.com/hashicorp/terraform-provider-aws/internal/framework/types" -) - -func PromptAttemptsSpecificationBlock(ctx context.Context) schema.ListNestedBlock { - return schema.ListNestedBlock{ - CustomType: fwtypes.NewListNestedObjectTypeOf[PromptAttemptsSpecificationData](ctx), - NestedObject: schema.NestedBlockObject{ - Attributes: map[string]schema.Attribute{ - "map_block_key": schema.StringAttribute{ - Required: true, - CustomType: fwtypes.StringEnumType[PromptAttemptsType](), - }, - "allow_interrupt": schema.BoolAttribute{ - Optional: true, - }, - }, - Blocks: map[string]schema.Block{ - "allowed_input_types": AllowedInputTypesBlock(ctx), - "audio_and_dtmf_input_specification": AudioAndDTMFInputSpecificationBlock(ctx), - "text_input_specification": TextInputSpecificationBlock(ctx), - }, - }, - } -} - -type PromptAttemptsSpecificationData struct { - AllowedInputTypes fwtypes.ListNestedObjectValueOf[AllowedInputTypes] `tfsdk:"allowed_input_types"` - AllowInterrupt types.Bool `tfsdk:"allow_interrupt"` - AudioAndDTMFInputSpecification fwtypes.ListNestedObjectValueOf[AudioAndDTMFInputSpecification] `tfsdk:"audio_and_dtmf_input_specification"` - MapBlockKey fwtypes.StringEnum[PromptAttemptsType] `tfsdk:"map_block_key"` - TextInputSpecification fwtypes.ListNestedObjectValueOf[TextInputSpecification] `tfsdk:"text_input_specification"` -} - -type PromptAttemptsType string - -func (PromptAttemptsType) Values() []PromptAttemptsType { - return []PromptAttemptsType{ - "Initial", - "Retry1", - "Retry2", - "Retry3", - "Retry4", - "Retry5", - } -} diff --git a/internal/service/lexv2models/schema/prompt_specification.go b/internal/service/lexv2models/schema/prompt_specification.go deleted file mode 100644 index 6d85b866be7..00000000000 --- a/internal/service/lexv2models/schema/prompt_specification.go +++ /dev/null @@ -1,86 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - -package schema - -import ( - "context" - - awstypes "github.com/aws/aws-sdk-go-v2/service/lexmodelsv2/types" - "github.com/hashicorp/terraform-plugin-framework-validators/listvalidator" - "github.com/hashicorp/terraform-plugin-framework/provider/schema" - "github.com/hashicorp/terraform-plugin-framework/schema/validator" - "github.com/hashicorp/terraform-plugin-framework/types" - "github.com/hashicorp/terraform-provider-aws/internal/enum" - fwtypes "github.com/hashicorp/terraform-provider-aws/internal/framework/types" -) - -func PromptSpecificationBlock(ctx context.Context) schema.ListNestedBlock { - return schema.ListNestedBlock{ - Validators: []validator.List{ - listvalidator.SizeBetween(1, 1), - }, - NestedObject: schema.NestedBlockObject{ - Attributes: map[string]schema.Attribute{ - "allow_interrupt": schema.BoolAttribute{ - Optional: true, - }, - "max_retries": schema.Int64Attribute{ - Required: true, - }, - "message_selection_strategy": schema.StringAttribute{ - Optional: true, - Validators: []validator.String{ - enum.FrameworkValidate[awstypes.MessageSelectionStrategy](), - }, - }, - }, - Blocks: map[string]schema.Block{ - "message_groups": MessageGroupsBlock(ctx), - "prompt_attempts_specification": PromptAttemptsSpecificationBlock(ctx), - }, - }, - } -} - -type PromptSpecificationData struct { - AllowInterrupt types.Bool `tfsdk:"allow_interrupt"` - MaxRetries types.Int64 `tfsdk:"max_retries"` - MessageGroup fwtypes.ListNestedObjectValueOf[MessageGroupData] `tfsdk:"message_groups"` - MessageSelectionStrategy fwtypes.StringEnum[awstypes.MessageSelectionStrategy] `tfsdk:"message_selection_strategy"` - PromptAttemptsSpecification fwtypes.ObjectMapValueOf[PromptAttemptSpecificationData] `tfsdk:"prompt_attempts_specification"` -} - -type PromptAttemptSpecificationData struct { - AllowedInputTypes fwtypes.ListNestedObjectValueOf[AllowedInputTypesData] `tfsdk:"allowed_input_types"` - AllowInterrupt types.Bool `tfsdk:"allow_interrupt"` - AudioAndDTMFInputSpecification fwtypes.ListNestedObjectValueOf[AudioAndDTMFInputSpecificationData] `tfsdk:"audio_and_dtmf_input_specification"` - TextInputSpecification fwtypes.ListNestedObjectValueOf[TextInputSpecificationData] `tfsdk:"text_input_specification"` -} - -type DTMFSpecificationData struct { - EndCharacter types.String `tfsdk:"end_character"` - EndTimeoutMs types.Int64 `tfsdk:"end_timeout_ms"` - DeletionCharacter types.String `tfsdk:"deletion_character"` - MaxLength types.Int64 `tfsdk:"max_length"` -} - -type TextInputSpecificationData struct { - StartTimeoutMs types.Int64 `tfsdk:"start_timeout_ms"` -} - -type AllowedInputTypesData struct { - AllowAudioInput types.Bool `tfsdk:"allow_audio_input"` - AllowDTMFInput types.Bool `tfsdk:"allow_dtmf_input"` -} - -type AudioAndDTMFInputSpecificationData struct { - AudioSpecification fwtypes.ListNestedObjectValueOf[AudioSpecificationData] `tfsdk:"audio_specification"` - StartTimeoutMs types.Int64 `tfsdk:"start_timeout_ms"` - DTMFSpecification fwtypes.ListNestedObjectValueOf[DTMFSpecificationData] `tfsdk:"dtmf_specification"` -} - -type AudioSpecificationData struct { - EndTimeoutMs types.Int64 `tfsdk:"end_timeout_ms"` - MaxLengthMs types.Int64 `tfsdk:"max_length_ms"` -} diff --git a/internal/service/lexv2models/schema/sample_utterance..go b/internal/service/lexv2models/schema/sample_utterance..go deleted file mode 100644 index 8d55fb1e246..00000000000 --- a/internal/service/lexv2models/schema/sample_utterance..go +++ /dev/null @@ -1,29 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - -package schema - -import ( - "context" - - "github.com/hashicorp/terraform-plugin-framework/provider/schema" - "github.com/hashicorp/terraform-plugin-framework/types" - fwtypes "github.com/hashicorp/terraform-provider-aws/internal/framework/types" -) - -func SampleUtteranceBlock(ctx context.Context) schema.ListNestedBlock { - return schema.ListNestedBlock{ - CustomType: fwtypes.NewListNestedObjectTypeOf[SampleUtteranceData](ctx), - NestedObject: schema.NestedBlockObject{ - Attributes: map[string]schema.Attribute{ - "utterance": schema.StringAttribute{ - Required: true, - }, - }, - }, - } -} - -type SampleUtteranceData struct { - Utterance types.String `tfsdk:"utterance"` -} diff --git a/internal/service/lexv2models/schema/slot_resolution_setting.go b/internal/service/lexv2models/schema/slot_resolution_setting.go deleted file mode 100644 index 3b6022bea12..00000000000 --- a/internal/service/lexv2models/schema/slot_resolution_setting.go +++ /dev/null @@ -1,30 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - -package schema - -import ( - "context" - - awstypes "github.com/aws/aws-sdk-go-v2/service/lexmodelsv2/types" - "github.com/hashicorp/terraform-plugin-framework/provider/schema" - fwtypes "github.com/hashicorp/terraform-provider-aws/internal/framework/types" -) - -type SlotResolutionSettingData struct { - SlotResolutionStrategy fwtypes.StringEnum[awstypes.SlotResolutionStrategy] `tfsdk:"slot_resolution_strategy"` -} - -func SlotResolutionSettingBlock(ctx context.Context) schema.ListNestedBlock { - return schema.ListNestedBlock{ - CustomType: fwtypes.NewListNestedObjectTypeOf[SlotResolutionSettingData](ctx), - NestedObject: schema.NestedBlockObject{ - Attributes: map[string]schema.Attribute{ - "slot_resolution_strategy": schema.StringAttribute{ - CustomType: fwtypes.StringEnumType[awstypes.SlotResolutionStrategy](), - Required: true, - }, - }, - }, - } -} diff --git a/internal/service/lexv2models/schema/text_input_specification.go b/internal/service/lexv2models/schema/text_input_specification.go deleted file mode 100644 index 01a94ea014e..00000000000 --- a/internal/service/lexv2models/schema/text_input_specification.go +++ /dev/null @@ -1,38 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - -package schema - -import ( - "context" - - "github.com/hashicorp/terraform-plugin-framework-validators/int64validator" - "github.com/hashicorp/terraform-plugin-framework-validators/listvalidator" - "github.com/hashicorp/terraform-plugin-framework/provider/schema" - "github.com/hashicorp/terraform-plugin-framework/schema/validator" - "github.com/hashicorp/terraform-plugin-framework/types" - fwtypes "github.com/hashicorp/terraform-provider-aws/internal/framework/types" -) - -func TextInputSpecificationBlock(ctx context.Context) schema.ListNestedBlock { - return schema.ListNestedBlock{ - Validators: []validator.List{ - listvalidator.SizeAtMost(1), - }, - CustomType: fwtypes.NewListNestedObjectTypeOf[TextInputSpecification](ctx), - NestedObject: schema.NestedBlockObject{ - Attributes: map[string]schema.Attribute{ - "start_timeout_ms": schema.Int64Attribute{ - Required: true, - Validators: []validator.Int64{ - int64validator.AtLeast(1), - }, - }, - }, - }, - } -} - -type TextInputSpecification struct { - StartTimeoutMs types.Int64 `tfsdk:"start_timeout_ms"` -} diff --git a/internal/service/lexv2models/schema/value_elicitatation_settings.go b/internal/service/lexv2models/schema/value_elicitatation_settings.go deleted file mode 100644 index b28af104932..00000000000 --- a/internal/service/lexv2models/schema/value_elicitatation_settings.go +++ /dev/null @@ -1,51 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - -package schema - -import ( - "context" - - awstypes "github.com/aws/aws-sdk-go-v2/service/lexmodelsv2/types" - "github.com/hashicorp/terraform-plugin-framework-validators/listvalidator" - "github.com/hashicorp/terraform-plugin-framework/provider/schema" - "github.com/hashicorp/terraform-plugin-framework/schema/validator" - "github.com/hashicorp/terraform-provider-aws/internal/enum" - fwtypes "github.com/hashicorp/terraform-provider-aws/internal/framework/types" -) - -func ValueElicitationSettingBlock(ctx context.Context) schema.ListNestedBlock { - return schema.ListNestedBlock{ - CustomType: fwtypes.NewListNestedObjectTypeOf[ValueElicitationSettingData](ctx), - Validators: []validator.List{ - listvalidator.IsRequired(), - listvalidator.SizeAtMost(1), - }, - NestedObject: schema.NestedBlockObject{ - Attributes: map[string]schema.Attribute{ - "slot_constraint": schema.StringAttribute{ - Required: true, - Validators: []validator.String{ - enum.FrameworkValidate[awstypes.SlotConstraint](), - }, - }, - }, - Blocks: map[string]schema.Block{ - "default_value_specification": DefaultValueSpecificationBlock(ctx), - "prompt_specification": PromptSpecificationBlock(ctx), - "sample_utterance": SampleUtteranceBlock(ctx), - "slot_resolution_setting": SlotResolutionSettingBlock(ctx), - "wait_and_continue_specification": WaitAndContinueSpecificationBlock(ctx), - }, - }, - } -} - -type ValueElicitationSettingData struct { - SlotConstraint fwtypes.StringEnum[awstypes.SlotConstraint] `tfsdk:"slot_constraint"` - DefaultValueSpecification fwtypes.ListNestedObjectValueOf[DefaultValueSpecificationData] `tfsdk:"default_value_specification"` - PromptSpecification fwtypes.ListNestedObjectValueOf[PromptSpecificationData] `tfsdk:"prompt_specification"` - SampleUtterance fwtypes.ListNestedObjectValueOf[SampleUtteranceData] `tfsdk:"sample_utterance"` - SlotResolutionSetting fwtypes.ListNestedObjectValueOf[SlotResolutionSettingData] `tfsdk:"slot_resolution_setting"` - WaitAndContinueSpecification fwtypes.ListNestedObjectValueOf[WaitAndContinueSpecificationData] `tfsdk:"wait_and_continue_specification"` -} diff --git a/internal/service/lexv2models/schema/wait_and_continue_specification.go b/internal/service/lexv2models/schema/wait_and_continue_specification.go deleted file mode 100644 index b6ee82c9f38..00000000000 --- a/internal/service/lexv2models/schema/wait_and_continue_specification.go +++ /dev/null @@ -1,87 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - -package schema - -import ( - "context" - - "github.com/hashicorp/terraform-plugin-framework/provider/schema" - "github.com/hashicorp/terraform-plugin-framework/types" - fwtypes "github.com/hashicorp/terraform-provider-aws/internal/framework/types" -) - -type WaitAndContinueSpecificationData struct { - Active types.Bool `tfsdk:"active"` - ContinueResponse fwtypes.ListNestedObjectValueOf[ResponseSpecificationData] `tfsdk:"continue_response"` - StillWaitingResponse fwtypes.ListNestedObjectValueOf[StillWaitingResponseSpecificationData] `tfsdk:"still_waiting_response"` - WaitingResponse fwtypes.ListNestedObjectValueOf[ResponseSpecificationData] `tfsdk:"waiting_response"` -} - -func WaitAndContinueSpecificationBlock(ctx context.Context) schema.ListNestedBlock { - return schema.ListNestedBlock{ - CustomType: fwtypes.NewListNestedObjectTypeOf[WaitAndContinueSpecificationData](ctx), - NestedObject: schema.NestedBlockObject{ - Attributes: map[string]schema.Attribute{ - "active": schema.BoolAttribute{ - Optional: true, - }, - }, - Blocks: map[string]schema.Block{ - "continue_response": ResponseSpecificationBlock(ctx), - "still_waiting_response": StillWaitingResponseSpecificationBlock(ctx), - "waiting_response": ResponseSpecificationBlock(ctx), - }, - }, - } -} - -type ResponseSpecificationData struct { - AllowInterrupt types.Bool `tfsdk:"allow_interrupt"` - MessageGroups fwtypes.ListNestedObjectValueOf[MessageGroupData] `tfsdk:"message_groups"` -} - -func ResponseSpecificationBlock(ctx context.Context) schema.ListNestedBlock { - return schema.ListNestedBlock{ - CustomType: fwtypes.NewListNestedObjectTypeOf[ResponseSpecificationData](ctx), - NestedObject: schema.NestedBlockObject{ - Attributes: map[string]schema.Attribute{ - "allow_interrupt": schema.BoolAttribute{ - Optional: true, - }, - }, - Blocks: map[string]schema.Block{ - "message_groups": MessageGroupsBlock(ctx), - }, - }, - } -} - -type StillWaitingResponseSpecificationData struct { - AllowInterrupt types.Bool `tfsdk:"allow_interrupt"` - FrequencyInSeconds types.Int64 `tfsdk:"frequency_in_seconds"` - MessageGroups fwtypes.ListNestedObjectValueOf[MessageGroupData] `tfsdk:"message_groups"` - TimeoutInSeconds types.Int64 `tfsdk:"timeout_in_seconds"` -} - -func StillWaitingResponseSpecificationBlock(ctx context.Context) schema.ListNestedBlock { - return schema.ListNestedBlock{ - CustomType: fwtypes.NewListNestedObjectTypeOf[StillWaitingResponseSpecificationData](ctx), - NestedObject: schema.NestedBlockObject{ - Attributes: map[string]schema.Attribute{ - "allow_interrupt": schema.BoolAttribute{ - Optional: true, - }, - "frequency_in_seconds": schema.Int64Attribute{ - Required: true, - }, - "timeout_in_seconds": schema.Int64Attribute{ - Required: true, - }, - }, - Blocks: map[string]schema.Block{ - "message_groups": MessageGroupsBlock(ctx), - }, - }, - } -} diff --git a/internal/service/lexv2models/slot.go b/internal/service/lexv2models/slot.go index c2a672020fc..507b505599c 100644 --- a/internal/service/lexv2models/slot.go +++ b/internal/service/lexv2models/slot.go @@ -8,18 +8,24 @@ import ( "errors" "time" + "github.com/YakDriver/regexache" "github.com/aws/aws-sdk-go-v2/aws" "github.com/aws/aws-sdk-go-v2/service/lexmodelsv2" awstypes "github.com/aws/aws-sdk-go-v2/service/lexmodelsv2/types" "github.com/hashicorp/terraform-plugin-framework-timeouts/resource/timeouts" + "github.com/hashicorp/terraform-plugin-framework-validators/int64validator" + "github.com/hashicorp/terraform-plugin-framework-validators/listvalidator" + "github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator" "github.com/hashicorp/terraform-plugin-framework/path" "github.com/hashicorp/terraform-plugin-framework/resource" "github.com/hashicorp/terraform-plugin-framework/resource/schema" "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" + "github.com/hashicorp/terraform-plugin-framework/schema/validator" "github.com/hashicorp/terraform-plugin-framework/types" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/retry" "github.com/hashicorp/terraform-provider-aws/internal/create" + "github.com/hashicorp/terraform-provider-aws/internal/enum" intflex "github.com/hashicorp/terraform-provider-aws/internal/flex" "github.com/hashicorp/terraform-provider-aws/internal/framework" "github.com/hashicorp/terraform-provider-aws/internal/framework/flex" @@ -56,6 +62,416 @@ func (r *resourceSlot) Metadata(_ context.Context, req resource.MetadataRequest, } func (r *resourceSlot) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { + multValueSettingsLNB := schema.ListNestedBlock{ + CustomType: fwtypes.NewListNestedObjectTypeOf[MultipleValuesSettingData](ctx), + NestedObject: schema.NestedBlockObject{ + Attributes: map[string]schema.Attribute{ + "allow_multiple_values": schema.BoolAttribute{ + Optional: true, + }, + }, + }, + } + + obfuscationSettingLNB := schema.ListNestedBlock{ + CustomType: fwtypes.NewListNestedObjectTypeOf[ObfuscationSettingData](ctx), + NestedObject: schema.NestedBlockObject{ + Attributes: map[string]schema.Attribute{ + "obfuscation_setting_type": schema.StringAttribute{ + CustomType: fwtypes.StringEnumType[awstypes.ObfuscationSettingType](), + Required: true, + }, + }, + }, + } + + defaultValueSpecificationLNB := schema.ListNestedBlock{ + CustomType: fwtypes.NewListNestedObjectTypeOf[DefaultValueSpecificationData](ctx), + NestedObject: schema.NestedBlockObject{ + Blocks: map[string]schema.Block{ + "default_value_list": schema.ListNestedBlock{ + CustomType: fwtypes.NewListNestedObjectTypeOf[DefaultValueData](ctx), + Validators: []validator.List{ + listvalidator.IsRequired(), + }, + NestedObject: schema.NestedBlockObject{ + Attributes: map[string]schema.Attribute{ + "default_value": schema.StringAttribute{ + Required: true, + Validators: []validator.String{ + stringvalidator.LengthBetween(1, 202), + }, + }, + }, + }, + }, + }, + }, + } + + messageNBO := schema.NestedBlockObject{ + Blocks: map[string]schema.Block{ + "custom_playload": schema.ListNestedBlock{ + Validators: []validator.List{ + listvalidator.SizeAtMost(1), + }, + CustomType: fwtypes.NewListNestedObjectTypeOf[CustomPayloadData](ctx), + NestedObject: schema.NestedBlockObject{ + Attributes: map[string]schema.Attribute{ + "value": schema.StringAttribute{ + Required: true, + }, + }, + }, + }, + "image_response_card": schema.ListNestedBlock{ + Validators: []validator.List{ + listvalidator.SizeAtMost(1), + }, + CustomType: fwtypes.NewListNestedObjectTypeOf[ImageResponseCardData](ctx), + NestedObject: schema.NestedBlockObject{ + Attributes: map[string]schema.Attribute{ + "image_url": schema.StringAttribute{ + Optional: true, + }, + "subtitle": schema.StringAttribute{ + Optional: true, + }, + "title": schema.StringAttribute{ + Required: true, + }, + }, + Blocks: map[string]schema.Block{ + "button": schema.ListNestedBlock{ + CustomType: fwtypes.NewListNestedObjectTypeOf[ButtonData](ctx), + NestedObject: schema.NestedBlockObject{ + Attributes: map[string]schema.Attribute{ + "text": schema.StringAttribute{ + Required: true, + }, + "value": schema.StringAttribute{ + Required: true, + }, + }, + }, + }, + }, + }, + }, + "plain_text_message": schema.ListNestedBlock{ + Validators: []validator.List{ + listvalidator.SizeAtMost(1), + }, + CustomType: fwtypes.NewListNestedObjectTypeOf[PlainTextMessageData](ctx), + NestedObject: schema.NestedBlockObject{ + Attributes: map[string]schema.Attribute{ + "value": schema.StringAttribute{ + Required: true, + }, + }, + }, + }, + "ssml_message": schema.ListNestedBlock{ + Validators: []validator.List{ + listvalidator.SizeAtMost(1), + }, + CustomType: fwtypes.NewListNestedObjectTypeOf[SSMLMessageData](ctx), + NestedObject: schema.NestedBlockObject{ + Attributes: map[string]schema.Attribute{ + "value": schema.StringAttribute{ + Required: true, + }, + }, + }, + }, + }, + } + + messageGroupLNB := schema.ListNestedBlock{ + Validators: []validator.List{ + listvalidator.SizeAtLeast(1), + }, + CustomType: fwtypes.NewListNestedObjectTypeOf[MessageGroupData](ctx), + NestedObject: schema.NestedBlockObject{ + Blocks: map[string]schema.Block{ + "message": schema.ListNestedBlock{ + Validators: []validator.List{ + listvalidator.SizeBetween(1, 1), + }, + CustomType: fwtypes.NewListNestedObjectTypeOf[MessageData](ctx), + NestedObject: messageNBO, + }, + "variation": schema.ListNestedBlock{ + CustomType: fwtypes.NewListNestedObjectTypeOf[MessageData](ctx), + NestedObject: messageNBO, + }, + }, + }, + } + + allowedInputTypesLNB := schema.ListNestedBlock{ + Validators: []validator.List{ + listvalidator.SizeBetween(1, 1), + }, + CustomType: fwtypes.NewListNestedObjectTypeOf[AllowedInputTypesData](ctx), + NestedObject: schema.NestedBlockObject{ + Attributes: map[string]schema.Attribute{ + "allow_audio_input": schema.BoolAttribute{ + Required: true, + }, + "allow_dtmf_input": schema.BoolAttribute{ + Required: true, + }, + }, + }, + } + + audioSpecificationLNB := schema.ListNestedBlock{ + Validators: []validator.List{ + listvalidator.SizeAtMost(1), + }, + CustomType: fwtypes.NewListNestedObjectTypeOf[AudioSpecificationData](ctx), + NestedObject: schema.NestedBlockObject{ + Attributes: map[string]schema.Attribute{ + "end_timeout_ms": schema.Int64Attribute{ + Required: true, + Validators: []validator.Int64{ + int64validator.AtLeast(1), + }, + }, + "max_length_ms": schema.Int64Attribute{ + Required: true, + Validators: []validator.Int64{ + int64validator.AtLeast(1), + }, + }, + }, + }, + } + + dmfSpecificationLNB := schema.ListNestedBlock{ + Validators: []validator.List{ + listvalidator.SizeAtMost(1), + }, + CustomType: fwtypes.NewListNestedObjectTypeOf[DTMFSpecificationData](ctx), + NestedObject: schema.NestedBlockObject{ + Attributes: map[string]schema.Attribute{ + "deletion_character": schema.StringAttribute{ + Required: true, + Validators: []validator.String{ + stringvalidator.RegexMatches( + regexache.MustCompile(`^[A-D0-9#*]{1}$`), + "alphanumeric characters", + ), + }, + }, + "end_character": schema.StringAttribute{ + Required: true, + Validators: []validator.String{ + stringvalidator.RegexMatches( + regexache.MustCompile(`^[A-D0-9#*]{1}$`), + "alphanumeric characters", + ), + }, + }, + "end_timeout_ms": schema.Int64Attribute{ + Required: true, + Validators: []validator.Int64{ + int64validator.AtLeast(1), + }, + }, + "max_length": schema.Int64Attribute{ + Required: true, + Validators: []validator.Int64{ + int64validator.Between(1, 1024), + }, + }, + }, + }, + } + + audioAndDTMFInputSpecificationLNB := schema.ListNestedBlock{ + Validators: []validator.List{ + listvalidator.SizeAtMost(1), + }, + CustomType: fwtypes.NewListNestedObjectTypeOf[AudioAndDTMFInputSpecificationData](ctx), + NestedObject: schema.NestedBlockObject{ + Attributes: map[string]schema.Attribute{ + "start_timeout_ms": schema.Int64Attribute{ + Required: true, + Validators: []validator.Int64{ + int64validator.AtLeast(1), + }, + }, + }, + Blocks: map[string]schema.Block{ + "audio_specification": audioSpecificationLNB, + "dtmf_specification": dmfSpecificationLNB, + }, + }, + } + + textInputSpecificationLNB := schema.ListNestedBlock{ + Validators: []validator.List{ + listvalidator.SizeAtMost(1), + }, + CustomType: fwtypes.NewListNestedObjectTypeOf[TextInputSpecificationData](ctx), + NestedObject: schema.NestedBlockObject{ + Attributes: map[string]schema.Attribute{ + "start_timeout_ms": schema.Int64Attribute{ + Required: true, + Validators: []validator.Int64{ + int64validator.AtLeast(1), + }, + }, + }, + }, + } + + promptAttemptsSpecificationLNB := schema.ListNestedBlock{ + CustomType: fwtypes.NewListNestedObjectTypeOf[PromptAttemptsSpecificationData](ctx), + NestedObject: schema.NestedBlockObject{ + Attributes: map[string]schema.Attribute{ + "map_block_key": schema.StringAttribute{ + Required: true, + CustomType: fwtypes.StringEnumType[PromptAttemptsType](), + }, + "allow_interrupt": schema.BoolAttribute{ + Optional: true, + }, + }, + Blocks: map[string]schema.Block{ + "allowed_input_types": allowedInputTypesLNB, + "audio_and_dtmf_input_specification": audioAndDTMFInputSpecificationLNB, + "text_input_specification": textInputSpecificationLNB, + }, + }, + } + + promptSpecificationLNB := schema.ListNestedBlock{ + Validators: []validator.List{ + listvalidator.SizeBetween(1, 1), + }, + NestedObject: schema.NestedBlockObject{ + Attributes: map[string]schema.Attribute{ + "allow_interrupt": schema.BoolAttribute{ + Optional: true, + }, + "max_retries": schema.Int64Attribute{ + Required: true, + }, + "message_selection_strategy": schema.StringAttribute{ + Optional: true, + Validators: []validator.String{ + enum.FrameworkValidate[awstypes.MessageSelectionStrategy](), + }, + }, + }, + Blocks: map[string]schema.Block{ + "message_groups": messageGroupLNB, + "prompt_attempts_specification": promptAttemptsSpecificationLNB, + }, + }, + } + + sampleUtteranceLNB := schema.ListNestedBlock{ + CustomType: fwtypes.NewListNestedObjectTypeOf[SampleUtteranceData](ctx), + NestedObject: schema.NestedBlockObject{ + Attributes: map[string]schema.Attribute{ + "utterance": schema.StringAttribute{ + Required: true, + }, + }, + }, + } + + slotResolutionSettingLNB := schema.ListNestedBlock{ + CustomType: fwtypes.NewListNestedObjectTypeOf[SlotResolutionSettingData](ctx), + NestedObject: schema.NestedBlockObject{ + Attributes: map[string]schema.Attribute{ + "slot_resolution_strategy": schema.StringAttribute{ + CustomType: fwtypes.StringEnumType[awstypes.SlotResolutionStrategy](), + Required: true, + }, + }, + }, + } + + responseSpecificationLNB := schema.ListNestedBlock{ + CustomType: fwtypes.NewListNestedObjectTypeOf[ResponseSpecificationData](ctx), + NestedObject: schema.NestedBlockObject{ + Attributes: map[string]schema.Attribute{ + "allow_interrupt": schema.BoolAttribute{ + Optional: true, + }, + }, + Blocks: map[string]schema.Block{ + "message_groups": messageGroupLNB, + }, + }, + } + + stillWaitingResponseSpecificationLNB := schema.ListNestedBlock{ + CustomType: fwtypes.NewListNestedObjectTypeOf[StillWaitingResponseSpecificationData](ctx), + NestedObject: schema.NestedBlockObject{ + Attributes: map[string]schema.Attribute{ + "allow_interrupt": schema.BoolAttribute{ + Optional: true, + }, + "frequency_in_seconds": schema.Int64Attribute{ + Required: true, + }, + "timeout_in_seconds": schema.Int64Attribute{ + Required: true, + }, + }, + Blocks: map[string]schema.Block{ + "message_groups": messageGroupLNB, + }, + }, + } + + waitAndContinueSpecificationLNB := schema.ListNestedBlock{ + CustomType: fwtypes.NewListNestedObjectTypeOf[WaitAndContinueSpecificationData](ctx), + NestedObject: schema.NestedBlockObject{ + Attributes: map[string]schema.Attribute{ + "active": schema.BoolAttribute{ + Optional: true, + }, + }, + Blocks: map[string]schema.Block{ + "continue_response": responseSpecificationLNB, + "still_waiting_response": stillWaitingResponseSpecificationLNB, + "waiting_response": responseSpecificationLNB, + }, + }, + } + + valueElicitationSettingLNB := schema.ListNestedBlock{ + CustomType: fwtypes.NewListNestedObjectTypeOf[ValueElicitationSettingData](ctx), + Validators: []validator.List{ + listvalidator.IsRequired(), + listvalidator.SizeAtMost(1), + }, + NestedObject: schema.NestedBlockObject{ + Attributes: map[string]schema.Attribute{ + "slot_constraint": schema.StringAttribute{ + Required: true, + Validators: []validator.String{ + enum.FrameworkValidate[awstypes.SlotConstraint](), + }, + }, + }, + Blocks: map[string]schema.Block{ + "default_value_specification": defaultValueSpecificationLNB, + "prompt_specification": promptSpecificationLNB, + "sample_utterance": sampleUtteranceLNB, + "slot_resolution_setting": slotResolutionSettingLNB, + "wait_and_continue_specification": waitAndContinueSpecificationLNB, + }, + }, + } + resp.Schema = schema.Schema{ Attributes: map[string]schema.Attribute{ "bot_id": schema.StringAttribute{ @@ -97,9 +513,9 @@ func (r *resourceSlot) Schema(ctx context.Context, req resource.SchemaRequest, r }, }, Blocks: map[string]schema.Block{ - "multiple_values_setting": lexschema.MultipleValuesSettingBlock(ctx), - "obfuscation_setting": lexschema.ObfuscationSettingBlock(ctx), - "value_elicitation_setting": lexschema.ValueElicitationSettingBlock(ctx), + "multiple_values_setting": multValueSettingsLNB, + "obfuscation_setting": obfuscationSettingLNB, + "value_elicitation_setting": valueElicitationSettingLNB, //sub_slot_setting "timeouts": timeouts.Block(ctx, timeouts.Opts{ Create: true, @@ -329,9 +745,146 @@ type resourceSlotData struct { ValueElicitationSettings fwtypes.ListNestedObjectValueOf[lexschema.ValueElicitationSettingData] `tfsdk:"value_elicitation_settings"` } +type MultipleValuesSettingData struct { + AllowMultipleValues types.Bool `tfsdk:"allow_multiple_values"` +} + +type ObfuscationSettingData struct { + ObfuscationSettingType fwtypes.StringEnum[awstypes.ObfuscationSettingType] `tfsdk:"obfuscation_setting_type"` +} + +type DefaultValueSpecificationData struct { + DefaultValueList fwtypes.ListNestedObjectValueOf[DefaultValueData] `tfsdk:"default_value_list"` +} + +type DefaultValueData struct { + DefaultValue types.String `tfsdk:"default_value"` +} + +type PromptSpecificationData struct { + AllowInterrupt types.Bool `tfsdk:"allow_interrupt"` + MaxRetries types.Int64 `tfsdk:"max_retries"` + MessageGroup fwtypes.ListNestedObjectValueOf[MessageGroupData] `tfsdk:"message_groups"` + MessageSelectionStrategy fwtypes.StringEnum[awstypes.MessageSelectionStrategy] `tfsdk:"message_selection_strategy"` + PromptAttemptsSpecification fwtypes.ObjectMapValueOf[PromptAttemptSpecificationData] `tfsdk:"prompt_attempts_specification"` +} +type PromptAttemptSpecificationData struct { + AllowedInputTypes fwtypes.ListNestedObjectValueOf[AllowedInputTypesData] `tfsdk:"allowed_input_types"` + AllowInterrupt types.Bool `tfsdk:"allow_interrupt"` + AudioAndDTMFInputSpecification fwtypes.ListNestedObjectValueOf[AudioAndDTMFInputSpecificationData] `tfsdk:"audio_and_dtmf_input_specification"` + TextInputSpecification fwtypes.ListNestedObjectValueOf[TextInputSpecificationData] `tfsdk:"text_input_specification"` +} + +type DTMFSpecificationData struct { + EndCharacter types.String `tfsdk:"end_character"` + EndTimeoutMs types.Int64 `tfsdk:"end_timeout_ms"` + DeletionCharacter types.String `tfsdk:"deletion_character"` + MaxLength types.Int64 `tfsdk:"max_length"` +} + +type TextInputSpecificationData struct { + StartTimeoutMs types.Int64 `tfsdk:"start_timeout_ms"` +} + +type AllowedInputTypesData struct { + AllowAudioInput types.Bool `tfsdk:"allow_audio_input"` + AllowDTMFInput types.Bool `tfsdk:"allow_dtmf_input"` +} + +type AudioAndDTMFInputSpecificationData struct { + AudioSpecification fwtypes.ListNestedObjectValueOf[AudioSpecificationData] `tfsdk:"audio_specification"` + StartTimeoutMs types.Int64 `tfsdk:"start_timeout_ms"` + DTMFSpecification fwtypes.ListNestedObjectValueOf[DTMFSpecificationData] `tfsdk:"dtmf_specification"` +} + +type AudioSpecificationData struct { + EndTimeoutMs types.Int64 `tfsdk:"end_timeout_ms"` + MaxLengthMs types.Int64 `tfsdk:"max_length_ms"` +} + +type CustomPayloadData struct { + Value types.String `tfsdk:"value"` +} + +type ImageResponseCardData struct { + Title types.String `tfsdk:"title"` + Button fwtypes.ListNestedObjectValueOf[ButtonData] `tfsdk:"buttons"` + ImageURL types.String `tfsdk:"image_url"` + Subtitle types.String `tfsdk:"subtitle"` +} + +type ButtonData struct { + Text types.String `tfsdk:"text"` + Value types.String `tfsdk:"value"` +} + +type PlainTextMessageData struct { + Value types.String `tfsdk:"value"` +} + +type SSMLMessageData struct { + Value types.String `tfsdk:"value"` +} +type MessageGroupData struct { + Message fwtypes.ListNestedObjectValueOf[MessageData] `tfsdk:"message"` + Variations fwtypes.ListNestedObjectValueOf[MessageData] `tfsdk:"variations"` +} + +type MessageData struct { + CustomPayload fwtypes.ListNestedObjectValueOf[CustomPayloadData] `tfsdk:"custom_payload"` + ImageResponseCard fwtypes.ListNestedObjectValueOf[ImageResponseCardData] `tfsdk:"image_response_card"` + PlainTextMessage fwtypes.ListNestedObjectValueOf[PlainTextMessageData] `tfsdk:"plain_text_message"` + SSMLMessage fwtypes.ListNestedObjectValueOf[SSMLMessageData] `tfsdk:"ssml_message"` +} + +type PromptAttemptsSpecificationData struct { + AllowedInputTypes fwtypes.ListNestedObjectValueOf[AllowedInputTypes] `tfsdk:"allowed_input_types"` + AllowInterrupt types.Bool `tfsdk:"allow_interrupt"` + AudioAndDTMFInputSpecification fwtypes.ListNestedObjectValueOf[AudioAndDTMFInputSpecification] `tfsdk:"audio_and_dtmf_input_specification"` + MapBlockKey fwtypes.StringEnum[PromptAttemptsType] `tfsdk:"map_block_key"` + TextInputSpecification fwtypes.ListNestedObjectValueOf[TextInputSpecification] `tfsdk:"text_input_specification"` +} + +type SampleUtteranceData struct { + Utterance types.String `tfsdk:"utterance"` +} + +type SlotResolutionSettingData struct { + SlotResolutionStrategy fwtypes.StringEnum[awstypes.SlotResolutionStrategy] `tfsdk:"slot_resolution_strategy"` +} + +type ResponseSpecificationData struct { + AllowInterrupt types.Bool `tfsdk:"allow_interrupt"` + MessageGroups fwtypes.ListNestedObjectValueOf[MessageGroupData] `tfsdk:"message_groups"` +} + +type StillWaitingResponseSpecificationData struct { + AllowInterrupt types.Bool `tfsdk:"allow_interrupt"` + FrequencyInSeconds types.Int64 `tfsdk:"frequency_in_seconds"` + MessageGroups fwtypes.ListNestedObjectValueOf[MessageGroupData] `tfsdk:"message_groups"` + TimeoutInSeconds types.Int64 `tfsdk:"timeout_in_seconds"` +} + +type WaitAndContinueSpecificationData struct { + Active types.Bool `tfsdk:"active"` + ContinueResponse fwtypes.ListNestedObjectValueOf[ResponseSpecificationData] `tfsdk:"continue_response"` + StillWaitingResponse fwtypes.ListNestedObjectValueOf[StillWaitingResponseSpecificationData] `tfsdk:"still_waiting_response"` + WaitingResponse fwtypes.ListNestedObjectValueOf[ResponseSpecificationData] `tfsdk:"waiting_response"` +} + +type ValueElicitationSettingData struct { + SlotConstraint fwtypes.StringEnum[awstypes.SlotConstraint] `tfsdk:"slot_constraint"` + DefaultValueSpecification fwtypes.ListNestedObjectValueOf[DefaultValueSpecificationData] `tfsdk:"default_value_specification"` + PromptSpecification fwtypes.ListNestedObjectValueOf[PromptSpecificationData] `tfsdk:"prompt_specification"` + SampleUtterance fwtypes.ListNestedObjectValueOf[SampleUtteranceData] `tfsdk:"sample_utterance"` + SlotResolutionSetting fwtypes.ListNestedObjectValueOf[SlotResolutionSettingData] `tfsdk:"slot_resolution_setting"` + WaitAndContinueSpecification fwtypes.ListNestedObjectValueOf[WaitAndContinueSpecificationData] `tfsdk:"wait_and_continue_specification"` +} + func slotHasChanges(_ context.Context, plan, state resourceSlotData) bool { return !plan.Description.Equal(state.Description) || !plan.Name.Equal(state.Name) || !plan.Description.Equal(state.Description) || - !plan.SlotTypeID.Equal(state.SlotTypeID) + !plan.SlotTypeID.Equal(state.SlotTypeID) || + !plan.ObfuscationSetting.Equal(state.ObfuscationSetting) } From 026933a730d2a72c30a12b13fc7497c7b235eafe Mon Sep 17 00:00:00 2001 From: Sharon Nam Date: Wed, 24 Jan 2024 12:26:16 -0800 Subject: [PATCH 21/42] Clean up schema and test file --- internal/service/lexv2models/slot.go | 70 ++++++++++++----------- internal/service/lexv2models/slot_test.go | 5 +- 2 files changed, 40 insertions(+), 35 deletions(-) diff --git a/internal/service/lexv2models/slot.go b/internal/service/lexv2models/slot.go index 507b505599c..2fa3efcad67 100644 --- a/internal/service/lexv2models/slot.go +++ b/internal/service/lexv2models/slot.go @@ -16,7 +16,6 @@ import ( "github.com/hashicorp/terraform-plugin-framework-validators/int64validator" "github.com/hashicorp/terraform-plugin-framework-validators/listvalidator" "github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator" - "github.com/hashicorp/terraform-plugin-framework/path" "github.com/hashicorp/terraform-plugin-framework/resource" "github.com/hashicorp/terraform-plugin-framework/resource/schema" "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" @@ -30,7 +29,6 @@ import ( "github.com/hashicorp/terraform-provider-aws/internal/framework" "github.com/hashicorp/terraform-provider-aws/internal/framework/flex" fwtypes "github.com/hashicorp/terraform-provider-aws/internal/framework/types" - lexschema "github.com/hashicorp/terraform-provider-aws/internal/service/lexv2models/schema" "github.com/hashicorp/terraform-provider-aws/internal/tfresource" "github.com/hashicorp/terraform-provider-aws/names" ) @@ -54,6 +52,7 @@ const ( type resourceSlot struct { framework.ResourceWithConfigure + framework.WithImportByID framework.WithTimeouts } @@ -328,8 +327,8 @@ func (r *resourceSlot) Schema(ctx context.Context, req resource.SchemaRequest, r }, } - promptAttemptsSpecificationLNB := schema.ListNestedBlock{ - CustomType: fwtypes.NewListNestedObjectTypeOf[PromptAttemptsSpecificationData](ctx), + promptAttemptsSpecificationLNB := schema.SetNestedBlock{ + CustomType: fwtypes.NewSetNestedObjectTypeOf[PromptAttemptsSpecificationData](ctx), NestedObject: schema.NestedBlockObject{ Attributes: map[string]schema.Attribute{ "map_block_key": schema.StringAttribute{ @@ -352,6 +351,7 @@ func (r *resourceSlot) Schema(ctx context.Context, req resource.SchemaRequest, r Validators: []validator.List{ listvalidator.SizeBetween(1, 1), }, + CustomType: fwtypes.NewListNestedObjectTypeOf[PromptSpecificationData](ctx), NestedObject: schema.NestedBlockObject{ Attributes: map[string]schema.Attribute{ "allow_interrupt": schema.BoolAttribute{ @@ -478,6 +478,7 @@ func (r *resourceSlot) Schema(ctx context.Context, req resource.SchemaRequest, r Required: true, PlanModifiers: []planmodifier.String{ stringplanmodifier.RequiresReplace(), + stringplanmodifier.UseStateForUnknown(), }, }, "bot_version": schema.StringAttribute{ @@ -492,6 +493,9 @@ func (r *resourceSlot) Schema(ctx context.Context, req resource.SchemaRequest, r "id": framework.IDAttribute(), "intent_id": schema.StringAttribute{ Required: true, + // Validators: []validator.String{ + // Fixed length of 10 + // }, PlanModifiers: []planmodifier.String{ stringplanmodifier.RequiresReplace(), }, @@ -504,9 +508,9 @@ func (r *resourceSlot) Schema(ctx context.Context, req resource.SchemaRequest, r }, "name": schema.StringAttribute{ Required: true, - PlanModifiers: []planmodifier.String{ - stringplanmodifier.RequiresReplace(), - }, + // PlanModifiers: []planmodifier.String{ + // stringplanmodifier.RequiresReplace(), + // }, }, "slot_type_id": schema.StringAttribute{ Optional: true, @@ -539,7 +543,7 @@ func (r *resourceSlot) Create(ctx context.Context, req resource.CreateRequest, r SlotName: aws.String(plan.Name.ValueString()), } - resp.Diagnostics.Append(flex.Expand(ctx, plan, &in)...) + resp.Diagnostics.Append(flex.Expand(context.WithValue(ctx, flex.ResourcePrefix, ResNameSlot), &plan, in)...) if resp.Diagnostics.HasError() { return } @@ -578,7 +582,7 @@ func (r *resourceSlot) Create(ctx context.Context, req resource.CreateRequest, r plan.ID = types.StringValue(id) - resp.Diagnostics.Append(flex.Flatten(ctx, out, &plan)...) + resp.Diagnostics.Append(flex.Flatten(context.WithValue(ctx, flex.ResourcePrefix, ResNameSlot), &plan, in)...) if resp.Diagnostics.HasError() { return } @@ -608,7 +612,7 @@ func (r *resourceSlot) Read(ctx context.Context, req resource.ReadRequest, resp return } - resp.Diagnostics.Append(flex.Flatten(ctx, out, &state)...) + resp.Diagnostics.Append(flex.Flatten(context.WithValue(ctx, flex.ResourcePrefix, ResNameSlot), out, &state)...) if resp.Diagnostics.HasError() { return } @@ -652,7 +656,7 @@ func (r *resourceSlot) Update(ctx context.Context, req resource.UpdateRequest, r return } - // resp.Diagnostics.Append(flex.Flatten(ctx, out, &plan)...) + resp.Diagnostics.Append(flex.Flatten(context.WithValue(ctx, flex.ResourcePrefix, ResNameSlot), &plan, input)...) if resp.Diagnostics.HasError() { return } @@ -692,9 +696,9 @@ func (r *resourceSlot) Delete(ctx context.Context, req resource.DeleteRequest, r } } -func (r *resourceSlot) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { - resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) -} +// func (r *resourceSlot) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { +// resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) +// } func findSlotByID(ctx context.Context, conn *lexmodelsv2.Client, id string) (*lexmodelsv2.DescribeSlotOutput, error) { parts, err := intflex.ExpandResourceId(id, slotIDPartCount, false) @@ -731,18 +735,18 @@ func findSlotByID(ctx context.Context, conn *lexmodelsv2.Client, id string) (*le } type resourceSlotData struct { - BotID types.String `tfsdk:"bot_id"` - BotVersion types.String `tfsdk:"bot_version"` - Description types.String `tfsdk:"description"` - ID types.String `tfsdk:"id"` - IntentID types.String `tfsdk:"intent_id"` - LocaleID types.String `tfsdk:"locale_id"` - MultipleValuesSetting fwtypes.ListNestedObjectValueOf[lexschema.MultipleValuesSettingData] `tfsdk:"multiple_values_setting"` - Name types.String `tfsdk:"name"` - ObfuscationSetting fwtypes.ListNestedObjectValueOf[lexschema.ObfuscationSettingData] `tfsdk:"obfuscation_setting"` - Timeouts timeouts.Value `tfsdk:"timeouts"` - SlotTypeID types.String `tfsdk:"slot_type_id"` - ValueElicitationSettings fwtypes.ListNestedObjectValueOf[lexschema.ValueElicitationSettingData] `tfsdk:"value_elicitation_settings"` + BotID types.String `tfsdk:"bot_id"` + BotVersion types.String `tfsdk:"bot_version"` + Description types.String `tfsdk:"description"` + ID types.String `tfsdk:"id"` + IntentID types.String `tfsdk:"intent_id"` + LocaleID types.String `tfsdk:"locale_id"` + MultipleValuesSetting fwtypes.ListNestedObjectValueOf[MultipleValuesSettingData] `tfsdk:"multiple_values_setting"` + Name types.String `tfsdk:"name"` + ObfuscationSetting fwtypes.ListNestedObjectValueOf[ObfuscationSettingData] `tfsdk:"obfuscation_setting"` + Timeouts timeouts.Value `tfsdk:"timeouts"` + SlotTypeID types.String `tfsdk:"slot_type_id"` + ValueElicitationSetting fwtypes.ListNestedObjectValueOf[ValueElicitationSettingData] `tfsdk:"value_elicitation_setting"` } type MultipleValuesSettingData struct { @@ -762,15 +766,16 @@ type DefaultValueData struct { } type PromptSpecificationData struct { - AllowInterrupt types.Bool `tfsdk:"allow_interrupt"` - MaxRetries types.Int64 `tfsdk:"max_retries"` - MessageGroup fwtypes.ListNestedObjectValueOf[MessageGroupData] `tfsdk:"message_groups"` - MessageSelectionStrategy fwtypes.StringEnum[awstypes.MessageSelectionStrategy] `tfsdk:"message_selection_strategy"` - PromptAttemptsSpecification fwtypes.ObjectMapValueOf[PromptAttemptSpecificationData] `tfsdk:"prompt_attempts_specification"` + AllowInterrupt types.Bool `tfsdk:"allow_interrupt"` + MaxRetries types.Int64 `tfsdk:"max_retries"` + MessageGroup fwtypes.ListNestedObjectValueOf[MessageGroupData] `tfsdk:"message_groups"` + MessageSelectionStrategy fwtypes.StringEnum[awstypes.MessageSelectionStrategy] `tfsdk:"message_selection_strategy"` + PromptAttemptsSpecification fwtypes.SetNestedObjectValueOf[PromptAttemptSpecificationData] `tfsdk:"prompt_attempts_specification"` } type PromptAttemptSpecificationData struct { AllowedInputTypes fwtypes.ListNestedObjectValueOf[AllowedInputTypesData] `tfsdk:"allowed_input_types"` AllowInterrupt types.Bool `tfsdk:"allow_interrupt"` + MapBlockKey fwtypes.StringEnum[PromptAttemptsType] `tfsdk:"map_block_key"` AudioAndDTMFInputSpecification fwtypes.ListNestedObjectValueOf[AudioAndDTMFInputSpecificationData] `tfsdk:"audio_and_dtmf_input_specification"` TextInputSpecification fwtypes.ListNestedObjectValueOf[TextInputSpecificationData] `tfsdk:"text_input_specification"` } @@ -885,6 +890,5 @@ func slotHasChanges(_ context.Context, plan, state resourceSlotData) bool { return !plan.Description.Equal(state.Description) || !plan.Name.Equal(state.Name) || !plan.Description.Equal(state.Description) || - !plan.SlotTypeID.Equal(state.SlotTypeID) || - !plan.ObfuscationSetting.Equal(state.ObfuscationSetting) + !plan.SlotTypeID.Equal(state.SlotTypeID) } diff --git a/internal/service/lexv2models/slot_test.go b/internal/service/lexv2models/slot_test.go index 14c02024c6e..81377e91eab 100644 --- a/internal/service/lexv2models/slot_test.go +++ b/internal/service/lexv2models/slot_test.go @@ -206,16 +206,17 @@ func testAccSlotConfig_basic(rName string) string { resource "aws_lexv2models_slot" "test" { bot_id = aws_lexv2models_bot.test.id bot_version = aws_lexv2models_bot_locale.test.bot_version - intent_id = aws_lexv2models_intent.test.id + intent_id = aws_lexv2models_intent.test.intent_id name = %[1]q locale_id = aws_lexv2models_bot_locale.test.locale_id value_elicitation_setting { + slot_constraint = "Optional" default_value_specification { default_value_list { default_value = "default" } - } + } } } `, rName)) From 7e988e41059e312f2778c9289762315ae03b2e42 Mon Sep 17 00:00:00 2001 From: Sharon Nam Date: Thu, 25 Jan 2024 16:33:31 -0800 Subject: [PATCH 22/42] Fix Flatten calls and add tests --- internal/service/lexv2models/slot.go | 54 +++++--- internal/service/lexv2models/slot_test.go | 161 +++++++++++++++++++++- 2 files changed, 189 insertions(+), 26 deletions(-) diff --git a/internal/service/lexv2models/slot.go b/internal/service/lexv2models/slot.go index 2fa3efcad67..c0b80c01c4a 100644 --- a/internal/service/lexv2models/slot.go +++ b/internal/service/lexv2models/slot.go @@ -25,6 +25,7 @@ import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/retry" "github.com/hashicorp/terraform-provider-aws/internal/create" "github.com/hashicorp/terraform-provider-aws/internal/enum" + "github.com/hashicorp/terraform-provider-aws/internal/errs" intflex "github.com/hashicorp/terraform-provider-aws/internal/flex" "github.com/hashicorp/terraform-provider-aws/internal/framework" "github.com/hashicorp/terraform-provider-aws/internal/framework/flex" @@ -506,6 +507,12 @@ func (r *resourceSlot) Schema(ctx context.Context, req resource.SchemaRequest, r stringplanmodifier.RequiresReplace(), }, }, + "slot_id": schema.StringAttribute{ + Computed: true, + PlanModifiers: []planmodifier.String{ + stringplanmodifier.UseStateForUnknown(), + }, + }, "name": schema.StringAttribute{ Required: true, // PlanModifiers: []planmodifier.String{ @@ -582,7 +589,7 @@ func (r *resourceSlot) Create(ctx context.Context, req resource.CreateRequest, r plan.ID = types.StringValue(id) - resp.Diagnostics.Append(flex.Flatten(context.WithValue(ctx, flex.ResourcePrefix, ResNameSlot), &plan, in)...) + resp.Diagnostics.Append(flex.Flatten(context.WithValue(ctx, flex.ResourcePrefix, ResNameSlot), out, &plan)...) if resp.Diagnostics.HasError() { return } @@ -635,7 +642,7 @@ func (r *resourceSlot) Update(ctx context.Context, req resource.UpdateRequest, r // TODO: expand here, or check for updatable arguments individually? - resp.Diagnostics.Append(flex.Expand(context.WithValue(ctx, flex.ResourcePrefix, ResNameSlot), &plan, input)...) + resp.Diagnostics.Append(flex.Expand(context.WithValue(ctx, flex.ResourcePrefix, ResNameSlot), plan, input)...) if resp.Diagnostics.HasError() { return } @@ -656,7 +663,7 @@ func (r *resourceSlot) Update(ctx context.Context, req resource.UpdateRequest, r return } - resp.Diagnostics.Append(flex.Flatten(context.WithValue(ctx, flex.ResourcePrefix, ResNameSlot), &plan, input)...) + resp.Diagnostics.Append(flex.Flatten(context.WithValue(ctx, flex.ResourcePrefix, ResNameSlot), input, &plan)...) if resp.Diagnostics.HasError() { return } @@ -675,19 +682,24 @@ func (r *resourceSlot) Delete(ctx context.Context, req resource.DeleteRequest, r } in := &lexmodelsv2.DeleteSlotInput{ - BotId: aws.String(state.ID.ValueString()), - BotVersion: aws.String(state.ID.ValueString()), - IntentId: aws.String(state.ID.ValueString()), - LocaleId: aws.String(state.ID.ValueString()), - SlotId: aws.String(state.ID.ValueString()), + BotId: aws.String(state.BotID.ValueString()), + BotVersion: aws.String(state.BotVersion.ValueString()), + IntentId: aws.String(state.IntentID.ValueString()), + LocaleId: aws.String(state.LocaleID.ValueString()), + SlotId: aws.String(state.SlotID.ValueString()), } _, err := conn.DeleteSlot(ctx, in) + + if errs.IsA[*awstypes.ResourceNotFoundException](err) { + return + } + + if errs.IsAErrorMessageContains[*awstypes.PreconditionFailedException](err, "does not exist") { + return + } + if err != nil { - var nfe *awstypes.ResourceNotFoundException - if errors.As(err, &nfe) { - return - } resp.Diagnostics.AddError( create.ProblemStandardMessage(names.LexV2Models, create.ErrActionDeleting, ResNameSlot, state.ID.String(), err), err.Error(), @@ -715,15 +727,15 @@ func findSlotByID(ctx context.Context, conn *lexmodelsv2.Client, id string) (*le } out, err := conn.DescribeSlot(ctx, in) - if err != nil { - var nfe *awstypes.ResourceNotFoundException - if errors.As(err, &nfe) { - return nil, &retry.NotFoundError{ - LastError: err, - LastRequest: in, - } + + if errs.IsA[*awstypes.ResourceNotFoundException](err) { + return nil, &retry.NotFoundError{ + LastError: err, + LastRequest: in, } + } + if err != nil { return nil, err } @@ -741,6 +753,7 @@ type resourceSlotData struct { ID types.String `tfsdk:"id"` IntentID types.String `tfsdk:"intent_id"` LocaleID types.String `tfsdk:"locale_id"` + SlotID types.String `tfsdk:"slot_id"` MultipleValuesSetting fwtypes.ListNestedObjectValueOf[MultipleValuesSettingData] `tfsdk:"multiple_values_setting"` Name types.String `tfsdk:"name"` ObfuscationSetting fwtypes.ListNestedObjectValueOf[ObfuscationSettingData] `tfsdk:"obfuscation_setting"` @@ -888,7 +901,6 @@ type ValueElicitationSettingData struct { func slotHasChanges(_ context.Context, plan, state resourceSlotData) bool { return !plan.Description.Equal(state.Description) || - !plan.Name.Equal(state.Name) || - !plan.Description.Equal(state.Description) || + !plan.MultipleValuesSetting.Equal(state.MultipleValuesSetting) || !plan.SlotTypeID.Equal(state.SlotTypeID) } diff --git a/internal/service/lexv2models/slot_test.go b/internal/service/lexv2models/slot_test.go index 81377e91eab..1aea060696b 100644 --- a/internal/service/lexv2models/slot_test.go +++ b/internal/service/lexv2models/slot_test.go @@ -48,6 +48,7 @@ func TestAccLexV2ModelsSlot_basic(t *testing.T) { resource.TestCheckResourceAttrPair(resourceName, "bot_id", botLocaleName, "bot_id"), resource.TestCheckResourceAttrPair(resourceName, "bot_version", botLocaleName, "bot_version"), resource.TestCheckResourceAttrPair(resourceName, "locale_id", botLocaleName, "locale_id"), + resource.TestCheckResourceAttrSet(resourceName, "intent_id"), ), }, { @@ -59,6 +60,102 @@ func TestAccLexV2ModelsSlot_basic(t *testing.T) { }) } +func TestAccLexV2ModelsSlot_updateMultipleValuesSetting(t *testing.T) { + ctx := acctest.Context(t) + + var slot lexmodelsv2.DescribeSlotOutput + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + resourceName := "aws_lexv2models_slot.test" + botLocaleName := "aws_lexv2models_bot_locale.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { + acctest.PreCheck(ctx, t) + acctest.PreCheckPartitionHasService(t, names.LexV2ModelsEndpointID) + testAccPreCheck(ctx, t) + }, + ErrorCheck: acctest.ErrorCheck(t, names.LexV2ModelsEndpointID), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + CheckDestroy: testAccCheckSlotDestroy(ctx), + Steps: []resource.TestStep{ + { + Config: testAccSlotConfig_updateMultipleValuesSetting(rName, true), + Check: resource.ComposeTestCheckFunc( + testAccCheckSlotExists(ctx, resourceName, &slot), + resource.TestCheckResourceAttr(resourceName, "name", rName), + resource.TestCheckResourceAttrPair(resourceName, "bot_id", botLocaleName, "bot_id"), + resource.TestCheckResourceAttrPair(resourceName, "bot_version", botLocaleName, "bot_version"), + resource.TestCheckResourceAttrPair(resourceName, "locale_id", botLocaleName, "locale_id"), + resource.TestCheckResourceAttr(resourceName, "multiple_values_setting.#", "1"), + resource.TestCheckResourceAttr(resourceName, "multiple_values_setting.0.%", "1"), + resource.TestCheckResourceAttr(resourceName, "multiple_values_setting.0.allow_multiple_values", "true"), + ), + }, + { + Config: testAccSlotConfig_updateMultipleValuesSetting(rName, false), + Check: resource.ComposeTestCheckFunc( + testAccCheckSlotExists(ctx, resourceName, &slot), + resource.TestCheckResourceAttr(resourceName, "name", rName), + resource.TestCheckResourceAttrPair(resourceName, "bot_id", botLocaleName, "bot_id"), + resource.TestCheckResourceAttrPair(resourceName, "bot_version", botLocaleName, "bot_version"), + resource.TestCheckResourceAttrPair(resourceName, "locale_id", botLocaleName, "locale_id"), + resource.TestCheckResourceAttr(resourceName, "multiple_values_setting.#", "1"), + resource.TestCheckResourceAttr(resourceName, "multiple_values_setting.0.%", "1"), + resource.TestCheckResourceAttr(resourceName, "multiple_values_setting.0.allow_multiple_values", "false"), + ), + }, + }, + }) +} + +func TestAccLexV2ModelsSlot_updateObfuscationSetting(t *testing.T) { + ctx := acctest.Context(t) + + var slot lexmodelsv2.DescribeSlotOutput + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + resourceName := "aws_lexv2models_slot.test" + botLocaleName := "aws_lexv2models_bot_locale.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { + acctest.PreCheck(ctx, t) + acctest.PreCheckPartitionHasService(t, names.LexV2ModelsEndpointID) + testAccPreCheck(ctx, t) + }, + ErrorCheck: acctest.ErrorCheck(t, names.LexV2ModelsEndpointID), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + CheckDestroy: testAccCheckSlotDestroy(ctx), + Steps: []resource.TestStep{ + { + Config: testAccSlotConfig_updateObfuscationSetting(rName, "DefaultObfuscation"), + Check: resource.ComposeTestCheckFunc( + testAccCheckSlotExists(ctx, resourceName, &slot), + resource.TestCheckResourceAttr(resourceName, "name", rName), + resource.TestCheckResourceAttrPair(resourceName, "bot_id", botLocaleName, "bot_id"), + resource.TestCheckResourceAttrPair(resourceName, "bot_version", botLocaleName, "bot_version"), + resource.TestCheckResourceAttrPair(resourceName, "locale_id", botLocaleName, "locale_id"), + resource.TestCheckResourceAttr(resourceName, "obfuscation_setting.#", "1"), + resource.TestCheckResourceAttr(resourceName, "obfuscation_setting.0.%", "1"), + resource.TestCheckResourceAttr(resourceName, "obfuscation_setting.0.obfuscation_setting_type", "DefaultObfuscation"), + ), + }, + { + Config: testAccSlotConfig_updateObfuscationSetting(rName, "None"), + Check: resource.ComposeTestCheckFunc( + testAccCheckSlotExists(ctx, resourceName, &slot), + resource.TestCheckResourceAttr(resourceName, "name", rName), + resource.TestCheckResourceAttrPair(resourceName, "bot_id", botLocaleName, "bot_id"), + resource.TestCheckResourceAttrPair(resourceName, "bot_version", botLocaleName, "bot_version"), + resource.TestCheckResourceAttrPair(resourceName, "locale_id", botLocaleName, "locale_id"), + resource.TestCheckResourceAttr(resourceName, "obfuscation_setting.#", "1"), + resource.TestCheckResourceAttr(resourceName, "obfuscation_setting.0.%", "1"), + resource.TestCheckResourceAttr(resourceName, "obfuscation_setting.0.obfuscation_setting_type", "None"), + ), + }, + }, + }) +} + func TestAccLexV2ModelsSlot_disappears(t *testing.T) { ctx := acctest.Context(t) if testing.Short() { @@ -191,11 +288,11 @@ resource "aws_lexv2models_bot_version" "test" { } resource "aws_lexv2models_intent" "test" { - bot_id = aws_lexv2models_bot.test.id - bot_version = aws_lexv2models_bot_locale.test.bot_version - name = %[1]q - locale_id = aws_lexv2models_bot_locale.test.locale_id - } + bot_id = aws_lexv2models_bot.test.id + bot_version = aws_lexv2models_bot_locale.test.bot_version + name = %[1]q + locale_id = aws_lexv2models_bot_locale.test.locale_id +} `, rName, ttl, dp) } @@ -221,3 +318,57 @@ resource "aws_lexv2models_slot" "test" { } `, rName)) } + +func testAccSlotConfig_updateMultipleValuesSetting(rName string, allow bool) string { + return acctest.ConfigCompose( + testAccSlotConfig_base(rName, 60, true), + fmt.Sprintf(` +resource "aws_lexv2models_slot" "test" { + bot_id = aws_lexv2models_bot.test.id + bot_version = aws_lexv2models_bot_locale.test.bot_version + intent_id = aws_lexv2models_intent.test.intent_id + name = %[1]q + locale_id = aws_lexv2models_bot_locale.test.locale_id + + value_elicitation_setting { + slot_constraint = "Optional" + default_value_specification { + default_value_list { + default_value = "default" + } + } + } + + multiple_values_setting { + allow_multiple_values = %[2]t + } +} +`, rName, allow)) +} + +func testAccSlotConfig_updateObfuscationSetting(rName, settingType string) string { + return acctest.ConfigCompose( + testAccSlotConfig_base(rName, 60, true), + fmt.Sprintf(` +resource "aws_lexv2models_slot" "test" { + bot_id = aws_lexv2models_bot.test.id + bot_version = aws_lexv2models_bot_locale.test.bot_version + intent_id = aws_lexv2models_intent.test.intent_id + name = %[1]q + locale_id = aws_lexv2models_bot_locale.test.locale_id + + value_elicitation_setting { + slot_constraint = "Optional" + default_value_specification { + default_value_list { + default_value = "default" + } + } + } + + obfuscation_setting { + obfuscation_setting_type = %[2]q + } +} +`, rName, settingType)) +} From a21793d3d32e99c554c18d4d372e98bc990e3a52 Mon Sep 17 00:00:00 2001 From: Sharon Nam Date: Thu, 25 Jan 2024 17:27:37 -0800 Subject: [PATCH 23/42] Clean Up --- internal/service/lexv2models/slot.go | 12 ------------ internal/service/lexv2models/slot_test.go | 15 +-------------- 2 files changed, 1 insertion(+), 26 deletions(-) diff --git a/internal/service/lexv2models/slot.go b/internal/service/lexv2models/slot.go index c0b80c01c4a..46813b6790d 100644 --- a/internal/service/lexv2models/slot.go +++ b/internal/service/lexv2models/slot.go @@ -494,9 +494,6 @@ func (r *resourceSlot) Schema(ctx context.Context, req resource.SchemaRequest, r "id": framework.IDAttribute(), "intent_id": schema.StringAttribute{ Required: true, - // Validators: []validator.String{ - // Fixed length of 10 - // }, PlanModifiers: []planmodifier.String{ stringplanmodifier.RequiresReplace(), }, @@ -515,9 +512,6 @@ func (r *resourceSlot) Schema(ctx context.Context, req resource.SchemaRequest, r }, "name": schema.StringAttribute{ Required: true, - // PlanModifiers: []planmodifier.String{ - // stringplanmodifier.RequiresReplace(), - // }, }, "slot_type_id": schema.StringAttribute{ Optional: true, @@ -640,8 +634,6 @@ func (r *resourceSlot) Update(ctx context.Context, req resource.UpdateRequest, r if slotHasChanges(ctx, plan, state) { input := &lexmodelsv2.UpdateSlotInput{} - // TODO: expand here, or check for updatable arguments individually? - resp.Diagnostics.Append(flex.Expand(context.WithValue(ctx, flex.ResourcePrefix, ResNameSlot), plan, input)...) if resp.Diagnostics.HasError() { return @@ -708,10 +700,6 @@ func (r *resourceSlot) Delete(ctx context.Context, req resource.DeleteRequest, r } } -// func (r *resourceSlot) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { -// resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) -// } - func findSlotByID(ctx context.Context, conn *lexmodelsv2.Client, id string) (*lexmodelsv2.DescribeSlotOutput, error) { parts, err := intflex.ExpandResourceId(id, slotIDPartCount, false) if err != nil { diff --git a/internal/service/lexv2models/slot_test.go b/internal/service/lexv2models/slot_test.go index 1aea060696b..d4960e2a458 100644 --- a/internal/service/lexv2models/slot_test.go +++ b/internal/service/lexv2models/slot_test.go @@ -108,7 +108,7 @@ func TestAccLexV2ModelsSlot_updateMultipleValuesSetting(t *testing.T) { }) } -func TestAccLexV2ModelsSlot_updateObfuscationSetting(t *testing.T) { +func TestAccLexV2ModelsSlot_ObfuscationSetting(t *testing.T) { ctx := acctest.Context(t) var slot lexmodelsv2.DescribeSlotOutput @@ -139,19 +139,6 @@ func TestAccLexV2ModelsSlot_updateObfuscationSetting(t *testing.T) { resource.TestCheckResourceAttr(resourceName, "obfuscation_setting.0.obfuscation_setting_type", "DefaultObfuscation"), ), }, - { - Config: testAccSlotConfig_updateObfuscationSetting(rName, "None"), - Check: resource.ComposeTestCheckFunc( - testAccCheckSlotExists(ctx, resourceName, &slot), - resource.TestCheckResourceAttr(resourceName, "name", rName), - resource.TestCheckResourceAttrPair(resourceName, "bot_id", botLocaleName, "bot_id"), - resource.TestCheckResourceAttrPair(resourceName, "bot_version", botLocaleName, "bot_version"), - resource.TestCheckResourceAttrPair(resourceName, "locale_id", botLocaleName, "locale_id"), - resource.TestCheckResourceAttr(resourceName, "obfuscation_setting.#", "1"), - resource.TestCheckResourceAttr(resourceName, "obfuscation_setting.0.%", "1"), - resource.TestCheckResourceAttr(resourceName, "obfuscation_setting.0.obfuscation_setting_type", "None"), - ), - }, }, }) } From d1890771d64da8db1f58e37267a7ac23bad62aee Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Fri, 26 Jan 2024 16:45:09 -0500 Subject: [PATCH 24/42] codegurureviewer: Use AWS SDK for Go v2. --- names/data/names_data.csv | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/names/data/names_data.csv b/names/data/names_data.csv index 8a42bb7e747..46ff30f334f 100644 --- a/names/data/names_data.csv +++ b/names/data/names_data.csv @@ -79,7 +79,7 @@ codebuild,codebuild,codebuild,codebuild,,codebuild,,,CodeBuild,CodeBuild,,,2,,aw codecommit,codecommit,codecommit,codecommit,,codecommit,,,CodeCommit,CodeCommit,,,2,,aws_codecommit_,,codecommit_,CodeCommit,AWS,,,,,,,CodeCommit,,, deploy,deploy,codedeploy,codedeploy,,deploy,,codedeploy,Deploy,CodeDeploy,,,2,aws_codedeploy_,aws_deploy_,,codedeploy_,CodeDeploy,AWS,,,,,,,CodeDeploy,,, codeguruprofiler,codeguruprofiler,codeguruprofiler,codeguruprofiler,,codeguruprofiler,,,CodeGuruProfiler,CodeGuruProfiler,,,2,,aws_codeguruprofiler_,,codeguruprofiler_,CodeGuru Profiler,Amazon,,,,,,,CodeGuruProfiler,ListProfilingGroups,, -codeguru-reviewer,codegurureviewer,codegurureviewer,codegurureviewer,,codegurureviewer,,,CodeGuruReviewer,CodeGuruReviewer,,1,,,aws_codegurureviewer_,,codegurureviewer_,CodeGuru Reviewer,Amazon,,,,,,,CodeGuru Reviewer,,, +codeguru-reviewer,codegurureviewer,codegurureviewer,codegurureviewer,,codegurureviewer,,,CodeGuruReviewer,CodeGuruReviewer,,,2,,aws_codegurureviewer_,,codegurureviewer_,CodeGuru Reviewer,Amazon,,,,,,,CodeGuru Reviewer,,, codepipeline,codepipeline,codepipeline,codepipeline,,codepipeline,,,CodePipeline,CodePipeline,,,2,aws_codepipeline,aws_codepipeline_,,codepipeline,CodePipeline,AWS,,,,,,,CodePipeline,ListPipelines,, codestar,codestar,codestar,codestar,,codestar,,,CodeStar,CodeStar,,1,,,aws_codestar_,,codestar_,CodeStar,AWS,,x,,,,,CodeStar,,, codestar-connections,codestarconnections,codestarconnections,codestarconnections,,codestarconnections,,,CodeStarConnections,CodeStarConnections,,,2,,aws_codestarconnections_,,codestarconnections_,CodeStar Connections,AWS,,,,,,,CodeStar connections,ListConnections,, From 6bcc88702613b49ee713201296bf049fad26732d Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Fri, 26 Jan 2024 16:47:06 -0500 Subject: [PATCH 25/42] Run 'make gen'. --- internal/conns/awsclient_gen.go | 6 ++-- internal/service/codegurureviewer/generate.go | 2 +- .../codegurureviewer/service_package_gen.go | 17 ++++++---- internal/service/codegurureviewer/tags_gen.go | 33 +++++++++---------- 4 files changed, 30 insertions(+), 28 deletions(-) diff --git a/internal/conns/awsclient_gen.go b/internal/conns/awsclient_gen.go index 722f17450b8..6b5356de4e0 100644 --- a/internal/conns/awsclient_gen.go +++ b/internal/conns/awsclient_gen.go @@ -26,6 +26,7 @@ import ( codecommit_sdkv2 "github.com/aws/aws-sdk-go-v2/service/codecommit" codedeploy_sdkv2 "github.com/aws/aws-sdk-go-v2/service/codedeploy" codeguruprofiler_sdkv2 "github.com/aws/aws-sdk-go-v2/service/codeguruprofiler" + codegurureviewer_sdkv2 "github.com/aws/aws-sdk-go-v2/service/codegurureviewer" codepipeline_sdkv2 "github.com/aws/aws-sdk-go-v2/service/codepipeline" codestarconnections_sdkv2 "github.com/aws/aws-sdk-go-v2/service/codestarconnections" codestarnotifications_sdkv2 "github.com/aws/aws-sdk-go-v2/service/codestarnotifications" @@ -138,7 +139,6 @@ import ( cloudwatch_sdkv1 "github.com/aws/aws-sdk-go/service/cloudwatch" cloudwatchrum_sdkv1 "github.com/aws/aws-sdk-go/service/cloudwatchrum" codeartifact_sdkv1 "github.com/aws/aws-sdk-go/service/codeartifact" - codegurureviewer_sdkv1 "github.com/aws/aws-sdk-go/service/codegurureviewer" cognitoidentity_sdkv1 "github.com/aws/aws-sdk-go/service/cognitoidentity" cognitoidentityprovider_sdkv1 "github.com/aws/aws-sdk-go/service/cognitoidentityprovider" configservice_sdkv1 "github.com/aws/aws-sdk-go/service/configservice" @@ -424,8 +424,8 @@ func (c *AWSClient) CodeGuruProfilerClient(ctx context.Context) *codeguruprofile return errs.Must(client[*codeguruprofiler_sdkv2.Client](ctx, c, names.CodeGuruProfiler, make(map[string]any))) } -func (c *AWSClient) CodeGuruReviewerConn(ctx context.Context) *codegurureviewer_sdkv1.CodeGuruReviewer { - return errs.Must(conn[*codegurureviewer_sdkv1.CodeGuruReviewer](ctx, c, names.CodeGuruReviewer, make(map[string]any))) +func (c *AWSClient) CodeGuruReviewerClient(ctx context.Context) *codegurureviewer_sdkv2.Client { + return errs.Must(client[*codegurureviewer_sdkv2.Client](ctx, c, names.CodeGuruReviewer, make(map[string]any))) } func (c *AWSClient) CodePipelineClient(ctx context.Context) *codepipeline_sdkv2.Client { diff --git a/internal/service/codegurureviewer/generate.go b/internal/service/codegurureviewer/generate.go index de601265188..dc0aab82113 100644 --- a/internal/service/codegurureviewer/generate.go +++ b/internal/service/codegurureviewer/generate.go @@ -1,7 +1,7 @@ // Copyright (c) HashiCorp, Inc. // SPDX-License-Identifier: MPL-2.0 -//go:generate go run ../../generate/tags/main.go -ListTags -ServiceTagsMap -UpdateTags +//go:generate go run ../../generate/tags/main.go -AWSSDKVersion=2 -KVTValues -SkipTypesImp -ServiceTagsMap -ListTags -UpdateTags //go:generate go run ../../generate/servicepackage/main.go // ONLY generate directives and package declaration! Do not add anything else to this file. diff --git a/internal/service/codegurureviewer/service_package_gen.go b/internal/service/codegurureviewer/service_package_gen.go index 422603b711d..fcc1146dae1 100644 --- a/internal/service/codegurureviewer/service_package_gen.go +++ b/internal/service/codegurureviewer/service_package_gen.go @@ -5,9 +5,8 @@ package codegurureviewer import ( "context" - aws_sdkv1 "github.com/aws/aws-sdk-go/aws" - session_sdkv1 "github.com/aws/aws-sdk-go/aws/session" - codegurureviewer_sdkv1 "github.com/aws/aws-sdk-go/service/codegurureviewer" + aws_sdkv2 "github.com/aws/aws-sdk-go-v2/aws" + codegurureviewer_sdkv2 "github.com/aws/aws-sdk-go-v2/service/codegurureviewer" "github.com/hashicorp/terraform-provider-aws/internal/conns" "github.com/hashicorp/terraform-provider-aws/internal/types" "github.com/hashicorp/terraform-provider-aws/names" @@ -44,11 +43,15 @@ func (p *servicePackage) ServicePackageName() string { return names.CodeGuruReviewer } -// NewConn returns a new AWS SDK for Go v1 client for this service package's AWS API. -func (p *servicePackage) NewConn(ctx context.Context, config map[string]any) (*codegurureviewer_sdkv1.CodeGuruReviewer, error) { - sess := config["session"].(*session_sdkv1.Session) +// NewClient returns a new AWS SDK for Go v2 client for this service package's AWS API. +func (p *servicePackage) NewClient(ctx context.Context, config map[string]any) (*codegurureviewer_sdkv2.Client, error) { + cfg := *(config["aws_sdkv2_config"].(*aws_sdkv2.Config)) - return codegurureviewer_sdkv1.New(sess.Copy(&aws_sdkv1.Config{Endpoint: aws_sdkv1.String(config["endpoint"].(string))})), nil + return codegurureviewer_sdkv2.NewFromConfig(cfg, func(o *codegurureviewer_sdkv2.Options) { + if endpoint := config["endpoint"].(string); endpoint != "" { + o.BaseEndpoint = aws_sdkv2.String(endpoint) + } + }), nil } func ServicePackage(ctx context.Context) conns.ServicePackage { diff --git a/internal/service/codegurureviewer/tags_gen.go b/internal/service/codegurureviewer/tags_gen.go index 9145195936b..2a375488963 100644 --- a/internal/service/codegurureviewer/tags_gen.go +++ b/internal/service/codegurureviewer/tags_gen.go @@ -5,9 +5,8 @@ import ( "context" "fmt" - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/service/codegurureviewer" - "github.com/aws/aws-sdk-go/service/codegurureviewer/codegururevieweriface" + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/service/codegurureviewer" "github.com/hashicorp/terraform-plugin-log/tflog" "github.com/hashicorp/terraform-provider-aws/internal/conns" "github.com/hashicorp/terraform-provider-aws/internal/logging" @@ -19,12 +18,12 @@ import ( // listTags lists codegurureviewer service tags. // The identifier is typically the Amazon Resource Name (ARN), although // it may also be a different identifier depending on the service. -func listTags(ctx context.Context, conn codegururevieweriface.CodeGuruReviewerAPI, identifier string) (tftags.KeyValueTags, error) { +func listTags(ctx context.Context, conn *codegurureviewer.Client, identifier string, optFns ...func(*codegurureviewer.Options)) (tftags.KeyValueTags, error) { input := &codegurureviewer.ListTagsForResourceInput{ ResourceArn: aws.String(identifier), } - output, err := conn.ListTagsForResourceWithContext(ctx, input) + output, err := conn.ListTagsForResource(ctx, input, optFns...) if err != nil { return tftags.New(ctx, nil), err @@ -36,7 +35,7 @@ func listTags(ctx context.Context, conn codegururevieweriface.CodeGuruReviewerAP // ListTags lists codegurureviewer service tags and set them in Context. // It is called from outside this package. func (p *servicePackage) ListTags(ctx context.Context, meta any, identifier string) error { - tags, err := listTags(ctx, meta.(*conns.AWSClient).CodeGuruReviewerConn(ctx), identifier) + tags, err := listTags(ctx, meta.(*conns.AWSClient).CodeGuruReviewerClient(ctx), identifier) if err != nil { return err @@ -49,21 +48,21 @@ func (p *servicePackage) ListTags(ctx context.Context, meta any, identifier stri return nil } -// map[string]*string handling +// map[string]string handling // Tags returns codegurureviewer service tags. -func Tags(tags tftags.KeyValueTags) map[string]*string { - return aws.StringMap(tags.Map()) +func Tags(tags tftags.KeyValueTags) map[string]string { + return tags.Map() } // KeyValueTags creates tftags.KeyValueTags from codegurureviewer service tags. -func KeyValueTags(ctx context.Context, tags map[string]*string) tftags.KeyValueTags { +func KeyValueTags(ctx context.Context, tags map[string]string) tftags.KeyValueTags { return tftags.New(ctx, tags) } // getTagsIn returns codegurureviewer service tags from Context. // nil is returned if there are no input tags. -func getTagsIn(ctx context.Context) map[string]*string { +func getTagsIn(ctx context.Context) map[string]string { if inContext, ok := tftags.FromContext(ctx); ok { if tags := Tags(inContext.TagsIn.UnwrapOrDefault()); len(tags) > 0 { return tags @@ -74,7 +73,7 @@ func getTagsIn(ctx context.Context) map[string]*string { } // setTagsOut sets codegurureviewer service tags in Context. -func setTagsOut(ctx context.Context, tags map[string]*string) { +func setTagsOut(ctx context.Context, tags map[string]string) { if inContext, ok := tftags.FromContext(ctx); ok { inContext.TagsOut = option.Some(KeyValueTags(ctx, tags)) } @@ -83,7 +82,7 @@ func setTagsOut(ctx context.Context, tags map[string]*string) { // updateTags updates codegurureviewer service tags. // The identifier is typically the Amazon Resource Name (ARN), although // it may also be a different identifier depending on the service. -func updateTags(ctx context.Context, conn codegururevieweriface.CodeGuruReviewerAPI, identifier string, oldTagsMap, newTagsMap any) error { +func updateTags(ctx context.Context, conn *codegurureviewer.Client, identifier string, oldTagsMap, newTagsMap any, optFns ...func(*codegurureviewer.Options)) error { oldTags := tftags.New(ctx, oldTagsMap) newTags := tftags.New(ctx, newTagsMap) @@ -94,10 +93,10 @@ func updateTags(ctx context.Context, conn codegururevieweriface.CodeGuruReviewer if len(removedTags) > 0 { input := &codegurureviewer.UntagResourceInput{ ResourceArn: aws.String(identifier), - TagKeys: aws.StringSlice(removedTags.Keys()), + TagKeys: removedTags.Keys(), } - _, err := conn.UntagResourceWithContext(ctx, input) + _, err := conn.UntagResource(ctx, input, optFns...) if err != nil { return fmt.Errorf("untagging resource (%s): %w", identifier, err) @@ -112,7 +111,7 @@ func updateTags(ctx context.Context, conn codegururevieweriface.CodeGuruReviewer Tags: Tags(updatedTags), } - _, err := conn.TagResourceWithContext(ctx, input) + _, err := conn.TagResource(ctx, input, optFns...) if err != nil { return fmt.Errorf("tagging resource (%s): %w", identifier, err) @@ -125,5 +124,5 @@ func updateTags(ctx context.Context, conn codegururevieweriface.CodeGuruReviewer // UpdateTags updates codegurureviewer service tags. // It is called from outside this package. func (p *servicePackage) UpdateTags(ctx context.Context, meta any, identifier string, oldTags, newTags any) error { - return updateTags(ctx, meta.(*conns.AWSClient).CodeGuruReviewerConn(ctx), identifier, oldTags, newTags) + return updateTags(ctx, meta.(*conns.AWSClient).CodeGuruReviewerClient(ctx), identifier, oldTags, newTags) } From 43aba3493f6d66e334f8c61706e62bc6f078949b Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Fri, 26 Jan 2024 16:48:03 -0500 Subject: [PATCH 26/42] Run 'go get github.com/aws/aws-sdk-go-v2/service/codegurureviewer@v1.22.6 && go mod tidy'. --- go.mod | 1 + go.sum | 2 ++ 2 files changed, 3 insertions(+) diff --git a/go.mod b/go.mod index 54efa1e035d..3f80f23b757 100644 --- a/go.mod +++ b/go.mod @@ -32,6 +32,7 @@ require ( github.com/aws/aws-sdk-go-v2/service/codecommit v1.20.1 github.com/aws/aws-sdk-go-v2/service/codedeploy v1.22.3 github.com/aws/aws-sdk-go-v2/service/codeguruprofiler v1.18.6 + github.com/aws/aws-sdk-go-v2/service/codegurureviewer v1.22.6 github.com/aws/aws-sdk-go-v2/service/codepipeline v1.22.6 github.com/aws/aws-sdk-go-v2/service/codestarconnections v1.22.1 github.com/aws/aws-sdk-go-v2/service/codestarnotifications v1.20.6 diff --git a/go.sum b/go.sum index aef12d1296a..6274621e8d9 100644 --- a/go.sum +++ b/go.sum @@ -84,6 +84,8 @@ github.com/aws/aws-sdk-go-v2/service/codedeploy v1.22.3 h1:KUQmoqL05L+fftMgWLVlk github.com/aws/aws-sdk-go-v2/service/codedeploy v1.22.3/go.mod h1:NqMyFU67rmETeGllV83ilhMC7r+7KnjeEvux4PYakPk= github.com/aws/aws-sdk-go-v2/service/codeguruprofiler v1.18.6 h1:x5j98Y39PwTePUmTdY5XG7OX9+76sKnA9D88xeCtXcc= github.com/aws/aws-sdk-go-v2/service/codeguruprofiler v1.18.6/go.mod h1:iDwHJEm4R1uHugNE19aiKPQ/hYNIPOgLSbbnI6YsPP4= +github.com/aws/aws-sdk-go-v2/service/codegurureviewer v1.22.6 h1:WFFgT8UwGwweIMUgDJZ1RfU1XESBQWNkOo0ohTtlJFE= +github.com/aws/aws-sdk-go-v2/service/codegurureviewer v1.22.6/go.mod h1:70WSgelOSLynj945kxLDv7J10zf0JN0s9HUOl49UdS4= github.com/aws/aws-sdk-go-v2/service/codepipeline v1.22.6 h1:I1sVOeBvwB0k6urXzQNeyHmK4tsqhBI4bZrxPmDRwK8= github.com/aws/aws-sdk-go-v2/service/codepipeline v1.22.6/go.mod h1:Kv4J83SNr492WbgfOKsEkcwqF8Xy10aAaGOh6mYgC8w= github.com/aws/aws-sdk-go-v2/service/codestarconnections v1.22.1 h1:BDHWW50nyoLyg/J+Tkwsh/SdTd1uUOkW9LISJH82JCA= From ee07ecdfb3a8561813a30e2360f570db0d6b8e4d Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Fri, 26 Jan 2024 17:25:55 -0500 Subject: [PATCH 27/42] r/aws_codegurureviewer_repository_association: Migrate to AWS SDK for Go v2. --- .../service/codegurureviewer/exports_test.go | 9 + .../repository_association.go | 218 +++++++++--------- .../codegurureviewer/service_package_gen.go | 2 +- 3 files changed, 115 insertions(+), 114 deletions(-) create mode 100644 internal/service/codegurureviewer/exports_test.go diff --git a/internal/service/codegurureviewer/exports_test.go b/internal/service/codegurureviewer/exports_test.go new file mode 100644 index 00000000000..410f1dd7446 --- /dev/null +++ b/internal/service/codegurureviewer/exports_test.go @@ -0,0 +1,9 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package codegurureviewer + +// Exports for use in tests only. +var ( + ResourceRepositoryAssociation = resourceRepositoryAssociation +) diff --git a/internal/service/codegurureviewer/repository_association.go b/internal/service/codegurureviewer/repository_association.go index a01c8c7e555..4c512f0b2a9 100644 --- a/internal/service/codegurureviewer/repository_association.go +++ b/internal/service/codegurureviewer/repository_association.go @@ -10,16 +10,17 @@ import ( "time" "github.com/YakDriver/regexache" - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/service/codegurureviewer" - "github.com/hashicorp/aws-sdk-go-base/v2/awsv1shim/v2/tfawserr" + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/service/codegurureviewer" + "github.com/aws/aws-sdk-go-v2/service/codegurureviewer/types" "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" + "github.com/hashicorp/terraform-provider-aws/internal/enum" + "github.com/hashicorp/terraform-provider-aws/internal/errs" + "github.com/hashicorp/terraform-provider-aws/internal/errs/sdkdiag" 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" @@ -28,7 +29,7 @@ import ( // @SDKResource("aws_codegurureviewer_repository_association", name="Repository Association") // @Tags(identifierAttribute="id") -func ResourceRepositoryAssociation() *schema.Resource { +func resourceRepositoryAssociation() *schema.Resource { return &schema.Resource{ CreateWithoutTimeout: resourceRepositoryAssociationCreate, ReadWithoutTimeout: resourceRepositoryAssociationRead, @@ -66,18 +67,18 @@ func ResourceRepositoryAssociation() *schema.Resource { } // Show difference if existing state reflects different default type _, defaultEncryptionOption := d.GetChange("kms_key_details.0.encryption_option") - if defaultEncryptionOption.(string) != codegurureviewer.EncryptionOptionAwsOwnedCmk { - return defaultEncryptionOption.(string) == codegurureviewer.EncryptionOptionAwsOwnedCmk + if defaultEncryptionOption := types.EncryptionOption(defaultEncryptionOption.(string)); defaultEncryptionOption != types.EncryptionOptionAoCmk { + return defaultEncryptionOption == types.EncryptionOptionAoCmk } return true }, Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ "encryption_option": { - Type: schema.TypeString, - ForceNew: true, - Optional: true, - ValidateFunc: validation.StringInSlice(codegurureviewer.EncryptionOption_Values(), false), + Type: schema.TypeString, + ForceNew: true, + Optional: true, + ValidateDiagFunc: enum.Validate[types.EncryptionOption](), }, "kms_key_id": { Type: schema.TypeString, @@ -258,45 +259,35 @@ func ResourceRepositoryAssociation() *schema.Resource { names.AttrTags: tftags.TagsSchema(), names.AttrTagsAll: tftags.TagsSchemaComputed(), }, - CustomizeDiff: customdiff.Sequence( - verify.SetTagsDiff, - ), + + CustomizeDiff: verify.SetTagsDiff, } } -const ( - ResNameRepositoryAssociation = "RepositoryAssociation" -) - func resourceRepositoryAssociationCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { var diags diag.Diagnostics + conn := meta.(*conns.AWSClient).CodeGuruReviewerClient(ctx) - conn := meta.(*conns.AWSClient).CodeGuruReviewerConn(ctx) - - in := &codegurureviewer.AssociateRepositoryInput{ + input := &codegurureviewer.AssociateRepositoryInput{ Tags: getTagsIn(ctx), } - in.KMSKeyDetails = expandKMSKeyDetails(d.Get("kms_key_details").([]interface{})) + input.KMSKeyDetails = expandKMSKeyDetails(d.Get("kms_key_details").([]interface{})) if v, ok := d.GetOk("repository"); ok { - in.Repository = expandRepository(v.([]interface{})) + input.Repository = expandRepository(v.([]interface{})) } - out, err := conn.AssociateRepositoryWithContext(ctx, in) + output, err := conn.AssociateRepository(ctx, input) if err != nil { - return create.AppendDiagError(diags, names.CodeGuruReviewer, create.ErrActionCreating, ResNameRepositoryAssociation, d.Get("name").(string), err) + return sdkdiag.AppendErrorf(diags, "creating CodeGuru Repository Association: %s", err) } - if out == nil || out.RepositoryAssociation == nil { - return create.AppendDiagError(diags, names.CodeGuruReviewer, create.ErrActionCreating, ResNameRepositoryAssociation, d.Get("name").(string), errors.New("empty output")) - } - - d.SetId(aws.StringValue(out.RepositoryAssociation.AssociationArn)) + d.SetId(aws.ToString(output.RepositoryAssociation.AssociationArn)) if _, err := waitRepositoryAssociationCreated(ctx, conn, d.Id(), d.Timeout(schema.TimeoutCreate)); err != nil { - return create.AppendDiagError(diags, names.CodeGuruReviewer, create.ErrActionWaitingForCreation, ResNameRepositoryAssociation, d.Id(), err) + return sdkdiag.AppendErrorf(diags, "waiting for CodeGuru Repository Association (%s) create: %s", d.Id(), err) } return append(diags, resourceRepositoryAssociationRead(ctx, d, meta)...) @@ -304,36 +295,32 @@ func resourceRepositoryAssociationCreate(ctx context.Context, d *schema.Resource func resourceRepositoryAssociationRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { var diags diag.Diagnostics - - conn := meta.(*conns.AWSClient).CodeGuruReviewerConn(ctx) + conn := meta.(*conns.AWSClient).CodeGuruReviewerClient(ctx) out, err := findRepositoryAssociationByID(ctx, conn, d.Id()) if !d.IsNewResource() && tfresource.NotFound(err) { - log.Printf("[WARN] CodeGuruReviewer RepositoryAssociation (%s) not found, removing from state", d.Id()) + log.Printf("[WARN] CodeGuru Reviewer Repository Association (%s) not found, removing from state", d.Id()) d.SetId("") return diags } + if err != nil { - return create.AppendDiagError(diags, names.CodeGuruReviewer, create.ErrActionReading, ResNameRepositoryAssociation, d.Id(), err) + return sdkdiag.AppendErrorf(diags, "reading CodeGuru Repository Association (%s): %s", d.Id(), err) } d.Set("arn", out.AssociationArn) d.Set("association_id", out.AssociationId) d.Set("connection_arn", out.ConnectionArn) - if err := d.Set("kms_key_details", flattenKMSKeyDetails(out.KMSKeyDetails)); err != nil { - return create.AppendDiagError(diags, names.CodeGuruReviewer, create.ErrActionSetting, ResNameRepositoryAssociation, d.Id(), err) + return sdkdiag.AppendErrorf(diags, "setting kms_key_details: %s", err) } - d.Set("name", out.Name) d.Set("owner", out.Owner) d.Set("provider_type", out.ProviderType) - if err := d.Set("s3_repository_details", flattenS3RepositoryDetails(out.S3RepositoryDetails)); err != nil { - return create.AppendDiagError(diags, names.CodeGuruReviewer, create.ErrActionSetting, ResNameRepositoryAssociation, d.Id(), err) + return sdkdiag.AppendErrorf(diags, "setting s3_repository_details: %s", err) } - d.Set("state", out.State) d.Set("state_reason", out.StateReason) @@ -350,67 +337,57 @@ func resourceRepositoryAssociationUpdate(ctx context.Context, d *schema.Resource func resourceRepositoryAssociationDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { var diags diag.Diagnostics + conn := meta.(*conns.AWSClient).CodeGuruReviewerClient(ctx) - conn := meta.(*conns.AWSClient).CodeGuruReviewerConn(ctx) - - log.Printf("[INFO] Deleting CodeGuruReviewer RepositoryAssociation %s", d.Id()) - - _, err := conn.DisassociateRepositoryWithContext(ctx, &codegurureviewer.DisassociateRepositoryInput{ + log.Printf("[INFO] Deleting CodeGuru Repository Association %s", d.Id()) + _, err := conn.DisassociateRepository(ctx, &codegurureviewer.DisassociateRepositoryInput{ AssociationArn: aws.String(d.Id()), }) - if tfawserr.ErrCodeEquals(err, codegurureviewer.ErrCodeNotFoundException) { + if errs.IsA[*types.NotFoundException](err) { return diags } if err != nil { - return create.AppendDiagError(diags, names.CodeGuruReviewer, create.ErrActionDeleting, ResNameRepositoryAssociation, d.Id(), err) + return sdkdiag.AppendErrorf(diags, "deleting CodeGuru Repository Association (%s): %s", d.Id(), err) } if _, err := waitRepositoryAssociationDeleted(ctx, conn, d.Id(), d.Timeout(schema.TimeoutDelete)); err != nil { - return create.AppendDiagError(diags, names.CodeGuruReviewer, create.ErrActionWaitingForDeletion, ResNameRepositoryAssociation, d.Id(), err) + return sdkdiag.AppendErrorf(diags, "waiting for CodeGuru Repository Association (%s) delete: %s", d.Id(), err) } return diags } -func waitRepositoryAssociationCreated(ctx context.Context, conn *codegurureviewer.CodeGuruReviewer, id string, timeout time.Duration) (*codegurureviewer.RepositoryAssociation, error) { - stateConf := &retry.StateChangeConf{ - Pending: []string{codegurureviewer.RepositoryAssociationStateAssociating}, - Target: []string{codegurureviewer.RepositoryAssociationStateAssociated}, - Refresh: statusRepositoryAssociation(ctx, conn, id), - Timeout: timeout, - NotFoundChecks: 20, - ContinuousTargetOccurence: 2, +func findRepositoryAssociationByID(ctx context.Context, conn *codegurureviewer.Client, id string) (*types.RepositoryAssociation, error) { + input := &codegurureviewer.DescribeRepositoryAssociationInput{ + AssociationArn: aws.String(id), } - outputRaw, err := stateConf.WaitForStateContext(ctx) - if out, ok := outputRaw.(*codegurureviewer.RepositoryAssociation); ok { - return out, err - } + output, err := conn.DescribeRepositoryAssociation(ctx, input) - return nil, err -} + if errs.IsA[*types.NotFoundException](err) { + return nil, &retry.NotFoundError{ + LastError: err, + LastRequest: input, + } + } -func waitRepositoryAssociationDeleted(ctx context.Context, conn *codegurureviewer.CodeGuruReviewer, id string, timeout time.Duration) (*codegurureviewer.RepositoryAssociation, error) { - stateConf := &retry.StateChangeConf{ - Pending: []string{codegurureviewer.RepositoryAssociationStateDisassociating, codegurureviewer.RepositoryAssociationStateAssociated}, - Target: []string{}, - Refresh: statusRepositoryAssociation(ctx, conn, id), - Timeout: timeout, + if err != nil { + return nil, err } - outputRaw, err := stateConf.WaitForStateContext(ctx) - if out, ok := outputRaw.(*codegurureviewer.RepositoryAssociation); ok { - return out, err + if output == nil || output.RepositoryAssociation == nil { + return nil, tfresource.NewEmptyResultError(input) } - return nil, err + return output.RepositoryAssociation, nil } -func statusRepositoryAssociation(ctx context.Context, conn *codegurureviewer.CodeGuruReviewer, id string) retry.StateRefreshFunc { +func statusRepositoryAssociation(ctx context.Context, conn *codegurureviewer.Client, id string) retry.StateRefreshFunc { return func() (interface{}, string, error) { - out, err := findRepositoryAssociationByID(ctx, conn, id) + output, err := findRepositoryAssociationByID(ctx, conn, id) + if tfresource.NotFound(err) { return nil, "", nil } @@ -419,52 +396,67 @@ func statusRepositoryAssociation(ctx context.Context, conn *codegurureviewer.Cod return nil, "", err } - return out, aws.StringValue(out.State), nil + return output, string(output.State), nil } } -func findRepositoryAssociationByID(ctx context.Context, conn *codegurureviewer.CodeGuruReviewer, id string) (*codegurureviewer.RepositoryAssociation, error) { - in := &codegurureviewer.DescribeRepositoryAssociationInput{ - AssociationArn: aws.String(id), +func waitRepositoryAssociationCreated(ctx context.Context, conn *codegurureviewer.Client, id string, timeout time.Duration) (*types.RepositoryAssociation, error) { + stateConf := &retry.StateChangeConf{ + Pending: enum.Slice(types.RepositoryAssociationStateAssociating), + Target: enum.Slice(types.RepositoryAssociationStateAssociated), + Refresh: statusRepositoryAssociation(ctx, conn, id), + Timeout: timeout, + NotFoundChecks: 20, + ContinuousTargetOccurence: 2, } - out, err := conn.DescribeRepositoryAssociationWithContext(ctx, in) - if tfawserr.ErrCodeEquals(err, codegurureviewer.ErrCodeNotFoundException) { - return nil, &retry.NotFoundError{ - LastError: err, - LastRequest: in, - } + + outputRaw, err := stateConf.WaitForStateContext(ctx) + + if output, ok := outputRaw.(*types.RepositoryAssociation); ok { + tfresource.SetLastError(err, errors.New(aws.ToString(output.StateReason))) + + return output, err } - if err != nil { - return nil, err + return nil, err +} + +func waitRepositoryAssociationDeleted(ctx context.Context, conn *codegurureviewer.Client, id string, timeout time.Duration) (*types.RepositoryAssociation, error) { + stateConf := &retry.StateChangeConf{ + Pending: enum.Slice(types.RepositoryAssociationStateDisassociating, types.RepositoryAssociationStateAssociated), + Target: []string{}, + Refresh: statusRepositoryAssociation(ctx, conn, id), + Timeout: timeout, } - if out == nil || out.RepositoryAssociation == nil { - return nil, tfresource.NewEmptyResultError(in) + outputRaw, err := stateConf.WaitForStateContext(ctx) + + if output, ok := outputRaw.(*types.RepositoryAssociation); ok { + tfresource.SetLastError(err, errors.New(aws.ToString(output.StateReason))) + + return output, err } - return out.RepositoryAssociation, nil + return nil, err } -func flattenKMSKeyDetails(kmsKeyDetails *codegurureviewer.KMSKeyDetails) []interface{} { +func flattenKMSKeyDetails(kmsKeyDetails *types.KMSKeyDetails) []interface{} { if kmsKeyDetails == nil { return nil } - values := map[string]interface{}{} - - if v := kmsKeyDetails.EncryptionOption; v != nil { - values["encryption_option"] = aws.StringValue(v) + values := map[string]interface{}{ + "encryption_option": kmsKeyDetails.EncryptionOption, } if v := kmsKeyDetails.KMSKeyId; v != nil { - values["kms_key_id"] = aws.StringValue(v) + values["kms_key_id"] = aws.ToString(v) } return []interface{}{values} } -func flattenS3RepositoryDetails(s3RepositoryDetails *codegurureviewer.S3RepositoryDetails) []interface{} { +func flattenS3RepositoryDetails(s3RepositoryDetails *types.S3RepositoryDetails) []interface{} { if s3RepositoryDetails == nil { return nil } @@ -472,7 +464,7 @@ func flattenS3RepositoryDetails(s3RepositoryDetails *codegurureviewer.S3Reposito values := map[string]interface{}{} if v := s3RepositoryDetails.BucketName; v != nil { - values["bucket_name"] = aws.StringValue(v) + values["bucket_name"] = aws.ToString(v) } if v := s3RepositoryDetails.CodeArtifacts; v != nil { @@ -482,7 +474,7 @@ func flattenS3RepositoryDetails(s3RepositoryDetails *codegurureviewer.S3Reposito return []interface{}{values} } -func flattenCodeArtifacts(apiObject *codegurureviewer.CodeArtifacts) map[string]interface{} { +func flattenCodeArtifacts(apiObject *types.CodeArtifacts) map[string]interface{} { if apiObject == nil { return nil } @@ -490,17 +482,17 @@ func flattenCodeArtifacts(apiObject *codegurureviewer.CodeArtifacts) map[string] m := map[string]interface{}{} if v := apiObject.BuildArtifactsObjectKey; v != nil { - m["build_artifacts_object_key"] = aws.StringValue(v) + m["build_artifacts_object_key"] = aws.ToString(v) } if v := apiObject.SourceCodeArtifactsObjectKey; v != nil { - m["source_code_artifacts_object_key"] = aws.StringValue(v) + m["source_code_artifacts_object_key"] = aws.ToString(v) } return m } -func expandKMSKeyDetails(kmsKeyDetails []interface{}) *codegurureviewer.KMSKeyDetails { +func expandKMSKeyDetails(kmsKeyDetails []interface{}) *types.KMSKeyDetails { if len(kmsKeyDetails) == 0 || kmsKeyDetails[0] == nil { return nil } @@ -510,10 +502,10 @@ func expandKMSKeyDetails(kmsKeyDetails []interface{}) *codegurureviewer.KMSKeyDe return nil } - result := &codegurureviewer.KMSKeyDetails{} + result := &types.KMSKeyDetails{} if v, ok := tfMap["encryption_option"].(string); ok && v != "" { - result.EncryptionOption = aws.String(v) + result.EncryptionOption = types.EncryptionOption(v) } if v, ok := tfMap["kms_key_id"].(string); ok && v != "" { @@ -523,7 +515,7 @@ func expandKMSKeyDetails(kmsKeyDetails []interface{}) *codegurureviewer.KMSKeyDe return result } -func expandCodeCommitRepository(repository []interface{}) *codegurureviewer.CodeCommitRepository { +func expandCodeCommitRepository(repository []interface{}) *types.CodeCommitRepository { if len(repository) == 0 || repository[0] == nil { return nil } @@ -533,7 +525,7 @@ func expandCodeCommitRepository(repository []interface{}) *codegurureviewer.Code return nil } - result := &codegurureviewer.CodeCommitRepository{} + result := &types.CodeCommitRepository{} if v, ok := tfMap["name"].(string); ok && v != "" { result.Name = aws.String(v) @@ -542,7 +534,7 @@ func expandCodeCommitRepository(repository []interface{}) *codegurureviewer.Code return result } -func expandRepository(repository []interface{}) *codegurureviewer.Repository { +func expandRepository(repository []interface{}) *types.Repository { if len(repository) == 0 || repository[0] == nil { return nil } @@ -552,7 +544,7 @@ func expandRepository(repository []interface{}) *codegurureviewer.Repository { return nil } - result := &codegurureviewer.Repository{} + result := &types.Repository{} if v, ok := tfMap["bitbucket"]; ok { result.Bitbucket = expandThirdPartySourceRepository(v.([]interface{})) @@ -570,7 +562,7 @@ func expandRepository(repository []interface{}) *codegurureviewer.Repository { return result } -func expandS3Repository(repository []interface{}) *codegurureviewer.S3Repository { +func expandS3Repository(repository []interface{}) *types.S3Repository { if len(repository) == 0 || repository[0] == nil { return nil } @@ -580,7 +572,7 @@ func expandS3Repository(repository []interface{}) *codegurureviewer.S3Repository return nil } - result := &codegurureviewer.S3Repository{} + result := &types.S3Repository{} if v, ok := tfMap["bucket_name"].(string); ok && v != "" { result.BucketName = aws.String(v) @@ -593,7 +585,7 @@ func expandS3Repository(repository []interface{}) *codegurureviewer.S3Repository return result } -func expandThirdPartySourceRepository(repository []interface{}) *codegurureviewer.ThirdPartySourceRepository { +func expandThirdPartySourceRepository(repository []interface{}) *types.ThirdPartySourceRepository { if len(repository) == 0 || repository[0] == nil { return nil } @@ -603,7 +595,7 @@ func expandThirdPartySourceRepository(repository []interface{}) *codegurureviewe return nil } - result := &codegurureviewer.ThirdPartySourceRepository{} + result := &types.ThirdPartySourceRepository{} if v, ok := tfMap["connection_arn"].(string); ok && v != "" { result.ConnectionArn = aws.String(v) diff --git a/internal/service/codegurureviewer/service_package_gen.go b/internal/service/codegurureviewer/service_package_gen.go index fcc1146dae1..074488edee0 100644 --- a/internal/service/codegurureviewer/service_package_gen.go +++ b/internal/service/codegurureviewer/service_package_gen.go @@ -29,7 +29,7 @@ func (p *servicePackage) SDKDataSources(ctx context.Context) []*types.ServicePac func (p *servicePackage) SDKResources(ctx context.Context) []*types.ServicePackageSDKResource { return []*types.ServicePackageSDKResource{ { - Factory: ResourceRepositoryAssociation, + Factory: resourceRepositoryAssociation, TypeName: "aws_codegurureviewer_repository_association", Name: "Repository Association", Tags: &types.ServicePackageResourceTags{ From c837dfd58af922a5509de1974846738d3712ac8f Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Fri, 26 Jan 2024 17:33:29 -0500 Subject: [PATCH 28/42] codegurureviewer: Migrate sweepers to AWS SDK for Go v2. --- .../service/codegurureviewer/list_pages.go | 30 +++++++++++++++++++ internal/service/codegurureviewer/sweep.go | 24 +++++++-------- 2 files changed, 41 insertions(+), 13 deletions(-) create mode 100644 internal/service/codegurureviewer/list_pages.go diff --git a/internal/service/codegurureviewer/list_pages.go b/internal/service/codegurureviewer/list_pages.go new file mode 100644 index 00000000000..02f5436c095 --- /dev/null +++ b/internal/service/codegurureviewer/list_pages.go @@ -0,0 +1,30 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package codegurureviewer + +import ( + "context" + + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/service/codegurureviewer" +) + +// Custom CodeGuruReviewer service lister functions using the same format as generated code. + +func listRepositoryAssociationsPages(ctx context.Context, conn *codegurureviewer.Client, input *codegurureviewer.ListRepositoryAssociationsInput, fn func(*codegurureviewer.ListRepositoryAssociationsOutput, bool) bool) error { + for { + output, err := conn.ListRepositoryAssociations(ctx, input) + if err != nil { + return err + } + + lastPage := aws.ToString(output.NextToken) == "" + if !fn(output, lastPage) || lastPage { + break + } + + input.NextToken = output.NextToken + } + return nil +} diff --git a/internal/service/codegurureviewer/sweep.go b/internal/service/codegurureviewer/sweep.go index afa5e4a350b..600445172dd 100644 --- a/internal/service/codegurureviewer/sweep.go +++ b/internal/service/codegurureviewer/sweep.go @@ -7,11 +7,11 @@ import ( "fmt" "log" - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/service/codegurureviewer" + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/service/codegurureviewer" "github.com/hashicorp/terraform-plugin-testing/helper/resource" "github.com/hashicorp/terraform-provider-aws/internal/sweep" - "github.com/hashicorp/terraform-provider-aws/internal/sweep/awsv1" + "github.com/hashicorp/terraform-provider-aws/internal/sweep/awsv2" ) func RegisterSweepers() { @@ -28,20 +28,18 @@ func sweepAssociations(region string) error { return fmt.Errorf("error getting client: %w", err) } input := &codegurureviewer.ListRepositoryAssociationsInput{} - conn := client.CodeGuruReviewerConn(ctx) - + conn := client.CodeGuruReviewerClient(ctx) sweepResources := make([]sweep.Sweepable, 0) - err = conn.ListRepositoryAssociationsPagesWithContext(ctx, input, func(page *codegurureviewer.ListRepositoryAssociationsOutput, lastPage bool) bool { + err = listRepositoryAssociationsPages(ctx, conn, input, func(page *codegurureviewer.ListRepositoryAssociationsOutput, lastPage bool) bool { if page == nil { return !lastPage } for _, v := range page.RepositoryAssociationSummaries { - r := ResourceRepositoryAssociation() + r := resourceRepositoryAssociation() d := r.Data(nil) - - d.SetId(aws.StringValue(v.Name)) + d.SetId(aws.ToString(v.Name)) sweepResources = append(sweepResources, sweep.NewSweepResource(r, d, client)) } @@ -49,19 +47,19 @@ func sweepAssociations(region string) error { return !lastPage }) - if awsv1.SkipSweepError(err) { - log.Printf("[WARN] Skipping CodeGuruReviewer Association sweep for %s: %s", region, err) + if awsv2.SkipSweepError(err) { + log.Printf("[WARN] Skipping CodeGuru Reviewer Repository Association sweep for %s: %s", region, err) return nil } if err != nil { - return fmt.Errorf("error listing CodeGuruReviewer Associations (%s): %w", region, err) + return fmt.Errorf("error listing CodeGuru Reviewer Repository Associations (%s): %w", region, err) } err = sweep.SweepOrchestrator(ctx, sweepResources) if err != nil { - return fmt.Errorf("error sweeping CodeGuruReviewer Associations (%s): %w", region, err) + return fmt.Errorf("error sweeping CodeGuru Reviewer Repository Associations (%s): %w", region, err) } return nil From 5411449604d343e205e3e52c0756687457f098ce Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Fri, 26 Jan 2024 17:39:14 -0500 Subject: [PATCH 29/42] codegurureviewer: Migrate acceptance tests to AWS SDK for Go v2. --- .../service/codegurureviewer/exports_test.go | 2 + .../repository_association_test.go | 78 +++++++++---------- names/names.go | 1 + 3 files changed, 38 insertions(+), 43 deletions(-) diff --git a/internal/service/codegurureviewer/exports_test.go b/internal/service/codegurureviewer/exports_test.go index 410f1dd7446..36faabebfef 100644 --- a/internal/service/codegurureviewer/exports_test.go +++ b/internal/service/codegurureviewer/exports_test.go @@ -6,4 +6,6 @@ package codegurureviewer // Exports for use in tests only. var ( ResourceRepositoryAssociation = resourceRepositoryAssociation + + FindRepositoryAssociationByID = findRepositoryAssociationByID ) diff --git a/internal/service/codegurureviewer/repository_association_test.go b/internal/service/codegurureviewer/repository_association_test.go index 925160fe424..2d238cb3825 100644 --- a/internal/service/codegurureviewer/repository_association_test.go +++ b/internal/service/codegurureviewer/repository_association_test.go @@ -5,21 +5,19 @@ package codegurureviewer_test import ( "context" - "errors" "fmt" "testing" "github.com/YakDriver/regexache" - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/service/codegurureviewer" - "github.com/hashicorp/aws-sdk-go-base/v2/awsv1shim/v2/tfawserr" + "github.com/aws/aws-sdk-go-v2/service/codegurureviewer" + "github.com/aws/aws-sdk-go-v2/service/codegurureviewer/types" sdkacctest "github.com/hashicorp/terraform-plugin-testing/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" tfcodegurureviewer "github.com/hashicorp/terraform-provider-aws/internal/service/codegurureviewer" + "github.com/hashicorp/terraform-provider-aws/internal/tfresource" "github.com/hashicorp/terraform-provider-aws/names" ) @@ -27,17 +25,17 @@ import ( // However, this has been manually tested successfully func TestAccCodeGuruReviewerRepositoryAssociation_basic(t *testing.T) { ctx := acctest.Context(t) - var repositoryassociation codegurureviewer.DescribeRepositoryAssociationOutput + var repositoryassociation types.RepositoryAssociation rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resourceName := "aws_codegurureviewer_repository_association.test" resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { acctest.PreCheck(ctx, t) - acctest.PreCheckPartitionHasService(t, codegurureviewer.EndpointsID) + acctest.PreCheckPartitionHasService(t, names.CodeGuruReviewerEndpointID) testAccPreCheck(ctx, t) }, - ErrorCheck: acctest.ErrorCheck(t, codegurureviewer.EndpointsID), + ErrorCheck: acctest.ErrorCheck(t, names.CodeGuruReviewerEndpointID), ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, CheckDestroy: testAccCheckRepositoryAssociationDestroy(ctx), Steps: []resource.TestStep{ @@ -62,17 +60,17 @@ func TestAccCodeGuruReviewerRepositoryAssociation_basic(t *testing.T) { func TestAccCodeGuruReviewerRepositoryAssociation_KMSKey(t *testing.T) { ctx := acctest.Context(t) - var repositoryassociation codegurureviewer.DescribeRepositoryAssociationOutput + var repositoryassociation types.RepositoryAssociation rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resourceName := "aws_codegurureviewer_repository_association.test" resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { acctest.PreCheck(ctx, t) - acctest.PreCheckPartitionHasService(t, codegurureviewer.EndpointsID) + acctest.PreCheckPartitionHasService(t, names.CodeGuruReviewerEndpointID) testAccPreCheck(ctx, t) }, - ErrorCheck: acctest.ErrorCheck(t, codegurureviewer.EndpointsID), + ErrorCheck: acctest.ErrorCheck(t, names.CodeGuruReviewerEndpointID), ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, CheckDestroy: testAccCheckRepositoryAssociationDestroy(ctx), Steps: []resource.TestStep{ @@ -97,17 +95,17 @@ func TestAccCodeGuruReviewerRepositoryAssociation_KMSKey(t *testing.T) { func TestAccCodeGuruReviewerRepositoryAssociation_S3Repository(t *testing.T) { ctx := acctest.Context(t) - var repositoryassociation codegurureviewer.DescribeRepositoryAssociationOutput - rName := "codeguru-reviewer-" + sdkacctest.RandString(10) + var repositoryassociation types.RepositoryAssociation + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resourceName := "aws_codegurureviewer_repository_association.test" resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { acctest.PreCheck(ctx, t) - acctest.PreCheckPartitionHasService(t, codegurureviewer.EndpointsID) + acctest.PreCheckPartitionHasService(t, names.CodeGuruReviewerEndpointID) testAccPreCheck(ctx, t) }, - ErrorCheck: acctest.ErrorCheck(t, codegurureviewer.EndpointsID), + ErrorCheck: acctest.ErrorCheck(t, names.CodeGuruReviewerEndpointID), ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, CheckDestroy: testAccCheckRepositoryAssociationDestroy(ctx), Steps: []resource.TestStep{ @@ -133,17 +131,17 @@ func TestAccCodeGuruReviewerRepositoryAssociation_S3Repository(t *testing.T) { func TestAccCodeGuruReviewerRepositoryAssociation_tags(t *testing.T) { ctx := acctest.Context(t) - var repositoryassociation codegurureviewer.DescribeRepositoryAssociationOutput + var repositoryassociation types.RepositoryAssociation resourceName := "aws_codegurureviewer_repository_association.test" rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { acctest.PreCheck(ctx, t) - acctest.PreCheckPartitionHasService(t, codegurureviewer.EndpointsID) + acctest.PreCheckPartitionHasService(t, names.CodeGuruReviewerEndpointID) testAccPreCheck(ctx, t) }, - ErrorCheck: acctest.ErrorCheck(t, codegurureviewer.EndpointsID), + ErrorCheck: acctest.ErrorCheck(t, names.CodeGuruReviewerEndpointID), ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, CheckDestroy: testAccCheckRepositoryAssociationDestroy(ctx), Steps: []resource.TestStep{ @@ -178,16 +176,16 @@ func TestAccCodeGuruReviewerRepositoryAssociation_tags(t *testing.T) { func TestAccCodeGuruReviewerRepositoryAssociation_disappears(t *testing.T) { ctx := acctest.Context(t) - var repositoryassociation codegurureviewer.DescribeRepositoryAssociationOutput + var repositoryassociation types.RepositoryAssociation rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resourceName := "aws_codegurureviewer_repository_association.test" resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { acctest.PreCheck(ctx, t) - acctest.PreCheckPartitionHasService(t, codegurureviewer.EndpointsID) + acctest.PreCheckPartitionHasService(t, names.CodeGuruReviewerEndpointID) testAccPreCheck(ctx, t) }, - ErrorCheck: acctest.ErrorCheck(t, codegurureviewer.EndpointsID), + ErrorCheck: acctest.ErrorCheck(t, names.CodeGuruReviewerEndpointID), ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, CheckDestroy: testAccCheckRepositoryAssociationDestroy(ctx), Steps: []resource.TestStep{ @@ -205,62 +203,56 @@ func TestAccCodeGuruReviewerRepositoryAssociation_disappears(t *testing.T) { func testAccCheckRepositoryAssociationDestroy(ctx context.Context) resource.TestCheckFunc { return func(s *terraform.State) error { - conn := acctest.Provider.Meta().(*conns.AWSClient).CodeGuruReviewerConn(ctx) + conn := acctest.Provider.Meta().(*conns.AWSClient).CodeGuruReviewerClient(ctx) for _, rs := range s.RootModule().Resources { if rs.Type != "aws_codegurureviewer_repository_association" { continue } - input := &codegurureviewer.DescribeRepositoryAssociationInput{ - AssociationArn: aws.String(rs.Primary.ID), + _, err := tfcodegurureviewer.FindRepositoryAssociationByID(ctx, conn, rs.Primary.ID) + + if tfresource.NotFound(err) { + continue } - _, err := conn.DescribeRepositoryAssociationWithContext(ctx, input) + if err != nil { - if tfawserr.ErrCodeEquals(err, codegurureviewer.ErrCodeNotFoundException) { - return nil - } return err } - return fmt.Errorf("CodeGuru Reviewer Association Connection %s still exists", rs.Primary.ID) + return fmt.Errorf("CodeGuru Reviewer Repository Association (%s) still exists", rs.Primary.ID) } return nil } } -func testAccCheckRepositoryAssociationExists(ctx context.Context, name string, repositoryassociation *codegurureviewer.DescribeRepositoryAssociationOutput) resource.TestCheckFunc { +func testAccCheckRepositoryAssociationExists(ctx context.Context, n string, v *types.RepositoryAssociation) resource.TestCheckFunc { return func(s *terraform.State) error { - rs, ok := s.RootModule().Resources[name] + rs, ok := s.RootModule().Resources[n] if !ok { - return create.Error(names.CodeGuruReviewer, create.ErrActionCheckingExistence, tfcodegurureviewer.ResNameRepositoryAssociation, name, errors.New("not found")) + return fmt.Errorf("Not found: %s", n) } - if rs.Primary.ID == "" { - return create.Error(names.CodeGuruReviewer, create.ErrActionCheckingExistence, tfcodegurureviewer.ResNameRepositoryAssociation, name, errors.New("not set")) - } + conn := acctest.Provider.Meta().(*conns.AWSClient).CodeGuruReviewerClient(ctx) - conn := acctest.Provider.Meta().(*conns.AWSClient).CodeGuruReviewerConn(ctx) - resp, err := conn.DescribeRepositoryAssociationWithContext(ctx, &codegurureviewer.DescribeRepositoryAssociationInput{ - AssociationArn: aws.String(rs.Primary.ID), - }) + output, err := tfcodegurureviewer.FindRepositoryAssociationByID(ctx, conn, rs.Primary.ID) if err != nil { - return create.Error(names.CodeGuruReviewer, create.ErrActionCheckingExistence, tfcodegurureviewer.ResNameRepositoryAssociation, rs.Primary.ID, err) + return err } - *repositoryassociation = *resp + *v = *output return nil } } func testAccPreCheck(ctx context.Context, t *testing.T) { - conn := acctest.Provider.Meta().(*conns.AWSClient).CodeGuruReviewerConn(ctx) + conn := acctest.Provider.Meta().(*conns.AWSClient).CodeGuruReviewerClient(ctx) input := &codegurureviewer.ListRepositoryAssociationsInput{} - _, err := conn.ListRepositoryAssociationsWithContext(ctx, input) + _, err := conn.ListRepositoryAssociations(ctx, input) if acctest.PreCheckSkipError(err) { t.Skipf("skipping acceptance testing: %s", err) diff --git a/names/names.go b/names/names.go index a3010eb67f9..26a68133fec 100644 --- a/names/names.go +++ b/names/names.go @@ -44,6 +44,7 @@ const ( CodeCommitEndpointID = "codecommit" CodeDeployEndpointID = "codedeploy" CodeGuruProfilerEndpointID = "codeguru-profiler" + CodeGuruReviewerEndpointID = "codeguru-reviewer" CodePipelineEndpointID = "codepipeline" CodeStarConnectionsEndpointID = "codestar-connections" CodeStarNotificationsEndpointID = "codestar-notifications" From d895d4757d83ca45e61a636a9007fdd97ffe97c3 Mon Sep 17 00:00:00 2001 From: Sharon Nam Date: Fri, 26 Jan 2024 16:07:11 -0800 Subject: [PATCH 30/42] Use Intent structs --- internal/service/lexv2models/slot.go | 150 +++++---------------------- 1 file changed, 28 insertions(+), 122 deletions(-) diff --git a/internal/service/lexv2models/slot.go b/internal/service/lexv2models/slot.go index 46813b6790d..7d1a45f52c4 100644 --- a/internal/service/lexv2models/slot.go +++ b/internal/service/lexv2models/slot.go @@ -115,7 +115,7 @@ func (r *resourceSlot) Schema(ctx context.Context, req resource.SchemaRequest, r Validators: []validator.List{ listvalidator.SizeAtMost(1), }, - CustomType: fwtypes.NewListNestedObjectTypeOf[CustomPayloadData](ctx), + CustomType: fwtypes.NewListNestedObjectTypeOf[CustomPayload](ctx), NestedObject: schema.NestedBlockObject{ Attributes: map[string]schema.Attribute{ "value": schema.StringAttribute{ @@ -128,7 +128,7 @@ func (r *resourceSlot) Schema(ctx context.Context, req resource.SchemaRequest, r Validators: []validator.List{ listvalidator.SizeAtMost(1), }, - CustomType: fwtypes.NewListNestedObjectTypeOf[ImageResponseCardData](ctx), + CustomType: fwtypes.NewListNestedObjectTypeOf[ImageResponseCard](ctx), NestedObject: schema.NestedBlockObject{ Attributes: map[string]schema.Attribute{ "image_url": schema.StringAttribute{ @@ -143,7 +143,7 @@ func (r *resourceSlot) Schema(ctx context.Context, req resource.SchemaRequest, r }, Blocks: map[string]schema.Block{ "button": schema.ListNestedBlock{ - CustomType: fwtypes.NewListNestedObjectTypeOf[ButtonData](ctx), + CustomType: fwtypes.NewListNestedObjectTypeOf[Button](ctx), NestedObject: schema.NestedBlockObject{ Attributes: map[string]schema.Attribute{ "text": schema.StringAttribute{ @@ -162,7 +162,7 @@ func (r *resourceSlot) Schema(ctx context.Context, req resource.SchemaRequest, r Validators: []validator.List{ listvalidator.SizeAtMost(1), }, - CustomType: fwtypes.NewListNestedObjectTypeOf[PlainTextMessageData](ctx), + CustomType: fwtypes.NewListNestedObjectTypeOf[PlainTextMessage](ctx), NestedObject: schema.NestedBlockObject{ Attributes: map[string]schema.Attribute{ "value": schema.StringAttribute{ @@ -175,7 +175,7 @@ func (r *resourceSlot) Schema(ctx context.Context, req resource.SchemaRequest, r Validators: []validator.List{ listvalidator.SizeAtMost(1), }, - CustomType: fwtypes.NewListNestedObjectTypeOf[SSMLMessageData](ctx), + CustomType: fwtypes.NewListNestedObjectTypeOf[SSMLMessage](ctx), NestedObject: schema.NestedBlockObject{ Attributes: map[string]schema.Attribute{ "value": schema.StringAttribute{ @@ -191,18 +191,18 @@ func (r *resourceSlot) Schema(ctx context.Context, req resource.SchemaRequest, r Validators: []validator.List{ listvalidator.SizeAtLeast(1), }, - CustomType: fwtypes.NewListNestedObjectTypeOf[MessageGroupData](ctx), + CustomType: fwtypes.NewListNestedObjectTypeOf[MessageGroup](ctx), NestedObject: schema.NestedBlockObject{ Blocks: map[string]schema.Block{ "message": schema.ListNestedBlock{ Validators: []validator.List{ listvalidator.SizeBetween(1, 1), }, - CustomType: fwtypes.NewListNestedObjectTypeOf[MessageData](ctx), + CustomType: fwtypes.NewListNestedObjectTypeOf[Message](ctx), NestedObject: messageNBO, }, "variation": schema.ListNestedBlock{ - CustomType: fwtypes.NewListNestedObjectTypeOf[MessageData](ctx), + CustomType: fwtypes.NewListNestedObjectTypeOf[Message](ctx), NestedObject: messageNBO, }, }, @@ -213,7 +213,7 @@ func (r *resourceSlot) Schema(ctx context.Context, req resource.SchemaRequest, r Validators: []validator.List{ listvalidator.SizeBetween(1, 1), }, - CustomType: fwtypes.NewListNestedObjectTypeOf[AllowedInputTypesData](ctx), + CustomType: fwtypes.NewListNestedObjectTypeOf[AllowedInputTypes](ctx), NestedObject: schema.NestedBlockObject{ Attributes: map[string]schema.Attribute{ "allow_audio_input": schema.BoolAttribute{ @@ -230,7 +230,7 @@ func (r *resourceSlot) Schema(ctx context.Context, req resource.SchemaRequest, r Validators: []validator.List{ listvalidator.SizeAtMost(1), }, - CustomType: fwtypes.NewListNestedObjectTypeOf[AudioSpecificationData](ctx), + CustomType: fwtypes.NewListNestedObjectTypeOf[AudioSpecification](ctx), NestedObject: schema.NestedBlockObject{ Attributes: map[string]schema.Attribute{ "end_timeout_ms": schema.Int64Attribute{ @@ -253,7 +253,7 @@ func (r *resourceSlot) Schema(ctx context.Context, req resource.SchemaRequest, r Validators: []validator.List{ listvalidator.SizeAtMost(1), }, - CustomType: fwtypes.NewListNestedObjectTypeOf[DTMFSpecificationData](ctx), + CustomType: fwtypes.NewListNestedObjectTypeOf[DTMFSpecification](ctx), NestedObject: schema.NestedBlockObject{ Attributes: map[string]schema.Attribute{ "deletion_character": schema.StringAttribute{ @@ -294,7 +294,7 @@ func (r *resourceSlot) Schema(ctx context.Context, req resource.SchemaRequest, r Validators: []validator.List{ listvalidator.SizeAtMost(1), }, - CustomType: fwtypes.NewListNestedObjectTypeOf[AudioAndDTMFInputSpecificationData](ctx), + CustomType: fwtypes.NewListNestedObjectTypeOf[AudioAndDTMFInputSpecification](ctx), NestedObject: schema.NestedBlockObject{ Attributes: map[string]schema.Attribute{ "start_timeout_ms": schema.Int64Attribute{ @@ -315,7 +315,7 @@ func (r *resourceSlot) Schema(ctx context.Context, req resource.SchemaRequest, r Validators: []validator.List{ listvalidator.SizeAtMost(1), }, - CustomType: fwtypes.NewListNestedObjectTypeOf[TextInputSpecificationData](ctx), + CustomType: fwtypes.NewListNestedObjectTypeOf[TextInputSpecification](ctx), NestedObject: schema.NestedBlockObject{ Attributes: map[string]schema.Attribute{ "start_timeout_ms": schema.Int64Attribute{ @@ -329,7 +329,7 @@ func (r *resourceSlot) Schema(ctx context.Context, req resource.SchemaRequest, r } promptAttemptsSpecificationLNB := schema.SetNestedBlock{ - CustomType: fwtypes.NewSetNestedObjectTypeOf[PromptAttemptsSpecificationData](ctx), + CustomType: fwtypes.NewSetNestedObjectTypeOf[PromptAttemptsSpecification](ctx), NestedObject: schema.NestedBlockObject{ Attributes: map[string]schema.Attribute{ "map_block_key": schema.StringAttribute{ @@ -352,7 +352,7 @@ func (r *resourceSlot) Schema(ctx context.Context, req resource.SchemaRequest, r Validators: []validator.List{ listvalidator.SizeBetween(1, 1), }, - CustomType: fwtypes.NewListNestedObjectTypeOf[PromptSpecificationData](ctx), + CustomType: fwtypes.NewListNestedObjectTypeOf[PromptSpecification](ctx), NestedObject: schema.NestedBlockObject{ Attributes: map[string]schema.Attribute{ "allow_interrupt": schema.BoolAttribute{ @@ -369,14 +369,14 @@ func (r *resourceSlot) Schema(ctx context.Context, req resource.SchemaRequest, r }, }, Blocks: map[string]schema.Block{ - "message_groups": messageGroupLNB, + "message_group": messageGroupLNB, "prompt_attempts_specification": promptAttemptsSpecificationLNB, }, }, } sampleUtteranceLNB := schema.ListNestedBlock{ - CustomType: fwtypes.NewListNestedObjectTypeOf[SampleUtteranceData](ctx), + CustomType: fwtypes.NewListNestedObjectTypeOf[SampleUtterance](ctx), NestedObject: schema.NestedBlockObject{ Attributes: map[string]schema.Attribute{ "utterance": schema.StringAttribute{ @@ -399,7 +399,7 @@ func (r *resourceSlot) Schema(ctx context.Context, req resource.SchemaRequest, r } responseSpecificationLNB := schema.ListNestedBlock{ - CustomType: fwtypes.NewListNestedObjectTypeOf[ResponseSpecificationData](ctx), + CustomType: fwtypes.NewListNestedObjectTypeOf[ResponseSpecification](ctx), NestedObject: schema.NestedBlockObject{ Attributes: map[string]schema.Attribute{ "allow_interrupt": schema.BoolAttribute{ @@ -407,7 +407,7 @@ func (r *resourceSlot) Schema(ctx context.Context, req resource.SchemaRequest, r }, }, Blocks: map[string]schema.Block{ - "message_groups": messageGroupLNB, + "message_group": messageGroupLNB, }, }, } @@ -427,7 +427,7 @@ func (r *resourceSlot) Schema(ctx context.Context, req resource.SchemaRequest, r }, }, Blocks: map[string]schema.Block{ - "message_groups": messageGroupLNB, + "message_group": messageGroupLNB, }, }, } @@ -766,123 +766,29 @@ type DefaultValueData struct { DefaultValue types.String `tfsdk:"default_value"` } -type PromptSpecificationData struct { - AllowInterrupt types.Bool `tfsdk:"allow_interrupt"` - MaxRetries types.Int64 `tfsdk:"max_retries"` - MessageGroup fwtypes.ListNestedObjectValueOf[MessageGroupData] `tfsdk:"message_groups"` - MessageSelectionStrategy fwtypes.StringEnum[awstypes.MessageSelectionStrategy] `tfsdk:"message_selection_strategy"` - PromptAttemptsSpecification fwtypes.SetNestedObjectValueOf[PromptAttemptSpecificationData] `tfsdk:"prompt_attempts_specification"` -} -type PromptAttemptSpecificationData struct { - AllowedInputTypes fwtypes.ListNestedObjectValueOf[AllowedInputTypesData] `tfsdk:"allowed_input_types"` - AllowInterrupt types.Bool `tfsdk:"allow_interrupt"` - MapBlockKey fwtypes.StringEnum[PromptAttemptsType] `tfsdk:"map_block_key"` - AudioAndDTMFInputSpecification fwtypes.ListNestedObjectValueOf[AudioAndDTMFInputSpecificationData] `tfsdk:"audio_and_dtmf_input_specification"` - TextInputSpecification fwtypes.ListNestedObjectValueOf[TextInputSpecificationData] `tfsdk:"text_input_specification"` -} - -type DTMFSpecificationData struct { - EndCharacter types.String `tfsdk:"end_character"` - EndTimeoutMs types.Int64 `tfsdk:"end_timeout_ms"` - DeletionCharacter types.String `tfsdk:"deletion_character"` - MaxLength types.Int64 `tfsdk:"max_length"` -} - -type TextInputSpecificationData struct { - StartTimeoutMs types.Int64 `tfsdk:"start_timeout_ms"` -} - -type AllowedInputTypesData struct { - AllowAudioInput types.Bool `tfsdk:"allow_audio_input"` - AllowDTMFInput types.Bool `tfsdk:"allow_dtmf_input"` -} - -type AudioAndDTMFInputSpecificationData struct { - AudioSpecification fwtypes.ListNestedObjectValueOf[AudioSpecificationData] `tfsdk:"audio_specification"` - StartTimeoutMs types.Int64 `tfsdk:"start_timeout_ms"` - DTMFSpecification fwtypes.ListNestedObjectValueOf[DTMFSpecificationData] `tfsdk:"dtmf_specification"` -} - -type AudioSpecificationData struct { - EndTimeoutMs types.Int64 `tfsdk:"end_timeout_ms"` - MaxLengthMs types.Int64 `tfsdk:"max_length_ms"` -} - -type CustomPayloadData struct { - Value types.String `tfsdk:"value"` -} - -type ImageResponseCardData struct { - Title types.String `tfsdk:"title"` - Button fwtypes.ListNestedObjectValueOf[ButtonData] `tfsdk:"buttons"` - ImageURL types.String `tfsdk:"image_url"` - Subtitle types.String `tfsdk:"subtitle"` -} - -type ButtonData struct { - Text types.String `tfsdk:"text"` - Value types.String `tfsdk:"value"` -} - -type PlainTextMessageData struct { - Value types.String `tfsdk:"value"` -} - -type SSMLMessageData struct { - Value types.String `tfsdk:"value"` -} -type MessageGroupData struct { - Message fwtypes.ListNestedObjectValueOf[MessageData] `tfsdk:"message"` - Variations fwtypes.ListNestedObjectValueOf[MessageData] `tfsdk:"variations"` -} - -type MessageData struct { - CustomPayload fwtypes.ListNestedObjectValueOf[CustomPayloadData] `tfsdk:"custom_payload"` - ImageResponseCard fwtypes.ListNestedObjectValueOf[ImageResponseCardData] `tfsdk:"image_response_card"` - PlainTextMessage fwtypes.ListNestedObjectValueOf[PlainTextMessageData] `tfsdk:"plain_text_message"` - SSMLMessage fwtypes.ListNestedObjectValueOf[SSMLMessageData] `tfsdk:"ssml_message"` -} - -type PromptAttemptsSpecificationData struct { - AllowedInputTypes fwtypes.ListNestedObjectValueOf[AllowedInputTypes] `tfsdk:"allowed_input_types"` - AllowInterrupt types.Bool `tfsdk:"allow_interrupt"` - AudioAndDTMFInputSpecification fwtypes.ListNestedObjectValueOf[AudioAndDTMFInputSpecification] `tfsdk:"audio_and_dtmf_input_specification"` - MapBlockKey fwtypes.StringEnum[PromptAttemptsType] `tfsdk:"map_block_key"` - TextInputSpecification fwtypes.ListNestedObjectValueOf[TextInputSpecification] `tfsdk:"text_input_specification"` -} - -type SampleUtteranceData struct { - Utterance types.String `tfsdk:"utterance"` -} - type SlotResolutionSettingData struct { SlotResolutionStrategy fwtypes.StringEnum[awstypes.SlotResolutionStrategy] `tfsdk:"slot_resolution_strategy"` } -type ResponseSpecificationData struct { - AllowInterrupt types.Bool `tfsdk:"allow_interrupt"` - MessageGroups fwtypes.ListNestedObjectValueOf[MessageGroupData] `tfsdk:"message_groups"` -} - type StillWaitingResponseSpecificationData struct { - AllowInterrupt types.Bool `tfsdk:"allow_interrupt"` - FrequencyInSeconds types.Int64 `tfsdk:"frequency_in_seconds"` - MessageGroups fwtypes.ListNestedObjectValueOf[MessageGroupData] `tfsdk:"message_groups"` - TimeoutInSeconds types.Int64 `tfsdk:"timeout_in_seconds"` + AllowInterrupt types.Bool `tfsdk:"allow_interrupt"` + FrequencyInSeconds types.Int64 `tfsdk:"frequency_in_seconds"` + MessageGroups fwtypes.ListNestedObjectValueOf[MessageGroup] `tfsdk:"message_groups"` + TimeoutInSeconds types.Int64 `tfsdk:"timeout_in_seconds"` } type WaitAndContinueSpecificationData struct { Active types.Bool `tfsdk:"active"` - ContinueResponse fwtypes.ListNestedObjectValueOf[ResponseSpecificationData] `tfsdk:"continue_response"` + ContinueResponse fwtypes.ListNestedObjectValueOf[ResponseSpecification] `tfsdk:"continue_response"` StillWaitingResponse fwtypes.ListNestedObjectValueOf[StillWaitingResponseSpecificationData] `tfsdk:"still_waiting_response"` - WaitingResponse fwtypes.ListNestedObjectValueOf[ResponseSpecificationData] `tfsdk:"waiting_response"` + WaitingResponse fwtypes.ListNestedObjectValueOf[ResponseSpecification] `tfsdk:"waiting_response"` } type ValueElicitationSettingData struct { SlotConstraint fwtypes.StringEnum[awstypes.SlotConstraint] `tfsdk:"slot_constraint"` DefaultValueSpecification fwtypes.ListNestedObjectValueOf[DefaultValueSpecificationData] `tfsdk:"default_value_specification"` - PromptSpecification fwtypes.ListNestedObjectValueOf[PromptSpecificationData] `tfsdk:"prompt_specification"` - SampleUtterance fwtypes.ListNestedObjectValueOf[SampleUtteranceData] `tfsdk:"sample_utterance"` + PromptSpecification fwtypes.ListNestedObjectValueOf[PromptSpecification] `tfsdk:"prompt_specification"` + SampleUtterance fwtypes.ListNestedObjectValueOf[SampleUtterance] `tfsdk:"sample_utterance"` SlotResolutionSetting fwtypes.ListNestedObjectValueOf[SlotResolutionSettingData] `tfsdk:"slot_resolution_setting"` WaitAndContinueSpecification fwtypes.ListNestedObjectValueOf[WaitAndContinueSpecificationData] `tfsdk:"wait_and_continue_specification"` } From d3065dbfe85b21ccfc3846262cbac61b263652d3 Mon Sep 17 00:00:00 2001 From: Sharon Nam Date: Fri, 26 Jan 2024 16:13:25 -0800 Subject: [PATCH 31/42] More slot clean up and doc changes --- internal/service/lexv2models/slot.go | 2 +- website/docs/r/lexv2models_slot.html.markdown | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/internal/service/lexv2models/slot.go b/internal/service/lexv2models/slot.go index 7d1a45f52c4..0a010368ad9 100644 --- a/internal/service/lexv2models/slot.go +++ b/internal/service/lexv2models/slot.go @@ -773,7 +773,7 @@ type SlotResolutionSettingData struct { type StillWaitingResponseSpecificationData struct { AllowInterrupt types.Bool `tfsdk:"allow_interrupt"` FrequencyInSeconds types.Int64 `tfsdk:"frequency_in_seconds"` - MessageGroups fwtypes.ListNestedObjectValueOf[MessageGroup] `tfsdk:"message_groups"` + MessageGroup fwtypes.ListNestedObjectValueOf[MessageGroup] `tfsdk:"message_group"` TimeoutInSeconds types.Int64 `tfsdk:"timeout_in_seconds"` } diff --git a/website/docs/r/lexv2models_slot.html.markdown b/website/docs/r/lexv2models_slot.html.markdown index 4b2638c5814..411e82f7d4e 100644 --- a/website/docs/r/lexv2models_slot.html.markdown +++ b/website/docs/r/lexv2models_slot.html.markdown @@ -33,19 +33,19 @@ The following arguments are required: * `intent_id` - (Required) Identifier of the intent that contains the slot. * `locale_id` - (Required) Identifier of the language and locale that the slot will be used in. * `name` - (Required) Name of the slot. -* `value_elicitation_setting` - (Required) Specifies prompts that Amazon Lex sends to the user to elicit a response that provides the value for the slot. +* `value_elicitation_setting` - (Required) Prompts that Amazon Lex sends to the user to elicit a response that provides the value for the slot. The following arguments are optional: * `description` - (Optional) Description of the slot. -* `multiple_values_setting` - (Optional) Indicates whether the slot returns multiple values in one response. See the [`multiple_values_setting` argument reference](#multiple_values_setting-argument-reference) below. +* `multiple_values_setting` - (Optional) Whether the slot returns multiple values in one response. See the [`multiple_values_setting` argument reference](#multiple_values_setting-argument-reference) below. * `obfuscation_setting` - (Optional) Determines how slot values are used in Amazon CloudWatch logs. See the [`obfuscation_setting` argument reference](#obfuscation_setting-argument-reference) below. * `slot_type_id` - (Optional) Unique identifier for the slot type associated with this slot. * `sub_slot_setting` - (Optional) Specifications for the constituent sub slots and the expression for the composite slot. ### `multiple_values_setting` Argument Reference -* `allow_multiple_values` - (Optional) Indicates whether a slot can return multiple values. When `true`, the slot may return more than one value in a response. When `false`, the slot returns only a single value. Multi-value slots are only available in the `en-US` locale. +* `allow_multiple_values` - (Optional) Whether a slot can return multiple values. When `true`, the slot may return more than one value in a response. When `false`, the slot returns only a single value. Multi-value slots are only available in the `en-US` locale. ### `obfuscation_setting` Argument Reference From 974d8363171129ab9e89994b8ef7d0afb31c669d Mon Sep 17 00:00:00 2001 From: Sharon Nam Date: Fri, 26 Jan 2024 16:39:58 -0800 Subject: [PATCH 32/42] Add changelog --- .changelog/34617.txt | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 .changelog/34617.txt diff --git a/.changelog/34617.txt b/.changelog/34617.txt new file mode 100644 index 00000000000..b44c3d50180 --- /dev/null +++ b/.changelog/34617.txt @@ -0,0 +1,3 @@ +```release-note:new-resource +aws_lexv2models_slot +``` \ No newline at end of file From 93537b6c60b3f0bd19bdd74561318c60ea82012a Mon Sep 17 00:00:00 2001 From: changelogbot Date: Sat, 27 Jan 2024 01:43:15 +0000 Subject: [PATCH 33/42] Update CHANGELOG.md for #34617 --- CHANGELOG.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index e891d18a3cf..674244280ea 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,9 @@ ## 5.35.0 (Unreleased) + +FEATURES: + +* **New Resource:** `aws_lexv2models_slot` ([#34617](https://github.com/hashicorp/terraform-provider-aws/issues/34617)) + ## 5.34.0 (January 26, 2024) FEATURES: From aed0b1b1847e47e64d84cddb0226ebd8d8a69aa5 Mon Sep 17 00:00:00 2001 From: Anthony Wat Date: Sat, 27 Jan 2024 04:22:23 -0500 Subject: [PATCH 34/42] docs: Rename IAM resource names for clarity in aws_iot_topic_rule example --- website/docs/r/iot_topic_rule.html.markdown | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/website/docs/r/iot_topic_rule.html.markdown b/website/docs/r/iot_topic_rule.html.markdown index fb3e83e15fe..a18e5593928 100644 --- a/website/docs/r/iot_topic_rule.html.markdown +++ b/website/docs/r/iot_topic_rule.html.markdown @@ -8,6 +8,8 @@ description: |- # Resource: aws_iot_topic_rule +Creates and manages an AWS IoT topic rule. + ## Example Usage ```terraform @@ -54,12 +56,12 @@ data "aws_iam_policy_document" "assume_role" { } } -resource "aws_iam_role" "role" { +resource "aws_iam_role" "myrole" { name = "myrole" assume_role_policy = data.aws_iam_policy_document.assume_role.json } -data "aws_iam_policy_document" "iam_policy_for_lambda" { +data "aws_iam_policy_document" "mypolicy" { statement { effect = "Allow" actions = ["sns:Publish"] @@ -67,10 +69,10 @@ data "aws_iam_policy_document" "iam_policy_for_lambda" { } } -resource "aws_iam_role_policy" "iam_policy_for_lambda" { +resource "aws_iam_role_policy" "mypolicy" { name = "mypolicy" - role = aws_iam_role.role.id - policy = data.aws_iam_policy_document.iam_policy_for_lambda.json + role = aws_iam_role.myrole.id + policy = data.aws_iam_policy_document.mypolicy.json } ``` From 935761632bf8be9463b9c38d61c4ae1e41434ab2 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Fri, 26 Jan 2024 17:58:24 -0500 Subject: [PATCH 35/42] Fix 'TestAccCodeGuruReviewerRepositoryAssociation_S3Repository'. --- .../service/codegurureviewer/repository_association_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/internal/service/codegurureviewer/repository_association_test.go b/internal/service/codegurureviewer/repository_association_test.go index 2d238cb3825..edd6046d652 100644 --- a/internal/service/codegurureviewer/repository_association_test.go +++ b/internal/service/codegurureviewer/repository_association_test.go @@ -119,7 +119,7 @@ func TestAccCodeGuruReviewerRepositoryAssociation_S3Repository(t *testing.T) { resource.TestCheckResourceAttr(resourceName, "repository.0.codecommit.#", "0"), resource.TestCheckResourceAttr(resourceName, "repository.0.github_enterprise_server.#", "0"), resource.TestCheckResourceAttr(resourceName, "repository.0.s3_bucket.#", "1"), - resource.TestCheckResourceAttr(resourceName, "repository.0.s3_bucket.0.bucket_name", rName), + resource.TestCheckResourceAttr(resourceName, "repository.0.s3_bucket.0.bucket_name", "codeguru-reviewer-"+rName), resource.TestCheckResourceAttr(resourceName, "repository.0.s3_bucket.0.name", "test"), resource.TestCheckResourceAttr(resourceName, "kms_key_details.#", "1"), resource.TestCheckResourceAttr(resourceName, "kms_key_details.0.encryption_option", "AWS_OWNED_CMK"), @@ -326,7 +326,7 @@ resource "aws_codegurureviewer_repository_association" "test" { func testAccRepositoryAssociationConfig_s3_repository(rName string) string { return fmt.Sprintf(` resource "aws_s3_bucket" "test" { - bucket = %[1]q + bucket = "codeguru-reviewer-%[1]s" } resource "aws_codegurureviewer_repository_association" "test" { From ca95a0e54f740ad3453f59315a6a4de3ef6e34e3 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Sat, 27 Jan 2024 16:27:45 -0500 Subject: [PATCH 36/42] Acceptance test output: % make testacc TESTARGS='-run=TestAccCodeGuruReviewerRepositoryAssociation_' PKG=codegurureviewer ACCTEST_PARALLELISM=2 ==> Checking that code complies with gofmt requirements... TF_ACC=1 go test ./internal/service/codegurureviewer/... -v -count 1 -parallel 2 -run=TestAccCodeGuruReviewerRepositoryAssociation_ -timeout 360m === RUN TestAccCodeGuruReviewerRepositoryAssociation_basic === PAUSE TestAccCodeGuruReviewerRepositoryAssociation_basic === RUN TestAccCodeGuruReviewerRepositoryAssociation_KMSKey === PAUSE TestAccCodeGuruReviewerRepositoryAssociation_KMSKey === RUN TestAccCodeGuruReviewerRepositoryAssociation_S3Repository === PAUSE TestAccCodeGuruReviewerRepositoryAssociation_S3Repository === RUN TestAccCodeGuruReviewerRepositoryAssociation_tags === PAUSE TestAccCodeGuruReviewerRepositoryAssociation_tags === RUN TestAccCodeGuruReviewerRepositoryAssociation_disappears === PAUSE TestAccCodeGuruReviewerRepositoryAssociation_disappears === CONT TestAccCodeGuruReviewerRepositoryAssociation_basic === CONT TestAccCodeGuruReviewerRepositoryAssociation_tags --- PASS: TestAccCodeGuruReviewerRepositoryAssociation_basic (75.49s) === CONT TestAccCodeGuruReviewerRepositoryAssociation_disappears --- PASS: TestAccCodeGuruReviewerRepositoryAssociation_tags (120.12s) === CONT TestAccCodeGuruReviewerRepositoryAssociation_S3Repository --- PASS: TestAccCodeGuruReviewerRepositoryAssociation_disappears (84.71s) === CONT TestAccCodeGuruReviewerRepositoryAssociation_KMSKey --- PASS: TestAccCodeGuruReviewerRepositoryAssociation_S3Repository (76.59s) --- PASS: TestAccCodeGuruReviewerRepositoryAssociation_KMSKey (73.97s) PASS ok github.com/hashicorp/terraform-provider-aws/internal/service/codegurureviewer 244.340s From 1fd4d3e07158876c96637503a850d6d2e8a27e35 Mon Sep 17 00:00:00 2001 From: alan_wang Date: Mon, 29 Jan 2024 03:49:11 +0000 Subject: [PATCH 37/42] docs: Fix EKS Access Argument: Update principal_arn Parameter Description --- website/docs/d/eks_access_entry.html.markdown | 2 +- website/docs/r/eks_access_entry.html.markdown | 2 +- website/docs/r/eks_access_policy_association.html.markdown | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/website/docs/d/eks_access_entry.html.markdown b/website/docs/d/eks_access_entry.html.markdown index b19c8149375..0e238c82f3a 100644 --- a/website/docs/d/eks_access_entry.html.markdown +++ b/website/docs/d/eks_access_entry.html.markdown @@ -26,7 +26,7 @@ output "eks_access_entry_outputs" { ## Argument Reference * `cluster_name` – (Required) Name of the EKS Cluster. Must be between 1-100 characters in length. Must begin with an alphanumeric character, and must only contain alphanumeric characters, dashes and underscores (`^[0-9A-Za-z][A-Za-z0-9\-_]+$`). -* `principal_arn` – (Required) The IAM Princial ARN which requires Authentication access to the EKS cluster. +* `principal_arn` – (Required) The IAM Principal ARN which requires Authentication access to the EKS cluster. ## Attribute Reference diff --git a/website/docs/r/eks_access_entry.html.markdown b/website/docs/r/eks_access_entry.html.markdown index fa956f81795..6b4e582e9bc 100644 --- a/website/docs/r/eks_access_entry.html.markdown +++ b/website/docs/r/eks_access_entry.html.markdown @@ -26,7 +26,7 @@ resource "aws_eks_access_entry" "example" { The following arguments are required: * `cluster_name` – (Required) Name of the EKS Cluster. Must be between 1-100 characters in length. Must begin with an alphanumeric character, and must only contain alphanumeric characters, dashes and underscores (`^[0-9A-Za-z][A-Za-z0-9\-_]+$`). -* `principal_arn` – (Required) The IAM Princial ARN which requires Authentication access to the EKS cluster. +* `principal_arn` – (Required) The IAM Principal ARN which requires Authentication access to the EKS cluster. The following arguments are optional: diff --git a/website/docs/r/eks_access_policy_association.html.markdown b/website/docs/r/eks_access_policy_association.html.markdown index c514b158963..041785470a0 100644 --- a/website/docs/r/eks_access_policy_association.html.markdown +++ b/website/docs/r/eks_access_policy_association.html.markdown @@ -31,7 +31,7 @@ The following arguments are required: * `cluster_name` – (Required) Name of the EKS Cluster. Must be between 1-100 characters in length. Must begin with an alphanumeric character, and must only contain alphanumeric characters, dashes and underscores (`^[0-9A-Za-z][A-Za-z0-9\-_]+$`). * `policy_arn` – (Required) The ARN of the access policy that you're associating. -* `principal_arn` – (Required) The IAM Princial ARN which requires Authentication access to the EKS cluster. +* `principal_arn` – (Required) The IAM Principal ARN which requires Authentication access to the EKS cluster. * `access_scope` – (Required) The configuration block to determine the scope of the access. See [`access_scope` Block](#access_scope-block) below. ### `access_scope` Block From 5cdcf5db924982a5384051c8d5e55a718a5d86f8 Mon Sep 17 00:00:00 2001 From: Andrew Titmuss Date: Mon, 29 Jan 2024 18:24:13 +1100 Subject: [PATCH 38/42] add iam retry to eks access entry --- internal/service/eks/access_entry.go | 4 +- internal/service/eks/access_entry_test.go | 67 +++++++++++++++++++++++ 2 files changed, 70 insertions(+), 1 deletion(-) diff --git a/internal/service/eks/access_entry.go b/internal/service/eks/access_entry.go index 47c72204b37..a3c42ab8a26 100644 --- a/internal/service/eks/access_entry.go +++ b/internal/service/eks/access_entry.go @@ -120,7 +120,9 @@ func resourceAccessEntryCreate(ctx context.Context, d *schema.ResourceData, meta input.Username = aws.String(v.(string)) } - _, err := conn.CreateAccessEntry(ctx, input) + _, err := tfresource.RetryWhenIsAErrorMessageContains[*types.InvalidParameterException](ctx, propagationTimeout, func() (interface{}, error) { + return conn.CreateAccessEntry(ctx, input) + }, "The specified principalArn is invalid: invalid principal") if err != nil { return sdkdiag.AppendErrorf(diags, "creating EKS Access Entry (%s): %s", id, err) diff --git a/internal/service/eks/access_entry_test.go b/internal/service/eks/access_entry_test.go index f36bcd1f367..bb570de9dd9 100644 --- a/internal/service/eks/access_entry_test.go +++ b/internal/service/eks/access_entry_test.go @@ -257,6 +257,43 @@ func TestAccEKSAccessEntry_username(t *testing.T) { }) } +func TestAccEKSAccessEntry_eventualConsistency(t *testing.T) { + ctx := acctest.Context(t) + if testing.Short() { + t.Skip("skipping long-running test in short mode") + } + + var accessentry types.AccessEntry + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + resourceName := "aws_eks_access_entry.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { + acctest.PreCheck(ctx, t) + testAccPreCheck(ctx, t) + }, + ErrorCheck: acctest.ErrorCheck(t, names.EKSEndpointID), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + CheckDestroy: testAccCheckAccessEntryDestroy(ctx), + Steps: []resource.TestStep{ + { + Config: testAccAccessEntryConfig_eventualConsistency(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckAccessEntryExists(ctx, resourceName, &accessentry), + acctest.CheckResourceAttrGreaterThanOrEqualValue(resourceName, "kubernetes_groups.#", 1), + resource.TestCheckResourceAttr(resourceName, "type", "EC2_LINUX"), + resource.TestCheckResourceAttrSet(resourceName, "user_name"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + func testAccCheckAccessEntryDestroy(ctx context.Context) resource.TestCheckFunc { return func(s *terraform.State) error { conn := acctest.Provider.Meta().(*conns.AWSClient).EKSClient(ctx) @@ -449,6 +486,36 @@ resource "aws_eks_access_entry" "test" { `, rName)) } +func testAccAccessEntryConfig_eventualConsistency(rName string) string { + return acctest.ConfigCompose(testAccAccessEntryConfig_base(rName), fmt.Sprintf(` +resource "aws_iam_role" "test2" { + name = "${aws_eks_cluster.test.name}-2" + + assume_role_policy = < Date: Mon, 29 Jan 2024 20:33:00 +1100 Subject: [PATCH 39/42] add changelog --- .changelog/35535.txt | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 .changelog/35535.txt diff --git a/.changelog/35535.txt b/.changelog/35535.txt new file mode 100644 index 00000000000..84669913126 --- /dev/null +++ b/.changelog/35535.txt @@ -0,0 +1,3 @@ +```release-note:bug +resource/aws_eks_access_entry: Fix IAM eventual consistency preventing creation +``` From 4da2e2bb2f5ee438a69a4911a76e8bc70892ddd2 Mon Sep 17 00:00:00 2001 From: Andrew Titmuss Date: Mon, 29 Jan 2024 20:57:55 +1100 Subject: [PATCH 40/42] fix sprintf args in eventual consistency test --- internal/service/eks/access_entry_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/service/eks/access_entry_test.go b/internal/service/eks/access_entry_test.go index bb570de9dd9..e4253711b69 100644 --- a/internal/service/eks/access_entry_test.go +++ b/internal/service/eks/access_entry_test.go @@ -513,7 +513,7 @@ resource "aws_eks_access_entry" "test" { type = "EC2_LINUX" } -`, rName)) +`)) } func testAccAccessEntryConfig_username(rName, username string) string { From 49b276cbffc6ececd71c9f8df11d52a238e007f5 Mon Sep 17 00:00:00 2001 From: Andrew Titmuss Date: Mon, 29 Jan 2024 21:32:23 +1100 Subject: [PATCH 41/42] sprintf isn't needed at all for this test --- internal/service/eks/access_entry_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/internal/service/eks/access_entry_test.go b/internal/service/eks/access_entry_test.go index e4253711b69..799e8b41dbb 100644 --- a/internal/service/eks/access_entry_test.go +++ b/internal/service/eks/access_entry_test.go @@ -487,7 +487,7 @@ resource "aws_eks_access_entry" "test" { } func testAccAccessEntryConfig_eventualConsistency(rName string) string { - return acctest.ConfigCompose(testAccAccessEntryConfig_base(rName), fmt.Sprintf(` + return acctest.ConfigCompose(testAccAccessEntryConfig_base(rName), ` resource "aws_iam_role" "test2" { name = "${aws_eks_cluster.test.name}-2" @@ -513,7 +513,7 @@ resource "aws_eks_access_entry" "test" { type = "EC2_LINUX" } -`)) +`) } func testAccAccessEntryConfig_username(rName, username string) string { From 87f54b769ab15bef872ed5955650879cf39b26ef Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Mon, 29 Jan 2024 08:20:51 -0500 Subject: [PATCH 42/42] Tweak CHANGELOG entry. --- .changelog/35535.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.changelog/35535.txt b/.changelog/35535.txt index 84669913126..15b65e6e9e4 100644 --- a/.changelog/35535.txt +++ b/.changelog/35535.txt @@ -1,3 +1,3 @@ ```release-note:bug -resource/aws_eks_access_entry: Fix IAM eventual consistency preventing creation +resource/aws_eks_access_entry: Retry IAM eventual consistency errors on create ```