From cffa1a22ab0d0e243c6eecc536238a987f115033 Mon Sep 17 00:00:00 2001 From: Rexredinger Date: Mon, 7 Feb 2022 16:04:09 -0500 Subject: [PATCH 01/31] Add VariableSets --- helper_test.go | 81 ++++++++++++++ tfe.go | 2 + variable_set.go | 254 +++++++++++++++++++++++++++++++++++++++++++ variable_set_test.go | 227 ++++++++++++++++++++++++++++++++++++++ 4 files changed, 564 insertions(+) create mode 100644 variable_set.go create mode 100644 variable_set_test.go diff --git a/helper_test.go b/helper_test.go index 0b049c9ff..aad00b105 100644 --- a/helper_test.go +++ b/helper_test.go @@ -1011,6 +1011,7 @@ func createWorkspaceWithVCS(t *testing.T, client *Client, org *Organization, opt ctx := context.Background() w, err := client.Workspaces.Create(ctx, org.Name, options) if err != nil { + t.Fatal(err) } @@ -1077,6 +1078,86 @@ func createWorkspaceRunTask(t *testing.T, client *Client, workspace *Workspace, } } +func createVariableSet(t *testing.T, client *Client, org *Organization, options VariableSetCreateOptions) (*VariableSet, func()) { + var orgCleanup func() + + if org == nil { + org, orgCleanup = createOrganization(t, client) + } + + if options.Name == nil { + options.Name = String(randomString(t)) + } + + if options.Global == nil { + options.Global = Bool(false) + } + + ctx := context.Background() + vs, err := client.VariableSets.Create(ctx, org.Name, options) + if err != nil { + t.Fatal(err) + } + + return vs, func() { + if err := client.VariableSets.Delete(ctx, vs.ID); err != nil { + t.Errorf("Error destroying variable set! WARNING: Dangling resources\n"+ + "may exist! The full error is shown below.\n\n"+ + "VariableSet: %s\nError: %s", vs.Name, err) + } + + if orgCleanup != nil { + orgCleanup() + } + } +} + +func createVariableSetVariable(t *testing.T, client *Client, vs *VariableSet, options VariableSetVariableCreateOptions) (*VariableSetVariable, func()) { + var vsCleanup func() + + if vs == nil { + vs, vsCleanup = createVariableSet(t, client, nil, VariableSetCreateOptions{}) + } + + if options.Key == nil { + options.Key = String(randomString(t)) + } + + if options.Value == nil { + options.Value = String(randomString(t)) + } + + if options.Category == nil { + options.Category = Category(CategoryTerraform) + } + + if options.HCL == nil { + options.HCL = Bool(false) + } + + if options.Sensitive == nil { + options.Sensitive = Bool(false) + } + + ctx := context.Background() + v, err := client.VariableSetVariables.Create(ctx, vs.ID, options) + if err != nil { + t.Fatal(err) + } + + return v, func() { + if err := client.VariableSetVariables.Delete(ctx, vs.ID, v.ID); err != nil { + t.Errorf("Error destroying variable! WARNING: Dangling resources\n"+ + "may exist! The full error is shown below.\n\n"+ + "Variable: %s\nError: %s", v.Key, err) + } + + if vsCleanup != nil { + vsCleanup() + } + } +} + func genSha(t *testing.T, secret, data string) string { h := hmac.New(sha256.New, []byte(secret)) _, err := h.Write([]byte(data)) diff --git a/tfe.go b/tfe.go index c255258c1..7b3b8c09e 100644 --- a/tfe.go +++ b/tfe.go @@ -142,6 +142,7 @@ type Client struct { Users Users UserTokens UserTokens Variables Variables + VariableSets VariableSets Workspaces Workspaces WorkspaceRunTasks WorkspaceRunTasks @@ -282,6 +283,7 @@ func NewClient(cfg *Config) (*Client, error) { client.Users = &users{client: client} client.UserTokens = &userTokens{client: client} client.Variables = &variables{client: client} + client.VariableSets = &variableSets{client: client} client.Workspaces = &workspaces{client: client} client.WorkspaceRunTasks = &workspaceRunTasks{client: client} diff --git a/variable_set.go b/variable_set.go new file mode 100644 index 000000000..1f9c4fb59 --- /dev/null +++ b/variable_set.go @@ -0,0 +1,254 @@ +package tfe + +import ( + "context" + "errors" + "fmt" + "net/url" +) + +// Compile-time proof of interface implementation. +var _ VariableSets = (*variableSets)(nil) + +// VariableSets describes all the Variable Set related methods that the +// Terraform Enterprise API supports. +// +// TFE API docs: https://www.terraform.io/cloud-docs/api-docs/variable-sets +type VariableSets interface { + // List all the variable sets within an organization. + List(ctx context.Context, organization string, options VariableSetListOptions) (*VariableSetList, error) + + // Create is used to create a new variable set. + Create(ctx context.Context, organization string, options VariableSetCreateOptions) (*VariableSet, error) + + // Read a variable set by its ID. + Read(ctx context.Context, variableSetID string) (*VariableSet, error) + + // Update an existing variable set. + Update(ctx context.Context, variableSetID string, options VariableSetUpdateOptions) (*VariableSet, error) + + // Delete a variable set by ID. + Delete(ctx context.Context, variableSetID string) error + + // Assign a variable set to workspaces + Assign(ctx context.Context, variableSetID string, options VariableSetAssignOptions) (*VariableSet, error) +} + +type variableSets struct { + client *Client +} + +type VariableSetList struct { + *Pagination + Items []*VariableSet +} + +type VariableSet struct { + ID string `jsonapi:"primary,varsets"` + Name string `jsonapi:"attr,name"` + Description string `jsonapi:"attr,description"` + Global bool `jsonapi:"attr,global"` + + // Relations + Workspaces []*Workspace `jsonapi:"relation,workspaces,omitempty"` +} + +type VariableSetListOptions struct { + ListOptions +} + +func (o VariableSetListOptions) valid() error { + return nil +} + +// List all Variable Sets in the organization +func (s *variableSets) List(ctx context.Context, organization string, options VariableSetListOptions) (*VariableSetList, error) { + if !validStringID(&organization) { + return nil, ErrInvalidOrg + } + if err := options.valid(); err != nil { + return nil, err + } + + u := fmt.Sprintf("organizations/%s/varsets", url.QueryEscape(organization)) + req, err := s.client.newRequest("GET", u, &options) + if err != nil { + return nil, err + } + + vl := &VariableSetList{} + err = s.client.do(ctx, req, vl) + if err != nil { + return nil, err + } + + return vl, nil +} + +// VariableSetCreateOptions represents the options for creating a new variable set within in a organization. +type VariableSetCreateOptions struct { + // Type is a public field utilized by JSON:API to + // set the resource type via the field tag. + // It is not a user-defined value and does not need to be set. + // https://jsonapi.org/format/#crud-creating + Type string `jsonapi:"primary,vars"` + + // The name of the variable set. + // Affects variable precedence when there are conflicts between Variable Sets + // TODO: Add link to documentation + Name *string `jsonapi:"attr,name"` + + // A description to provide context for the variable set. + Description *string `jsonapi:"attr,description"` + + // If true the variable set is considered in all runs in the organization. + Global *bool `jsonapi:"attr,global"` +} + +func (o VariableSetCreateOptions) valid() error { + if !validString(o.Name) { + return ErrRequiredName + } + if o.Global == nil { + return errors.New("global flag is required") + } + return nil +} + +// Create is used to create a new variable set. +func (s *variableSets) Create(ctx context.Context, organization string, options VariableSetCreateOptions) (*VariableSet, error) { + if !validStringID(&organization) { + return nil, ErrInvalidOrg + } + if err := options.valid(); err != nil { + return nil, err + } + + u := fmt.Sprintf("organizations/%s/varsets", url.QueryEscape(organization)) + req, err := s.client.newRequest("POST", u, &options) + if err != nil { + return nil, err + } + + vl := &VariableSet{} + err = s.client.do(ctx, req, vl) + if err != nil { + return nil, err + } + + return vl, nil +} + +// Read is used to inspect a given variable set based on ID +func (s *variableSets) Read(ctx context.Context, variableSetID string) (*VariableSet, error) { + if !validStringID(&variableSetID) { + return nil, errors.New("invalid variable set ID") + } + + u := fmt.Sprintf("varsets/%s?include=workspaces", url.QueryEscape(variableSetID)) + req, err := s.client.newRequest("GET", u, nil) + if err != nil { + return nil, err + } + + vs := &VariableSet{} + err = s.client.do(ctx, req, vs) + if err != nil { + return nil, err + } + + return vs, err +} + +// VariableSetUpdateOptions represents the options for updating a variable set. +type VariableSetUpdateOptions struct { + // Type is a public field utilized by JSON:API to + // set the resource type via the field tag. + // It is not a user-defined value and does not need to be set. + // https://jsonapi.org/format/#crud-creating + Type string `jsonapi:"primary,vars"` + + // The name of the variable set. + // Affects variable precedence when there are conflicts between Variable Sets + // TODO: Add link to documentation + Name *string `jsonapi:"attr,name"` + + // A description to provide context for the variable set. + Description *string `jsonapi:"attr,description"` + + // If true the variable set is considered in all runs in the organization. + Global *bool `jsonapi:"attr,global"` +} + +func (s *variableSets) Update(ctx context.Context, variableSetID string, options VariableSetUpdateOptions) (*VariableSet, error) { + if !validStringID(&variableSetID) { + return nil, errors.New("invalid value for variable set ID") + } + + u := fmt.Sprintf("varsets/%s?include=workspaces", url.QueryEscape(variableSetID)) + req, err := s.client.newRequest("PATCH", u, &options) + if err != nil { + return nil, err + } + + v := &VariableSet{} + err = s.client.do(ctx, req, v) + if err != nil { + return nil, err + } + + return v, nil +} + +// Delete a variable set by its ID. +func (s *variableSets) Delete(ctx context.Context, variableSetID string) error { + if !validStringID(&variableSetID) { + return errors.New("invalid value for variable set ID") + } + + u := fmt.Sprintf("varsets/%s", url.QueryEscape(variableSetID)) + req, err := s.client.newRequest("DELETE", u, nil) + if err != nil { + return err + } + + return s.client.do(ctx, req, nil) +} + +// VariableSetAssignOptions represents a subset of update options specifically for assigning variable sets to workspaces +type VariableSetAssignOptions struct { + // Type is a public field utilized by JSON:API to + // set the resource type via the field tag. + // It is not a user-defined value and does not need to be set. + // https://jsonapi.org/format/#crud-creating + Type string `jsonapi:"primary,vars"` + + // Used to set the variable set from Global to not Global if necessary + Global *bool `jsonapi:"attr,global"` + + // The workspaces to be assigned to. An empty set means remove all assignments + Workspaces []*Workspace `jsonapi:"relation,workspaces"` +} + +// Use Update to assign a variable set to workspaces +func (s *variableSets) Assign(ctx context.Context, variableSetID string, options VariableSetAssignOptions) (*VariableSet, error) { + if options.Workspaces == nil { + return nil, errors.New("No workspaces list provided") + } + + options.Global = Bool(false) + + u := fmt.Sprintf("varsets/%s?include=workspaces", url.QueryEscape(variableSetID)) + req, err := s.client.newRequest("PATCH", u, &options) + if err != nil { + return nil, err + } + + v := &VariableSet{} + err = s.client.do(ctx, req, v) + if err != nil { + return nil, err + } + + return v, nil +} diff --git a/variable_set_test.go b/variable_set_test.go new file mode 100644 index 000000000..b845af682 --- /dev/null +++ b/variable_set_test.go @@ -0,0 +1,227 @@ +package tfe + +import ( + "context" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestVariableSetsList(t *testing.T) { + client := testClient(t) + ctx := context.Background() + + orgTest, orgTestCleanup := createOrganization(t, client) + defer orgTestCleanup() + + //wTest, wTestCleanup := createWorkspace(t, client, orgTest) + //defer wTestCleanup() + + vsTest1, vsTestCleanup1 := createVariableSet(t, client, orgTest, VariableSetCreateOptions{}) + defer vsTestCleanup1() + vsTest2, vsTestCleanup2 := createVariableSet(t, client, orgTest, VariableSetCreateOptions{}) + defer vsTestCleanup2() + + t.Run("without list options", func(t *testing.T) { + vsl, err := client.VariableSets.List(ctx, orgTest.Name, VariableSetListOptions{}) + require.NoError(t, err) + assert.Contains(t, vsl.Items, vsTest1) + assert.Contains(t, vsl.Items, vsTest2) + + t.Skip("paging not supported yet in API") + assert.Equal(t, 1, vsl.CurrentPage) + assert.Equal(t, 2, vsl.TotalCount) + }) + + t.Run("with list options", func(t *testing.T) { + t.Skip("paging not supported yet in API") + // Request a page number which is out of range. The result should + // be successful, but return no results if the paging options are + // properly passed along. + vsl, err := client.VariableSets.List(ctx, orgTest.Name, VariableSetListOptions{ + ListOptions: ListOptions{ + PageNumber: 999, + PageSize: 100, + }, + }) + require.NoError(t, err) + assert.Empty(t, vsl.Items) + assert.Equal(t, 999, vsl.CurrentPage) + assert.Equal(t, 2, vsl.TotalCount) + }) + + t.Run("when Organization name is invalid ID", func(t *testing.T) { + vsl, err := client.VariableSets.List(ctx, badIdentifier, VariableSetListOptions{}) + assert.Nil(t, vsl) + assert.EqualError(t, err, ErrInvalidOrg.Error()) + }) +} + +func TestVariableSetsCreate(t *testing.T) { + client := testClient(t) + ctx := context.Background() + + orgTest, orgTestCleanup := createOrganization(t, client) + defer orgTestCleanup() + + t.Run("with valid options", func(t *testing.T) { + options := VariableSetCreateOptions{ + Name: String("varset"), + Description: String("a variable set"), + Global: Bool(false), + } + + vs, err := client.VariableSets.Create(ctx, orgTest.Name, options) + require.NoError(t, err) + + //Get refreshed view from the API + refreshed, err := client.VariableSets.Read(ctx, vs.ID) + require.NoError(t, err) + + for _, item := range []*VariableSet{ + vs, + refreshed, + } { + assert.NotEmpty(t, item.ID) + assert.Equal(t, *options.Name, item.Name) + assert.Equal(t, *options.Description, item.Description) + assert.Equal(t, *options.Global, item.Global) + } + }) + + t.Run("when options is missing name", func(t *testing.T) { + vs, err := client.VariableSets.Create(ctx, "foo", VariableSetCreateOptions{ + Global: Bool(true), + }) + assert.Nil(t, vs) + assert.EqualError(t, err, ErrRequiredName.Error()) + }) + + t.Run("when options is missing global flag", func(t *testing.T) { + vs, err := client.VariableSets.Create(ctx, "foo", VariableSetCreateOptions{ + Name: String("foo"), + }) + assert.Nil(t, vs) + assert.EqualError(t, err, "global flag is required") + }) +} + +func TestVariableSetsRead(t *testing.T) { + client := testClient(t) + ctx := context.Background() + + orgTest, orgTestCleanup := createOrganization(t, client) + defer orgTestCleanup() + + vsTest, vsTestCleanup := createVariableSet(t, client, orgTest, VariableSetCreateOptions{}) + defer vsTestCleanup() + + t.Run("when the variable set exists", func(t *testing.T) { + vs, err := client.VariableSets.Read(ctx, vsTest.ID) + require.NoError(t, err) + assert.Equal(t, vsTest, vs) + }) + + t.Run("when variable set does not exist", func(t *testing.T) { + vs, err := client.VariableSets.Read(ctx, "nonexisting") + assert.Nil(t, vs) + assert.Error(t, err) + }) +} + +func TestVariableSetsUpdate(t *testing.T) { + client := testClient(t) + ctx := context.Background() + + orgTest, orgTestCleanup := createOrganization(t, client) + defer orgTestCleanup() + + vsTest, _ := createVariableSet(t, client, orgTest, VariableSetCreateOptions{ + Name: String("OrinigalName"), + Description: String("Original Description"), + Global: Bool(false), + }) + + t.Run("when updating a subset of values", func(t *testing.T) { + options := VariableSetUpdateOptions{ + Name: String("UpdatedName"), + Description: String("Updated Description"), + Global: Bool(true), + } + + vsAfter, err := client.VariableSets.Update(ctx, vsTest.ID, options) + require.NoError(t, err) + + assert.Equal(t, *options.Name, vsAfter.Name) + assert.Equal(t, *options.Description, vsAfter.Description) + assert.Equal(t, *options.Global, vsAfter.Global) + }) + + t.Run("when options has an invalid variable set ID", func(t *testing.T) { + vsAfter, err := client.VariableSets.Update(ctx, badIdentifier, VariableSetUpdateOptions{ + Name: String("UpdatedName"), + Description: String("Updated Description"), + Global: Bool(true), + }) + assert.Nil(t, vsAfter) + assert.EqualError(t, err, "invalid value for variable set ID") + }) +} + +func TestVariableSetsDelete(t *testing.T) { + client := testClient(t) + ctx := context.Background() + + orgTest, orgTestCleanup := createOrganization(t, client) + defer orgTestCleanup() + + vsTest, _ := createVariableSet(t, client, orgTest, VariableSetCreateOptions{}) + + t.Run("with valid ID", func(t *testing.T) { + err := client.VariableSets.Delete(ctx, vsTest.ID) + require.NoError(t, err) + + // Try loading the variable set - it should fail. + _, err = client.VariableSets.Read(ctx, vsTest.ID) + assert.Equal(t, ErrResourceNotFound, err) + }) + + t.Run("when ID is invlaid", func(t *testing.T) { + err := client.VariableSets.Delete(ctx, badIdentifier) + assert.EqualError(t, err, "invalid value for variable set ID") + }) +} + +func TestVariableSetsAssign(t *testing.T) { + client := testClient(t) + ctx := context.Background() + + orgTest, orgTestCleanup := createOrganization(t, client) + defer orgTestCleanup() + + vsTest, _ := createVariableSet(t, client, orgTest, VariableSetCreateOptions{}) + + wTest, _ := createWorkspace(t, client, orgTest) + + t.Run("with valid workspaces", func(t *testing.T) { + options := VariableSetAssignOptions{ + Workspaces: []*Workspace{wTest}, + } + + vsAfter, err := client.VariableSets.Assign(ctx, vsTest.ID, options) + require.NoError(t, err) + + assert.Equal(t, len(options.Workspaces), len(vsAfter.Workspaces)) + assert.Equal(t, options.Workspaces[0].ID, vsAfter.Workspaces[0].ID) + + options = VariableSetAssignOptions{ + Workspaces: []*Workspace{}, + } + + vsAfter, err = client.VariableSets.Assign(ctx, vsTest.ID, options) + require.NoError(t, err) + + assert.Equal(t, len(options.Workspaces), len(vsAfter.Workspaces)) + }) +} From be30a5e0d6c3cfcc0f91d28cc39fe8d468c4e867 Mon Sep 17 00:00:00 2001 From: Rexredinger Date: Mon, 7 Feb 2022 16:05:02 -0500 Subject: [PATCH 02/31] Add VariableSetVariables, needs more tests --- tfe.go | 2 + variable_set.go | 3 +- variable_set_variable.go | 206 ++++++++++++++++++++++++++++++++++ variable_set_variable_test.go | 43 +++++++ 4 files changed, 253 insertions(+), 1 deletion(-) create mode 100644 variable_set_variable.go create mode 100644 variable_set_variable_test.go diff --git a/tfe.go b/tfe.go index 7b3b8c09e..080ac0a50 100644 --- a/tfe.go +++ b/tfe.go @@ -143,6 +143,7 @@ type Client struct { UserTokens UserTokens Variables Variables VariableSets VariableSets + VariableSetVariables VariableSetVariables Workspaces Workspaces WorkspaceRunTasks WorkspaceRunTasks @@ -284,6 +285,7 @@ func NewClient(cfg *Config) (*Client, error) { client.UserTokens = &userTokens{client: client} client.Variables = &variables{client: client} client.VariableSets = &variableSets{client: client} + client.VariableSetVariables = &variableSetVariables{client: client} client.Workspaces = &workspaces{client: client} client.WorkspaceRunTasks = &workspaceRunTasks{client: client} diff --git a/variable_set.go b/variable_set.go index 1f9c4fb59..f307aee0f 100644 --- a/variable_set.go +++ b/variable_set.go @@ -50,7 +50,8 @@ type VariableSet struct { Global bool `jsonapi:"attr,global"` // Relations - Workspaces []*Workspace `jsonapi:"relation,workspaces,omitempty"` + Workspaces []*Workspace `jsonapi:"relation,workspaces,omitempty"` + Variables []*VariableSetVariable `jsonapi:"relation,vars,omitempty"` } type VariableSetListOptions struct { diff --git a/variable_set_variable.go b/variable_set_variable.go new file mode 100644 index 000000000..1ed0a3097 --- /dev/null +++ b/variable_set_variable.go @@ -0,0 +1,206 @@ +package tfe + +import ( + "context" + "errors" + "fmt" + "net/url" +) + +// Compile-time proof of interface implementation. +var _ VariableSetVariables = (*variableSetVariables)(nil) + +// VariableSetVariables describes all variable variable related methods within the scope of +// Variable Sets that the Terraform Enterprise API supports +// +// TFE API docs: https://www.terraform.io/cloud-docs/api-docs/variable-sets#variable-relationships +type VariableSetVariables interface { + // List all variables in the variable set. + List(ctx context.Context, variableSetID string, options VariableSetVariableListOptions) (*VariableSetVariableList, error) + + // Create is used to create a new variable within a given variable set + Create(ctx context.Context, variableSetID string, options VariableSetVariableCreateOptions) (*VariableSetVariable, error) + + // Update valuse of an existing variable + Update(ctx context.Context, variableSetID string, variableID string, options VariableSetVariableUpdateOptions) (*VariableSetVariable, error) + + // Delete a variable by its ID + Delete(ctx context.Context, variableSetID string, variableID string) error +} + +type variableSetVariables struct { + client *Client +} + +type VariableSetVariableList struct { + *Pagination + Items []*VariableSetVariable +} + +type VariableSetVariable struct { + ID string `jsonapi:"primary,vars"` + Key string `jsonapi:"attr,key"` + Value string `jsonapi:"attr,value"` + Category CategoryType `jsonapi:"attr,category"` + HCL string `jsonapi:"attr,hcl"` + Sensitive bool `jsonapi:"attr,sensitive"` + + // Relations + VariableSet *VariableSet `jsonapi:"relation,configurable"` +} + +type VariableSetVariableListOptions struct { + ListOptions +} + +func (o VariableSetVariableListOptions) valid() error { + return nil +} + +// List all variables associated with the given variable set. +func (s *variableSetVariables) List(ctx context.Context, variableSetID string, options VariableSetVariableListOptions) (*VariableSetVariableList, error) { + if !validStringID(&variableSetID) { + return nil, errors.New("invalid value for variable set ID") + } + if err := options.valid(); err != nil { + return nil, err + } + + u := fmt.Sprintf("varsets/%s/relationships/vars", variableSetID) + req, err := s.client.newRequest("GET", u, &options) + if err != nil { + return nil, err + } + + vl := &VariableSetVariableList{} + err = s.client.do(ctx, req, vl) + if err != nil { + return nil, err + } + + return vl, nil +} + +// VariableSetVariableCreatOptions represents the options for creating a new variable within a variable set +type VariableSetVariableCreateOptions struct { + // Type is a public field utilized by JSON:API to + // set the resource type via the field tag. + // It is not a user-defined value and does not need to be set. + // https://jsonapi.org/format/#crud-creating + Type string `jsonapi:"primary,vars"` + + // The name of the variable. + Key *string `jsonapi:"attr,key"` + + // The value of the variable. + Value *string `jsonapi:"attr,value,omitempty"` + + // Whether this is a Terraform or environment variable. + Category *CategoryType `jsonapi:"attr,category"` + + // Whether to evaluate the value of the variable as a string of HCL code. + HCL *bool `jsonapi:"attr,hcl,omitempty"` + + // Whether the value is sensitive. + Sensitive *bool `jsonapi:"attr,sensitive,omitempty"` +} + +func (o VariableSetVariableCreateOptions) valid() error { + if !validString(o.Key) { + return errors.New("key is required") + } + if o.Category == nil { + return errors.New("category is required") + } + return nil +} + +// Create is used to create a new variable. +func (s *variableSetVariables) Create(ctx context.Context, variableSetID string, options VariableSetVariableCreateOptions) (*VariableSetVariable, error) { + if !validStringID(&variableSetID) { + return nil, errors.New("invalid value for variable set ID") + } + if err := options.valid(); err != nil { + return nil, err + } + + u := fmt.Sprintf("varsets/%s/relationships/vars", url.QueryEscape(variableSetID)) + req, err := s.client.newRequest("POST", u, &options) + if err != nil { + return nil, err + } + + v := &VariableSetVariable{} + err = s.client.do(ctx, req, v) + if err != nil { + return nil, err + } + + return v, nil +} + +// VariableSetVariableUpdateOptions represents the options for updating a variable. +type VariableSetVariableUpdateOptions struct { + // Type is a public field utilized by JSON:API to + // set the resource type via the field tag. + // It is not a user-defined value and does not need to be set. + // https://jsonapi.org/format/#crud-creating + Type string `jsonapi:"primary,vars"` + + // The name of the variable. + Key *string `jsonapi:"attr,key,omitempty"` + + // The value of the variable. + Value *string `jsonapi:"attr,value,omitempty"` + + // The description of the variable. + Description *string `jsonapi:"attr,description,omitempty"` + + // Whether to evaluate the value of the variable as a string of HCL code. + HCL *bool `jsonapi:"attr,hcl,omitempty"` + + // Whether the value is sensitive. + Sensitive *bool `jsonapi:"attr,sensitive,omitempty"` +} + +// Update values of an existing variable. +func (s *variableSetVariables) Update(ctx context.Context, variableSetID string, variableID string, options VariableSetVariableUpdateOptions) (*VariableSetVariable, error) { + if !validStringID(&variableSetID) { + return nil, errors.New("invalid value for variable set ID") + } + if !validStringID(&variableID) { + return nil, errors.New("invalid value for variable ID") + } + + u := fmt.Sprintf("varsets/%s/vars/%s", url.QueryEscape(variableSetID), url.QueryEscape(variableID)) + req, err := s.client.newRequest("PATCH", u, &options) + if err != nil { + return nil, err + } + + v := &VariableSetVariable{} + err = s.client.do(ctx, req, v) + if err != nil { + return nil, err + } + + return v, nil +} + +// Delete a variable by its ID. +func (s *variableSetVariables) Delete(ctx context.Context, variableSetID string, variableID string) error { + if !validStringID(&variableSetID) { + return errors.New("invalid value for variable set ID") + } + if !validStringID(&variableID) { + return errors.New("invalid value for variable ID") + } + + u := fmt.Sprintf("varsets/%s/relationships/vars/%s", url.QueryEscape(variableSetID), url.QueryEscape(variableID)) + req, err := s.client.newRequest("DELETE", u, nil) + if err != nil { + return err + } + + return s.client.do(ctx, req, nil) +} diff --git a/variable_set_variable_test.go b/variable_set_variable_test.go new file mode 100644 index 000000000..6b79af0a4 --- /dev/null +++ b/variable_set_variable_test.go @@ -0,0 +1,43 @@ +package tfe + +import ( + "context" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "testing" +) + +func TestVariableSetVariablesList(t *testing.T) { + client := testClient(t) + ctx := context.Background() + + orgTest, orgTestCleanup := createOrganization(t, client) + defer orgTestCleanup() + + vsTest, vsTestCleanup := createVariableSet(t, client, orgTest, VariableSetCreateOptions{}) + defer vsTestCleanup() + + vTest1, vTestCleanup1 := createVariableSetVariable(t, client, vsTest, VariableSetVariableCreateOptions{ + Key: String("vTest1"), + Value: String("vTest1"), + Category: Category(CategoryTerraform), + }) + defer vTestCleanup1() + vTest2, vTestCleanup2 := createVariableSetVariable(t, client, vsTest, VariableSetVariableCreateOptions{ + Key: String("vTest2"), + Value: String("vTest2"), + Category: Category(CategoryTerraform), + }) + defer vTestCleanup2() + + t.Run("without list options", func(t *testing.T) { + vl, err := client.VariableSetVariables.List(ctx, vsTest.ID, VariableSetVariableListOptions{}) + require.NoError(t, err) + assert.Contains(t, vl.Items, vTest1) + assert.Contains(t, vl.Items, vTest2) + + t.Skip("paging not supported yet in API") + assert.Equal(t, 1, vl.CurrentPage) + assert.Equal(t, 2, vl.TotalCount) + }) +} From 3b76d5b83d0a51b6273ce23112157c3367e9015a Mon Sep 17 00:00:00 2001 From: Rexredinger Date: Tue, 8 Feb 2022 14:24:09 -0500 Subject: [PATCH 03/31] VariableSetVariable tests (not working yet) --- go.sum | 15 ++ helper_test.go | 8 ++ variable_set_variable.go | 22 ++- variable_set_variable_test.go | 257 ++++++++++++++++++++++++++++++++++ 4 files changed, 296 insertions(+), 6 deletions(-) diff --git a/go.sum b/go.sum index 2918378f2..d00699182 100644 --- a/go.sum +++ b/go.sum @@ -1,6 +1,7 @@ github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +<<<<<<< HEAD github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc= github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs= github.com/google/go-cmp v0.5.2 h1:X2ev0eStA3AbceY54o37/0PQ/UWqKEiiO2dKL5OPaFM= @@ -20,6 +21,20 @@ github.com/hashicorp/go-uuid v1.0.2 h1:cfejS+Tpcp13yd5nYHWDI6qVCny6wyX2Mt5SGur2I github.com/hashicorp/go-uuid v1.0.2/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= github.com/hashicorp/jsonapi v0.0.0-20210826224640-ee7dae0fb22d h1:9ARUJJ1VVynB176G1HCwleORqCaXm/Vx0uUi0dL26I0= github.com/hashicorp/jsonapi v0.0.0-20210826224640-ee7dae0fb22d/go.mod h1:Yog5+CPEM3c99L1CL2CFCYoSzgWm5vTU58idbRUaLik= +github.com/google/go-querystring v1.0.0 h1:Xkwi/a1rcvNg1PPYe5vI8GbeBY/jrVuDX5ASuANWTrk= +github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= +github.com/hashicorp/go-cleanhttp v0.5.0 h1:wvCrVc9TjDls6+YGAF2hAifE1E5U1+b4tH6KdvN3Gig= +github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= +github.com/hashicorp/go-retryablehttp v0.5.2 h1:AoISa4P4IsW0/m4T6St8Yw38gTl5GtBAgfkhYh1xAz4= +github.com/hashicorp/go-retryablehttp v0.5.2/go.mod h1:9B5zBasrRhHXnJnui7y6sL7es7NDiJgTc6Er0maI1Xs= +github.com/hashicorp/go-slug v0.4.1 h1:/jAo8dNuLgSImoLXaX7Od7QB4TfYCVPam+OpAt5bZqc= +github.com/hashicorp/go-slug v0.4.1/go.mod h1:I5tq5Lv0E2xcNXNkmx7BSfzi1PsJ2cNjs3cC3LwyhK8= +github.com/hashicorp/go-slug v0.7.0 h1:8HIi6oreWPtnhpYd8lIGQBgp4rXzDWQTOhfILZm+nok= +github.com/hashicorp/go-slug v0.7.0/go.mod h1:Ib+IWBYfEfJGI1ZyXMGNbu2BU+aa3Dzu41RKLH301v4= +github.com/hashicorp/go-uuid v1.0.1 h1:fv1ep09latC32wFoVwnqcnKJGnMSdBanPczbHAYm1BE= +github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hashicorp/jsonapi v0.0.0-20210518035559-1e50d74c8db3 h1:mzwkutymYIXR5oQT9YnfbLuuw7LZmksiHKRPUTN5ijo= +github.com/hashicorp/jsonapi v0.0.0-20210518035559-1e50d74c8db3/go.mod h1:Yog5+CPEM3c99L1CL2CFCYoSzgWm5vTU58idbRUaLik= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= diff --git a/helper_test.go b/helper_test.go index aad00b105..f7947c84b 100644 --- a/helper_test.go +++ b/helper_test.go @@ -1127,6 +1127,10 @@ func createVariableSetVariable(t *testing.T, client *Client, vs *VariableSet, op options.Value = String(randomString(t)) } + if options.Description == nil { + options.Description = String("") + } + if options.Category == nil { options.Category = Category(CategoryTerraform) } @@ -1139,6 +1143,10 @@ func createVariableSetVariable(t *testing.T, client *Client, vs *VariableSet, op options.Sensitive = Bool(false) } + out := bytes.NewBuffer(nil) + jsonstuff := jsonapi.MarshalOnePayloadEmbedded(out, options) + fmt.Printf("%+v\n", jsonstuff) + ctx := context.Background() v, err := client.VariableSetVariables.Create(ctx, vs.ID, options) if err != nil { diff --git a/variable_set_variable.go b/variable_set_variable.go index 1ed0a3097..a4b4e87a9 100644 --- a/variable_set_variable.go +++ b/variable_set_variable.go @@ -38,12 +38,13 @@ type VariableSetVariableList struct { } type VariableSetVariable struct { - ID string `jsonapi:"primary,vars"` - Key string `jsonapi:"attr,key"` - Value string `jsonapi:"attr,value"` - Category CategoryType `jsonapi:"attr,category"` - HCL string `jsonapi:"attr,hcl"` - Sensitive bool `jsonapi:"attr,sensitive"` + ID string `jsonapi:"primary,vars"` + Key string `jsonapi:"attr,key"` + Value string `jsonapi:"attr,value"` + Description string `jsonapi:"attr,description"` + Category CategoryType `jsonapi:"attr,category"` + HCL string `jsonapi:"attr,hcl"` + Sensitive bool `jsonapi:"attr,sensitive"` // Relations VariableSet *VariableSet `jsonapi:"relation,configurable"` @@ -95,6 +96,9 @@ type VariableSetVariableCreateOptions struct { // The value of the variable. Value *string `jsonapi:"attr,value,omitempty"` + // The description of the variable. + Description *string `jsonapi:"attr,description,omitempty"` + // Whether this is a Terraform or environment variable. Category *CategoryType `jsonapi:"attr,category"` @@ -124,6 +128,12 @@ func (s *variableSetVariables) Create(ctx context.Context, variableSetID string, return nil, err } + /* + out := bytes.NewBuffer(nil) + jsonstuff := jsonapi.MarshalOnePayloadEmbedded(out, i&options) + fmt.Printf("%+v\n", jsonstuff) + */ + u := fmt.Sprintf("varsets/%s/relationships/vars", url.QueryEscape(variableSetID)) req, err := s.client.newRequest("POST", u, &options) if err != nil { diff --git a/variable_set_variable_test.go b/variable_set_variable_test.go index 6b79af0a4..111a1711e 100644 --- a/variable_set_variable_test.go +++ b/variable_set_variable_test.go @@ -40,4 +40,261 @@ func TestVariableSetVariablesList(t *testing.T) { assert.Equal(t, 1, vl.CurrentPage) assert.Equal(t, 2, vl.TotalCount) }) + + t.Run("with list options", func(t *testing.T) { + t.Skip("paging not supported yet in API") + // Request a page number which is out of range. The result should + // be successful, but return no results if the paging options are + // properly passed along. + vl, err := client.VariableSetVariables.List(ctx, vsTest.ID, VariableSetVariableListOptions{ + ListOptions: ListOptions{ + PageNumber: 999, + PageSize: 100, + }, + }) + require.NoError(t, err) + assert.Empty(t, vl.Items) + assert.Equal(t, 999, vl.CurrentPage) + assert.Equal(t, 2, vl.TotalCount) + }) + + t.Run("when variable set ID is invalid ID", func(t *testing.T) { + vl, err := client.VariableSetVariables.List(ctx, badIdentifier, VariableSetVariableListOptions{}) + assert.Nil(t, vl) + assert.EqualError(t, err, "invalid value for variable set ID") + }) +} + +func TestVariableSetVariablesCreate(t *testing.T) { + client := testClient(t) + ctx := context.Background() + + orgTest, orgTestCleanup := createOrganization(t, client) + defer orgTestCleanup() + + vsTest, vsTestCleanup := createVariableSet(t, client, orgTest, VariableSetCreateOptions{}) + defer vsTestCleanup() + + t.Run("with valid options", func(t *testing.T) { + options := VariableSetVariableCreateOptions{ + Key: String(randomString(t)), + Value: String(randomString(t)), + Category: Category(CategoryTerraform), + Description: String(randomString(t)), + HCL: Bool(false), + Sensitive: Bool(false), + } + + v, err := client.VariableSetVariables.Create(ctx, vsTest.ID, options) + require.NoError(t, err) + + assert.NotEmpty(t, v.ID) + assert.Equal(t, *options.Key, v.Key) + assert.Equal(t, *options.Value, v.Value) + assert.Equal(t, *options.Description, v.Description) + assert.Equal(t, *options.Category, v.Category) + }) + + t.Run("when options has an empty string value", func(t *testing.T) { + options := VariableSetVariableCreateOptions{ + Key: String(randomString(t)), + Value: String(""), + Description: String(randomString(t)), + Category: Category(CategoryTerraform), + } + + v, err := client.VariableSetVariables.Create(ctx, vsTest.ID, options) + require.NoError(t, err) + + assert.NotEmpty(t, v.ID) + assert.Equal(t, *options.Key, v.Key) + assert.Equal(t, *options.Value, v.Value) + assert.Equal(t, *options.Description, v.Description) + assert.Equal(t, *options.Category, v.Category) + }) + + t.Run("when options has an empty string description", func(t *testing.T) { + options := VariableSetVariableCreateOptions{ + Key: String(randomString(t)), + Value: String(randomString(t)), + Description: String(""), + Category: Category(CategoryTerraform), + } + + v, err := client.VariableSetVariables.Create(ctx, vsTest.ID, options) + require.NoError(t, err) + + assert.NotEmpty(t, v.ID) + assert.Equal(t, *options.Key, v.Key) + assert.Equal(t, *options.Value, v.Value) + assert.Equal(t, *options.Description, v.Description) + assert.Equal(t, *options.Category, v.Category) + }) + + t.Run("when options has a too-long description", func(t *testing.T) { + options := VariableSetVariableCreateOptions{ + Key: String(randomString(t)), + Value: String(randomString(t)), + Description: String("tortor aliquam nulla facilisi cras fermentum odio eu feugiat pretium nibh ipsum consequat nisl vel pretium lectus quam id leo in vitae turpis massa sed elementum tempus egestas sed sed risus pretium quam vulputate dignissim suspendisse in est ante in nibh mauris cursus mattis molestie a iaculis at erat pellentesque adipiscing commodo elit at imperdiet dui accumsan sit amet nulla facilisi morbi tempus iaculis urna id volutpat lacus laoreet non curabitur gravida arcu ac tortor dignissim convallis aenean et tortor"), + Category: Category(CategoryTerraform), + } + + _, err := client.VariableSetVariables.Create(ctx, vsTest.ID, options) + assert.Error(t, err) + }) + + t.Run("when options is missing value", func(t *testing.T) { + options := VariableSetVariableCreateOptions{ + Key: String(randomString(t)), + Category: Category(CategoryTerraform), + } + + v, err := client.VariableSetVariables.Create(ctx, vsTest.ID, options) + require.NoError(t, err) + + assert.NotEmpty(t, v.ID) + assert.Equal(t, *options.Key, v.Key) + assert.Equal(t, "", v.Value) + assert.Equal(t, *options.Category, v.Category) + }) + + t.Run("when options is missing key", func(t *testing.T) { + options := VariableSetVariableCreateOptions{ + Value: String(randomString(t)), + Category: Category(CategoryTerraform), + } + + _, err := client.VariableSetVariables.Create(ctx, vsTest.ID, options) + assert.EqualError(t, err, "key is required") + }) + + t.Run("when options has an empty key", func(t *testing.T) { + options := VariableSetVariableCreateOptions{ + Key: String(""), + Value: String(randomString(t)), + Category: Category(CategoryTerraform), + } + + _, err := client.VariableSetVariables.Create(ctx, vsTest.ID, options) + assert.EqualError(t, err, "key is required") + }) + + t.Run("when options is missing category", func(t *testing.T) { + options := VariableSetVariableCreateOptions{ + Key: String(randomString(t)), + Value: String(randomString(t)), + } + + _, err := client.VariableSetVariables.Create(ctx, vsTest.ID, options) + assert.EqualError(t, err, "category is required") + }) + + t.Run("when workspace ID is invalid", func(t *testing.T) { + options := VariableSetVariableCreateOptions{ + Key: String(randomString(t)), + Value: String(randomString(t)), + Category: Category(CategoryTerraform), + } + + _, err := client.VariableSetVariables.Create(ctx, badIdentifier, options) + assert.EqualError(t, err, "invalid value for variable set ID") + }) +} + +func TestVariableSetVariablesUpdate(t *testing.T) { + client := testClient(t) + ctx := context.Background() + + vTest, vTestCleanup := createVariableSetVariable(t, client, nil, VariableSetVariableCreateOptions{}) + defer vTestCleanup() + + t.Run("with valid options", func(t *testing.T) { + options := VariableSetVariableUpdateOptions{ + Key: String("newname"), + Value: String("newvalue"), + HCL: Bool(true), + } + + v, err := client.VariableSetVariables.Update(ctx, vTest.VariableSet.ID, vTest.ID, options) + require.NoError(t, err) + + assert.Equal(t, *options.Key, v.Key) + assert.Equal(t, *options.HCL, v.HCL) + assert.Equal(t, *options.Value, v.Value) + }) + + t.Run("when updating a subset of values", func(t *testing.T) { + options := VariableSetVariableUpdateOptions{ + Key: String("someothername"), + HCL: Bool(false), + } + + v, err := client.VariableSetVariables.Update(ctx, vTest.VariableSet.ID, vTest.ID, options) + require.NoError(t, err) + + assert.Equal(t, *options.Key, v.Key) + assert.Equal(t, *options.HCL, v.HCL) + }) + + t.Run("with sensitive set", func(t *testing.T) { + options := VariableSetVariableUpdateOptions{ + Sensitive: Bool(true), + } + + v, err := client.VariableSetVariables.Update(ctx, vTest.VariableSet.ID, vTest.ID, options) + require.NoError(t, err) + + assert.Equal(t, *options.Sensitive, v.Sensitive) + assert.Empty(t, v.Value) // Because its now sensitive + }) + + t.Run("without any changes", func(t *testing.T) { + vTest, vTestCleanup := createVariableSetVariable(t, client, nil, VariableSetVariableCreateOptions{}) + defer vTestCleanup() + + v, err := client.VariableSetVariables.Update(ctx, vTest.VariableSet.ID, vTest.ID, VariableSetVariableUpdateOptions{}) + require.NoError(t, err) + + assert.Equal(t, vTest, v) + }) + + t.Run("with invalid variable ID", func(t *testing.T) { + _, err := client.VariableSetVariables.Update(ctx, badIdentifier, vTest.ID, VariableSetVariableUpdateOptions{}) + assert.EqualError(t, err, "invalid value for variable set ID") + }) + + t.Run("with invalid variable ID", func(t *testing.T) { + _, err := client.VariableSetVariables.Update(ctx, vTest.VariableSet.ID, badIdentifier, VariableSetVariableUpdateOptions{}) + assert.EqualError(t, err, "invalid value for variable ID") + }) +} + +func TestVariableSetVariablesDelete(t *testing.T) { + client := testClient(t) + ctx := context.Background() + + vsTest, vsTestCleanup := createVariableSet(t, client, nil, VariableSetCreateOptions{}) + defer vsTestCleanup() + + vTest, _ := createVariableSetVariable(t, client, vsTest, VariableSetVariableCreateOptions{}) + + t.Run("with valid options", func(t *testing.T) { + err := client.VariableSetVariables.Delete(ctx, vsTest.ID, vTest.ID) + assert.NoError(t, err) + }) + + t.Run("with non existing variable ID", func(t *testing.T) { + err := client.VariableSetVariables.Delete(ctx, vsTest.ID, "nonexisting") + assert.Equal(t, err, ErrResourceNotFound) + }) + + t.Run("with invalid workspace ID", func(t *testing.T) { + err := client.VariableSetVariables.Delete(ctx, badIdentifier, vTest.ID) + assert.EqualError(t, err, "invalid value for variable set ID") + }) + + t.Run("with invalid variable ID", func(t *testing.T) { + err := client.VariableSetVariables.Delete(ctx, vsTest.ID, badIdentifier) + assert.EqualError(t, err, "invalid value for variable ID") + }) } From 86c145f87893f5713ca36f2f9adb2cd3040f181e Mon Sep 17 00:00:00 2001 From: Rexredinger Date: Thu, 10 Feb 2022 09:14:59 -0500 Subject: [PATCH 04/31] Fix variableSetVariable tests --- helper_test.go | 4 ---- variable_set_variable.go | 10 ++-------- variable_set_variable_test.go | 25 ++++++++++++++++++------- 3 files changed, 20 insertions(+), 19 deletions(-) diff --git a/helper_test.go b/helper_test.go index f7947c84b..8a87082d7 100644 --- a/helper_test.go +++ b/helper_test.go @@ -1143,10 +1143,6 @@ func createVariableSetVariable(t *testing.T, client *Client, vs *VariableSet, op options.Sensitive = Bool(false) } - out := bytes.NewBuffer(nil) - jsonstuff := jsonapi.MarshalOnePayloadEmbedded(out, options) - fmt.Printf("%+v\n", jsonstuff) - ctx := context.Background() v, err := client.VariableSetVariables.Create(ctx, vs.ID, options) if err != nil { diff --git a/variable_set_variable.go b/variable_set_variable.go index a4b4e87a9..d4ea541f0 100644 --- a/variable_set_variable.go +++ b/variable_set_variable.go @@ -43,7 +43,7 @@ type VariableSetVariable struct { Value string `jsonapi:"attr,value"` Description string `jsonapi:"attr,description"` Category CategoryType `jsonapi:"attr,category"` - HCL string `jsonapi:"attr,hcl"` + HCL bool `jsonapi:"attr,hcl"` Sensitive bool `jsonapi:"attr,sensitive"` // Relations @@ -128,12 +128,6 @@ func (s *variableSetVariables) Create(ctx context.Context, variableSetID string, return nil, err } - /* - out := bytes.NewBuffer(nil) - jsonstuff := jsonapi.MarshalOnePayloadEmbedded(out, i&options) - fmt.Printf("%+v\n", jsonstuff) - */ - u := fmt.Sprintf("varsets/%s/relationships/vars", url.QueryEscape(variableSetID)) req, err := s.client.newRequest("POST", u, &options) if err != nil { @@ -182,7 +176,7 @@ func (s *variableSetVariables) Update(ctx context.Context, variableSetID string, return nil, errors.New("invalid value for variable ID") } - u := fmt.Sprintf("varsets/%s/vars/%s", url.QueryEscape(variableSetID), url.QueryEscape(variableID)) + u := fmt.Sprintf("varsets/%s/relationships/vars/%s", url.QueryEscape(variableSetID), url.QueryEscape(variableID)) req, err := s.client.newRequest("PATCH", u, &options) if err != nil { return nil, err diff --git a/variable_set_variable_test.go b/variable_set_variable_test.go index 111a1711e..065266bfb 100644 --- a/variable_set_variable_test.go +++ b/variable_set_variable_test.go @@ -205,7 +205,10 @@ func TestVariableSetVariablesUpdate(t *testing.T) { client := testClient(t) ctx := context.Background() - vTest, vTestCleanup := createVariableSetVariable(t, client, nil, VariableSetVariableCreateOptions{}) + vsTest, vsTestCleanup := createVariableSet(t, client, nil, VariableSetCreateOptions{}) + defer vsTestCleanup() + + vTest, vTestCleanup := createVariableSetVariable(t, client, vsTest, VariableSetVariableCreateOptions{}) defer vTestCleanup() t.Run("with valid options", func(t *testing.T) { @@ -215,7 +218,7 @@ func TestVariableSetVariablesUpdate(t *testing.T) { HCL: Bool(true), } - v, err := client.VariableSetVariables.Update(ctx, vTest.VariableSet.ID, vTest.ID, options) + v, err := client.VariableSetVariables.Update(ctx, vsTest.ID, vTest.ID, options) require.NoError(t, err) assert.Equal(t, *options.Key, v.Key) @@ -229,7 +232,7 @@ func TestVariableSetVariablesUpdate(t *testing.T) { HCL: Bool(false), } - v, err := client.VariableSetVariables.Update(ctx, vTest.VariableSet.ID, vTest.ID, options) + v, err := client.VariableSetVariables.Update(ctx, vsTest.ID, vTest.ID, options) require.NoError(t, err) assert.Equal(t, *options.Key, v.Key) @@ -241,7 +244,7 @@ func TestVariableSetVariablesUpdate(t *testing.T) { Sensitive: Bool(true), } - v, err := client.VariableSetVariables.Update(ctx, vTest.VariableSet.ID, vTest.ID, options) + v, err := client.VariableSetVariables.Update(ctx, vsTest.ID, vTest.ID, options) require.NoError(t, err) assert.Equal(t, *options.Sensitive, v.Sensitive) @@ -249,10 +252,18 @@ func TestVariableSetVariablesUpdate(t *testing.T) { }) t.Run("without any changes", func(t *testing.T) { - vTest, vTestCleanup := createVariableSetVariable(t, client, nil, VariableSetVariableCreateOptions{}) + vTest, vTestCleanup := createVariableSetVariable(t, client, vsTest, VariableSetVariableCreateOptions{}) defer vTestCleanup() - v, err := client.VariableSetVariables.Update(ctx, vTest.VariableSet.ID, vTest.ID, VariableSetVariableUpdateOptions{}) + ua := VariableSetVariableUpdateOptions{ + Key: String(vTest.Key), + Value: String(vTest.Value), + Description: String(vTest.Description), + Sensitive: Bool(vTest.Sensitive), + HCL: Bool(vTest.HCL), + } + + v, err := client.VariableSetVariables.Update(ctx, vsTest.ID, vTest.ID, ua) require.NoError(t, err) assert.Equal(t, vTest, v) @@ -264,7 +275,7 @@ func TestVariableSetVariablesUpdate(t *testing.T) { }) t.Run("with invalid variable ID", func(t *testing.T) { - _, err := client.VariableSetVariables.Update(ctx, vTest.VariableSet.ID, badIdentifier, VariableSetVariableUpdateOptions{}) + _, err := client.VariableSetVariables.Update(ctx, vsTest.ID, badIdentifier, VariableSetVariableUpdateOptions{}) assert.EqualError(t, err, "invalid value for variable ID") }) } From 20c32bf620400802e0ae043ae023f42659683076 Mon Sep 17 00:00:00 2001 From: Rexredinger Date: Thu, 10 Feb 2022 10:15:51 -0500 Subject: [PATCH 05/31] Cleanup ws and go.sum --- helper_test.go | 1 - 1 file changed, 1 deletion(-) diff --git a/helper_test.go b/helper_test.go index 8a87082d7..200294331 100644 --- a/helper_test.go +++ b/helper_test.go @@ -1011,7 +1011,6 @@ func createWorkspaceWithVCS(t *testing.T, client *Client, org *Organization, opt ctx := context.Background() w, err := client.Workspaces.Create(ctx, org.Name, options) if err != nil { - t.Fatal(err) } From f0c80c787a085e039e7a398d2fdd89e77e45c544 Mon Sep 17 00:00:00 2001 From: Rexredinger Date: Thu, 10 Feb 2022 10:51:14 -0500 Subject: [PATCH 06/31] Remove TODOs --- variable_set.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/variable_set.go b/variable_set.go index f307aee0f..88f7d018a 100644 --- a/variable_set.go +++ b/variable_set.go @@ -96,7 +96,7 @@ type VariableSetCreateOptions struct { // The name of the variable set. // Affects variable precedence when there are conflicts between Variable Sets - // TODO: Add link to documentation + // https://www.terraform.io/cloud-docs/api-docs/variable-sets#apply-variable-set-to-workspaces Name *string `jsonapi:"attr,name"` // A description to provide context for the variable set. @@ -171,7 +171,7 @@ type VariableSetUpdateOptions struct { // The name of the variable set. // Affects variable precedence when there are conflicts between Variable Sets - // TODO: Add link to documentation + // https://www.terraform.io/cloud-docs/api-docs/variable-sets#apply-variable-set-to-workspaces Name *string `jsonapi:"attr,name"` // A description to provide context for the variable set. From 95f8fc030fb98fb5995073b0c950e821fb53db4d Mon Sep 17 00:00:00 2001 From: Rexredinger Date: Tue, 15 Feb 2022 11:02:32 -0500 Subject: [PATCH 07/31] Use typed strings for include options --- variable_set.go | 33 +++++++++++++++++++++++++-------- variable_set_test.go | 8 ++++---- 2 files changed, 29 insertions(+), 12 deletions(-) diff --git a/variable_set.go b/variable_set.go index 88f7d018a..3d093be2b 100644 --- a/variable_set.go +++ b/variable_set.go @@ -22,7 +22,7 @@ type VariableSets interface { Create(ctx context.Context, organization string, options VariableSetCreateOptions) (*VariableSet, error) // Read a variable set by its ID. - Read(ctx context.Context, variableSetID string) (*VariableSet, error) + Read(ctx context.Context, variableSetVariableSetReadOptionsID string, options VariableSetReadOptions) (*VariableSet, error) // Update an existing variable set. Update(ctx context.Context, variableSetID string, options VariableSetUpdateOptions) (*VariableSet, error) @@ -54,8 +54,18 @@ type VariableSet struct { Variables []*VariableSetVariable `jsonapi:"relation,vars,omitempty"` } +// A list of relations to include. See available resources +// https://www.terraform.io/docs/cloud/api/admin/organizations.html#available-related-resources +type VariableSetIncludeOps string + +const ( + VariableSetWorkspaces VariableSetIncludeOps = "workspaces" + VariableSetVars VariableSetIncludeOps = "vars" +) + type VariableSetListOptions struct { ListOptions + Include string `url:"include"` } func (o VariableSetListOptions) valid() error { @@ -140,14 +150,18 @@ func (s *variableSets) Create(ctx context.Context, organization string, options return vl, nil } +type VariableSetReadOptions struct { + Include *[]VariableSetIncludeOps `url:"include:omitempty"` +} + // Read is used to inspect a given variable set based on ID -func (s *variableSets) Read(ctx context.Context, variableSetID string) (*VariableSet, error) { +func (s *variableSets) Read(ctx context.Context, variableSetID string, options VariableSetReadOptions) (*VariableSet, error) { if !validStringID(&variableSetID) { return nil, errors.New("invalid variable set ID") } - u := fmt.Sprintf("varsets/%s?include=workspaces", url.QueryEscape(variableSetID)) - req, err := s.client.newRequest("GET", u, nil) + u := fmt.Sprintf("varsets/%s", url.QueryEscape(variableSetID)) + req, err := s.client.newRequest("GET", u, options) if err != nil { return nil, err } @@ -175,10 +189,12 @@ type VariableSetUpdateOptions struct { Name *string `jsonapi:"attr,name"` // A description to provide context for the variable set. - Description *string `jsonapi:"attr,description"` + Description *string `jsonapi:"attr,description,omitempty"` // If true the variable set is considered in all runs in the organization. - Global *bool `jsonapi:"attr,global"` + Global *bool `jsonapi:"attr,global,omitempty"` + + Include *[]VariableSetIncludeOps `url:"include:omitempty"` } func (s *variableSets) Update(ctx context.Context, variableSetID string, options VariableSetUpdateOptions) (*VariableSet, error) { @@ -186,7 +202,7 @@ func (s *variableSets) Update(ctx context.Context, variableSetID string, options return nil, errors.New("invalid value for variable set ID") } - u := fmt.Sprintf("varsets/%s?include=workspaces", url.QueryEscape(variableSetID)) + u := fmt.Sprintf("varsets/%s", url.QueryEscape(variableSetID)) req, err := s.client.newRequest("PATCH", u, &options) if err != nil { return nil, err @@ -239,7 +255,8 @@ func (s *variableSets) Assign(ctx context.Context, variableSetID string, options options.Global = Bool(false) - u := fmt.Sprintf("varsets/%s?include=workspaces", url.QueryEscape(variableSetID)) + // We force inclusion of workspaces as that is the primary data for which we are concerned with confirming changes. + u := fmt.Sprintf("varsets/%s?include=%s", url.QueryEscape(variableSetID), VariableSetWorkspaces) req, err := s.client.newRequest("PATCH", u, &options) if err != nil { return nil, err diff --git a/variable_set_test.go b/variable_set_test.go index b845af682..82e3110eb 100644 --- a/variable_set_test.go +++ b/variable_set_test.go @@ -76,7 +76,7 @@ func TestVariableSetsCreate(t *testing.T) { require.NoError(t, err) //Get refreshed view from the API - refreshed, err := client.VariableSets.Read(ctx, vs.ID) + refreshed, err := client.VariableSets.Read(ctx, vs.ID, VariableSetReadOptions{}) require.NoError(t, err) for _, item := range []*VariableSet{ @@ -118,13 +118,13 @@ func TestVariableSetsRead(t *testing.T) { defer vsTestCleanup() t.Run("when the variable set exists", func(t *testing.T) { - vs, err := client.VariableSets.Read(ctx, vsTest.ID) + vs, err := client.VariableSets.Read(ctx, vsTest.ID, VariableSetReadOptions{}) require.NoError(t, err) assert.Equal(t, vsTest, vs) }) t.Run("when variable set does not exist", func(t *testing.T) { - vs, err := client.VariableSets.Read(ctx, "nonexisting") + vs, err := client.VariableSets.Read(ctx, "nonexisting", VariableSetReadOptions{}) assert.Nil(t, vs) assert.Error(t, err) }) @@ -183,7 +183,7 @@ func TestVariableSetsDelete(t *testing.T) { require.NoError(t, err) // Try loading the variable set - it should fail. - _, err = client.VariableSets.Read(ctx, vsTest.ID) + _, err = client.VariableSets.Read(ctx, vsTest.ID, VariableSetReadOptions{}) assert.Equal(t, ErrResourceNotFound, err) }) From a1990e6ec65f6ff49c8d2fbbbede99ae48081e8a Mon Sep 17 00:00:00 2001 From: Rexredinger Date: Tue, 15 Feb 2022 12:04:51 -0500 Subject: [PATCH 08/31] Use option pointers in the 1.0 style --- helper_test.go | 4 ++-- variable_set.go | 42 +++++++++++++++++++---------------- variable_set_test.go | 28 +++++++++++------------ variable_set_variable.go | 30 ++++++++++++++----------- variable_set_variable_test.go | 38 +++++++++++++++---------------- 5 files changed, 75 insertions(+), 67 deletions(-) diff --git a/helper_test.go b/helper_test.go index 200294331..cfb96f2c2 100644 --- a/helper_test.go +++ b/helper_test.go @@ -1093,7 +1093,7 @@ func createVariableSet(t *testing.T, client *Client, org *Organization, options } ctx := context.Background() - vs, err := client.VariableSets.Create(ctx, org.Name, options) + vs, err := client.VariableSets.Create(ctx, org.Name, &options) if err != nil { t.Fatal(err) } @@ -1143,7 +1143,7 @@ func createVariableSetVariable(t *testing.T, client *Client, vs *VariableSet, op } ctx := context.Background() - v, err := client.VariableSetVariables.Create(ctx, vs.ID, options) + v, err := client.VariableSetVariables.Create(ctx, vs.ID, &options) if err != nil { t.Fatal(err) } diff --git a/variable_set.go b/variable_set.go index 3d093be2b..44ac5b62f 100644 --- a/variable_set.go +++ b/variable_set.go @@ -16,22 +16,22 @@ var _ VariableSets = (*variableSets)(nil) // TFE API docs: https://www.terraform.io/cloud-docs/api-docs/variable-sets type VariableSets interface { // List all the variable sets within an organization. - List(ctx context.Context, organization string, options VariableSetListOptions) (*VariableSetList, error) + List(ctx context.Context, organization string, options *VariableSetListOptions) (*VariableSetList, error) // Create is used to create a new variable set. - Create(ctx context.Context, organization string, options VariableSetCreateOptions) (*VariableSet, error) + Create(ctx context.Context, organization string, options *VariableSetCreateOptions) (*VariableSet, error) // Read a variable set by its ID. - Read(ctx context.Context, variableSetVariableSetReadOptionsID string, options VariableSetReadOptions) (*VariableSet, error) + Read(ctx context.Context, variableSetVariableSetReadOptionsID string, options *VariableSetReadOptions) (*VariableSet, error) // Update an existing variable set. - Update(ctx context.Context, variableSetID string, options VariableSetUpdateOptions) (*VariableSet, error) + Update(ctx context.Context, variableSetID string, options *VariableSetUpdateOptions) (*VariableSet, error) // Delete a variable set by ID. Delete(ctx context.Context, variableSetID string) error // Assign a variable set to workspaces - Assign(ctx context.Context, variableSetID string, options VariableSetAssignOptions) (*VariableSet, error) + Assign(ctx context.Context, variableSetID string, options *VariableSetAssignOptions) (*VariableSet, error) } type variableSets struct { @@ -73,16 +73,18 @@ func (o VariableSetListOptions) valid() error { } // List all Variable Sets in the organization -func (s *variableSets) List(ctx context.Context, organization string, options VariableSetListOptions) (*VariableSetList, error) { +func (s *variableSets) List(ctx context.Context, organization string, options *VariableSetListOptions) (*VariableSetList, error) { if !validStringID(&organization) { return nil, ErrInvalidOrg } - if err := options.valid(); err != nil { - return nil, err + if options != nil { + if err := options.valid(); err != nil { + return nil, err + } } u := fmt.Sprintf("organizations/%s/varsets", url.QueryEscape(organization)) - req, err := s.client.newRequest("GET", u, &options) + req, err := s.client.newRequest("GET", u, options) if err != nil { return nil, err } @@ -127,16 +129,18 @@ func (o VariableSetCreateOptions) valid() error { } // Create is used to create a new variable set. -func (s *variableSets) Create(ctx context.Context, organization string, options VariableSetCreateOptions) (*VariableSet, error) { +func (s *variableSets) Create(ctx context.Context, organization string, options *VariableSetCreateOptions) (*VariableSet, error) { if !validStringID(&organization) { return nil, ErrInvalidOrg } - if err := options.valid(); err != nil { - return nil, err + if options != nil { + if err := options.valid(); err != nil { + return nil, err + } } u := fmt.Sprintf("organizations/%s/varsets", url.QueryEscape(organization)) - req, err := s.client.newRequest("POST", u, &options) + req, err := s.client.newRequest("POST", u, options) if err != nil { return nil, err } @@ -155,7 +159,7 @@ type VariableSetReadOptions struct { } // Read is used to inspect a given variable set based on ID -func (s *variableSets) Read(ctx context.Context, variableSetID string, options VariableSetReadOptions) (*VariableSet, error) { +func (s *variableSets) Read(ctx context.Context, variableSetID string, options *VariableSetReadOptions) (*VariableSet, error) { if !validStringID(&variableSetID) { return nil, errors.New("invalid variable set ID") } @@ -197,13 +201,13 @@ type VariableSetUpdateOptions struct { Include *[]VariableSetIncludeOps `url:"include:omitempty"` } -func (s *variableSets) Update(ctx context.Context, variableSetID string, options VariableSetUpdateOptions) (*VariableSet, error) { +func (s *variableSets) Update(ctx context.Context, variableSetID string, options *VariableSetUpdateOptions) (*VariableSet, error) { if !validStringID(&variableSetID) { return nil, errors.New("invalid value for variable set ID") } u := fmt.Sprintf("varsets/%s", url.QueryEscape(variableSetID)) - req, err := s.client.newRequest("PATCH", u, &options) + req, err := s.client.newRequest("PATCH", u, options) if err != nil { return nil, err } @@ -248,8 +252,8 @@ type VariableSetAssignOptions struct { } // Use Update to assign a variable set to workspaces -func (s *variableSets) Assign(ctx context.Context, variableSetID string, options VariableSetAssignOptions) (*VariableSet, error) { - if options.Workspaces == nil { +func (s *variableSets) Assign(ctx context.Context, variableSetID string, options *VariableSetAssignOptions) (*VariableSet, error) { + if options == nil || options.Workspaces == nil { return nil, errors.New("No workspaces list provided") } @@ -257,7 +261,7 @@ func (s *variableSets) Assign(ctx context.Context, variableSetID string, options // We force inclusion of workspaces as that is the primary data for which we are concerned with confirming changes. u := fmt.Sprintf("varsets/%s?include=%s", url.QueryEscape(variableSetID), VariableSetWorkspaces) - req, err := s.client.newRequest("PATCH", u, &options) + req, err := s.client.newRequest("PATCH", u, options) if err != nil { return nil, err } diff --git a/variable_set_test.go b/variable_set_test.go index 82e3110eb..1b73c916b 100644 --- a/variable_set_test.go +++ b/variable_set_test.go @@ -24,7 +24,7 @@ func TestVariableSetsList(t *testing.T) { defer vsTestCleanup2() t.Run("without list options", func(t *testing.T) { - vsl, err := client.VariableSets.List(ctx, orgTest.Name, VariableSetListOptions{}) + vsl, err := client.VariableSets.List(ctx, orgTest.Name, nil) require.NoError(t, err) assert.Contains(t, vsl.Items, vsTest1) assert.Contains(t, vsl.Items, vsTest2) @@ -39,7 +39,7 @@ func TestVariableSetsList(t *testing.T) { // Request a page number which is out of range. The result should // be successful, but return no results if the paging options are // properly passed along. - vsl, err := client.VariableSets.List(ctx, orgTest.Name, VariableSetListOptions{ + vsl, err := client.VariableSets.List(ctx, orgTest.Name, &VariableSetListOptions{ ListOptions: ListOptions{ PageNumber: 999, PageSize: 100, @@ -52,7 +52,7 @@ func TestVariableSetsList(t *testing.T) { }) t.Run("when Organization name is invalid ID", func(t *testing.T) { - vsl, err := client.VariableSets.List(ctx, badIdentifier, VariableSetListOptions{}) + vsl, err := client.VariableSets.List(ctx, badIdentifier, nil) assert.Nil(t, vsl) assert.EqualError(t, err, ErrInvalidOrg.Error()) }) @@ -72,11 +72,11 @@ func TestVariableSetsCreate(t *testing.T) { Global: Bool(false), } - vs, err := client.VariableSets.Create(ctx, orgTest.Name, options) + vs, err := client.VariableSets.Create(ctx, orgTest.Name, &options) require.NoError(t, err) //Get refreshed view from the API - refreshed, err := client.VariableSets.Read(ctx, vs.ID, VariableSetReadOptions{}) + refreshed, err := client.VariableSets.Read(ctx, vs.ID, nil) require.NoError(t, err) for _, item := range []*VariableSet{ @@ -91,7 +91,7 @@ func TestVariableSetsCreate(t *testing.T) { }) t.Run("when options is missing name", func(t *testing.T) { - vs, err := client.VariableSets.Create(ctx, "foo", VariableSetCreateOptions{ + vs, err := client.VariableSets.Create(ctx, "foo", &VariableSetCreateOptions{ Global: Bool(true), }) assert.Nil(t, vs) @@ -99,7 +99,7 @@ func TestVariableSetsCreate(t *testing.T) { }) t.Run("when options is missing global flag", func(t *testing.T) { - vs, err := client.VariableSets.Create(ctx, "foo", VariableSetCreateOptions{ + vs, err := client.VariableSets.Create(ctx, "foo", &VariableSetCreateOptions{ Name: String("foo"), }) assert.Nil(t, vs) @@ -118,13 +118,13 @@ func TestVariableSetsRead(t *testing.T) { defer vsTestCleanup() t.Run("when the variable set exists", func(t *testing.T) { - vs, err := client.VariableSets.Read(ctx, vsTest.ID, VariableSetReadOptions{}) + vs, err := client.VariableSets.Read(ctx, vsTest.ID, nil) require.NoError(t, err) assert.Equal(t, vsTest, vs) }) t.Run("when variable set does not exist", func(t *testing.T) { - vs, err := client.VariableSets.Read(ctx, "nonexisting", VariableSetReadOptions{}) + vs, err := client.VariableSets.Read(ctx, "nonexisting", nil) assert.Nil(t, vs) assert.Error(t, err) }) @@ -150,7 +150,7 @@ func TestVariableSetsUpdate(t *testing.T) { Global: Bool(true), } - vsAfter, err := client.VariableSets.Update(ctx, vsTest.ID, options) + vsAfter, err := client.VariableSets.Update(ctx, vsTest.ID, &options) require.NoError(t, err) assert.Equal(t, *options.Name, vsAfter.Name) @@ -159,7 +159,7 @@ func TestVariableSetsUpdate(t *testing.T) { }) t.Run("when options has an invalid variable set ID", func(t *testing.T) { - vsAfter, err := client.VariableSets.Update(ctx, badIdentifier, VariableSetUpdateOptions{ + vsAfter, err := client.VariableSets.Update(ctx, badIdentifier, &VariableSetUpdateOptions{ Name: String("UpdatedName"), Description: String("Updated Description"), Global: Bool(true), @@ -183,7 +183,7 @@ func TestVariableSetsDelete(t *testing.T) { require.NoError(t, err) // Try loading the variable set - it should fail. - _, err = client.VariableSets.Read(ctx, vsTest.ID, VariableSetReadOptions{}) + _, err = client.VariableSets.Read(ctx, vsTest.ID, nil) assert.Equal(t, ErrResourceNotFound, err) }) @@ -209,7 +209,7 @@ func TestVariableSetsAssign(t *testing.T) { Workspaces: []*Workspace{wTest}, } - vsAfter, err := client.VariableSets.Assign(ctx, vsTest.ID, options) + vsAfter, err := client.VariableSets.Assign(ctx, vsTest.ID, &options) require.NoError(t, err) assert.Equal(t, len(options.Workspaces), len(vsAfter.Workspaces)) @@ -219,7 +219,7 @@ func TestVariableSetsAssign(t *testing.T) { Workspaces: []*Workspace{}, } - vsAfter, err = client.VariableSets.Assign(ctx, vsTest.ID, options) + vsAfter, err = client.VariableSets.Assign(ctx, vsTest.ID, &options) require.NoError(t, err) assert.Equal(t, len(options.Workspaces), len(vsAfter.Workspaces)) diff --git a/variable_set_variable.go b/variable_set_variable.go index d4ea541f0..da2d6f808 100644 --- a/variable_set_variable.go +++ b/variable_set_variable.go @@ -16,13 +16,13 @@ var _ VariableSetVariables = (*variableSetVariables)(nil) // TFE API docs: https://www.terraform.io/cloud-docs/api-docs/variable-sets#variable-relationships type VariableSetVariables interface { // List all variables in the variable set. - List(ctx context.Context, variableSetID string, options VariableSetVariableListOptions) (*VariableSetVariableList, error) + List(ctx context.Context, variableSetID string, options *VariableSetVariableListOptions) (*VariableSetVariableList, error) // Create is used to create a new variable within a given variable set - Create(ctx context.Context, variableSetID string, options VariableSetVariableCreateOptions) (*VariableSetVariable, error) + Create(ctx context.Context, variableSetID string, options *VariableSetVariableCreateOptions) (*VariableSetVariable, error) // Update valuse of an existing variable - Update(ctx context.Context, variableSetID string, variableID string, options VariableSetVariableUpdateOptions) (*VariableSetVariable, error) + Update(ctx context.Context, variableSetID string, variableID string, options *VariableSetVariableUpdateOptions) (*VariableSetVariable, error) // Delete a variable by its ID Delete(ctx context.Context, variableSetID string, variableID string) error @@ -59,16 +59,18 @@ func (o VariableSetVariableListOptions) valid() error { } // List all variables associated with the given variable set. -func (s *variableSetVariables) List(ctx context.Context, variableSetID string, options VariableSetVariableListOptions) (*VariableSetVariableList, error) { +func (s *variableSetVariables) List(ctx context.Context, variableSetID string, options *VariableSetVariableListOptions) (*VariableSetVariableList, error) { if !validStringID(&variableSetID) { return nil, errors.New("invalid value for variable set ID") } - if err := options.valid(); err != nil { - return nil, err + if options != nil { + if err := options.valid(); err != nil { + return nil, err + } } u := fmt.Sprintf("varsets/%s/relationships/vars", variableSetID) - req, err := s.client.newRequest("GET", u, &options) + req, err := s.client.newRequest("GET", u, options) if err != nil { return nil, err } @@ -120,16 +122,18 @@ func (o VariableSetVariableCreateOptions) valid() error { } // Create is used to create a new variable. -func (s *variableSetVariables) Create(ctx context.Context, variableSetID string, options VariableSetVariableCreateOptions) (*VariableSetVariable, error) { +func (s *variableSetVariables) Create(ctx context.Context, variableSetID string, options *VariableSetVariableCreateOptions) (*VariableSetVariable, error) { if !validStringID(&variableSetID) { return nil, errors.New("invalid value for variable set ID") } - if err := options.valid(); err != nil { - return nil, err + if options != nil { + if err := options.valid(); err != nil { + return nil, err + } } u := fmt.Sprintf("varsets/%s/relationships/vars", url.QueryEscape(variableSetID)) - req, err := s.client.newRequest("POST", u, &options) + req, err := s.client.newRequest("POST", u, options) if err != nil { return nil, err } @@ -168,7 +172,7 @@ type VariableSetVariableUpdateOptions struct { } // Update values of an existing variable. -func (s *variableSetVariables) Update(ctx context.Context, variableSetID string, variableID string, options VariableSetVariableUpdateOptions) (*VariableSetVariable, error) { +func (s *variableSetVariables) Update(ctx context.Context, variableSetID string, variableID string, options *VariableSetVariableUpdateOptions) (*VariableSetVariable, error) { if !validStringID(&variableSetID) { return nil, errors.New("invalid value for variable set ID") } @@ -177,7 +181,7 @@ func (s *variableSetVariables) Update(ctx context.Context, variableSetID string, } u := fmt.Sprintf("varsets/%s/relationships/vars/%s", url.QueryEscape(variableSetID), url.QueryEscape(variableID)) - req, err := s.client.newRequest("PATCH", u, &options) + req, err := s.client.newRequest("PATCH", u, options) if err != nil { return nil, err } diff --git a/variable_set_variable_test.go b/variable_set_variable_test.go index 065266bfb..3c48e5479 100644 --- a/variable_set_variable_test.go +++ b/variable_set_variable_test.go @@ -31,7 +31,7 @@ func TestVariableSetVariablesList(t *testing.T) { defer vTestCleanup2() t.Run("without list options", func(t *testing.T) { - vl, err := client.VariableSetVariables.List(ctx, vsTest.ID, VariableSetVariableListOptions{}) + vl, err := client.VariableSetVariables.List(ctx, vsTest.ID, nil) require.NoError(t, err) assert.Contains(t, vl.Items, vTest1) assert.Contains(t, vl.Items, vTest2) @@ -46,7 +46,7 @@ func TestVariableSetVariablesList(t *testing.T) { // Request a page number which is out of range. The result should // be successful, but return no results if the paging options are // properly passed along. - vl, err := client.VariableSetVariables.List(ctx, vsTest.ID, VariableSetVariableListOptions{ + vl, err := client.VariableSetVariables.List(ctx, vsTest.ID, &VariableSetVariableListOptions{ ListOptions: ListOptions{ PageNumber: 999, PageSize: 100, @@ -59,7 +59,7 @@ func TestVariableSetVariablesList(t *testing.T) { }) t.Run("when variable set ID is invalid ID", func(t *testing.T) { - vl, err := client.VariableSetVariables.List(ctx, badIdentifier, VariableSetVariableListOptions{}) + vl, err := client.VariableSetVariables.List(ctx, badIdentifier, nil) assert.Nil(t, vl) assert.EqualError(t, err, "invalid value for variable set ID") }) @@ -85,7 +85,7 @@ func TestVariableSetVariablesCreate(t *testing.T) { Sensitive: Bool(false), } - v, err := client.VariableSetVariables.Create(ctx, vsTest.ID, options) + v, err := client.VariableSetVariables.Create(ctx, vsTest.ID, &options) require.NoError(t, err) assert.NotEmpty(t, v.ID) @@ -103,7 +103,7 @@ func TestVariableSetVariablesCreate(t *testing.T) { Category: Category(CategoryTerraform), } - v, err := client.VariableSetVariables.Create(ctx, vsTest.ID, options) + v, err := client.VariableSetVariables.Create(ctx, vsTest.ID, &options) require.NoError(t, err) assert.NotEmpty(t, v.ID) @@ -121,7 +121,7 @@ func TestVariableSetVariablesCreate(t *testing.T) { Category: Category(CategoryTerraform), } - v, err := client.VariableSetVariables.Create(ctx, vsTest.ID, options) + v, err := client.VariableSetVariables.Create(ctx, vsTest.ID, &options) require.NoError(t, err) assert.NotEmpty(t, v.ID) @@ -139,7 +139,7 @@ func TestVariableSetVariablesCreate(t *testing.T) { Category: Category(CategoryTerraform), } - _, err := client.VariableSetVariables.Create(ctx, vsTest.ID, options) + _, err := client.VariableSetVariables.Create(ctx, vsTest.ID, &options) assert.Error(t, err) }) @@ -149,7 +149,7 @@ func TestVariableSetVariablesCreate(t *testing.T) { Category: Category(CategoryTerraform), } - v, err := client.VariableSetVariables.Create(ctx, vsTest.ID, options) + v, err := client.VariableSetVariables.Create(ctx, vsTest.ID, &options) require.NoError(t, err) assert.NotEmpty(t, v.ID) @@ -164,7 +164,7 @@ func TestVariableSetVariablesCreate(t *testing.T) { Category: Category(CategoryTerraform), } - _, err := client.VariableSetVariables.Create(ctx, vsTest.ID, options) + _, err := client.VariableSetVariables.Create(ctx, vsTest.ID, &options) assert.EqualError(t, err, "key is required") }) @@ -175,7 +175,7 @@ func TestVariableSetVariablesCreate(t *testing.T) { Category: Category(CategoryTerraform), } - _, err := client.VariableSetVariables.Create(ctx, vsTest.ID, options) + _, err := client.VariableSetVariables.Create(ctx, vsTest.ID, &options) assert.EqualError(t, err, "key is required") }) @@ -185,7 +185,7 @@ func TestVariableSetVariablesCreate(t *testing.T) { Value: String(randomString(t)), } - _, err := client.VariableSetVariables.Create(ctx, vsTest.ID, options) + _, err := client.VariableSetVariables.Create(ctx, vsTest.ID, &options) assert.EqualError(t, err, "category is required") }) @@ -196,7 +196,7 @@ func TestVariableSetVariablesCreate(t *testing.T) { Category: Category(CategoryTerraform), } - _, err := client.VariableSetVariables.Create(ctx, badIdentifier, options) + _, err := client.VariableSetVariables.Create(ctx, badIdentifier, &options) assert.EqualError(t, err, "invalid value for variable set ID") }) } @@ -218,7 +218,7 @@ func TestVariableSetVariablesUpdate(t *testing.T) { HCL: Bool(true), } - v, err := client.VariableSetVariables.Update(ctx, vsTest.ID, vTest.ID, options) + v, err := client.VariableSetVariables.Update(ctx, vsTest.ID, vTest.ID, &options) require.NoError(t, err) assert.Equal(t, *options.Key, v.Key) @@ -232,7 +232,7 @@ func TestVariableSetVariablesUpdate(t *testing.T) { HCL: Bool(false), } - v, err := client.VariableSetVariables.Update(ctx, vsTest.ID, vTest.ID, options) + v, err := client.VariableSetVariables.Update(ctx, vsTest.ID, vTest.ID, &options) require.NoError(t, err) assert.Equal(t, *options.Key, v.Key) @@ -244,7 +244,7 @@ func TestVariableSetVariablesUpdate(t *testing.T) { Sensitive: Bool(true), } - v, err := client.VariableSetVariables.Update(ctx, vsTest.ID, vTest.ID, options) + v, err := client.VariableSetVariables.Update(ctx, vsTest.ID, vTest.ID, &options) require.NoError(t, err) assert.Equal(t, *options.Sensitive, v.Sensitive) @@ -255,7 +255,7 @@ func TestVariableSetVariablesUpdate(t *testing.T) { vTest, vTestCleanup := createVariableSetVariable(t, client, vsTest, VariableSetVariableCreateOptions{}) defer vTestCleanup() - ua := VariableSetVariableUpdateOptions{ + options := VariableSetVariableUpdateOptions{ Key: String(vTest.Key), Value: String(vTest.Value), Description: String(vTest.Description), @@ -263,19 +263,19 @@ func TestVariableSetVariablesUpdate(t *testing.T) { HCL: Bool(vTest.HCL), } - v, err := client.VariableSetVariables.Update(ctx, vsTest.ID, vTest.ID, ua) + v, err := client.VariableSetVariables.Update(ctx, vsTest.ID, vTest.ID, &options) require.NoError(t, err) assert.Equal(t, vTest, v) }) t.Run("with invalid variable ID", func(t *testing.T) { - _, err := client.VariableSetVariables.Update(ctx, badIdentifier, vTest.ID, VariableSetVariableUpdateOptions{}) + _, err := client.VariableSetVariables.Update(ctx, badIdentifier, vTest.ID, nil) assert.EqualError(t, err, "invalid value for variable set ID") }) t.Run("with invalid variable ID", func(t *testing.T) { - _, err := client.VariableSetVariables.Update(ctx, vsTest.ID, badIdentifier, VariableSetVariableUpdateOptions{}) + _, err := client.VariableSetVariables.Update(ctx, vsTest.ID, badIdentifier, nil) assert.EqualError(t, err, "invalid value for variable ID") }) } From 30d11f13c3964d0c3367b008e4a2120a4edc643a Mon Sep 17 00:00:00 2001 From: Rexredinger Date: Tue, 15 Feb 2022 12:29:23 -0500 Subject: [PATCH 09/31] fix lint complaint --- variable_set.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/variable_set.go b/variable_set.go index 44ac5b62f..a1a70705b 100644 --- a/variable_set.go +++ b/variable_set.go @@ -254,7 +254,7 @@ type VariableSetAssignOptions struct { // Use Update to assign a variable set to workspaces func (s *variableSets) Assign(ctx context.Context, variableSetID string, options *VariableSetAssignOptions) (*VariableSet, error) { if options == nil || options.Workspaces == nil { - return nil, errors.New("No workspaces list provided") + return nil, errors.New("no workspaces list provided") } options.Global = Bool(false) From b3e6a263e7134854a5846531382b1017de0900aa Mon Sep 17 00:00:00 2001 From: Rexredinger Date: Thu, 17 Feb 2022 17:37:22 -0500 Subject: [PATCH 10/31] recognize organizaiton relation for VariableSet --- variable_set.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/variable_set.go b/variable_set.go index a1a70705b..426e256ce 100644 --- a/variable_set.go +++ b/variable_set.go @@ -50,8 +50,9 @@ type VariableSet struct { Global bool `jsonapi:"attr,global"` // Relations - Workspaces []*Workspace `jsonapi:"relation,workspaces,omitempty"` - Variables []*VariableSetVariable `jsonapi:"relation,vars,omitempty"` + Organization *Organization `jsonapi:"relation,organization"` + Workspaces []*Workspace `jsonapi:"relation,workspaces,omitempty"` + Variables []*VariableSetVariable `jsonapi:"relation,vars,omitempty"` } // A list of relations to include. See available resources From 79ddc1add79ff0d67d82f4b357edc7cf3998e004 Mon Sep 17 00:00:00 2001 From: Rexredinger Date: Wed, 2 Mar 2022 10:54:49 -0500 Subject: [PATCH 11/31] Fix varset interface typo --- variable_set.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/variable_set.go b/variable_set.go index 426e256ce..e8c3942ac 100644 --- a/variable_set.go +++ b/variable_set.go @@ -22,7 +22,7 @@ type VariableSets interface { Create(ctx context.Context, organization string, options *VariableSetCreateOptions) (*VariableSet, error) // Read a variable set by its ID. - Read(ctx context.Context, variableSetVariableSetReadOptionsID string, options *VariableSetReadOptions) (*VariableSet, error) + Read(ctx context.Context, variableSetID string, options *VariableSetReadOptions) (*VariableSet, error) // Update an existing variable set. Update(ctx context.Context, variableSetID string, options *VariableSetUpdateOptions) (*VariableSet, error) From d4e919d5e2181c05ab94f0dd2cc59b8b81a51f0f Mon Sep 17 00:00:00 2001 From: Rexredinger Date: Wed, 2 Mar 2022 17:02:18 -0500 Subject: [PATCH 12/31] Add VariablSetVariable Read function --- variable_set_variable.go | 28 +++++++++++++++++++++++ variable_set_variable_test.go | 43 +++++++++++++++++++++++++++++++++++ 2 files changed, 71 insertions(+) diff --git a/variable_set_variable.go b/variable_set_variable.go index da2d6f808..0d2a2e9e4 100644 --- a/variable_set_variable.go +++ b/variable_set_variable.go @@ -21,6 +21,9 @@ type VariableSetVariables interface { // Create is used to create a new variable within a given variable set Create(ctx context.Context, variableSetID string, options *VariableSetVariableCreateOptions) (*VariableSetVariable, error) + // Read a variable by its ID + Read(ctx context.Context, variableSetID string, variableId string) (*VariableSetVariable, error) + // Update valuse of an existing variable Update(ctx context.Context, variableSetID string, variableID string, options *VariableSetVariableUpdateOptions) (*VariableSetVariable, error) @@ -147,6 +150,31 @@ func (s *variableSetVariables) Create(ctx context.Context, variableSetID string, return v, nil } +// Read a variable by its ID. +func (s *variableSetVariables) Read(ctx context.Context, variableSetID string, variableID string) (*VariableSetVariable, error) { + if !validStringID(&variableSetID) { + return nil, errors.New("invalid value for variable set ID") + } + if !validStringID(&variableID) { + return nil, errors.New("invalid value for variable ID") + } + + u := fmt.Sprintf("varsets/%s/relationships/vars/%s", url.QueryEscape(variableSetID), url.QueryEscape(variableID)) + req, err := s.client.newRequest("GET", u, nil) + + if err != nil { + return nil, err + } + + v := &VariableSetVariable{} + err = s.client.do(ctx, req, v) + if err != nil { + return nil, err + } + + return v, err +} + // VariableSetVariableUpdateOptions represents the options for updating a variable. type VariableSetVariableUpdateOptions struct { // Type is a public field utilized by JSON:API to diff --git a/variable_set_variable_test.go b/variable_set_variable_test.go index 3c48e5479..fce872b99 100644 --- a/variable_set_variable_test.go +++ b/variable_set_variable_test.go @@ -201,6 +201,49 @@ func TestVariableSetVariablesCreate(t *testing.T) { }) } +func TestVariableSetVariablesRead(t *testing.T) { + client := testClient(t) + ctx := context.Background() + + orgTest, orgTestCleanup := createOrganization(t, client) + defer orgTestCleanup() + + vsTest, vsTestCleanup := createVariableSet(t, client, orgTest, VariableSetCreateOptions{}) + defer vsTestCleanup() + + vTest, vTestCleanup := createVariableSetVariable(t, client, vsTest, VariableSetVariableCreateOptions{}) + defer vTestCleanup() + + t.Run("when the variable exists", func(t *testing.T) { + v, err := client.VariableSetVariables.Read(ctx, vsTest.ID, vTest.ID) + require.NoError(t, err) + assert.Equal(t, vTest.ID, v.ID) + assert.Equal(t, vTest.Category, v.Category) + assert.Equal(t, vTest.HCL, v.HCL) + assert.Equal(t, vTest.Key, v.Key) + assert.Equal(t, vTest.Sensitive, v.Sensitive) + assert.Equal(t, vTest.Value, v.Value) + }) + + t.Run("when the variable does not exist", func(t *testing.T) { + v, err := client.VariableSetVariables.Read(ctx, vsTest.ID, "nonexisting") + assert.Nil(t, v) + assert.Equal(t, ErrResourceNotFound, err) + }) + + t.Run("without a valid variable set ID", func(t *testing.T) { + v, err := client.VariableSetVariables.Read(ctx, badIdentifier, vTest.ID) + assert.Nil(t, v) + assert.EqualError(t, err, "invalid value for variable set ID") + }) + + t.Run("without a valid variable ID", func(t *testing.T) { + v, err := client.VariableSetVariables.Read(ctx, vsTest.ID, badIdentifier) + assert.Nil(t, v) + assert.EqualError(t, err, "invalid value for variable ID") + }) +} + func TestVariableSetVariablesUpdate(t *testing.T) { client := testClient(t) ctx := context.Background() From e815f86d4877a9b7f6f0e76570c9c4c2847b9f4c Mon Sep 17 00:00:00 2001 From: Rexredinger Date: Tue, 15 Mar 2022 12:36:36 -0400 Subject: [PATCH 13/31] Fix style issue in params --- variable_set_variable.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/variable_set_variable.go b/variable_set_variable.go index 0d2a2e9e4..aa99c61a5 100644 --- a/variable_set_variable.go +++ b/variable_set_variable.go @@ -22,7 +22,7 @@ type VariableSetVariables interface { Create(ctx context.Context, variableSetID string, options *VariableSetVariableCreateOptions) (*VariableSetVariable, error) // Read a variable by its ID - Read(ctx context.Context, variableSetID string, variableId string) (*VariableSetVariable, error) + Read(ctx context.Context, variableSetID string, variableID string) (*VariableSetVariable, error) // Update valuse of an existing variable Update(ctx context.Context, variableSetID string, variableID string, options *VariableSetVariableUpdateOptions) (*VariableSetVariable, error) From b9513fc900166f192a97aa726b941d89ca75bf3e Mon Sep 17 00:00:00 2001 From: Rexredinger Date: Tue, 15 Mar 2022 15:51:03 -0400 Subject: [PATCH 14/31] omitempty for nonessential create params --- variable_set.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/variable_set.go b/variable_set.go index e8c3942ac..d70ebeec3 100644 --- a/variable_set.go +++ b/variable_set.go @@ -113,10 +113,10 @@ type VariableSetCreateOptions struct { Name *string `jsonapi:"attr,name"` // A description to provide context for the variable set. - Description *string `jsonapi:"attr,description"` + Description *string `jsonapi:"attr,description,omitempty"` // If true the variable set is considered in all runs in the organization. - Global *bool `jsonapi:"attr,global"` + Global *bool `jsonapi:"attr,global,omitempty"` } func (o VariableSetCreateOptions) valid() error { From a351fa499a6775fd23b5e84fdf23d10e01907ba5 Mon Sep 17 00:00:00 2001 From: Rexredinger Date: Mon, 21 Mar 2022 11:55:38 -0400 Subject: [PATCH 15/31] fix bad merge --- go.sum | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/go.sum b/go.sum index d00699182..2918378f2 100644 --- a/go.sum +++ b/go.sum @@ -1,7 +1,6 @@ github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -<<<<<<< HEAD github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc= github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs= github.com/google/go-cmp v0.5.2 h1:X2ev0eStA3AbceY54o37/0PQ/UWqKEiiO2dKL5OPaFM= @@ -21,20 +20,6 @@ github.com/hashicorp/go-uuid v1.0.2 h1:cfejS+Tpcp13yd5nYHWDI6qVCny6wyX2Mt5SGur2I github.com/hashicorp/go-uuid v1.0.2/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= github.com/hashicorp/jsonapi v0.0.0-20210826224640-ee7dae0fb22d h1:9ARUJJ1VVynB176G1HCwleORqCaXm/Vx0uUi0dL26I0= github.com/hashicorp/jsonapi v0.0.0-20210826224640-ee7dae0fb22d/go.mod h1:Yog5+CPEM3c99L1CL2CFCYoSzgWm5vTU58idbRUaLik= -github.com/google/go-querystring v1.0.0 h1:Xkwi/a1rcvNg1PPYe5vI8GbeBY/jrVuDX5ASuANWTrk= -github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= -github.com/hashicorp/go-cleanhttp v0.5.0 h1:wvCrVc9TjDls6+YGAF2hAifE1E5U1+b4tH6KdvN3Gig= -github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= -github.com/hashicorp/go-retryablehttp v0.5.2 h1:AoISa4P4IsW0/m4T6St8Yw38gTl5GtBAgfkhYh1xAz4= -github.com/hashicorp/go-retryablehttp v0.5.2/go.mod h1:9B5zBasrRhHXnJnui7y6sL7es7NDiJgTc6Er0maI1Xs= -github.com/hashicorp/go-slug v0.4.1 h1:/jAo8dNuLgSImoLXaX7Od7QB4TfYCVPam+OpAt5bZqc= -github.com/hashicorp/go-slug v0.4.1/go.mod h1:I5tq5Lv0E2xcNXNkmx7BSfzi1PsJ2cNjs3cC3LwyhK8= -github.com/hashicorp/go-slug v0.7.0 h1:8HIi6oreWPtnhpYd8lIGQBgp4rXzDWQTOhfILZm+nok= -github.com/hashicorp/go-slug v0.7.0/go.mod h1:Ib+IWBYfEfJGI1ZyXMGNbu2BU+aa3Dzu41RKLH301v4= -github.com/hashicorp/go-uuid v1.0.1 h1:fv1ep09latC32wFoVwnqcnKJGnMSdBanPczbHAYm1BE= -github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= -github.com/hashicorp/jsonapi v0.0.0-20210518035559-1e50d74c8db3 h1:mzwkutymYIXR5oQT9YnfbLuuw7LZmksiHKRPUTN5ijo= -github.com/hashicorp/jsonapi v0.0.0-20210518035559-1e50d74c8db3/go.mod h1:Yog5+CPEM3c99L1CL2CFCYoSzgWm5vTU58idbRUaLik= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= From e946aaeabcb6ba7013d2806ba1ae49006cb9e38d Mon Sep 17 00:00:00 2001 From: Rexredinger Date: Mon, 21 Mar 2022 13:25:23 -0400 Subject: [PATCH 16/31] Refactor to typed errors --- errors.go | 6 ++++++ variable_set.go | 11 +++++------ variable_set_test.go | 6 +++--- 3 files changed, 14 insertions(+), 9 deletions(-) diff --git a/errors.go b/errors.go index f15501f30..d466518e3 100644 --- a/errors.go +++ b/errors.go @@ -145,6 +145,8 @@ var ( ErrInvalidVariableID = errors.New("invalid value for variable ID") ErrInvalidNotificationTrigger = errors.New("invalid value for notification trigger") + + ErrInvalidVariableSetID = errors.New("invalid variable set ID") ) // Missing values for required field/option @@ -248,4 +250,8 @@ var ( ErrRequiredOnlyOneField = errors.New("only one of usernames or organization membership ids can be provided") ErrRequiredUsernameOrMembershipIds = errors.New("usernames or organization membership ids are required") + + ErrRequiredGlobalFlag = errors.New("global flag is required") + + ErrRequiredWorkspacesList = errors.New("no workspaces list provided") ) diff --git a/variable_set.go b/variable_set.go index d70ebeec3..329b62d26 100644 --- a/variable_set.go +++ b/variable_set.go @@ -2,7 +2,6 @@ package tfe import ( "context" - "errors" "fmt" "net/url" ) @@ -124,7 +123,7 @@ func (o VariableSetCreateOptions) valid() error { return ErrRequiredName } if o.Global == nil { - return errors.New("global flag is required") + return ErrRequiredGlobalFlag } return nil } @@ -162,7 +161,7 @@ type VariableSetReadOptions struct { // Read is used to inspect a given variable set based on ID func (s *variableSets) Read(ctx context.Context, variableSetID string, options *VariableSetReadOptions) (*VariableSet, error) { if !validStringID(&variableSetID) { - return nil, errors.New("invalid variable set ID") + return nil, ErrInvalidVariableSetID } u := fmt.Sprintf("varsets/%s", url.QueryEscape(variableSetID)) @@ -204,7 +203,7 @@ type VariableSetUpdateOptions struct { func (s *variableSets) Update(ctx context.Context, variableSetID string, options *VariableSetUpdateOptions) (*VariableSet, error) { if !validStringID(&variableSetID) { - return nil, errors.New("invalid value for variable set ID") + return nil, ErrInvalidVariableSetID } u := fmt.Sprintf("varsets/%s", url.QueryEscape(variableSetID)) @@ -225,7 +224,7 @@ func (s *variableSets) Update(ctx context.Context, variableSetID string, options // Delete a variable set by its ID. func (s *variableSets) Delete(ctx context.Context, variableSetID string) error { if !validStringID(&variableSetID) { - return errors.New("invalid value for variable set ID") + return ErrInvalidVariableSetID } u := fmt.Sprintf("varsets/%s", url.QueryEscape(variableSetID)) @@ -255,7 +254,7 @@ type VariableSetAssignOptions struct { // Use Update to assign a variable set to workspaces func (s *variableSets) Assign(ctx context.Context, variableSetID string, options *VariableSetAssignOptions) (*VariableSet, error) { if options == nil || options.Workspaces == nil { - return nil, errors.New("no workspaces list provided") + return nil, ErrRequiredWorkspacesList } options.Global = Bool(false) diff --git a/variable_set_test.go b/variable_set_test.go index 1b73c916b..9fd0588c9 100644 --- a/variable_set_test.go +++ b/variable_set_test.go @@ -103,7 +103,7 @@ func TestVariableSetsCreate(t *testing.T) { Name: String("foo"), }) assert.Nil(t, vs) - assert.EqualError(t, err, "global flag is required") + assert.EqualError(t, err, ErrRequiredGlobalFlag.Error()) }) } @@ -165,7 +165,7 @@ func TestVariableSetsUpdate(t *testing.T) { Global: Bool(true), }) assert.Nil(t, vsAfter) - assert.EqualError(t, err, "invalid value for variable set ID") + assert.EqualError(t, err, ErrInvalidVariableSetID.Error()) }) } @@ -189,7 +189,7 @@ func TestVariableSetsDelete(t *testing.T) { t.Run("when ID is invlaid", func(t *testing.T) { err := client.VariableSets.Delete(ctx, badIdentifier) - assert.EqualError(t, err, "invalid value for variable set ID") + assert.EqualError(t, err, ErrInvalidVariableSetID.Error()) }) } From 31b467651e1d27bbb6d65a2499cd32788073723f Mon Sep 17 00:00:00 2001 From: Rexredinger Date: Mon, 21 Mar 2022 14:19:44 -0400 Subject: [PATCH 17/31] Address more error typing and lint fixes --- variable_set_test.go | 5 +---- variable_set_variable.go | 27 +++++++++++++-------------- 2 files changed, 14 insertions(+), 18 deletions(-) diff --git a/variable_set_test.go b/variable_set_test.go index 9fd0588c9..23129c7e2 100644 --- a/variable_set_test.go +++ b/variable_set_test.go @@ -15,9 +15,6 @@ func TestVariableSetsList(t *testing.T) { orgTest, orgTestCleanup := createOrganization(t, client) defer orgTestCleanup() - //wTest, wTestCleanup := createWorkspace(t, client, orgTest) - //defer wTestCleanup() - vsTest1, vsTestCleanup1 := createVariableSet(t, client, orgTest, VariableSetCreateOptions{}) defer vsTestCleanup1() vsTest2, vsTestCleanup2 := createVariableSet(t, client, orgTest, VariableSetCreateOptions{}) @@ -75,7 +72,7 @@ func TestVariableSetsCreate(t *testing.T) { vs, err := client.VariableSets.Create(ctx, orgTest.Name, &options) require.NoError(t, err) - //Get refreshed view from the API + // Get refreshed view from the API refreshed, err := client.VariableSets.Read(ctx, vs.ID, nil) require.NoError(t, err) diff --git a/variable_set_variable.go b/variable_set_variable.go index aa99c61a5..696fa6f80 100644 --- a/variable_set_variable.go +++ b/variable_set_variable.go @@ -2,7 +2,6 @@ package tfe import ( "context" - "errors" "fmt" "net/url" ) @@ -64,7 +63,7 @@ func (o VariableSetVariableListOptions) valid() error { // List all variables associated with the given variable set. func (s *variableSetVariables) List(ctx context.Context, variableSetID string, options *VariableSetVariableListOptions) (*VariableSetVariableList, error) { if !validStringID(&variableSetID) { - return nil, errors.New("invalid value for variable set ID") + return nil, ErrInvalidVariableSetID } if options != nil { if err := options.valid(); err != nil { @@ -116,10 +115,10 @@ type VariableSetVariableCreateOptions struct { func (o VariableSetVariableCreateOptions) valid() error { if !validString(o.Key) { - return errors.New("key is required") + return ErrRequiredKey } if o.Category == nil { - return errors.New("category is required") + return ErrRequiredCategory } return nil } @@ -127,7 +126,7 @@ func (o VariableSetVariableCreateOptions) valid() error { // Create is used to create a new variable. func (s *variableSetVariables) Create(ctx context.Context, variableSetID string, options *VariableSetVariableCreateOptions) (*VariableSetVariable, error) { if !validStringID(&variableSetID) { - return nil, errors.New("invalid value for variable set ID") + return nil, ErrInvalidVariableSetID } if options != nil { if err := options.valid(); err != nil { @@ -151,12 +150,12 @@ func (s *variableSetVariables) Create(ctx context.Context, variableSetID string, } // Read a variable by its ID. -func (s *variableSetVariables) Read(ctx context.Context, variableSetID string, variableID string) (*VariableSetVariable, error) { +func (s *variableSetVariables) Read(ctx context.Context, variableSetID, variableID string) (*VariableSetVariable, error) { if !validStringID(&variableSetID) { - return nil, errors.New("invalid value for variable set ID") + return nil, ErrInvalidVariableSetID } if !validStringID(&variableID) { - return nil, errors.New("invalid value for variable ID") + return nil, ErrInvalidVariableID } u := fmt.Sprintf("varsets/%s/relationships/vars/%s", url.QueryEscape(variableSetID), url.QueryEscape(variableID)) @@ -200,12 +199,12 @@ type VariableSetVariableUpdateOptions struct { } // Update values of an existing variable. -func (s *variableSetVariables) Update(ctx context.Context, variableSetID string, variableID string, options *VariableSetVariableUpdateOptions) (*VariableSetVariable, error) { +func (s *variableSetVariables) Update(ctx context.Context, variableSetID, variableID string, options *VariableSetVariableUpdateOptions) (*VariableSetVariable, error) { if !validStringID(&variableSetID) { - return nil, errors.New("invalid value for variable set ID") + return nil, ErrInvalidVariableSetID } if !validStringID(&variableID) { - return nil, errors.New("invalid value for variable ID") + return nil, ErrInvalidVariableID } u := fmt.Sprintf("varsets/%s/relationships/vars/%s", url.QueryEscape(variableSetID), url.QueryEscape(variableID)) @@ -224,12 +223,12 @@ func (s *variableSetVariables) Update(ctx context.Context, variableSetID string, } // Delete a variable by its ID. -func (s *variableSetVariables) Delete(ctx context.Context, variableSetID string, variableID string) error { +func (s *variableSetVariables) Delete(ctx context.Context, variableSetID, variableID string) error { if !validStringID(&variableSetID) { - return errors.New("invalid value for variable set ID") + return ErrInvalidVariableSetID } if !validStringID(&variableID) { - return errors.New("invalid value for variable ID") + return ErrInvalidVariableID } u := fmt.Sprintf("varsets/%s/relationships/vars/%s", url.QueryEscape(variableSetID), url.QueryEscape(variableID)) From ddf76d7152310a2c551c38df2cd166bc467a6bba Mon Sep 17 00:00:00 2001 From: Rexredinger Date: Mon, 21 Mar 2022 15:22:41 -0400 Subject: [PATCH 18/31] somehow I thought I already committed this --- variable_set_variable_test.go | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/variable_set_variable_test.go b/variable_set_variable_test.go index fce872b99..4d09e2cd8 100644 --- a/variable_set_variable_test.go +++ b/variable_set_variable_test.go @@ -61,7 +61,7 @@ func TestVariableSetVariablesList(t *testing.T) { t.Run("when variable set ID is invalid ID", func(t *testing.T) { vl, err := client.VariableSetVariables.List(ctx, badIdentifier, nil) assert.Nil(t, vl) - assert.EqualError(t, err, "invalid value for variable set ID") + assert.EqualError(t, err, ErrInvalidVariableSetID.Error()) }) } @@ -165,7 +165,7 @@ func TestVariableSetVariablesCreate(t *testing.T) { } _, err := client.VariableSetVariables.Create(ctx, vsTest.ID, &options) - assert.EqualError(t, err, "key is required") + assert.EqualError(t, err, ErrRequiredKey.Error()) }) t.Run("when options has an empty key", func(t *testing.T) { @@ -176,7 +176,7 @@ func TestVariableSetVariablesCreate(t *testing.T) { } _, err := client.VariableSetVariables.Create(ctx, vsTest.ID, &options) - assert.EqualError(t, err, "key is required") + assert.EqualError(t, err, ErrRequiredKey.Error()) }) t.Run("when options is missing category", func(t *testing.T) { @@ -186,7 +186,7 @@ func TestVariableSetVariablesCreate(t *testing.T) { } _, err := client.VariableSetVariables.Create(ctx, vsTest.ID, &options) - assert.EqualError(t, err, "category is required") + assert.EqualError(t, err, ErrRequiredCategory.Error()) }) t.Run("when workspace ID is invalid", func(t *testing.T) { @@ -197,7 +197,7 @@ func TestVariableSetVariablesCreate(t *testing.T) { } _, err := client.VariableSetVariables.Create(ctx, badIdentifier, &options) - assert.EqualError(t, err, "invalid value for variable set ID") + assert.EqualError(t, err, ErrInvalidVariableSetID.Error()) }) } @@ -234,13 +234,13 @@ func TestVariableSetVariablesRead(t *testing.T) { t.Run("without a valid variable set ID", func(t *testing.T) { v, err := client.VariableSetVariables.Read(ctx, badIdentifier, vTest.ID) assert.Nil(t, v) - assert.EqualError(t, err, "invalid value for variable set ID") + assert.EqualError(t, err, ErrInvalidVariableSetID.Error()) }) t.Run("without a valid variable ID", func(t *testing.T) { v, err := client.VariableSetVariables.Read(ctx, vsTest.ID, badIdentifier) assert.Nil(t, v) - assert.EqualError(t, err, "invalid value for variable ID") + assert.EqualError(t, err, ErrInvalidVariableID.Error()) }) } @@ -314,12 +314,12 @@ func TestVariableSetVariablesUpdate(t *testing.T) { t.Run("with invalid variable ID", func(t *testing.T) { _, err := client.VariableSetVariables.Update(ctx, badIdentifier, vTest.ID, nil) - assert.EqualError(t, err, "invalid value for variable set ID") + assert.EqualError(t, err, ErrInvalidVariableSetID.Error()) }) t.Run("with invalid variable ID", func(t *testing.T) { _, err := client.VariableSetVariables.Update(ctx, vsTest.ID, badIdentifier, nil) - assert.EqualError(t, err, "invalid value for variable ID") + assert.EqualError(t, err, ErrInvalidVariableSetID.Error()) }) } @@ -339,16 +339,16 @@ func TestVariableSetVariablesDelete(t *testing.T) { t.Run("with non existing variable ID", func(t *testing.T) { err := client.VariableSetVariables.Delete(ctx, vsTest.ID, "nonexisting") - assert.Equal(t, err, ErrResourceNotFound) + assert.Equal(t, err, ErrResourceNotFound.Error()) }) t.Run("with invalid workspace ID", func(t *testing.T) { err := client.VariableSetVariables.Delete(ctx, badIdentifier, vTest.ID) - assert.EqualError(t, err, "invalid value for variable set ID") + assert.EqualError(t, err, ErrInvalidVariableSetID.Error()) }) t.Run("with invalid variable ID", func(t *testing.T) { err := client.VariableSetVariables.Delete(ctx, vsTest.ID, badIdentifier) - assert.EqualError(t, err, "invalid value for variable ID") + assert.EqualError(t, err, ErrInvalidVariableID.Error()) }) } From 566ce6fb9ae510a1ed58714db2de98907887184b Mon Sep 17 00:00:00 2001 From: Rexredinger Date: Mon, 21 Mar 2022 16:07:48 -0400 Subject: [PATCH 19/31] variable set variable test fix --- variable_set_variable_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/variable_set_variable_test.go b/variable_set_variable_test.go index 4d09e2cd8..3c4698f20 100644 --- a/variable_set_variable_test.go +++ b/variable_set_variable_test.go @@ -319,7 +319,7 @@ func TestVariableSetVariablesUpdate(t *testing.T) { t.Run("with invalid variable ID", func(t *testing.T) { _, err := client.VariableSetVariables.Update(ctx, vsTest.ID, badIdentifier, nil) - assert.EqualError(t, err, ErrInvalidVariableSetID.Error()) + assert.EqualError(t, err, ErrInvalidVariableID.Error()) }) } @@ -339,7 +339,7 @@ func TestVariableSetVariablesDelete(t *testing.T) { t.Run("with non existing variable ID", func(t *testing.T) { err := client.VariableSetVariables.Delete(ctx, vsTest.ID, "nonexisting") - assert.Equal(t, err, ErrResourceNotFound.Error()) + assert.EqualError(t, err, ErrResourceNotFound.Error()) }) t.Run("with invalid workspace ID", func(t *testing.T) { From 30c245e12683c5b3f388f4be36049c6b21a8329d Mon Sep 17 00:00:00 2001 From: Rexredinger Date: Mon, 21 Mar 2022 16:51:38 -0400 Subject: [PATCH 20/31] Remove latin that go lint hates --- variable_integration_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/variable_integration_test.go b/variable_integration_test.go index 1393c0a9c..10b507a5c 100644 --- a/variable_integration_test.go +++ b/variable_integration_test.go @@ -128,7 +128,7 @@ func TestVariablesCreate(t *testing.T) { options := VariableCreateOptions{ Key: String(randomString(t)), Value: String(randomString(t)), - Description: String("tortor aliquam nulla facilisi cras fermentum odio eu feugiat pretium nibh ipsum consequat nisl vel pretium lectus quam id leo in vitae turpis massa sed elementum tempus egestas sed sed risus pretium quam vulputate dignissim suspendisse in est ante in nibh mauris cursus mattis molestie a iaculis at erat pellentesque adipiscing commodo elit at imperdiet dui accumsan sit amet nulla facilisi morbi tempus iaculis urna id volutpat lacus laoreet non curabitur gravida arcu ac tortor dignissim convallis aenean et tortor"), + Description: String("tortor aliquam nulla go lint is fussy about spelling cras fermentum odio eu feugiat pretium nibh ipsum consequat nisl vel pretium lectus quam id leo in vitae turpis massa sed elementum tempus egestas sed sed risus pretium quam vulputate dignissim suspendisse in est ante in nibh mauris cursus mattis molestie a iaculis at erat pellentesque adipiscing commodo elit at imperdiet dui accumsan sit amet nulla facilisi morbi tempus iaculis urna id volutpat lacus laoreet non curabitur gravida arcu ac tortor dignissim convallis aenean et tortor"), Category: Category(CategoryTerraform), } From d7c7b8c3af3d72be113f948d5e365403ac7d4d0f Mon Sep 17 00:00:00 2001 From: Rexredinger Date: Tue, 22 Mar 2022 16:32:04 -0400 Subject: [PATCH 21/31] Update Assign documentation to be clearer --- variable_set.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/variable_set.go b/variable_set.go index 329b62d26..cc2bbf325 100644 --- a/variable_set.go +++ b/variable_set.go @@ -29,7 +29,7 @@ type VariableSets interface { // Delete a variable set by ID. Delete(ctx context.Context, variableSetID string) error - // Assign a variable set to workspaces + // Add and remove workspace assignments to match the supplied list Assign(ctx context.Context, variableSetID string, options *VariableSetAssignOptions) (*VariableSet, error) } @@ -251,7 +251,7 @@ type VariableSetAssignOptions struct { Workspaces []*Workspace `jsonapi:"relation,workspaces"` } -// Use Update to assign a variable set to workspaces +// Update variable set assignments to match the supplied workspaces list. func (s *variableSets) Assign(ctx context.Context, variableSetID string, options *VariableSetAssignOptions) (*VariableSet, error) { if options == nil || options.Workspaces == nil { return nil, ErrRequiredWorkspacesList From fa9ea31efaabda5c57a82d755e939fe22fddd4f9 Mon Sep 17 00:00:00 2001 From: Rexredinger Date: Tue, 22 Mar 2022 16:34:53 -0400 Subject: [PATCH 22/31] remove more latin from the lint trap --- variable_integration_test.go | 2 +- variable_set_variable_test.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/variable_integration_test.go b/variable_integration_test.go index 10b507a5c..90c0b5722 100644 --- a/variable_integration_test.go +++ b/variable_integration_test.go @@ -128,7 +128,7 @@ func TestVariablesCreate(t *testing.T) { options := VariableCreateOptions{ Key: String(randomString(t)), Value: String(randomString(t)), - Description: String("tortor aliquam nulla go lint is fussy about spelling cras fermentum odio eu feugiat pretium nibh ipsum consequat nisl vel pretium lectus quam id leo in vitae turpis massa sed elementum tempus egestas sed sed risus pretium quam vulputate dignissim suspendisse in est ante in nibh mauris cursus mattis molestie a iaculis at erat pellentesque adipiscing commodo elit at imperdiet dui accumsan sit amet nulla facilisi morbi tempus iaculis urna id volutpat lacus laoreet non curabitur gravida arcu ac tortor dignissim convallis aenean et tortor"), + Description: String("tortor aliquam nulla go lint is fussy about spelling cras fermentum odio eu feugiat pretium nibh ipsum consequat nisl vel pretium lectus quam id leo in vitae turpis massa sed elementum tempus egestas sed sed risus pretium quam vulputate dignissim suspendisse in est ante in nibh mauris cursus mattis molestie a iaculis at erat pellentesque adipiscing commodo elit at imperdiet dui accumsan sit amet nulla redacted morbi tempus iaculis urna id volutpat lacus laoreet non curabitur gravida arcu ac tortor dignissim convallis aenean et tortor"), Category: Category(CategoryTerraform), } diff --git a/variable_set_variable_test.go b/variable_set_variable_test.go index 3c4698f20..cc2107103 100644 --- a/variable_set_variable_test.go +++ b/variable_set_variable_test.go @@ -135,7 +135,7 @@ func TestVariableSetVariablesCreate(t *testing.T) { options := VariableSetVariableCreateOptions{ Key: String(randomString(t)), Value: String(randomString(t)), - Description: String("tortor aliquam nulla facilisi cras fermentum odio eu feugiat pretium nibh ipsum consequat nisl vel pretium lectus quam id leo in vitae turpis massa sed elementum tempus egestas sed sed risus pretium quam vulputate dignissim suspendisse in est ante in nibh mauris cursus mattis molestie a iaculis at erat pellentesque adipiscing commodo elit at imperdiet dui accumsan sit amet nulla facilisi morbi tempus iaculis urna id volutpat lacus laoreet non curabitur gravida arcu ac tortor dignissim convallis aenean et tortor"), + Description: String("tortor aliquam nulla redacted cras fermentum odio eu feugiat pretium nibh ipsum consequat nisl vel pretium lectus quam id leo in vitae turpis massa sed elementum tempus egestas sed sed risus pretium quam vulputate dignissim suspendisse in est ante in nibh mauris cursus mattis molestie a iaculis at erat pellentesque adipiscing commodo elit at imperdiet dui accumsan sit amet nulla redacted morbi tempus iaculis urna id volutpat lacus laoreet non curabitur gravida arcu ac tortor dignissim convallis aenean et tortor"), Category: Category(CategoryTerraform), } From 2ca2ccf0d2b1e9d6a1708e0ca5b9bb4918a2928a Mon Sep 17 00:00:00 2001 From: Rexredinger Date: Tue, 22 Mar 2022 17:10:14 -0400 Subject: [PATCH 23/31] Bring include opt usage inline with other resources --- variable_set.go | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/variable_set.go b/variable_set.go index cc2bbf325..1e92345b7 100644 --- a/variable_set.go +++ b/variable_set.go @@ -56,11 +56,11 @@ type VariableSet struct { // A list of relations to include. See available resources // https://www.terraform.io/docs/cloud/api/admin/organizations.html#available-related-resources -type VariableSetIncludeOps string +type VariableSetIncludeOpt string const ( - VariableSetWorkspaces VariableSetIncludeOps = "workspaces" - VariableSetVars VariableSetIncludeOps = "vars" + VariableSetWorkspaces VariableSetIncludeOpt = "workspaces" + VariableSetVars VariableSetIncludeOpt = "vars" ) type VariableSetListOptions struct { @@ -155,7 +155,7 @@ func (s *variableSets) Create(ctx context.Context, organization string, options } type VariableSetReadOptions struct { - Include *[]VariableSetIncludeOps `url:"include:omitempty"` + Include *[]VariableSetIncludeOpt `url:"include:omitempty"` } // Read is used to inspect a given variable set based on ID @@ -190,15 +190,13 @@ type VariableSetUpdateOptions struct { // The name of the variable set. // Affects variable precedence when there are conflicts between Variable Sets // https://www.terraform.io/cloud-docs/api-docs/variable-sets#apply-variable-set-to-workspaces - Name *string `jsonapi:"attr,name"` + Name *string `jsonapi:"attr,name,omitempty"` // A description to provide context for the variable set. Description *string `jsonapi:"attr,description,omitempty"` // If true the variable set is considered in all runs in the organization. Global *bool `jsonapi:"attr,global,omitempty"` - - Include *[]VariableSetIncludeOps `url:"include:omitempty"` } func (s *variableSets) Update(ctx context.Context, variableSetID string, options *VariableSetUpdateOptions) (*VariableSet, error) { @@ -246,9 +244,6 @@ type VariableSetAssignOptions struct { // Used to set the variable set from Global to not Global if necessary Global *bool `jsonapi:"attr,global"` - - // The workspaces to be assigned to. An empty set means remove all assignments - Workspaces []*Workspace `jsonapi:"relation,workspaces"` } // Update variable set assignments to match the supplied workspaces list. From 6ab16fd26a6888a193dc1851a055bec728377c95 Mon Sep 17 00:00:00 2001 From: Rexredinger Date: Tue, 22 Mar 2022 17:10:43 -0400 Subject: [PATCH 24/31] fix missing url.QueryEscape --- variable_set_variable.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/variable_set_variable.go b/variable_set_variable.go index 696fa6f80..dea5835ff 100644 --- a/variable_set_variable.go +++ b/variable_set_variable.go @@ -71,7 +71,7 @@ func (s *variableSetVariables) List(ctx context.Context, variableSetID string, o } } - u := fmt.Sprintf("varsets/%s/relationships/vars", variableSetID) + u := fmt.Sprintf("varsets/%s/relationships/vars", url.QueryEscape(variableSetID)) req, err := s.client.newRequest("GET", u, options) if err != nil { return nil, err From 7ef83ee228a90529ac37e73002ab2f65b58b3ff9 Mon Sep 17 00:00:00 2001 From: Rexredinger Date: Tue, 22 Mar 2022 17:22:09 -0400 Subject: [PATCH 25/31] Put that back. Can you tell I'm not firing on all cylinders --- variable_set.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/variable_set.go b/variable_set.go index 1e92345b7..d4b50feb0 100644 --- a/variable_set.go +++ b/variable_set.go @@ -244,6 +244,9 @@ type VariableSetAssignOptions struct { // Used to set the variable set from Global to not Global if necessary Global *bool `jsonapi:"attr,global"` + + // The workspaces to be assigned to. An empty set means remove all assignments + Workspaces []*Workspace `jsonapi:"relation,workspaces"` } // Update variable set assignments to match the supplied workspaces list. From 6bd3eadfb42e188d41be39af5ff891390392965d Mon Sep 17 00:00:00 2001 From: Rexredinger Date: Tue, 22 Mar 2022 17:32:31 -0400 Subject: [PATCH 26/31] _Apply_ variable sets --- variable_set.go | 14 +++++++------- variable_set_test.go | 10 +++++----- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/variable_set.go b/variable_set.go index d4b50feb0..b2415501d 100644 --- a/variable_set.go +++ b/variable_set.go @@ -29,8 +29,8 @@ type VariableSets interface { // Delete a variable set by ID. Delete(ctx context.Context, variableSetID string) error - // Add and remove workspace assignments to match the supplied list - Assign(ctx context.Context, variableSetID string, options *VariableSetAssignOptions) (*VariableSet, error) + // Update list of workspaces to which the variable set is applied to match the supplied list + Apply(ctx context.Context, variableSetID string, options *VariableSetApplyOptions) (*VariableSet, error) } type variableSets struct { @@ -234,8 +234,8 @@ func (s *variableSets) Delete(ctx context.Context, variableSetID string) error { return s.client.do(ctx, req, nil) } -// VariableSetAssignOptions represents a subset of update options specifically for assigning variable sets to workspaces -type VariableSetAssignOptions struct { +// VariableSetApplyOptions represents a subset of update options specifically for applying variable sets to workspaces +type VariableSetAapplyOptions struct { // Type is a public field utilized by JSON:API to // set the resource type via the field tag. // It is not a user-defined value and does not need to be set. @@ -245,12 +245,12 @@ type VariableSetAssignOptions struct { // Used to set the variable set from Global to not Global if necessary Global *bool `jsonapi:"attr,global"` - // The workspaces to be assigned to. An empty set means remove all assignments + // The workspaces to be applied to. An empty set means remove all applied Workspaces []*Workspace `jsonapi:"relation,workspaces"` } -// Update variable set assignments to match the supplied workspaces list. -func (s *variableSets) Assign(ctx context.Context, variableSetID string, options *VariableSetAssignOptions) (*VariableSet, error) { +// Update variable set to be applied to only the workspaces in the supplied list. +func (s *variableSets) Apply(ctx context.Context, variableSetID string, options *VariableSetApplyOptions) (*VariableSet, error) { if options == nil || options.Workspaces == nil { return nil, ErrRequiredWorkspacesList } diff --git a/variable_set_test.go b/variable_set_test.go index 23129c7e2..d57363ef0 100644 --- a/variable_set_test.go +++ b/variable_set_test.go @@ -190,7 +190,7 @@ func TestVariableSetsDelete(t *testing.T) { }) } -func TestVariableSetsAssign(t *testing.T) { +func TestVariableSetsApply(t *testing.T) { client := testClient(t) ctx := context.Background() @@ -202,21 +202,21 @@ func TestVariableSetsAssign(t *testing.T) { wTest, _ := createWorkspace(t, client, orgTest) t.Run("with valid workspaces", func(t *testing.T) { - options := VariableSetAssignOptions{ + options := VariableSetApplyOptions{ Workspaces: []*Workspace{wTest}, } - vsAfter, err := client.VariableSets.Assign(ctx, vsTest.ID, &options) + vsAfter, err := client.VariableSets.Apply(ctx, vsTest.ID, &options) require.NoError(t, err) assert.Equal(t, len(options.Workspaces), len(vsAfter.Workspaces)) assert.Equal(t, options.Workspaces[0].ID, vsAfter.Workspaces[0].ID) - options = VariableSetAssignOptions{ + options = VariableSetApplyOptions{ Workspaces: []*Workspace{}, } - vsAfter, err = client.VariableSets.Assign(ctx, vsTest.ID, &options) + vsAfter, err = client.VariableSets.Apply(ctx, vsTest.ID, &options) require.NoError(t, err) assert.Equal(t, len(options.Workspaces), len(vsAfter.Workspaces)) From 20889e373e12cd0f20c35d7df3a37cfb711c7d70 Mon Sep 17 00:00:00 2001 From: Rexredinger Date: Tue, 22 Mar 2022 17:38:11 -0400 Subject: [PATCH 27/31] typo --- variable_set.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/variable_set.go b/variable_set.go index b2415501d..9412474cf 100644 --- a/variable_set.go +++ b/variable_set.go @@ -235,7 +235,7 @@ func (s *variableSets) Delete(ctx context.Context, variableSetID string) error { } // VariableSetApplyOptions represents a subset of update options specifically for applying variable sets to workspaces -type VariableSetAapplyOptions struct { +type VariableSetApplyOptions struct { // Type is a public field utilized by JSON:API to // set the resource type via the field tag. // It is not a user-defined value and does not need to be set. From 8ca362fc05f5a072909318255bada7b2cbd2f223 Mon Sep 17 00:00:00 2001 From: Rexredinger Date: Wed, 23 Mar 2022 18:12:53 -0400 Subject: [PATCH 28/31] Rename, use pointers, validate, hide details --- variable_set.go | 55 ++++++++++++++++++++++++++++---------------- variable_set_test.go | 10 ++++---- 2 files changed, 40 insertions(+), 25 deletions(-) diff --git a/variable_set.go b/variable_set.go index 9412474cf..9eefd9879 100644 --- a/variable_set.go +++ b/variable_set.go @@ -30,7 +30,7 @@ type VariableSets interface { Delete(ctx context.Context, variableSetID string) error // Update list of workspaces to which the variable set is applied to match the supplied list - Apply(ctx context.Context, variableSetID string, options *VariableSetApplyOptions) (*VariableSet, error) + UpdateWorkspaces(ctx context.Context, variableSetID string, options *VariableSetUpdateWorkspacesOptions) (*VariableSet, error) } type variableSets struct { @@ -68,7 +68,7 @@ type VariableSetListOptions struct { Include string `url:"include"` } -func (o VariableSetListOptions) valid() error { +func (o *VariableSetListOptions) valid() error { return nil } @@ -104,7 +104,7 @@ type VariableSetCreateOptions struct { // set the resource type via the field tag. // It is not a user-defined value and does not need to be set. // https://jsonapi.org/format/#crud-creating - Type string `jsonapi:"primary,vars"` + Type string `jsonapi:"primary,varsets"` // The name of the variable set. // Affects variable precedence when there are conflicts between Variable Sets @@ -118,7 +118,10 @@ type VariableSetCreateOptions struct { Global *bool `jsonapi:"attr,global,omitempty"` } -func (o VariableSetCreateOptions) valid() error { +func (o *VariableSetCreateOptions) valid() error { + if o == nil { + return nil + } if !validString(o.Name) { return ErrRequiredName } @@ -133,10 +136,8 @@ func (s *variableSets) Create(ctx context.Context, organization string, options if !validStringID(&organization) { return nil, ErrInvalidOrg } - if options != nil { - if err := options.valid(); err != nil { - return nil, err - } + if err := options.valid(); err != nil { + return nil, err } u := fmt.Sprintf("organizations/%s/varsets", url.QueryEscape(organization)) @@ -185,7 +186,7 @@ type VariableSetUpdateOptions struct { // set the resource type via the field tag. // It is not a user-defined value and does not need to be set. // https://jsonapi.org/format/#crud-creating - Type string `jsonapi:"primary,vars"` + Type string `jsonapi:"primary,varsets"` // The name of the variable set. // Affects variable precedence when there are conflicts between Variable Sets @@ -234,32 +235,46 @@ func (s *variableSets) Delete(ctx context.Context, variableSetID string) error { return s.client.do(ctx, req, nil) } -// VariableSetApplyOptions represents a subset of update options specifically for applying variable sets to workspaces -type VariableSetApplyOptions struct { +// VariableSetUpdateWorkspacesOptions represents a subset of update options specifically for applying variable sets to workspaces +type VariableSetUpdateWorkspacesOptions struct { // Type is a public field utilized by JSON:API to // set the resource type via the field tag. // It is not a user-defined value and does not need to be set. // https://jsonapi.org/format/#crud-creating - Type string `jsonapi:"primary,vars"` - - // Used to set the variable set from Global to not Global if necessary - Global *bool `jsonapi:"attr,global"` + Type string `jsonapi:"primary,varsets"` // The workspaces to be applied to. An empty set means remove all applied Workspaces []*Workspace `jsonapi:"relation,workspaces"` } +func (o *VariableSetUpdateWorkspacesOptions) valid() error { + if o == nil || o.Workspaces == nil { + return ErrRequiredWorkspacesList + } + return nil +} + +type privateVariableSetUpdateWorkspacesOptions struct { + Type string `jsonapi:"primary,varsets"` + Global bool `jsonapi:"attr,global"` + Workspaces []*Workspace `jsonapi:"relation,workspaces"` +} + // Update variable set to be applied to only the workspaces in the supplied list. -func (s *variableSets) Apply(ctx context.Context, variableSetID string, options *VariableSetApplyOptions) (*VariableSet, error) { - if options == nil || options.Workspaces == nil { - return nil, ErrRequiredWorkspacesList +func (s *variableSets) UpdateWorkspaces(ctx context.Context, variableSetID string, options *VariableSetUpdateWorkspacesOptions) (*VariableSet, error) { + if err := options.valid(); err != nil { + return nil, err } - options.Global = Bool(false) + // Use private strcut to ensure global is set to false when applying to workspaces + o := privateVariableSetUpdateWorkspacesOptions{ + Global: bool(false), + Workspaces: options.Workspaces, + } // We force inclusion of workspaces as that is the primary data for which we are concerned with confirming changes. u := fmt.Sprintf("varsets/%s?include=%s", url.QueryEscape(variableSetID), VariableSetWorkspaces) - req, err := s.client.newRequest("PATCH", u, options) + req, err := s.client.newRequest("PATCH", u, &o) if err != nil { return nil, err } diff --git a/variable_set_test.go b/variable_set_test.go index d57363ef0..0bc41846b 100644 --- a/variable_set_test.go +++ b/variable_set_test.go @@ -190,7 +190,7 @@ func TestVariableSetsDelete(t *testing.T) { }) } -func TestVariableSetsApply(t *testing.T) { +func TestVariableSetsUpdateWorkspaces(t *testing.T) { client := testClient(t) ctx := context.Background() @@ -202,21 +202,21 @@ func TestVariableSetsApply(t *testing.T) { wTest, _ := createWorkspace(t, client, orgTest) t.Run("with valid workspaces", func(t *testing.T) { - options := VariableSetApplyOptions{ + options := VariableSetUpdateWorkspacesOptions{ Workspaces: []*Workspace{wTest}, } - vsAfter, err := client.VariableSets.Apply(ctx, vsTest.ID, &options) + vsAfter, err := client.VariableSets.UpdateWorkspaces(ctx, vsTest.ID, &options) require.NoError(t, err) assert.Equal(t, len(options.Workspaces), len(vsAfter.Workspaces)) assert.Equal(t, options.Workspaces[0].ID, vsAfter.Workspaces[0].ID) - options = VariableSetApplyOptions{ + options = VariableSetUpdateWorkspacesOptions{ Workspaces: []*Workspace{}, } - vsAfter, err = client.VariableSets.Apply(ctx, vsTest.ID, &options) + vsAfter, err = client.VariableSets.UpdateWorkspaces(ctx, vsTest.ID, &options) require.NoError(t, err) assert.Equal(t, len(options.Workspaces), len(vsAfter.Workspaces)) From 5bb1a6c3927686bc68c4b983b57ecd39294ae6f6 Mon Sep 17 00:00:00 2001 From: Alexander Redinger Date: Thu, 24 Mar 2022 14:00:04 -0400 Subject: [PATCH 29/31] spelling~ Co-authored-by: Nick Fagerlund --- variable_set_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/variable_set_test.go b/variable_set_test.go index 0bc41846b..f2b264a0d 100644 --- a/variable_set_test.go +++ b/variable_set_test.go @@ -135,7 +135,7 @@ func TestVariableSetsUpdate(t *testing.T) { defer orgTestCleanup() vsTest, _ := createVariableSet(t, client, orgTest, VariableSetCreateOptions{ - Name: String("OrinigalName"), + Name: String("OriginalName"), Description: String("Original Description"), Global: Bool(false), }) From c67cf9b52187856aa6d0f5b034cf02ab731d224a Mon Sep 17 00:00:00 2001 From: Alexander Redinger Date: Thu, 24 Mar 2022 14:00:13 -0400 Subject: [PATCH 30/31] spelling~ Co-authored-by: Nick Fagerlund --- variable_set_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/variable_set_test.go b/variable_set_test.go index f2b264a0d..0290661bc 100644 --- a/variable_set_test.go +++ b/variable_set_test.go @@ -184,7 +184,7 @@ func TestVariableSetsDelete(t *testing.T) { assert.Equal(t, ErrResourceNotFound, err) }) - t.Run("when ID is invlaid", func(t *testing.T) { + t.Run("when ID is invalid", func(t *testing.T) { err := client.VariableSets.Delete(ctx, badIdentifier) assert.EqualError(t, err, ErrInvalidVariableSetID.Error()) }) From 9188b96e46464efbb195277860004891f1ef2272 Mon Sep 17 00:00:00 2001 From: Alexander Redinger Date: Thu, 24 Mar 2022 14:00:25 -0400 Subject: [PATCH 31/31] spelling~ Co-authored-by: Nick Fagerlund --- variable_set.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/variable_set.go b/variable_set.go index 9eefd9879..1e2e438f2 100644 --- a/variable_set.go +++ b/variable_set.go @@ -266,7 +266,7 @@ func (s *variableSets) UpdateWorkspaces(ctx context.Context, variableSetID strin return nil, err } - // Use private strcut to ensure global is set to false when applying to workspaces + // Use private struct to ensure global is set to false when applying to workspaces o := privateVariableSetUpdateWorkspacesOptions{ Global: bool(false), Workspaces: options.Workspaces,