From 0c41524ea9f76d539e239891b82272bc57c88bad Mon Sep 17 00:00:00 2001 From: Sebastian Rivera Date: Thu, 26 Dec 2024 13:12:20 -0500 Subject: [PATCH 1/4] Add DeleteAllTagBindings to projects --- project.go | 27 +++++++++++++++++++++++++++ projects_integration_test.go | 27 +++++++++++++++++++++++++++ 2 files changed, 54 insertions(+) diff --git a/project.go b/project.go index faf1da8c5..433549f3d 100644 --- a/project.go +++ b/project.go @@ -44,6 +44,9 @@ type Projects interface { // AddTagBindings adds or modifies the value of existing tag binding keys for a project. AddTagBindings(ctx context.Context, projectID string, options ProjectAddTagBindingsOptions) ([]*TagBinding, error) + + // DeleteAllTagBindings removes all existing tag bindings for a project. + DeleteAllTagBindings(ctx context.Context, projectID string) error } // projects implements Projects @@ -326,6 +329,30 @@ func (s *projects) Delete(ctx context.Context, projectID string) error { return req.Do(ctx, nil) } +// Delete all tag bindings associated with a project. +func (s *projects) DeleteAllTagBindings(ctx context.Context, projectID string) error { + if !validStringID(&projectID) { + return ErrInvalidProjectID + } + + type aliasOpts struct { + Type string `jsonapi:"primary,projects"` + TagBindings []*TagBinding `jsonapi:"relation,tag-bindings"` + } + + opts := &aliasOpts{ + TagBindings: []*TagBinding{}, + } + + u := fmt.Sprintf("projects/%s", url.PathEscape(projectID)) + req, err := s.client.NewRequest("PATCH", u, opts) + if err != nil { + return err + } + + return req.Do(ctx, nil) +} + func (o ProjectCreateOptions) valid() error { if !validString(&o.Name) { return ErrRequiredName diff --git a/projects_integration_test.go b/projects_integration_test.go index b643aad9e..b84a9e869 100644 --- a/projects_integration_test.go +++ b/projects_integration_test.go @@ -380,6 +380,33 @@ func TestProjectsAddTagBindings(t *testing.T) { }) } +func TestProjects_DeleteAllTagBindings(t *testing.T) { + skipUnlessBeta(t) + + client := testClient(t) + ctx := context.Background() + + pTest, wCleanup := createProject(t, client, nil) + t.Cleanup(wCleanup) + + tagBindings := []*TagBinding{ + {Key: "foo", Value: "bar"}, + {Key: "baz", Value: "qux"}, + } + + _, err := client.Projects.AddTagBindings(ctx, pTest.ID, ProjectAddTagBindingsOptions{ + TagBindings: tagBindings, + }) + require.NoError(t, err) + + err = client.Projects.DeleteAllTagBindings(ctx, pTest.ID) + require.NoError(t, err) + + bindings, err := client.Projects.ListTagBindings(ctx, pTest.ID) + require.NoError(t, err) + require.Empty(t, bindings) +} + func TestProjectsDelete(t *testing.T) { client := testClient(t) ctx := context.Background() From eb3b7b9ea38d0b4086a6402f1538391504c7a034 Mon Sep 17 00:00:00 2001 From: Sebastian Rivera Date: Thu, 26 Dec 2024 13:22:42 -0500 Subject: [PATCH 2/4] Add DeleteAllTagBindings to workspaces --- workspace.go | 29 +++++++++++++++++++++++++++++ workspace_integration_test.go | 27 +++++++++++++++++++++++++++ 2 files changed, 56 insertions(+) diff --git a/workspace.go b/workspace.go index 40ee48d71..be1004686 100644 --- a/workspace.go +++ b/workspace.go @@ -141,6 +141,9 @@ type Workspaces interface { // AddTagBindings adds or modifies the value of existing tag binding keys for a workspace. AddTagBindings(ctx context.Context, workspaceID string, options WorkspaceAddTagBindingsOptions) ([]*TagBinding, error) + + // DeleteAllTagBindings removes all tag bindings for a workspace. + DeleteAllTagBindings(ctx context.Context, workspaceID string) error } // workspaces implements Workspaces. @@ -829,6 +832,32 @@ func (s *workspaces) AddTagBindings(ctx context.Context, workspaceID string, opt return response.Items, err } +// DeleteAllTagBindings removes all tag bindings associated with a workspace. +// This method will not remove any inherited tag bindings, which must be +// explicitly removed from the parent project. +func (s *workspaces) DeleteAllTagBindings(ctx context.Context, workspaceID string) error { + if !validStringID(&workspaceID) { + return ErrInvalidWorkspaceID + } + + type aliasOpts struct { + Type string `jsonapi:"primary,workspaces"` + TagBindings []*TagBinding `jsonapi:"relation,tag-bindings"` + } + + opts := &aliasOpts{ + TagBindings: []*TagBinding{}, + } + + u := fmt.Sprintf("workspaces/%s", url.PathEscape(workspaceID)) + req, err := s.client.NewRequest("PATCH", u, opts) + if err != nil { + return err + } + + return req.Do(ctx, nil) +} + // Create is used to create a new workspace. func (s *workspaces) Create(ctx context.Context, organization string, options WorkspaceCreateOptions) (*Workspace, error) { if !validStringID(&organization) { diff --git a/workspace_integration_test.go b/workspace_integration_test.go index 28e34c61c..04b28c543 100644 --- a/workspace_integration_test.go +++ b/workspace_integration_test.go @@ -1237,6 +1237,33 @@ func TestWorkspacesAddTagBindings(t *testing.T) { }) } +func TestWorkspaces_DeleteAllTagBindings(t *testing.T) { + skipUnlessBeta(t) + + client := testClient(t) + ctx := context.Background() + + wTest, wCleanup := createWorkspace(t, client, nil) + t.Cleanup(wCleanup) + + tagBindings := []*TagBinding{ + {Key: "foo", Value: "bar"}, + {Key: "baz", Value: "qux"}, + } + + _, err := client.Workspaces.AddTagBindings(ctx, wTest.ID, WorkspaceAddTagBindingsOptions{ + TagBindings: tagBindings, + }) + require.NoError(t, err) + + err = client.Workspaces.DeleteAllTagBindings(ctx, wTest.ID) + require.NoError(t, err) + + bindings, err := client.Workspaces.ListTagBindings(ctx, wTest.ID) + require.NoError(t, err) + require.Empty(t, bindings) +} + func TestWorkspacesUpdate(t *testing.T) { client := testClient(t) ctx := context.Background() From 6303726c182dc747bbd1a05f1df197bc8d7e2fe8 Mon Sep 17 00:00:00 2001 From: Sebastian Rivera Date: Thu, 26 Dec 2024 13:30:34 -0500 Subject: [PATCH 3/4] Update changelog entry --- CHANGELOG.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7aa40a053..2490c133a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,7 +4,8 @@ * Add support for project level auto destroy settings @simonxmh [#1011](https://github.com/hashicorp/go-tfe/pull/1011) * Add BETA support for Linux arm64 agents, which is EXPERIMENTAL, SUBJECT TO CHANGE, and may not be available to all users @natalie-todd [#1022](https://github.com/hashicorp/go-tfe/pull/1022) - +* Adds support to delete all tag bindings on either a project or workspace by @sebasslash [#1023](https://github.com/hashicorp/go-tfe/pull/1023) + # v1.71.0 ## Enhancements From e1ea9cbf58a5cd6c9805996ce8c4492c2e45f56a Mon Sep 17 00:00:00 2001 From: Sebastian Rivera Date: Thu, 26 Dec 2024 13:48:53 -0500 Subject: [PATCH 4/4] Run generate_mocks script --- mocks/project_mocks.go | 14 ++++++++++++++ mocks/workspace_mocks.go | 14 ++++++++++++++ 2 files changed, 28 insertions(+) diff --git a/mocks/project_mocks.go b/mocks/project_mocks.go index 405c8afd7..9f2ed5c8c 100644 --- a/mocks/project_mocks.go +++ b/mocks/project_mocks.go @@ -84,6 +84,20 @@ func (mr *MockProjectsMockRecorder) Delete(ctx, projectID any) *gomock.Call { return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Delete", reflect.TypeOf((*MockProjects)(nil).Delete), ctx, projectID) } +// DeleteAllTagBindings mocks base method. +func (m *MockProjects) DeleteAllTagBindings(ctx context.Context, projectID string) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "DeleteAllTagBindings", ctx, projectID) + ret0, _ := ret[0].(error) + return ret0 +} + +// DeleteAllTagBindings indicates an expected call of DeleteAllTagBindings. +func (mr *MockProjectsMockRecorder) DeleteAllTagBindings(ctx, projectID any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteAllTagBindings", reflect.TypeOf((*MockProjects)(nil).DeleteAllTagBindings), ctx, projectID) +} + // List mocks base method. func (m *MockProjects) List(ctx context.Context, organization string, options *tfe.ProjectListOptions) (*tfe.ProjectList, error) { m.ctrl.T.Helper() diff --git a/mocks/workspace_mocks.go b/mocks/workspace_mocks.go index f9d89571d..537ad370d 100644 --- a/mocks/workspace_mocks.go +++ b/mocks/workspace_mocks.go @@ -128,6 +128,20 @@ func (mr *MockWorkspacesMockRecorder) Delete(ctx, organization, workspace any) * return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Delete", reflect.TypeOf((*MockWorkspaces)(nil).Delete), ctx, organization, workspace) } +// DeleteAllTagBindings mocks base method. +func (m *MockWorkspaces) DeleteAllTagBindings(ctx context.Context, workspaceID string) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "DeleteAllTagBindings", ctx, workspaceID) + ret0, _ := ret[0].(error) + return ret0 +} + +// DeleteAllTagBindings indicates an expected call of DeleteAllTagBindings. +func (mr *MockWorkspacesMockRecorder) DeleteAllTagBindings(ctx, workspaceID any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteAllTagBindings", reflect.TypeOf((*MockWorkspaces)(nil).DeleteAllTagBindings), ctx, workspaceID) +} + // DeleteByID mocks base method. func (m *MockWorkspaces) DeleteByID(ctx context.Context, workspaceID string) error { m.ctrl.T.Helper()