diff --git a/astro-client-core/api.gen.go b/astro-client-core/api.gen.go index 21c588a03..19960a8d1 100644 --- a/astro-client-core/api.gen.go +++ b/astro-client-core/api.gen.go @@ -950,9 +950,10 @@ type CreateSsoConnectionRequest struct { // CreateTeamRequest defines model for CreateTeamRequest. type CreateTeamRequest struct { - Description *string `json:"description,omitempty"` - MemberIds *[]string `json:"memberIds,omitempty"` - Name string `json:"name"` + Description *string `json:"description,omitempty"` + MemberIds *[]string `json:"memberIds,omitempty"` + Name string `json:"name"` + OrganizationRole *string `json:"organizationRole,omitempty"` } // CreateUserInviteRequest defines model for CreateUserInviteRequest. @@ -1322,6 +1323,11 @@ type MetronomeDashboard struct { Url string `json:"url"` } +// MutateOrgTeamRoleRequest defines model for MutateOrgTeamRoleRequest. +type MutateOrgTeamRoleRequest struct { + Role string `json:"role"` +} + // MutateOrgUserRoleRequest defines model for MutateOrgUserRoleRequest. type MutateOrgUserRoleRequest struct { Role string `json:"role"` @@ -1695,19 +1701,20 @@ type TaskInstanceState string // Team defines model for Team. type Team struct { - CreatedAt time.Time `json:"createdAt"` - CreatedBy *BasicSubjectProfile `json:"createdBy,omitempty"` - Description *string `json:"description,omitempty"` - Id string `json:"id"` - IsIdpManaged bool `json:"isIdpManaged"` - Members *[]TeamMember `json:"members,omitempty"` - MembersCount *int `json:"membersCount,omitempty"` - Name string `json:"name"` - OrganizationId string `json:"organizationId"` - Roles *[]TeamRole `json:"roles,omitempty"` - RolesCount *int `json:"rolesCount,omitempty"` - UpdatedAt time.Time `json:"updatedAt"` - UpdatedBy *BasicSubjectProfile `json:"updatedBy,omitempty"` + CreatedAt time.Time `json:"createdAt"` + CreatedBy *BasicSubjectProfile `json:"createdBy,omitempty"` + Description *string `json:"description,omitempty"` + Id string `json:"id"` + IsIdpManaged bool `json:"isIdpManaged"` + Members *[]TeamMember `json:"members,omitempty"` + MembersCount *int `json:"membersCount,omitempty"` + Name string `json:"name"` + OrganizationId string `json:"organizationId"` + OrganizationRole string `json:"organizationRole"` + Roles *[]TeamRole `json:"roles,omitempty"` + RolesCount *int `json:"rolesCount,omitempty"` + UpdatedAt time.Time `json:"updatedAt"` + UpdatedBy *BasicSubjectProfile `json:"updatedBy,omitempty"` } // TeamMember defines model for TeamMember. @@ -2560,6 +2567,9 @@ type UpdateTeamJSONRequestBody = UpdateTeamRequest // AddTeamMembersJSONRequestBody defines body for AddTeamMembers for application/json ContentType. type AddTeamMembersJSONRequestBody = AddTeamMembersRequest +// MutateOrgTeamRoleJSONRequestBody defines body for MutateOrgTeamRole for application/json ContentType. +type MutateOrgTeamRoleJSONRequestBody = MutateOrgTeamRoleRequest + // MutateOrgUserRoleJSONRequestBody defines body for MutateOrgUserRole for application/json ContentType. type MutateOrgUserRoleJSONRequestBody = MutateOrgUserRoleRequest @@ -2875,6 +2885,11 @@ type ClientInterface interface { // RemoveTeamMember request RemoveTeamMember(ctx context.Context, organizationId string, teamId string, memberId string, reqEditors ...RequestEditorFn) (*http.Response, error) + // MutateOrgTeamRole request with any body + MutateOrgTeamRoleWithBody(ctx context.Context, organizationId string, teamId string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) + + MutateOrgTeamRole(ctx context.Context, organizationId string, teamId string, body MutateOrgTeamRoleJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) + // ListOrgUsers request ListOrgUsers(ctx context.Context, organizationId string, params *ListOrgUsersParams, reqEditors ...RequestEditorFn) (*http.Response, error) @@ -3901,6 +3916,30 @@ func (c *Client) RemoveTeamMember(ctx context.Context, organizationId string, te return c.Client.Do(req) } +func (c *Client) MutateOrgTeamRoleWithBody(ctx context.Context, organizationId string, teamId string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewMutateOrgTeamRoleRequestWithBody(c.Server, organizationId, teamId, contentType, body) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +func (c *Client) MutateOrgTeamRole(ctx context.Context, organizationId string, teamId string, body MutateOrgTeamRoleJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewMutateOrgTeamRoleRequest(c.Server, organizationId, teamId, body) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + func (c *Client) ListOrgUsers(ctx context.Context, organizationId string, params *ListOrgUsersParams, reqEditors ...RequestEditorFn) (*http.Response, error) { req, err := NewListOrgUsersRequest(c.Server, organizationId, params) if err != nil { @@ -7599,6 +7638,60 @@ func NewRemoveTeamMemberRequest(server string, organizationId string, teamId str return req, nil } +// NewMutateOrgTeamRoleRequest calls the generic MutateOrgTeamRole builder with application/json body +func NewMutateOrgTeamRoleRequest(server string, organizationId string, teamId string, body MutateOrgTeamRoleJSONRequestBody) (*http.Request, error) { + var bodyReader io.Reader + buf, err := json.Marshal(body) + if err != nil { + return nil, err + } + bodyReader = bytes.NewReader(buf) + return NewMutateOrgTeamRoleRequestWithBody(server, organizationId, teamId, "application/json", bodyReader) +} + +// NewMutateOrgTeamRoleRequestWithBody generates requests for MutateOrgTeamRole with any type of body +func NewMutateOrgTeamRoleRequestWithBody(server string, organizationId string, teamId string, contentType string, body io.Reader) (*http.Request, error) { + var err error + + var pathParam0 string + + pathParam0, err = runtime.StyleParamWithLocation("simple", false, "organizationId", runtime.ParamLocationPath, organizationId) + if err != nil { + return nil, err + } + + var pathParam1 string + + pathParam1, err = runtime.StyleParamWithLocation("simple", false, "teamId", runtime.ParamLocationPath, teamId) + if err != nil { + return nil, err + } + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/organizations/%s/teams/%s/role", pathParam0, pathParam1) + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + req, err := http.NewRequest("POST", queryURL.String(), body) + if err != nil { + return nil, err + } + + req.Header.Add("Content-Type", contentType) + + return req, nil +} + // NewListOrgUsersRequest generates requests for ListOrgUsers func NewListOrgUsersRequest(server string, organizationId string, params *ListOrgUsersParams) (*http.Request, error) { var err error @@ -9723,6 +9816,11 @@ type ClientWithResponsesInterface interface { // RemoveTeamMember request RemoveTeamMemberWithResponse(ctx context.Context, organizationId string, teamId string, memberId string, reqEditors ...RequestEditorFn) (*RemoveTeamMemberResponse, error) + // MutateOrgTeamRole request with any body + MutateOrgTeamRoleWithBodyWithResponse(ctx context.Context, organizationId string, teamId string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*MutateOrgTeamRoleResponse, error) + + MutateOrgTeamRoleWithResponse(ctx context.Context, organizationId string, teamId string, body MutateOrgTeamRoleJSONRequestBody, reqEditors ...RequestEditorFn) (*MutateOrgTeamRoleResponse, error) + // ListOrgUsers request ListOrgUsersWithResponse(ctx context.Context, organizationId string, params *ListOrgUsersParams, reqEditors ...RequestEditorFn) (*ListOrgUsersResponse, error) @@ -11378,6 +11476,33 @@ func (r RemoveTeamMemberResponse) StatusCode() int { return 0 } +type MutateOrgTeamRoleResponse struct { + Body []byte + HTTPResponse *http.Response + JSON200 *TeamRole + JSON400 *Error + JSON401 *Error + JSON403 *Error + JSON404 *Error + JSON500 *Error +} + +// Status returns HTTPResponse.Status +func (r MutateOrgTeamRoleResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r MutateOrgTeamRoleResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + type ListOrgUsersResponse struct { Body []byte HTTPResponse *http.Response @@ -12773,6 +12898,23 @@ func (c *ClientWithResponses) RemoveTeamMemberWithResponse(ctx context.Context, return ParseRemoveTeamMemberResponse(rsp) } +// MutateOrgTeamRoleWithBodyWithResponse request with arbitrary body returning *MutateOrgTeamRoleResponse +func (c *ClientWithResponses) MutateOrgTeamRoleWithBodyWithResponse(ctx context.Context, organizationId string, teamId string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*MutateOrgTeamRoleResponse, error) { + rsp, err := c.MutateOrgTeamRoleWithBody(ctx, organizationId, teamId, contentType, body, reqEditors...) + if err != nil { + return nil, err + } + return ParseMutateOrgTeamRoleResponse(rsp) +} + +func (c *ClientWithResponses) MutateOrgTeamRoleWithResponse(ctx context.Context, organizationId string, teamId string, body MutateOrgTeamRoleJSONRequestBody, reqEditors ...RequestEditorFn) (*MutateOrgTeamRoleResponse, error) { + rsp, err := c.MutateOrgTeamRole(ctx, organizationId, teamId, body, reqEditors...) + if err != nil { + return nil, err + } + return ParseMutateOrgTeamRoleResponse(rsp) +} + // ListOrgUsersWithResponse request returning *ListOrgUsersResponse func (c *ClientWithResponses) ListOrgUsersWithResponse(ctx context.Context, organizationId string, params *ListOrgUsersParams, reqEditors ...RequestEditorFn) (*ListOrgUsersResponse, error) { rsp, err := c.ListOrgUsers(ctx, organizationId, params, reqEditors...) @@ -16543,6 +16685,67 @@ func ParseRemoveTeamMemberResponse(rsp *http.Response) (*RemoveTeamMemberRespons return response, nil } +// ParseMutateOrgTeamRoleResponse parses an HTTP response from a MutateOrgTeamRoleWithResponse call +func ParseMutateOrgTeamRoleResponse(rsp *http.Response) (*MutateOrgTeamRoleResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &MutateOrgTeamRoleResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + switch { + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: + var dest TeamRole + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON200 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 400: + var dest Error + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON400 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 401: + var dest Error + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON401 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 403: + var dest Error + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON403 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 404: + var dest Error + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON404 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 500: + var dest Error + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON500 = &dest + + } + + return response, nil +} + // ParseListOrgUsersResponse parses an HTTP response from a ListOrgUsersWithResponse call func ParseListOrgUsersResponse(rsp *http.Response) (*ListOrgUsersResponse, error) { bodyBytes, err := io.ReadAll(rsp.Body) diff --git a/astro-client-core/mocks/client.go b/astro-client-core/mocks/client.go index 612db5536..f4570ec98 100644 --- a/astro-client-core/mocks/client.go +++ b/astro-client-core/mocks/client.go @@ -2267,6 +2267,66 @@ func (_m *ClientWithResponsesInterface) ListWorkspacesWithResponse(ctx context.C return r0, r1 } +// MutateOrgTeamRoleWithBodyWithResponse provides a mock function with given fields: ctx, organizationId, teamId, contentType, body, reqEditors +func (_m *ClientWithResponsesInterface) MutateOrgTeamRoleWithBodyWithResponse(ctx context.Context, organizationId string, teamId string, contentType string, body io.Reader, reqEditors ...astrocore.RequestEditorFn) (*astrocore.MutateOrgTeamRoleResponse, error) { + _va := make([]interface{}, len(reqEditors)) + for _i := range reqEditors { + _va[_i] = reqEditors[_i] + } + var _ca []interface{} + _ca = append(_ca, ctx, organizationId, teamId, contentType, body) + _ca = append(_ca, _va...) + ret := _m.Called(_ca...) + + var r0 *astrocore.MutateOrgTeamRoleResponse + if rf, ok := ret.Get(0).(func(context.Context, string, string, string, io.Reader, ...astrocore.RequestEditorFn) *astrocore.MutateOrgTeamRoleResponse); ok { + r0 = rf(ctx, organizationId, teamId, contentType, body, reqEditors...) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*astrocore.MutateOrgTeamRoleResponse) + } + } + + var r1 error + if rf, ok := ret.Get(1).(func(context.Context, string, string, string, io.Reader, ...astrocore.RequestEditorFn) error); ok { + r1 = rf(ctx, organizationId, teamId, contentType, body, reqEditors...) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// MutateOrgTeamRoleWithResponse provides a mock function with given fields: ctx, organizationId, teamId, body, reqEditors +func (_m *ClientWithResponsesInterface) MutateOrgTeamRoleWithResponse(ctx context.Context, organizationId string, teamId string, body astrocore.MutateOrgTeamRoleRequest, reqEditors ...astrocore.RequestEditorFn) (*astrocore.MutateOrgTeamRoleResponse, error) { + _va := make([]interface{}, len(reqEditors)) + for _i := range reqEditors { + _va[_i] = reqEditors[_i] + } + var _ca []interface{} + _ca = append(_ca, ctx, organizationId, teamId, body) + _ca = append(_ca, _va...) + ret := _m.Called(_ca...) + + var r0 *astrocore.MutateOrgTeamRoleResponse + if rf, ok := ret.Get(0).(func(context.Context, string, string, astrocore.MutateOrgTeamRoleRequest, ...astrocore.RequestEditorFn) *astrocore.MutateOrgTeamRoleResponse); ok { + r0 = rf(ctx, organizationId, teamId, body, reqEditors...) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*astrocore.MutateOrgTeamRoleResponse) + } + } + + var r1 error + if rf, ok := ret.Get(1).(func(context.Context, string, string, astrocore.MutateOrgTeamRoleRequest, ...astrocore.RequestEditorFn) error); ok { + r1 = rf(ctx, organizationId, teamId, body, reqEditors...) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + // MutateOrgUserRoleWithBodyWithResponse provides a mock function with given fields: ctx, organizationId, userId, contentType, body, reqEditors func (_m *ClientWithResponsesInterface) MutateOrgUserRoleWithBodyWithResponse(ctx context.Context, organizationId string, userId string, contentType string, body io.Reader, reqEditors ...astrocore.RequestEditorFn) (*astrocore.MutateOrgUserRoleResponse, error) { _va := make([]interface{}, len(reqEditors)) diff --git a/cloud/team/team.go b/cloud/team/team.go index f7730c277..372a3221b 100644 --- a/cloud/team/team.go +++ b/cloud/team/team.go @@ -36,7 +36,11 @@ func confirmOperation() bool { return y } -func CreateTeam(name, description string, out io.Writer, client astrocore.CoreClient) error { +func CreateTeam(name, description, role string, out io.Writer, client astrocore.CoreClient) error { + err := user.IsOrganizationRoleValid(role) + if err != nil { + return err + } if name == "" { return ErrInvalidName } @@ -48,8 +52,9 @@ func CreateTeam(name, description string, out io.Writer, client astrocore.CoreCl return user.ErrNoShortName } teamCreateRequest := astrocore.CreateTeamJSONRequestBody{ - Description: &description, - Name: name, + Description: &description, + Name: name, + OrganizationRole: &role, } resp, err := client.CreateTeamWithResponse(httpContext.Background(), ctx.OrganizationShortName, teamCreateRequest) if err != nil { @@ -140,7 +145,7 @@ func UpdateWorkspaceTeamRole(id, role, workspace string, out io.Writer, client a return nil } -func UpdateTeam(id, name, description string, out io.Writer, client astrocore.CoreClient) error { +func UpdateTeam(id, name, description, role string, out io.Writer, client astrocore.CoreClient) error { ctx, err := context.GetCurrentContext() if err != nil { return err @@ -202,6 +207,23 @@ func UpdateTeam(id, name, description string, out io.Writer, client astrocore.Co return err } fmt.Fprintf(out, "Astro Team %s was successfully updated\n", team.Name) + + if role != "" { + err := user.IsOrganizationRoleValid(role) + if err != nil { + return err + } + teamMutateRoleRequest := astrocore.MutateOrgTeamRoleRequest{Role: role} + resp, err := client.MutateOrgTeamRoleWithResponse(httpContext.Background(), ctx.OrganizationShortName, teamID, teamMutateRoleRequest) + if err != nil { + return err + } + err = astrocore.NormalizeAPIError(resp.HTTPResponse, resp.Body) + if err != nil { + return err + } + fmt.Fprintf(out, "Astro Team role %s was successfully updated to %s\n", team.Name, role) + } return nil } @@ -454,7 +476,7 @@ func GetOrgTeams(client astrocore.CoreClient) ([]astrocore.Team, error) { func ListOrgTeams(out io.Writer, client astrocore.CoreClient) error { table := printutil.Table{ DynamicPadding: true, - Header: []string{"ID", "NAME", "DESCRIPTION", "IDP MANAGED", "CREATE DATE"}, + Header: []string{"ID", "NAME", "DESCRIPTION", "ORG ROLE", "IDP MANAGED", "CREATE DATE"}, } teams, err := GetOrgTeams(client) if err != nil { @@ -466,6 +488,7 @@ func ListOrgTeams(out io.Writer, client astrocore.CoreClient) error { teams[i].Id, teams[i].Name, *teams[i].Description, + teams[i].OrganizationRole, strconv.FormatBool(teams[i].IsIdpManaged), teams[i].CreatedAt.Format(time.RFC3339), }, false) diff --git a/cloud/team/team_test.go b/cloud/team/team_test.go index c32cac284..b3e035fa0 100644 --- a/cloud/team/team_test.go +++ b/cloud/team/team_test.go @@ -193,6 +193,9 @@ var ( Role: "WORKSPACE_MEMBER", }, } + errorBodyUpdateRole, _ = json.Marshal(astrocore.Error{ + Message: "failed to update team role", + }) errorBodyUpdate, _ = json.Marshal(astrocore.Error{ Message: "failed to update team", }) @@ -236,6 +239,17 @@ var ( }, Body: errorBodyUpdate, } + MutateOrgTeamRoleResponseOK = astrocore.MutateOrgTeamRoleResponse{ + HTTPResponse: &http.Response{ + StatusCode: 200, + }, + } + MutateOrgTeamRoleResponseError = astrocore.MutateOrgTeamRoleResponse{ + HTTPResponse: &http.Response{ + StatusCode: 500, + }, + Body: errorBodyUpdateRole, + } CreateTeamResponseOK = astrocore.CreateTeamResponse{ HTTPResponse: &http.Response{ StatusCode: 200, @@ -816,11 +830,56 @@ func TestUpdate(t *testing.T) { mockClient := new(astrocore_mocks.ClientWithResponsesInterface) mockClient.On("GetTeamWithResponse", mock.Anything, mock.Anything, mock.Anything).Return(&GetTeamWithResponseOK, nil).Twice() mockClient.On("UpdateTeamWithResponse", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(&UpdateTeamResponseOK, nil).Once() - err := UpdateTeam(team1.Id, "name", "description", out, mockClient) + err := UpdateTeam(team1.Id, "name", "description", "", out, mockClient) assert.NoError(t, err) assert.Equal(t, expectedOutMessage, out.String()) }) + t.Run("happy path Update - with role", func(t *testing.T) { + role := "ORGANIZATION_OWNER" + expectedOutMessage := fmt.Sprintf("Astro Team %s was successfully updated\nAstro Team role %s was successfully updated to %s\n", team1.Name, team1.Name, role) + out := new(bytes.Buffer) + mockClient := new(astrocore_mocks.ClientWithResponsesInterface) + mockClient.On("GetTeamWithResponse", mock.Anything, mock.Anything, mock.Anything).Return(&GetTeamWithResponseOK, nil).Twice() + mockClient.On("UpdateTeamWithResponse", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(&UpdateTeamResponseOK, nil).Once() + mockClient.On("MutateOrgTeamRoleWithResponse", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(&MutateOrgTeamRoleResponseOK, nil).Once() + err := UpdateTeam(team1.Id, "name", "description", role, out, mockClient) + assert.NoError(t, err) + assert.Equal(t, expectedOutMessage, out.String()) + }) + + t.Run("unhappy path Update - with invalid role", func(t *testing.T) { + role := "WORKSPACE_VIEWER" + out := new(bytes.Buffer) + mockClient := new(astrocore_mocks.ClientWithResponsesInterface) + mockClient.On("GetTeamWithResponse", mock.Anything, mock.Anything, mock.Anything).Return(&GetTeamWithResponseOK, nil).Twice() + mockClient.On("UpdateTeamWithResponse", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(&UpdateTeamResponseOK, nil).Once() + err := UpdateTeam(team1.Id, "name", "description", role, out, mockClient) + assert.EqualError(t, err, "requested role is invalid. Possible values are ORGANIZATION_MEMBER, ORGANIZATION_BILLING_ADMIN and ORGANIZATION_OWNER ") + }) + + t.Run("unhappy path Update - with role MutateOrgTeamRoleWithResponse error", func(t *testing.T) { + role := "ORGANIZATION_OWNER" + out := new(bytes.Buffer) + mockClient := new(astrocore_mocks.ClientWithResponsesInterface) + mockClient.On("GetTeamWithResponse", mock.Anything, mock.Anything, mock.Anything).Return(&GetTeamWithResponseOK, nil).Twice() + mockClient.On("UpdateTeamWithResponse", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(&UpdateTeamResponseOK, nil).Once() + mockClient.On("MutateOrgTeamRoleWithResponse", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(&MutateOrgTeamRoleResponseError, nil).Once() + err := UpdateTeam(team1.Id, "name", "description", role, out, mockClient) + assert.EqualError(t, err, "failed to update team role") + }) + + t.Run("unhappy path Update - with role MutateOrgTeamRoleWithResponse network error", func(t *testing.T) { + role := "ORGANIZATION_OWNER" + out := new(bytes.Buffer) + mockClient := new(astrocore_mocks.ClientWithResponsesInterface) + mockClient.On("GetTeamWithResponse", mock.Anything, mock.Anything, mock.Anything).Return(&GetTeamWithResponseOK, nil).Twice() + mockClient.On("UpdateTeamWithResponse", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(&UpdateTeamResponseOK, nil).Once() + mockClient.On("MutateOrgTeamRoleWithResponse", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(nil, errorNetwork).Once() + err := UpdateTeam(team1.Id, "name", "description", role, out, mockClient) + assert.EqualError(t, err, "network error") + }) + t.Run("happy path Update with idp managed team", func(t *testing.T) { expectedOutMessage := fmt.Sprintf("Astro Team %s was successfully updated\n", team2.Name) out := new(bytes.Buffer) @@ -828,7 +887,7 @@ func TestUpdate(t *testing.T) { mockClient.On("GetTeamWithResponse", mock.Anything, mock.Anything, mock.Anything).Return(&GetIDPManagedTeamWithResponseOK, nil).Twice() mockClient.On("UpdateTeamWithResponse", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(&UpdateTeamResponseOK, nil).Once() defer testUtil.MockUserInput(t, "y")() - err := UpdateTeam(team2.Id, "name", "description", out, mockClient) + err := UpdateTeam(team2.Id, "name", "description", "", out, mockClient) assert.NoError(t, err) assert.Equal(t, expectedOutMessage, out.String()) }) @@ -839,7 +898,7 @@ func TestUpdate(t *testing.T) { mockClient.On("GetTeamWithResponse", mock.Anything, mock.Anything, mock.Anything).Return(&GetIDPManagedTeamWithResponseOK, nil).Twice() mockClient.On("UpdateTeamWithResponse", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(&UpdateTeamResponseOK, nil).Once() defer testUtil.MockUserInput(t, "n")() - err := UpdateTeam(team2.Id, "name", "description", out, mockClient) + err := UpdateTeam(team2.Id, "name", "description", "", out, mockClient) assert.NoError(t, err) assert.Equal(t, "", out.String()) }) @@ -850,7 +909,7 @@ func TestUpdate(t *testing.T) { mockClient := new(astrocore_mocks.ClientWithResponsesInterface) mockClient.On("GetTeamWithResponse", mock.Anything, mock.Anything, mock.Anything).Return(&GetTeamWithResponseOK, nil).Twice() mockClient.On("UpdateTeamWithResponse", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(&UpdateTeamResponseOK, nil).Once() - err := UpdateTeam(team1.Id, "name", "", out, mockClient) + err := UpdateTeam(team1.Id, "name", "", "", out, mockClient) assert.NoError(t, err) assert.Equal(t, expectedOutMessage, out.String()) }) @@ -861,7 +920,7 @@ func TestUpdate(t *testing.T) { mockClient := new(astrocore_mocks.ClientWithResponsesInterface) mockClient.On("GetTeamWithResponse", mock.Anything, mock.Anything, mock.Anything).Return(&GetTeamWithResponseOK, nil).Twice() mockClient.On("UpdateTeamWithResponse", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(&UpdateTeamResponseOK, nil).Once() - err := UpdateTeam(team1.Id, "", "description", out, mockClient) + err := UpdateTeam(team1.Id, "", "description", "", out, mockClient) assert.NoError(t, err) assert.Equal(t, expectedOutMessage, out.String()) }) @@ -870,7 +929,7 @@ func TestUpdate(t *testing.T) { out := new(bytes.Buffer) mockClient := new(astrocore_mocks.ClientWithResponsesInterface) mockClient.On("ListOrganizationTeamsWithResponse", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(&ListOrganizationTeamsResponseEmpty, nil).Twice() - err := UpdateTeam("", "name", "description", out, mockClient) + err := UpdateTeam("", "name", "description", "", out, mockClient) assert.EqualError(t, err, "no teams found in your organization") }) @@ -878,7 +937,7 @@ func TestUpdate(t *testing.T) { out := new(bytes.Buffer) mockClient := new(astrocore_mocks.ClientWithResponsesInterface) mockClient.On("GetTeamWithResponse", mock.Anything, mock.Anything, mock.Anything).Return(nil, errorNetwork).Twice() - err := UpdateTeam(team1.Id, "name", "description", out, mockClient) + err := UpdateTeam(team1.Id, "name", "description", "", out, mockClient) assert.EqualError(t, err, "network error") }) @@ -887,7 +946,7 @@ func TestUpdate(t *testing.T) { mockClient := new(astrocore_mocks.ClientWithResponsesInterface) mockClient.On("GetTeamWithResponse", mock.Anything, mock.Anything, mock.Anything).Return(&GetTeamWithResponseOK, nil).Twice() mockClient.On("UpdateTeamWithResponse", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(nil, errorNetwork).Once() - err := UpdateTeam(team1.Id, "name", "description", out, mockClient) + err := UpdateTeam(team1.Id, "name", "description", "", out, mockClient) assert.EqualError(t, err, "network error") }) @@ -896,7 +955,7 @@ func TestUpdate(t *testing.T) { mockClient := new(astrocore_mocks.ClientWithResponsesInterface) mockClient.On("GetTeamWithResponse", mock.Anything, mock.Anything, mock.Anything).Return(&GetTeamWithResponseOK, nil).Twice() mockClient.On("UpdateTeamWithResponse", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(&UpdateTeamResponseError, nil).Once() - err := UpdateTeam(team1.Id, "name", "description", out, mockClient) + err := UpdateTeam(team1.Id, "name", "description", "", out, mockClient) assert.EqualError(t, err, "failed to update team") }) @@ -908,7 +967,7 @@ func TestUpdate(t *testing.T) { assert.NoError(t, err) out := new(bytes.Buffer) mockClient := new(astrocore_mocks.ClientWithResponsesInterface) - err = UpdateTeam(team1.Id, "name", "description", out, mockClient) + err = UpdateTeam(team1.Id, "name", "description", "", out, mockClient) assert.EqualError(t, err, "cannot retrieve organization short name from context") }) @@ -917,7 +976,7 @@ func TestUpdate(t *testing.T) { expectedOutMessage := "" out := new(bytes.Buffer) mockClient := new(astrocore_mocks.ClientWithResponsesInterface) - err := UpdateTeam(team1.Id, "name", "description", out, mockClient) + err := UpdateTeam(team1.Id, "name", "description", "", out, mockClient) assert.Error(t, err) assert.Equal(t, expectedOutMessage, out.String()) }) @@ -942,7 +1001,7 @@ func TestUpdate(t *testing.T) { expectedOutMessage := fmt.Sprintf("Astro Team %s was successfully updated\n", team1.Name) mockClient.On("UpdateTeamWithResponse", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(&UpdateTeamResponseOK, nil).Once() - err = UpdateTeam("", "name", "description", out, mockClient) + err = UpdateTeam("", "name", "description", "", out, mockClient) assert.NoError(t, err) assert.Equal(t, expectedOutMessage, out.String()) }) @@ -955,7 +1014,7 @@ func TestCreate(t *testing.T) { out := new(bytes.Buffer) mockClient := new(astrocore_mocks.ClientWithResponsesInterface) mockClient.On("CreateTeamWithResponse", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(&CreateTeamResponseOK, nil).Once() - err := CreateTeam(team1.Name, *team1.Description, out, mockClient) + err := CreateTeam(team1.Name, *team1.Description, "ORGANIZATION_MEMBER", out, mockClient) assert.NoError(t, err) assert.Equal(t, expectedOutMessage, out.String()) }) @@ -964,7 +1023,7 @@ func TestCreate(t *testing.T) { out := new(bytes.Buffer) mockClient := new(astrocore_mocks.ClientWithResponsesInterface) mockClient.On("CreateTeamWithResponse", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(nil, errorNetwork).Once() - err := CreateTeam(team1.Name, *team1.Description, out, mockClient) + err := CreateTeam(team1.Name, *team1.Description, "ORGANIZATION_MEMBER", out, mockClient) assert.EqualError(t, err, "network error") }) @@ -972,7 +1031,7 @@ func TestCreate(t *testing.T) { out := new(bytes.Buffer) mockClient := new(astrocore_mocks.ClientWithResponsesInterface) mockClient.On("CreateTeamWithResponse", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(&CreateTeamResponseError, nil).Once() - err := CreateTeam(team1.Name, *team1.Description, out, mockClient) + err := CreateTeam(team1.Name, *team1.Description, "ORGANIZATION_MEMBER", out, mockClient) assert.EqualError(t, err, "failed to update team") }) @@ -984,7 +1043,7 @@ func TestCreate(t *testing.T) { assert.NoError(t, err) out := new(bytes.Buffer) mockClient := new(astrocore_mocks.ClientWithResponsesInterface) - err = CreateTeam(team1.Name, *team1.Description, out, mockClient) + err = CreateTeam(team1.Name, *team1.Description, "ORGANIZATION_MEMBER", out, mockClient) assert.EqualError(t, err, "cannot retrieve organization short name from context") }) @@ -993,7 +1052,7 @@ func TestCreate(t *testing.T) { expectedOutMessage := "" out := new(bytes.Buffer) mockClient := new(astrocore_mocks.ClientWithResponsesInterface) - err := CreateTeam(team1.Name, *team1.Description, out, mockClient) + err := CreateTeam(team1.Name, *team1.Description, "ORGANIZATION_MEMBER", out, mockClient) assert.Error(t, err) assert.Equal(t, expectedOutMessage, out.String()) }) @@ -1001,9 +1060,17 @@ func TestCreate(t *testing.T) { testUtil.InitTestConfig(testUtil.CloudPlatform) out := new(bytes.Buffer) mockClient := new(astrocore_mocks.ClientWithResponsesInterface) - err := CreateTeam("", *team1.Description, out, mockClient) + err := CreateTeam("", *team1.Description, "ORGANIZATION_MEMBER", out, mockClient) assert.EqualError(t, err, "no name provided for the team. Retry with a valid name") }) + + t.Run("error path invalid org role", func(t *testing.T) { + testUtil.InitTestConfig(testUtil.CloudPlatform) + out := new(bytes.Buffer) + mockClient := new(astrocore_mocks.ClientWithResponsesInterface) + err := CreateTeam("", *team1.Description, "WORKSPACE_OWNER", out, mockClient) + assert.EqualError(t, err, "requested role is invalid. Possible values are ORGANIZATION_MEMBER, ORGANIZATION_BILLING_ADMIN and ORGANIZATION_OWNER ") + }) } func TestAddUser(t *testing.T) { diff --git a/cmd/cloud/organization.go b/cmd/cloud/organization.go index 3f2b46448..4284128c6 100644 --- a/cmd/cloud/organization.go +++ b/cmd/cloud/organization.go @@ -39,6 +39,8 @@ var ( teamID string userID string organizationID string + updateOrganizationRole string + teamOrgRole string ) func newOrganizationCmd(out io.Writer) *cobra.Command { @@ -312,6 +314,8 @@ func newTeamUpdateCmd(out io.Writer) *cobra.Command { } cmd.Flags().StringVarP(&teamName, "name", "n", "", "The Team's name. If the name contains a space, specify the entire name within quotes \"\" ") cmd.Flags().StringVarP(&teamDescription, "description", "d", "", "Description of the Team. If the description contains a space, specify the entire team description in quotes \"\"") + cmd.Flags().StringVarP(&updateOrganizationRole, "role", "r", "", "The new role for the "+ + "team. Possible values are ORGANIZATION_MEMBER, ORGANIZATION_BILLING_ADMIN, ORGANIZATION_OWNER ") return cmd } @@ -324,7 +328,7 @@ func teamUpdate(cmd *cobra.Command, out io.Writer, args []string) error { id = args[0] } - return team.UpdateTeam(id, teamName, teamDescription, out, astroCoreClient) + return team.UpdateTeam(id, teamName, teamDescription, updateOrganizationRole, out, astroCoreClient) } func newTeamCreateCmd(out io.Writer) *cobra.Command { @@ -339,12 +343,24 @@ func newTeamCreateCmd(out io.Writer) *cobra.Command { } cmd.Flags().StringVarP(&teamName, "name", "n", "", "The Team's name. If the name contains a space, specify the entire team within quotes \"\" ") cmd.Flags().StringVarP(&teamDescription, "description", "d", "", "Description of the Team. If the description contains a space, specify the entire team in quotes \"\"") + cmd.Flags().StringVarP(&teamOrgRole, "role", "r", "", "The role for the "+ + "token. Possible values are ORGANIZATION_MEMBER, ORGANIZATION_BILLING_ADMIN and ORGANIZATION_OWNER") + return cmd } func teamCreate(cmd *cobra.Command, out io.Writer) error { cmd.SilenceUsage = true - return team.CreateTeam(teamName, teamDescription, out, astroCoreClient) + if teamOrgRole == "" { + fmt.Println("select a Organization Role for the new team:") + // no role was provided so ask the user for it + var err error + teamOrgRole, err = selectOrganizationRole() + if err != nil { + return err + } + } + return team.CreateTeam(teamName, teamDescription, teamOrgRole, out, astroCoreClient) } func newTeamDeleteCmd(out io.Writer) *cobra.Command { diff --git a/cmd/cloud/organization_test.go b/cmd/cloud/organization_test.go index 654ef2688..5ffb926ed 100644 --- a/cmd/cloud/organization_test.go +++ b/cmd/cloud/organization_test.go @@ -248,6 +248,17 @@ var ( Body: errorBodyUpdate, JSON200: nil, } + MutateOrgTeamRoleResponseOK = astrocore.MutateOrgTeamRoleResponse{ + HTTPResponse: &http.Response{ + StatusCode: 200, + }, + } + MutateOrgTeamRoleResponseError = astrocore.MutateOrgTeamRoleResponse{ + HTTPResponse: &http.Response{ + StatusCode: 500, + }, + Body: teamRequestErrorBodyUpdate, + } UpdateTeamResponseError = astrocore.UpdateTeamResponse{ HTTPResponse: &http.Response{ StatusCode: 500, @@ -634,6 +645,30 @@ func TestTeamUpdate(t *testing.T) { assert.NoError(t, err) assert.Contains(t, resp, expectedOut) }) + t.Run("valid id with role should update role", func(t *testing.T) { + expectedOut := fmt.Sprintf("Astro Team %s was successfully updated\n", team1.Name) + mockClient := new(astrocore_mocks.ClientWithResponsesInterface) + mockClient.On("GetTeamWithResponse", mock.Anything, mock.Anything, mock.Anything).Return(&GetTeamWithResponseOK, nil).Twice() + mockClient.On("UpdateTeamWithResponse", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(&UpdateTeamResponseOK, nil).Once() + mockClient.On("MutateOrgTeamRoleWithResponse", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(&MutateOrgTeamRoleResponseOK, nil).Once() + + astroCoreClient = mockClient + cmdArgs := []string{"team", "update", team1.Id, "--role", "ORGANIZATION_OWNER"} + resp, err := execOrganizationCmd(cmdArgs...) + assert.NoError(t, err) + assert.Contains(t, resp, expectedOut) + }) + t.Run("will error on invalid role", func(t *testing.T) { + mockClient := new(astrocore_mocks.ClientWithResponsesInterface) + mockClient.On("GetTeamWithResponse", mock.Anything, mock.Anything, mock.Anything).Return(&GetTeamWithResponseOK, nil).Twice() + mockClient.On("UpdateTeamWithResponse", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(&UpdateTeamResponseOK, nil).Once() + mockClient.On("MutateOrgTeamRoleWithResponse", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(&MutateOrgTeamRoleResponseOK, nil).Once() + + astroCoreClient = mockClient + cmdArgs := []string{"team", "update", team1.Id, "--role", "WORKSPACE_OPERATOR"} + _, err := execOrganizationCmd(cmdArgs...) + assert.ErrorContains(t, err, "requested role is invalid") + }) } func TestTeamCreate(t *testing.T) { @@ -647,21 +682,66 @@ func TestTeamCreate(t *testing.T) { assert.Contains(t, resp, expectedHelp) }) t.Run("valid id with valid name and description updates team", func(t *testing.T) { + expectedOut := fmt.Sprintf("Astro Team %s was successfully created\n", team1.Name) + mockClient := new(astrocore_mocks.ClientWithResponsesInterface) + mockClient.On("CreateTeamWithResponse", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(&CreateTeamResponseOK, nil).Once() + astroCoreClient = mockClient + cmdArgs := []string{"team", "create", team1.Id, "--role", "ORGANIZATION_MEMBER", "--name", team1.Name, "--description", *team1.Description} + resp, err := execOrganizationCmd(cmdArgs...) + assert.NoError(t, err) + assert.Contains(t, resp, expectedOut) + }) + t.Run("valid id with valid name and description updates team no role passed in", func(t *testing.T) { expectedOut := fmt.Sprintf("Astro Team %s was successfully created\n", team1.Name) mockClient := new(astrocore_mocks.ClientWithResponsesInterface) mockClient.On("CreateTeamWithResponse", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(&CreateTeamResponseOK, nil).Once() astroCoreClient = mockClient cmdArgs := []string{"team", "create", team1.Id, "--name", team1.Name, "--description", *team1.Description} + + // mock os.Stdin + expectedInput := []byte("1") + r, w, err := os.Pipe() + assert.NoError(t, err) + _, err = w.Write(expectedInput) + assert.NoError(t, err) + w.Close() + stdin := os.Stdin + // Restore stdin right after the test. + defer func() { os.Stdin = stdin }() + os.Stdin = r + resp, err := execOrganizationCmd(cmdArgs...) assert.NoError(t, err) assert.Contains(t, resp, expectedOut) }) + t.Run("error no role passed in index out of range", func(t *testing.T) { + mockClient := new(astrocore_mocks.ClientWithResponsesInterface) + mockClient.On("CreateTeamWithResponse", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(&CreateTeamResponseOK, nil).Once() + astroCoreClient = mockClient + cmdArgs := []string{"team", "create", team1.Id, "--name", team1.Name, "--description", *team1.Description} + + // mock os.Stdin + expectedInput := []byte("4") + r, w, err := os.Pipe() + assert.NoError(t, err) + _, err = w.Write(expectedInput) + assert.NoError(t, err) + w.Close() + stdin := os.Stdin + // Restore stdin right after the test. + defer func() { os.Stdin = stdin }() + os.Stdin = r + + _, err = execOrganizationCmd(cmdArgs...) + assert.EqualError(t, err, "invalid organization role selection") + }) + t.Run("any errors from api are returned and role is not created", func(t *testing.T) { mockClient := new(astrocore_mocks.ClientWithResponsesInterface) mockClient.On("CreateTeamWithResponse", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(&CreateTeamResponseError, nil).Once() astroCoreClient = mockClient - cmdArgs := []string{"team", "create", team1.Id, "--name", team1.Name} + cmdArgs := []string{"team", "create", team1.Id, "--role", "ORGANIZATION_MEMBER", "--name", team1.Name} _, err := execOrganizationCmd(cmdArgs...) assert.EqualError(t, err, "failed to create team") }) @@ -671,7 +751,7 @@ func TestTeamCreate(t *testing.T) { mockClient := new(astrocore_mocks.ClientWithResponsesInterface) mockClient.On("CreateTeamWithResponse", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(&CreateTeamResponseOK, nil).Once() astroCoreClient = mockClient - cmdArgs := []string{"create", team1.Id, "--name", team1.Name} + cmdArgs := []string{"create", team1.Id, "--role", "ORGANIZATION_MEMBER", "--name", team1.Name} _, err := execOrganizationCmd(cmdArgs...) assert.Error(t, err) })