From 6e2ffd10fb2e531bf589e9a663089d98d85d7075 Mon Sep 17 00:00:00 2001 From: McKayla Washburn Date: Tue, 4 Jun 2024 23:20:37 +0000 Subject: [PATCH 1/8] feat: allow editing org display name and description --- coderd/apidoc/docs.go | 20 ++++- coderd/apidoc/swagger.json | 27 ++++++- coderd/database/dump.sql | 3 +- .../000215_organization_display_name.down.sql | 2 + .../000215_organization_display_name.up.sql | 10 +++ coderd/database/models.go | 1 + coderd/database/queries.sql.go | 47 ++++++++---- coderd/database/queries/organizations.sql | 8 +- coderd/organizations.go | 48 +++++++++--- coderd/organizations_test.go | 76 ++++++++++++++++--- coderd/workspacebuilds_test.go | 2 +- codersdk/organizations.go | 19 +++-- docs/api/organizations.md | 9 +++ docs/api/schemas.md | 36 +++++---- docs/api/users.md | 22 ++++-- site/src/api/typesGenerated.ts | 5 ++ site/src/testHelpers/entities.ts | 4 +- 17 files changed, 265 insertions(+), 74 deletions(-) create mode 100644 coderd/database/migrations/000215_organization_display_name.down.sql create mode 100644 coderd/database/migrations/000215_organization_display_name.up.sql diff --git a/coderd/apidoc/docs.go b/coderd/apidoc/docs.go index c5e2a6041526f..9f91156273bf4 100644 --- a/coderd/apidoc/docs.go +++ b/coderd/apidoc/docs.go @@ -8970,9 +8970,13 @@ const docTemplate = `{ "codersdk.CreateOrganizationRequest": { "type": "object", "required": [ + "display_name", "name" ], "properties": { + "display_name": { + "type": "string" + }, "name": { "type": "string" } @@ -10584,6 +10588,7 @@ const docTemplate = `{ "type": "object", "required": [ "created_at", + "display_name", "id", "is_default", "name", @@ -10594,6 +10599,12 @@ const docTemplate = `{ "type": "string", "format": "date-time" }, + "description": { + "type": "string" + }, + "display_name": { + "type": "string" + }, "id": { "type": "string", "format": "uuid" @@ -12299,10 +12310,13 @@ const docTemplate = `{ }, "codersdk.UpdateOrganizationRequest": { "type": "object", - "required": [ - "name" - ], "properties": { + "description": { + "type": "string" + }, + "display_name": { + "type": "string" + }, "name": { "type": "string" } diff --git a/coderd/apidoc/swagger.json b/coderd/apidoc/swagger.json index 66afad1f041f0..028be8f9722be 100644 --- a/coderd/apidoc/swagger.json +++ b/coderd/apidoc/swagger.json @@ -7993,8 +7993,11 @@ }, "codersdk.CreateOrganizationRequest": { "type": "object", - "required": ["name"], + "required": ["display_name", "name"], "properties": { + "display_name": { + "type": "string" + }, "name": { "type": "string" } @@ -9513,12 +9516,25 @@ }, "codersdk.Organization": { "type": "object", - "required": ["created_at", "id", "is_default", "name", "updated_at"], + "required": [ + "created_at", + "display_name", + "id", + "is_default", + "name", + "updated_at" + ], "properties": { "created_at": { "type": "string", "format": "date-time" }, + "description": { + "type": "string" + }, + "display_name": { + "type": "string" + }, "id": { "type": "string", "format": "uuid" @@ -11139,8 +11155,13 @@ }, "codersdk.UpdateOrganizationRequest": { "type": "object", - "required": ["name"], "properties": { + "description": { + "type": "string" + }, + "display_name": { + "type": "string" + }, "name": { "type": "string" } diff --git a/coderd/database/dump.sql b/coderd/database/dump.sql index fde9c9556ac84..9f04b81be885e 100644 --- a/coderd/database/dump.sql +++ b/coderd/database/dump.sql @@ -585,7 +585,8 @@ CREATE TABLE organizations ( description text NOT NULL, created_at timestamp with time zone NOT NULL, updated_at timestamp with time zone NOT NULL, - is_default boolean DEFAULT false NOT NULL + is_default boolean DEFAULT false NOT NULL, + display_name text NOT NULL ); CREATE TABLE parameter_schemas ( diff --git a/coderd/database/migrations/000215_organization_display_name.down.sql b/coderd/database/migrations/000215_organization_display_name.down.sql new file mode 100644 index 0000000000000..4dea440465b11 --- /dev/null +++ b/coderd/database/migrations/000215_organization_display_name.down.sql @@ -0,0 +1,2 @@ +alter table organizations + drop column display_name; diff --git a/coderd/database/migrations/000215_organization_display_name.up.sql b/coderd/database/migrations/000215_organization_display_name.up.sql new file mode 100644 index 0000000000000..26245f03fc525 --- /dev/null +++ b/coderd/database/migrations/000215_organization_display_name.up.sql @@ -0,0 +1,10 @@ +-- This default is just a temporary thing to avoid null errors when first creating the column. +alter table organizations + add column display_name text not null default ''; + +update organizations + set display_name = name; + +-- We can remove the default now that everything has been copied. +alter table organizations + alter column display_name drop default; diff --git a/coderd/database/models.go b/coderd/database/models.go index e5ba9fcea6841..aa69054abc2aa 100644 --- a/coderd/database/models.go +++ b/coderd/database/models.go @@ -1927,6 +1927,7 @@ type Organization struct { CreatedAt time.Time `db:"created_at" json:"created_at"` UpdatedAt time.Time `db:"updated_at" json:"updated_at"` IsDefault bool `db:"is_default" json:"is_default"` + DisplayName string `db:"display_name" json:"display_name"` } type OrganizationMember struct { diff --git a/coderd/database/queries.sql.go b/coderd/database/queries.sql.go index 5bc7552117b58..a4ae5cedb3bfa 100644 --- a/coderd/database/queries.sql.go +++ b/coderd/database/queries.sql.go @@ -3949,7 +3949,7 @@ func (q *sqlQuerier) DeleteOrganization(ctx context.Context, id uuid.UUID) error const getDefaultOrganization = `-- name: GetDefaultOrganization :one SELECT - id, name, description, created_at, updated_at, is_default + id, name, description, created_at, updated_at, is_default, display_name FROM organizations WHERE @@ -3968,13 +3968,14 @@ func (q *sqlQuerier) GetDefaultOrganization(ctx context.Context) (Organization, &i.CreatedAt, &i.UpdatedAt, &i.IsDefault, + &i.DisplayName, ) return i, err } const getOrganizationByID = `-- name: GetOrganizationByID :one SELECT - id, name, description, created_at, updated_at, is_default + id, name, description, created_at, updated_at, is_default, display_name FROM organizations WHERE @@ -3991,13 +3992,14 @@ func (q *sqlQuerier) GetOrganizationByID(ctx context.Context, id uuid.UUID) (Org &i.CreatedAt, &i.UpdatedAt, &i.IsDefault, + &i.DisplayName, ) return i, err } const getOrganizationByName = `-- name: GetOrganizationByName :one SELECT - id, name, description, created_at, updated_at, is_default + id, name, description, created_at, updated_at, is_default, display_name FROM organizations WHERE @@ -4016,13 +4018,14 @@ func (q *sqlQuerier) GetOrganizationByName(ctx context.Context, name string) (Or &i.CreatedAt, &i.UpdatedAt, &i.IsDefault, + &i.DisplayName, ) return i, err } const getOrganizations = `-- name: GetOrganizations :many SELECT - id, name, description, created_at, updated_at, is_default + id, name, description, created_at, updated_at, is_default, display_name FROM organizations ` @@ -4043,6 +4046,7 @@ func (q *sqlQuerier) GetOrganizations(ctx context.Context) ([]Organization, erro &i.CreatedAt, &i.UpdatedAt, &i.IsDefault, + &i.DisplayName, ); err != nil { return nil, err } @@ -4059,7 +4063,7 @@ func (q *sqlQuerier) GetOrganizations(ctx context.Context) ([]Organization, erro const getOrganizationsByUserID = `-- name: GetOrganizationsByUserID :many SELECT - id, name, description, created_at, updated_at, is_default + id, name, description, created_at, updated_at, is_default, display_name FROM organizations WHERE @@ -4089,6 +4093,7 @@ func (q *sqlQuerier) GetOrganizationsByUserID(ctx context.Context, userID uuid.U &i.CreatedAt, &i.UpdatedAt, &i.IsDefault, + &i.DisplayName, ); err != nil { return nil, err } @@ -4105,15 +4110,16 @@ func (q *sqlQuerier) GetOrganizationsByUserID(ctx context.Context, userID uuid.U const insertOrganization = `-- name: InsertOrganization :one INSERT INTO - organizations (id, "name", description, created_at, updated_at, is_default) + organizations (id, "name", display_name, description, created_at, updated_at, is_default) VALUES -- If no organizations exist, and this is the first, make it the default. - ($1, $2, $3, $4, $5, (SELECT TRUE FROM organizations LIMIT 1) IS NULL) RETURNING id, name, description, created_at, updated_at, is_default + ($1, $2, $3, $4, $5, $6, (SELECT TRUE FROM organizations LIMIT 1) IS NULL) RETURNING id, name, description, created_at, updated_at, is_default, display_name ` type InsertOrganizationParams struct { ID uuid.UUID `db:"id" json:"id"` Name string `db:"name" json:"name"` + DisplayName string `db:"display_name" json:"display_name"` Description string `db:"description" json:"description"` CreatedAt time.Time `db:"created_at" json:"created_at"` UpdatedAt time.Time `db:"updated_at" json:"updated_at"` @@ -4123,6 +4129,7 @@ func (q *sqlQuerier) InsertOrganization(ctx context.Context, arg InsertOrganizat row := q.db.QueryRowContext(ctx, insertOrganization, arg.ID, arg.Name, + arg.DisplayName, arg.Description, arg.CreatedAt, arg.UpdatedAt, @@ -4135,6 +4142,7 @@ func (q *sqlQuerier) InsertOrganization(ctx context.Context, arg InsertOrganizat &i.CreatedAt, &i.UpdatedAt, &i.IsDefault, + &i.DisplayName, ) return i, err } @@ -4144,20 +4152,30 @@ UPDATE organizations SET updated_at = $1, - name = $2 + name = $2, + display_name = $3, + description = $4 WHERE - id = $3 -RETURNING id, name, description, created_at, updated_at, is_default + id = $5 +RETURNING id, name, description, created_at, updated_at, is_default, display_name ` type UpdateOrganizationParams struct { - UpdatedAt time.Time `db:"updated_at" json:"updated_at"` - Name string `db:"name" json:"name"` - ID uuid.UUID `db:"id" json:"id"` + UpdatedAt time.Time `db:"updated_at" json:"updated_at"` + Name string `db:"name" json:"name"` + DisplayName string `db:"display_name" json:"display_name"` + Description string `db:"description" json:"description"` + ID uuid.UUID `db:"id" json:"id"` } func (q *sqlQuerier) UpdateOrganization(ctx context.Context, arg UpdateOrganizationParams) (Organization, error) { - row := q.db.QueryRowContext(ctx, updateOrganization, arg.UpdatedAt, arg.Name, arg.ID) + row := q.db.QueryRowContext(ctx, updateOrganization, + arg.UpdatedAt, + arg.Name, + arg.DisplayName, + arg.Description, + arg.ID, + ) var i Organization err := row.Scan( &i.ID, @@ -4166,6 +4184,7 @@ func (q *sqlQuerier) UpdateOrganization(ctx context.Context, arg UpdateOrganizat &i.CreatedAt, &i.UpdatedAt, &i.IsDefault, + &i.DisplayName, ) return i, err } diff --git a/coderd/database/queries/organizations.sql b/coderd/database/queries/organizations.sql index 9d5cec1324fe6..dbefb9f8ad711 100644 --- a/coderd/database/queries/organizations.sql +++ b/coderd/database/queries/organizations.sql @@ -49,17 +49,19 @@ WHERE -- name: InsertOrganization :one INSERT INTO - organizations (id, "name", description, created_at, updated_at, is_default) + organizations (id, "name", display_name, description, created_at, updated_at, is_default) VALUES -- If no organizations exist, and this is the first, make it the default. - ($1, $2, $3, $4, $5, (SELECT TRUE FROM organizations LIMIT 1) IS NULL) RETURNING *; + (@id, @name, @display_name, @description, @created_at, @updated_at, (SELECT TRUE FROM organizations LIMIT 1) IS NULL) RETURNING *; -- name: UpdateOrganization :one UPDATE organizations SET updated_at = @updated_at, - name = @name + name = @name, + display_name = @display_name, + description = @description WHERE id = @id RETURNING *; diff --git a/coderd/organizations.go b/coderd/organizations.go index 2a43ed2a7011a..6b60c553575b4 100644 --- a/coderd/organizations.go +++ b/coderd/organizations.go @@ -77,6 +77,7 @@ func (api *API) postOrganizations(rw http.ResponseWriter, r *http.Request) { organization, err = tx.InsertOrganization(ctx, database.InsertOrganizationParams{ ID: uuid.New(), Name: req.Name, + DisplayName: req.DisplayName, CreatedAt: dbtime.Now(), UpdatedAt: dbtime.Now(), Description: "", @@ -146,11 +147,38 @@ func (api *API) patchOrganization(rw http.ResponseWriter, r *http.Request) { return } - organization, err := api.Database.UpdateOrganization(ctx, database.UpdateOrganizationParams{ - ID: organization.ID, - UpdatedAt: dbtime.Now(), - Name: req.Name, + err := database.ReadModifyUpdate(api.Database, func(tx database.Store) error { + var err error + organization, err = tx.GetOrganizationByID(ctx, organization.ID) + if err != nil { + return err + } + + updateOrgParams := database.UpdateOrganizationParams{ + UpdatedAt: dbtime.Now(), + ID: organization.ID, + Name: organization.Name, + DisplayName: organization.DisplayName, + Description: organization.Description, + } + + if req.Name != "" { + updateOrgParams.Name = req.Name + } + if req.DisplayName != "" { + updateOrgParams.DisplayName = req.DisplayName + } + if req.Description != "" { + updateOrgParams.Description = req.Description + } + + organization, err = tx.UpdateOrganization(ctx, updateOrgParams) + if err != nil { + return err + } + return nil }) + if httpapi.Is404Error(err) { httpapi.ResourceNotFound(rw) return @@ -212,10 +240,12 @@ func (api *API) deleteOrganization(rw http.ResponseWriter, r *http.Request) { // convertOrganization consumes the database representation and outputs an API friendly representation. func convertOrganization(organization database.Organization) codersdk.Organization { return codersdk.Organization{ - ID: organization.ID, - Name: organization.Name, - CreatedAt: organization.CreatedAt, - UpdatedAt: organization.UpdatedAt, - IsDefault: organization.IsDefault, + ID: organization.ID, + Name: organization.Name, + DisplayName: organization.DisplayName, + Description: organization.Description, + CreatedAt: organization.CreatedAt, + UpdatedAt: organization.UpdatedAt, + IsDefault: organization.IsDefault, } } diff --git a/coderd/organizations_test.go b/coderd/organizations_test.go index 8ce39c5593d90..55bf7c5e54ad7 100644 --- a/coderd/organizations_test.go +++ b/coderd/organizations_test.go @@ -20,7 +20,8 @@ func TestMultiOrgFetch(t *testing.T) { makeOrgs := []string{"foo", "bar", "baz"} for _, name := range makeOrgs { _, err := client.CreateOrganization(ctx, codersdk.CreateOrganizationRequest{ - Name: name, + Name: name, + DisplayName: name, }) require.NoError(t, err) } @@ -45,7 +46,8 @@ func TestOrganizationsByUser(t *testing.T) { // Make an extra org, and it should not be defaulted. notDefault, err := client.CreateOrganization(ctx, codersdk.CreateOrganizationRequest{ - Name: "another", + Name: "another", + DisplayName: "Another", }) require.NoError(t, err) require.False(t, notDefault.IsDefault, "only 1 default org allowed") @@ -73,7 +75,8 @@ func TestOrganizationByUserAndName(t *testing.T) { ctx := testutil.Context(t, testutil.WaitLong) org, err := client.CreateOrganization(ctx, codersdk.CreateOrganizationRequest{ - Name: "another", + Name: "another", + DisplayName: "Another", }) require.NoError(t, err) _, err = other.OrganizationByUserAndName(ctx, codersdk.Me, org.Name) @@ -106,7 +109,8 @@ func TestPostOrganizationsByUser(t *testing.T) { org, err := client.Organization(ctx, user.OrganizationID) require.NoError(t, err) _, err = client.CreateOrganization(ctx, codersdk.CreateOrganizationRequest{ - Name: org.Name, + Name: org.Name, + DisplayName: org.DisplayName, }) var apiErr *codersdk.Error require.ErrorAs(t, err, &apiErr) @@ -120,7 +124,8 @@ func TestPostOrganizationsByUser(t *testing.T) { ctx := testutil.Context(t, testutil.WaitLong) _, err := client.CreateOrganization(ctx, codersdk.CreateOrganizationRequest{ - Name: "new", + Name: "new", + DisplayName: "New", }) require.NoError(t, err) }) @@ -137,7 +142,8 @@ func TestPatchOrganizationsByUser(t *testing.T) { originalOrg, err := client.Organization(ctx, user.OrganizationID) require.NoError(t, err) o, err := client.CreateOrganization(ctx, codersdk.CreateOrganizationRequest{ - Name: "something-unique", + Name: "something-unique", + DisplayName: "Something Unique", }) require.NoError(t, err) @@ -156,7 +162,8 @@ func TestPatchOrganizationsByUser(t *testing.T) { ctx := testutil.Context(t, testutil.WaitMedium) o, err := client.CreateOrganization(ctx, codersdk.CreateOrganizationRequest{ - Name: "something-unique", + Name: "something-unique", + DisplayName: "Something Unique", }) require.NoError(t, err) @@ -175,7 +182,8 @@ func TestPatchOrganizationsByUser(t *testing.T) { ctx := testutil.Context(t, testutil.WaitMedium) o, err := client.CreateOrganization(ctx, codersdk.CreateOrganizationRequest{ - Name: "new", + Name: "new", + DisplayName: "New", }) require.NoError(t, err) @@ -193,7 +201,8 @@ func TestPatchOrganizationsByUser(t *testing.T) { ctx := testutil.Context(t, testutil.WaitMedium) o, err := client.CreateOrganization(ctx, codersdk.CreateOrganizationRequest{ - Name: "new", + Name: "new", + DisplayName: "New", }) require.NoError(t, err) @@ -202,6 +211,49 @@ func TestPatchOrganizationsByUser(t *testing.T) { }) require.NoError(t, err) require.Equal(t, "new-new", o.Name) + require.Equal(t, "New", o.DisplayName) // didn't change + }) + + t.Run("UpdateDisplayName", func(t *testing.T) { + t.Parallel() + client := coderdtest.New(t, nil) + _ = coderdtest.CreateFirstUser(t, client) + ctx := testutil.Context(t, testutil.WaitMedium) + + o, err := client.CreateOrganization(ctx, codersdk.CreateOrganizationRequest{ + Name: "new", + DisplayName: "New", + }) + require.NoError(t, err) + + o, err = client.UpdateOrganization(ctx, o.Name, codersdk.UpdateOrganizationRequest{ + DisplayName: "The Newest One", + }) + require.NoError(t, err) + require.Equal(t, "new", o.Name) // didn't change + require.Equal(t, "The Newest One", o.DisplayName) + }) + + t.Run("UpdateDescription", func(t *testing.T) { + t.Parallel() + client := coderdtest.New(t, nil) + _ = coderdtest.CreateFirstUser(t, client) + ctx := testutil.Context(t, testutil.WaitMedium) + + o, err := client.CreateOrganization(ctx, codersdk.CreateOrganizationRequest{ + Name: "new", + DisplayName: "New", + }) + require.NoError(t, err) + + o, err = client.UpdateOrganization(ctx, o.Name, codersdk.UpdateOrganizationRequest{ + Description: "wow, this organization description is so updated!", + }) + + require.NoError(t, err) + require.Equal(t, "new", o.Name) // didn't change + require.Equal(t, "New", o.DisplayName) // didn't change + require.Equal(t, "wow, this organization description is so updated!", o.Description) }) } @@ -229,7 +281,8 @@ func TestDeleteOrganizationsByUser(t *testing.T) { ctx := testutil.Context(t, testutil.WaitMedium) o, err := client.CreateOrganization(ctx, codersdk.CreateOrganizationRequest{ - Name: "doomed", + Name: "doomed", + DisplayName: "Doomed", }) require.NoError(t, err) @@ -244,7 +297,8 @@ func TestDeleteOrganizationsByUser(t *testing.T) { ctx := testutil.Context(t, testutil.WaitMedium) o, err := client.CreateOrganization(ctx, codersdk.CreateOrganizationRequest{ - Name: "doomed", + Name: "doomed", + DisplayName: "Doomed", }) require.NoError(t, err) diff --git a/coderd/workspacebuilds_test.go b/coderd/workspacebuilds_test.go index eb76239b84658..5d99e56820aa1 100644 --- a/coderd/workspacebuilds_test.go +++ b/coderd/workspacebuilds_test.go @@ -728,7 +728,7 @@ func TestWorkspaceDeleteSuspendedUser(t *testing.T) { validateCalls++ if userSuspended { // Simulate the user being suspended from the IDP too. - return "", http.StatusForbidden, fmt.Errorf("user is suspended") + return "", http.StatusForbidden, xerrors.New("user is suspended") } return "OK", 0, nil }, diff --git a/codersdk/organizations.go b/codersdk/organizations.go index 646eae71d2475..2b6c625f8bbaa 100644 --- a/codersdk/organizations.go +++ b/codersdk/organizations.go @@ -40,11 +40,13 @@ func ProvisionerTypeValid[T ProvisionerType | string](pt T) error { // Organization is the JSON representation of a Coder organization. type Organization struct { - ID uuid.UUID `table:"id" json:"id" validate:"required" format:"uuid"` - Name string `table:"name,default_sort" json:"name" validate:"required"` - CreatedAt time.Time `table:"created_at" json:"created_at" validate:"required" format:"date-time"` - UpdatedAt time.Time `table:"updated_at" json:"updated_at" validate:"required" format:"date-time"` - IsDefault bool `table:"default" json:"is_default" validate:"required"` + ID uuid.UUID `table:"id" json:"id" validate:"required" format:"uuid"` + Name string `table:"name,default_sort" json:"name" validate:"required,username"` + DisplayName string `table:"display_name" json:"display_name" validate:"required"` + Description string `table:"description" json:"description"` + CreatedAt time.Time `table:"created_at" json:"created_at" validate:"required" format:"date-time"` + UpdatedAt time.Time `table:"updated_at" json:"updated_at" validate:"required" format:"date-time"` + IsDefault bool `table:"default" json:"is_default" validate:"required"` } type OrganizationMember struct { @@ -56,11 +58,14 @@ type OrganizationMember struct { } type CreateOrganizationRequest struct { - Name string `json:"name" validate:"required,username"` + Name string `json:"name" validate:"required,username"` + DisplayName string `json:"display_name" validate:"required"` } type UpdateOrganizationRequest struct { - Name string `json:"name" validate:"required,username"` + Name string `json:"name" validate:"omitempty,username"` + DisplayName string `json:"display_name"` + Description string `json:"description"` } // CreateTemplateVersionRequest enables callers to create a new Template Version. diff --git a/docs/api/organizations.md b/docs/api/organizations.md index c6f4514eb9bad..31fead270b2d3 100644 --- a/docs/api/organizations.md +++ b/docs/api/organizations.md @@ -105,6 +105,7 @@ curl -X POST http://coder-server:8080/api/v2/organizations \ ```json { + "display_name": "string", "name": "string" } ``` @@ -122,6 +123,8 @@ curl -X POST http://coder-server:8080/api/v2/organizations \ ```json { "created_at": "2019-08-24T14:15:22Z", + "description": "string", + "display_name": "string", "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", "is_default": true, "name": "string", @@ -163,6 +166,8 @@ curl -X GET http://coder-server:8080/api/v2/organizations/{organization} \ ```json { "created_at": "2019-08-24T14:15:22Z", + "description": "string", + "display_name": "string", "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", "is_default": true, "name": "string", @@ -240,6 +245,8 @@ curl -X PATCH http://coder-server:8080/api/v2/organizations/{organization} \ ```json { + "description": "string", + "display_name": "string", "name": "string" } ``` @@ -258,6 +265,8 @@ curl -X PATCH http://coder-server:8080/api/v2/organizations/{organization} \ ```json { "created_at": "2019-08-24T14:15:22Z", + "description": "string", + "display_name": "string", "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", "is_default": true, "name": "string", diff --git a/docs/api/schemas.md b/docs/api/schemas.md index 7770b091878bd..1e69329fb3dcb 100644 --- a/docs/api/schemas.md +++ b/docs/api/schemas.md @@ -1414,15 +1414,17 @@ AuthorizationObject can represent a "set" of objects, such as: all workspaces in ```json { + "display_name": "string", "name": "string" } ``` ### Properties -| Name | Type | Required | Restrictions | Description | -| ------ | ------ | -------- | ------------ | ----------- | -| `name` | string | true | | | +| Name | Type | Required | Restrictions | Description | +| -------------- | ------ | -------- | ------------ | ----------- | +| `display_name` | string | true | | | +| `name` | string | true | | | ## codersdk.CreateTemplateRequest @@ -3590,6 +3592,8 @@ CreateWorkspaceRequest provides options for creating a new workspace. Only one o ```json { "created_at": "2019-08-24T14:15:22Z", + "description": "string", + "display_name": "string", "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", "is_default": true, "name": "string", @@ -3599,13 +3603,15 @@ CreateWorkspaceRequest provides options for creating a new workspace. Only one o ### Properties -| Name | Type | Required | Restrictions | Description | -| ------------ | ------- | -------- | ------------ | ----------- | -| `created_at` | string | true | | | -| `id` | string | true | | | -| `is_default` | boolean | true | | | -| `name` | string | true | | | -| `updated_at` | string | true | | | +| Name | Type | Required | Restrictions | Description | +| -------------- | ------- | -------- | ------------ | ----------- | +| `created_at` | string | true | | | +| `description` | string | false | | | +| `display_name` | string | true | | | +| `id` | string | true | | | +| `is_default` | boolean | true | | | +| `name` | string | true | | | +| `updated_at` | string | true | | | ## codersdk.OrganizationMember @@ -5367,15 +5373,19 @@ CreateWorkspaceRequest provides options for creating a new workspace. Only one o ```json { + "description": "string", + "display_name": "string", "name": "string" } ``` ### Properties -| Name | Type | Required | Restrictions | Description | -| ------ | ------ | -------- | ------------ | ----------- | -| `name` | string | true | | | +| Name | Type | Required | Restrictions | Description | +| -------------- | ------ | -------- | ------------ | ----------- | +| `description` | string | false | | | +| `display_name` | string | false | | | +| `name` | string | false | | | ## codersdk.UpdateRoles diff --git a/docs/api/users.md b/docs/api/users.md index c9910bf66c1c7..c1df07a84baf2 100644 --- a/docs/api/users.md +++ b/docs/api/users.md @@ -993,6 +993,8 @@ curl -X GET http://coder-server:8080/api/v2/users/{user}/organizations \ [ { "created_at": "2019-08-24T14:15:22Z", + "description": "string", + "display_name": "string", "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", "is_default": true, "name": "string", @@ -1011,14 +1013,16 @@ curl -X GET http://coder-server:8080/api/v2/users/{user}/organizations \ Status Code **200** -| Name | Type | Required | Restrictions | Description | -| -------------- | ----------------- | -------- | ------------ | ----------- | -| `[array item]` | array | false | | | -| `» created_at` | string(date-time) | true | | | -| `» id` | string(uuid) | true | | | -| `» is_default` | boolean | true | | | -| `» name` | string | true | | | -| `» updated_at` | string(date-time) | true | | | +| Name | Type | Required | Restrictions | Description | +| ---------------- | ----------------- | -------- | ------------ | ----------- | +| `[array item]` | array | false | | | +| `» created_at` | string(date-time) | true | | | +| `» description` | string | false | | | +| `» display_name` | string | true | | | +| `» id` | string(uuid) | true | | | +| `» is_default` | boolean | true | | | +| `» name` | string | true | | | +| `» updated_at` | string(date-time) | true | | | To perform this operation, you must be authenticated. [Learn more](authentication.md). @@ -1049,6 +1053,8 @@ curl -X GET http://coder-server:8080/api/v2/users/{user}/organizations/{organiza ```json { "created_at": "2019-08-24T14:15:22Z", + "description": "string", + "display_name": "string", "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", "is_default": true, "name": "string", diff --git a/site/src/api/typesGenerated.ts b/site/src/api/typesGenerated.ts index 88e5c7e508f67..1f8e6ed87b246 100644 --- a/site/src/api/typesGenerated.ts +++ b/site/src/api/typesGenerated.ts @@ -226,6 +226,7 @@ export interface CreateGroupRequest { // From codersdk/organizations.go export interface CreateOrganizationRequest { readonly name: string; + readonly display_name: string; } // From codersdk/organizations.go @@ -776,6 +777,8 @@ export interface OIDCConfig { export interface Organization { readonly id: string; readonly name: string; + readonly display_name: string; + readonly description: string; readonly created_at: string; readonly updated_at: string; readonly is_default: boolean; @@ -1322,6 +1325,8 @@ export interface UpdateCheckResponse { // From codersdk/organizations.go export interface UpdateOrganizationRequest { readonly name: string; + readonly display_name: string; + readonly description: string; } // From codersdk/users.go diff --git a/site/src/testHelpers/entities.ts b/site/src/testHelpers/entities.ts index 1e2cf21e23383..424c1b7ef331d 100644 --- a/site/src/testHelpers/entities.ts +++ b/site/src/testHelpers/entities.ts @@ -13,7 +13,9 @@ import type { TemplateVersionFiles } from "utils/templateVersion"; export const MockOrganization: TypesGen.Organization = { id: "fc0774ce-cc9e-48d4-80ae-88f7a4d4a8b0", - name: "Test Organization", + name: "test-organization", + display_name: "Test Organization", + description: "", created_at: "", updated_at: "", is_default: true, From 3904a0775740a033a5b90c3d1a56e7387c983623 Mon Sep 17 00:00:00 2001 From: McKayla Washburn Date: Wed, 5 Jun 2024 16:17:08 +0000 Subject: [PATCH 2/8] fix dbmem and add additional tests --- coderd/database/dbmem/dbmem.go | 15 ++++++++----- coderd/organizations.go | 2 +- coderd/organizations_test.go | 41 +++++++++++++++++++++++++++++++++- codersdk/organizations.go | 1 + 4 files changed, 52 insertions(+), 7 deletions(-) diff --git a/coderd/database/dbmem/dbmem.go b/coderd/database/dbmem/dbmem.go index fe9b56e35ebdb..08e29f2da4bb4 100644 --- a/coderd/database/dbmem/dbmem.go +++ b/coderd/database/dbmem/dbmem.go @@ -86,6 +86,7 @@ func New() database.Store { defaultOrg, err := q.InsertOrganization(context.Background(), database.InsertOrganizationParams{ ID: uuid.New(), Name: "first-organization", + DisplayName: "first-organization", Description: "Builtin default organization.", CreatedAt: dbtime.Now(), UpdatedAt: dbtime.Now(), @@ -6177,11 +6178,13 @@ func (q *FakeQuerier) InsertOrganization(_ context.Context, arg database.InsertO defer q.mutex.Unlock() organization := database.Organization{ - ID: arg.ID, - Name: arg.Name, - CreatedAt: arg.CreatedAt, - UpdatedAt: arg.UpdatedAt, - IsDefault: len(q.organizations) == 0, + ID: arg.ID, + Name: arg.Name, + DisplayName: arg.DisplayName, + Description: arg.Description, + CreatedAt: arg.CreatedAt, + UpdatedAt: arg.UpdatedAt, + IsDefault: len(q.organizations) == 0, } q.organizations = append(q.organizations, organization) return organization, nil @@ -7322,6 +7325,8 @@ func (q *FakeQuerier) UpdateOrganization(_ context.Context, arg database.UpdateO for i, org := range q.organizations { if org.ID == arg.ID { org.Name = arg.Name + org.DisplayName = arg.DisplayName + org.Description = arg.Description q.organizations[i] = org return org, nil } diff --git a/coderd/organizations.go b/coderd/organizations.go index 6b60c553575b4..bf5685d4a4235 100644 --- a/coderd/organizations.go +++ b/coderd/organizations.go @@ -78,9 +78,9 @@ func (api *API) postOrganizations(rw http.ResponseWriter, r *http.Request) { ID: uuid.New(), Name: req.Name, DisplayName: req.DisplayName, + Description: req.Description, CreatedAt: dbtime.Now(), UpdatedAt: dbtime.Now(), - Description: "", }) if err != nil { return xerrors.Errorf("create organization: %w", err) diff --git a/coderd/organizations_test.go b/coderd/organizations_test.go index 55bf7c5e54ad7..5ee8a41307c9c 100644 --- a/coderd/organizations_test.go +++ b/coderd/organizations_test.go @@ -117,17 +117,36 @@ func TestPostOrganizationsByUser(t *testing.T) { require.Equal(t, http.StatusConflict, apiErr.StatusCode()) }) - t.Run("Create", func(t *testing.T) { + t.Run("InvalidName", func(t *testing.T) { t.Parallel() client := coderdtest.New(t, nil) _ = coderdtest.CreateFirstUser(t, client) ctx := testutil.Context(t, testutil.WaitLong) _, err := client.CreateOrganization(ctx, codersdk.CreateOrganizationRequest{ + Name: "A name which is definitely! not! url! safe!", + DisplayName: "New", + }) + var apiErr *codersdk.Error + require.ErrorAs(t, err, &apiErr) + require.Equal(t, http.StatusBadRequest, apiErr.StatusCode()) + }) + + t.Run("Create", func(t *testing.T) { + t.Parallel() + client := coderdtest.New(t, nil) + _ = coderdtest.CreateFirstUser(t, client) + ctx := testutil.Context(t, testutil.WaitLong) + + o, err := client.CreateOrganization(ctx, codersdk.CreateOrganizationRequest{ Name: "new", DisplayName: "New", + Description: "A new organization to love and cherish forever.", }) require.NoError(t, err) + require.Equal(t, "new", o.Name) + require.Equal(t, "New", o.DisplayName) + require.Equal(t, "A new organization to love and cherish forever.", o.Description) }) } @@ -175,6 +194,26 @@ func TestPatchOrganizationsByUser(t *testing.T) { require.Equal(t, http.StatusBadRequest, apiErr.StatusCode()) }) + t.Run("InvalidName", func(t *testing.T) { + t.Parallel() + client := coderdtest.New(t, nil) + _ = coderdtest.CreateFirstUser(t, client) + ctx := testutil.Context(t, testutil.WaitMedium) + + o, err := client.CreateOrganization(ctx, codersdk.CreateOrganizationRequest{ + Name: "something-unique", + DisplayName: "Something Unique", + }) + require.NoError(t, err) + + _, err = client.UpdateOrganization(ctx, o.ID.String(), codersdk.UpdateOrganizationRequest{ + Name: "something! unique! but not! url! safe!", + }) + var apiErr *codersdk.Error + require.ErrorAs(t, err, &apiErr) + require.Equal(t, http.StatusBadRequest, apiErr.StatusCode()) + }) + t.Run("UpdateById", func(t *testing.T) { t.Parallel() client := coderdtest.New(t, nil) diff --git a/codersdk/organizations.go b/codersdk/organizations.go index 2b6c625f8bbaa..457c6926b5216 100644 --- a/codersdk/organizations.go +++ b/codersdk/organizations.go @@ -60,6 +60,7 @@ type OrganizationMember struct { type CreateOrganizationRequest struct { Name string `json:"name" validate:"required,username"` DisplayName string `json:"display_name" validate:"required"` + Description string `json:"description"` } type UpdateOrganizationRequest struct { From 50d046f7c2a994a2c0373ae8ffa08a5e16de94a9 Mon Sep 17 00:00:00 2001 From: McKayla Washburn Date: Wed, 5 Jun 2024 16:38:51 +0000 Subject: [PATCH 3/8] =?UTF-8?q?=F0=9F=A7=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- coderd/database/dbgen/dbgen.go | 1 + coderd/organizations_test.go | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/coderd/database/dbgen/dbgen.go b/coderd/database/dbgen/dbgen.go index 4dea6bdb39f75..61c44d0779307 100644 --- a/coderd/database/dbgen/dbgen.go +++ b/coderd/database/dbgen/dbgen.go @@ -336,6 +336,7 @@ func Organization(t testing.TB, db database.Store, orig database.Organization) d org, err := db.InsertOrganization(genCtx, database.InsertOrganizationParams{ ID: takeFirst(orig.ID, uuid.New()), Name: takeFirst(orig.Name, namesgenerator.GetRandomName(1)), + DisplayName: takeFirst(orig.Name, namesgenerator.GetRandomName(1)), Description: takeFirst(orig.Description, namesgenerator.GetRandomName(1)), CreatedAt: takeFirst(orig.CreatedAt, dbtime.Now()), UpdatedAt: takeFirst(orig.UpdatedAt, dbtime.Now()), diff --git a/coderd/organizations_test.go b/coderd/organizations_test.go index 5ee8a41307c9c..9011bc0428e21 100644 --- a/coderd/organizations_test.go +++ b/coderd/organizations_test.go @@ -124,7 +124,7 @@ func TestPostOrganizationsByUser(t *testing.T) { ctx := testutil.Context(t, testutil.WaitLong) _, err := client.CreateOrganization(ctx, codersdk.CreateOrganizationRequest{ - Name: "A name which is definitely! not! url! safe!", + Name: "A name which is definitely not url safe", DisplayName: "New", }) var apiErr *codersdk.Error @@ -207,7 +207,7 @@ func TestPatchOrganizationsByUser(t *testing.T) { require.NoError(t, err) _, err = client.UpdateOrganization(ctx, o.ID.String(), codersdk.UpdateOrganizationRequest{ - Name: "something! unique! but not! url! safe!", + Name: "something unique but not url safe", }) var apiErr *codersdk.Error require.ErrorAs(t, err, &apiErr) From 7e77d6a8ecc9115385a9fadfb2e8301788a8c205 Mon Sep 17 00:00:00 2001 From: McKayla Washburn Date: Wed, 5 Jun 2024 16:44:13 +0000 Subject: [PATCH 4/8] =?UTF-8?q?=F0=9F=94=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- coderd/apidoc/docs.go | 3 +++ coderd/apidoc/swagger.json | 3 +++ codersdk/organizations.go | 8 ++++---- docs/api/organizations.md | 1 + docs/api/schemas.md | 2 ++ site/src/api/typesGenerated.ts | 7 ++++--- 6 files changed, 17 insertions(+), 7 deletions(-) diff --git a/coderd/apidoc/docs.go b/coderd/apidoc/docs.go index 9f91156273bf4..424b9b57cc8ef 100644 --- a/coderd/apidoc/docs.go +++ b/coderd/apidoc/docs.go @@ -8974,6 +8974,9 @@ const docTemplate = `{ "name" ], "properties": { + "description": { + "type": "string" + }, "display_name": { "type": "string" }, diff --git a/coderd/apidoc/swagger.json b/coderd/apidoc/swagger.json index 028be8f9722be..1a78756ae203b 100644 --- a/coderd/apidoc/swagger.json +++ b/coderd/apidoc/swagger.json @@ -7995,6 +7995,9 @@ "type": "object", "required": ["display_name", "name"], "properties": { + "description": { + "type": "string" + }, "display_name": { "type": "string" }, diff --git a/codersdk/organizations.go b/codersdk/organizations.go index 457c6926b5216..445bfa9a39b09 100644 --- a/codersdk/organizations.go +++ b/codersdk/organizations.go @@ -60,13 +60,13 @@ type OrganizationMember struct { type CreateOrganizationRequest struct { Name string `json:"name" validate:"required,username"` DisplayName string `json:"display_name" validate:"required"` - Description string `json:"description"` + Description string `json:"description,omitempty"` } type UpdateOrganizationRequest struct { - Name string `json:"name" validate:"omitempty,username"` - DisplayName string `json:"display_name"` - Description string `json:"description"` + Name string `json:"name,omitempty" validate:"omitempty,username"` + DisplayName string `json:"display_name,omitempty"` + Description string `json:"description,omitempty"` } // CreateTemplateVersionRequest enables callers to create a new Template Version. diff --git a/docs/api/organizations.md b/docs/api/organizations.md index 31fead270b2d3..820d4be64d281 100644 --- a/docs/api/organizations.md +++ b/docs/api/organizations.md @@ -105,6 +105,7 @@ curl -X POST http://coder-server:8080/api/v2/organizations \ ```json { + "description": "string", "display_name": "string", "name": "string" } diff --git a/docs/api/schemas.md b/docs/api/schemas.md index 1e69329fb3dcb..780daaacacd8d 100644 --- a/docs/api/schemas.md +++ b/docs/api/schemas.md @@ -1414,6 +1414,7 @@ AuthorizationObject can represent a "set" of objects, such as: all workspaces in ```json { + "description": "string", "display_name": "string", "name": "string" } @@ -1423,6 +1424,7 @@ AuthorizationObject can represent a "set" of objects, such as: all workspaces in | Name | Type | Required | Restrictions | Description | | -------------- | ------ | -------- | ------------ | ----------- | +| `description` | string | false | | | | `display_name` | string | true | | | | `name` | string | true | | | diff --git a/site/src/api/typesGenerated.ts b/site/src/api/typesGenerated.ts index 1f8e6ed87b246..3f604ea7d9aed 100644 --- a/site/src/api/typesGenerated.ts +++ b/site/src/api/typesGenerated.ts @@ -227,6 +227,7 @@ export interface CreateGroupRequest { export interface CreateOrganizationRequest { readonly name: string; readonly display_name: string; + readonly description?: string; } // From codersdk/organizations.go @@ -1324,9 +1325,9 @@ export interface UpdateCheckResponse { // From codersdk/organizations.go export interface UpdateOrganizationRequest { - readonly name: string; - readonly display_name: string; - readonly description: string; + readonly name?: string; + readonly display_name?: string; + readonly description?: string; } // From codersdk/users.go From 55be1bc80abafeb72988a0223124e1d03306368b Mon Sep 17 00:00:00 2001 From: McKayla Washburn Date: Wed, 5 Jun 2024 16:51:36 +0000 Subject: [PATCH 5/8] tweak validation --- coderd/httpapi/httpapi.go | 16 +++++++++------- coderd/httpapi/name.go | 4 ++-- coderd/httpapi/name_test.go | 2 +- codersdk/organizations.go | 8 ++++---- 4 files changed, 16 insertions(+), 14 deletions(-) diff --git a/coderd/httpapi/httpapi.go b/coderd/httpapi/httpapi.go index fb5e4361ec32c..e8229cf5477c1 100644 --- a/coderd/httpapi/httpapi.go +++ b/coderd/httpapi/httpapi.go @@ -46,25 +46,27 @@ func init() { valid := NameValid(str) return valid == nil } - for _, tag := range []string{"username", "template_name", "workspace_name", "oauth2_app_name"} { + for _, tag := range []string{"username", "organization_name", "template_name", "workspace_name", "oauth2_app_name"} { err := Validate.RegisterValidation(tag, nameValidator) if err != nil { panic(err) } } - templateDisplayNameValidator := func(fl validator.FieldLevel) bool { + displayNameValidator := func(fl validator.FieldLevel) bool { f := fl.Field().Interface() str, ok := f.(string) if !ok { return false } - valid := TemplateDisplayNameValid(str) + valid := DisplayNameValid(str) return valid == nil } - err := Validate.RegisterValidation("template_display_name", templateDisplayNameValidator) - if err != nil { - panic(err) + for _, displayNameTag := range []string{"organization_display_name", "template_display_name"} { + err := Validate.RegisterValidation(displayNameTag, displayNameValidator) + if err != nil { + panic(err) + } } templateVersionNameValidator := func(fl validator.FieldLevel) bool { @@ -76,7 +78,7 @@ func init() { valid := TemplateVersionNameValid(str) return valid == nil } - err = Validate.RegisterValidation("template_version_name", templateVersionNameValidator) + err := Validate.RegisterValidation("template_version_name", templateVersionNameValidator) if err != nil { panic(err) } diff --git a/coderd/httpapi/name.go b/coderd/httpapi/name.go index d8b64a71bdc44..2ef4585464bd2 100644 --- a/coderd/httpapi/name.go +++ b/coderd/httpapi/name.go @@ -65,8 +65,8 @@ func TemplateVersionNameValid(str string) error { return nil } -// TemplateDisplayNameValid returns whether the input string is a valid template display name. -func TemplateDisplayNameValid(str string) error { +// DisplayNameValid returns whether the input string is a valid template display name. +func DisplayNameValid(str string) error { if len(str) == 0 { return nil // empty display_name is correct } diff --git a/coderd/httpapi/name_test.go b/coderd/httpapi/name_test.go index a6313c54034f5..496e1f8b69038 100644 --- a/coderd/httpapi/name_test.go +++ b/coderd/httpapi/name_test.go @@ -115,7 +115,7 @@ func TestTemplateDisplayNameValid(t *testing.T) { testCase := testCase t.Run(testCase.Name, func(t *testing.T) { t.Parallel() - valid := httpapi.TemplateDisplayNameValid(testCase.Name) + valid := httpapi.DisplayNameValid(testCase.Name) require.Equal(t, testCase.Valid, valid == nil) }) } diff --git a/codersdk/organizations.go b/codersdk/organizations.go index 445bfa9a39b09..cb92a5d3ed89c 100644 --- a/codersdk/organizations.go +++ b/codersdk/organizations.go @@ -58,14 +58,14 @@ type OrganizationMember struct { } type CreateOrganizationRequest struct { - Name string `json:"name" validate:"required,username"` - DisplayName string `json:"display_name" validate:"required"` + Name string `json:"name" validate:"required,organization_name"` + DisplayName string `json:"display_name" validate:"required,organization_display_name"` Description string `json:"description,omitempty"` } type UpdateOrganizationRequest struct { - Name string `json:"name,omitempty" validate:"omitempty,username"` - DisplayName string `json:"display_name,omitempty"` + Name string `json:"name,omitempty" validate:"omitempty,organization_name"` + DisplayName string `json:"display_name,omitempty" validate:"omitempty,organization_display_name"` Description string `json:"description,omitempty"` } From 78c19b7c4382397fa46562ddcac134f2dc978ba0 Mon Sep 17 00:00:00 2001 From: McKayla Washburn Date: Wed, 5 Jun 2024 16:52:37 +0000 Subject: [PATCH 6/8] fix migration order --- ...ay_name.down.sql => 000216_organization_display_name.down.sql} | 0 ...isplay_name.up.sql => 000216_organization_display_name.up.sql} | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename coderd/database/migrations/{000215_organization_display_name.down.sql => 000216_organization_display_name.down.sql} (100%) rename coderd/database/migrations/{000215_organization_display_name.up.sql => 000216_organization_display_name.up.sql} (100%) diff --git a/coderd/database/migrations/000215_organization_display_name.down.sql b/coderd/database/migrations/000216_organization_display_name.down.sql similarity index 100% rename from coderd/database/migrations/000215_organization_display_name.down.sql rename to coderd/database/migrations/000216_organization_display_name.down.sql diff --git a/coderd/database/migrations/000215_organization_display_name.up.sql b/coderd/database/migrations/000216_organization_display_name.up.sql similarity index 100% rename from coderd/database/migrations/000215_organization_display_name.up.sql rename to coderd/database/migrations/000216_organization_display_name.up.sql From c5f8ccb3d7bb95fe9623679c3992306fe6b108e5 Mon Sep 17 00:00:00 2001 From: McKayla Washburn Date: Wed, 5 Jun 2024 17:04:06 +0000 Subject: [PATCH 7/8] make display name optional to avoid breakage --- coderd/organizations.go | 4 ++++ coderd/organizations_test.go | 14 ++++++++++++++ codersdk/organizations.go | 2 +- 3 files changed, 19 insertions(+), 1 deletion(-) diff --git a/coderd/organizations.go b/coderd/organizations.go index bf5685d4a4235..6a0073f30c2f1 100644 --- a/coderd/organizations.go +++ b/coderd/organizations.go @@ -74,6 +74,10 @@ func (api *API) postOrganizations(rw http.ResponseWriter, r *http.Request) { var organization database.Organization err = api.Database.InTx(func(tx database.Store) error { + if req.DisplayName == "" { + req.DisplayName = req.Name + } + organization, err = tx.InsertOrganization(ctx, database.InsertOrganizationParams{ ID: uuid.New(), Name: req.Name, diff --git a/coderd/organizations_test.go b/coderd/organizations_test.go index 9011bc0428e21..20fb7243faa5b 100644 --- a/coderd/organizations_test.go +++ b/coderd/organizations_test.go @@ -148,6 +148,20 @@ func TestPostOrganizationsByUser(t *testing.T) { require.Equal(t, "New", o.DisplayName) require.Equal(t, "A new organization to love and cherish forever.", o.Description) }) + + t.Run("CreateWithoutExplicitDisplayName", func(t *testing.T) { + t.Parallel() + client := coderdtest.New(t, nil) + _ = coderdtest.CreateFirstUser(t, client) + ctx := testutil.Context(t, testutil.WaitLong) + + o, err := client.CreateOrganization(ctx, codersdk.CreateOrganizationRequest{ + Name: "new", + }) + require.NoError(t, err) + require.Equal(t, "new", o.Name) + require.Equal(t, "new", o.DisplayName) // should match the given `Name` + }) } func TestPatchOrganizationsByUser(t *testing.T) { diff --git a/codersdk/organizations.go b/codersdk/organizations.go index cb92a5d3ed89c..5b307b5b1f932 100644 --- a/codersdk/organizations.go +++ b/codersdk/organizations.go @@ -59,7 +59,7 @@ type OrganizationMember struct { type CreateOrganizationRequest struct { Name string `json:"name" validate:"required,organization_name"` - DisplayName string `json:"display_name" validate:"required,organization_display_name"` + DisplayName string `json:"display_name" validate:"omitempty,organization_display_name"` Description string `json:"description,omitempty"` } From 24af34ecf86d07d77f02e27270207c7fc40e9ae4 Mon Sep 17 00:00:00 2001 From: McKayla Washburn Date: Wed, 5 Jun 2024 17:12:53 +0000 Subject: [PATCH 8/8] =?UTF-8?q?=E2=9C=8F=EF=B8=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- coderd/apidoc/docs.go | 2 +- coderd/apidoc/swagger.json | 3 ++- codersdk/organizations.go | 3 ++- docs/api/schemas.md | 10 +++++----- 4 files changed, 10 insertions(+), 8 deletions(-) diff --git a/coderd/apidoc/docs.go b/coderd/apidoc/docs.go index 424b9b57cc8ef..f1f219216ffa1 100644 --- a/coderd/apidoc/docs.go +++ b/coderd/apidoc/docs.go @@ -8970,7 +8970,6 @@ const docTemplate = `{ "codersdk.CreateOrganizationRequest": { "type": "object", "required": [ - "display_name", "name" ], "properties": { @@ -8978,6 +8977,7 @@ const docTemplate = `{ "type": "string" }, "display_name": { + "description": "DisplayName will default to the same value as ` + "`" + `Name` + "`" + ` if not provided.", "type": "string" }, "name": { diff --git a/coderd/apidoc/swagger.json b/coderd/apidoc/swagger.json index 1a78756ae203b..4591e9fe11fd6 100644 --- a/coderd/apidoc/swagger.json +++ b/coderd/apidoc/swagger.json @@ -7993,12 +7993,13 @@ }, "codersdk.CreateOrganizationRequest": { "type": "object", - "required": ["display_name", "name"], + "required": ["name"], "properties": { "description": { "type": "string" }, "display_name": { + "description": "DisplayName will default to the same value as `Name` if not provided.", "type": "string" }, "name": { diff --git a/codersdk/organizations.go b/codersdk/organizations.go index 5b307b5b1f932..b9ff98d1a3917 100644 --- a/codersdk/organizations.go +++ b/codersdk/organizations.go @@ -58,7 +58,8 @@ type OrganizationMember struct { } type CreateOrganizationRequest struct { - Name string `json:"name" validate:"required,organization_name"` + Name string `json:"name" validate:"required,organization_name"` + // DisplayName will default to the same value as `Name` if not provided. DisplayName string `json:"display_name" validate:"omitempty,organization_display_name"` Description string `json:"description,omitempty"` } diff --git a/docs/api/schemas.md b/docs/api/schemas.md index 780daaacacd8d..5637879043de9 100644 --- a/docs/api/schemas.md +++ b/docs/api/schemas.md @@ -1422,11 +1422,11 @@ AuthorizationObject can represent a "set" of objects, such as: all workspaces in ### Properties -| Name | Type | Required | Restrictions | Description | -| -------------- | ------ | -------- | ------------ | ----------- | -| `description` | string | false | | | -| `display_name` | string | true | | | -| `name` | string | true | | | +| Name | Type | Required | Restrictions | Description | +| -------------- | ------ | -------- | ------------ | ---------------------------------------------------------------------- | +| `description` | string | false | | | +| `display_name` | string | false | | Display name will default to the same value as `Name` if not provided. | +| `name` | string | true | | | ## codersdk.CreateTemplateRequest