diff --git a/client/api_client.go b/client/api_client.go index 82a40167..8fd5e09f 100644 --- a/client/api_client.go +++ b/client/api_client.go @@ -144,6 +144,8 @@ type ApiClientInterface interface { ApprovalPolicyAssign(assignment *ApprovalPolicyAssignment) (*ApprovalPolicyAssignment, error) ApprovalPolicyUnassign(scope string, scopeId string) error ApprovalPolicyByScope(scope string, scopeId string) ([]ApprovalPolicyByScope, error) + ApprovalPolicyCreate(payload *ApprovalPolicyCreatePayload) (*ApprovalPolicy, error) + ApprovalPolicyUpdate(payload *ApprovalPolicyUpdatePayload) (*ApprovalPolicy, error) ProjectBudget(projectId string) (*ProjectBudget, error) ProjectBudgetUpdate(projectId string, payload *ProjectBudgetUpdatePayload) (*ProjectBudget, error) ProjectBudgetDelete(projectId string) error diff --git a/client/api_client_mock.go b/client/api_client_mock.go index ad358f45..5b8a9089 100644 --- a/client/api_client_mock.go +++ b/client/api_client_mock.go @@ -156,6 +156,21 @@ func (mr *MockApiClientInterfaceMockRecorder) ApprovalPolicyByScope(arg0, arg1 a return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ApprovalPolicyByScope", reflect.TypeOf((*MockApiClientInterface)(nil).ApprovalPolicyByScope), arg0, arg1) } +// ApprovalPolicyCreate mocks base method. +func (m *MockApiClientInterface) ApprovalPolicyCreate(arg0 *ApprovalPolicyCreatePayload) (*ApprovalPolicy, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "ApprovalPolicyCreate", arg0) + ret0, _ := ret[0].(*ApprovalPolicy) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// ApprovalPolicyCreate indicates an expected call of ApprovalPolicyCreate. +func (mr *MockApiClientInterfaceMockRecorder) ApprovalPolicyCreate(arg0 any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ApprovalPolicyCreate", reflect.TypeOf((*MockApiClientInterface)(nil).ApprovalPolicyCreate), arg0) +} + // ApprovalPolicyUnassign mocks base method. func (m *MockApiClientInterface) ApprovalPolicyUnassign(arg0, arg1 string) error { m.ctrl.T.Helper() @@ -170,6 +185,21 @@ func (mr *MockApiClientInterfaceMockRecorder) ApprovalPolicyUnassign(arg0, arg1 return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ApprovalPolicyUnassign", reflect.TypeOf((*MockApiClientInterface)(nil).ApprovalPolicyUnassign), arg0, arg1) } +// ApprovalPolicyUpdate mocks base method. +func (m *MockApiClientInterface) ApprovalPolicyUpdate(arg0 *ApprovalPolicyUpdatePayload) (*ApprovalPolicy, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "ApprovalPolicyUpdate", arg0) + ret0, _ := ret[0].(*ApprovalPolicy) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// ApprovalPolicyUpdate indicates an expected call of ApprovalPolicyUpdate. +func (mr *MockApiClientInterfaceMockRecorder) ApprovalPolicyUpdate(arg0 any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ApprovalPolicyUpdate", reflect.TypeOf((*MockApiClientInterface)(nil).ApprovalPolicyUpdate), arg0) +} + // AssignAgentsToProjects mocks base method. func (m *MockApiClientInterface) AssignAgentsToProjects(arg0 AssignProjectsAgentsAssignmentsPayload) (*ProjectsAgentsAssignments, error) { m.ctrl.T.Helper() diff --git a/client/approval_policy.go b/client/approval_policy.go index 2c42c5d9..ead206fe 100644 --- a/client/approval_policy.go +++ b/client/approval_policy.go @@ -26,6 +26,37 @@ type ApprovalPolicyByScope struct { ApprovalPolicy *ApprovalPolicy `json:"blueprint"` } +type ApprovalPolicyCreatePayload struct { + Name string `json:"name"` + Repository string `json:"repository"` + Revision string `json:"revision,omitempty"` + Path string `json:"path,omitempty"` + TokenId string `json:"tokenId,omitempty"` + GithubInstallationId int `json:"githubInstallationId,omitempty"` + BitbucketClientKey string `json:"bitbucketClientKey,omitempty"` + IsBitbucketServer bool `json:"isBitbucketServer,omitempty"` + IsGitlabEnterprise bool `json:"isGitLabEnterprise"` + IsGithubEnterprise bool `json:"isGitHubEnterprise"` + IsGitLab bool `json:"isGitLab" tfschema:"is_gitlab"` + IsAzureDevOps bool `json:"isAzureDevOps" tfschema:"is_azure_devops"` +} + +type ApprovalPolicyUpdatePayload struct { + Id string `json:"id"` + Name string `json:"name"` + Repository string `json:"repository"` + Revision string `json:"revision,omitempty"` + Path string `json:"path,omitempty"` + TokenId string `json:"tokenId,omitempty"` + GithubInstallationId int `json:"githubInstallationId,omitempty"` + BitbucketClientKey string `json:"bitbucketClientKey,omitempty"` + IsBitbucketServer bool `json:"isBitbucketServer,omitempty"` + IsGitlabEnterprise bool `json:"isGitLabEnterprise"` + IsGithubEnterprise bool `json:"isGitHubEnterprise"` + IsGitLab bool `json:"isGitLab" tfschema:"is_gitlab"` + IsAzureDevOps bool `json:"isAzureDevOps" tfschema:"is_azure_devops"` +} + type ApprovalPolicyAssignmentScope string const ( @@ -38,6 +69,37 @@ type ApprovalPolicyAssignment struct { BlueprintId string `json:"blueprintId"` } +func (client *ApiClient) ApprovalPolicyCreate(payload *ApprovalPolicyCreatePayload) (*ApprovalPolicy, error) { + organizationId, err := client.OrganizationId() + if err != nil { + return nil, err + } + + payloadWithOrganizationId := struct { + ApprovalPolicyCreatePayload + OrganizationId string `json:"organizationId"` + }{ + *payload, + organizationId, + } + + var result ApprovalPolicy + if err := client.http.Post("/approval-policy", &payloadWithOrganizationId, &result); err != nil { + return nil, err + } + + return &result, nil +} + +func (client *ApiClient) ApprovalPolicyUpdate(payload *ApprovalPolicyUpdatePayload) (*ApprovalPolicy, error) { + var result ApprovalPolicy + if err := client.http.Put("/approval-policy", payload, &result); err != nil { + return nil, err + } + + return &result, nil +} + func (client *ApiClient) ApprovalPolicies(name string) ([]ApprovalPolicy, error) { organizationId, err := client.OrganizationId() if err != nil { diff --git a/client/approval_policy_test.go b/client/approval_policy_test.go index 35d7a70f..8fba463f 100644 --- a/client/approval_policy_test.go +++ b/client/approval_policy_test.go @@ -4,6 +4,7 @@ import ( "fmt" . "github.com/env0/terraform-provider-env0/client" + "github.com/jinzhu/copier" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" "go.uber.org/mock/gomock" @@ -117,4 +118,66 @@ var _ = Describe("Approval Policy Client", func() { Expect(ret).To(Equal(mockApprovalPolicyByScopeArr)) }) }) + + Describe("Create ApprovalPolicy", func() { + var result *ApprovalPolicy + var err error + + BeforeEach(func() { + mockOrganizationIdCall(organizationId).Times(1) + + createApprovalPolicyPayload := ApprovalPolicyCreatePayload{} + copier.Copy(&createApprovalPolicyPayload, &mockApprovalPolicy) + + expectedCreateRequest := struct { + ApprovalPolicyCreatePayload + OrganizationId string `json:"organizationId"` + }{ + createApprovalPolicyPayload, + organizationId, + } + + httpCall = mockHttpClient.EXPECT(). + Post("/approval-policy", &expectedCreateRequest, gomock.Any()). + Do(func(path string, request interface{}, response *ApprovalPolicy) { + *response = mockApprovalPolicy + }).Times(1) + + result, err = apiClient.ApprovalPolicyCreate(&createApprovalPolicyPayload) + }) + + It("Should not return error", func() { + Expect(err).To(BeNil()) + }) + + It("Should return created Approval Policy", func() { + Expect(*result).To(Equal(mockApprovalPolicy)) + }) + }) + + Describe("Update ApprovalPolicy", func() { + var result *ApprovalPolicy + var err error + + BeforeEach(func() { + updateApprovalPolicyPayload := ApprovalPolicyUpdatePayload{} + copier.Copy(&updateApprovalPolicyPayload, &mockApprovalPolicy) + + httpCall = mockHttpClient.EXPECT(). + Put("/approval-policy", &updateApprovalPolicyPayload, gomock.Any()). + Do(func(path string, request interface{}, response *ApprovalPolicy) { + *response = mockApprovalPolicy + }).Times(1) + + result, err = apiClient.ApprovalPolicyUpdate(&updateApprovalPolicyPayload) + }) + + It("Should not return error", func() { + Expect(err).To(BeNil()) + }) + + It("Should return updated Approval Policy", func() { + Expect(*result).To(Equal(mockApprovalPolicy)) + }) + }) }) diff --git a/env0/resource_approval_policy.go b/env0/resource_approval_policy.go index 8161bf3c..88fb3e89 100644 --- a/env0/resource_approval_policy.go +++ b/env0/resource_approval_policy.go @@ -27,19 +27,17 @@ func resourceApprovalPolicy() *schema.Resource { func resourceApprovalPolicyCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { apiClient := meta.(client.ApiClientInterface) - var payload client.TemplateCreatePayload + var payload client.ApprovalPolicyCreatePayload if err := readResourceData(&payload, d); err != nil { return diag.Errorf("schema resource data deserialization failed: %v", err) } - payload.Type = string(ApprovalPolicy) - - template, err := apiClient.TemplateCreate(payload) + approvalPolicy, err := apiClient.ApprovalPolicyCreate(&payload) if err != nil { - return diag.Errorf("could not create approval policy template: %v", err) + return diag.Errorf("failed to create approval policy: %v", err) } - d.SetId(template.Id) + d.SetId(approvalPolicy.Id) return nil } @@ -68,16 +66,13 @@ func resourceApprovalPolicyRead(ctx context.Context, d *schema.ResourceData, met func resourceApprovalPolicyUpdate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { apiClient := meta.(client.ApiClientInterface) - request, problem := templateCreatePayloadFromParameters("", d) - if problem != nil { - return problem + var payload client.ApprovalPolicyUpdatePayload + if err := readResourceData(&payload, d); err != nil { + return diag.Errorf("schema resource data deserialization failed: %v", err) } - request.Type = string(ApprovalPolicy) - - _, err := apiClient.TemplateUpdate(d.Id(), request) - if err != nil { - return diag.Errorf("could not update approval policy template: %v", err) + if _, err := apiClient.ApprovalPolicyUpdate(&payload); err != nil { + return diag.Errorf("failed to update approval policy: %v", err) } return nil diff --git a/env0/resource_approval_policy_test.go b/env0/resource_approval_policy_test.go index 4069bd82..5b0aeb52 100644 --- a/env0/resource_approval_policy_test.go +++ b/env0/resource_approval_policy_test.go @@ -54,9 +54,9 @@ func TestUnitApprovalPolicyResource(t *testing.T) { var updatedTemplate client.Template copier.Copy(&updatedTemplate, &updatedApprovalPolicy) - template.Type = string(ApprovalPolicy) + updatedTemplate.Type = string(ApprovalPolicy) - createPayload := client.TemplateCreatePayload{ + createPayload := client.ApprovalPolicyCreatePayload{ Name: approvalPolicy.Name, Repository: approvalPolicy.Repository, Path: approvalPolicy.Path, @@ -64,17 +64,16 @@ func TestUnitApprovalPolicyResource(t *testing.T) { TokenId: approvalPolicy.TokenId, GithubInstallationId: approvalPolicy.GithubInstallationId, IsGithubEnterprise: approvalPolicy.IsGithubEnterprise, - Type: string(ApprovalPolicy), } - updatePayload := client.TemplateCreatePayload{ + updatePayload := client.ApprovalPolicyUpdatePayload{ Name: updatedApprovalPolicy.Name, Repository: updatedApprovalPolicy.Repository, Path: updatedApprovalPolicy.Path, Revision: updatedApprovalPolicy.Revision, TokenId: updatedApprovalPolicy.TokenId, IsAzureDevOps: updatedApprovalPolicy.IsAzureDevOps, - Type: string(ApprovalPolicy), + Id: approvalPolicy.Id, } t.Run("Success", func(t *testing.T) { @@ -127,9 +126,9 @@ func TestUnitApprovalPolicyResource(t *testing.T) { runUnitTest(t, testCase, func(mock *client.MockApiClientInterface) { gomock.InOrder( - mock.EXPECT().TemplateCreate(createPayload).Times(1).Return(template, nil), + mock.EXPECT().ApprovalPolicyCreate(&createPayload).Times(1).Return(&approvalPolicy, nil), mock.EXPECT().Template(approvalPolicy.Id).Times(2).Return(template, nil), - mock.EXPECT().TemplateUpdate(approvalPolicy.Id, updatePayload).Times(1).Return(updatedTemplate, nil), + mock.EXPECT().ApprovalPolicyUpdate(&updatePayload).Times(1).Return(&updatedApprovalPolicy, nil), mock.EXPECT().Template(approvalPolicy.Id).Times(1).Return(updatedTemplate, nil), mock.EXPECT().TemplateDelete(approvalPolicy.Id).Times(1).Return(nil), ) @@ -189,7 +188,7 @@ func TestUnitApprovalPolicyResource(t *testing.T) { runUnitTest(t, testCase, func(mock *client.MockApiClientInterface) { gomock.InOrder( - mock.EXPECT().TemplateCreate(createPayload).Times(1).Return(template, nil), + mock.EXPECT().ApprovalPolicyCreate(&createPayload).Times(1).Return(&approvalPolicy, nil), mock.EXPECT().Template(template.Id).Times(2).Return(deletedTemplate, nil), ) }) @@ -248,7 +247,7 @@ func TestUnitApprovalPolicyResource(t *testing.T) { runUnitTest(t, testCase, func(mock *client.MockApiClientInterface) { gomock.InOrder( - mock.EXPECT().TemplateCreate(createPayload).Times(1).Return(template, nil), + mock.EXPECT().ApprovalPolicyCreate(&createPayload).Times(1).Return(&approvalPolicy, nil), mock.EXPECT().Template(template.Id).Times(2).Return(client.Template{}, http.NewMockFailedResponseError(404)), ) }) @@ -267,13 +266,13 @@ func TestUnitApprovalPolicyResource(t *testing.T) { "github_installation_id": approvalPolicy.GithubInstallationId, "is_github_enterprise": "true", }), - ExpectError: regexp.MustCompile("could not create approval policy template: error"), + ExpectError: regexp.MustCompile("failed to create approval policy: error"), }, }, } runUnitTest(t, testCase, func(mock *client.MockApiClientInterface) { - mock.EXPECT().TemplateCreate(createPayload).Times(1).Return(client.Template{}, errors.New("error")) + mock.EXPECT().ApprovalPolicyCreate(&createPayload).Times(1).Return(nil, errors.New("error")) }) }) @@ -300,16 +299,16 @@ func TestUnitApprovalPolicyResource(t *testing.T) { "token_id": updatedApprovalPolicy.TokenId, "is_azure_devops": "true", }), - ExpectError: regexp.MustCompile("could not update approval policy template: error"), + ExpectError: regexp.MustCompile("failed to update approval policy: error"), }, }, } runUnitTest(t, testCase, func(mock *client.MockApiClientInterface) { gomock.InOrder( - mock.EXPECT().TemplateCreate(createPayload).Times(1).Return(template, nil), + mock.EXPECT().ApprovalPolicyCreate(&createPayload).Times(1).Return(&approvalPolicy, nil), mock.EXPECT().Template(approvalPolicy.Id).Times(2).Return(template, nil), - mock.EXPECT().TemplateUpdate(approvalPolicy.Id, updatePayload).Times(1).Return(client.Template{}, errors.New("error")), + mock.EXPECT().ApprovalPolicyUpdate(&updatePayload).Times(1).Return(nil, errors.New("error")), mock.EXPECT().TemplateDelete(approvalPolicy.Id).Times(1).Return(nil), ) }) @@ -340,7 +339,7 @@ func TestUnitApprovalPolicyResource(t *testing.T) { runUnitTest(t, testCase, func(mock *client.MockApiClientInterface) { gomock.InOrder( - mock.EXPECT().TemplateCreate(createPayload).Times(1).Return(template, nil), + mock.EXPECT().ApprovalPolicyCreate(&createPayload).Times(1).Return(&approvalPolicy, nil), mock.EXPECT().Template(approvalPolicy.Id).Times(3).Return(template, nil), mock.EXPECT().TemplateDelete(approvalPolicy.Id).Times(1).Return(nil), ) @@ -373,7 +372,7 @@ func TestUnitApprovalPolicyResource(t *testing.T) { runUnitTest(t, testCase, func(mock *client.MockApiClientInterface) { gomock.InOrder( - mock.EXPECT().TemplateCreate(createPayload).Times(1).Return(template, nil), + mock.EXPECT().ApprovalPolicyCreate(&createPayload).Times(1).Return(&approvalPolicy, nil), mock.EXPECT().Template(approvalPolicy.Id).Times(1).Return(template, nil), mock.EXPECT().Template(approvalPolicy.Id).Times(1).Return(notApprovalPolicyTemplate, nil), mock.EXPECT().TemplateDelete(approvalPolicy.Id).Times(1).Return(nil), @@ -406,7 +405,7 @@ func TestUnitApprovalPolicyResource(t *testing.T) { runUnitTest(t, testCase, func(mock *client.MockApiClientInterface) { gomock.InOrder( - mock.EXPECT().TemplateCreate(createPayload).Times(1).Return(template, nil), + mock.EXPECT().ApprovalPolicyCreate(&createPayload).Times(1).Return(&approvalPolicy, nil), mock.EXPECT().Template(approvalPolicy.Id).Times(1).Return(template, nil), mock.EXPECT().ApprovalPolicies(approvalPolicy.Name).Times(1).Return([]client.ApprovalPolicy{approvalPolicy}, nil), mock.EXPECT().Template(approvalPolicy.Id).Times(1).Return(template, nil), @@ -441,7 +440,7 @@ func TestUnitApprovalPolicyResource(t *testing.T) { runUnitTest(t, testCase, func(mock *client.MockApiClientInterface) { gomock.InOrder( - mock.EXPECT().TemplateCreate(createPayload).Times(1).Return(template, nil), + mock.EXPECT().ApprovalPolicyCreate(&createPayload).Times(1).Return(&approvalPolicy, nil), mock.EXPECT().Template(approvalPolicy.Id).Times(1).Return(template, nil), mock.EXPECT().ApprovalPolicies(approvalPolicy.Name).Times(1).Return([]client.ApprovalPolicy{}, nil), mock.EXPECT().TemplateDelete(approvalPolicy.Id).Times(1).Return(nil), @@ -475,7 +474,7 @@ func TestUnitApprovalPolicyResource(t *testing.T) { runUnitTest(t, testCase, func(mock *client.MockApiClientInterface) { gomock.InOrder( - mock.EXPECT().TemplateCreate(createPayload).Times(1).Return(template, nil), + mock.EXPECT().ApprovalPolicyCreate(&createPayload).Times(1).Return(&approvalPolicy, nil), mock.EXPECT().Template(approvalPolicy.Id).Times(1).Return(template, nil), mock.EXPECT().ApprovalPolicies(approvalPolicy.Name).Times(1).Return([]client.ApprovalPolicy{approvalPolicy, approvalPolicy}, nil), mock.EXPECT().TemplateDelete(approvalPolicy.Id).Times(1).Return(nil),