teamcity/versioned_settings.go (265 lines of code) (raw):

package teamcity import ( "context" "strconv" "terraform-provider-teamcity/client" "terraform-provider-teamcity/models" "github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator" "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" "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" ) var ( _ resource.Resource = &versionedSettingsResource{} _ resource.ResourceWithConfigure = &versionedSettingsResource{} _ resource.ResourceWithImportState = &versionedSettingsResource{} ) func NewVersionedSettingsResource() resource.Resource { return &versionedSettingsResource{} } type versionedSettingsResource struct { client *client.Client } func (r *versionedSettingsResource) Metadata(_ context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { resp.TypeName = req.ProviderTypeName + "_versioned_settings" } func (r *versionedSettingsResource) Schema(_ context.Context, _ resource.SchemaRequest, resp *resource.SchemaResponse) { resp.Schema = schema.Schema{ Description: "TeamCity allows synchronizing project settings with the version control repository (VCS). More info [here](https://www.jetbrains.com/help/teamcity/storing-project-settings-in-version-control.html)", Attributes: map[string]schema.Attribute{ "project_id": schema.StringAttribute{ Required: true, PlanModifiers: []planmodifier.String{ stringplanmodifier.RequiresReplace(), }, }, "vcsroot_id": schema.StringAttribute{Required: true}, "allow_ui_editing": schema.BoolAttribute{Required: true}, "settings": schema.StringAttribute{ Required: true, Validators: []validator.String{ stringvalidator.OneOf([]string{"alwaysUseCurrent", "useCurrentByDefault", "useFromVCS"}...), }, }, "show_changes": schema.BoolAttribute{Required: true}, }, } } func (r *versionedSettingsResource) Configure(_ context.Context, req resource.ConfigureRequest, _ *resource.ConfigureResponse) { if req.ProviderData == nil { return } r.client = req.ProviderData.(*client.Client) } func (r *versionedSettingsResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { var plan models.VersionedSettingsModel diags := req.Plan.Get(ctx, &plan) resp.Diagnostics.Append(diags...) if resp.Diagnostics.HasError() { return } root := plan.VcsRoot.ValueString() format := "kotlin" editing := plan.AllowUIEditing.ValueBool() secureValuesOutsideVcs := true buildSettings := plan.Settings.ValueString() showChanges := plan.ShowChanges.ValueBool() decision := "importFromVCS" settings := models.VersionedSettingsJson{ SynchronizationMode: "enabled", VcsRootId: &root, Format: &format, AllowUIEditing: &editing, StoreSecureValuesOutsideVcs: &secureValuesOutsideVcs, BuildSettingsMode: &buildSettings, ShowSettingsChanges: &showChanges, ImportDecision: &decision, } projectId := plan.ProjectId.ValueString() result, err := r.client.SetVersionedSettings(projectId, settings) if err != nil { resp.Diagnostics.AddError( "Error setting versioned settings", err.Error(), ) return } newState, err := r.readState(*result) if err != nil { resp.Diagnostics.AddError( "Error reading versioned settings", err.Error(), ) return } newState.ProjectId = plan.ProjectId diags = resp.State.Set(ctx, *newState) resp.Diagnostics.Append(diags...) if resp.Diagnostics.HasError() { return } } func (r *versionedSettingsResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { var oldState models.VersionedSettingsModel diags := req.State.Get(ctx, &oldState) resp.Diagnostics.Append(diags...) if resp.Diagnostics.HasError() { return } actual, err := r.client.GetVersionedSettings(oldState.ProjectId.ValueString()) if err != nil { resp.Diagnostics.AddError( "Error Reading versioned settings", err.Error(), ) return } if actual == nil { resp.State.RemoveResource(ctx) return } if *actual.Format != "kotlin" { resp.State.RemoveResource(ctx) return } newState, err := r.readState(*actual) if err != nil { resp.Diagnostics.AddError( "Error Reading versioned settings", err.Error(), ) return } newState.ProjectId = oldState.ProjectId diags = resp.State.Set(ctx, newState) resp.Diagnostics.Append(diags...) if resp.Diagnostics.HasError() { return } } func (r *versionedSettingsResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { var plan models.VersionedSettingsModel diags := req.Plan.Get(ctx, &plan) resp.Diagnostics.Append(diags...) if resp.Diagnostics.HasError() { return } var oldState models.VersionedSettingsModel diags = req.State.Get(ctx, &oldState) resp.Diagnostics.Append(diags...) if resp.Diagnostics.HasError() { return } var newState models.VersionedSettingsModel projectId := plan.ProjectId.ValueString() newState.ProjectId = plan.ProjectId if result, ok := r.setPropertyString(projectId, "vcsRootId", oldState.VcsRoot, plan.VcsRoot, &resp.Diagnostics); ok { newState.VcsRoot = result } else { return } if result, ok := r.setPropertyBool(projectId, "allowUIEditing", oldState.AllowUIEditing, plan.AllowUIEditing, &resp.Diagnostics); ok { newState.AllowUIEditing = result } else { return } if result, ok := r.setPropertyString(projectId, "buildSettingsMode", oldState.Settings, plan.Settings, &resp.Diagnostics); ok { newState.Settings = result } else { return } if result, ok := r.setPropertyBool(projectId, "showSettingsChanges", oldState.ShowChanges, plan.ShowChanges, &resp.Diagnostics); ok { newState.ShowChanges = result } else { return } diags = resp.State.Set(ctx, newState) resp.Diagnostics.Append(diags...) if resp.Diagnostics.HasError() { return } } func (r *versionedSettingsResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { var state models.VersionedSettingsModel diags := req.State.Get(ctx, &state) resp.Diagnostics.Append(diags...) if resp.Diagnostics.HasError() { return } settings := models.VersionedSettingsJson{ SynchronizationMode: "disabled", } projectId := state.ProjectId.ValueString() _, err := r.client.SetVersionedSettings(projectId, settings) if err != nil { resp.Diagnostics.AddError( "Error disabling versioned settings", err.Error(), ) return } } func (r *versionedSettingsResource) readState(result models.VersionedSettingsJson) (*models.VersionedSettingsModel, error) { settings := models.VersionedSettingsModel{ VcsRoot: types.StringValue(*result.VcsRootId), AllowUIEditing: types.BoolValue(*result.AllowUIEditing), Settings: types.StringValue(*result.BuildSettingsMode), ShowChanges: types.BoolValue(*result.ShowSettingsChanges), } return &settings, nil } func (r *versionedSettingsResource) setPropertyString(projectId, name string, state, plan types.String, diag *diag.Diagnostics) (types.String, bool) { if plan.Equal(state) { return state, true } val := plan.ValueString() result, err := r.client.SetField("projects", projectId, "versionedSettings/config/parameters/"+name, &val) if err != nil { diag.AddError( "Error setting project feature property", err.Error(), ) return types.String{}, false } return types.StringValue(result), true } func (r *versionedSettingsResource) setPropertyBool(projectId, name string, state, plan types.Bool, diag *diag.Diagnostics) (types.Bool, bool) { if plan.Equal(state) { return state, true } var strVal *string if plan.IsNull() { strVal = nil } else { val := strconv.FormatBool(plan.ValueBool()) strVal = &val } result, err := r.client.SetField("projects", projectId, "versionedSettings/config/parameters/"+name, strVal) if err != nil { diag.AddError( "Error setting project feature property", err.Error(), ) return types.Bool{}, false } if result == "" { return types.BoolNull(), true } val, err := strconv.ParseBool(result) if err != nil { diag.AddError( "Error setting field", err.Error(), ) return types.Bool{}, false } return types.BoolValue(val), true } func (r *versionedSettingsResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { resource.ImportStatePassthroughID(ctx, path.Root("project_id"), req, resp) }