mirror of
https://github.com/coder/coder.git
synced 2025-07-15 22:20:27 +00:00
feat: Add users create and list commands (#1111)
This allows for *extremely basic* user management.
This commit is contained in:
90
cli/usercreate.go
Normal file
90
cli/usercreate.go
Normal file
@ -0,0 +1,90 @@
|
|||||||
|
package cli
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/go-playground/validator/v10"
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
"golang.org/x/xerrors"
|
||||||
|
|
||||||
|
"github.com/coder/coder/cli/cliui"
|
||||||
|
"github.com/coder/coder/codersdk"
|
||||||
|
"github.com/coder/coder/cryptorand"
|
||||||
|
)
|
||||||
|
|
||||||
|
func userCreate() *cobra.Command {
|
||||||
|
var (
|
||||||
|
email string
|
||||||
|
username string
|
||||||
|
password string
|
||||||
|
)
|
||||||
|
cmd := &cobra.Command{
|
||||||
|
Use: "create",
|
||||||
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
|
client, err := createClient(cmd)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
organization, err := currentOrganization(cmd, client)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if username == "" {
|
||||||
|
username, err = cliui.Prompt(cmd, cliui.PromptOptions{
|
||||||
|
Text: "Username:",
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if email == "" {
|
||||||
|
email, err = cliui.Prompt(cmd, cliui.PromptOptions{
|
||||||
|
Text: "Email:",
|
||||||
|
Validate: func(s string) error {
|
||||||
|
err := validator.New().Var(s, "email")
|
||||||
|
if err != nil {
|
||||||
|
return xerrors.New("That's not a valid email address!")
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
},
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if password == "" {
|
||||||
|
password, err = cryptorand.StringCharset(cryptorand.Human, 12)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = client.CreateUser(cmd.Context(), codersdk.CreateUserRequest{
|
||||||
|
Email: email,
|
||||||
|
Username: username,
|
||||||
|
Password: password,
|
||||||
|
OrganizationID: organization.ID,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
_, _ = fmt.Fprintln(cmd.ErrOrStderr(), `A new user has been created!
|
||||||
|
Share the instructions below to get them started.
|
||||||
|
`+cliui.Styles.Placeholder.Render("—————————————————————————————————————————————————")+`
|
||||||
|
Download the Coder command line for your operating system:
|
||||||
|
https://github.com/coder/coder/releases
|
||||||
|
|
||||||
|
Run `+cliui.Styles.Code.Render("coder login "+client.URL.String())+` to authenticate.
|
||||||
|
|
||||||
|
Your email is: `+cliui.Styles.Field.Render(email)+`
|
||||||
|
Your password is: `+cliui.Styles.Field.Render(password)+`
|
||||||
|
|
||||||
|
Create a workspace `+cliui.Styles.Code.Render("coder workspaces create")+`!`)
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
}
|
||||||
|
cmd.Flags().StringVarP(&email, "email", "e", "", "Specifies an email address for the new user.")
|
||||||
|
cmd.Flags().StringVarP(&username, "username", "u", "", "Specifies a username for the new user.")
|
||||||
|
cmd.Flags().StringVarP(&password, "password", "p", "", "Specifies a password for the new user.")
|
||||||
|
return cmd
|
||||||
|
}
|
42
cli/usercreate_test.go
Normal file
42
cli/usercreate_test.go
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
package cli_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
|
||||||
|
"github.com/coder/coder/cli/clitest"
|
||||||
|
"github.com/coder/coder/coderd/coderdtest"
|
||||||
|
"github.com/coder/coder/pty/ptytest"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestUserCreate(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
t.Run("Prompts", func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
client := coderdtest.New(t, nil)
|
||||||
|
coderdtest.CreateFirstUser(t, client)
|
||||||
|
cmd, root := clitest.New(t, "users", "create")
|
||||||
|
clitest.SetupConfig(t, client, root)
|
||||||
|
doneChan := make(chan struct{})
|
||||||
|
pty := ptytest.New(t)
|
||||||
|
cmd.SetIn(pty.Input())
|
||||||
|
cmd.SetOut(pty.Output())
|
||||||
|
go func() {
|
||||||
|
defer close(doneChan)
|
||||||
|
err := cmd.Execute()
|
||||||
|
require.NoError(t, err)
|
||||||
|
}()
|
||||||
|
matches := []string{
|
||||||
|
"Username", "dean",
|
||||||
|
"Email", "dean@coder.com",
|
||||||
|
}
|
||||||
|
for i := 0; i < len(matches); i += 2 {
|
||||||
|
match := matches[i]
|
||||||
|
value := matches[i+1]
|
||||||
|
pty.ExpectMatch(match)
|
||||||
|
pty.WriteLine(value)
|
||||||
|
}
|
||||||
|
<-doneChan
|
||||||
|
})
|
||||||
|
}
|
46
cli/userlist.go
Normal file
46
cli/userlist.go
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
package cli
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"sort"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/jedib0t/go-pretty/v6/table"
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
|
||||||
|
"github.com/coder/coder/codersdk"
|
||||||
|
)
|
||||||
|
|
||||||
|
func userList() *cobra.Command {
|
||||||
|
return &cobra.Command{
|
||||||
|
Use: "list",
|
||||||
|
Aliases: []string{"ls"},
|
||||||
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
|
client, err := createClient(cmd)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
users, err := client.Users(cmd.Context(), codersdk.UsersRequest{})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
sort.Slice(users, func(i, j int) bool {
|
||||||
|
return users[i].Username < users[j].Username
|
||||||
|
})
|
||||||
|
|
||||||
|
tableWriter := table.NewWriter()
|
||||||
|
tableWriter.SetStyle(table.StyleLight)
|
||||||
|
tableWriter.Style().Options.SeparateColumns = false
|
||||||
|
tableWriter.AppendHeader(table.Row{"Username", "Email", "Created At"})
|
||||||
|
for _, user := range users {
|
||||||
|
tableWriter.AppendRow(table.Row{
|
||||||
|
user.Username,
|
||||||
|
user.Email,
|
||||||
|
user.CreatedAt.Format(time.Stamp),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
_, err = fmt.Fprintln(cmd.OutOrStdout(), tableWriter.Render())
|
||||||
|
return err
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
30
cli/userlist_test.go
Normal file
30
cli/userlist_test.go
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
package cli_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
|
||||||
|
"github.com/coder/coder/cli/clitest"
|
||||||
|
"github.com/coder/coder/coderd/coderdtest"
|
||||||
|
"github.com/coder/coder/pty/ptytest"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestUserList(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
client := coderdtest.New(t, nil)
|
||||||
|
coderdtest.CreateFirstUser(t, client)
|
||||||
|
cmd, root := clitest.New(t, "users", "list")
|
||||||
|
clitest.SetupConfig(t, client, root)
|
||||||
|
doneChan := make(chan struct{})
|
||||||
|
pty := ptytest.New(t)
|
||||||
|
cmd.SetIn(pty.Input())
|
||||||
|
cmd.SetOut(pty.Output())
|
||||||
|
go func() {
|
||||||
|
defer close(doneChan)
|
||||||
|
err := cmd.Execute()
|
||||||
|
require.NoError(t, err)
|
||||||
|
}()
|
||||||
|
pty.ExpectMatch("coder.com")
|
||||||
|
<-doneChan
|
||||||
|
}
|
@ -6,5 +6,6 @@ func users() *cobra.Command {
|
|||||||
cmd := &cobra.Command{
|
cmd := &cobra.Command{
|
||||||
Use: "users",
|
Use: "users",
|
||||||
}
|
}
|
||||||
|
cmd.AddCommand(userCreate(), userList())
|
||||||
return cmd
|
return cmd
|
||||||
}
|
}
|
||||||
|
@ -207,8 +207,6 @@ func (q *fakeQuerier) GetUsers(_ context.Context, params database.GetUsersParams
|
|||||||
tmp = append(tmp, users[i])
|
tmp = append(tmp, users[i])
|
||||||
} else if strings.Contains(user.Username, params.Search) {
|
} else if strings.Contains(user.Username, params.Search) {
|
||||||
tmp = append(tmp, users[i])
|
tmp = append(tmp, users[i])
|
||||||
} else if strings.Contains(user.Name, params.Search) {
|
|
||||||
tmp = append(tmp, users[i])
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
users = tmp
|
users = tmp
|
||||||
@ -1116,8 +1114,6 @@ func (q *fakeQuerier) InsertUser(_ context.Context, arg database.InsertUserParam
|
|||||||
user := database.User{
|
user := database.User{
|
||||||
ID: arg.ID,
|
ID: arg.ID,
|
||||||
Email: arg.Email,
|
Email: arg.Email,
|
||||||
Name: arg.Name,
|
|
||||||
LoginType: arg.LoginType,
|
|
||||||
HashedPassword: arg.HashedPassword,
|
HashedPassword: arg.HashedPassword,
|
||||||
CreatedAt: arg.CreatedAt,
|
CreatedAt: arg.CreatedAt,
|
||||||
UpdatedAt: arg.UpdatedAt,
|
UpdatedAt: arg.UpdatedAt,
|
||||||
@ -1135,7 +1131,6 @@ func (q *fakeQuerier) UpdateUserProfile(_ context.Context, arg database.UpdateUs
|
|||||||
if user.ID != arg.ID {
|
if user.ID != arg.ID {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
user.Name = arg.Name
|
|
||||||
user.Email = arg.Email
|
user.Email = arg.Email
|
||||||
user.Username = arg.Username
|
user.Username = arg.Username
|
||||||
q.users[index] = user
|
q.users[index] = user
|
||||||
|
7
coderd/database/dump.sql
generated
7
coderd/database/dump.sql
generated
@ -218,13 +218,10 @@ CREATE TABLE templates (
|
|||||||
CREATE TABLE users (
|
CREATE TABLE users (
|
||||||
id uuid NOT NULL,
|
id uuid NOT NULL,
|
||||||
email text NOT NULL,
|
email text NOT NULL,
|
||||||
name text NOT NULL,
|
username text DEFAULT ''::text NOT NULL,
|
||||||
revoked boolean NOT NULL,
|
|
||||||
login_type login_type NOT NULL,
|
|
||||||
hashed_password bytea NOT NULL,
|
hashed_password bytea NOT NULL,
|
||||||
created_at timestamp with time zone NOT NULL,
|
created_at timestamp with time zone NOT NULL,
|
||||||
updated_at timestamp with time zone NOT NULL,
|
updated_at timestamp with time zone NOT NULL
|
||||||
username text DEFAULT ''::text NOT NULL
|
|
||||||
);
|
);
|
||||||
|
|
||||||
CREATE TABLE workspace_agents (
|
CREATE TABLE workspace_agents (
|
||||||
|
@ -12,13 +12,10 @@ CREATE TYPE login_type AS ENUM (
|
|||||||
CREATE TABLE IF NOT EXISTS users (
|
CREATE TABLE IF NOT EXISTS users (
|
||||||
id uuid NOT NULL,
|
id uuid NOT NULL,
|
||||||
email text NOT NULL,
|
email text NOT NULL,
|
||||||
name text NOT NULL,
|
username text DEFAULT ''::text NOT NULL,
|
||||||
revoked boolean NOT NULL,
|
|
||||||
login_type login_type NOT NULL,
|
|
||||||
hashed_password bytea NOT NULL,
|
hashed_password bytea NOT NULL,
|
||||||
created_at timestamp with time zone NOT NULL,
|
created_at timestamp with time zone NOT NULL,
|
||||||
updated_at timestamp with time zone NOT NULL,
|
updated_at timestamp with time zone NOT NULL,
|
||||||
username text DEFAULT ''::text NOT NULL,
|
|
||||||
PRIMARY KEY (id)
|
PRIMARY KEY (id)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -374,13 +374,10 @@ type TemplateVersion struct {
|
|||||||
type User struct {
|
type User struct {
|
||||||
ID uuid.UUID `db:"id" json:"id"`
|
ID uuid.UUID `db:"id" json:"id"`
|
||||||
Email string `db:"email" json:"email"`
|
Email string `db:"email" json:"email"`
|
||||||
Name string `db:"name" json:"name"`
|
Username string `db:"username" json:"username"`
|
||||||
Revoked bool `db:"revoked" json:"revoked"`
|
|
||||||
LoginType LoginType `db:"login_type" json:"login_type"`
|
|
||||||
HashedPassword []byte `db:"hashed_password" json:"hashed_password"`
|
HashedPassword []byte `db:"hashed_password" json:"hashed_password"`
|
||||||
CreatedAt time.Time `db:"created_at" json:"created_at"`
|
CreatedAt time.Time `db:"created_at" json:"created_at"`
|
||||||
UpdatedAt time.Time `db:"updated_at" json:"updated_at"`
|
UpdatedAt time.Time `db:"updated_at" json:"updated_at"`
|
||||||
Username string `db:"username" json:"username"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type Workspace struct {
|
type Workspace struct {
|
||||||
|
@ -1782,7 +1782,7 @@ func (q *sqlQuerier) UpdateTemplateVersionByID(ctx context.Context, arg UpdateTe
|
|||||||
|
|
||||||
const getUserByEmailOrUsername = `-- name: GetUserByEmailOrUsername :one
|
const getUserByEmailOrUsername = `-- name: GetUserByEmailOrUsername :one
|
||||||
SELECT
|
SELECT
|
||||||
id, email, name, revoked, login_type, hashed_password, created_at, updated_at, username
|
id, email, username, hashed_password, created_at, updated_at
|
||||||
FROM
|
FROM
|
||||||
users
|
users
|
||||||
WHERE
|
WHERE
|
||||||
@ -1803,20 +1803,17 @@ func (q *sqlQuerier) GetUserByEmailOrUsername(ctx context.Context, arg GetUserBy
|
|||||||
err := row.Scan(
|
err := row.Scan(
|
||||||
&i.ID,
|
&i.ID,
|
||||||
&i.Email,
|
&i.Email,
|
||||||
&i.Name,
|
&i.Username,
|
||||||
&i.Revoked,
|
|
||||||
&i.LoginType,
|
|
||||||
&i.HashedPassword,
|
&i.HashedPassword,
|
||||||
&i.CreatedAt,
|
&i.CreatedAt,
|
||||||
&i.UpdatedAt,
|
&i.UpdatedAt,
|
||||||
&i.Username,
|
|
||||||
)
|
)
|
||||||
return i, err
|
return i, err
|
||||||
}
|
}
|
||||||
|
|
||||||
const getUserByID = `-- name: GetUserByID :one
|
const getUserByID = `-- name: GetUserByID :one
|
||||||
SELECT
|
SELECT
|
||||||
id, email, name, revoked, login_type, hashed_password, created_at, updated_at, username
|
id, email, username, hashed_password, created_at, updated_at
|
||||||
FROM
|
FROM
|
||||||
users
|
users
|
||||||
WHERE
|
WHERE
|
||||||
@ -1831,13 +1828,10 @@ func (q *sqlQuerier) GetUserByID(ctx context.Context, id uuid.UUID) (User, error
|
|||||||
err := row.Scan(
|
err := row.Scan(
|
||||||
&i.ID,
|
&i.ID,
|
||||||
&i.Email,
|
&i.Email,
|
||||||
&i.Name,
|
&i.Username,
|
||||||
&i.Revoked,
|
|
||||||
&i.LoginType,
|
|
||||||
&i.HashedPassword,
|
&i.HashedPassword,
|
||||||
&i.CreatedAt,
|
&i.CreatedAt,
|
||||||
&i.UpdatedAt,
|
&i.UpdatedAt,
|
||||||
&i.Username,
|
|
||||||
)
|
)
|
||||||
return i, err
|
return i, err
|
||||||
}
|
}
|
||||||
@ -1858,7 +1852,7 @@ func (q *sqlQuerier) GetUserCount(ctx context.Context) (int64, error) {
|
|||||||
|
|
||||||
const getUsers = `-- name: GetUsers :many
|
const getUsers = `-- name: GetUsers :many
|
||||||
SELECT
|
SELECT
|
||||||
id, email, name, revoked, login_type, hashed_password, created_at, updated_at, username
|
id, email, username, hashed_password, created_at, updated_at
|
||||||
FROM
|
FROM
|
||||||
users
|
users
|
||||||
WHERE
|
WHERE
|
||||||
@ -1888,7 +1882,6 @@ WHERE
|
|||||||
WHEN $2 :: text != '' THEN (
|
WHEN $2 :: text != '' THEN (
|
||||||
email LIKE concat('%', $2, '%')
|
email LIKE concat('%', $2, '%')
|
||||||
OR username LIKE concat('%', $2, '%')
|
OR username LIKE concat('%', $2, '%')
|
||||||
OR 'name' LIKE concat('%', $2, '%')
|
|
||||||
)
|
)
|
||||||
ELSE true
|
ELSE true
|
||||||
END
|
END
|
||||||
@ -1925,13 +1918,10 @@ func (q *sqlQuerier) GetUsers(ctx context.Context, arg GetUsersParams) ([]User,
|
|||||||
if err := rows.Scan(
|
if err := rows.Scan(
|
||||||
&i.ID,
|
&i.ID,
|
||||||
&i.Email,
|
&i.Email,
|
||||||
&i.Name,
|
&i.Username,
|
||||||
&i.Revoked,
|
|
||||||
&i.LoginType,
|
|
||||||
&i.HashedPassword,
|
&i.HashedPassword,
|
||||||
&i.CreatedAt,
|
&i.CreatedAt,
|
||||||
&i.UpdatedAt,
|
&i.UpdatedAt,
|
||||||
&i.Username,
|
|
||||||
); err != nil {
|
); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -1951,51 +1941,41 @@ INSERT INTO
|
|||||||
users (
|
users (
|
||||||
id,
|
id,
|
||||||
email,
|
email,
|
||||||
"name",
|
username,
|
||||||
login_type,
|
|
||||||
revoked,
|
|
||||||
hashed_password,
|
hashed_password,
|
||||||
created_at,
|
created_at,
|
||||||
updated_at,
|
updated_at
|
||||||
username
|
|
||||||
)
|
)
|
||||||
VALUES
|
VALUES
|
||||||
($1, $2, $3, $4, FALSE, $5, $6, $7, $8) RETURNING id, email, name, revoked, login_type, hashed_password, created_at, updated_at, username
|
($1, $2, $3, $4, $5, $6) RETURNING id, email, username, hashed_password, created_at, updated_at
|
||||||
`
|
`
|
||||||
|
|
||||||
type InsertUserParams struct {
|
type InsertUserParams struct {
|
||||||
ID uuid.UUID `db:"id" json:"id"`
|
ID uuid.UUID `db:"id" json:"id"`
|
||||||
Email string `db:"email" json:"email"`
|
Email string `db:"email" json:"email"`
|
||||||
Name string `db:"name" json:"name"`
|
Username string `db:"username" json:"username"`
|
||||||
LoginType LoginType `db:"login_type" json:"login_type"`
|
|
||||||
HashedPassword []byte `db:"hashed_password" json:"hashed_password"`
|
HashedPassword []byte `db:"hashed_password" json:"hashed_password"`
|
||||||
CreatedAt time.Time `db:"created_at" json:"created_at"`
|
CreatedAt time.Time `db:"created_at" json:"created_at"`
|
||||||
UpdatedAt time.Time `db:"updated_at" json:"updated_at"`
|
UpdatedAt time.Time `db:"updated_at" json:"updated_at"`
|
||||||
Username string `db:"username" json:"username"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (q *sqlQuerier) InsertUser(ctx context.Context, arg InsertUserParams) (User, error) {
|
func (q *sqlQuerier) InsertUser(ctx context.Context, arg InsertUserParams) (User, error) {
|
||||||
row := q.db.QueryRowContext(ctx, insertUser,
|
row := q.db.QueryRowContext(ctx, insertUser,
|
||||||
arg.ID,
|
arg.ID,
|
||||||
arg.Email,
|
arg.Email,
|
||||||
arg.Name,
|
arg.Username,
|
||||||
arg.LoginType,
|
|
||||||
arg.HashedPassword,
|
arg.HashedPassword,
|
||||||
arg.CreatedAt,
|
arg.CreatedAt,
|
||||||
arg.UpdatedAt,
|
arg.UpdatedAt,
|
||||||
arg.Username,
|
|
||||||
)
|
)
|
||||||
var i User
|
var i User
|
||||||
err := row.Scan(
|
err := row.Scan(
|
||||||
&i.ID,
|
&i.ID,
|
||||||
&i.Email,
|
&i.Email,
|
||||||
&i.Name,
|
&i.Username,
|
||||||
&i.Revoked,
|
|
||||||
&i.LoginType,
|
|
||||||
&i.HashedPassword,
|
&i.HashedPassword,
|
||||||
&i.CreatedAt,
|
&i.CreatedAt,
|
||||||
&i.UpdatedAt,
|
&i.UpdatedAt,
|
||||||
&i.Username,
|
|
||||||
)
|
)
|
||||||
return i, err
|
return i, err
|
||||||
}
|
}
|
||||||
@ -2005,17 +1985,15 @@ UPDATE
|
|||||||
users
|
users
|
||||||
SET
|
SET
|
||||||
email = $2,
|
email = $2,
|
||||||
"name" = $3,
|
username = $3,
|
||||||
username = $4,
|
updated_at = $4
|
||||||
updated_at = $5
|
|
||||||
WHERE
|
WHERE
|
||||||
id = $1 RETURNING id, email, name, revoked, login_type, hashed_password, created_at, updated_at, username
|
id = $1 RETURNING id, email, username, hashed_password, created_at, updated_at
|
||||||
`
|
`
|
||||||
|
|
||||||
type UpdateUserProfileParams struct {
|
type UpdateUserProfileParams struct {
|
||||||
ID uuid.UUID `db:"id" json:"id"`
|
ID uuid.UUID `db:"id" json:"id"`
|
||||||
Email string `db:"email" json:"email"`
|
Email string `db:"email" json:"email"`
|
||||||
Name string `db:"name" json:"name"`
|
|
||||||
Username string `db:"username" json:"username"`
|
Username string `db:"username" json:"username"`
|
||||||
UpdatedAt time.Time `db:"updated_at" json:"updated_at"`
|
UpdatedAt time.Time `db:"updated_at" json:"updated_at"`
|
||||||
}
|
}
|
||||||
@ -2024,7 +2002,6 @@ func (q *sqlQuerier) UpdateUserProfile(ctx context.Context, arg UpdateUserProfil
|
|||||||
row := q.db.QueryRowContext(ctx, updateUserProfile,
|
row := q.db.QueryRowContext(ctx, updateUserProfile,
|
||||||
arg.ID,
|
arg.ID,
|
||||||
arg.Email,
|
arg.Email,
|
||||||
arg.Name,
|
|
||||||
arg.Username,
|
arg.Username,
|
||||||
arg.UpdatedAt,
|
arg.UpdatedAt,
|
||||||
)
|
)
|
||||||
@ -2032,13 +2009,10 @@ func (q *sqlQuerier) UpdateUserProfile(ctx context.Context, arg UpdateUserProfil
|
|||||||
err := row.Scan(
|
err := row.Scan(
|
||||||
&i.ID,
|
&i.ID,
|
||||||
&i.Email,
|
&i.Email,
|
||||||
&i.Name,
|
&i.Username,
|
||||||
&i.Revoked,
|
|
||||||
&i.LoginType,
|
|
||||||
&i.HashedPassword,
|
&i.HashedPassword,
|
||||||
&i.CreatedAt,
|
&i.CreatedAt,
|
||||||
&i.UpdatedAt,
|
&i.UpdatedAt,
|
||||||
&i.Username,
|
|
||||||
)
|
)
|
||||||
return i, err
|
return i, err
|
||||||
}
|
}
|
||||||
|
@ -30,25 +30,21 @@ INSERT INTO
|
|||||||
users (
|
users (
|
||||||
id,
|
id,
|
||||||
email,
|
email,
|
||||||
"name",
|
username,
|
||||||
login_type,
|
|
||||||
revoked,
|
|
||||||
hashed_password,
|
hashed_password,
|
||||||
created_at,
|
created_at,
|
||||||
updated_at,
|
updated_at
|
||||||
username
|
|
||||||
)
|
)
|
||||||
VALUES
|
VALUES
|
||||||
($1, $2, $3, $4, FALSE, $5, $6, $7, $8) RETURNING *;
|
($1, $2, $3, $4, $5, $6) RETURNING *;
|
||||||
|
|
||||||
-- name: UpdateUserProfile :one
|
-- name: UpdateUserProfile :one
|
||||||
UPDATE
|
UPDATE
|
||||||
users
|
users
|
||||||
SET
|
SET
|
||||||
email = $2,
|
email = $2,
|
||||||
"name" = $3,
|
username = $3,
|
||||||
username = $4,
|
updated_at = $4
|
||||||
updated_at = $5
|
|
||||||
WHERE
|
WHERE
|
||||||
id = $1 RETURNING *;
|
id = $1 RETURNING *;
|
||||||
|
|
||||||
@ -84,7 +80,6 @@ WHERE
|
|||||||
WHEN @search :: text != '' THEN (
|
WHEN @search :: text != '' THEN (
|
||||||
email LIKE concat('%', @search, '%')
|
email LIKE concat('%', @search, '%')
|
||||||
OR username LIKE concat('%', @search, '%')
|
OR username LIKE concat('%', @search, '%')
|
||||||
OR 'name' LIKE concat('%', @search, '%')
|
|
||||||
)
|
)
|
||||||
ELSE true
|
ELSE true
|
||||||
END
|
END
|
||||||
|
@ -40,8 +40,6 @@ func TestOrganizationParam(t *testing.T) {
|
|||||||
user, err := db.InsertUser(r.Context(), database.InsertUserParams{
|
user, err := db.InsertUser(r.Context(), database.InsertUserParams{
|
||||||
ID: userID,
|
ID: userID,
|
||||||
Email: "testaccount@coder.com",
|
Email: "testaccount@coder.com",
|
||||||
Name: "example",
|
|
||||||
LoginType: database.LoginTypePassword,
|
|
||||||
HashedPassword: hashed[:],
|
HashedPassword: hashed[:],
|
||||||
Username: username,
|
Username: username,
|
||||||
CreatedAt: database.Now(),
|
CreatedAt: database.Now(),
|
||||||
|
@ -39,8 +39,6 @@ func TestTemplateParam(t *testing.T) {
|
|||||||
user, err := db.InsertUser(r.Context(), database.InsertUserParams{
|
user, err := db.InsertUser(r.Context(), database.InsertUserParams{
|
||||||
ID: userID,
|
ID: userID,
|
||||||
Email: "testaccount@coder.com",
|
Email: "testaccount@coder.com",
|
||||||
Name: "example",
|
|
||||||
LoginType: database.LoginTypePassword,
|
|
||||||
HashedPassword: hashed[:],
|
HashedPassword: hashed[:],
|
||||||
Username: username,
|
Username: username,
|
||||||
CreatedAt: database.Now(),
|
CreatedAt: database.Now(),
|
||||||
|
@ -39,8 +39,6 @@ func TestTemplateVersionParam(t *testing.T) {
|
|||||||
user, err := db.InsertUser(r.Context(), database.InsertUserParams{
|
user, err := db.InsertUser(r.Context(), database.InsertUserParams{
|
||||||
ID: userID,
|
ID: userID,
|
||||||
Email: "testaccount@coder.com",
|
Email: "testaccount@coder.com",
|
||||||
Name: "example",
|
|
||||||
LoginType: database.LoginTypePassword,
|
|
||||||
HashedPassword: hashed[:],
|
HashedPassword: hashed[:],
|
||||||
Username: username,
|
Username: username,
|
||||||
CreatedAt: database.Now(),
|
CreatedAt: database.Now(),
|
||||||
|
@ -39,8 +39,6 @@ func TestWorkspaceAgentParam(t *testing.T) {
|
|||||||
user, err := db.InsertUser(r.Context(), database.InsertUserParams{
|
user, err := db.InsertUser(r.Context(), database.InsertUserParams{
|
||||||
ID: userID,
|
ID: userID,
|
||||||
Email: "testaccount@coder.com",
|
Email: "testaccount@coder.com",
|
||||||
Name: "example",
|
|
||||||
LoginType: database.LoginTypePassword,
|
|
||||||
HashedPassword: hashed[:],
|
HashedPassword: hashed[:],
|
||||||
Username: username,
|
Username: username,
|
||||||
CreatedAt: database.Now(),
|
CreatedAt: database.Now(),
|
||||||
|
@ -39,8 +39,6 @@ func TestWorkspaceBuildParam(t *testing.T) {
|
|||||||
user, err := db.InsertUser(r.Context(), database.InsertUserParams{
|
user, err := db.InsertUser(r.Context(), database.InsertUserParams{
|
||||||
ID: userID,
|
ID: userID,
|
||||||
Email: "testaccount@coder.com",
|
Email: "testaccount@coder.com",
|
||||||
Name: "example",
|
|
||||||
LoginType: database.LoginTypePassword,
|
|
||||||
HashedPassword: hashed[:],
|
HashedPassword: hashed[:],
|
||||||
Username: username,
|
Username: username,
|
||||||
CreatedAt: database.Now(),
|
CreatedAt: database.Now(),
|
||||||
|
@ -39,8 +39,6 @@ func TestWorkspaceParam(t *testing.T) {
|
|||||||
user, err := db.InsertUser(r.Context(), database.InsertUserParams{
|
user, err := db.InsertUser(r.Context(), database.InsertUserParams{
|
||||||
ID: userID,
|
ID: userID,
|
||||||
Email: "testaccount@coder.com",
|
Email: "testaccount@coder.com",
|
||||||
Name: "example",
|
|
||||||
LoginType: database.LoginTypePassword,
|
|
||||||
HashedPassword: hashed[:],
|
HashedPassword: hashed[:],
|
||||||
Username: username,
|
Username: username,
|
||||||
CreatedAt: database.Now(),
|
CreatedAt: database.Now(),
|
||||||
|
@ -233,11 +233,6 @@ func (api *api) putUserProfile(rw http.ResponseWriter, r *http.Request) {
|
|||||||
if !httpapi.Read(rw, r, ¶ms) {
|
if !httpapi.Read(rw, r, ¶ms) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if params.Name == nil {
|
|
||||||
params.Name = &user.Name
|
|
||||||
}
|
|
||||||
|
|
||||||
existentUser, err := api.Database.GetUserByEmailOrUsername(r.Context(), database.GetUserByEmailOrUsernameParams{
|
existentUser, err := api.Database.GetUserByEmailOrUsername(r.Context(), database.GetUserByEmailOrUsernameParams{
|
||||||
Email: params.Email,
|
Email: params.Email,
|
||||||
Username: params.Username,
|
Username: params.Username,
|
||||||
@ -273,7 +268,6 @@ func (api *api) putUserProfile(rw http.ResponseWriter, r *http.Request) {
|
|||||||
|
|
||||||
updatedUserProfile, err := api.Database.UpdateUserProfile(r.Context(), database.UpdateUserProfileParams{
|
updatedUserProfile, err := api.Database.UpdateUserProfile(r.Context(), database.UpdateUserProfileParams{
|
||||||
ID: user.ID,
|
ID: user.ID,
|
||||||
Name: *params.Name,
|
|
||||||
Email: params.Email,
|
Email: params.Email,
|
||||||
Username: params.Username,
|
Username: params.Username,
|
||||||
UpdatedAt: database.Now(),
|
UpdatedAt: database.Now(),
|
||||||
@ -896,7 +890,6 @@ func (api *api) createUser(ctx context.Context, req codersdk.CreateUserRequest)
|
|||||||
ID: uuid.New(),
|
ID: uuid.New(),
|
||||||
Email: req.Email,
|
Email: req.Email,
|
||||||
Username: req.Username,
|
Username: req.Username,
|
||||||
LoginType: database.LoginTypePassword,
|
|
||||||
CreatedAt: database.Now(),
|
CreatedAt: database.Now(),
|
||||||
UpdatedAt: database.Now(),
|
UpdatedAt: database.Now(),
|
||||||
}
|
}
|
||||||
@ -949,7 +942,6 @@ func convertUser(user database.User) codersdk.User {
|
|||||||
Email: user.Email,
|
Email: user.Email,
|
||||||
CreatedAt: user.CreatedAt,
|
CreatedAt: user.CreatedAt,
|
||||||
Username: user.Username,
|
Username: user.Username,
|
||||||
Name: user.Name,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -283,28 +283,6 @@ func TestUpdateUserProfile(t *testing.T) {
|
|||||||
require.Equal(t, userProfile.Username, me.Username)
|
require.Equal(t, userProfile.Username, me.Username)
|
||||||
require.Equal(t, userProfile.Email, "newemail@coder.com")
|
require.Equal(t, userProfile.Email, "newemail@coder.com")
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("KeepUserName", func(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
client := coderdtest.New(t, nil)
|
|
||||||
coderdtest.CreateFirstUser(t, client)
|
|
||||||
me, _ := client.User(context.Background(), codersdk.Me)
|
|
||||||
newName := "New Name"
|
|
||||||
firstProfile, _ := client.UpdateUserProfile(context.Background(), codersdk.Me, codersdk.UpdateUserProfileRequest{
|
|
||||||
Username: me.Username,
|
|
||||||
Email: me.Email,
|
|
||||||
Name: &newName,
|
|
||||||
})
|
|
||||||
t.Log(firstProfile)
|
|
||||||
userProfile, err := client.UpdateUserProfile(context.Background(), codersdk.Me, codersdk.UpdateUserProfileRequest{
|
|
||||||
Username: "newusername",
|
|
||||||
Email: "newemail@coder.com",
|
|
||||||
})
|
|
||||||
require.NoError(t, err)
|
|
||||||
require.Equal(t, userProfile.Username, "newusername")
|
|
||||||
require.Equal(t, userProfile.Email, "newemail@coder.com")
|
|
||||||
require.Equal(t, userProfile.Name, newName)
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestUserByName(t *testing.T) {
|
func TestUserByName(t *testing.T) {
|
||||||
|
@ -34,7 +34,6 @@ type User struct {
|
|||||||
Email string `json:"email" validate:"required"`
|
Email string `json:"email" validate:"required"`
|
||||||
CreatedAt time.Time `json:"created_at" validate:"required"`
|
CreatedAt time.Time `json:"created_at" validate:"required"`
|
||||||
Username string `json:"username" validate:"required"`
|
Username string `json:"username" validate:"required"`
|
||||||
Name string `json:"name"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type CreateFirstUserRequest struct {
|
type CreateFirstUserRequest struct {
|
||||||
@ -60,7 +59,6 @@ type CreateUserRequest struct {
|
|||||||
type UpdateUserProfileRequest struct {
|
type UpdateUserProfileRequest struct {
|
||||||
Email string `json:"email" validate:"required,email"`
|
Email string `json:"email" validate:"required,email"`
|
||||||
Username string `json:"username" validate:"required,username"`
|
Username string `json:"username" validate:"required,username"`
|
||||||
Name *string `json:"name"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// LoginWithPasswordRequest enables callers to authenticate with email and password.
|
// LoginWithPasswordRequest enables callers to authenticate with email and password.
|
||||||
|
@ -81,10 +81,9 @@ export interface UsersRequest {
|
|||||||
export interface User {
|
export interface User {
|
||||||
readonly email: string
|
readonly email: string
|
||||||
readonly username: string
|
readonly username: string
|
||||||
readonly name: string
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// From codersdk/users.go:40:6.
|
// From codersdk/users.go:39:6.
|
||||||
export interface CreateFirstUserRequest {
|
export interface CreateFirstUserRequest {
|
||||||
readonly email: string
|
readonly email: string
|
||||||
readonly username: string
|
readonly username: string
|
||||||
@ -92,47 +91,46 @@ export interface CreateFirstUserRequest {
|
|||||||
readonly organization: string
|
readonly organization: string
|
||||||
}
|
}
|
||||||
|
|
||||||
// From codersdk/users.go:53:6.
|
// From codersdk/users.go:52:6.
|
||||||
export interface CreateUserRequest {
|
export interface CreateUserRequest {
|
||||||
readonly email: string
|
readonly email: string
|
||||||
readonly username: string
|
readonly username: string
|
||||||
readonly password: string
|
readonly password: string
|
||||||
}
|
}
|
||||||
|
|
||||||
// From codersdk/users.go:60:6.
|
// From codersdk/users.go:59:6.
|
||||||
export interface UpdateUserProfileRequest {
|
export interface UpdateUserProfileRequest {
|
||||||
readonly email: string
|
readonly email: string
|
||||||
readonly username: string
|
readonly username: string
|
||||||
readonly name?: string
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// From codersdk/users.go:67:6.
|
// From codersdk/users.go:65:6.
|
||||||
export interface LoginWithPasswordRequest {
|
export interface LoginWithPasswordRequest {
|
||||||
readonly email: string
|
readonly email: string
|
||||||
readonly password: string
|
readonly password: string
|
||||||
}
|
}
|
||||||
|
|
||||||
// From codersdk/users.go:73:6.
|
// From codersdk/users.go:71:6.
|
||||||
export interface LoginWithPasswordResponse {
|
export interface LoginWithPasswordResponse {
|
||||||
readonly session_token: string
|
readonly session_token: string
|
||||||
}
|
}
|
||||||
|
|
||||||
// From codersdk/users.go:78:6.
|
// From codersdk/users.go:76:6.
|
||||||
export interface GenerateAPIKeyResponse {
|
export interface GenerateAPIKeyResponse {
|
||||||
readonly key: string
|
readonly key: string
|
||||||
}
|
}
|
||||||
|
|
||||||
// From codersdk/users.go:82:6.
|
// From codersdk/users.go:80:6.
|
||||||
export interface CreateOrganizationRequest {
|
export interface CreateOrganizationRequest {
|
||||||
readonly name: string
|
readonly name: string
|
||||||
}
|
}
|
||||||
|
|
||||||
// From codersdk/users.go:87:6.
|
// From codersdk/users.go:85:6.
|
||||||
export interface CreateWorkspaceRequest {
|
export interface CreateWorkspaceRequest {
|
||||||
readonly name: string
|
readonly name: string
|
||||||
}
|
}
|
||||||
|
|
||||||
// From codersdk/users.go:96:6.
|
// From codersdk/users.go:94:6.
|
||||||
export interface AuthMethods {
|
export interface AuthMethods {
|
||||||
readonly password: boolean
|
readonly password: boolean
|
||||||
readonly github: boolean
|
readonly github: boolean
|
||||||
|
Reference in New Issue
Block a user