Skip to content

Commit

Permalink
Merge pull request #996 from hashicorp/TF-21917-go-tfe-support-for-ef…
Browse files Browse the repository at this point in the history
…fective-tag-bindings

Adds ListEffectiveTagBindings to Workspaces/Projects
  • Loading branch information
brandonc authored Nov 4, 2024
2 parents 1318444 + 169df9b commit d388f7e
Show file tree
Hide file tree
Showing 8 changed files with 144 additions and 1 deletion.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
# Unreleased

## Enhancements

* Add support for listing effective tag bindings for a workspace or project by @brandonc

# v1.69.0

## Enhancements
Expand Down
15 changes: 15 additions & 0 deletions mocks/project_mocks.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

15 changes: 15 additions & 0 deletions mocks/workspace_mocks.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

29 changes: 29 additions & 0 deletions project.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,11 @@ type Projects interface {
// ListTagBindings lists all tag bindings associated with the project.
ListTagBindings(ctx context.Context, projectID string) ([]*TagBinding, error)

// ListEffectiveTagBindings lists all tag bindings associated with the project. In practice,
// this should be the same as ListTagBindings since projects do not currently inherit
// tag bindings.
ListEffectiveTagBindings(ctx context.Context, workspaceID string) ([]*EffectiveTagBinding, error)

// AddTagBindings adds or modifies the value of existing tag binding keys for a project.
AddTagBindings(ctx context.Context, projectID string, options ProjectAddTagBindingsOptions) ([]*TagBinding, error)
}
Expand Down Expand Up @@ -218,6 +223,30 @@ func (s *projects) ListTagBindings(ctx context.Context, projectID string) ([]*Ta
return list.Items, nil
}

func (s *projects) ListEffectiveTagBindings(ctx context.Context, projectID string) ([]*EffectiveTagBinding, error) {
if !validStringID(&projectID) {
return nil, ErrInvalidProjectID
}

u := fmt.Sprintf("projects/%s/effective-tag-bindings", url.PathEscape(projectID))
req, err := s.client.NewRequest("GET", u, nil)
if err != nil {
return nil, err
}

var list struct {
*Pagination
Items []*EffectiveTagBinding
}

err = req.Do(ctx, &list)
if err != nil {
return nil, err
}

return list.Items, nil
}

