mirror of
https://github.com/coder/coder.git
synced 2026-09-22 05:05:20 +08:00
feat: edit org display names and descriptions (#13474)
This commit is contained in:
Generated
+20
-3
@@ -8973,6 +8973,13 @@ const docTemplate = `{
|
||||
"name"
|
||||
],
|
||||
"properties": {
|
||||
"description": {
|
||||
"type": "string"
|
||||
},
|
||||
"display_name": {
|
||||
"description": "DisplayName will default to the same value as ` + "`" + `Name` + "`" + ` if not provided.",
|
||||
"type": "string"
|
||||
},
|
||||
"name": {
|
||||
"type": "string"
|
||||
}
|
||||
@@ -10587,6 +10594,7 @@ const docTemplate = `{
|
||||
"type": "object",
|
||||
"required": [
|
||||
"created_at",
|
||||
"display_name",
|
||||
"id",
|
||||
"is_default",
|
||||
"name",
|
||||
@@ -10597,6 +10605,12 @@ const docTemplate = `{
|
||||
"type": "string",
|
||||
"format": "date-time"
|
||||
},
|
||||
"description": {
|
||||
"type": "string"
|
||||
},
|
||||
"display_name": {
|
||||
"type": "string"
|
||||
},
|
||||
"id": {
|
||||
"type": "string",
|
||||
"format": "uuid"
|
||||
@@ -12305,10 +12319,13 @@ const docTemplate = `{
|
||||
},
|
||||
"codersdk.UpdateOrganizationRequest": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"name"
|
||||
],
|
||||
"properties": {
|
||||
"description": {
|
||||
"type": "string"
|
||||
},
|
||||
"display_name": {
|
||||
"type": "string"
|
||||
},
|
||||
"name": {
|
||||
"type": "string"
|
||||
}
|
||||
|
||||
Generated
+27
-2
@@ -7995,6 +7995,13 @@
|
||||
"type": "object",
|
||||
"required": ["name"],
|
||||
"properties": {
|
||||
"description": {
|
||||
"type": "string"
|
||||
},
|
||||
"display_name": {
|
||||
"description": "DisplayName will default to the same value as `Name` if not provided.",
|
||||
"type": "string"
|
||||
},
|
||||
"name": {
|
||||
"type": "string"
|
||||
}
|
||||
@@ -9516,12 +9523,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"
|
||||
@@ -11145,8 +11165,13 @@
|
||||
},
|
||||
"codersdk.UpdateOrganizationRequest": {
|
||||
"type": "object",
|
||||
"required": ["name"],
|
||||
"properties": {
|
||||
"description": {
|
||||
"type": "string"
|
||||
},
|
||||
"display_name": {
|
||||
"type": "string"
|
||||
},
|
||||
"name": {
|
||||
"type": "string"
|
||||
}
|
||||
|
||||
@@ -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()),
|
||||
|
||||
@@ -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(),
|
||||
@@ -6179,11 +6180,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
|
||||
@@ -7324,6 +7327,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
|
||||
}
|
||||
|
||||
Generated
+2
-1
@@ -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 (
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
alter table organizations
|
||||
drop column display_name;
|
||||
@@ -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;
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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 *;
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -117,7 +117,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)
|
||||
})
|
||||
}
|
||||
|
||||
+44
-10
@@ -74,12 +74,17 @@ 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,
|
||||
DisplayName: req.DisplayName,
|
||||
Description: req.Description,
|
||||
CreatedAt: dbtime.Now(),
|
||||
UpdatedAt: dbtime.Now(),
|
||||
Description: "",
|
||||
})
|
||||
if err != nil {
|
||||
return xerrors.Errorf("create organization: %w", err)
|
||||
@@ -146,11 +151,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 +244,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,
|
||||
}
|
||||
}
|
||||
|
||||
+118
-11
@@ -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,23 +109,58 @@ 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)
|
||||
require.Equal(t, http.StatusConflict, 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.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)
|
||||
|
||||
_, err := client.CreateOrganization(ctx, codersdk.CreateOrganizationRequest{
|
||||
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)
|
||||
})
|
||||
|
||||
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`
|
||||
})
|
||||
}
|
||||
|
||||
@@ -137,7 +175,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 +195,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)
|
||||
|
||||
@@ -168,6 +208,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)
|
||||
@@ -175,7 +235,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 +254,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 +264,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 +334,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 +350,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)
|
||||
|
||||
|
||||
@@ -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
|
||||
},
|
||||
|
||||
@@ -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,16 @@ type OrganizationMember struct {
|
||||
}
|
||||
|
||||
type CreateOrganizationRequest struct {
|
||||
Name string `json:"name" validate:"required,username"`
|
||||
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"`
|
||||
}
|
||||
|
||||
type UpdateOrganizationRequest struct {
|
||||
Name string `json:"name" validate:"required,username"`
|
||||
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"`
|
||||
}
|
||||
|
||||
// CreateTemplateVersionRequest enables callers to create a new Template Version.
|
||||
|
||||
Generated
+10
@@ -105,6 +105,8 @@ curl -X POST http://coder-server:8080/api/v2/organizations \
|
||||
|
||||
```json
|
||||
{
|
||||
"description": "string",
|
||||
"display_name": "string",
|
||||
"name": "string"
|
||||
}
|
||||
```
|
||||
@@ -122,6 +124,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 +167,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 +246,8 @@ curl -X PATCH http://coder-server:8080/api/v2/organizations/{organization} \
|
||||
|
||||
```json
|
||||
{
|
||||
"description": "string",
|
||||
"display_name": "string",
|
||||
"name": "string"
|
||||
}
|
||||
```
|
||||
@@ -258,6 +266,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",
|
||||
|
||||
Generated
+25
-13
@@ -1416,15 +1416,19 @@ AuthorizationObject can represent a "set" of objects, such as: all workspaces in
|
||||
|
||||
```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 | | Display name will default to the same value as `Name` if not provided. |
|
||||
| `name` | string | true | | |
|
||||
|
||||
## codersdk.CreateTemplateRequest
|
||||
|
||||
@@ -3597,6 +3601,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",
|
||||
@@ -3606,13 +3612,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
|
||||
|
||||
@@ -5378,15 +5386,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
|
||||
|
||||
|
||||
Generated
+14
-8
@@ -998,6 +998,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",
|
||||
@@ -1016,14 +1018,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).
|
||||
|
||||
@@ -1054,6 +1058,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",
|
||||
|
||||
Generated
+7
-1
@@ -226,6 +226,8 @@ export interface CreateGroupRequest {
|
||||
// From codersdk/organizations.go
|
||||
export interface CreateOrganizationRequest {
|
||||
readonly name: string;
|
||||
readonly display_name: string;
|
||||
readonly description?: string;
|
||||
}
|
||||
|
||||
// From codersdk/organizations.go
|
||||
@@ -777,6 +779,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;
|
||||
@@ -1323,7 +1327,9 @@ export interface UpdateCheckResponse {
|
||||
|
||||
// From codersdk/organizations.go
|
||||
export interface UpdateOrganizationRequest {
|
||||
readonly name: string;
|
||||
readonly name?: string;
|
||||
readonly display_name?: string;
|
||||
readonly description?: string;
|
||||
}
|
||||
|
||||
// From codersdk/users.go
|
||||
|
||||
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user