diff --git a/generate_mocks.sh b/generate_mocks.sh index e2beaf1d3..8389cc14c 100755 --- a/generate_mocks.sh +++ b/generate_mocks.sh @@ -61,3 +61,4 @@ mockgen -source=variable_set_variable.go -destination=mocks/variable_set_variabl mockgen -source=workspace.go -destination=mocks/workspace_mocks.go -package=mocks mockgen -source=workspace_run_task.go -destination=mocks/workspace_run_tasks_mocks.go -package=mocks mockgen -source=agent.go -destination=mocks/agents.go -package=mocks +mockgen -source=projects.go -destination=mocks/projects.go -package=mocks diff --git a/mocks/organization_mocks.go b/mocks/organization_mocks.go index 87361eed7..6df851100 100644 --- a/mocks/organization_mocks.go +++ b/mocks/organization_mocks.go @@ -139,6 +139,21 @@ func (mr *MockOrganizationsMockRecorder) ReadRunQueue(ctx, organization, options return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ReadRunQueue", reflect.TypeOf((*MockOrganizations)(nil).ReadRunQueue), ctx, organization, options) } +// ReadWithOptions mocks base method. +func (m *MockOrganizations) ReadWithOptions(ctx context.Context, organization string, options tfe.OrganizationReadOptions) (*tfe.Organization, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "ReadWithOptions", ctx, organization, options) + ret0, _ := ret[0].(*tfe.Organization) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// ReadWithOptions indicates an expected call of ReadWithOptions. +func (mr *MockOrganizationsMockRecorder) ReadWithOptions(ctx, organization, options interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ReadWithOptions", reflect.TypeOf((*MockOrganizations)(nil).ReadWithOptions), ctx, organization, options) +} + // Update mocks base method. func (m *MockOrganizations) Update(ctx context.Context, organization string, options tfe.OrganizationUpdateOptions) (*tfe.Organization, error) { m.ctrl.T.Helper() diff --git a/mocks/projects.go b/mocks/projects.go new file mode 100644 index 000000000..17bdee4e4 --- /dev/null +++ b/mocks/projects.go @@ -0,0 +1,110 @@ +// Code generated by MockGen. DO NOT EDIT. +// Source: projects.go + +// Package mocks is a generated GoMock package. +package mocks + +import ( + context "context" + reflect "reflect" + + gomock "github.com/golang/mock/gomock" + tfe "github.com/hashicorp/go-tfe" +) + +// MockProjects is a mock of Projects interface. +type MockProjects struct { + ctrl *gomock.Controller + recorder *MockProjectsMockRecorder +} + +// MockProjectsMockRecorder is the mock recorder for MockProjects. +type MockProjectsMockRecorder struct { + mock *MockProjects +} + +// NewMockProjects creates a new mock instance. +func NewMockProjects(ctrl *gomock.Controller) *MockProjects { + mock := &MockProjects{ctrl: ctrl} + mock.recorder = &MockProjectsMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockProjects) EXPECT() *MockProjectsMockRecorder { + return m.recorder +} + +// Create mocks base method. +func (m *MockProjects) Create(ctx context.Context, organization string, options tfe.ProjectCreateOptions) (*tfe.Project, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Create", ctx, organization, options) + ret0, _ := ret[0].(*tfe.Project) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// Create indicates an expected call of Create. +func (mr *MockProjectsMockRecorder) Create(ctx, organization, options interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Create", reflect.TypeOf((*MockProjects)(nil).Create), ctx, organization, options) +} + +// Delete mocks base method. +func (m *MockProjects) Delete(ctx context.Context, ProjectID string) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Delete", ctx, ProjectID) + ret0, _ := ret[0].(error) + return ret0 +} + +// Delete indicates an expected call of Delete. +func (mr *MockProjectsMockRecorder) Delete(ctx, ProjectID interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Delete", reflect.TypeOf((*MockProjects)(nil).Delete), ctx, ProjectID) +} + +// List mocks base method. +func (m *MockProjects) List(ctx context.Context, organization string, options *tfe.ProjectListOptions) (*tfe.ProjectList, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "List", ctx, organization, options) + ret0, _ := ret[0].(*tfe.ProjectList) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// List indicates an expected call of List. +func (mr *MockProjectsMockRecorder) List(ctx, organization, options interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "List", reflect.TypeOf((*MockProjects)(nil).List), ctx, organization, options) +} + +// Read mocks base method. +func (m *MockProjects) Read(ctx context.Context, ProjectID string) (*tfe.Project, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Read", ctx, ProjectID) + ret0, _ := ret[0].(*tfe.Project) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// Read indicates an expected call of Read. +func (mr *MockProjectsMockRecorder) Read(ctx, ProjectID interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Read", reflect.TypeOf((*MockProjects)(nil).Read), ctx, ProjectID) +} + +// Update mocks base method. +func (m *MockProjects) Update(ctx context.Context, ProjectID string, options tfe.ProjectUpdateOptions) (*tfe.Project, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Update", ctx, ProjectID, options) + ret0, _ := ret[0].(*tfe.Project) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// Update indicates an expected call of Update. +func (mr *MockProjectsMockRecorder) Update(ctx, ProjectID, options interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Update", reflect.TypeOf((*MockProjects)(nil).Update), ctx, ProjectID, options) +} diff --git a/projects_integration_test.go b/projects_integration_test.go index 8bc4ebe48..4feeb092f 100644 --- a/projects_integration_test.go +++ b/projects_integration_test.go @@ -191,8 +191,7 @@ func TestProjectsDelete(t *testing.T) { orgTest, orgTestCleanup := createOrganization(t, client) defer orgTestCleanup() - pTest, pTestCleanup := createProject(t, client, orgTest) - defer pTestCleanup() + pTest, _ := createProject(t, client, orgTest) t.Run("with valid options", func(t *testing.T) { err := client.Projects.Delete(ctx, pTest.ID) diff --git a/workspace.go b/workspace.go index 092547df2..f94f4aa72 100644 --- a/workspace.go +++ b/workspace.go @@ -363,6 +363,10 @@ type WorkspaceCreateOptions struct { // A list of tags to attach to the workspace. If the tag does not already // exist, it is created and added to the workspace. Tags []*Tag `jsonapi:"relation,tags,omitempty"` + + // Associated Project with the workspace. If not provided, default project + // of the organization will be assigned to the workspace + Project *Project `jsonapi:"relation,project,omitempty"` } // TODO: move this struct out. VCSRepoOptions is used by workspaces, policy sets, and registry modules @@ -466,6 +470,10 @@ type WorkspaceUpdateOptions struct { // the environment when multiple environments exist within the same // repository. WorkingDirectory *string `jsonapi:"attr,working-directory,omitempty"` + + // Associated Project with the workspace. If not provided, default project + // of the organization will be assigned to the workspace + Project *Project `jsonapi:"relation,project,omitempty"` } // WorkspaceLockOptions represents the options for locking a workspace. diff --git a/workspace_integration_test.go b/workspace_integration_test.go index 83131e8f0..8536a9c37 100644 --- a/workspace_integration_test.go +++ b/workspace_integration_test.go @@ -327,6 +327,53 @@ func TestWorkspacesCreate(t *testing.T) { orgTest, orgTestCleanup := createOrganization(t, client) t.Cleanup(orgTestCleanup) + t.Run("with valid project option", func(t *testing.T) { + skipIfBeta(t) + + options := WorkspaceCreateOptions{ + Name: String(fmt.Sprintf("foo-%s", randomString(t))), + AllowDestroyPlan: Bool(false), + AutoApply: Bool(true), + Description: String("qux"), + AssessmentsEnabled: Bool(false), + FileTriggersEnabled: Bool(true), + Operations: Bool(true), + QueueAllRuns: Bool(true), + SpeculativeEnabled: Bool(true), + SourceName: String("my-app"), + SourceURL: String("http://my-app-hostname.io"), + StructuredRunOutputEnabled: Bool(true), + TerraformVersion: String("0.11.0"), + TriggerPrefixes: []string{"/modules", "/shared"}, + WorkingDirectory: String("bar/"), + Project: orgTest.DefaultProject, + Tags: []*Tag{ + { + Name: "tag1", + }, + { + Name: "tag2", + }, + }, + } + + w, err := client.Workspaces.Create(ctx, orgTest.Name, options) + require.NoError(t, err) + + // Get a refreshed view from the API. + refreshed, err := client.Workspaces.Read(ctx, orgTest.Name, *options.Name) + require.NoError(t, err) + + for _, item := range []*Workspace{ + w, + refreshed, + } { + assert.NotEmpty(t, item.ID) + assert.Equal(t, *options.Name, item.Name) + assert.Equal(t, options.Project.ID, item.Project.ID) + } + }) + t.Run("with valid options", func(t *testing.T) { options := WorkspaceCreateOptions{ Name: String("foo"), @@ -763,6 +810,36 @@ func TestWorkspacesUpdate(t *testing.T) { assert.Equal(t, wTest.WorkingDirectory, wAfter.WorkingDirectory) }) + t.Run("when updating project", func(t *testing.T) { + skipIfBeta(t) + + kBefore, kTestCleanup := createProject(t, client, orgTest) + defer kTestCleanup() + + wBefore, wBeforeCleanup := createWorkspaceWithOptions(t, client, orgTest, WorkspaceCreateOptions{ + Name: String(randomString(t)), + Project: kBefore, + }) + defer wBeforeCleanup() + + options := WorkspaceUpdateOptions{ + Name: String(wBefore.Name), + AllowDestroyPlan: Bool(false), + AutoApply: Bool(true), + Operations: Bool(true), + QueueAllRuns: Bool(true), + AssessmentsEnabled: Bool(true), + TerraformVersion: String("0.15.4"), + Project: orgTest.DefaultProject, + } + + wAfter, err := client.Workspaces.Update(ctx, orgTest.Name, wBefore.Name, options) + require.NoError(t, err) + + assert.Equal(t, wBefore.Name, wAfter.Name) + assert.Equal(t, wAfter.Project.ID, orgTest.DefaultProject.ID) + }) + t.Run("with valid options", func(t *testing.T) { options := WorkspaceUpdateOptions{ Name: String(randomString(t)),