From 7ee5867a21f6031fb1f7e22eb2aab8e3b2b52965 Mon Sep 17 00:00:00 2001 From: Josh Freda Date: Fri, 6 Dec 2024 19:27:03 -0600 Subject: [PATCH 01/17] TEMP: add replace until go-tfe release --- go.mod | 2 ++ 1 file changed, 2 insertions(+) diff --git a/go.mod b/go.mod index 00dc7bc9d..fcc353be0 100644 --- a/go.mod +++ b/go.mod @@ -2,6 +2,8 @@ module github.com/hashicorp/terraform-provider-tfe go 1.21 +replace github.com/hashicorp/go-tfe => ../go-tfe + require ( github.com/agext/levenshtein v1.2.3 // indirect github.com/fatih/color v1.16.0 // indirect From afb433bf70e3f5028d64bdd50d233aa0867ee395 Mon Sep 17 00:00:00 2001 From: Josh Freda Date: Fri, 6 Dec 2024 19:27:26 -0600 Subject: [PATCH 02/17] Add missing method --- internal/provider/client_mock_workspaces_test.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/internal/provider/client_mock_workspaces_test.go b/internal/provider/client_mock_workspaces_test.go index 69894575d..d931e6770 100644 --- a/internal/provider/client_mock_workspaces_test.go +++ b/internal/provider/client_mock_workspaces_test.go @@ -204,3 +204,7 @@ func (m *mockWorkspaces) ListTagBindings(ctx context.Context, workspaceID string func (m *mockWorkspaces) AddTagBindings(ctx context.Context, workspaceID string, options tfe.WorkspaceAddTagBindingsOptions) ([]*tfe.TagBinding, error) { panic("not implemented") } + +func (m *mockWorkspaces) ListEffectiveTagBindings(ctx context.Context, workspaceID string) ([]*tfe.EffectiveTagBinding, error) { + panic("not implemented") +} From ef53a92ef8f5fa6930dd5068967d731d7aca2dbd Mon Sep 17 00:00:00 2001 From: Josh Freda Date: Fri, 6 Dec 2024 19:29:06 -0600 Subject: [PATCH 03/17] Add tfe_team_notification_configuration resource --- internal/provider/provider_next.go | 1 + ...rce_tfe_team_notification_configuration.go | 455 ++++++ ...fe_team_notification_configuration_test.go | 1273 +++++++++++++++++ .../attribute_required_if_value_string.go | 53 + .../attribute_value_conflict_set.go | 54 + .../attribute_value_conflict_string.go | 56 + ...m_notification_configuration.html.markdown | 132 ++ 7 files changed, 2024 insertions(+) create mode 100644 internal/provider/resource_tfe_team_notification_configuration.go create mode 100644 internal/provider/resource_tfe_team_notification_configuration_test.go create mode 100644 internal/provider/validators/attribute_required_if_value_string.go create mode 100644 internal/provider/validators/attribute_value_conflict_set.go create mode 100644 internal/provider/validators/attribute_value_conflict_string.go create mode 100644 website/docs/r/team_notification_configuration.html.markdown diff --git a/internal/provider/provider_next.go b/internal/provider/provider_next.go index 65dde8488..a345f6df8 100644 --- a/internal/provider/provider_next.go +++ b/internal/provider/provider_next.go @@ -138,6 +138,7 @@ func (p *frameworkProvider) Resources(ctx context.Context) []func() resource.Res NewResourceWorkspaceSettings, NewSAMLSettingsResource, NewStackResource, + NewTeamNotificationConfigurationResource, NewTestVariableResource, NewWorkspaceRunTaskResource, } diff --git a/internal/provider/resource_tfe_team_notification_configuration.go b/internal/provider/resource_tfe_team_notification_configuration.go new file mode 100644 index 000000000..e9c194561 --- /dev/null +++ b/internal/provider/resource_tfe_team_notification_configuration.go @@ -0,0 +1,455 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package provider + +import ( + "context" + "fmt" + + tfe "github.com/hashicorp/go-tfe" + "github.com/hashicorp/terraform-plugin-framework-validators/setvalidator" + "github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator" + "github.com/hashicorp/terraform-plugin-framework/attr" + "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/booldefault" + "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-log/tflog" + "github.com/hashicorp/terraform-provider-tfe/internal/provider/validators" +) + +// Ensure provider defined types fully satisfy framework interfaces. +var _ resource.Resource = &resourceTFETeamNotificationConfiguration{} +var _ resource.ResourceWithConfigure = &resourceTFETeamNotificationConfiguration{} +var _ resource.ResourceWithImportState = &resourceTFETeamNotificationConfiguration{} + +func NewTeamNotificationConfigurationResource() resource.Resource { + return &resourceTFETeamNotificationConfiguration{} +} + +// resourceTFETeamNotificationConfiguration implements the tfe_team_notification_configuration resource type +type resourceTFETeamNotificationConfiguration struct { + config ConfiguredClient +} + +func (r *resourceTFETeamNotificationConfiguration) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { + resp.TypeName = req.ProviderTypeName + "_team_notification_configuration" +} + +type modelTFETeamNotificationConfiguration struct { + ID types.String `tfsdk:"id"` + Name types.String `tfsdk:"name"` + DestinationType types.String `tfsdk:"destination_type"` + EmailAddresses types.Set `tfsdk:"email_addresses"` + EmailUserIDs types.Set `tfsdk:"email_user_ids"` + Enabled types.Bool `tfsdk:"enabled"` + Token types.String `tfsdk:"token"` + Triggers types.Set `tfsdk:"triggers"` + URL types.String `tfsdk:"url"` + TeamID types.String `tfsdk:"team_id"` +} + +// modelFromTFETeamNotificationConfiguration builds a modelTFETeamNotificationConfiguration +// struct from a tfe.TeamNotificationConfiguration value. +func modelFromTFETeamNotificationConfiguration(v *tfe.TeamNotificationConfiguration) modelTFETeamNotificationConfiguration { + result := modelTFETeamNotificationConfiguration{ + ID: types.StringValue(v.ID), + Name: types.StringValue(v.Name), + DestinationType: types.StringValue(string(v.DestinationType)), + Enabled: types.BoolValue(v.Enabled), + TeamID: types.StringValue(v.Subscribable.ID), + } + + emailAddresses := make([]attr.Value, len(v.EmailAddresses)) + for i, emailAddress := range v.EmailAddresses { + emailAddresses[i] = types.StringValue(emailAddress) + } + if len(emailAddresses) > 0 { + result.EmailAddresses = types.SetValueMust(types.StringType, emailAddresses) + } else { + result.EmailAddresses = types.SetNull(types.StringType) + } + + emailUserIDs := make([]attr.Value, len(v.EmailUsers)) + for i, emailUser := range v.EmailUsers { + emailUserIDs[i] = types.StringValue(emailUser.ID) + } + if len(emailUserIDs) > 0 { + result.EmailUserIDs = types.SetValueMust(types.StringType, emailUserIDs) + } else { + result.EmailUserIDs = types.SetNull(types.StringType) + } + + triggers := make([]attr.Value, len(v.Triggers)) + for i, trigger := range v.Triggers { + triggers[i] = types.StringValue(trigger) + } + if len(v.Triggers) > 0 { + result.Triggers = types.SetValueMust(types.StringType, triggers) + } else { + result.Triggers = types.SetNull(types.StringType) + } + + if v.Token != "" { + result.Token = types.StringValue(v.Token) + } + + if v.URL != "" { + result.URL = types.StringValue(v.URL) + } + + return result +} + +func (r *resourceTFETeamNotificationConfiguration) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { + resp.Schema = schema.Schema{ + Description: "Defines a team notification configuration resource.", + Version: 0, + + Attributes: map[string]schema.Attribute{ + "id": schema.StringAttribute{ + Description: "ID of the team notification configuration.", + Computed: true, + PlanModifiers: []planmodifier.String{ + stringplanmodifier.UseStateForUnknown(), + }, + }, + + "name": schema.StringAttribute{ + Description: "Name of the team notification configuration.", + Required: true, + }, + + "destination_type": schema.StringAttribute{ + Description: "The type of notification configuration payload to send.", + Required: true, + PlanModifiers: []planmodifier.String{ + stringplanmodifier.RequiresReplace(), + }, + Validators: []validator.String{ + stringvalidator.OneOf( + string(tfe.NotificationDestinationTypeEmail), + string(tfe.NotificationDestinationTypeGeneric), + string(tfe.NotificationDestinationTypeSlack), + string(tfe.NotificationDestinationTypeMicrosoftTeams), + ), + }, + }, + + "email_addresses": schema.SetAttribute{ + Description: "A list of email addresses. This value must not be provided if `destination_type` is `generic`, `microsoft-teams`, or `slack`.", + Optional: true, + Computed: true, + ElementType: types.StringType, + Validators: []validator.Set{ + validators.AttributeValueConflictSetValidator( + "destination_type", + []string{"generic", "microsoft-teams", "slack"}, + ), + setvalidator.ConflictsWith( + path.MatchRelative().AtParent().AtName("token"), + path.MatchRelative().AtParent().AtName("url"), + ), + }, + }, + + "email_user_ids": schema.SetAttribute{ + Description: "A list of user IDs. This value must not be provided if `destination_type` is `generic`, `microsoft-teams`, or `slack`.", + Optional: true, + Computed: true, + ElementType: types.StringType, + Validators: []validator.Set{ + validators.AttributeValueConflictSetValidator( + "destination_type", + []string{"generic", "microsoft-teams", "slack"}, + ), + setvalidator.ConflictsWith( + path.MatchRelative().AtParent().AtName("token"), + path.MatchRelative().AtParent().AtName("url"), + ), + }, + }, + + "enabled": schema.BoolAttribute{ + Description: "Whether the team notification configuration should be enabled or not. Disabled configurations will not send any notifications. Defaults to `false`.", + Optional: true, + Computed: true, + Default: booldefault.StaticBool(false), + }, + + "token": schema.StringAttribute{ + Description: "A write-only secure token for the notification configuration, which can be used by the receiving server to verify request authenticity when configured for notification configurations with a destination type of `generic`. Defaults to `null`. This value _must not_ be provided if `destination_type` is `email`, `microsoft-teams`, or `slack`.", + Optional: true, + Sensitive: true, + Validators: []validator.String{ + validators.AttributeValueConflictStringValidator( + "destination_type", + []string{"email", "microsoft-teams", "slack"}, + ), + }, + }, + + "triggers": schema.SetAttribute{ + Description: "The array of triggers for which this team notification configuration will send notifications. If omitted, no notification triggers are configured.", + Optional: true, + ElementType: types.StringType, + Validators: []validator.Set{ + setvalidator.ValueStringsAre( + stringvalidator.OneOf( + string(tfe.NotificationTriggerChangeRequestCreated), + ), + ), + }, + }, + + "url": schema.StringAttribute{ + Description: "The HTTP or HTTPS URL where notification requests will be made. This value must not be provided if `destination_type` is `email`.", + Optional: true, + Validators: []validator.String{ + validators.AttributeRequiredIfValueString( + "destination_type", + []string{"generic", "microsoft-teams", "slack"}, + ), + validators.AttributeValueConflictStringValidator( + "destination_type", + []string{"email"}, + ), + stringvalidator.ConflictsWith( + path.MatchRelative().AtParent().AtName("email_addresses"), + path.MatchRelative().AtParent().AtName("email_user_ids"), + ), + }, + }, + + "team_id": schema.StringAttribute{ + Description: "The ID of the team that owns the notification configuration.", + Required: true, + PlanModifiers: []planmodifier.String{ + stringplanmodifier.RequiresReplace(), + }, + }, + }, + } +} + +// Configure implements resource.ResourceWithConfigure +func (r *resourceTFETeamNotificationConfiguration) Configure(ctx context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { + // Prevent panic if the provider has not been configured. + if req.ProviderData == nil { + return + } + + client, ok := req.ProviderData.(ConfiguredClient) + if !ok { + resp.Diagnostics.AddError( + "Unexpected resource Configure type", + fmt.Sprintf("Expected tfe.ConfiguredClient, got %T. This is a bug in the tfe provider, so please report it on GitHub.", req.ProviderData), + ) + } + r.config = client +} + +func (r *resourceTFETeamNotificationConfiguration) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { + var plan modelTFETeamNotificationConfiguration + + // Read Terraform plan data into the model + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + + if resp.Diagnostics.HasError() { + return + } + + if resp.Diagnostics.HasError() { + return + } + + // Get team + teamID := plan.TeamID.ValueString() + + // Create a new options struct + options := tfe.TeamNotificationConfigurationCreateOptions{ + DestinationType: tfe.NotificationDestination(tfe.NotificationDestinationType(plan.DestinationType.ValueString())), + Enabled: plan.Enabled.ValueBoolPointer(), + Name: plan.Name.ValueStringPointer(), + Token: plan.Token.ValueStringPointer(), + URL: plan.URL.ValueStringPointer(), + } + + // Add triggers set to the options struct + var triggers []types.String + if diags := plan.Triggers.ElementsAs(ctx, &triggers, true); diags != nil && diags.HasError() { + resp.Diagnostics.Append(diags...) + return + } + options.Triggers = []tfe.NotificationTriggerType{} + for _, trigger := range triggers { + options.Triggers = append(options.Triggers, tfe.NotificationTriggerType(trigger.ValueString())) + } + + // Add email_addresses set to the options struct + emailAddresses := make([]types.String, len(plan.EmailAddresses.Elements())) + if diags := plan.EmailAddresses.ElementsAs(ctx, &emailAddresses, true); diags != nil && diags.HasError() { + resp.Diagnostics.Append(diags...) + return + } + options.EmailAddresses = []string{} + for _, emailAddress := range emailAddresses { + options.EmailAddresses = append(options.EmailAddresses, emailAddress.ValueString()) + } + + // Add email_user_ids set to the options struct + emailUserIDs := make([]types.String, len(plan.EmailUserIDs.Elements())) + if diags := plan.EmailUserIDs.ElementsAs(ctx, &emailUserIDs, true); diags != nil && diags.HasError() { + resp.Diagnostics.Append(diags...) + return + } + options.EmailUsers = []*tfe.User{} + for _, emailUserID := range emailUserIDs { + options.EmailUsers = append(options.EmailUsers, &tfe.User{ID: emailUserID.ValueString()}) + } + + tflog.Debug(ctx, "Creating team notification configuration") + tnc, err := r.config.Client.TeamNotificationConfigurations.Create(ctx, teamID, options) + if err != nil { + resp.Diagnostics.AddError("Unable to create team notification configuration", err.Error()) + return + } + + // Restore token from plan because it is write only + if !plan.Token.IsNull() { + tnc.Token = plan.Token.ValueString() + } + + result := modelFromTFETeamNotificationConfiguration(tnc) + + // Save data into Terraform state + resp.Diagnostics.Append(resp.State.Set(ctx, &result)...) +} + +func (r *resourceTFETeamNotificationConfiguration) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { + var state modelTFETeamNotificationConfiguration + + // Read Terraform prior state data into the model + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + + if resp.Diagnostics.HasError() { + return + } + + tflog.Debug(ctx, fmt.Sprintf("Reading team notification configuration %q", state.ID.ValueString())) + tnc, err := r.config.Client.TeamNotificationConfigurations.Read(ctx, state.ID.ValueString()) + if err != nil { + resp.Diagnostics.AddError("Unable to read team notification configuration", err.Error()) + return + } + + // Restore token from state because it is write only + if !state.Token.IsNull() { + tnc.Token = state.Token.ValueString() + } + + result := modelFromTFETeamNotificationConfiguration(tnc) + + // Save updated data into Terraform state + resp.Diagnostics.Append(resp.State.Set(ctx, &result)...) +} + +func (r *resourceTFETeamNotificationConfiguration) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { + var plan modelTFETeamNotificationConfiguration + var state modelTFETeamNotificationConfiguration + + // Read Terraform plan data into the model + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + + // Read Terraform prior state data into the model + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + + if resp.Diagnostics.HasError() { + return + } + + // Create a new options struct + options := tfe.TeamNotificationConfigurationUpdateOptions{ + Enabled: plan.Enabled.ValueBoolPointer(), + Name: plan.Name.ValueStringPointer(), + Token: plan.Token.ValueStringPointer(), + URL: plan.URL.ValueStringPointer(), + } + + // Add triggers set to the options struct + triggers := make([]types.String, len(plan.Triggers.Elements())) + if diags := plan.Triggers.ElementsAs(ctx, &triggers, true); diags != nil && diags.HasError() { + resp.Diagnostics.Append(diags...) + return + } + options.Triggers = []tfe.NotificationTriggerType{} + for _, trigger := range triggers { + options.Triggers = append(options.Triggers, tfe.NotificationTriggerType(trigger.ValueString())) + } + + // Add email_addresses set to the options struct + emailAddresses := make([]types.String, len(plan.EmailAddresses.Elements())) + if diags := plan.EmailAddresses.ElementsAs(ctx, &emailAddresses, true); diags != nil && diags.HasError() { + resp.Diagnostics.Append(diags...) + return + } + options.EmailAddresses = []string{} + for _, emailAddress := range emailAddresses { + options.EmailAddresses = append(options.EmailAddresses, emailAddress.ValueString()) + } + + // Add email_user_ids set to the options struct + emailUserIDs := make([]types.String, len(plan.EmailUserIDs.Elements())) + if diags := plan.EmailUserIDs.ElementsAs(ctx, &emailUserIDs, true); diags != nil && diags.HasError() { + resp.Diagnostics.Append(diags...) + return + } + options.EmailUsers = []*tfe.User{} + for _, emailUserID := range emailUserIDs { + options.EmailUsers = append(options.EmailUsers, &tfe.User{ID: emailUserID.ValueString()}) + } + + tflog.Debug(ctx, "Updating team notification configuration") + tnc, err := r.config.Client.TeamNotificationConfigurations.Update(ctx, state.ID.ValueString(), options) + if err != nil { + resp.Diagnostics.AddError("Unable to update team notification configuration", err.Error()) + return + } + + // Restore token from plan because it is write only + if !plan.Token.IsNull() { + tnc.Token = plan.Token.ValueString() + } + + result := modelFromTFETeamNotificationConfiguration(tnc) + + // Save data into Terraform state + resp.Diagnostics.Append(resp.State.Set(ctx, &result)...) +} + +func (r *resourceTFETeamNotificationConfiguration) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { + var state modelTFETeamNotificationConfiguration + + // Read Terraform prior state data into the model + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + + if resp.Diagnostics.HasError() { + return + } + + tflog.Debug(ctx, "Deleting team notification configuration") + err := r.config.Client.TeamNotificationConfigurations.Delete(ctx, state.ID.ValueString()) + if err != nil { + resp.Diagnostics.AddError("Unable to delete team notification configuration", err.Error()) + return + } +} + +func (r *resourceTFETeamNotificationConfiguration) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { + resp.Diagnostics.Append(resp.State.SetAttribute(ctx, path.Root("id"), req.ID)...) +} diff --git a/internal/provider/resource_tfe_team_notification_configuration_test.go b/internal/provider/resource_tfe_team_notification_configuration_test.go new file mode 100644 index 000000000..f6d2712d9 --- /dev/null +++ b/internal/provider/resource_tfe_team_notification_configuration_test.go @@ -0,0 +1,1273 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package provider + +import ( + "fmt" + "math/rand" + "reflect" + "regexp" + "testing" + "time" + + "github.com/hashicorp/go-tfe" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" +) + +func TestAccTFETeamNotificationConfiguration_basic(t *testing.T) { + notificationConfiguration := &tfe.TeamNotificationConfiguration{} + rInt := rand.New(rand.NewSource(time.Now().UnixNano())).Int() + + resource.Test(t, resource.TestCase{ + PreCheck: func() { preCheckTFETeamNotificationConfiguration(t) }, + ProtoV5ProviderFactories: testAccMuxedProviders, + CheckDestroy: testAccCheckTFETeamNotificationConfigurationDestroy, + Steps: []resource.TestStep{ + { + Config: testAccTFETeamNotificationConfiguration_basic(rInt), + Check: resource.ComposeTestCheckFunc( + testAccCheckTFETeamNotificationConfigurationExists( + "tfe_team_notification_configuration.foobar", notificationConfiguration), + testAccCheckTFETeamNotificationConfigurationAttributes(notificationConfiguration), + resource.TestCheckResourceAttr( + "tfe_team_notification_configuration.foobar", "destination_type", "generic"), + resource.TestCheckResourceAttr( + "tfe_team_notification_configuration.foobar", "name", "notification_basic"), + // Just test the number of items in triggers + // Values in triggers attribute are tested by testCheckTFETeamNotificationConfigurationAttributes + resource.TestCheckResourceAttr( + "tfe_team_notification_configuration.foobar", "triggers.#", "0"), + resource.TestCheckResourceAttr( + "tfe_team_notification_configuration.foobar", "url", runTasksURL()), + ), + }, + }, + }) +} + +func TestAccTFETeamNotificationConfiguration_emailUserIDs(t *testing.T) { + notificationConfiguration := &tfe.TeamNotificationConfiguration{} + rInt := rand.New(rand.NewSource(time.Now().UnixNano())).Int() + + resource.Test(t, resource.TestCase{ + PreCheck: func() { preCheckTFETeamNotificationConfiguration(t) }, + ProtoV5ProviderFactories: testAccMuxedProviders, + CheckDestroy: testAccCheckTFETeamNotificationConfigurationDestroy, + Steps: []resource.TestStep{ + { + Config: testAccTFETeamNotificationConfiguration_emailUserIDs(rInt), + Check: resource.ComposeTestCheckFunc( + testAccCheckTFETeamNotificationConfigurationExists( + "tfe_team_notification_configuration.foobar", notificationConfiguration), + testAccCheckTFETeamNotificationConfigurationAttributesEmailUserIDs(notificationConfiguration), + resource.TestCheckResourceAttr( + "tfe_team_notification_configuration.foobar", "destination_type", "email"), + resource.TestCheckResourceAttr( + "tfe_team_notification_configuration.foobar", "name", "notification_email"), + // Just test the number of items in triggers + // Values in triggers attribute are tested by testCheckTFETeamNotificationConfigurationAttributesEmailUserIDs + resource.TestCheckResourceAttr( + "tfe_team_notification_configuration.foobar", "triggers.#", "0"), + resource.TestCheckResourceAttr( + "tfe_team_notification_configuration.foobar", "email_user_ids.#", "0"), + ), + }, + }, + }) +} + +func TestAccTFETeamNotificationConfiguration_update(t *testing.T) { + notificationConfiguration := &tfe.TeamNotificationConfiguration{} + rInt := rand.New(rand.NewSource(time.Now().UnixNano())).Int() + + resource.Test(t, resource.TestCase{ + PreCheck: func() { preCheckTFETeamNotificationConfiguration(t) }, + ProtoV5ProviderFactories: testAccMuxedProviders, + CheckDestroy: testAccCheckTFETeamNotificationConfigurationDestroy, + Steps: []resource.TestStep{ + { + Config: testAccTFETeamNotificationConfiguration_basic(rInt), + Check: resource.ComposeTestCheckFunc( + testAccCheckTFETeamNotificationConfigurationExists( + "tfe_team_notification_configuration.foobar", notificationConfiguration), + testAccCheckTFETeamNotificationConfigurationAttributes(notificationConfiguration), + resource.TestCheckResourceAttr( + "tfe_team_notification_configuration.foobar", "destination_type", "generic"), + resource.TestCheckResourceAttr( + "tfe_team_notification_configuration.foobar", "name", "notification_basic"), + // Just test the number of items in triggers + // Values in triggers attribute are tested by testCheckTFETeamNotificationConfigurationAttributes + resource.TestCheckResourceAttr( + "tfe_team_notification_configuration.foobar", "triggers.#", "0"), + resource.TestCheckResourceAttr( + "tfe_team_notification_configuration.foobar", "url", runTasksURL()), + ), + }, + { + Config: testAccTFETeamNotificationConfiguration_update(rInt), + Check: resource.ComposeTestCheckFunc( + testAccCheckTFETeamNotificationConfigurationExists( + "tfe_team_notification_configuration.foobar", notificationConfiguration), + testAccCheckTFETeamNotificationConfigurationAttributesUpdate(notificationConfiguration), + resource.TestCheckResourceAttr( + "tfe_team_notification_configuration.foobar", "destination_type", "generic"), + resource.TestCheckResourceAttr( + "tfe_team_notification_configuration.foobar", "enabled", "true"), + resource.TestCheckResourceAttr( + "tfe_team_notification_configuration.foobar", "name", "notification_update"), + resource.TestCheckResourceAttr( + "tfe_team_notification_configuration.foobar", "token", "1234567890_update"), + // Just test the number of items in triggers + // Values in triggers attribute are tested by testCheckTFETeamNotificationConfigurationAttributesUpdate + resource.TestCheckResourceAttr( + "tfe_team_notification_configuration.foobar", "triggers.#", "1"), + resource.TestCheckResourceAttr( + "tfe_team_notification_configuration.foobar", "url", fmt.Sprintf("%s?update=true", runTasksURL())), + ), + }, + }, + }) +} + +func TestAccTFETeamNotificationConfiguration_updateEmailUserIDs(t *testing.T) { + notificationConfiguration := &tfe.TeamNotificationConfiguration{} + rInt := rand.New(rand.NewSource(time.Now().UnixNano())).Int() + + resource.Test(t, resource.TestCase{ + PreCheck: func() { preCheckTFETeamNotificationConfiguration(t) }, + ProtoV5ProviderFactories: testAccMuxedProviders, + CheckDestroy: testAccCheckTFETeamNotificationConfigurationDestroy, + Steps: []resource.TestStep{ + { + Config: testAccTFETeamNotificationConfiguration_emailUserIDs(rInt), + Check: resource.ComposeTestCheckFunc( + testAccCheckTFETeamNotificationConfigurationExists( + "tfe_team_notification_configuration.foobar", notificationConfiguration), + testAccCheckTFETeamNotificationConfigurationAttributesEmailUserIDs(notificationConfiguration), + resource.TestCheckResourceAttr( + "tfe_team_notification_configuration.foobar", "destination_type", "email"), + resource.TestCheckResourceAttr( + "tfe_team_notification_configuration.foobar", "name", "notification_email"), + // Just test the number of items in triggers + // Values in triggers attribute are tested by testCheckTFETeamNotificationConfigurationAttributesEmailUserIDs + resource.TestCheckResourceAttr( + "tfe_team_notification_configuration.foobar", "triggers.#", "0"), + resource.TestCheckResourceAttr( + "tfe_team_notification_configuration.foobar", "email_user_ids.#", "0"), + ), + }, + { + Config: testAccTFETeamNotificationConfiguration_updateEmailUserIDs(rInt), + Check: resource.ComposeTestCheckFunc( + testAccCheckTFETeamNotificationConfigurationExists( + "tfe_team_notification_configuration.foobar", notificationConfiguration), + testAccCheckTFETeamNotificationConfigurationAttributesUpdateEmailUserIDs(notificationConfiguration), + resource.TestCheckResourceAttr( + "tfe_team_notification_configuration.foobar", "destination_type", "email"), + resource.TestCheckResourceAttr( + "tfe_team_notification_configuration.foobar", "enabled", "true"), + resource.TestCheckResourceAttr( + "tfe_team_notification_configuration.foobar", "name", "notification_email_update"), + // Just test the number of items in triggers + // Values in triggers attribute are tested by testCheckTFETeamNotificationConfigurationAttributesUpdateEmailUserIDs + resource.TestCheckResourceAttr( + "tfe_team_notification_configuration.foobar", "triggers.#", "1"), + resource.TestCheckResourceAttr( + "tfe_team_notification_configuration.foobar", "email_user_ids.#", "1"), + ), + }, + }, + }) +} + +func TestAccTFETeamNotificationConfiguration_validateSchemaAttributesEmail(t *testing.T) { + rInt := rand.New(rand.NewSource(time.Now().UnixNano())).Int() + + resource.Test(t, resource.TestCase{ + PreCheck: func() { preCheckTFETeamNotificationConfiguration(t) }, + ProtoV5ProviderFactories: testAccMuxedProviders, + Steps: []resource.TestStep{ + { + Config: testAccTFETeamNotificationConfiguration_emailWithURL(rInt), + ExpectError: regexp.MustCompile(`The attribute 'url' cannot be set when 'destination_type' is 'email'`), + }, + { + Config: testAccTFETeamNotificationConfiguration_emailWithToken(rInt), + ExpectError: regexp.MustCompile(`The attribute 'token' cannot be set when 'destination_type' is 'email'`), + }, + }, + }) +} + +func TestAccTFETeamNotificationConfiguration_validateSchemaAttributesGeneric(t *testing.T) { + rInt := rand.New(rand.NewSource(time.Now().UnixNano())).Int() + + resource.Test(t, resource.TestCase{ + PreCheck: func() { preCheckTFETeamNotificationConfiguration(t) }, + ProtoV5ProviderFactories: testAccMuxedProviders, + Steps: []resource.TestStep{ + { + Config: testAccTFETeamNotificationConfiguration_genericWithEmailAddresses(rInt), + ExpectError: regexp.MustCompile(`(?s).*The attribute 'email_addresses' cannot be set when 'destination_type' is.*'generic'`), + }, + { + Config: testAccTFETeamNotificationConfiguration_genericWithEmailUserIDs(rInt), + ExpectError: regexp.MustCompile(`(?s).*The attribute 'email_user_ids' cannot be set when 'destination_type' is.*'generic'`), + }, + { + Config: testAccTFETeamNotificationConfiguration_genericWithoutURL(rInt), + ExpectError: regexp.MustCompile(`The attribute 'url' is required when 'destination_type' is 'generic'`), + }, + }, + }) +} + +func TestAccTFETeamNotificationConfiguration_validateSchemaAttributesSlack(t *testing.T) { + rInt := rand.New(rand.NewSource(time.Now().UnixNano())).Int() + + resource.Test(t, resource.TestCase{ + PreCheck: func() { preCheckTFETeamNotificationConfiguration(t) }, + ProtoV5ProviderFactories: testAccMuxedProviders, + Steps: []resource.TestStep{ + { + Config: testAccTFETeamNotificationConfiguration_slackWithEmailAddresses(rInt), + ExpectError: regexp.MustCompile(`(?s).*The attribute 'email_addresses' cannot be set when 'destination_type' is.*'slack'`), + }, + { + Config: testAccTFETeamNotificationConfiguration_slackWithEmailUserIDs(rInt), + ExpectError: regexp.MustCompile(`(?s).*The attribute 'email_user_ids' cannot be set when 'destination_type' is.*'slack'`), + }, + { + Config: testAccTFETeamNotificationConfiguration_slackWithToken(rInt), + ExpectError: regexp.MustCompile(`The attribute 'token' cannot be set when 'destination_type' is 'slack'`), + }, + { + Config: testAccTFETeamNotificationConfiguration_slackWithoutURL(rInt), + ExpectError: regexp.MustCompile(`The attribute 'url' is required when 'destination_type' is 'slack'`), + }, + }, + }) +} + +func TestAccTFETeamNotificationConfiguration_validateSchemaAttributesMicrosoftTeams(t *testing.T) { + rInt := rand.New(rand.NewSource(time.Now().UnixNano())).Int() + + resource.Test(t, resource.TestCase{ + PreCheck: func() { preCheckTFETeamNotificationConfiguration(t) }, + ProtoV5ProviderFactories: testAccMuxedProviders, + Steps: []resource.TestStep{ + { + Config: testAccTFETeamNotificationConfiguration_microsoftTeamsWithEmailAddresses(rInt), + ExpectError: regexp.MustCompile(`(?s).*The attribute 'email_addresses' cannot be set when 'destination_type' is.*'microsoft-teams'`), + }, + { + Config: testAccTFETeamNotificationConfiguration_microsoftTeamsWithEmailUserIDs(rInt), + ExpectError: regexp.MustCompile(`(?s).*The attribute 'email_user_ids' cannot be set when 'destination_type' is.*'microsoft-teams'`), + }, + { + Config: testAccTFETeamNotificationConfiguration_microsoftTeamsWithToken(rInt), + ExpectError: regexp.MustCompile(`(?s).*The attribute 'token' cannot be set when 'destination_type' is.*'microsoft-teams'`), + }, + { + Config: testAccTFETeamNotificationConfiguration_microsoftTeamsWithoutURL(rInt), + ExpectError: regexp.MustCompile(`The attribute 'url' is required when 'destination_type' is 'microsoft-teams'`), + }, + }, + }) +} + +func TestAccTFETeamNotificationConfiguration_validateSchemaAttributesBadDestinationType(t *testing.T) { + rInt := rand.New(rand.NewSource(time.Now().UnixNano())).Int() + + resource.Test(t, resource.TestCase{ + PreCheck: func() { preCheckTFETeamNotificationConfiguration(t) }, + ProtoV5ProviderFactories: testAccMuxedProviders, + Steps: []resource.TestStep{ + { + Config: testAccTFETeamNotificationConfiguration_badDestinationType(rInt), + ExpectError: regexp.MustCompile(`.*Invalid Attribute Value Match.*`), + }, + }, + }) +} + +func TestAccTFETeamNotificationConfiguration_updateValidateSchemaAttributesEmail(t *testing.T) { + notificationConfiguration := &tfe.TeamNotificationConfiguration{} + rInt := rand.New(rand.NewSource(time.Now().UnixNano())).Int() + + resource.Test(t, resource.TestCase{ + PreCheck: func() { preCheckTFETeamNotificationConfiguration(t) }, + ProtoV5ProviderFactories: testAccMuxedProviders, + CheckDestroy: testAccCheckTFETeamNotificationConfigurationDestroy, + Steps: []resource.TestStep{ + { + Config: testAccTFETeamNotificationConfiguration_emailUserIDs(rInt), + Check: resource.ComposeTestCheckFunc( + testAccCheckTFETeamNotificationConfigurationExists( + "tfe_team_notification_configuration.foobar", notificationConfiguration), + testAccCheckTFETeamNotificationConfigurationAttributesEmailUserIDs(notificationConfiguration), + resource.TestCheckResourceAttr( + "tfe_team_notification_configuration.foobar", "destination_type", "email"), + resource.TestCheckResourceAttr( + "tfe_team_notification_configuration.foobar", "name", "notification_email"), + // Just test the number of items in triggers + // Values in triggers attribute are tested by testCheckTFETeamNotificationConfigurationAttributesEmailUserIDs + resource.TestCheckResourceAttr( + "tfe_team_notification_configuration.foobar", "triggers.#", "0"), + resource.TestCheckResourceAttr( + "tfe_team_notification_configuration.foobar", "email_user_ids.#", "0"), + ), + }, + { + Config: testAccTFETeamNotificationConfiguration_emailWithURL(rInt), + ExpectError: regexp.MustCompile(`The attribute 'url' cannot be set when 'destination_type' is 'email'`), + }, + { + Config: testAccTFETeamNotificationConfiguration_emailWithToken(rInt), + ExpectError: regexp.MustCompile(`The attribute 'token' cannot be set when 'destination_type' is 'email'`), + }, + { + Config: testAccTFETeamNotificationConfiguration_emailUserIDs(rInt), + }, + }, + }) +} + +func TestAccTFETeamNotificationConfiguration_updateValidateSchemaAttributesGeneric(t *testing.T) { + notificationConfiguration := &tfe.TeamNotificationConfiguration{} + rInt := rand.New(rand.NewSource(time.Now().UnixNano())).Int() + + resource.Test(t, resource.TestCase{ + PreCheck: func() { preCheckTFETeamNotificationConfiguration(t) }, + ProtoV5ProviderFactories: testAccMuxedProviders, + CheckDestroy: testAccCheckTFETeamNotificationConfigurationDestroy, + Steps: []resource.TestStep{ + { + Config: testAccTFETeamNotificationConfiguration_basic(rInt), + Check: resource.ComposeTestCheckFunc( + testAccCheckTFETeamNotificationConfigurationExists( + "tfe_team_notification_configuration.foobar", notificationConfiguration), + testAccCheckTFETeamNotificationConfigurationAttributes(notificationConfiguration), + resource.TestCheckResourceAttr( + "tfe_team_notification_configuration.foobar", "destination_type", "generic"), + resource.TestCheckResourceAttr( + "tfe_team_notification_configuration.foobar", "name", "notification_basic"), + // Just test the number of items in triggers + // Values in triggers attribute are tested by testCheckTFETeamNotificationConfigurationAttributes + resource.TestCheckResourceAttr( + "tfe_team_notification_configuration.foobar", "triggers.#", "0"), + resource.TestCheckResourceAttr( + "tfe_team_notification_configuration.foobar", "url", runTasksURL()), + ), + }, + { + Config: testAccTFETeamNotificationConfiguration_genericWithEmailAddresses(rInt), + ExpectError: regexp.MustCompile(`(?s).*The attribute 'email_addresses' cannot be set when 'destination_type' is.*'generic'`), + }, + { + Config: testAccTFETeamNotificationConfiguration_genericWithEmailUserIDs(rInt), + ExpectError: regexp.MustCompile(`(?s).*The attribute 'email_user_ids' cannot be set when 'destination_type' is.*'generic'`), + }, + { + Config: testAccTFETeamNotificationConfiguration_genericWithoutURL(rInt), + ExpectError: regexp.MustCompile(`The attribute 'url' is required when 'destination_type' is 'generic'`), + }, + { + Config: testAccTFETeamNotificationConfiguration_basic(rInt), + }, + }, + }) +} + +func TestAccTFETeamNotificationConfiguration_updateValidateSchemaAttributesSlack(t *testing.T) { + notificationConfiguration := &tfe.TeamNotificationConfiguration{} + rInt := rand.New(rand.NewSource(time.Now().UnixNano())).Int() + + resource.Test(t, resource.TestCase{ + PreCheck: func() { preCheckTFETeamNotificationConfiguration(t) }, + ProtoV5ProviderFactories: testAccMuxedProviders, + CheckDestroy: testAccCheckTFETeamNotificationConfigurationDestroy, + Steps: []resource.TestStep{ + { + Config: testAccTFETeamNotificationConfiguration_slack(rInt), + Check: resource.ComposeTestCheckFunc( + testAccCheckTFETeamNotificationConfigurationExists( + "tfe_team_notification_configuration.foobar", notificationConfiguration), + testAccCheckTFETeamNotificationConfigurationAttributesSlack(notificationConfiguration), + resource.TestCheckResourceAttr( + "tfe_team_notification_configuration.foobar", "destination_type", "slack"), + resource.TestCheckResourceAttr( + "tfe_team_notification_configuration.foobar", "name", "notification_slack"), + // Just test the number of items in triggers + // Values in triggers attribute are tested by testCheckTFETeamNotificationConfigurationAttributes + resource.TestCheckResourceAttr( + "tfe_team_notification_configuration.foobar", "triggers.#", "0"), + resource.TestCheckResourceAttr( + "tfe_team_notification_configuration.foobar", "url", runTasksURL()), + ), + }, + { + Config: testAccTFETeamNotificationConfiguration_slackWithEmailAddresses(rInt), + ExpectError: regexp.MustCompile(`(?s).*The attribute 'email_addresses' cannot be set when 'destination_type' is.*'slack'`), + }, + { + Config: testAccTFETeamNotificationConfiguration_slackWithEmailUserIDs(rInt), + ExpectError: regexp.MustCompile(`(?s).*The attribute 'email_user_ids' cannot be set when 'destination_type' is.*'slack'`), + }, + { + Config: testAccTFETeamNotificationConfiguration_slackWithToken(rInt), + ExpectError: regexp.MustCompile(`The attribute 'token' cannot be set when 'destination_type' is 'slack'`), + }, + { + Config: testAccTFETeamNotificationConfiguration_slackWithoutURL(rInt), + ExpectError: regexp.MustCompile(`The attribute 'url' is required when 'destination_type' is 'slack'`), + }, + { + Config: testAccTFETeamNotificationConfiguration_slack(rInt), + }, + }, + }) +} + +func TestAccTFETeamNotificationConfiguration_updateValidateSchemaAttributesMicrosoftTeams(t *testing.T) { + notificationConfiguration := &tfe.TeamNotificationConfiguration{} + rInt := rand.New(rand.NewSource(time.Now().UnixNano())).Int() + + resource.Test(t, resource.TestCase{ + PreCheck: func() { preCheckTFETeamNotificationConfiguration(t) }, + ProtoV5ProviderFactories: testAccMuxedProviders, + CheckDestroy: testAccCheckTFETeamNotificationConfigurationDestroy, + Steps: []resource.TestStep{ + { + Config: testAccTFETeamNotificationConfiguration_microsoftTeams(rInt), + Check: resource.ComposeTestCheckFunc( + testAccCheckTFETeamNotificationConfigurationExists( + "tfe_team_notification_configuration.foobar", notificationConfiguration), + testAccCheckTFETeamNotificationConfigurationAttributesMicrosoftTeams(notificationConfiguration), + resource.TestCheckResourceAttr( + "tfe_team_notification_configuration.foobar", "destination_type", "microsoft-teams"), + resource.TestCheckResourceAttr( + "tfe_team_notification_configuration.foobar", "name", "notification_msteams"), + resource.TestCheckResourceAttr( + "tfe_team_notification_configuration.foobar", "url", runTasksURL()), + ), + }, + { + Config: testAccTFETeamNotificationConfiguration_microsoftTeamsWithEmailAddresses(rInt), + ExpectError: regexp.MustCompile(`(?s).*The attribute 'email_addresses' cannot be set when 'destination_type' is.*'microsoft-teams'`), + }, + { + Config: testAccTFETeamNotificationConfiguration_microsoftTeamsWithEmailUserIDs(rInt), + ExpectError: regexp.MustCompile(`(?s).*The attribute 'email_user_ids' cannot be set when 'destination_type' is.*'microsoft-teams'`), + }, + { + Config: testAccTFETeamNotificationConfiguration_microsoftTeamsWithToken(rInt), + ExpectError: regexp.MustCompile(`(?s).*The attribute 'token' cannot be set when 'destination_type' is.*'microsoft-teams'`), + }, + { + Config: testAccTFETeamNotificationConfiguration_microsoftTeamsWithoutURL(rInt), + ExpectError: regexp.MustCompile(`The attribute 'url' is required when 'destination_type' is 'microsoft-teams'`), + }, + { + Config: testAccTFETeamNotificationConfiguration_microsoftTeams(rInt), + }, + }, + }) +} + +func TestAccTFETeamNotificationConfiguration_duplicateTriggers(t *testing.T) { + notificationConfiguration := &tfe.TeamNotificationConfiguration{} + rInt := rand.New(rand.NewSource(time.Now().UnixNano())).Int() + + resource.Test(t, resource.TestCase{ + PreCheck: func() { preCheckTFETeamNotificationConfiguration(t) }, + ProtoV5ProviderFactories: testAccMuxedProviders, + CheckDestroy: testAccCheckTFETeamNotificationConfigurationDestroy, + Steps: []resource.TestStep{ + { + Config: testAccTFETeamNotificationConfiguration_duplicateTriggers(rInt), + Check: resource.ComposeTestCheckFunc( + testAccCheckTFETeamNotificationConfigurationExists( + "tfe_team_notification_configuration.foobar", notificationConfiguration), + testAccCheckTFETeamNotificationConfigurationAttributesDuplicateTriggers(notificationConfiguration), + resource.TestCheckResourceAttr( + "tfe_team_notification_configuration.foobar", "destination_type", "generic"), + resource.TestCheckResourceAttr( + "tfe_team_notification_configuration.foobar", "name", "notification_duplicate_triggers"), + // Just test the number of items in triggers + // Values in triggers attribute are tested by testCheckTFETeamNotificationConfigurationAttributes + resource.TestCheckResourceAttr( + "tfe_team_notification_configuration.foobar", "triggers.#", "1"), + resource.TestCheckResourceAttr( + "tfe_team_notification_configuration.foobar", "url", runTasksURL()), + ), + }, + }, + }) +} + +func TestAccTFETeamNotificationConfigurationImport_basic(t *testing.T) { + rInt := rand.New(rand.NewSource(time.Now().UnixNano())).Int() + + resource.Test(t, resource.TestCase{ + PreCheck: func() { preCheckTFETeamNotificationConfiguration(t) }, + ProtoV5ProviderFactories: testAccMuxedProviders, + CheckDestroy: testAccCheckTFETeamNotificationConfigurationDestroy, + Steps: []resource.TestStep{ + { + Config: testAccTFETeamNotificationConfiguration_update(rInt), + }, + + { + ResourceName: "tfe_team_notification_configuration.foobar", + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"token"}, + }, + }, + }) +} + +func TestAccTFETeamNotificationConfigurationImport_emailUserIDs(t *testing.T) { + rInt := rand.New(rand.NewSource(time.Now().UnixNano())).Int() + + resource.Test(t, resource.TestCase{ + PreCheck: func() { preCheckTFETeamNotificationConfiguration(t) }, + ProtoV5ProviderFactories: testAccMuxedProviders, + CheckDestroy: testAccCheckTFETeamNotificationConfigurationDestroy, + Steps: []resource.TestStep{ + { + Config: testAccTFETeamNotificationConfiguration_updateEmailUserIDs(rInt), + }, + + { + ResourceName: "tfe_team_notification_configuration.foobar", + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"token"}, + }, + }, + }) +} + +func TestAccTFETeamNotificationConfigurationImport_emptyEmailUserIDs(t *testing.T) { + rInt := rand.New(rand.NewSource(time.Now().UnixNano())).Int() + + resource.Test(t, resource.TestCase{ + PreCheck: func() { preCheckTFETeamNotificationConfiguration(t) }, + ProtoV5ProviderFactories: testAccMuxedProviders, + CheckDestroy: testAccCheckTFETeamNotificationConfigurationDestroy, + Steps: []resource.TestStep{ + { + Config: testAccTFETeamNotificationConfiguration_emailUserIDs(rInt), + }, + + { + ResourceName: "tfe_team_notification_configuration.foobar", + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"token"}, + }, + }, + }) +} + +func testAccCheckTFETeamNotificationConfigurationExists(n string, notificationConfiguration *tfe.TeamNotificationConfiguration) resource.TestCheckFunc { + return func(s *terraform.State) error { + config := testAccProvider.Meta().(ConfiguredClient) + + rs, ok := s.RootModule().Resources[n] + if !ok { + return fmt.Errorf("Not found: %s", n) + } + + if rs.Primary.ID == "" { + return fmt.Errorf("No instance ID is set") + } + + nc, err := config.Client.TeamNotificationConfigurations.Read(ctx, rs.Primary.ID) + if err != nil { + return err + } + + *notificationConfiguration = *nc + + return nil + } +} + +func testAccCheckTFETeamNotificationConfigurationAttributes(notificationConfiguration *tfe.TeamNotificationConfiguration) resource.TestCheckFunc { + return func(s *terraform.State) error { + if notificationConfiguration.Name != "notification_basic" { + return fmt.Errorf("Bad name: %s", notificationConfiguration.Name) + } + + if notificationConfiguration.DestinationType != tfe.NotificationDestinationTypeGeneric { + return fmt.Errorf("Bad destination type: %s", notificationConfiguration.DestinationType) + } + + if notificationConfiguration.Enabled != false { + return fmt.Errorf("Bad enabled value: %t", notificationConfiguration.Enabled) + } + + // Token is write only, can't read it + + if !reflect.DeepEqual(notificationConfiguration.Triggers, []string{}) { + return fmt.Errorf("Bad triggers: %v", notificationConfiguration.Triggers) + } + + if notificationConfiguration.URL != runTasksURL() { + return fmt.Errorf("Bad URL: %s", notificationConfiguration.URL) + } + + return nil + } +} + +func testAccCheckTFETeamNotificationConfigurationAttributesUpdate(notificationConfiguration *tfe.TeamNotificationConfiguration) resource.TestCheckFunc { + return func(s *terraform.State) error { + if notificationConfiguration.Name != "notification_update" { + return fmt.Errorf("Bad name: %s", notificationConfiguration.Name) + } + + if notificationConfiguration.DestinationType != tfe.NotificationDestinationTypeGeneric { + return fmt.Errorf("Bad destination type: %s", notificationConfiguration.DestinationType) + } + + if notificationConfiguration.Enabled != true { + return fmt.Errorf("Bad enabled value: %t", notificationConfiguration.Enabled) + } + + // Token is write only, can't read it + + if !reflect.DeepEqual(notificationConfiguration.Triggers, []string{string(tfe.NotificationTriggerChangeRequestCreated)}) { + return fmt.Errorf("Bad triggers: %v", notificationConfiguration.Triggers) + } + + if notificationConfiguration.URL != fmt.Sprintf("%s?update=true", runTasksURL()) { + return fmt.Errorf("Bad URL: %s", notificationConfiguration.URL) + } + + return nil + } +} + +func testAccCheckTFETeamNotificationConfigurationAttributesEmailUserIDs(notificationConfiguration *tfe.TeamNotificationConfiguration) resource.TestCheckFunc { + return func(s *terraform.State) error { + if notificationConfiguration.Name != "notification_email" { + return fmt.Errorf("Bad name: %s", notificationConfiguration.Name) + } + + if notificationConfiguration.DestinationType != tfe.NotificationDestinationTypeEmail { + return fmt.Errorf("Bad destination type: %s", notificationConfiguration.DestinationType) + } + + if notificationConfiguration.Enabled != false { + return fmt.Errorf("Bad enabled value: %t", notificationConfiguration.Enabled) + } + + // Token is write only, can't read it + + if !reflect.DeepEqual(notificationConfiguration.Triggers, []string{}) { + return fmt.Errorf("Bad triggers: %v", notificationConfiguration.Triggers) + } + + if len(notificationConfiguration.EmailUsers) != 0 { + return fmt.Errorf("Wrong number of email users: %v", len(notificationConfiguration.EmailUsers)) + } + + return nil + } +} + +func testAccCheckTFETeamNotificationConfigurationAttributesUpdateEmailUserIDs(notificationConfiguration *tfe.TeamNotificationConfiguration) resource.TestCheckFunc { + return func(s *terraform.State) error { + if notificationConfiguration.Name != "notification_email_update" { + return fmt.Errorf("Bad name: %s", notificationConfiguration.Name) + } + + if notificationConfiguration.DestinationType != tfe.NotificationDestinationTypeEmail { + return fmt.Errorf("Bad destination type: %s", notificationConfiguration.DestinationType) + } + + if notificationConfiguration.Enabled != true { + return fmt.Errorf("Bad enabled value: %t", notificationConfiguration.Enabled) + } + + // Token is write only, can't read it + + if !reflect.DeepEqual(notificationConfiguration.Triggers, []string{string(tfe.NotificationTriggerChangeRequestCreated)}) { + return fmt.Errorf("Bad triggers: %v", notificationConfiguration.Triggers) + } + + if len(notificationConfiguration.EmailUsers) != 1 { + return fmt.Errorf("Wrong number of email users: %v", len(notificationConfiguration.EmailUsers)) + } + + return nil + } +} + +func testAccCheckTFETeamNotificationConfigurationAttributesSlack(notificationConfiguration *tfe.TeamNotificationConfiguration) resource.TestCheckFunc { + return func(s *terraform.State) error { + if notificationConfiguration.Name != "notification_slack" { + return fmt.Errorf("Bad name: %s", notificationConfiguration.Name) + } + + if notificationConfiguration.DestinationType != tfe.NotificationDestinationTypeSlack { + return fmt.Errorf("Bad destination type: %s", notificationConfiguration.DestinationType) + } + + if notificationConfiguration.Enabled != false { + return fmt.Errorf("Bad enabled value: %t", notificationConfiguration.Enabled) + } + + // Token is write only, can't read it + + if !reflect.DeepEqual(notificationConfiguration.Triggers, []string{}) { + return fmt.Errorf("Bad triggers: %v", notificationConfiguration.Triggers) + } + + return nil + } +} + +func testAccCheckTFETeamNotificationConfigurationAttributesMicrosoftTeams(notificationConfiguration *tfe.TeamNotificationConfiguration) resource.TestCheckFunc { + return func(s *terraform.State) error { + if notificationConfiguration.Name != "notification_msteams" { + return fmt.Errorf("Bad name: %s", notificationConfiguration.Name) + } + + if notificationConfiguration.DestinationType != tfe.NotificationDestinationTypeMicrosoftTeams { + return fmt.Errorf("Bad destination type: %s", notificationConfiguration.DestinationType) + } + + if notificationConfiguration.Enabled != false { + return fmt.Errorf("Bad enabled value: %t", notificationConfiguration.Enabled) + } + + if !reflect.DeepEqual(notificationConfiguration.Triggers, []string{}) { + return fmt.Errorf("Bad triggers: %v", notificationConfiguration.Triggers) + } + + if notificationConfiguration.URL != runTasksURL() { + return fmt.Errorf("Bad URL: %s", notificationConfiguration.URL) + } + + return nil + } +} + +func testAccCheckTFETeamNotificationConfigurationAttributesDuplicateTriggers(notificationConfiguration *tfe.TeamNotificationConfiguration) resource.TestCheckFunc { + return func(s *terraform.State) error { + if notificationConfiguration.Name != "notification_duplicate_triggers" { + return fmt.Errorf("Bad name: %s", notificationConfiguration.Name) + } + + if notificationConfiguration.DestinationType != tfe.NotificationDestinationTypeGeneric { + return fmt.Errorf("Bad destination type: %s", notificationConfiguration.DestinationType) + } + + if notificationConfiguration.Enabled != false { + return fmt.Errorf("Bad enabled value: %t", notificationConfiguration.Enabled) + } + + // Token is write only, can't read it + + if !reflect.DeepEqual(notificationConfiguration.Triggers, []string{string(tfe.NotificationTriggerChangeRequestCreated)}) { + return fmt.Errorf("Bad triggers: %v", notificationConfiguration.Triggers) + } + + if notificationConfiguration.URL != runTasksURL() { + return fmt.Errorf("Bad URL: %s", notificationConfiguration.URL) + } + + return nil + } +} + +func testAccCheckTFETeamNotificationConfigurationDestroy(s *terraform.State) error { + config := testAccProvider.Meta().(ConfiguredClient) + + for _, rs := range s.RootModule().Resources { + if rs.Type != "tfe_team_notification_configuration" { + continue + } + + if rs.Primary.ID == "" { + return fmt.Errorf("No instance ID is set") + } + + _, err := config.Client.TeamNotificationConfigurations.Read(ctx, rs.Primary.ID) + if err == nil { + return fmt.Errorf("Notification configuration %s still exists", rs.Primary.ID) + } + } + + return nil +} + +func testAccTFETeamNotificationConfiguration_basic(rInt int) string { + return fmt.Sprintf(` +resource "tfe_organization" "foobar" { + name = "tst-terraform-%d" + email = "admin@company.com" +} + +resource "tfe_team" "foobar" { + name = "team-test" + organization = tfe_organization.foobar.id +} + +resource "tfe_team_notification_configuration" "foobar" { + name = "notification_basic" + destination_type = "generic" + url = "%s" + team_id = tfe_team.foobar.id +}`, rInt, runTasksURL()) +} + +func testAccTFETeamNotificationConfiguration_emailUserIDs(rInt int) string { + return fmt.Sprintf(` +resource "tfe_organization" "foobar" { + name = "tst-terraform-%d" + email = "admin@company.com" +} + +resource "tfe_team" "foobar" { + name = "team-test" + organization = tfe_organization.foobar.id +} + +resource "tfe_organization_membership" "foobar" { + organization = tfe_organization.foobar.id + email = "foo@foobar.com" +} + +resource "tfe_team_notification_configuration" "foobar" { + name = "notification_email" + destination_type = "email" + team_id = tfe_team.foobar.id +}`, rInt) +} + +func testAccTFETeamNotificationConfiguration_slack(rInt int) string { + return fmt.Sprintf(` +resource "tfe_organization" "foobar" { + name = "tst-terraform-%d" + email = "admin@company.com" +} + +resource "tfe_team" "foobar" { + name = "team-test" + organization = tfe_organization.foobar.id +} + +resource "tfe_team_notification_configuration" "foobar" { + name = "notification_slack" + destination_type = "slack" + url = "%s" + team_id = tfe_team.foobar.id +}`, rInt, runTasksURL()) +} + +func testAccTFETeamNotificationConfiguration_microsoftTeams(rInt int) string { + return fmt.Sprintf(` +resource "tfe_organization" "foobar" { + name = "tst-terraform-%d" + email = "admin@company.com" +} + +resource "tfe_team" "foobar" { + name = "team-test" + organization = tfe_organization.foobar.id +} + +resource "tfe_team_notification_configuration" "foobar" { + name = "notification_msteams" + destination_type = "microsoft-teams" + url = "%s" + team_id = tfe_team.foobar.id +}`, rInt, runTasksURL()) +} + +func testAccTFETeamNotificationConfiguration_badDestinationType(rInt int) string { + return fmt.Sprintf(` +resource "tfe_organization" "foobar" { + name = "tst-terraform-%d" + email = "admin@company.com" +} + +resource "tfe_team" "foobar" { + name = "team-test" + organization = tfe_organization.foobar.id +} + +resource "tfe_team_notification_configuration" "foobar" { + name = "notification_basic" + destination_type = "bad_type" + url = "%s" + team_id = tfe_team.foobar.id +}`, rInt, runTasksURL()) +} + +func testAccTFETeamNotificationConfiguration_update(rInt int) string { + return fmt.Sprintf(` +resource "tfe_organization" "foobar" { + name = "tst-terraform-%d" + email = "admin@company.com" +} + +resource "tfe_team" "foobar" { + name = "team-test" + organization = tfe_organization.foobar.id +} + +resource "tfe_team_notification_configuration" "foobar" { + name = "notification_update" + destination_type = "generic" + enabled = true + token = "1234567890_update" + triggers = ["change_request:created"] + url = "%s?update=true" + team_id = tfe_team.foobar.id +}`, rInt, runTasksURL()) +} + +func testAccTFETeamNotificationConfiguration_updateEmailUserIDs(rInt int) string { + return fmt.Sprintf(`resource "tfe_organization" "foobar" { + name = "tst-terraform-%d" + email = "admin@company.com" +} + +resource "tfe_team" "foobar" { + name = "team-test" + organization = tfe_organization.foobar.id +} + +resource "tfe_organization_membership" "foobar" { + organization = tfe_organization.foobar.id + email = "foo@foobar.com" +} + +resource "tfe_team_organization_member" "foobar" { + team_id = tfe_team.foobar.id + organization_membership_id = tfe_organization_membership.foobar.id +} + +resource "tfe_team_notification_configuration" "foobar" { + name = "notification_email_update" + destination_type = "email" + email_user_ids = [tfe_organization_membership.foobar.user_id] + enabled = true + triggers = ["change_request:created"] + team_id = tfe_team.foobar.id + + depends_on = [tfe_team_organization_member.foobar] +}`, rInt) +} + +func testAccTFETeamNotificationConfiguration_emailWithURL(rInt int) string { + return fmt.Sprintf(` +resource "tfe_organization" "foobar" { + name = "tst-terraform-%d" + email = "admin@company.com" +} + +resource "tfe_team" "foobar" { + name = "team-test" + organization = tfe_organization.foobar.id +} + +resource "tfe_team_notification_configuration" "foobar" { + name = "notification_email_with_url" + destination_type = "email" + url = "%s" + team_id = tfe_team.foobar.id +}`, rInt, runTasksURL()) +} + +func testAccTFETeamNotificationConfiguration_emailWithToken(rInt int) string { + return fmt.Sprintf(` +resource "tfe_organization" "foobar" { + name = "tst-terraform-%d" + email = "admin@company.com" +} + +resource "tfe_team" "foobar" { + name = "team-test" + organization = tfe_organization.foobar.id +} + +resource "tfe_team_notification_configuration" "foobar" { + name = "notification_email_with_token" + destination_type = "email" + token = "1234567890" + team_id = tfe_team.foobar.id +}`, rInt) +} + +func testAccTFETeamNotificationConfiguration_genericWithEmailAddresses(rInt int) string { + return fmt.Sprintf(` +resource "tfe_organization" "foobar" { + name = "tst-terraform-%d" + email = "admin@company.com" +} + +resource "tfe_team" "foobar" { + name = "team-test" + organization = tfe_organization.foobar.id +} + +resource "tfe_team_notification_configuration" "foobar" { + name = "notification_generic_with_email_addresses" + destination_type = "generic" + email_addresses = ["test@example.com", "test2@example.com"] + team_id = tfe_team.foobar.id +}`, rInt) +} + +func testAccTFETeamNotificationConfiguration_genericWithEmailUserIDs(rInt int) string { + return fmt.Sprintf(` +resource "tfe_organization" "foobar" { + name = "tst-terraform-%d" + email = "admin@company.com" +} + +resource "tfe_team" "foobar" { + name = "team-test" + organization = tfe_organization.foobar.id +} + +resource "tfe_organization_membership" "foobar" { + organization = tfe_organization.foobar.id + email = "foo@foobar.com" +} + +resource "tfe_team_notification_configuration" "foobar" { + name = "notification_generic_with_email_user_ids" + destination_type = "generic" + email_user_ids = [tfe_organization_membership.foobar.id] + team_id = tfe_team.foobar.id +}`, rInt) +} + +func testAccTFETeamNotificationConfiguration_genericWithoutURL(rInt int) string { + return fmt.Sprintf(` +resource "tfe_organization" "foobar" { + name = "tst-terraform-%d" + email = "admin@company.com" +} + +resource "tfe_team" "foobar" { + name = "team-test" + organization = tfe_organization.foobar.id +} + +resource "tfe_team_notification_configuration" "foobar" { + name = "notification_generic_without_url" + destination_type = "generic" + team_id = tfe_team.foobar.id +}`, rInt) +} + +func testAccTFETeamNotificationConfiguration_slackWithEmailAddresses(rInt int) string { + return fmt.Sprintf(` +resource "tfe_organization" "foobar" { + name = "tst-terraform-%d" + email = "admin@company.com" +} + +resource "tfe_team" "foobar" { + name = "team-test" + organization = tfe_organization.foobar.id +} + +resource "tfe_team_notification_configuration" "foobar" { + name = "notification_slack_with_email_addresses" + destination_type = "slack" + email_addresses = ["test@example.com", "test2@example.com"] + team_id = tfe_team.foobar.id +}`, rInt) +} + +func testAccTFETeamNotificationConfiguration_slackWithEmailUserIDs(rInt int) string { + return fmt.Sprintf(` +resource "tfe_organization" "foobar" { + name = "tst-terraform-%d" + email = "admin@company.com" +} + +resource "tfe_team" "foobar" { + name = "team-test" + organization = tfe_organization.foobar.id +} + +resource "tfe_organization_membership" "foobar" { + organization = tfe_organization.foobar.id + email = "foo@foobar.com" +} + +resource "tfe_team_notification_configuration" "foobar" { + name = "notification_slack_with_email_user_ids" + destination_type = "slack" + email_user_ids = [tfe_organization_membership.foobar.id] + team_id = tfe_team.foobar.id +}`, rInt) +} + +func testAccTFETeamNotificationConfiguration_slackWithToken(rInt int) string { + return fmt.Sprintf(` +resource "tfe_organization" "foobar" { + name = "tst-terraform-%d" + email = "admin@company.com" +} + +resource "tfe_team" "foobar" { + name = "team-test" + organization = tfe_organization.foobar.id +} + +resource "tfe_team_notification_configuration" "foobar" { + name = "notification_slack_with_token" + destination_type = "slack" + token = "1234567890" + url = "%s" + team_id = tfe_team.foobar.id +}`, rInt, runTasksURL()) +} + +func testAccTFETeamNotificationConfiguration_slackWithoutURL(rInt int) string { + return fmt.Sprintf(` +resource "tfe_organization" "foobar" { + name = "tst-terraform-%d" + email = "admin@company.com" +} + +resource "tfe_team" "foobar" { + name = "team-test" + organization = tfe_organization.foobar.id +} + +resource "tfe_team_notification_configuration" "foobar" { + name = "notification_slack_without_url" + destination_type = "slack" + team_id = tfe_team.foobar.id +}`, rInt) +} + +func testAccTFETeamNotificationConfiguration_microsoftTeamsWithEmailAddresses(rInt int) string { + return fmt.Sprintf(` +resource "tfe_organization" "foobar" { + name = "tst-terraform-%d" + email = "admin@company.com" +} + +resource "tfe_team" "foobar" { + name = "team-test" + organization = tfe_organization.foobar.id +} + +resource "tfe_team_notification_configuration" "foobar" { + name = "notification_msteams_with_email_addresses" + destination_type = "microsoft-teams" + email_addresses = ["test@example.com", "test2@example.com"] + team_id = tfe_team.foobar.id +}`, rInt) +} + +func testAccTFETeamNotificationConfiguration_microsoftTeamsWithEmailUserIDs(rInt int) string { + return fmt.Sprintf(` +resource "tfe_organization" "foobar" { + name = "tst-terraform-%d" + email = "admin@company.com" +} + +resource "tfe_team" "foobar" { + name = "team-test" + organization = tfe_organization.foobar.id +} + +resource "tfe_organization_membership" "foobar" { + organization = tfe_organization.foobar.id + email = "foo@foobar.com" +} + +resource "tfe_team_notification_configuration" "foobar" { + name = "notification_msteams_with_email_user_ids" + destination_type = "microsoft-teams" + email_user_ids = [tfe_organization_membership.foobar.id] + team_id = tfe_team.foobar.id +}`, rInt) +} + +func testAccTFETeamNotificationConfiguration_microsoftTeamsWithToken(rInt int) string { + return fmt.Sprintf(` +resource "tfe_organization" "foobar" { + name = "tst-terraform-%d" + email = "admin@company.com" +} + +resource "tfe_team" "foobar" { + name = "team-test" + organization = tfe_organization.foobar.id +} + +resource "tfe_team_notification_configuration" "foobar" { + name = "notification_msteams_with_token" + destination_type = "microsoft-teams" + token = "1234567890" + url = "%s" + team_id = tfe_team.foobar.id +}`, rInt, runTasksURL()) +} + +func testAccTFETeamNotificationConfiguration_microsoftTeamsWithoutURL(rInt int) string { + return fmt.Sprintf(` +resource "tfe_organization" "foobar" { + name = "tst-terraform-%d" + email = "admin@company.com" +} + +resource "tfe_team" "foobar" { + name = "team-test" + organization = tfe_organization.foobar.id +} + +resource "tfe_team_notification_configuration" "foobar" { + name = "notification_msteams_without_url" + destination_type = "microsoft-teams" + team_id = tfe_team.foobar.id +}`, rInt) +} + +func testAccTFETeamNotificationConfiguration_duplicateTriggers(rInt int) string { + return fmt.Sprintf(` +resource "tfe_organization" "foobar" { + name = "tst-terraform-%d" + email = "admin@company.com" +} + +resource "tfe_team" "foobar" { + name = "team-test" + organization = tfe_organization.foobar.id +} + +resource "tfe_team_notification_configuration" "foobar" { + name = "notification_duplicate_triggers" + destination_type = "generic" + triggers = ["change_request:created", "change_request:created", "change_request:created"] + url = "%s" + team_id = tfe_team.foobar.id +}`, rInt, runTasksURL()) +} + +func preCheckTFETeamNotificationConfiguration(t *testing.T) { + testAccPreCheck(t) + + if runTasksURL() == "" { + t.Skip("RUN_TASKS_URL must be set for team notification configuration acceptance tests") + } +} diff --git a/internal/provider/validators/attribute_required_if_value_string.go b/internal/provider/validators/attribute_required_if_value_string.go new file mode 100644 index 000000000..6ffb09528 --- /dev/null +++ b/internal/provider/validators/attribute_required_if_value_string.go @@ -0,0 +1,53 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package validators + +import ( + "context" + "fmt" + + "github.com/hashicorp/terraform-plugin-framework/path" + "github.com/hashicorp/terraform-plugin-framework/schema/validator" + "github.com/hashicorp/terraform-plugin-framework/types" +) + +type attributeRequiredIfValueStringValidator struct { + attributeName string + requiredValues []string +} + +func (v attributeRequiredIfValueStringValidator) Description(ctx context.Context) string { + return fmt.Sprintf("Ensures the attribute is required if '%s' is one of %v", v.attributeName, v.requiredValues) +} + +func (v attributeRequiredIfValueStringValidator) MarkdownDescription(ctx context.Context) string { + return v.Description(ctx) +} + +func (v attributeRequiredIfValueStringValidator) ValidateString(ctx context.Context, req validator.StringRequest, resp *validator.StringResponse) { + var attributeValue types.String + diags := req.Config.GetAttribute(ctx, path.Root(v.attributeName), &attributeValue) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + for _, requiredValue := range v.requiredValues { + if attributeValue.ValueString() == requiredValue && (req.ConfigValue.IsNull() || req.ConfigValue.IsUnknown()) { + resp.Diagnostics.AddAttributeError( + req.Path, + "Missing Required Attribute", + fmt.Sprintf("The attribute '%s' is required when '%s' is '%s'", req.Path, v.attributeName, requiredValue), + ) + return + } + } +} + +func AttributeRequiredIfValueString(attributeName string, requiredValues []string) validator.String { + return attributeRequiredIfValueStringValidator{ + attributeName: attributeName, + requiredValues: requiredValues, + } +} diff --git a/internal/provider/validators/attribute_value_conflict_set.go b/internal/provider/validators/attribute_value_conflict_set.go new file mode 100644 index 000000000..457a9b9ba --- /dev/null +++ b/internal/provider/validators/attribute_value_conflict_set.go @@ -0,0 +1,54 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package validators + +import ( + "context" + "fmt" + + "github.com/hashicorp/terraform-plugin-framework/path" + "github.com/hashicorp/terraform-plugin-framework/schema/validator" + "github.com/hashicorp/terraform-plugin-framework/types" +) + +type attributeValueConflictSetValidator struct { + attributeName string + conflictingValues []string +} + +func (v attributeValueConflictSetValidator) Description(ctx context.Context) string { + return fmt.Sprintf("Ensures the attribute is not set if %s is one of %v", v.attributeName, v.conflictingValues) +} + +func (v attributeValueConflictSetValidator) MarkdownDescription(ctx context.Context) string { + return v.Description(ctx) +} + +func (v attributeValueConflictSetValidator) ValidateSet(ctx context.Context, req validator.SetRequest, resp *validator.SetResponse) { + if req.ConfigValue.IsNull() || req.ConfigValue.IsUnknown() { + return + } + + var attributeValue types.String + diags := req.Config.GetAttribute(ctx, path.Root(v.attributeName), &attributeValue) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + for _, conflictingValue := range v.conflictingValues { + if attributeValue.ValueString() == conflictingValue { + resp.Diagnostics.AddError( + "Invalid Attribute Value", + fmt.Sprintf("The attribute '%s' cannot be set when '%s' is '%s'", req.Path, v.attributeName, conflictingValue), + ) + + return + } + } +} + +func AttributeValueConflictSetValidator(attributeName string, conflictingValues []string) validator.Set { + return attributeValueConflictSetValidator{attributeName: attributeName, conflictingValues: conflictingValues} +} diff --git a/internal/provider/validators/attribute_value_conflict_string.go b/internal/provider/validators/attribute_value_conflict_string.go new file mode 100644 index 000000000..94a7d112f --- /dev/null +++ b/internal/provider/validators/attribute_value_conflict_string.go @@ -0,0 +1,56 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package validators + +import ( + "context" + "fmt" + + "github.com/hashicorp/terraform-plugin-framework/path" + "github.com/hashicorp/terraform-plugin-framework/schema/validator" + "github.com/hashicorp/terraform-plugin-framework/types" +) + +type attributeValueConflictStringValidator struct { + attributeName string + conflictingValues []string +} + +func (v attributeValueConflictStringValidator) Description(ctx context.Context) string { + return fmt.Sprintf("Ensures the attribute is not set if %s is one of %v", v.attributeName, v.conflictingValues) +} + +func (v attributeValueConflictStringValidator) MarkdownDescription(ctx context.Context) string { + return v.Description(ctx) +} + +func (v attributeValueConflictStringValidator) ValidateString(ctx context.Context, req validator.StringRequest, resp *validator.StringResponse) { + if req.ConfigValue.IsNull() || req.ConfigValue.IsUnknown() { + return + } + + var attributeValue types.String + diags := req.Config.GetAttribute(ctx, path.Root(v.attributeName), &attributeValue) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + for _, conflictingValue := range v.conflictingValues { + if attributeValue.ValueString() == conflictingValue { + resp.Diagnostics.AddError( + "Invalid Attribute Value", + fmt.Sprintf("The attribute '%s' cannot be set when '%s' is '%s'", req.Path, v.attributeName, conflictingValue), + ) + return + } + } +} + +func AttributeValueConflictStringValidator(attributeName string, conflictingValues []string) validator.String { + return attributeValueConflictStringValidator{ + attributeName: attributeName, + conflictingValues: conflictingValues, + } +} diff --git a/website/docs/r/team_notification_configuration.html.markdown b/website/docs/r/team_notification_configuration.html.markdown new file mode 100644 index 000000000..c25f93afe --- /dev/null +++ b/website/docs/r/team_notification_configuration.html.markdown @@ -0,0 +1,132 @@ +--- +layout: "tfe" +page_title: "Terraform Enterprise: tfe_team_notification_configuration" +description: |- + Manages team notifications configurations. +--- + +# tfe_team_notification_configuration + +HCP Terraform can be configured to send notifications to a team for certain events. +Team notification configurations allow you to specify a URL, destination type, and what events will trigger the notification. +Each team can have up to 20 notification configurations, and they apply to configured events for all workspaces that the configured team has access to. + +## Example Usage + +Basic usage: + +```hcl +resource "tfe_organization" "test" { + name = "my-org-name" + email = "admin@company.com" +} + +resource "tfe_team" "test" { + name = "my-team-name" + organization = tfe_organization.test.id +} + +resource "tfe_team_notification_configuration" "test" { + name = "my-test-notification-configuration" + enabled = true + destination_type = "generic" + triggers = ["change_request:created"] + url = "https://example.com" + team_id = tfe_team.test.id +} +``` + +With `destination_type` of `email`: + +```hcl +resource "tfe_organization" "test" { + name = "my-org-name" + email = "admin@company.com" +} + +resource "tfe_team" "test" { + name = "my-team-name" + organization = tfe_organization.test.id +} + +resource "tfe_organization_membership" "test" { + organization = "my-org-name" + email = "test.member@company.com" +} + +resource "tfe_team_notification_configuration" "test" { + name = "my-test-email-notification-configuration" + enabled = true + destination_type = "email" + email_user_ids = [tfe_organization_membership.test.user_id] + triggers = ["change_request:created"] + team_id = tfe_team.test.id +} +``` + +(**TFE only**) With `destination_type` of `email`, using `email_addresses` list and `email_users`: + +```hcl +resource "tfe_organization" "test" { + name = "my-org-name" + email = "admin@company.com" +} + +resource "tfe_team" "test" { + name = "my-team-name" + organization = tfe_organization.test.id +} + +resource "tfe_organization_membership" "test" { + organization = "my-org-name" + email = "test.member@company.com" +} + +resource "tfe_team_notification_configuration" "test" { + name = "my-test-email-notification-configuration" + enabled = true + destination_type = "email" + email_user_ids = [tfe_organization_membership.test.user_id] + email_addresses = ["user1@company.com", "user2@company.com", "user3@company.com"] + triggers = ["change_request:created"] + team_id = tfe_team.test.id +} +``` + +## Argument Reference + +The following arguments are supported: + +- `name` - (Required) Name of the notification configuration. +- `destination_type` - (Required) The type of notification configuration payload to send. + Valid values are: + - `generic` + - `email` available in HCP Terraform or Terraform Enterprise v202005-1 or later + - `slack` + - `microsoft-teams` available in HCP Terraform or Terraform Enterprise v202206-1 or later +- `email_addresses` - (Optional) **TFE only** A list of email addresses. This value + _must not_ be provided if `destination_type` is `generic`, `microsoft-teams`, or `slack`. +- `email_user_ids` - (Optional) A list of user IDs. This value _must not_ be provided + if `destination_type` is `generic`, `microsoft-teams`, or `slack`. +- `enabled` - (Optional) Whether the notification configuration should be enabled or not. + Disabled configurations will not send any notifications. Defaults to `false`. +- `token` - (Optional) A write-only secure token for the notification configuration, which can + be used by the receiving server to verify request authenticity when configured for notification + configurations with a destination type of `generic`. Defaults to `null`. + This value _must not_ be provided if `destination_type` is `email`, `microsoft-teams`, or `slack`. +- `triggers` - (Optional) The array of triggers for which this notification configuration will + send notifications. Currently, the only valid value is `change_request:created`. +- `url` - (Required if `destination_type` is `generic`, `microsoft-teams`, or `slack`) The HTTP or HTTPS URL of the notification configuration where notification requests will be made. This value _must not_ be provided if `destination_type` is `email`. +- `team_id` - (Required) The ID of the team that owns the notification configuration. + +## Attributes Reference + +- `id` - The ID of the notification configuration. + +## Import + +Team notification configurations can be imported; use `` as the import ID. For example: + +```shell +terraform import tfe_team_notification_configuration.test nc-qV9JnKRkmtMa4zcA +``` From d58284e6b480f5905ff675ae67b32f0464118443 Mon Sep 17 00:00:00 2001 From: Josh Freda Date: Fri, 6 Dec 2024 20:00:28 -0600 Subject: [PATCH 04/17] Update CHANGELOG --- CHANGELOG.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 995da04b3..7d590e17c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,8 @@ +## Unreleased + +FEATURES: +* **New Resource:** `tfe_team_notification_configuration` is a new resource for managing team notification configurations, by @jfreda ([#1540](https://github.com/hashicorp/terraform-provider-tfe/pull/1540)) + ## v0.60.1 BUG FIXES: From 90bbd4665468aa598149a77be200100582bb15cb Mon Sep 17 00:00:00 2001 From: Taylor Chaparro Date: Tue, 7 Jan 2025 16:29:17 -0800 Subject: [PATCH 05/17] base on latest go-tfe changes, update structs --- go.mod | 6 +-- go.sum | 8 ++-- .../provider/client_mock_workspaces_test.go | 4 ++ ...rce_tfe_team_notification_configuration.go | 14 +++---- ...fe_team_notification_configuration_test.go | 38 +++++++++---------- 5 files changed, 37 insertions(+), 33 deletions(-) diff --git a/go.mod b/go.mod index fcc353be0..a775adb87 100644 --- a/go.mod +++ b/go.mod @@ -2,8 +2,6 @@ module github.com/hashicorp/terraform-provider-tfe go 1.21 -replace github.com/hashicorp/go-tfe => ../go-tfe - require ( github.com/agext/levenshtein v1.2.3 // indirect github.com/fatih/color v1.16.0 // indirect @@ -44,7 +42,7 @@ require ( github.com/hashicorp/go-cty v1.4.1-0.20200414143053-d3edf31b6320 // indirect github.com/hashicorp/go-plugin v1.6.0 // indirect github.com/hashicorp/go-uuid v1.0.3 - github.com/hashicorp/jsonapi v1.3.1 + github.com/hashicorp/jsonapi v1.3.2 github.com/hashicorp/logutils v1.0.0 // indirect github.com/hashicorp/terraform-exec v0.20.0 // indirect github.com/hashicorp/terraform-json v0.21.0 // indirect @@ -84,3 +82,5 @@ require ( gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) + +replace github.com/hashicorp/go-tfe => github.com/hashicorp/go-tfe v1.72.1-0.20250107212614-518ceb022e03 diff --git a/go.sum b/go.sum index e54be349f..9d16d6148 100644 --- a/go.sum +++ b/go.sum @@ -66,8 +66,8 @@ github.com/hashicorp/go-retryablehttp v0.7.7 h1:C8hUCYzor8PIfXHa4UrZkU4VvK8o9ISH github.com/hashicorp/go-retryablehttp v0.7.7/go.mod h1:pkQpWZeYWskR+D1tR2O5OcBFOxfA7DoAO6xtkuQnHTk= github.com/hashicorp/go-slug v0.16.0 h1:S/ko9fms1gf6305ktJNUKGxFmscZ+yWvAtsas0SYUyA= github.com/hashicorp/go-slug v0.16.0/go.mod h1:THWVTAXwJEinbsp4/bBRcmbaO5EYNLTqxbG4tZ3gCYQ= -github.com/hashicorp/go-tfe v1.70.0 h1:R5a9Z+jdVz6eRWtSLsl1nw+5Qe/swunZcJgeKK5NQtQ= -github.com/hashicorp/go-tfe v1.70.0/go.mod h1:2rOcdTxXwbWm0W7dCKjC3Ec8KQ+HhW165GiurXNshc4= +github.com/hashicorp/go-tfe v1.72.1-0.20250107212614-518ceb022e03 h1:s5LThLEu3U7J+puf9HKzqswU6ySD1Wqgez8UwOuCxw8= +github.com/hashicorp/go-tfe v1.72.1-0.20250107212614-518ceb022e03/go.mod h1:4/yd7fl8960i71v5q8cmpIiA5wB9rBRfvTpFfD/dbvE= github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= github.com/hashicorp/go-uuid v1.0.3 h1:2gKiV6YVmrJ1i2CKKa9obLvRieoRGviZFL26PcT/Co8= github.com/hashicorp/go-uuid v1.0.3/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= @@ -79,8 +79,8 @@ github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= github.com/hashicorp/hcl/v2 v2.19.1 h1://i05Jqznmb2EXqa39Nsvyan2o5XyMowW5fnCKW5RPI= github.com/hashicorp/hcl/v2 v2.19.1/go.mod h1:ThLC89FV4p9MPW804KVbe/cEXoQ8NZEh+JtMeeGErHE= -github.com/hashicorp/jsonapi v1.3.1 h1:GtPvnmcWgYwCuDGvYT5VZBHcUyFdq9lSyCzDjn1DdPo= -github.com/hashicorp/jsonapi v1.3.1/go.mod h1:kWfdn49yCjQvbpnvY1dxxAuAFzISwrrMDQOcu6NsFoM= +github.com/hashicorp/jsonapi v1.3.2 h1:gP3fX2ZT7qXi+PbwieptzkspIohO2kCSiBUvUTBAbMs= +github.com/hashicorp/jsonapi v1.3.2/go.mod h1:kWfdn49yCjQvbpnvY1dxxAuAFzISwrrMDQOcu6NsFoM= github.com/hashicorp/logutils v1.0.0 h1:dLEQVugN8vlakKOUE3ihGLTZJRB4j+M2cdTm/ORI65Y= github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= github.com/hashicorp/terraform-exec v0.20.0 h1:DIZnPsqzPGuUnq6cH8jWcPunBfY+C+M8JyYF3vpnuEo= diff --git a/internal/provider/client_mock_workspaces_test.go b/internal/provider/client_mock_workspaces_test.go index d931e6770..ff2d10938 100644 --- a/internal/provider/client_mock_workspaces_test.go +++ b/internal/provider/client_mock_workspaces_test.go @@ -165,6 +165,10 @@ func (m *mockWorkspaces) RemoveTags(ctx context.Context, workspaceID string, opt panic("not implemented") } +func (m *mockWorkspaces) DeleteAllTagBindings(ctx context.Context, workspaceID string) error { + panic("not implemented") +} + func (m *mockWorkspaces) SafeDelete(ctx context.Context, organization string, workspace string) error { panic("not implemented") } diff --git a/internal/provider/resource_tfe_team_notification_configuration.go b/internal/provider/resource_tfe_team_notification_configuration.go index e9c194561..bf7193ffe 100644 --- a/internal/provider/resource_tfe_team_notification_configuration.go +++ b/internal/provider/resource_tfe_team_notification_configuration.go @@ -56,7 +56,7 @@ type modelTFETeamNotificationConfiguration struct { // modelFromTFETeamNotificationConfiguration builds a modelTFETeamNotificationConfiguration // struct from a tfe.TeamNotificationConfiguration value. -func modelFromTFETeamNotificationConfiguration(v *tfe.TeamNotificationConfiguration) modelTFETeamNotificationConfiguration { +func modelFromTFETeamNotificationConfiguration(v *tfe.NotificationConfiguration) modelTFETeamNotificationConfiguration { result := modelTFETeamNotificationConfiguration{ ID: types.StringValue(v.ID), Name: types.StringValue(v.Name), @@ -272,7 +272,7 @@ func (r *resourceTFETeamNotificationConfiguration) Create(ctx context.Context, r teamID := plan.TeamID.ValueString() // Create a new options struct - options := tfe.TeamNotificationConfigurationCreateOptions{ + options := tfe.NotificationConfigurationCreateOptions{ DestinationType: tfe.NotificationDestination(tfe.NotificationDestinationType(plan.DestinationType.ValueString())), Enabled: plan.Enabled.ValueBoolPointer(), Name: plan.Name.ValueStringPointer(), @@ -314,7 +314,7 @@ func (r *resourceTFETeamNotificationConfiguration) Create(ctx context.Context, r } tflog.Debug(ctx, "Creating team notification configuration") - tnc, err := r.config.Client.TeamNotificationConfigurations.Create(ctx, teamID, options) + tnc, err := r.config.Client.NotificationConfigurations.Create(ctx, teamID, options) if err != nil { resp.Diagnostics.AddError("Unable to create team notification configuration", err.Error()) return @@ -342,7 +342,7 @@ func (r *resourceTFETeamNotificationConfiguration) Read(ctx context.Context, req } tflog.Debug(ctx, fmt.Sprintf("Reading team notification configuration %q", state.ID.ValueString())) - tnc, err := r.config.Client.TeamNotificationConfigurations.Read(ctx, state.ID.ValueString()) + tnc, err := r.config.Client.NotificationConfigurations.Read(ctx, state.ID.ValueString()) if err != nil { resp.Diagnostics.AddError("Unable to read team notification configuration", err.Error()) return @@ -374,7 +374,7 @@ func (r *resourceTFETeamNotificationConfiguration) Update(ctx context.Context, r } // Create a new options struct - options := tfe.TeamNotificationConfigurationUpdateOptions{ + options := tfe.NotificationConfigurationUpdateOptions{ Enabled: plan.Enabled.ValueBoolPointer(), Name: plan.Name.ValueStringPointer(), Token: plan.Token.ValueStringPointer(), @@ -415,7 +415,7 @@ func (r *resourceTFETeamNotificationConfiguration) Update(ctx context.Context, r } tflog.Debug(ctx, "Updating team notification configuration") - tnc, err := r.config.Client.TeamNotificationConfigurations.Update(ctx, state.ID.ValueString(), options) + tnc, err := r.config.Client.NotificationConfigurations.Update(ctx, state.ID.ValueString(), options) if err != nil { resp.Diagnostics.AddError("Unable to update team notification configuration", err.Error()) return @@ -443,7 +443,7 @@ func (r *resourceTFETeamNotificationConfiguration) Delete(ctx context.Context, r } tflog.Debug(ctx, "Deleting team notification configuration") - err := r.config.Client.TeamNotificationConfigurations.Delete(ctx, state.ID.ValueString()) + err := r.config.Client.NotificationConfigurations.Delete(ctx, state.ID.ValueString()) if err != nil { resp.Diagnostics.AddError("Unable to delete team notification configuration", err.Error()) return diff --git a/internal/provider/resource_tfe_team_notification_configuration_test.go b/internal/provider/resource_tfe_team_notification_configuration_test.go index f6d2712d9..3a598329c 100644 --- a/internal/provider/resource_tfe_team_notification_configuration_test.go +++ b/internal/provider/resource_tfe_team_notification_configuration_test.go @@ -17,7 +17,7 @@ import ( ) func TestAccTFETeamNotificationConfiguration_basic(t *testing.T) { - notificationConfiguration := &tfe.TeamNotificationConfiguration{} + notificationConfiguration := &tfe.NotificationConfiguration{} rInt := rand.New(rand.NewSource(time.Now().UnixNano())).Int() resource.Test(t, resource.TestCase{ @@ -48,7 +48,7 @@ func TestAccTFETeamNotificationConfiguration_basic(t *testing.T) { } func TestAccTFETeamNotificationConfiguration_emailUserIDs(t *testing.T) { - notificationConfiguration := &tfe.TeamNotificationConfiguration{} + notificationConfiguration := &tfe.NotificationConfiguration{} rInt := rand.New(rand.NewSource(time.Now().UnixNano())).Int() resource.Test(t, resource.TestCase{ @@ -79,7 +79,7 @@ func TestAccTFETeamNotificationConfiguration_emailUserIDs(t *testing.T) { } func TestAccTFETeamNotificationConfiguration_update(t *testing.T) { - notificationConfiguration := &tfe.TeamNotificationConfiguration{} + notificationConfiguration := &tfe.NotificationConfiguration{} rInt := rand.New(rand.NewSource(time.Now().UnixNano())).Int() resource.Test(t, resource.TestCase{ @@ -132,7 +132,7 @@ func TestAccTFETeamNotificationConfiguration_update(t *testing.T) { } func TestAccTFETeamNotificationConfiguration_updateEmailUserIDs(t *testing.T) { - notificationConfiguration := &tfe.TeamNotificationConfiguration{} + notificationConfiguration := &tfe.NotificationConfiguration{} rInt := rand.New(rand.NewSource(time.Now().UnixNano())).Int() resource.Test(t, resource.TestCase{ @@ -294,7 +294,7 @@ func TestAccTFETeamNotificationConfiguration_validateSchemaAttributesBadDestinat } func TestAccTFETeamNotificationConfiguration_updateValidateSchemaAttributesEmail(t *testing.T) { - notificationConfiguration := &tfe.TeamNotificationConfiguration{} + notificationConfiguration := &tfe.NotificationConfiguration{} rInt := rand.New(rand.NewSource(time.Now().UnixNano())).Int() resource.Test(t, resource.TestCase{ @@ -336,7 +336,7 @@ func TestAccTFETeamNotificationConfiguration_updateValidateSchemaAttributesEmail } func TestAccTFETeamNotificationConfiguration_updateValidateSchemaAttributesGeneric(t *testing.T) { - notificationConfiguration := &tfe.TeamNotificationConfiguration{} + notificationConfiguration := &tfe.NotificationConfiguration{} rInt := rand.New(rand.NewSource(time.Now().UnixNano())).Int() resource.Test(t, resource.TestCase{ @@ -382,7 +382,7 @@ func TestAccTFETeamNotificationConfiguration_updateValidateSchemaAttributesGener } func TestAccTFETeamNotificationConfiguration_updateValidateSchemaAttributesSlack(t *testing.T) { - notificationConfiguration := &tfe.TeamNotificationConfiguration{} + notificationConfiguration := &tfe.NotificationConfiguration{} rInt := rand.New(rand.NewSource(time.Now().UnixNano())).Int() resource.Test(t, resource.TestCase{ @@ -432,7 +432,7 @@ func TestAccTFETeamNotificationConfiguration_updateValidateSchemaAttributesSlack } func TestAccTFETeamNotificationConfiguration_updateValidateSchemaAttributesMicrosoftTeams(t *testing.T) { - notificationConfiguration := &tfe.TeamNotificationConfiguration{} + notificationConfiguration := &tfe.NotificationConfiguration{} rInt := rand.New(rand.NewSource(time.Now().UnixNano())).Int() resource.Test(t, resource.TestCase{ @@ -478,7 +478,7 @@ func TestAccTFETeamNotificationConfiguration_updateValidateSchemaAttributesMicro } func TestAccTFETeamNotificationConfiguration_duplicateTriggers(t *testing.T) { - notificationConfiguration := &tfe.TeamNotificationConfiguration{} + notificationConfiguration := &tfe.NotificationConfiguration{} rInt := rand.New(rand.NewSource(time.Now().UnixNano())).Int() resource.Test(t, resource.TestCase{ @@ -574,7 +574,7 @@ func TestAccTFETeamNotificationConfigurationImport_emptyEmailUserIDs(t *testing. }) } -func testAccCheckTFETeamNotificationConfigurationExists(n string, notificationConfiguration *tfe.TeamNotificationConfiguration) resource.TestCheckFunc { +func testAccCheckTFETeamNotificationConfigurationExists(n string, notificationConfiguration *tfe.NotificationConfiguration) resource.TestCheckFunc { return func(s *terraform.State) error { config := testAccProvider.Meta().(ConfiguredClient) @@ -587,7 +587,7 @@ func testAccCheckTFETeamNotificationConfigurationExists(n string, notificationCo return fmt.Errorf("No instance ID is set") } - nc, err := config.Client.TeamNotificationConfigurations.Read(ctx, rs.Primary.ID) + nc, err := config.Client.NotificationConfigurations.Read(ctx, rs.Primary.ID) if err != nil { return err } @@ -598,7 +598,7 @@ func testAccCheckTFETeamNotificationConfigurationExists(n string, notificationCo } } -func testAccCheckTFETeamNotificationConfigurationAttributes(notificationConfiguration *tfe.TeamNotificationConfiguration) resource.TestCheckFunc { +func testAccCheckTFETeamNotificationConfigurationAttributes(notificationConfiguration *tfe.NotificationConfiguration) resource.TestCheckFunc { return func(s *terraform.State) error { if notificationConfiguration.Name != "notification_basic" { return fmt.Errorf("Bad name: %s", notificationConfiguration.Name) @@ -626,7 +626,7 @@ func testAccCheckTFETeamNotificationConfigurationAttributes(notificationConfigur } } -func testAccCheckTFETeamNotificationConfigurationAttributesUpdate(notificationConfiguration *tfe.TeamNotificationConfiguration) resource.TestCheckFunc { +func testAccCheckTFETeamNotificationConfigurationAttributesUpdate(notificationConfiguration *tfe.NotificationConfiguration) resource.TestCheckFunc { return func(s *terraform.State) error { if notificationConfiguration.Name != "notification_update" { return fmt.Errorf("Bad name: %s", notificationConfiguration.Name) @@ -654,7 +654,7 @@ func testAccCheckTFETeamNotificationConfigurationAttributesUpdate(notificationCo } } -func testAccCheckTFETeamNotificationConfigurationAttributesEmailUserIDs(notificationConfiguration *tfe.TeamNotificationConfiguration) resource.TestCheckFunc { +func testAccCheckTFETeamNotificationConfigurationAttributesEmailUserIDs(notificationConfiguration *tfe.NotificationConfiguration) resource.TestCheckFunc { return func(s *terraform.State) error { if notificationConfiguration.Name != "notification_email" { return fmt.Errorf("Bad name: %s", notificationConfiguration.Name) @@ -682,7 +682,7 @@ func testAccCheckTFETeamNotificationConfigurationAttributesEmailUserIDs(notifica } } -func testAccCheckTFETeamNotificationConfigurationAttributesUpdateEmailUserIDs(notificationConfiguration *tfe.TeamNotificationConfiguration) resource.TestCheckFunc { +func testAccCheckTFETeamNotificationConfigurationAttributesUpdateEmailUserIDs(notificationConfiguration *tfe.NotificationConfiguration) resource.TestCheckFunc { return func(s *terraform.State) error { if notificationConfiguration.Name != "notification_email_update" { return fmt.Errorf("Bad name: %s", notificationConfiguration.Name) @@ -710,7 +710,7 @@ func testAccCheckTFETeamNotificationConfigurationAttributesUpdateEmailUserIDs(no } } -func testAccCheckTFETeamNotificationConfigurationAttributesSlack(notificationConfiguration *tfe.TeamNotificationConfiguration) resource.TestCheckFunc { +func testAccCheckTFETeamNotificationConfigurationAttributesSlack(notificationConfiguration *tfe.NotificationConfiguration) resource.TestCheckFunc { return func(s *terraform.State) error { if notificationConfiguration.Name != "notification_slack" { return fmt.Errorf("Bad name: %s", notificationConfiguration.Name) @@ -734,7 +734,7 @@ func testAccCheckTFETeamNotificationConfigurationAttributesSlack(notificationCon } } -func testAccCheckTFETeamNotificationConfigurationAttributesMicrosoftTeams(notificationConfiguration *tfe.TeamNotificationConfiguration) resource.TestCheckFunc { +func testAccCheckTFETeamNotificationConfigurationAttributesMicrosoftTeams(notificationConfiguration *tfe.NotificationConfiguration) resource.TestCheckFunc { return func(s *terraform.State) error { if notificationConfiguration.Name != "notification_msteams" { return fmt.Errorf("Bad name: %s", notificationConfiguration.Name) @@ -760,7 +760,7 @@ func testAccCheckTFETeamNotificationConfigurationAttributesMicrosoftTeams(notifi } } -func testAccCheckTFETeamNotificationConfigurationAttributesDuplicateTriggers(notificationConfiguration *tfe.TeamNotificationConfiguration) resource.TestCheckFunc { +func testAccCheckTFETeamNotificationConfigurationAttributesDuplicateTriggers(notificationConfiguration *tfe.NotificationConfiguration) resource.TestCheckFunc { return func(s *terraform.State) error { if notificationConfiguration.Name != "notification_duplicate_triggers" { return fmt.Errorf("Bad name: %s", notificationConfiguration.Name) @@ -800,7 +800,7 @@ func testAccCheckTFETeamNotificationConfigurationDestroy(s *terraform.State) err return fmt.Errorf("No instance ID is set") } - _, err := config.Client.TeamNotificationConfigurations.Read(ctx, rs.Primary.ID) + _, err := config.Client.NotificationConfigurations.Read(ctx, rs.Primary.ID) if err == nil { return fmt.Errorf("Notification configuration %s still exists", rs.Primary.ID) } From 0ee9d4b7cf7968df84a452e82ba71ff6dd9b0bb9 Mon Sep 17 00:00:00 2001 From: Taylor Chaparro Date: Wed, 8 Jan 2025 09:41:37 -0800 Subject: [PATCH 06/17] fix: using polymorphic field when creating --- .../provider/resource_tfe_team_notification_configuration.go | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/internal/provider/resource_tfe_team_notification_configuration.go b/internal/provider/resource_tfe_team_notification_configuration.go index bf7193ffe..d187d89c5 100644 --- a/internal/provider/resource_tfe_team_notification_configuration.go +++ b/internal/provider/resource_tfe_team_notification_configuration.go @@ -62,7 +62,7 @@ func modelFromTFETeamNotificationConfiguration(v *tfe.NotificationConfiguration) Name: types.StringValue(v.Name), DestinationType: types.StringValue(string(v.DestinationType)), Enabled: types.BoolValue(v.Enabled), - TeamID: types.StringValue(v.Subscribable.ID), + TeamID: types.StringValue(v.SubscribableChoice.Team.ID), } emailAddresses := make([]attr.Value, len(v.EmailAddresses)) @@ -278,6 +278,9 @@ func (r *resourceTFETeamNotificationConfiguration) Create(ctx context.Context, r Name: plan.Name.ValueStringPointer(), Token: plan.Token.ValueStringPointer(), URL: plan.URL.ValueStringPointer(), + SubscribableChoice: &tfe.NotificationConfigurationSubscribableChoice{ + Team: &tfe.Team{ID: teamID}, + }, } // Add triggers set to the options struct From f8323cafdd2cdd37916e9de043df4b9be746f06c Mon Sep 17 00:00:00 2001 From: Taylor Chaparro Date: Wed, 8 Jan 2025 10:47:12 -0800 Subject: [PATCH 07/17] use business org for tests --- ...fe_team_notification_configuration_test.go | 363 ++++++++++++------ 1 file changed, 236 insertions(+), 127 deletions(-) diff --git a/internal/provider/resource_tfe_team_notification_configuration_test.go b/internal/provider/resource_tfe_team_notification_configuration_test.go index 3a598329c..38a1d0811 100644 --- a/internal/provider/resource_tfe_team_notification_configuration_test.go +++ b/internal/provider/resource_tfe_team_notification_configuration_test.go @@ -5,11 +5,9 @@ package provider import ( "fmt" - "math/rand" "reflect" "regexp" "testing" - "time" "github.com/hashicorp/go-tfe" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" @@ -17,8 +15,15 @@ import ( ) func TestAccTFETeamNotificationConfiguration_basic(t *testing.T) { + tfeClient, err := getClientUsingEnv() + if err != nil { + t.Fatal(err) + } + + org, cleanupOrg := createBusinessOrganization(t, tfeClient) + t.Cleanup(cleanupOrg) + notificationConfiguration := &tfe.NotificationConfiguration{} - rInt := rand.New(rand.NewSource(time.Now().UnixNano())).Int() resource.Test(t, resource.TestCase{ PreCheck: func() { preCheckTFETeamNotificationConfiguration(t) }, @@ -26,7 +31,7 @@ func TestAccTFETeamNotificationConfiguration_basic(t *testing.T) { CheckDestroy: testAccCheckTFETeamNotificationConfigurationDestroy, Steps: []resource.TestStep{ { - Config: testAccTFETeamNotificationConfiguration_basic(rInt), + Config: testAccTFETeamNotificationConfiguration_basic(org.Name), Check: resource.ComposeTestCheckFunc( testAccCheckTFETeamNotificationConfigurationExists( "tfe_team_notification_configuration.foobar", notificationConfiguration), @@ -48,8 +53,15 @@ func TestAccTFETeamNotificationConfiguration_basic(t *testing.T) { } func TestAccTFETeamNotificationConfiguration_emailUserIDs(t *testing.T) { + tfeClient, err := getClientUsingEnv() + if err != nil { + t.Fatal(err) + } + + org, cleanupOrg := createBusinessOrganization(t, tfeClient) + t.Cleanup(cleanupOrg) + notificationConfiguration := &tfe.NotificationConfiguration{} - rInt := rand.New(rand.NewSource(time.Now().UnixNano())).Int() resource.Test(t, resource.TestCase{ PreCheck: func() { preCheckTFETeamNotificationConfiguration(t) }, @@ -57,7 +69,7 @@ func TestAccTFETeamNotificationConfiguration_emailUserIDs(t *testing.T) { CheckDestroy: testAccCheckTFETeamNotificationConfigurationDestroy, Steps: []resource.TestStep{ { - Config: testAccTFETeamNotificationConfiguration_emailUserIDs(rInt), + Config: testAccTFETeamNotificationConfiguration_emailUserIDs(org.Name), Check: resource.ComposeTestCheckFunc( testAccCheckTFETeamNotificationConfigurationExists( "tfe_team_notification_configuration.foobar", notificationConfiguration), @@ -79,8 +91,15 @@ func TestAccTFETeamNotificationConfiguration_emailUserIDs(t *testing.T) { } func TestAccTFETeamNotificationConfiguration_update(t *testing.T) { + tfeClient, err := getClientUsingEnv() + if err != nil { + t.Fatal(err) + } + + org, cleanupOrg := createBusinessOrganization(t, tfeClient) + t.Cleanup(cleanupOrg) + notificationConfiguration := &tfe.NotificationConfiguration{} - rInt := rand.New(rand.NewSource(time.Now().UnixNano())).Int() resource.Test(t, resource.TestCase{ PreCheck: func() { preCheckTFETeamNotificationConfiguration(t) }, @@ -88,7 +107,7 @@ func TestAccTFETeamNotificationConfiguration_update(t *testing.T) { CheckDestroy: testAccCheckTFETeamNotificationConfigurationDestroy, Steps: []resource.TestStep{ { - Config: testAccTFETeamNotificationConfiguration_basic(rInt), + Config: testAccTFETeamNotificationConfiguration_basic(org.Name), Check: resource.ComposeTestCheckFunc( testAccCheckTFETeamNotificationConfigurationExists( "tfe_team_notification_configuration.foobar", notificationConfiguration), @@ -106,7 +125,7 @@ func TestAccTFETeamNotificationConfiguration_update(t *testing.T) { ), }, { - Config: testAccTFETeamNotificationConfiguration_update(rInt), + Config: testAccTFETeamNotificationConfiguration_update(org.Name), Check: resource.ComposeTestCheckFunc( testAccCheckTFETeamNotificationConfigurationExists( "tfe_team_notification_configuration.foobar", notificationConfiguration), @@ -132,8 +151,15 @@ func TestAccTFETeamNotificationConfiguration_update(t *testing.T) { } func TestAccTFETeamNotificationConfiguration_updateEmailUserIDs(t *testing.T) { + tfeClient, err := getClientUsingEnv() + if err != nil { + t.Fatal(err) + } + + org, cleanupOrg := createBusinessOrganization(t, tfeClient) + t.Cleanup(cleanupOrg) + notificationConfiguration := &tfe.NotificationConfiguration{} - rInt := rand.New(rand.NewSource(time.Now().UnixNano())).Int() resource.Test(t, resource.TestCase{ PreCheck: func() { preCheckTFETeamNotificationConfiguration(t) }, @@ -141,7 +167,7 @@ func TestAccTFETeamNotificationConfiguration_updateEmailUserIDs(t *testing.T) { CheckDestroy: testAccCheckTFETeamNotificationConfigurationDestroy, Steps: []resource.TestStep{ { - Config: testAccTFETeamNotificationConfiguration_emailUserIDs(rInt), + Config: testAccTFETeamNotificationConfiguration_emailUserIDs(org.Name), Check: resource.ComposeTestCheckFunc( testAccCheckTFETeamNotificationConfigurationExists( "tfe_team_notification_configuration.foobar", notificationConfiguration), @@ -159,7 +185,7 @@ func TestAccTFETeamNotificationConfiguration_updateEmailUserIDs(t *testing.T) { ), }, { - Config: testAccTFETeamNotificationConfiguration_updateEmailUserIDs(rInt), + Config: testAccTFETeamNotificationConfiguration_updateEmailUserIDs(org.Name), Check: resource.ComposeTestCheckFunc( testAccCheckTFETeamNotificationConfigurationExists( "tfe_team_notification_configuration.foobar", notificationConfiguration), @@ -183,18 +209,24 @@ func TestAccTFETeamNotificationConfiguration_updateEmailUserIDs(t *testing.T) { } func TestAccTFETeamNotificationConfiguration_validateSchemaAttributesEmail(t *testing.T) { - rInt := rand.New(rand.NewSource(time.Now().UnixNano())).Int() + tfeClient, err := getClientUsingEnv() + if err != nil { + t.Fatal(err) + } + + org, cleanupOrg := createBusinessOrganization(t, tfeClient) + t.Cleanup(cleanupOrg) resource.Test(t, resource.TestCase{ PreCheck: func() { preCheckTFETeamNotificationConfiguration(t) }, ProtoV5ProviderFactories: testAccMuxedProviders, Steps: []resource.TestStep{ { - Config: testAccTFETeamNotificationConfiguration_emailWithURL(rInt), + Config: testAccTFETeamNotificationConfiguration_emailWithURL(org.Name), ExpectError: regexp.MustCompile(`The attribute 'url' cannot be set when 'destination_type' is 'email'`), }, { - Config: testAccTFETeamNotificationConfiguration_emailWithToken(rInt), + Config: testAccTFETeamNotificationConfiguration_emailWithToken(org.Name), ExpectError: regexp.MustCompile(`The attribute 'token' cannot be set when 'destination_type' is 'email'`), }, }, @@ -202,22 +234,28 @@ func TestAccTFETeamNotificationConfiguration_validateSchemaAttributesEmail(t *te } func TestAccTFETeamNotificationConfiguration_validateSchemaAttributesGeneric(t *testing.T) { - rInt := rand.New(rand.NewSource(time.Now().UnixNano())).Int() + tfeClient, err := getClientUsingEnv() + if err != nil { + t.Fatal(err) + } + + org, cleanupOrg := createBusinessOrganization(t, tfeClient) + t.Cleanup(cleanupOrg) resource.Test(t, resource.TestCase{ PreCheck: func() { preCheckTFETeamNotificationConfiguration(t) }, ProtoV5ProviderFactories: testAccMuxedProviders, Steps: []resource.TestStep{ { - Config: testAccTFETeamNotificationConfiguration_genericWithEmailAddresses(rInt), + Config: testAccTFETeamNotificationConfiguration_genericWithEmailAddresses(org.Name), ExpectError: regexp.MustCompile(`(?s).*The attribute 'email_addresses' cannot be set when 'destination_type' is.*'generic'`), }, { - Config: testAccTFETeamNotificationConfiguration_genericWithEmailUserIDs(rInt), + Config: testAccTFETeamNotificationConfiguration_genericWithEmailUserIDs(org.Name), ExpectError: regexp.MustCompile(`(?s).*The attribute 'email_user_ids' cannot be set when 'destination_type' is.*'generic'`), }, { - Config: testAccTFETeamNotificationConfiguration_genericWithoutURL(rInt), + Config: testAccTFETeamNotificationConfiguration_genericWithoutURL(org.Name), ExpectError: regexp.MustCompile(`The attribute 'url' is required when 'destination_type' is 'generic'`), }, }, @@ -225,26 +263,32 @@ func TestAccTFETeamNotificationConfiguration_validateSchemaAttributesGeneric(t * } func TestAccTFETeamNotificationConfiguration_validateSchemaAttributesSlack(t *testing.T) { - rInt := rand.New(rand.NewSource(time.Now().UnixNano())).Int() + tfeClient, err := getClientUsingEnv() + if err != nil { + t.Fatal(err) + } + + org, cleanupOrg := createBusinessOrganization(t, tfeClient) + t.Cleanup(cleanupOrg) resource.Test(t, resource.TestCase{ PreCheck: func() { preCheckTFETeamNotificationConfiguration(t) }, ProtoV5ProviderFactories: testAccMuxedProviders, Steps: []resource.TestStep{ { - Config: testAccTFETeamNotificationConfiguration_slackWithEmailAddresses(rInt), + Config: testAccTFETeamNotificationConfiguration_slackWithEmailAddresses(org.Name), ExpectError: regexp.MustCompile(`(?s).*The attribute 'email_addresses' cannot be set when 'destination_type' is.*'slack'`), }, { - Config: testAccTFETeamNotificationConfiguration_slackWithEmailUserIDs(rInt), + Config: testAccTFETeamNotificationConfiguration_slackWithEmailUserIDs(org.Name), ExpectError: regexp.MustCompile(`(?s).*The attribute 'email_user_ids' cannot be set when 'destination_type' is.*'slack'`), }, { - Config: testAccTFETeamNotificationConfiguration_slackWithToken(rInt), + Config: testAccTFETeamNotificationConfiguration_slackWithToken(org.Name), ExpectError: regexp.MustCompile(`The attribute 'token' cannot be set when 'destination_type' is 'slack'`), }, { - Config: testAccTFETeamNotificationConfiguration_slackWithoutURL(rInt), + Config: testAccTFETeamNotificationConfiguration_slackWithoutURL(org.Name), ExpectError: regexp.MustCompile(`The attribute 'url' is required when 'destination_type' is 'slack'`), }, }, @@ -252,26 +296,32 @@ func TestAccTFETeamNotificationConfiguration_validateSchemaAttributesSlack(t *te } func TestAccTFETeamNotificationConfiguration_validateSchemaAttributesMicrosoftTeams(t *testing.T) { - rInt := rand.New(rand.NewSource(time.Now().UnixNano())).Int() + tfeClient, err := getClientUsingEnv() + if err != nil { + t.Fatal(err) + } + + org, cleanupOrg := createBusinessOrganization(t, tfeClient) + t.Cleanup(cleanupOrg) resource.Test(t, resource.TestCase{ PreCheck: func() { preCheckTFETeamNotificationConfiguration(t) }, ProtoV5ProviderFactories: testAccMuxedProviders, Steps: []resource.TestStep{ { - Config: testAccTFETeamNotificationConfiguration_microsoftTeamsWithEmailAddresses(rInt), + Config: testAccTFETeamNotificationConfiguration_microsoftTeamsWithEmailAddresses(org.Name), ExpectError: regexp.MustCompile(`(?s).*The attribute 'email_addresses' cannot be set when 'destination_type' is.*'microsoft-teams'`), }, { - Config: testAccTFETeamNotificationConfiguration_microsoftTeamsWithEmailUserIDs(rInt), + Config: testAccTFETeamNotificationConfiguration_microsoftTeamsWithEmailUserIDs(org.Name), ExpectError: regexp.MustCompile(`(?s).*The attribute 'email_user_ids' cannot be set when 'destination_type' is.*'microsoft-teams'`), }, { - Config: testAccTFETeamNotificationConfiguration_microsoftTeamsWithToken(rInt), + Config: testAccTFETeamNotificationConfiguration_microsoftTeamsWithToken(org.Name), ExpectError: regexp.MustCompile(`(?s).*The attribute 'token' cannot be set when 'destination_type' is.*'microsoft-teams'`), }, { - Config: testAccTFETeamNotificationConfiguration_microsoftTeamsWithoutURL(rInt), + Config: testAccTFETeamNotificationConfiguration_microsoftTeamsWithoutURL(org.Name), ExpectError: regexp.MustCompile(`The attribute 'url' is required when 'destination_type' is 'microsoft-teams'`), }, }, @@ -279,14 +329,20 @@ func TestAccTFETeamNotificationConfiguration_validateSchemaAttributesMicrosoftTe } func TestAccTFETeamNotificationConfiguration_validateSchemaAttributesBadDestinationType(t *testing.T) { - rInt := rand.New(rand.NewSource(time.Now().UnixNano())).Int() + tfeClient, err := getClientUsingEnv() + if err != nil { + t.Fatal(err) + } + + org, cleanupOrg := createBusinessOrganization(t, tfeClient) + t.Cleanup(cleanupOrg) resource.Test(t, resource.TestCase{ PreCheck: func() { preCheckTFETeamNotificationConfiguration(t) }, ProtoV5ProviderFactories: testAccMuxedProviders, Steps: []resource.TestStep{ { - Config: testAccTFETeamNotificationConfiguration_badDestinationType(rInt), + Config: testAccTFETeamNotificationConfiguration_badDestinationType(org.Name), ExpectError: regexp.MustCompile(`.*Invalid Attribute Value Match.*`), }, }, @@ -294,8 +350,15 @@ func TestAccTFETeamNotificationConfiguration_validateSchemaAttributesBadDestinat } func TestAccTFETeamNotificationConfiguration_updateValidateSchemaAttributesEmail(t *testing.T) { + tfeClient, err := getClientUsingEnv() + if err != nil { + t.Fatal(err) + } + + org, cleanupOrg := createBusinessOrganization(t, tfeClient) + t.Cleanup(cleanupOrg) + notificationConfiguration := &tfe.NotificationConfiguration{} - rInt := rand.New(rand.NewSource(time.Now().UnixNano())).Int() resource.Test(t, resource.TestCase{ PreCheck: func() { preCheckTFETeamNotificationConfiguration(t) }, @@ -303,7 +366,7 @@ func TestAccTFETeamNotificationConfiguration_updateValidateSchemaAttributesEmail CheckDestroy: testAccCheckTFETeamNotificationConfigurationDestroy, Steps: []resource.TestStep{ { - Config: testAccTFETeamNotificationConfiguration_emailUserIDs(rInt), + Config: testAccTFETeamNotificationConfiguration_emailUserIDs(org.Name), Check: resource.ComposeTestCheckFunc( testAccCheckTFETeamNotificationConfigurationExists( "tfe_team_notification_configuration.foobar", notificationConfiguration), @@ -321,23 +384,30 @@ func TestAccTFETeamNotificationConfiguration_updateValidateSchemaAttributesEmail ), }, { - Config: testAccTFETeamNotificationConfiguration_emailWithURL(rInt), + Config: testAccTFETeamNotificationConfiguration_emailWithURL(org.Name), ExpectError: regexp.MustCompile(`The attribute 'url' cannot be set when 'destination_type' is 'email'`), }, { - Config: testAccTFETeamNotificationConfiguration_emailWithToken(rInt), + Config: testAccTFETeamNotificationConfiguration_emailWithToken(org.Name), ExpectError: regexp.MustCompile(`The attribute 'token' cannot be set when 'destination_type' is 'email'`), }, { - Config: testAccTFETeamNotificationConfiguration_emailUserIDs(rInt), + Config: testAccTFETeamNotificationConfiguration_emailUserIDs(org.Name), }, }, }) } func TestAccTFETeamNotificationConfiguration_updateValidateSchemaAttributesGeneric(t *testing.T) { + tfeClient, err := getClientUsingEnv() + if err != nil { + t.Fatal(err) + } + + org, cleanupOrg := createBusinessOrganization(t, tfeClient) + t.Cleanup(cleanupOrg) + notificationConfiguration := &tfe.NotificationConfiguration{} - rInt := rand.New(rand.NewSource(time.Now().UnixNano())).Int() resource.Test(t, resource.TestCase{ PreCheck: func() { preCheckTFETeamNotificationConfiguration(t) }, @@ -345,7 +415,7 @@ func TestAccTFETeamNotificationConfiguration_updateValidateSchemaAttributesGener CheckDestroy: testAccCheckTFETeamNotificationConfigurationDestroy, Steps: []resource.TestStep{ { - Config: testAccTFETeamNotificationConfiguration_basic(rInt), + Config: testAccTFETeamNotificationConfiguration_basic(org.Name), Check: resource.ComposeTestCheckFunc( testAccCheckTFETeamNotificationConfigurationExists( "tfe_team_notification_configuration.foobar", notificationConfiguration), @@ -363,27 +433,34 @@ func TestAccTFETeamNotificationConfiguration_updateValidateSchemaAttributesGener ), }, { - Config: testAccTFETeamNotificationConfiguration_genericWithEmailAddresses(rInt), + Config: testAccTFETeamNotificationConfiguration_genericWithEmailAddresses(org.Name), ExpectError: regexp.MustCompile(`(?s).*The attribute 'email_addresses' cannot be set when 'destination_type' is.*'generic'`), }, { - Config: testAccTFETeamNotificationConfiguration_genericWithEmailUserIDs(rInt), + Config: testAccTFETeamNotificationConfiguration_genericWithEmailUserIDs(org.Name), ExpectError: regexp.MustCompile(`(?s).*The attribute 'email_user_ids' cannot be set when 'destination_type' is.*'generic'`), }, { - Config: testAccTFETeamNotificationConfiguration_genericWithoutURL(rInt), + Config: testAccTFETeamNotificationConfiguration_genericWithoutURL(org.Name), ExpectError: regexp.MustCompile(`The attribute 'url' is required when 'destination_type' is 'generic'`), }, { - Config: testAccTFETeamNotificationConfiguration_basic(rInt), + Config: testAccTFETeamNotificationConfiguration_basic(org.Name), }, }, }) } func TestAccTFETeamNotificationConfiguration_updateValidateSchemaAttributesSlack(t *testing.T) { + tfeClient, err := getClientUsingEnv() + if err != nil { + t.Fatal(err) + } + + org, cleanupOrg := createBusinessOrganization(t, tfeClient) + t.Cleanup(cleanupOrg) + notificationConfiguration := &tfe.NotificationConfiguration{} - rInt := rand.New(rand.NewSource(time.Now().UnixNano())).Int() resource.Test(t, resource.TestCase{ PreCheck: func() { preCheckTFETeamNotificationConfiguration(t) }, @@ -391,7 +468,7 @@ func TestAccTFETeamNotificationConfiguration_updateValidateSchemaAttributesSlack CheckDestroy: testAccCheckTFETeamNotificationConfigurationDestroy, Steps: []resource.TestStep{ { - Config: testAccTFETeamNotificationConfiguration_slack(rInt), + Config: testAccTFETeamNotificationConfiguration_slack(org.Name), Check: resource.ComposeTestCheckFunc( testAccCheckTFETeamNotificationConfigurationExists( "tfe_team_notification_configuration.foobar", notificationConfiguration), @@ -409,31 +486,38 @@ func TestAccTFETeamNotificationConfiguration_updateValidateSchemaAttributesSlack ), }, { - Config: testAccTFETeamNotificationConfiguration_slackWithEmailAddresses(rInt), + Config: testAccTFETeamNotificationConfiguration_slackWithEmailAddresses(org.Name), ExpectError: regexp.MustCompile(`(?s).*The attribute 'email_addresses' cannot be set when 'destination_type' is.*'slack'`), }, { - Config: testAccTFETeamNotificationConfiguration_slackWithEmailUserIDs(rInt), + Config: testAccTFETeamNotificationConfiguration_slackWithEmailUserIDs(org.Name), ExpectError: regexp.MustCompile(`(?s).*The attribute 'email_user_ids' cannot be set when 'destination_type' is.*'slack'`), }, { - Config: testAccTFETeamNotificationConfiguration_slackWithToken(rInt), + Config: testAccTFETeamNotificationConfiguration_slackWithToken(org.Name), ExpectError: regexp.MustCompile(`The attribute 'token' cannot be set when 'destination_type' is 'slack'`), }, { - Config: testAccTFETeamNotificationConfiguration_slackWithoutURL(rInt), + Config: testAccTFETeamNotificationConfiguration_slackWithoutURL(org.Name), ExpectError: regexp.MustCompile(`The attribute 'url' is required when 'destination_type' is 'slack'`), }, { - Config: testAccTFETeamNotificationConfiguration_slack(rInt), + Config: testAccTFETeamNotificationConfiguration_slack(org.Name), }, }, }) } func TestAccTFETeamNotificationConfiguration_updateValidateSchemaAttributesMicrosoftTeams(t *testing.T) { + tfeClient, err := getClientUsingEnv() + if err != nil { + t.Fatal(err) + } + + org, cleanupOrg := createBusinessOrganization(t, tfeClient) + t.Cleanup(cleanupOrg) + notificationConfiguration := &tfe.NotificationConfiguration{} - rInt := rand.New(rand.NewSource(time.Now().UnixNano())).Int() resource.Test(t, resource.TestCase{ PreCheck: func() { preCheckTFETeamNotificationConfiguration(t) }, @@ -441,7 +525,7 @@ func TestAccTFETeamNotificationConfiguration_updateValidateSchemaAttributesMicro CheckDestroy: testAccCheckTFETeamNotificationConfigurationDestroy, Steps: []resource.TestStep{ { - Config: testAccTFETeamNotificationConfiguration_microsoftTeams(rInt), + Config: testAccTFETeamNotificationConfiguration_microsoftTeams(org.Name), Check: resource.ComposeTestCheckFunc( testAccCheckTFETeamNotificationConfigurationExists( "tfe_team_notification_configuration.foobar", notificationConfiguration), @@ -455,31 +539,38 @@ func TestAccTFETeamNotificationConfiguration_updateValidateSchemaAttributesMicro ), }, { - Config: testAccTFETeamNotificationConfiguration_microsoftTeamsWithEmailAddresses(rInt), + Config: testAccTFETeamNotificationConfiguration_microsoftTeamsWithEmailAddresses(org.Name), ExpectError: regexp.MustCompile(`(?s).*The attribute 'email_addresses' cannot be set when 'destination_type' is.*'microsoft-teams'`), }, { - Config: testAccTFETeamNotificationConfiguration_microsoftTeamsWithEmailUserIDs(rInt), + Config: testAccTFETeamNotificationConfiguration_microsoftTeamsWithEmailUserIDs(org.Name), ExpectError: regexp.MustCompile(`(?s).*The attribute 'email_user_ids' cannot be set when 'destination_type' is.*'microsoft-teams'`), }, { - Config: testAccTFETeamNotificationConfiguration_microsoftTeamsWithToken(rInt), + Config: testAccTFETeamNotificationConfiguration_microsoftTeamsWithToken(org.Name), ExpectError: regexp.MustCompile(`(?s).*The attribute 'token' cannot be set when 'destination_type' is.*'microsoft-teams'`), }, { - Config: testAccTFETeamNotificationConfiguration_microsoftTeamsWithoutURL(rInt), + Config: testAccTFETeamNotificationConfiguration_microsoftTeamsWithoutURL(org.Name), ExpectError: regexp.MustCompile(`The attribute 'url' is required when 'destination_type' is 'microsoft-teams'`), }, { - Config: testAccTFETeamNotificationConfiguration_microsoftTeams(rInt), + Config: testAccTFETeamNotificationConfiguration_microsoftTeams(org.Name), }, }, }) } func TestAccTFETeamNotificationConfiguration_duplicateTriggers(t *testing.T) { + tfeClient, err := getClientUsingEnv() + if err != nil { + t.Fatal(err) + } + + org, cleanupOrg := createBusinessOrganization(t, tfeClient) + t.Cleanup(cleanupOrg) + notificationConfiguration := &tfe.NotificationConfiguration{} - rInt := rand.New(rand.NewSource(time.Now().UnixNano())).Int() resource.Test(t, resource.TestCase{ PreCheck: func() { preCheckTFETeamNotificationConfiguration(t) }, @@ -487,7 +578,7 @@ func TestAccTFETeamNotificationConfiguration_duplicateTriggers(t *testing.T) { CheckDestroy: testAccCheckTFETeamNotificationConfigurationDestroy, Steps: []resource.TestStep{ { - Config: testAccTFETeamNotificationConfiguration_duplicateTriggers(rInt), + Config: testAccTFETeamNotificationConfiguration_duplicateTriggers(org.Name), Check: resource.ComposeTestCheckFunc( testAccCheckTFETeamNotificationConfigurationExists( "tfe_team_notification_configuration.foobar", notificationConfiguration), @@ -509,7 +600,13 @@ func TestAccTFETeamNotificationConfiguration_duplicateTriggers(t *testing.T) { } func TestAccTFETeamNotificationConfigurationImport_basic(t *testing.T) { - rInt := rand.New(rand.NewSource(time.Now().UnixNano())).Int() + tfeClient, err := getClientUsingEnv() + if err != nil { + t.Fatal(err) + } + + org, cleanupOrg := createBusinessOrganization(t, tfeClient) + t.Cleanup(cleanupOrg) resource.Test(t, resource.TestCase{ PreCheck: func() { preCheckTFETeamNotificationConfiguration(t) }, @@ -517,7 +614,7 @@ func TestAccTFETeamNotificationConfigurationImport_basic(t *testing.T) { CheckDestroy: testAccCheckTFETeamNotificationConfigurationDestroy, Steps: []resource.TestStep{ { - Config: testAccTFETeamNotificationConfiguration_update(rInt), + Config: testAccTFETeamNotificationConfiguration_update(org.Name), }, { @@ -531,7 +628,13 @@ func TestAccTFETeamNotificationConfigurationImport_basic(t *testing.T) { } func TestAccTFETeamNotificationConfigurationImport_emailUserIDs(t *testing.T) { - rInt := rand.New(rand.NewSource(time.Now().UnixNano())).Int() + tfeClient, err := getClientUsingEnv() + if err != nil { + t.Fatal(err) + } + + org, cleanupOrg := createBusinessOrganization(t, tfeClient) + t.Cleanup(cleanupOrg) resource.Test(t, resource.TestCase{ PreCheck: func() { preCheckTFETeamNotificationConfiguration(t) }, @@ -539,7 +642,7 @@ func TestAccTFETeamNotificationConfigurationImport_emailUserIDs(t *testing.T) { CheckDestroy: testAccCheckTFETeamNotificationConfigurationDestroy, Steps: []resource.TestStep{ { - Config: testAccTFETeamNotificationConfiguration_updateEmailUserIDs(rInt), + Config: testAccTFETeamNotificationConfiguration_updateEmailUserIDs(org.Name), }, { @@ -553,7 +656,13 @@ func TestAccTFETeamNotificationConfigurationImport_emailUserIDs(t *testing.T) { } func TestAccTFETeamNotificationConfigurationImport_emptyEmailUserIDs(t *testing.T) { - rInt := rand.New(rand.NewSource(time.Now().UnixNano())).Int() + tfeClient, err := getClientUsingEnv() + if err != nil { + t.Fatal(err) + } + + org, cleanupOrg := createBusinessOrganization(t, tfeClient) + t.Cleanup(cleanupOrg) resource.Test(t, resource.TestCase{ PreCheck: func() { preCheckTFETeamNotificationConfiguration(t) }, @@ -561,7 +670,7 @@ func TestAccTFETeamNotificationConfigurationImport_emptyEmailUserIDs(t *testing. CheckDestroy: testAccCheckTFETeamNotificationConfigurationDestroy, Steps: []resource.TestStep{ { - Config: testAccTFETeamNotificationConfiguration_emailUserIDs(rInt), + Config: testAccTFETeamNotificationConfiguration_emailUserIDs(org.Name), }, { @@ -809,10 +918,10 @@ func testAccCheckTFETeamNotificationConfigurationDestroy(s *terraform.State) err return nil } -func testAccTFETeamNotificationConfiguration_basic(rInt int) string { +func testAccTFETeamNotificationConfiguration_basic(orgName string) string { return fmt.Sprintf(` resource "tfe_organization" "foobar" { - name = "tst-terraform-%d" + name = "%s" email = "admin@company.com" } @@ -826,13 +935,13 @@ resource "tfe_team_notification_configuration" "foobar" { destination_type = "generic" url = "%s" team_id = tfe_team.foobar.id -}`, rInt, runTasksURL()) +}`, orgName, runTasksURL()) } -func testAccTFETeamNotificationConfiguration_emailUserIDs(rInt int) string { +func testAccTFETeamNotificationConfiguration_emailUserIDs(orgName string) string { return fmt.Sprintf(` resource "tfe_organization" "foobar" { - name = "tst-terraform-%d" + name = "%s" email = "admin@company.com" } @@ -850,13 +959,13 @@ resource "tfe_team_notification_configuration" "foobar" { name = "notification_email" destination_type = "email" team_id = tfe_team.foobar.id -}`, rInt) +}`, orgName) } -func testAccTFETeamNotificationConfiguration_slack(rInt int) string { +func testAccTFETeamNotificationConfiguration_slack(orgName string) string { return fmt.Sprintf(` resource "tfe_organization" "foobar" { - name = "tst-terraform-%d" + name = "%s" email = "admin@company.com" } @@ -870,13 +979,13 @@ resource "tfe_team_notification_configuration" "foobar" { destination_type = "slack" url = "%s" team_id = tfe_team.foobar.id -}`, rInt, runTasksURL()) +}`, orgName, runTasksURL()) } -func testAccTFETeamNotificationConfiguration_microsoftTeams(rInt int) string { +func testAccTFETeamNotificationConfiguration_microsoftTeams(orgName string) string { return fmt.Sprintf(` resource "tfe_organization" "foobar" { - name = "tst-terraform-%d" + name = "%s" email = "admin@company.com" } @@ -890,13 +999,13 @@ resource "tfe_team_notification_configuration" "foobar" { destination_type = "microsoft-teams" url = "%s" team_id = tfe_team.foobar.id -}`, rInt, runTasksURL()) +}`, orgName, runTasksURL()) } -func testAccTFETeamNotificationConfiguration_badDestinationType(rInt int) string { +func testAccTFETeamNotificationConfiguration_badDestinationType(orgName string) string { return fmt.Sprintf(` resource "tfe_organization" "foobar" { - name = "tst-terraform-%d" + name = "%s" email = "admin@company.com" } @@ -910,13 +1019,13 @@ resource "tfe_team_notification_configuration" "foobar" { destination_type = "bad_type" url = "%s" team_id = tfe_team.foobar.id -}`, rInt, runTasksURL()) +}`, orgName, runTasksURL()) } -func testAccTFETeamNotificationConfiguration_update(rInt int) string { +func testAccTFETeamNotificationConfiguration_update(orgName string) string { return fmt.Sprintf(` resource "tfe_organization" "foobar" { - name = "tst-terraform-%d" + name = "%s" email = "admin@company.com" } @@ -933,12 +1042,12 @@ resource "tfe_team_notification_configuration" "foobar" { triggers = ["change_request:created"] url = "%s?update=true" team_id = tfe_team.foobar.id -}`, rInt, runTasksURL()) +}`, orgName, runTasksURL()) } -func testAccTFETeamNotificationConfiguration_updateEmailUserIDs(rInt int) string { +func testAccTFETeamNotificationConfiguration_updateEmailUserIDs(orgName string) string { return fmt.Sprintf(`resource "tfe_organization" "foobar" { - name = "tst-terraform-%d" + name = "%s" email = "admin@company.com" } @@ -966,13 +1075,13 @@ resource "tfe_team_notification_configuration" "foobar" { team_id = tfe_team.foobar.id depends_on = [tfe_team_organization_member.foobar] -}`, rInt) +}`, orgName) } -func testAccTFETeamNotificationConfiguration_emailWithURL(rInt int) string { +func testAccTFETeamNotificationConfiguration_emailWithURL(orgName string) string { return fmt.Sprintf(` resource "tfe_organization" "foobar" { - name = "tst-terraform-%d" + name = "%s" email = "admin@company.com" } @@ -986,13 +1095,13 @@ resource "tfe_team_notification_configuration" "foobar" { destination_type = "email" url = "%s" team_id = tfe_team.foobar.id -}`, rInt, runTasksURL()) +}`, orgName, runTasksURL()) } -func testAccTFETeamNotificationConfiguration_emailWithToken(rInt int) string { +func testAccTFETeamNotificationConfiguration_emailWithToken(orgName string) string { return fmt.Sprintf(` resource "tfe_organization" "foobar" { - name = "tst-terraform-%d" + name = "%s" email = "admin@company.com" } @@ -1006,13 +1115,13 @@ resource "tfe_team_notification_configuration" "foobar" { destination_type = "email" token = "1234567890" team_id = tfe_team.foobar.id -}`, rInt) +}`, orgName) } -func testAccTFETeamNotificationConfiguration_genericWithEmailAddresses(rInt int) string { +func testAccTFETeamNotificationConfiguration_genericWithEmailAddresses(orgName string) string { return fmt.Sprintf(` resource "tfe_organization" "foobar" { - name = "tst-terraform-%d" + name = "%s" email = "admin@company.com" } @@ -1026,13 +1135,13 @@ resource "tfe_team_notification_configuration" "foobar" { destination_type = "generic" email_addresses = ["test@example.com", "test2@example.com"] team_id = tfe_team.foobar.id -}`, rInt) +}`, orgName) } -func testAccTFETeamNotificationConfiguration_genericWithEmailUserIDs(rInt int) string { +func testAccTFETeamNotificationConfiguration_genericWithEmailUserIDs(orgName string) string { return fmt.Sprintf(` resource "tfe_organization" "foobar" { - name = "tst-terraform-%d" + name = "%s" email = "admin@company.com" } @@ -1051,13 +1160,13 @@ resource "tfe_team_notification_configuration" "foobar" { destination_type = "generic" email_user_ids = [tfe_organization_membership.foobar.id] team_id = tfe_team.foobar.id -}`, rInt) +}`, orgName) } -func testAccTFETeamNotificationConfiguration_genericWithoutURL(rInt int) string { +func testAccTFETeamNotificationConfiguration_genericWithoutURL(orgName string) string { return fmt.Sprintf(` resource "tfe_organization" "foobar" { - name = "tst-terraform-%d" + name = "%s" email = "admin@company.com" } @@ -1070,13 +1179,13 @@ resource "tfe_team_notification_configuration" "foobar" { name = "notification_generic_without_url" destination_type = "generic" team_id = tfe_team.foobar.id -}`, rInt) +}`, orgName) } -func testAccTFETeamNotificationConfiguration_slackWithEmailAddresses(rInt int) string { +func testAccTFETeamNotificationConfiguration_slackWithEmailAddresses(orgName string) string { return fmt.Sprintf(` resource "tfe_organization" "foobar" { - name = "tst-terraform-%d" + name = "%s" email = "admin@company.com" } @@ -1090,13 +1199,13 @@ resource "tfe_team_notification_configuration" "foobar" { destination_type = "slack" email_addresses = ["test@example.com", "test2@example.com"] team_id = tfe_team.foobar.id -}`, rInt) +}`, orgName) } -func testAccTFETeamNotificationConfiguration_slackWithEmailUserIDs(rInt int) string { +func testAccTFETeamNotificationConfiguration_slackWithEmailUserIDs(orgName string) string { return fmt.Sprintf(` resource "tfe_organization" "foobar" { - name = "tst-terraform-%d" + name = "%s" email = "admin@company.com" } @@ -1115,13 +1224,13 @@ resource "tfe_team_notification_configuration" "foobar" { destination_type = "slack" email_user_ids = [tfe_organization_membership.foobar.id] team_id = tfe_team.foobar.id -}`, rInt) +}`, orgName) } -func testAccTFETeamNotificationConfiguration_slackWithToken(rInt int) string { +func testAccTFETeamNotificationConfiguration_slackWithToken(orgName string) string { return fmt.Sprintf(` resource "tfe_organization" "foobar" { - name = "tst-terraform-%d" + name = "%s" email = "admin@company.com" } @@ -1136,13 +1245,13 @@ resource "tfe_team_notification_configuration" "foobar" { token = "1234567890" url = "%s" team_id = tfe_team.foobar.id -}`, rInt, runTasksURL()) +}`, orgName, runTasksURL()) } -func testAccTFETeamNotificationConfiguration_slackWithoutURL(rInt int) string { +func testAccTFETeamNotificationConfiguration_slackWithoutURL(orgName string) string { return fmt.Sprintf(` resource "tfe_organization" "foobar" { - name = "tst-terraform-%d" + name = "%s" email = "admin@company.com" } @@ -1155,13 +1264,13 @@ resource "tfe_team_notification_configuration" "foobar" { name = "notification_slack_without_url" destination_type = "slack" team_id = tfe_team.foobar.id -}`, rInt) +}`, orgName) } -func testAccTFETeamNotificationConfiguration_microsoftTeamsWithEmailAddresses(rInt int) string { +func testAccTFETeamNotificationConfiguration_microsoftTeamsWithEmailAddresses(orgName string) string { return fmt.Sprintf(` resource "tfe_organization" "foobar" { - name = "tst-terraform-%d" + name = "%s" email = "admin@company.com" } @@ -1175,13 +1284,13 @@ resource "tfe_team_notification_configuration" "foobar" { destination_type = "microsoft-teams" email_addresses = ["test@example.com", "test2@example.com"] team_id = tfe_team.foobar.id -}`, rInt) +}`, orgName) } -func testAccTFETeamNotificationConfiguration_microsoftTeamsWithEmailUserIDs(rInt int) string { +func testAccTFETeamNotificationConfiguration_microsoftTeamsWithEmailUserIDs(orgName string) string { return fmt.Sprintf(` resource "tfe_organization" "foobar" { - name = "tst-terraform-%d" + name = "%s" email = "admin@company.com" } @@ -1200,13 +1309,13 @@ resource "tfe_team_notification_configuration" "foobar" { destination_type = "microsoft-teams" email_user_ids = [tfe_organization_membership.foobar.id] team_id = tfe_team.foobar.id -}`, rInt) +}`, orgName) } -func testAccTFETeamNotificationConfiguration_microsoftTeamsWithToken(rInt int) string { +func testAccTFETeamNotificationConfiguration_microsoftTeamsWithToken(orgName string) string { return fmt.Sprintf(` resource "tfe_organization" "foobar" { - name = "tst-terraform-%d" + name = "%s" email = "admin@company.com" } @@ -1221,13 +1330,13 @@ resource "tfe_team_notification_configuration" "foobar" { token = "1234567890" url = "%s" team_id = tfe_team.foobar.id -}`, rInt, runTasksURL()) +}`, orgName, runTasksURL()) } -func testAccTFETeamNotificationConfiguration_microsoftTeamsWithoutURL(rInt int) string { +func testAccTFETeamNotificationConfiguration_microsoftTeamsWithoutURL(orgName string) string { return fmt.Sprintf(` resource "tfe_organization" "foobar" { - name = "tst-terraform-%d" + name = "%s" email = "admin@company.com" } @@ -1240,13 +1349,13 @@ resource "tfe_team_notification_configuration" "foobar" { name = "notification_msteams_without_url" destination_type = "microsoft-teams" team_id = tfe_team.foobar.id -}`, rInt) +}`, orgName) } -func testAccTFETeamNotificationConfiguration_duplicateTriggers(rInt int) string { +func testAccTFETeamNotificationConfiguration_duplicateTriggers(orgName string) string { return fmt.Sprintf(` resource "tfe_organization" "foobar" { - name = "tst-terraform-%d" + name = "%s" email = "admin@company.com" } @@ -1261,7 +1370,7 @@ resource "tfe_team_notification_configuration" "foobar" { triggers = ["change_request:created", "change_request:created", "change_request:created"] url = "%s" team_id = tfe_team.foobar.id -}`, rInt, runTasksURL()) +}`, orgName, runTasksURL()) } func preCheckTFETeamNotificationConfiguration(t *testing.T) { From 28ede15895b1a834a954203be5c5502d9a5a2b41 Mon Sep 17 00:00:00 2001 From: Taylor Chaparro Date: Wed, 8 Jan 2025 11:53:13 -0800 Subject: [PATCH 08/17] update test configs --- ...fe_team_notification_configuration_test.go | 157 ++++++++---------- 1 file changed, 68 insertions(+), 89 deletions(-) diff --git a/internal/provider/resource_tfe_team_notification_configuration_test.go b/internal/provider/resource_tfe_team_notification_configuration_test.go index 38a1d0811..537fa772e 100644 --- a/internal/provider/resource_tfe_team_notification_configuration_test.go +++ b/internal/provider/resource_tfe_team_notification_configuration_test.go @@ -920,14 +920,13 @@ func testAccCheckTFETeamNotificationConfigurationDestroy(s *terraform.State) err func testAccTFETeamNotificationConfiguration_basic(orgName string) string { return fmt.Sprintf(` -resource "tfe_organization" "foobar" { - name = "%s" - email = "admin@company.com" +data "tfe_organization" "foobar" { + name = "%s" } resource "tfe_team" "foobar" { name = "team-test" - organization = tfe_organization.foobar.id + organization = data.tfe_organization.foobar.name } resource "tfe_team_notification_configuration" "foobar" { @@ -940,18 +939,17 @@ resource "tfe_team_notification_configuration" "foobar" { func testAccTFETeamNotificationConfiguration_emailUserIDs(orgName string) string { return fmt.Sprintf(` -resource "tfe_organization" "foobar" { - name = "%s" - email = "admin@company.com" +data "tfe_organization" "foobar" { + name = "%s" } resource "tfe_team" "foobar" { name = "team-test" - organization = tfe_organization.foobar.id + organization = data.tfe_organization.foobar.name } resource "tfe_organization_membership" "foobar" { - organization = tfe_organization.foobar.id + organization = data.tfe_organization.foobar.name email = "foo@foobar.com" } @@ -964,14 +962,13 @@ resource "tfe_team_notification_configuration" "foobar" { func testAccTFETeamNotificationConfiguration_slack(orgName string) string { return fmt.Sprintf(` -resource "tfe_organization" "foobar" { - name = "%s" - email = "admin@company.com" +data "tfe_organization" "foobar" { + name = "%s" } resource "tfe_team" "foobar" { name = "team-test" - organization = tfe_organization.foobar.id + organization = data.tfe_organization.foobar.name } resource "tfe_team_notification_configuration" "foobar" { @@ -984,14 +981,13 @@ resource "tfe_team_notification_configuration" "foobar" { func testAccTFETeamNotificationConfiguration_microsoftTeams(orgName string) string { return fmt.Sprintf(` -resource "tfe_organization" "foobar" { - name = "%s" - email = "admin@company.com" +data "tfe_organization" "foobar" { + name = "%s" } resource "tfe_team" "foobar" { name = "team-test" - organization = tfe_organization.foobar.id + organization = data.tfe_organization.foobar.name } resource "tfe_team_notification_configuration" "foobar" { @@ -1004,14 +1000,13 @@ resource "tfe_team_notification_configuration" "foobar" { func testAccTFETeamNotificationConfiguration_badDestinationType(orgName string) string { return fmt.Sprintf(` -resource "tfe_organization" "foobar" { - name = "%s" - email = "admin@company.com" +data "tfe_organization" "foobar" { + name = "%s" } resource "tfe_team" "foobar" { name = "team-test" - organization = tfe_organization.foobar.id + organization = data.tfe_organization.foobar.name } resource "tfe_team_notification_configuration" "foobar" { @@ -1024,14 +1019,13 @@ resource "tfe_team_notification_configuration" "foobar" { func testAccTFETeamNotificationConfiguration_update(orgName string) string { return fmt.Sprintf(` -resource "tfe_organization" "foobar" { - name = "%s" - email = "admin@company.com" +data "tfe_organization" "foobar" { + name = "%s" } resource "tfe_team" "foobar" { name = "team-test" - organization = tfe_organization.foobar.id + organization = data.tfe_organization.foobar.name } resource "tfe_team_notification_configuration" "foobar" { @@ -1046,18 +1040,17 @@ resource "tfe_team_notification_configuration" "foobar" { } func testAccTFETeamNotificationConfiguration_updateEmailUserIDs(orgName string) string { - return fmt.Sprintf(`resource "tfe_organization" "foobar" { - name = "%s" - email = "admin@company.com" + return fmt.Sprintf(`data "tfe_organization" "foobar" { + name = "%s" } resource "tfe_team" "foobar" { name = "team-test" - organization = tfe_organization.foobar.id + organization = data.tfe_organization.foobar.name } resource "tfe_organization_membership" "foobar" { - organization = tfe_organization.foobar.id + organization = data.tfe_organization.foobar.name email = "foo@foobar.com" } @@ -1080,14 +1073,13 @@ resource "tfe_team_notification_configuration" "foobar" { func testAccTFETeamNotificationConfiguration_emailWithURL(orgName string) string { return fmt.Sprintf(` -resource "tfe_organization" "foobar" { - name = "%s" - email = "admin@company.com" +data "tfe_organization" "foobar" { + name = "%s" } resource "tfe_team" "foobar" { name = "team-test" - organization = tfe_organization.foobar.id + organization = data.tfe_organization.foobar.name } resource "tfe_team_notification_configuration" "foobar" { @@ -1100,14 +1092,13 @@ resource "tfe_team_notification_configuration" "foobar" { func testAccTFETeamNotificationConfiguration_emailWithToken(orgName string) string { return fmt.Sprintf(` -resource "tfe_organization" "foobar" { - name = "%s" - email = "admin@company.com" +data "tfe_organization" "foobar" { + name = "%s" } resource "tfe_team" "foobar" { name = "team-test" - organization = tfe_organization.foobar.id + organization = data.tfe_organization.foobar.name } resource "tfe_team_notification_configuration" "foobar" { @@ -1120,14 +1111,13 @@ resource "tfe_team_notification_configuration" "foobar" { func testAccTFETeamNotificationConfiguration_genericWithEmailAddresses(orgName string) string { return fmt.Sprintf(` -resource "tfe_organization" "foobar" { - name = "%s" - email = "admin@company.com" +data "tfe_organization" "foobar" { + name = "%s" } resource "tfe_team" "foobar" { name = "team-test" - organization = tfe_organization.foobar.id + organization = data.tfe_organization.foobar.name } resource "tfe_team_notification_configuration" "foobar" { @@ -1140,18 +1130,17 @@ resource "tfe_team_notification_configuration" "foobar" { func testAccTFETeamNotificationConfiguration_genericWithEmailUserIDs(orgName string) string { return fmt.Sprintf(` -resource "tfe_organization" "foobar" { - name = "%s" - email = "admin@company.com" +data "tfe_organization" "foobar" { + name = "%s" } resource "tfe_team" "foobar" { name = "team-test" - organization = tfe_organization.foobar.id + organization = data.tfe_organization.foobar.name } resource "tfe_organization_membership" "foobar" { - organization = tfe_organization.foobar.id + organization = data.tfe_organization.foobar.name email = "foo@foobar.com" } @@ -1165,14 +1154,13 @@ resource "tfe_team_notification_configuration" "foobar" { func testAccTFETeamNotificationConfiguration_genericWithoutURL(orgName string) string { return fmt.Sprintf(` -resource "tfe_organization" "foobar" { - name = "%s" - email = "admin@company.com" +data "tfe_organization" "foobar" { + name = "%s" } resource "tfe_team" "foobar" { name = "team-test" - organization = tfe_organization.foobar.id + organization = data.tfe_organization.foobar.name } resource "tfe_team_notification_configuration" "foobar" { @@ -1184,14 +1172,13 @@ resource "tfe_team_notification_configuration" "foobar" { func testAccTFETeamNotificationConfiguration_slackWithEmailAddresses(orgName string) string { return fmt.Sprintf(` -resource "tfe_organization" "foobar" { - name = "%s" - email = "admin@company.com" +data "tfe_organization" "foobar" { + name = "%s" } resource "tfe_team" "foobar" { name = "team-test" - organization = tfe_organization.foobar.id + organization = data.tfe_organization.foobar.name } resource "tfe_team_notification_configuration" "foobar" { @@ -1204,18 +1191,17 @@ resource "tfe_team_notification_configuration" "foobar" { func testAccTFETeamNotificationConfiguration_slackWithEmailUserIDs(orgName string) string { return fmt.Sprintf(` -resource "tfe_organization" "foobar" { - name = "%s" - email = "admin@company.com" +data "tfe_organization" "foobar" { + name = "%s" } resource "tfe_team" "foobar" { name = "team-test" - organization = tfe_organization.foobar.id + organization = data.tfe_organization.foobar.name } resource "tfe_organization_membership" "foobar" { - organization = tfe_organization.foobar.id + organization = data.tfe_organization.foobar.name email = "foo@foobar.com" } @@ -1229,14 +1215,13 @@ resource "tfe_team_notification_configuration" "foobar" { func testAccTFETeamNotificationConfiguration_slackWithToken(orgName string) string { return fmt.Sprintf(` -resource "tfe_organization" "foobar" { - name = "%s" - email = "admin@company.com" +data "tfe_organization" "foobar" { + name = "%s" } resource "tfe_team" "foobar" { name = "team-test" - organization = tfe_organization.foobar.id + organization = data.tfe_organization.foobar.name } resource "tfe_team_notification_configuration" "foobar" { @@ -1250,14 +1235,13 @@ resource "tfe_team_notification_configuration" "foobar" { func testAccTFETeamNotificationConfiguration_slackWithoutURL(orgName string) string { return fmt.Sprintf(` -resource "tfe_organization" "foobar" { - name = "%s" - email = "admin@company.com" +data "tfe_organization" "foobar" { + name = "%s" } resource "tfe_team" "foobar" { name = "team-test" - organization = tfe_organization.foobar.id + organization = data.tfe_organization.foobar.name } resource "tfe_team_notification_configuration" "foobar" { @@ -1269,14 +1253,13 @@ resource "tfe_team_notification_configuration" "foobar" { func testAccTFETeamNotificationConfiguration_microsoftTeamsWithEmailAddresses(orgName string) string { return fmt.Sprintf(` -resource "tfe_organization" "foobar" { - name = "%s" - email = "admin@company.com" +data "tfe_organization" "foobar" { + name = "%s" } resource "tfe_team" "foobar" { name = "team-test" - organization = tfe_organization.foobar.id + organization = data.tfe_organization.foobar.name } resource "tfe_team_notification_configuration" "foobar" { @@ -1289,18 +1272,17 @@ resource "tfe_team_notification_configuration" "foobar" { func testAccTFETeamNotificationConfiguration_microsoftTeamsWithEmailUserIDs(orgName string) string { return fmt.Sprintf(` -resource "tfe_organization" "foobar" { - name = "%s" - email = "admin@company.com" +data "tfe_organization" "foobar" { + name = "%s" } resource "tfe_team" "foobar" { name = "team-test" - organization = tfe_organization.foobar.id + organization = data.tfe_organization.foobar.name } resource "tfe_organization_membership" "foobar" { - organization = tfe_organization.foobar.id + organization = data.tfe_organization.foobar.name email = "foo@foobar.com" } @@ -1314,14 +1296,13 @@ resource "tfe_team_notification_configuration" "foobar" { func testAccTFETeamNotificationConfiguration_microsoftTeamsWithToken(orgName string) string { return fmt.Sprintf(` -resource "tfe_organization" "foobar" { - name = "%s" - email = "admin@company.com" +data "tfe_organization" "foobar" { + name = "%s" } resource "tfe_team" "foobar" { name = "team-test" - organization = tfe_organization.foobar.id + organization = data.tfe_organization.foobar.name } resource "tfe_team_notification_configuration" "foobar" { @@ -1335,14 +1316,13 @@ resource "tfe_team_notification_configuration" "foobar" { func testAccTFETeamNotificationConfiguration_microsoftTeamsWithoutURL(orgName string) string { return fmt.Sprintf(` -resource "tfe_organization" "foobar" { - name = "%s" - email = "admin@company.com" +data "tfe_organization" "foobar" { + name = "%s" } resource "tfe_team" "foobar" { name = "team-test" - organization = tfe_organization.foobar.id + organization = data.tfe_organization.foobar.name } resource "tfe_team_notification_configuration" "foobar" { @@ -1354,14 +1334,13 @@ resource "tfe_team_notification_configuration" "foobar" { func testAccTFETeamNotificationConfiguration_duplicateTriggers(orgName string) string { return fmt.Sprintf(` -resource "tfe_organization" "foobar" { - name = "%s" - email = "admin@company.com" +data "tfe_organization" "foobar" { + name = "%s" } resource "tfe_team" "foobar" { name = "team-test" - organization = tfe_organization.foobar.id + organization = data.tfe_organization.foobar.name } resource "tfe_team_notification_configuration" "foobar" { From e66e0656eea39b84aacaad5b160d6166a8a1b5ef Mon Sep 17 00:00:00 2001 From: Taylor Chaparro Date: Fri, 10 Jan 2025 08:34:37 -0800 Subject: [PATCH 09/17] fix lint --- internal/provider/client_mock_workspaces_test.go | 4 ---- 1 file changed, 4 deletions(-) diff --git a/internal/provider/client_mock_workspaces_test.go b/internal/provider/client_mock_workspaces_test.go index 9c6d2f77a..31a87ae88 100644 --- a/internal/provider/client_mock_workspaces_test.go +++ b/internal/provider/client_mock_workspaces_test.go @@ -212,7 +212,3 @@ func (m *mockWorkspaces) ListTagBindings(ctx context.Context, workspaceID string func (m *mockWorkspaces) AddTagBindings(ctx context.Context, workspaceID string, options tfe.WorkspaceAddTagBindingsOptions) ([]*tfe.TagBinding, error) { panic("not implemented") } - -func (m *mockWorkspaces) ListEffectiveTagBindings(ctx context.Context, workspaceID string) ([]*tfe.EffectiveTagBinding, error) { - panic("not implemented") -} From 42a4b5f3178dba79f55ad12b0f63b1c34b547f51 Mon Sep 17 00:00:00 2001 From: Taylor Chaparro Date: Fri, 10 Jan 2025 09:15:49 -0800 Subject: [PATCH 10/17] fix: allow tests with premium entitlement orgs --- internal/provider/helper_test.go | 11 ++++++ ...fe_team_notification_configuration_test.go | 34 +++++++++---------- .../provider/subscription_updater_test.go | 24 ++++++++++--- 3 files changed, 47 insertions(+), 22 deletions(-) diff --git a/internal/provider/helper_test.go b/internal/provider/helper_test.go index 4ff682a67..5353f8b96 100644 --- a/internal/provider/helper_test.go +++ b/internal/provider/helper_test.go @@ -66,6 +66,17 @@ func createBusinessOrganization(t *testing.T, client *tfe.Client) (*tfe.Organiza return org, orgCleanup } +func createPlusOrganization(t *testing.T, client *tfe.Client) (*tfe.Organization, func()) { + org, orgCleanup := createOrganization(t, client, tfe.OrganizationCreateOptions{ + Name: tfe.String("tst-" + randomString(t)), + Email: tfe.String(fmt.Sprintf("%s@tfe.local", randomString(t))), + }) + + newSubscriptionUpdater(org).WithPlusEntitlementPlan().Update(t) + + return org, orgCleanup +} + func createTrialOrganization(t *testing.T, client *tfe.Client) (*tfe.Organization, func()) { org, orgCleanup := createOrganization(t, client, tfe.OrganizationCreateOptions{ Name: tfe.String("tst-" + randomString(t)), diff --git a/internal/provider/resource_tfe_team_notification_configuration_test.go b/internal/provider/resource_tfe_team_notification_configuration_test.go index 537fa772e..a4055974a 100644 --- a/internal/provider/resource_tfe_team_notification_configuration_test.go +++ b/internal/provider/resource_tfe_team_notification_configuration_test.go @@ -20,7 +20,7 @@ func TestAccTFETeamNotificationConfiguration_basic(t *testing.T) { t.Fatal(err) } - org, cleanupOrg := createBusinessOrganization(t, tfeClient) + org, cleanupOrg := createPlusOrganization(t, tfeClient) t.Cleanup(cleanupOrg) notificationConfiguration := &tfe.NotificationConfiguration{} @@ -58,7 +58,7 @@ func TestAccTFETeamNotificationConfiguration_emailUserIDs(t *testing.T) { t.Fatal(err) } - org, cleanupOrg := createBusinessOrganization(t, tfeClient) + org, cleanupOrg := createPlusOrganization(t, tfeClient) t.Cleanup(cleanupOrg) notificationConfiguration := &tfe.NotificationConfiguration{} @@ -96,7 +96,7 @@ func TestAccTFETeamNotificationConfiguration_update(t *testing.T) { t.Fatal(err) } - org, cleanupOrg := createBusinessOrganization(t, tfeClient) + org, cleanupOrg := createPlusOrganization(t, tfeClient) t.Cleanup(cleanupOrg) notificationConfiguration := &tfe.NotificationConfiguration{} @@ -156,7 +156,7 @@ func TestAccTFETeamNotificationConfiguration_updateEmailUserIDs(t *testing.T) { t.Fatal(err) } - org, cleanupOrg := createBusinessOrganization(t, tfeClient) + org, cleanupOrg := createPlusOrganization(t, tfeClient) t.Cleanup(cleanupOrg) notificationConfiguration := &tfe.NotificationConfiguration{} @@ -214,7 +214,7 @@ func TestAccTFETeamNotificationConfiguration_validateSchemaAttributesEmail(t *te t.Fatal(err) } - org, cleanupOrg := createBusinessOrganization(t, tfeClient) + org, cleanupOrg := createPlusOrganization(t, tfeClient) t.Cleanup(cleanupOrg) resource.Test(t, resource.TestCase{ @@ -239,7 +239,7 @@ func TestAccTFETeamNotificationConfiguration_validateSchemaAttributesGeneric(t * t.Fatal(err) } - org, cleanupOrg := createBusinessOrganization(t, tfeClient) + org, cleanupOrg := createPlusOrganization(t, tfeClient) t.Cleanup(cleanupOrg) resource.Test(t, resource.TestCase{ @@ -268,7 +268,7 @@ func TestAccTFETeamNotificationConfiguration_validateSchemaAttributesSlack(t *te t.Fatal(err) } - org, cleanupOrg := createBusinessOrganization(t, tfeClient) + org, cleanupOrg := createPlusOrganization(t, tfeClient) t.Cleanup(cleanupOrg) resource.Test(t, resource.TestCase{ @@ -301,7 +301,7 @@ func TestAccTFETeamNotificationConfiguration_validateSchemaAttributesMicrosoftTe t.Fatal(err) } - org, cleanupOrg := createBusinessOrganization(t, tfeClient) + org, cleanupOrg := createPlusOrganization(t, tfeClient) t.Cleanup(cleanupOrg) resource.Test(t, resource.TestCase{ @@ -334,7 +334,7 @@ func TestAccTFETeamNotificationConfiguration_validateSchemaAttributesBadDestinat t.Fatal(err) } - org, cleanupOrg := createBusinessOrganization(t, tfeClient) + org, cleanupOrg := createPlusOrganization(t, tfeClient) t.Cleanup(cleanupOrg) resource.Test(t, resource.TestCase{ @@ -355,7 +355,7 @@ func TestAccTFETeamNotificationConfiguration_updateValidateSchemaAttributesEmail t.Fatal(err) } - org, cleanupOrg := createBusinessOrganization(t, tfeClient) + org, cleanupOrg := createPlusOrganization(t, tfeClient) t.Cleanup(cleanupOrg) notificationConfiguration := &tfe.NotificationConfiguration{} @@ -404,7 +404,7 @@ func TestAccTFETeamNotificationConfiguration_updateValidateSchemaAttributesGener t.Fatal(err) } - org, cleanupOrg := createBusinessOrganization(t, tfeClient) + org, cleanupOrg := createPlusOrganization(t, tfeClient) t.Cleanup(cleanupOrg) notificationConfiguration := &tfe.NotificationConfiguration{} @@ -457,7 +457,7 @@ func TestAccTFETeamNotificationConfiguration_updateValidateSchemaAttributesSlack t.Fatal(err) } - org, cleanupOrg := createBusinessOrganization(t, tfeClient) + org, cleanupOrg := createPlusOrganization(t, tfeClient) t.Cleanup(cleanupOrg) notificationConfiguration := &tfe.NotificationConfiguration{} @@ -514,7 +514,7 @@ func TestAccTFETeamNotificationConfiguration_updateValidateSchemaAttributesMicro t.Fatal(err) } - org, cleanupOrg := createBusinessOrganization(t, tfeClient) + org, cleanupOrg := createPlusOrganization(t, tfeClient) t.Cleanup(cleanupOrg) notificationConfiguration := &tfe.NotificationConfiguration{} @@ -567,7 +567,7 @@ func TestAccTFETeamNotificationConfiguration_duplicateTriggers(t *testing.T) { t.Fatal(err) } - org, cleanupOrg := createBusinessOrganization(t, tfeClient) + org, cleanupOrg := createPlusOrganization(t, tfeClient) t.Cleanup(cleanupOrg) notificationConfiguration := &tfe.NotificationConfiguration{} @@ -605,7 +605,7 @@ func TestAccTFETeamNotificationConfigurationImport_basic(t *testing.T) { t.Fatal(err) } - org, cleanupOrg := createBusinessOrganization(t, tfeClient) + org, cleanupOrg := createPlusOrganization(t, tfeClient) t.Cleanup(cleanupOrg) resource.Test(t, resource.TestCase{ @@ -633,7 +633,7 @@ func TestAccTFETeamNotificationConfigurationImport_emailUserIDs(t *testing.T) { t.Fatal(err) } - org, cleanupOrg := createBusinessOrganization(t, tfeClient) + org, cleanupOrg := createPlusOrganization(t, tfeClient) t.Cleanup(cleanupOrg) resource.Test(t, resource.TestCase{ @@ -661,7 +661,7 @@ func TestAccTFETeamNotificationConfigurationImport_emptyEmailUserIDs(t *testing. t.Fatal(err) } - org, cleanupOrg := createBusinessOrganization(t, tfeClient) + org, cleanupOrg := createPlusOrganization(t, tfeClient) t.Cleanup(cleanupOrg) resource.Test(t, resource.TestCase{ diff --git a/internal/provider/subscription_updater_test.go b/internal/provider/subscription_updater_test.go index 50813447e..c3d7d7645 100644 --- a/internal/provider/subscription_updater_test.go +++ b/internal/provider/subscription_updater_test.go @@ -27,11 +27,12 @@ type featureSetListOptions struct { } type updateFeatureSetOptions struct { - Type string `jsonapi:"primary,subscription"` - RunsCeiling *int `jsonapi:"attr,runs-ceiling,omitempty"` - ContractStartAt *time.Time `jsonapi:"attr,contract-start-at,iso8601,omitempty"` - ContractUserLimit *int `jsonapi:"attr,contract-user-limit,omitempty"` - ContractApplyLimit *int `jsonapi:"attr,contract-apply-limit,omitempty"` + Type string `jsonapi:"primary,subscription"` + RunsCeiling *int `jsonapi:"attr,runs-ceiling,omitempty"` + ContractStartAt *time.Time `jsonapi:"attr,contract-start-at,iso8601,omitempty"` + ContractUserLimit *int `jsonapi:"attr,contract-user-limit,omitempty"` + ContractApplyLimit *int `jsonapi:"attr,contract-apply-limit,omitempty"` + ContractManagedResourcesLimit *int `jsonapi:"attr,contract-managed-resources-limit,omitempty"` FeatureSet *featureSet `jsonapi:"relation,feature-set"` } @@ -49,6 +50,19 @@ func newSubscriptionUpdater(organization *tfe.Organization) *organizationSubscri } } +func (b *organizationSubscriptionUpdater) WithPlusEntitlementPlan() *organizationSubscriptionUpdater { + b.planName = "Plus (entitlement)" + + start := time.Now() + ceiling := 1 + managedResourcesLimit := 1000 + + b.updateOpts.ContractStartAt = &start + b.updateOpts.RunsCeiling = &ceiling + b.updateOpts.ContractManagedResourcesLimit = &managedResourcesLimit + return b +} + func (b *organizationSubscriptionUpdater) WithBusinessPlan() *organizationSubscriptionUpdater { b.planName = "Business" From ee5f1341b408264b39d887dbedf07fe294806aa1 Mon Sep 17 00:00:00 2001 From: Taylor Chaparro Date: Fri, 10 Jan 2025 10:15:24 -0800 Subject: [PATCH 11/17] skip tests that interact with API --- .../resource_tfe_team_notification_configuration_test.go | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/internal/provider/resource_tfe_team_notification_configuration_test.go b/internal/provider/resource_tfe_team_notification_configuration_test.go index a4055974a..03080c0db 100644 --- a/internal/provider/resource_tfe_team_notification_configuration_test.go +++ b/internal/provider/resource_tfe_team_notification_configuration_test.go @@ -15,6 +15,7 @@ import ( ) func TestAccTFETeamNotificationConfiguration_basic(t *testing.T) { + skipUnlessBeta(t) tfeClient, err := getClientUsingEnv() if err != nil { t.Fatal(err) @@ -53,6 +54,7 @@ func TestAccTFETeamNotificationConfiguration_basic(t *testing.T) { } func TestAccTFETeamNotificationConfiguration_emailUserIDs(t *testing.T) { + skipUnlessBeta(t) tfeClient, err := getClientUsingEnv() if err != nil { t.Fatal(err) @@ -91,6 +93,7 @@ func TestAccTFETeamNotificationConfiguration_emailUserIDs(t *testing.T) { } func TestAccTFETeamNotificationConfiguration_update(t *testing.T) { + skipUnlessBeta(t) tfeClient, err := getClientUsingEnv() if err != nil { t.Fatal(err) @@ -151,6 +154,7 @@ func TestAccTFETeamNotificationConfiguration_update(t *testing.T) { } func TestAccTFETeamNotificationConfiguration_updateEmailUserIDs(t *testing.T) { + skipUnlessBeta(t) tfeClient, err := getClientUsingEnv() if err != nil { t.Fatal(err) @@ -562,6 +566,7 @@ func TestAccTFETeamNotificationConfiguration_updateValidateSchemaAttributesMicro } func TestAccTFETeamNotificationConfiguration_duplicateTriggers(t *testing.T) { + skipUnlessBeta(t) tfeClient, err := getClientUsingEnv() if err != nil { t.Fatal(err) @@ -600,6 +605,7 @@ func TestAccTFETeamNotificationConfiguration_duplicateTriggers(t *testing.T) { } func TestAccTFETeamNotificationConfigurationImport_basic(t *testing.T) { + skipUnlessBeta(t) tfeClient, err := getClientUsingEnv() if err != nil { t.Fatal(err) @@ -628,6 +634,7 @@ func TestAccTFETeamNotificationConfigurationImport_basic(t *testing.T) { } func TestAccTFETeamNotificationConfigurationImport_emailUserIDs(t *testing.T) { + skipUnlessBeta(t) tfeClient, err := getClientUsingEnv() if err != nil { t.Fatal(err) @@ -656,6 +663,7 @@ func TestAccTFETeamNotificationConfigurationImport_emailUserIDs(t *testing.T) { } func TestAccTFETeamNotificationConfigurationImport_emptyEmailUserIDs(t *testing.T) { + skipUnlessBeta(t) tfeClient, err := getClientUsingEnv() if err != nil { t.Fatal(err) From e20718128414fca11a396437490a309d06bb4555 Mon Sep 17 00:00:00 2001 From: Taylor Chaparro Date: Fri, 10 Jan 2025 10:39:47 -0800 Subject: [PATCH 12/17] skip additional team notification tests --- .../resource_tfe_team_notification_configuration_test.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/internal/provider/resource_tfe_team_notification_configuration_test.go b/internal/provider/resource_tfe_team_notification_configuration_test.go index 03080c0db..ef21b5b01 100644 --- a/internal/provider/resource_tfe_team_notification_configuration_test.go +++ b/internal/provider/resource_tfe_team_notification_configuration_test.go @@ -354,6 +354,7 @@ func TestAccTFETeamNotificationConfiguration_validateSchemaAttributesBadDestinat } func TestAccTFETeamNotificationConfiguration_updateValidateSchemaAttributesEmail(t *testing.T) { + skipUnlessBeta(t) tfeClient, err := getClientUsingEnv() if err != nil { t.Fatal(err) @@ -403,6 +404,7 @@ func TestAccTFETeamNotificationConfiguration_updateValidateSchemaAttributesEmail } func TestAccTFETeamNotificationConfiguration_updateValidateSchemaAttributesGeneric(t *testing.T) { + skipUnlessBeta(t) tfeClient, err := getClientUsingEnv() if err != nil { t.Fatal(err) @@ -456,6 +458,7 @@ func TestAccTFETeamNotificationConfiguration_updateValidateSchemaAttributesGener } func TestAccTFETeamNotificationConfiguration_updateValidateSchemaAttributesSlack(t *testing.T) { + skipUnlessBeta(t) tfeClient, err := getClientUsingEnv() if err != nil { t.Fatal(err) @@ -513,6 +516,7 @@ func TestAccTFETeamNotificationConfiguration_updateValidateSchemaAttributesSlack } func TestAccTFETeamNotificationConfiguration_updateValidateSchemaAttributesMicrosoftTeams(t *testing.T) { + skipUnlessBeta(t) tfeClient, err := getClientUsingEnv() if err != nil { t.Fatal(err) From ed5979e858c1c1d52d08fa180db757cd5b7a6cf5 Mon Sep 17 00:00:00 2001 From: Taylor Chaparro Date: Wed, 15 Jan 2025 11:29:26 -0800 Subject: [PATCH 13/17] consolidate new validators --- ...rce_tfe_team_notification_configuration.go | 63 +++++---------- .../validators/attribute_value_conflict.go | 77 +++++++++++++++++++ .../attribute_value_conflict_set.go | 54 ------------- .../attribute_value_conflict_string.go | 56 -------------- 4 files changed, 98 insertions(+), 152 deletions(-) create mode 100644 internal/provider/validators/attribute_value_conflict.go delete mode 100644 internal/provider/validators/attribute_value_conflict_set.go delete mode 100644 internal/provider/validators/attribute_value_conflict_string.go diff --git a/internal/provider/resource_tfe_team_notification_configuration.go b/internal/provider/resource_tfe_team_notification_configuration.go index d187d89c5..9df818a1e 100644 --- a/internal/provider/resource_tfe_team_notification_configuration.go +++ b/internal/provider/resource_tfe_team_notification_configuration.go @@ -65,35 +65,21 @@ func modelFromTFETeamNotificationConfiguration(v *tfe.NotificationConfiguration) TeamID: types.StringValue(v.SubscribableChoice.Team.ID), } - emailAddresses := make([]attr.Value, len(v.EmailAddresses)) - for i, emailAddress := range v.EmailAddresses { - emailAddresses[i] = types.StringValue(emailAddress) + if emailAddresses, err := types.SetValueFrom(ctx, types.StringType, v.EmailAddresses); err == nil { + result.EmailAddresses = emailAddresses } - if len(emailAddresses) > 0 { - result.EmailAddresses = types.SetValueMust(types.StringType, emailAddresses) - } else { - result.EmailAddresses = types.SetNull(types.StringType) + + if len(v.Triggers) == 0 { + result.Triggers = types.SetNull(types.StringType) + } else if triggers, err := types.SetValueFrom(ctx, types.StringType, v.Triggers); err == nil { + result.Triggers = triggers } emailUserIDs := make([]attr.Value, len(v.EmailUsers)) for i, emailUser := range v.EmailUsers { emailUserIDs[i] = types.StringValue(emailUser.ID) } - if len(emailUserIDs) > 0 { - result.EmailUserIDs = types.SetValueMust(types.StringType, emailUserIDs) - } else { - result.EmailUserIDs = types.SetNull(types.StringType) - } - - triggers := make([]attr.Value, len(v.Triggers)) - for i, trigger := range v.Triggers { - triggers[i] = types.StringValue(trigger) - } - if len(v.Triggers) > 0 { - result.Triggers = types.SetValueMust(types.StringType, triggers) - } else { - result.Triggers = types.SetNull(types.StringType) - } + result.EmailUserIDs = types.SetValueMust(types.StringType, emailUserIDs) if v.Token != "" { result.Token = types.StringValue(v.Token) @@ -147,14 +133,10 @@ func (r *resourceTFETeamNotificationConfiguration) Schema(ctx context.Context, r Computed: true, ElementType: types.StringType, Validators: []validator.Set{ - validators.AttributeValueConflictSetValidator( + validators.AttributeValueConflictValidator( "destination_type", []string{"generic", "microsoft-teams", "slack"}, ), - setvalidator.ConflictsWith( - path.MatchRelative().AtParent().AtName("token"), - path.MatchRelative().AtParent().AtName("url"), - ), }, }, @@ -164,14 +146,10 @@ func (r *resourceTFETeamNotificationConfiguration) Schema(ctx context.Context, r Computed: true, ElementType: types.StringType, Validators: []validator.Set{ - validators.AttributeValueConflictSetValidator( + validators.AttributeValueConflictValidator( "destination_type", []string{"generic", "microsoft-teams", "slack"}, ), - setvalidator.ConflictsWith( - path.MatchRelative().AtParent().AtName("token"), - path.MatchRelative().AtParent().AtName("url"), - ), }, }, @@ -187,7 +165,7 @@ func (r *resourceTFETeamNotificationConfiguration) Schema(ctx context.Context, r Optional: true, Sensitive: true, Validators: []validator.String{ - validators.AttributeValueConflictStringValidator( + validators.AttributeValueConflictValidator( "destination_type", []string{"email", "microsoft-teams", "slack"}, ), @@ -215,7 +193,7 @@ func (r *resourceTFETeamNotificationConfiguration) Schema(ctx context.Context, r "destination_type", []string{"generic", "microsoft-teams", "slack"}, ), - validators.AttributeValueConflictStringValidator( + validators.AttributeValueConflictValidator( "destination_type", []string{"email"}, ), @@ -264,10 +242,6 @@ func (r *resourceTFETeamNotificationConfiguration) Create(ctx context.Context, r return } - if resp.Diagnostics.HasError() { - return - } - // Get team teamID := plan.TeamID.ValueString() @@ -295,11 +269,12 @@ func (r *resourceTFETeamNotificationConfiguration) Create(ctx context.Context, r } // Add email_addresses set to the options struct - emailAddresses := make([]types.String, len(plan.EmailAddresses.Elements())) + emailAddresses := make([]types.String, 0) if diags := plan.EmailAddresses.ElementsAs(ctx, &emailAddresses, true); diags != nil && diags.HasError() { resp.Diagnostics.Append(diags...) return } + options.EmailAddresses = []string{} for _, emailAddress := range emailAddresses { options.EmailAddresses = append(options.EmailAddresses, emailAddress.ValueString()) @@ -321,6 +296,8 @@ func (r *resourceTFETeamNotificationConfiguration) Create(ctx context.Context, r if err != nil { resp.Diagnostics.AddError("Unable to create team notification configuration", err.Error()) return + } else if len(tnc.EmailUsers) != len(plan.EmailUserIDs.Elements()) { + resp.Diagnostics.AddError("Email user IDs produced an inconsistent result", "API returned a different number of email user IDs than were provided in the plan.") } // Restore token from plan because it is write only @@ -385,7 +362,7 @@ func (r *resourceTFETeamNotificationConfiguration) Update(ctx context.Context, r } // Add triggers set to the options struct - triggers := make([]types.String, len(plan.Triggers.Elements())) + triggers := make([]types.String, 0) if diags := plan.Triggers.ElementsAs(ctx, &triggers, true); diags != nil && diags.HasError() { resp.Diagnostics.Append(diags...) return @@ -396,7 +373,7 @@ func (r *resourceTFETeamNotificationConfiguration) Update(ctx context.Context, r } // Add email_addresses set to the options struct - emailAddresses := make([]types.String, len(plan.EmailAddresses.Elements())) + emailAddresses := make([]types.String, 0) if diags := plan.EmailAddresses.ElementsAs(ctx, &emailAddresses, true); diags != nil && diags.HasError() { resp.Diagnostics.Append(diags...) return @@ -407,7 +384,7 @@ func (r *resourceTFETeamNotificationConfiguration) Update(ctx context.Context, r } // Add email_user_ids set to the options struct - emailUserIDs := make([]types.String, len(plan.EmailUserIDs.Elements())) + emailUserIDs := make([]types.String, 0) if diags := plan.EmailUserIDs.ElementsAs(ctx, &emailUserIDs, true); diags != nil && diags.HasError() { resp.Diagnostics.Append(diags...) return @@ -422,6 +399,8 @@ func (r *resourceTFETeamNotificationConfiguration) Update(ctx context.Context, r if err != nil { resp.Diagnostics.AddError("Unable to update team notification configuration", err.Error()) return + } else if len(tnc.EmailUsers) != len(plan.EmailUserIDs.Elements()) { + resp.Diagnostics.AddError("Email user IDs produced an inconsistent result", "API returned a different number of email user IDs than were provided in the plan.") } // Restore token from plan because it is write only diff --git a/internal/provider/validators/attribute_value_conflict.go b/internal/provider/validators/attribute_value_conflict.go new file mode 100644 index 000000000..a2644ad88 --- /dev/null +++ b/internal/provider/validators/attribute_value_conflict.go @@ -0,0 +1,77 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package validators + +import ( + "context" + "fmt" + + "github.com/hashicorp/terraform-plugin-framework/path" + "github.com/hashicorp/terraform-plugin-framework/schema/validator" + "github.com/hashicorp/terraform-plugin-framework/types" +) + +type attributeValueConflictValidator struct { + attributeName string + conflictingValues []string +} + +func (v attributeValueConflictValidator) Description(ctx context.Context) string { + return fmt.Sprintf("Ensures the attribute is not set if %s is one of %v", v.attributeName, v.conflictingValues) +} + +func (v attributeValueConflictValidator) MarkdownDescription(ctx context.Context) string { + return v.Description(ctx) +} + +func (v attributeValueConflictValidator) ValidateSet(ctx context.Context, req validator.SetRequest, resp *validator.SetResponse) { + if req.ConfigValue.IsNull() || req.ConfigValue.IsUnknown() { + return + } + + var attributeValue types.String + diags := req.Config.GetAttribute(ctx, path.Root(v.attributeName), &attributeValue) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + for _, conflictingValue := range v.conflictingValues { + if attributeValue.ValueString() == conflictingValue { + resp.Diagnostics.AddError( + "Invalid Attribute Value", + fmt.Sprintf("The attribute '%s' cannot be set when '%s' is '%s'", req.Path, v.attributeName, conflictingValue), + ) + + return + } + } +} + +func (v attributeValueConflictValidator) ValidateString(ctx context.Context, req validator.StringRequest, resp *validator.StringResponse) { + if req.ConfigValue.IsNull() || req.ConfigValue.IsUnknown() { + return + } + + var attributeValue types.String + diags := req.Config.GetAttribute(ctx, path.Root(v.attributeName), &attributeValue) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + for _, conflictingValue := range v.conflictingValues { + if attributeValue.ValueString() == conflictingValue { + resp.Diagnostics.AddError( + "Invalid Attribute Value", + fmt.Sprintf("The attribute '%s' cannot be set when '%s' is '%s'", req.Path, v.attributeName, conflictingValue), + ) + return + } + } +} + +func AttributeValueConflictValidator(attributeName string, conflictingValues []string) attributeValueConflictValidator { + return attributeValueConflictValidator{attributeName: attributeName, conflictingValues: conflictingValues} +} diff --git a/internal/provider/validators/attribute_value_conflict_set.go b/internal/provider/validators/attribute_value_conflict_set.go deleted file mode 100644 index 457a9b9ba..000000000 --- a/internal/provider/validators/attribute_value_conflict_set.go +++ /dev/null @@ -1,54 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - -package validators - -import ( - "context" - "fmt" - - "github.com/hashicorp/terraform-plugin-framework/path" - "github.com/hashicorp/terraform-plugin-framework/schema/validator" - "github.com/hashicorp/terraform-plugin-framework/types" -) - -type attributeValueConflictSetValidator struct { - attributeName string - conflictingValues []string -} - -func (v attributeValueConflictSetValidator) Description(ctx context.Context) string { - return fmt.Sprintf("Ensures the attribute is not set if %s is one of %v", v.attributeName, v.conflictingValues) -} - -func (v attributeValueConflictSetValidator) MarkdownDescription(ctx context.Context) string { - return v.Description(ctx) -} - -func (v attributeValueConflictSetValidator) ValidateSet(ctx context.Context, req validator.SetRequest, resp *validator.SetResponse) { - if req.ConfigValue.IsNull() || req.ConfigValue.IsUnknown() { - return - } - - var attributeValue types.String - diags := req.Config.GetAttribute(ctx, path.Root(v.attributeName), &attributeValue) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - for _, conflictingValue := range v.conflictingValues { - if attributeValue.ValueString() == conflictingValue { - resp.Diagnostics.AddError( - "Invalid Attribute Value", - fmt.Sprintf("The attribute '%s' cannot be set when '%s' is '%s'", req.Path, v.attributeName, conflictingValue), - ) - - return - } - } -} - -func AttributeValueConflictSetValidator(attributeName string, conflictingValues []string) validator.Set { - return attributeValueConflictSetValidator{attributeName: attributeName, conflictingValues: conflictingValues} -} diff --git a/internal/provider/validators/attribute_value_conflict_string.go b/internal/provider/validators/attribute_value_conflict_string.go deleted file mode 100644 index 94a7d112f..000000000 --- a/internal/provider/validators/attribute_value_conflict_string.go +++ /dev/null @@ -1,56 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - -package validators - -import ( - "context" - "fmt" - - "github.com/hashicorp/terraform-plugin-framework/path" - "github.com/hashicorp/terraform-plugin-framework/schema/validator" - "github.com/hashicorp/terraform-plugin-framework/types" -) - -type attributeValueConflictStringValidator struct { - attributeName string - conflictingValues []string -} - -func (v attributeValueConflictStringValidator) Description(ctx context.Context) string { - return fmt.Sprintf("Ensures the attribute is not set if %s is one of %v", v.attributeName, v.conflictingValues) -} - -func (v attributeValueConflictStringValidator) MarkdownDescription(ctx context.Context) string { - return v.Description(ctx) -} - -func (v attributeValueConflictStringValidator) ValidateString(ctx context.Context, req validator.StringRequest, resp *validator.StringResponse) { - if req.ConfigValue.IsNull() || req.ConfigValue.IsUnknown() { - return - } - - var attributeValue types.String - diags := req.Config.GetAttribute(ctx, path.Root(v.attributeName), &attributeValue) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - for _, conflictingValue := range v.conflictingValues { - if attributeValue.ValueString() == conflictingValue { - resp.Diagnostics.AddError( - "Invalid Attribute Value", - fmt.Sprintf("The attribute '%s' cannot be set when '%s' is '%s'", req.Path, v.attributeName, conflictingValue), - ) - return - } - } -} - -func AttributeValueConflictStringValidator(attributeName string, conflictingValues []string) validator.String { - return attributeValueConflictStringValidator{ - attributeName: attributeName, - conflictingValues: conflictingValues, - } -} From 55049e980a1f9e6f02ee0e04a5972f8b62bb07c3 Mon Sep 17 00:00:00 2001 From: Taylor Chaparro Date: Thu, 16 Jan 2025 08:38:53 -0800 Subject: [PATCH 14/17] update team notification docs example to add user to team --- ...m_notification_configuration.html.markdown | 22 ++++++++++++++----- 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/website/docs/r/team_notification_configuration.html.markdown b/website/docs/r/team_notification_configuration.html.markdown index c25f93afe..68a120d0a 100644 --- a/website/docs/r/team_notification_configuration.html.markdown +++ b/website/docs/r/team_notification_configuration.html.markdown @@ -49,9 +49,14 @@ resource "tfe_team" "test" { organization = tfe_organization.test.id } -resource "tfe_organization_membership" "test" { - organization = "my-org-name" - email = "test.member@company.com" +data "tfe_organization_membership" "test" { + organization = tfe_organization.test.name + email = "example@example.com" +} + +resource "tfe_team_organization_member" "test" { + team_id = tfe_team.test.id + organization_membership_id = data.tfe_organization_membership.test.id } resource "tfe_team_notification_configuration" "test" { @@ -77,9 +82,14 @@ resource "tfe_team" "test" { organization = tfe_organization.test.id } -resource "tfe_organization_membership" "test" { - organization = "my-org-name" - email = "test.member@company.com" +data "tfe_organization_membership" "test" { + organization = tfe_organization.test.name + email = "example@example.com" +} + +resource "tfe_team_organization_member" "test" { + team_id = tfe_team.test.id + organization_membership_id = data.tfe_organization_membership.test.id } resource "tfe_team_notification_configuration" "test" { From c0c6a000f1811c6bfc4564fcead22d42704a4ac6 Mon Sep 17 00:00:00 2001 From: Taylor Chaparro Date: Fri, 17 Jan 2025 13:33:34 -0800 Subject: [PATCH 15/17] update docs and use known length for slices --- .../resource_tfe_team_notification_configuration.go | 10 +++++----- .../r/team_notification_configuration.html.markdown | 4 ++-- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/internal/provider/resource_tfe_team_notification_configuration.go b/internal/provider/resource_tfe_team_notification_configuration.go index 9df818a1e..0f31e1f43 100644 --- a/internal/provider/resource_tfe_team_notification_configuration.go +++ b/internal/provider/resource_tfe_team_notification_configuration.go @@ -186,7 +186,7 @@ func (r *resourceTFETeamNotificationConfiguration) Schema(ctx context.Context, r }, "url": schema.StringAttribute{ - Description: "The HTTP or HTTPS URL where notification requests will be made. This value must not be provided if `destination_type` is `email`.", + Description: "The HTTP or HTTPS URL where notification requests will be made. This value must not be provided if `email_addresses` or `email_user_ids` is present, or if `destination_type` is `email`.", Optional: true, Validators: []validator.String{ validators.AttributeRequiredIfValueString( @@ -269,7 +269,7 @@ func (r *resourceTFETeamNotificationConfiguration) Create(ctx context.Context, r } // Add email_addresses set to the options struct - emailAddresses := make([]types.String, 0) + emailAddresses := make([]types.String, len(plan.EmailAddresses.Elements())) if diags := plan.EmailAddresses.ElementsAs(ctx, &emailAddresses, true); diags != nil && diags.HasError() { resp.Diagnostics.Append(diags...) return @@ -362,7 +362,7 @@ func (r *resourceTFETeamNotificationConfiguration) Update(ctx context.Context, r } // Add triggers set to the options struct - triggers := make([]types.String, 0) + triggers := make([]types.String, len(plan.Triggers.Elements())) if diags := plan.Triggers.ElementsAs(ctx, &triggers, true); diags != nil && diags.HasError() { resp.Diagnostics.Append(diags...) return @@ -373,7 +373,7 @@ func (r *resourceTFETeamNotificationConfiguration) Update(ctx context.Context, r } // Add email_addresses set to the options struct - emailAddresses := make([]types.String, 0) + emailAddresses := make([]types.String, len(plan.EmailAddresses.Elements())) if diags := plan.EmailAddresses.ElementsAs(ctx, &emailAddresses, true); diags != nil && diags.HasError() { resp.Diagnostics.Append(diags...) return @@ -384,7 +384,7 @@ func (r *resourceTFETeamNotificationConfiguration) Update(ctx context.Context, r } // Add email_user_ids set to the options struct - emailUserIDs := make([]types.String, 0) + emailUserIDs := make([]types.String, len(plan.EmailUserIDs.Elements())) if diags := plan.EmailUserIDs.ElementsAs(ctx, &emailUserIDs, true); diags != nil && diags.HasError() { resp.Diagnostics.Append(diags...) return diff --git a/website/docs/r/team_notification_configuration.html.markdown b/website/docs/r/team_notification_configuration.html.markdown index 68a120d0a..9231cffb7 100644 --- a/website/docs/r/team_notification_configuration.html.markdown +++ b/website/docs/r/team_notification_configuration.html.markdown @@ -114,6 +114,8 @@ The following arguments are supported: - `email` available in HCP Terraform or Terraform Enterprise v202005-1 or later - `slack` - `microsoft-teams` available in HCP Terraform or Terraform Enterprise v202206-1 or later +- `team_id` - (Required) The ID of the team that owns the notification configuration. +- `url` - (Required if `destination_type` is `generic`, `microsoft-teams`, or `slack`) The HTTP or HTTPS URL of the notification configuration where notification requests will be made. This value _must not_ be provided if `destination_type` is `email`. - `email_addresses` - (Optional) **TFE only** A list of email addresses. This value _must not_ be provided if `destination_type` is `generic`, `microsoft-teams`, or `slack`. - `email_user_ids` - (Optional) A list of user IDs. This value _must not_ be provided @@ -126,8 +128,6 @@ The following arguments are supported: This value _must not_ be provided if `destination_type` is `email`, `microsoft-teams`, or `slack`. - `triggers` - (Optional) The array of triggers for which this notification configuration will send notifications. Currently, the only valid value is `change_request:created`. -- `url` - (Required if `destination_type` is `generic`, `microsoft-teams`, or `slack`) The HTTP or HTTPS URL of the notification configuration where notification requests will be made. This value _must not_ be provided if `destination_type` is `email`. -- `team_id` - (Required) The ID of the team that owns the notification configuration. ## Attributes Reference From 49806ea34c9a6a900ecfe63e4ae6a7893c0814f9 Mon Sep 17 00:00:00 2001 From: Taylor Chaparro Date: Fri, 17 Jan 2025 13:48:42 -0800 Subject: [PATCH 16/17] expose diagnostics when parsing model --- ...rce_tfe_team_notification_configuration.go | 54 +++++++++++++++---- 1 file changed, 43 insertions(+), 11 deletions(-) diff --git a/internal/provider/resource_tfe_team_notification_configuration.go b/internal/provider/resource_tfe_team_notification_configuration.go index 0f31e1f43..fcfba7c32 100644 --- a/internal/provider/resource_tfe_team_notification_configuration.go +++ b/internal/provider/resource_tfe_team_notification_configuration.go @@ -11,6 +11,7 @@ import ( "github.com/hashicorp/terraform-plugin-framework-validators/setvalidator" "github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator" "github.com/hashicorp/terraform-plugin-framework/attr" + "github.com/hashicorp/terraform-plugin-framework/diag" "github.com/hashicorp/terraform-plugin-framework/path" "github.com/hashicorp/terraform-plugin-framework/resource" "github.com/hashicorp/terraform-plugin-framework/resource/schema" @@ -56,7 +57,7 @@ type modelTFETeamNotificationConfiguration struct { // modelFromTFETeamNotificationConfiguration builds a modelTFETeamNotificationConfiguration // struct from a tfe.TeamNotificationConfiguration value. -func modelFromTFETeamNotificationConfiguration(v *tfe.NotificationConfiguration) modelTFETeamNotificationConfiguration { +func modelFromTFETeamNotificationConfiguration(v *tfe.NotificationConfiguration) (*modelTFETeamNotificationConfiguration, *diag.Diagnostics) { result := modelTFETeamNotificationConfiguration{ ID: types.StringValue(v.ID), Name: types.StringValue(v.Name), @@ -65,21 +66,38 @@ func modelFromTFETeamNotificationConfiguration(v *tfe.NotificationConfiguration) TeamID: types.StringValue(v.SubscribableChoice.Team.ID), } - if emailAddresses, err := types.SetValueFrom(ctx, types.StringType, v.EmailAddresses); err == nil { + if len(v.EmailAddresses) == 0 { + result.EmailAddresses = types.SetNull(types.StringType) + } else { + emailAddresses, diags := types.SetValueFrom(ctx, types.StringType, v.EmailAddresses) + if diags != nil && diags.HasError() { + return nil, &diags + } result.EmailAddresses = emailAddresses } if len(v.Triggers) == 0 { result.Triggers = types.SetNull(types.StringType) - } else if triggers, err := types.SetValueFrom(ctx, types.StringType, v.Triggers); err == nil { + } else { + triggers, diags := types.SetValueFrom(ctx, types.StringType, v.Triggers) + if diags != nil && diags.HasError() { + return nil, &diags + } + result.Triggers = triggers + } - emailUserIDs := make([]attr.Value, len(v.EmailUsers)) - for i, emailUser := range v.EmailUsers { - emailUserIDs[i] = types.StringValue(emailUser.ID) + if len(v.EmailUsers) == 0 { + result.EmailUserIDs = types.SetNull(types.StringType) + } else { + emailUserIDs := make([]attr.Value, len(v.EmailUsers)) + for i, emailUser := range v.EmailUsers { + emailUserIDs[i] = types.StringValue(emailUser.ID) + } + + result.EmailUserIDs = types.SetValueMust(types.StringType, emailUserIDs) } - result.EmailUserIDs = types.SetValueMust(types.StringType, emailUserIDs) if v.Token != "" { result.Token = types.StringValue(v.Token) @@ -89,7 +107,7 @@ func modelFromTFETeamNotificationConfiguration(v *tfe.NotificationConfiguration) result.URL = types.StringValue(v.URL) } - return result + return &result, nil } func (r *resourceTFETeamNotificationConfiguration) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { @@ -298,6 +316,7 @@ func (r *resourceTFETeamNotificationConfiguration) Create(ctx context.Context, r return } else if len(tnc.EmailUsers) != len(plan.EmailUserIDs.Elements()) { resp.Diagnostics.AddError("Email user IDs produced an inconsistent result", "API returned a different number of email user IDs than were provided in the plan.") + return } // Restore token from plan because it is write only @@ -305,7 +324,11 @@ func (r *resourceTFETeamNotificationConfiguration) Create(ctx context.Context, r tnc.Token = plan.Token.ValueString() } - result := modelFromTFETeamNotificationConfiguration(tnc) + result, diags := modelFromTFETeamNotificationConfiguration(tnc) + if diags != nil && diags.HasError() { + resp.Diagnostics.Append((*diags)...) + return + } // Save data into Terraform state resp.Diagnostics.Append(resp.State.Set(ctx, &result)...) @@ -333,7 +356,11 @@ func (r *resourceTFETeamNotificationConfiguration) Read(ctx context.Context, req tnc.Token = state.Token.ValueString() } - result := modelFromTFETeamNotificationConfiguration(tnc) + result, diags := modelFromTFETeamNotificationConfiguration(tnc) + if diags != nil && diags.HasError() { + resp.Diagnostics.Append((*diags)...) + return + } // Save updated data into Terraform state resp.Diagnostics.Append(resp.State.Set(ctx, &result)...) @@ -401,6 +428,7 @@ func (r *resourceTFETeamNotificationConfiguration) Update(ctx context.Context, r return } else if len(tnc.EmailUsers) != len(plan.EmailUserIDs.Elements()) { resp.Diagnostics.AddError("Email user IDs produced an inconsistent result", "API returned a different number of email user IDs than were provided in the plan.") + return } // Restore token from plan because it is write only @@ -408,7 +436,11 @@ func (r *resourceTFETeamNotificationConfiguration) Update(ctx context.Context, r tnc.Token = plan.Token.ValueString() } - result := modelFromTFETeamNotificationConfiguration(tnc) + result, diags := modelFromTFETeamNotificationConfiguration(tnc) + if diags != nil && diags.HasError() { + resp.Diagnostics.Append((*diags)...) + return + } // Save data into Terraform state resp.Diagnostics.Append(resp.State.Set(ctx, &result)...) From ce8630a67eeed5cac640161671f27a236b2037d5 Mon Sep 17 00:00:00 2001 From: Taylor Chaparro Date: Fri, 17 Jan 2025 13:51:19 -0800 Subject: [PATCH 17/17] fix lint --- .../provider/resource_tfe_team_notification_configuration.go | 1 - 1 file changed, 1 deletion(-) diff --git a/internal/provider/resource_tfe_team_notification_configuration.go b/internal/provider/resource_tfe_team_notification_configuration.go index fcfba7c32..74e899686 100644 --- a/internal/provider/resource_tfe_team_notification_configuration.go +++ b/internal/provider/resource_tfe_team_notification_configuration.go @@ -85,7 +85,6 @@ func modelFromTFETeamNotificationConfiguration(v *tfe.NotificationConfiguration) } result.Triggers = triggers - } if len(v.EmailUsers) == 0 {