Skip to content

Commit

Permalink
SCALRCORE-22261 Adjust workspace tags methods
Browse files Browse the repository at this point in the history
[API_BRANCH]
  • Loading branch information
petroprotsakh committed Jul 29, 2022
1 parent fc9eb9d commit 243a197
Show file tree
Hide file tree
Showing 6 changed files with 166 additions and 183 deletions.
15 changes: 14 additions & 1 deletion helper_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -261,7 +261,7 @@ func createVcsProvider(t *testing.T, client *Client, envs []*Environment) (*VcsP
func createTag(t *testing.T, client *Client) (*Tag, func()) {
ctx := context.Background()
tag, err := client.Tags.Create(ctx, TagCreateOptions{
Name: String("test-role-" + randomString(t)),
Name: String("tst-" + randomString(t)),
Account: &Account{ID: defaultAccountID},
})
if err != nil {
Expand Down Expand Up @@ -424,3 +424,16 @@ func createProviderConfigurationScalr(t *testing.T, client *Client, providerName
}
}
}

func assignTagsToWorkspace(t *testing.T, client *Client, workspace *Workspace, tags []*Tag) {
ctx := context.Background()
tagRels := make([]*TagRelation, len(tags))
for i, tag := range tags {
tagRels[i] = &TagRelation{ID: tag.ID}
}
err := client.WorkspaceTags.Add(ctx, workspace.ID, tagRels)

if err != nil {
t.Fatal(err)
}
}
45 changes: 10 additions & 35 deletions tag.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,8 @@ type Tags interface {
List(ctx context.Context, options TagListOptions) (*TagList, error)
// Create is used to create a new tag.
Create(ctx context.Context, options TagCreateOptions) (*Tag, error)
// Read a tag by its account ID and name.
Read(ctx context.Context, accountID, tagName string) (*Tag, error)
// ReadByID reads a tag by its ID.
ReadByID(ctx context.Context, tagID string) (*Tag, error)
// Read reads a tag by its ID.
Read(ctx context.Context, tagID string) (*Tag, error)
// Update existing tag by its ID.
Update(ctx context.Context, tagID string, options TagUpdateOptions) (*Tag, error)
// Delete deletes a tag by its ID.
Expand All @@ -45,12 +43,17 @@ type Tag struct {
Account *Account `jsonapi:"relation,account"`
}

type TagRelation struct {
ID string `jsonapi:"primary,tags"`
}

// TagListOptions represents the options for listing tags.
type TagListOptions struct {
ListOptions

Account *string `url:"filter[account],omitempty"`
Name *string `url:"query,omitempty"`
Name *string `url:"filter[name],omitempty"`
Query *string `url:"query,omitempty"`
}

// TagCreateOptions represents the options for creating a new tag.
Expand Down Expand Up @@ -87,36 +90,8 @@ func (s *tags) List(ctx context.Context, options TagListOptions) (*TagList, erro
return tl, nil
}

// Read tag by account ID and name.
func (s *tags) Read(ctx context.Context, accountID, tagName string) (*Tag, error) {
if !validStringID(&accountID) {
return nil, errors.New("invalid value for account")
}
if !validStringID(&tagName) {
return nil, errors.New("invalid value for tag")
}

options := TagListOptions{Account: &accountID, Name: &tagName}

req, err := s.client.newRequest("GET", "tags", &options)
if err != nil {
return nil, err
}

tl := &TagList{}
err = s.client.do(ctx, req, tl)
if err != nil {
return nil, err
}
if len(tl.Items) != 1 {
return nil, errors.New("invalid filters")
}

return tl.Items[0], nil
}

// ReadByID reads a tag by its ID.
func (s *tags) ReadByID(ctx context.Context, tagID string) (*Tag, error) {
// Read reads a tag by its ID.
func (s *tags) Read(ctx context.Context, tagID string) (*Tag, error) {
if !validStringID(&tagID) {
return nil, errors.New("invalid value for tag ID")
}
Expand Down
56 changes: 19 additions & 37 deletions tag_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,23 +21,23 @@ func TestTagsList(t *testing.T) {
t.Run("without options", func(t *testing.T) {
tagl, err := client.Tags.List(ctx, TagListOptions{})
require.NoError(t, err)
taglIDs := make([]string, len(tagl.Items))
for _, tag := range tagl.Items {
taglIDs = append(taglIDs, tag.ID)
assert.Equal(t, 2, tagl.TotalCount)

tagIDs := make([]string, len(tagl.Items))
for i, tag := range tagl.Items {
tagIDs[i] = tag.ID
}
assert.Contains(t, taglIDs, tagTest1.ID)
assert.Contains(t, taglIDs, tagTest2.ID)
assert.Contains(t, tagIDs, tagTest1.ID)
assert.Contains(t, tagIDs, tagTest2.ID)
})

t.Run("with options", func(t *testing.T) {
tagl, err := client.Tags.List(ctx, TagListOptions{Account: String(defaultAccountID)})
tagl, err := client.Tags.List(ctx,
TagListOptions{Account: String(defaultAccountID), Name: String(tagTest1.Name)},
)
require.NoError(t, err)
taglIDs := make([]string, len(tagl.Items))
for _, tag := range tagl.Items {
taglIDs = append(taglIDs, tag.ID)
}
assert.Contains(t, taglIDs, tagTest1.ID)
assert.Contains(t, taglIDs, tagTest2.ID)
assert.Equal(t, 1, tagl.TotalCount)
assert.Equal(t, tagTest1.ID, tagl.Items[0].ID)
})
}

Expand All @@ -47,15 +47,15 @@ func TestTagsCreate(t *testing.T) {

t.Run("with valid options", func(t *testing.T) {
options := TagCreateOptions{
Name: String("test-role-" + randomString(t)),
Name: String("tst-" + randomString(t)),
Account: &Account{ID: defaultAccountID},
}

tag, err := client.Tags.Create(ctx, options)
require.NoError(t, err)

// Get a refreshed view from the API.
refreshed, err := client.Tags.ReadByID(ctx, tag.ID)
refreshed, err := client.Tags.Read(ctx, tag.ID)
require.NoError(t, err)

for _, item := range []*Tag{
Expand Down Expand Up @@ -111,40 +111,22 @@ func TestTagsRead(t *testing.T) {
defer tagTestCleanup()

t.Run("by ID when the tag exists", func(t *testing.T) {
tag, err := client.Tags.ReadByID(ctx, tagTest.ID)
tag, err := client.Tags.Read(ctx, tagTest.ID)
require.NoError(t, err)
assert.Equal(t, tagTest.ID, tag.ID)
})

t.Run("by ID when the tag does not exist", func(t *testing.T) {
tag, err := client.Tags.ReadByID(ctx, "tag-nonexisting")
tag, err := client.Tags.Read(ctx, "tag-nonexisting")
assert.Nil(t, tag)
assert.Error(t, err)
})

t.Run("by ID without a valid tag ID", func(t *testing.T) {
tag, err := client.Tags.ReadByID(ctx, badIdentifier)
tag, err := client.Tags.Read(ctx, badIdentifier)
assert.Nil(t, tag)
assert.EqualError(t, err, "invalid value for tag ID")
})

t.Run("by name when the tag exists", func(t *testing.T) {
tag, err := client.Tags.Read(ctx, defaultAccountID, tagTest.Name)
require.NoError(t, err)
assert.Equal(t, tagTest.ID, tag.ID)
})

t.Run("by name when the tag does not exist", func(t *testing.T) {
tag, err := client.Tags.Read(ctx, defaultAccountID, "tag-nonexisting")
assert.Nil(t, tag)
assert.Error(t, err)
})

t.Run("by name without a valid account ID", func(t *testing.T) {
tag, err := client.Tags.Read(ctx, "acc-nonexisting", tagTest.Name)
assert.Nil(t, tag)
assert.Error(t, err)
})
}

func TestTagsUpdate(t *testing.T) {
Expand All @@ -163,7 +145,7 @@ func TestTagsUpdate(t *testing.T) {
require.NoError(t, err)

// Get a refreshed view from the API.
refreshed, err := client.Tags.ReadByID(ctx, tagTest.ID)
refreshed, err := client.Tags.Read(ctx, tagTest.ID)
require.NoError(t, err)

for _, item := range []*Tag{
Expand Down Expand Up @@ -193,7 +175,7 @@ func TestTagsDelete(t *testing.T) {
err := client.Tags.Delete(ctx, tagTest.ID)
require.NoError(t, err)

_, err = client.Tags.ReadByID(ctx, tagTest.ID)
_, err = client.Tags.Read(ctx, tagTest.ID)
assert.Equal(
t,
ResourceNotFoundError{
Expand Down
5 changes: 4 additions & 1 deletion workspace.go
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ type Workspace struct {
VcsProvider *VcsProvider `jsonapi:"relation,vcs-provider"`
AgentPool *AgentPool `jsonapi:"relation,agent-pool"`
ModuleVersion *ModuleVersion `jsonapi:"relation,module-version,omitempty"`
Tags []*Tag `jsonapi:"relation,tags,omitempty"`
Tags []*Tag `jsonapi:"relation,tags"`
}

// Hooks contains the custom hooks field.
Expand Down Expand Up @@ -211,6 +211,9 @@ type WorkspaceCreateOptions struct {

// Specifies the number of minutes run operation can be executed before termination.
RunOperationTimeout *int `jsonapi:"attr,run-operation-timeout"`

// Specifies tags assigned to the workspace
Tags []*Tag `jsonapi:"relation,tags,omitempty"`
}

// WorkspaceVCSRepoOptions represents the configuration options of a VCS integration.
Expand Down
66 changes: 18 additions & 48 deletions workspace_tags.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ package scalr

import (
"context"
"errors"
"fmt"
"net/url"
)
Expand All @@ -13,71 +12,42 @@ var _ WorkspaceTags = (*workspaceTag)(nil)
// WorkspaceTags describes all the workspace tags related methods that the
// Scalr API supports.
type WorkspaceTags interface {
Create(ctx context.Context, options WorkspaceTagsCreateOptions) error
Update(ctx context.Context, options WorkspaceTagsUpdateOptions) error
Add(ctx context.Context, wsID string, tags []*TagRelation) error
Replace(ctx context.Context, wsID string, tags []*TagRelation) error
Delete(ctx context.Context, wsID string, tags []*TagRelation) error
}

// workspaceTag implements WorkspaceTags.
type workspaceTag struct {
client *Client
}

// WorkspaceTag represents a single workspace tag relation.
type WorkspaceTag struct {
ID string `jsonapi:"primary,tags"`
}

// WorkspaceTagsCreateOptions represents options for adding tags to a workspace.
type WorkspaceTagsCreateOptions struct {
WorkspaceID string
WorkspaceTags []*WorkspaceTag
}

// WorkspaceTagsUpdateOptions represents options for updating tags in a workspace.
type WorkspaceTagsUpdateOptions struct {
WorkspaceID string
WorkspaceTags []*WorkspaceTag
}

func (o WorkspaceTagsCreateOptions) valid() error {
if !validStringID(&o.WorkspaceID) {
return errors.New("invalid value for workspace ID")
}
if o.WorkspaceTags == nil || len(o.WorkspaceTags) < 1 {
return errors.New("list of tags is required")
// Add tags to the workspace
func (s *workspaceTag) Add(ctx context.Context, wsID string, trs []*TagRelation) error {
u := fmt.Sprintf("workspaces/%s/relationships/tags", url.QueryEscape(wsID))
req, err := s.client.newRequest("POST", u, trs)
if err != nil {
return err
}
return nil
}

func (o WorkspaceTagsUpdateOptions) valid() error {
if !validStringID(&o.WorkspaceID) {
return errors.New("invalid value for workspace ID")
}
return nil
return s.client.do(ctx, req, nil)
}

// Create is used for adding tags to the workspace.
func (s *workspaceTag) Create(ctx context.Context, options WorkspaceTagsCreateOptions) error {
if err := options.valid(); err != nil {
return err
}
u := fmt.Sprintf("workspaces/%s/relationships/tags", url.QueryEscape(options.WorkspaceID))
req, err := s.client.newRequest("POST", u, options.WorkspaceTags)
// Replace workspace's tags
func (s *workspaceTag) Replace(ctx context.Context, wsID string, trs []*TagRelation) error {
u := fmt.Sprintf("workspaces/%s/relationships/tags", url.QueryEscape(wsID))
req, err := s.client.newRequest("PATCH", u, trs)
if err != nil {
return err
}

return s.client.do(ctx, req, nil)
}

// Update is used for tags replacement in the workspace.
func (s *workspaceTag) Update(ctx context.Context, options WorkspaceTagsUpdateOptions) error {
if err := options.valid(); err != nil {
return err
}

u := fmt.Sprintf("workspaces/%s/relationships/tags", url.QueryEscape(options.WorkspaceID))
req, err := s.client.newRequest("PATCH", u, options.WorkspaceTags)
// Delete workspace's tags
func (s *workspaceTag) Delete(ctx context.Context, wsID string, trs []*TagRelation) error {
u := fmt.Sprintf("workspaces/%s/relationships/tags", url.QueryEscape(wsID))
req, err := s.client.newRequest("DELETE", u, trs)
if err != nil {
return err
}
Expand Down

0 comments on commit 243a197

Please sign in to comment.