From d5d18eb673aba890e6195ae4a2da642c941c3a79 Mon Sep 17 00:00:00 2001 From: Alon Noga <54034642+alonnoga@users.noreply.github.com> Date: Wed, 5 Jun 2024 17:23:54 +0300 Subject: [PATCH] Chore: add soft delete to configuration variable (#866) * add soft delete to configuration variable * add soft delete flag to environment import resource * streamline the soft delete test * streamline soft delete test on config variable * add default and change GetOk to Get * fix configuration variable import --------- Co-authored-by: Alon Noga Co-authored-by: Tomer Heber --- client/api_client.go | 1 + client/api_client_mock.go | 14 +++++++++ client/environment_import.go | 4 +++ client/environment_import_test.go | 12 ++++++++ env0/resource_configuration_variable.go | 13 +++++++++ env0/resource_configuration_variable_test.go | 23 +++++++++++++++ env0/resource_environment_import.go | 20 +++++++++++-- env0/resource_environment_import_test.go | 30 ++++++++++++++++++++ 8 files changed, 114 insertions(+), 3 deletions(-) diff --git a/client/api_client.go b/client/api_client.go index ea26cddf..cab3a195 100644 --- a/client/api_client.go +++ b/client/api_client.go @@ -155,6 +155,7 @@ type ApiClientInterface interface { EnvironmentImportCreate(payload *EnvironmentImportCreatePayload) (*EnvironmentImport, error) EnvironmentImportUpdate(id string, payload *EnvironmentImportUpdatePayload) (*EnvironmentImport, error) EnvironmentImportGet(id string) (*EnvironmentImport, error) + EnvironmentImportDelete(id string) error ConfigurationSetCreate(payload *CreateConfigurationSetPayload) (*ConfigurationSet, error) ConfigurationSetUpdate(id string, payload *UpdateConfigurationSetPayload) (*ConfigurationSet, error) ConfigurationSet(id string) (*ConfigurationSet, error) diff --git a/client/api_client_mock.go b/client/api_client_mock.go index 1035874f..292c682d 100644 --- a/client/api_client_mock.go +++ b/client/api_client_mock.go @@ -807,6 +807,20 @@ func (mr *MockApiClientInterfaceMockRecorder) EnvironmentImportCreate(arg0 any) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "EnvironmentImportCreate", reflect.TypeOf((*MockApiClientInterface)(nil).EnvironmentImportCreate), arg0) } +// EnvironmentImportDelete mocks base method. +func (m *MockApiClientInterface) EnvironmentImportDelete(arg0 string) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "EnvironmentImportDelete", arg0) + ret0, _ := ret[0].(error) + return ret0 +} + +// EnvironmentImportDelete indicates an expected call of EnvironmentImportDelete. +func (mr *MockApiClientInterfaceMockRecorder) EnvironmentImportDelete(arg0 any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "EnvironmentImportDelete", reflect.TypeOf((*MockApiClientInterface)(nil).EnvironmentImportDelete), arg0) +} + // EnvironmentImportGet mocks base method. func (m *MockApiClientInterface) EnvironmentImportGet(arg0 string) (*EnvironmentImport, error) { m.ctrl.T.Helper() diff --git a/client/environment_import.go b/client/environment_import.go index a3417983..caf5a123 100644 --- a/client/environment_import.go +++ b/client/environment_import.go @@ -84,3 +84,7 @@ func (client *ApiClient) EnvironmentImportGet(id string) (*EnvironmentImport, er return &result, nil } + +func (client *ApiClient) EnvironmentImportDelete(id string) error { + return client.http.Delete("/environment-imports/"+id, nil) +} diff --git a/client/environment_import_test.go b/client/environment_import_test.go index ad145d0a..e6f52d44 100644 --- a/client/environment_import_test.go +++ b/client/environment_import_test.go @@ -107,4 +107,16 @@ var _ = Describe("Environment Import Client", func() { }) }) + Describe("EnvironmentImportDelete", func() { + var err error + + BeforeEach(func() { + mockHttpClient.EXPECT().Delete("/environment-imports/"+mockEnvironmentImport.Id, nil).Times(1) + err = apiClient.EnvironmentImportDelete(mockEnvironmentImport.Id) + }) + + It("Should not return error", func() { + Expect(err).To(BeNil()) + }) + }) }) diff --git a/env0/resource_configuration_variable.go b/env0/resource_configuration_variable.go index 91fb5f08..bf9dd2ab 100644 --- a/env0/resource_configuration_variable.go +++ b/env0/resource_configuration_variable.go @@ -110,6 +110,12 @@ func resourceConfigurationVariable() *schema.Resource { Description: "the value of this variable must match provided regular expression (enforced only in env0 UI)", Optional: true, }, + "soft_delete": { + Type: schema.TypeBool, + Description: "soft delete the configuration variable, once removed from the configuration it won't be deleted from env0", + Optional: true, + Default: false, + }, }, } } @@ -237,6 +243,11 @@ func resourceConfigurationVariableUpdate(ctx context.Context, d *schema.Resource } func resourceConfigurationVariableDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + // don't delete if soft delete is set + if softDelete := d.Get("soft_delete"); softDelete.(bool) { + return nil + } + apiClient := meta.(client.ApiClientInterface) id := d.Id() @@ -250,6 +261,8 @@ func resourceConfigurationVariableDelete(ctx context.Context, d *schema.Resource func resourceConfigurationVariableImport(ctx context.Context, d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) { var configurationParams ConfigurationVariableParams inputData := d.Id() + // soft delete isn't part of the configuration variable, so we need to set it + d.Set("soft_delete", false) err := json.Unmarshal([]byte(inputData), &configurationParams) // We need this conversion since getConfigurationVariable query by the scope and in our BE we use blueprint as the scope name instead of template if string(configurationParams.Scope) == "TEMPLATE" { diff --git a/env0/resource_configuration_variable_test.go b/env0/resource_configuration_variable_test.go index 639b267a..ccd390d6 100644 --- a/env0/resource_configuration_variable_test.go +++ b/env0/resource_configuration_variable_test.go @@ -773,4 +773,27 @@ resource "%s" "test" { mock.EXPECT().ConfigurationVariableDelete(configVar.Id).Times(1).Return(nil) }) }) + + t.Run("When soft delete is on, it should not actually delete", func(t *testing.T) { + createTestCase := resource.TestCase{ + Steps: []resource.TestStep{ + { + Config: resourceConfigCreate(resourceType, resourceName, map[string]interface{}{ + "name": configVar.Name, + "description": configVar.Description, + "value": configVar.Value, + "is_read_only": strconv.FormatBool(*configVar.IsReadOnly), + "is_required": strconv.FormatBool(*configVar.IsRequired), + "soft_delete": true, + }), + }, + }, + } + + runUnitTest(t, createTestCase, func(mock *client.MockApiClientInterface) { + mock.EXPECT().ConfigurationVariableCreate(configurationVariableCreateParams).Times(1).Return(configVar, nil) + mock.EXPECT().ConfigurationVariablesById(configVar.Id).Times(1).Return(configVar, nil) + mock.EXPECT().ConfigurationVariableDelete(configVar.Id).Times(0) + }) + }) } diff --git a/env0/resource_environment_import.go b/env0/resource_environment_import.go index b7f427ee..d6cc0d21 100644 --- a/env0/resource_environment_import.go +++ b/env0/resource_environment_import.go @@ -58,6 +58,12 @@ func resourceEnvironmentImport() *schema.Resource { Description: "iac version of the environment", Optional: true, }, + "soft_delete": { + Type: schema.TypeBool, + Description: "soft delete the configuration variable, once removed from the configuration it won't be deleted from env0", + Optional: true, + Default: false, + }, }, } } @@ -126,9 +132,17 @@ func resourceEnvironmentImportUpdate(ctx context.Context, d *schema.ResourceData return nil } -// should not actually delete the environment import -// this resource is used to populate the environment import wizard data -// we don't want to delete the environment import after it's been created outside the wizard func resourceEnvironmentImportDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + // don't delete if soft delete is set + if softDelete := d.Get("soft_delete"); softDelete.(bool) { + return nil + } + + apiClient := meta.(client.ApiClientInterface) + + if err := apiClient.EnvironmentImportDelete(d.Id()); err != nil { + return diag.Errorf("could not delete environment import: %v", err) + } + return nil } diff --git a/env0/resource_environment_import_test.go b/env0/resource_environment_import_test.go index 20eadae4..bea2ed53 100644 --- a/env0/resource_environment_import_test.go +++ b/env0/resource_environment_import_test.go @@ -116,7 +116,37 @@ func TestEnvironmentImportResource(t *testing.T) { gomock.InOrder( mock.EXPECT().EnvironmentImportGet(gomock.Any()).Times(2).Return(&environmentImport, nil), // 1 after create, 1 before update mock.EXPECT().EnvironmentImportGet(gomock.Any()).Times(1).Return(&updatedEnvironmentImport, nil), // 1 after update + mock.EXPECT().EnvironmentImportDelete(environmentImport.Id).Times(1), // 1 after update ) }) }) + + t.Run("Environment Import soft delete", func(t *testing.T) { + environmentImport := client.EnvironmentImport{ + Id: "id0", + Name: "name0", + } + + testCase := resource.TestCase{ + Steps: []resource.TestStep{ + { + Config: resourceConfigCreate(resourceType, resourceName, map[string]interface{}{ + "name": environmentImport.Name, + "soft_delete": true, + })}, + }, + } + + runUnitTest(t, testCase, func(mock *client.MockApiClientInterface) { + mock.EXPECT().EnvironmentImportCreate(&client.EnvironmentImportCreatePayload{ + Name: environmentImport.Name, + }).Times(1).Return(&environmentImport, nil) + + gomock.InOrder( + mock.EXPECT().EnvironmentImportGet(gomock.Any()).Times(2).Return(&environmentImport, nil), + mock.EXPECT().EnvironmentImportDelete(environmentImport.Id).Times(0), + ) + }) + }) + }