// AddTagBindings adds or modifies the value of existing tag binding keys for a project
func (s *projects) AddTagBindings(ctx context.Context, projectID string, options ProjectAddTagBindingsOptions) ([]*TagBinding, error) {
if !validStringID(&projectID) {
Expand Down
41 changes: 40 additions & 1 deletion projects_integration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -204,7 +204,7 @@ func TestProjectsUpdate(t *testing.T) {

t.Run("with valid options", func(t *testing.T) {
kBefore, kTestCleanup := createProject(t, client, orgTest)
defer kTestCleanup()
t.Cleanup(kTestCleanup)

kAfter, err := client.Projects.Update(ctx, kBefore.ID, ProjectUpdateOptions{
Name: String("new project name"),
Expand All @@ -226,6 +226,45 @@ func TestProjectsUpdate(t *testing.T) {
assert.Len(t, bindings, 1)
assert.Equal(t, "foo", bindings[0].Key)
assert.Equal(t, "bar", bindings[0].Value)

effectiveBindings, err := client.Projects.ListEffectiveTagBindings(ctx, kAfter.ID)
require.NoError(t, err)

assert.Len(t, effectiveBindings, 1)
assert.Equal(t, "foo", effectiveBindings[0].Key)
assert.Equal(t, "bar", effectiveBindings[0].Value)

ws, err := client.Workspaces.Create(ctx, orgTest.Name, WorkspaceCreateOptions{
Name: String("new-workspace-inherits-tags"),
Project: kAfter,
TagBindings: []*TagBinding{
{Key: "baz", Value: "qux"},
},
})
require.NoError(t, err)

t.Cleanup(func() {
err := client.Workspaces.DeleteByID(ctx, ws.ID)
if err != nil {
t.Errorf("Error destroying workspace! WARNING: Dangling resources\n"+
"may exist! The full error is shown below.\n\n"+
"Error: %s", err)
}
})

wsEffectiveBindings, err := client.Workspaces.ListEffectiveTagBindings(ctx, ws.ID)
require.NoError(t, err)

assert.Len(t, wsEffectiveBindings, 2)
for _, b := range wsEffectiveBindings {
if b.Key == "foo" {
assert.Equal(t, "bar", b.Value)
} else if b.Key == "baz" {
assert.Equal(t, "qux", b.Value)
} else {
assert.Fail(t, "unexpected tag binding %q", b.Key)
}
}
}
})

Expand Down
6 changes: 6 additions & 0 deletions tag.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,12 @@ type TagBinding struct {
Value string `jsonapi:"attr,value,omitempty"`
}

type EffectiveTagBinding struct {
ID string `jsonapi:"primary,effective-tag-bindings"`
Key string `jsonapi:"attr,key"`
Value string `jsonapi:"attr,value,omitempty"`
}

func encodeTagFiltersAsParams(filters []*TagBinding) map[string][]string {
if len(filters) == 0 {
return nil
Expand Down
28 changes: 28 additions & 0 deletions workspace.go
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,10 @@ type Workspaces interface {
// ListTagBindings lists all tag bindings associated with the workspace.
ListTagBindings(ctx context.Context, workspaceID string) ([]*TagBinding, error)

// ListEffectiveTagBindings lists all tag bindings associated with the workspace which may be
// either inherited from a project or binded to the workspace itself.
ListEffectiveTagBindings(ctx context.Context, workspaceID string) ([]*EffectiveTagBinding, error)

// AddTagBindings adds or modifies the value of existing tag binding keys for a workspace.
AddTagBindings(ctx context.Context, workspaceID string, options WorkspaceAddTagBindingsOptions) ([]*TagBinding, error)
}
Expand Down Expand Up @@ -769,6 +773,30 @@ func (s *workspaces) ListTagBindings(ctx context.Context, workspaceID string) ([
return list.Items, nil
}

func (s *workspaces) ListEffectiveTagBindings(ctx context.Context, workspaceID string) ([]*EffectiveTagBinding, error) {
if !validStringID(&workspaceID) {
return nil, ErrInvalidWorkspaceID
}

u := fmt.Sprintf("workspaces/%s/effective-tag-bindings", url.PathEscape(workspaceID))
req, err := s.client.NewRequest("GET", u, nil)
if err != nil {
return nil, err
}

var list struct {
*Pagination
Items []*EffectiveTagBinding
}

err = req.Do(ctx, &list)
if err != nil {
return nil, err
}

return list.Items, nil
}

// AddTagBindings adds or modifies the value of existing tag binding keys for a workspace.
func (s *workspaces) AddTagBindings(ctx context.Context, workspaceID string, options WorkspaceAddTagBindingsOptions) ([]*TagBinding, error) {
if !validStringID(&workspaceID) {
Expand Down
7 changes: 7 additions & 0 deletions workspace_integration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1373,6 +1373,13 @@ func TestWorkspacesUpdate(t *testing.T) {
assert.Len(t, bindings, 1)
assert.Equal(t, "foo", bindings[0].Key)
assert.Equal(t, "bar", bindings[0].Value)

effectiveBindings, err := client.Workspaces.ListEffectiveTagBindings(ctx, wTest.ID)
require.NoError(t, err)

assert.Len(t, effectiveBindings, 1)
assert.Equal(t, "foo", effectiveBindings[0].Key)
assert.Equal(t, "bar", effectiveBindings[0].Value)
}
})

Expand Down

0 comments on commit d388f7e

Please sign in to comment.