Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
codersdk client functions, tests
  • Loading branch information
aslilac committed May 16, 2024
commit e6a0ec02a10b04e3cd92afe61f51baf04e54ae55
25 changes: 24 additions & 1 deletion coderd/database/dbauthz/dbauthz_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -624,7 +624,7 @@ func (s *MethodTestSuite) TestOrganization() {
s.Run("InsertOrganization", s.Subtest(func(db database.Store, check *expects) {
check.Args(database.InsertOrganizationParams{
ID: uuid.New(),
Name: "random",
Name: "new-org",
}).Asserts(rbac.ResourceOrganization, policy.ActionCreate)
}))
s.Run("InsertOrganizationMember", s.Subtest(func(db database.Store, check *expects) {
Expand All @@ -639,6 +639,29 @@ func (s *MethodTestSuite) TestOrganization() {
rbac.ResourceAssignRole.InOrg(o.ID), policy.ActionAssign,
rbac.ResourceOrganizationMember.InOrg(o.ID).WithID(u.ID), policy.ActionCreate)
}))
s.Run("UpdateOrganization", s.Subtest(func(db database.Store, check *expects) {
ctx := testutil.Context(s.T(), testutil.WaitShort)
o, err := db.InsertOrganization(ctx, database.InsertOrganizationParams{
ID: uuid.New(),
Name: "something-unique",
})
require.NoError(s.T(), err)
check.Args(database.UpdateOrganizationParams{
ID: o.ID,
Name: "something-different",
}).Asserts(rbac.ResourceOrganization, policy.ActionUpdate)
}))
s.Run("DeleteOrganization", s.Subtest(func(db database.Store, check *expects) {
ctx := testutil.Context(s.T(), testutil.WaitShort)
o, err := db.InsertOrganization(ctx, database.InsertOrganizationParams{
ID: uuid.New(),
Name: "doomed",
})
require.NoError(s.T(), err)
check.Args(
o.ID,
).Asserts(rbac.ResourceOrganization, policy.ActionDelete)
}))
s.Run("UpdateMemberRoles", s.Subtest(func(db database.Store, check *expects) {
o := dbgen.Organization(s.T(), db, database.Organization{})
u := dbgen.User(s.T(), db, database.User{})
Expand Down
4 changes: 2 additions & 2 deletions coderd/organizations.go
Original file line number Diff line number Diff line change
Expand Up @@ -124,14 +124,14 @@ func (api *API) postOrganizations(rw http.ResponseWriter, r *http.Request) {
// @Accept json
// @Produce json
// @Tags Organizations
// @Param request body codersdk.PatchOrganizationRequest true "Patch organization request"
// @Param request body codersdk.UpdateOrganizationRequest true "Patch organization request"
// @Success 200 {object} codersdk.Organization
// @Router /organizations/{organization} [patch]
func (api *API) patchOrganization(rw http.ResponseWriter, r *http.Request) {
ctx := r.Context()
organization := httpmw.OrganizationParam(r)

var req codersdk.PatchOrganizationRequest
var req codersdk.UpdateOrganizationRequest
if !httpapi.Read(ctx, rw, r, &req) {
return
}
Expand Down
67 changes: 67 additions & 0 deletions coderd/organizations_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -140,3 +140,70 @@ func TestPostOrganizationsByUser(t *testing.T) {
require.NoError(t, err)
})
}

func TestPatchOrganizationsByUser(t *testing.T) {
t.Parallel()
t.Run("Conflict", func(t *testing.T) {
t.Parallel()
client := coderdtest.New(t, nil)
user := coderdtest.CreateFirstUser(t, client)

ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitLong)
defer cancel()

originalOrg, err := client.Organization(ctx, user.OrganizationID)
require.NoError(t, err)
o, err := client.CreateOrganization(ctx, codersdk.CreateOrganizationRequest{
Name: "something-unique",
})
require.NoError(t, err)

_, err = client.UpdateOrganization(ctx, o.ID.String(), codersdk.UpdateOrganizationRequest{
Name: originalOrg.Name,
})
var apiErr *codersdk.Error
require.ErrorAs(t, err, &apiErr)
require.Equal(t, http.StatusConflict, apiErr.StatusCode())
})

t.Run("ReservedName", func(t *testing.T) {
t.Parallel()
client := coderdtest.New(t, nil)
_ = coderdtest.CreateFirstUser(t, client)

ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitLong)
defer cancel()

o, err := client.CreateOrganization(ctx, codersdk.CreateOrganizationRequest{
Name: "something-unique",
})
require.NoError(t, err)

_, err = client.UpdateOrganization(ctx, o.ID.String(), codersdk.UpdateOrganizationRequest{
Name: codersdk.DefaultOrganization,
})
var apiErr *codersdk.Error
require.ErrorAs(t, err, &apiErr)
require.Equal(t, http.StatusBadRequest, apiErr.StatusCode())
})

t.Run("Update", func(t *testing.T) {
t.Parallel()
client := coderdtest.New(t, nil)
_ = coderdtest.CreateFirstUser(t, client)

ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitLong)
defer cancel()

o, err := client.CreateOrganization(ctx, codersdk.CreateOrganizationRequest{
Name: "new",
})
require.NoError(t, err)

o, err = client.UpdateOrganization(ctx, o.Name, codersdk.UpdateOrganizationRequest{
Name: "new-new",
})
require.NoError(t, err)
require.Equal(t, "new-new", o.Name)
})
}
57 changes: 57 additions & 0 deletions codersdk/organizations.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,14 @@ type OrganizationMember struct {
Roles []Role `db:"roles" json:"roles"`
}

type CreateOrganizationRequest struct {
Name string `json:"name" validate:"required,username"`
}

type UpdateOrganizationRequest struct {
Name string `json:"name" validate:"required,username"`
}

// CreateTemplateVersionRequest enables callers to create a new Template Version.
type CreateTemplateVersionRequest struct {
Name string `json:"name,omitempty" validate:"omitempty,template_version_name"`
Expand Down Expand Up @@ -187,6 +195,55 @@ func (c *Client) Organization(ctx context.Context, id uuid.UUID) (Organization,
return c.OrganizationByName(ctx, id.String())
}

// CreateOrganization creates an organization and adds the provided user as an admin.
func (c *Client) CreateOrganization(ctx context.Context, req CreateOrganizationRequest) (Organization, error) {
res, err := c.Request(ctx, http.MethodPost, "/api/v2/organizations", req)
if err != nil {
return Organization{}, err
}
defer res.Body.Close()

if res.StatusCode != http.StatusCreated {
return Organization{}, ReadBodyAsError(res)
}

var org Organization
return org, json.NewDecoder(res.Body).Decode(&org)
}

// UpdateOrganization will update information about the corresponding organization, based on
// the UUID/name provided as `orgID`.
func (c *Client) UpdateOrganization(ctx context.Context, orgID string, req UpdateOrganizationRequest) (Organization, error) {
res, err := c.Request(ctx, http.MethodPatch, fmt.Sprintf("/api/v2/organizations/%s", orgID), req)
if err != nil {
return Organization{}, xerrors.Errorf("execute request: %w", err)
}
defer res.Body.Close()

if res.StatusCode != http.StatusOK {
return Organization{}, ReadBodyAsError(res)
}

var organization Organization
return organization, json.NewDecoder(res.Body).Decode(&organization)
}

// DeleteOrganization will remove the corresponding organization from the deployment, based on
// the UUID/name provided as `orgID`.
func (c *Client) DeleteOrganization(ctx context.Context, orgID string) error {
res, err := c.Request(ctx, http.MethodDelete, fmt.Sprintf("/api/v2/organizations/%s", orgID), nil)
if err != nil {
return xerrors.Errorf("execute request: %w", err)
}
defer res.Body.Close()

if res.StatusCode != http.StatusOK {
return ReadBodyAsError(res)
}

return nil
}

// ProvisionerDaemons returns provisioner daemons available.
func (c *Client) ProvisionerDaemons(ctx context.Context) ([]ProvisionerDaemon, error) {
res, err := c.Request(ctx, http.MethodGet,
Expand Down
24 changes: 0 additions & 24 deletions codersdk/users.go
Original file line number Diff line number Diff line change
Expand Up @@ -203,14 +203,6 @@ type OAuthConversionResponse struct {
UserID uuid.UUID `json:"user_id" format:"uuid"`
}

type CreateOrganizationRequest struct {
Name string `json:"name" validate:"required,username"`
}

type PatchOrganizationRequest struct {
Name string `json:"name" validate:"required,username"`
}

// AuthMethods contains authentication method information like whether they are enabled or not or custom text, etc.
type AuthMethods struct {
TermsOfServiceURL string `json:"terms_of_service_url,omitempty"`
Expand Down Expand Up @@ -591,22 +583,6 @@ func (c *Client) OrganizationByUserAndName(ctx context.Context, user string, nam
return org, json.NewDecoder(res.Body).Decode(&org)
}

// CreateOrganization creates an organization and adds the provided user as an admin.
func (c *Client) CreateOrganization(ctx context.Context, req CreateOrganizationRequest) (Organization, error) {
res, err := c.Request(ctx, http.MethodPost, "/api/v2/organizations", req)
if err != nil {
return Organization{}, err
}
defer res.Body.Close()

if res.StatusCode != http.StatusCreated {
return Organization{}, ReadBodyAsError(res)
}

var org Organization
return org, json.NewDecoder(res.Body).Decode(&org)
}

// AuthMethods returns types of authentication available to the user.
func (c *Client) AuthMethods(ctx context.Context) (AuthMethods, error) {
res, err := c.Request(ctx, http.MethodGet, "/api/v2/users/authmethods", nil)
Expand Down