mirror of
https://github.com/coder/coder.git
synced 2026-09-24 15:04:27 +08:00
feat: add GET /api/v2/users (#1028)
This commit is contained in:
@@ -145,6 +145,7 @@ func New(options *Options) (http.Handler, func()) {
|
||||
r.Group(func(r chi.Router) {
|
||||
r.Use(httpmw.ExtractAPIKey(options.Database, nil))
|
||||
r.Post("/", api.postUsers)
|
||||
r.Get("/", api.users)
|
||||
r.Route("/{user}", func(r chi.Router) {
|
||||
r.Use(httpmw.ExtractUserParam(options.Database))
|
||||
r.Get("/", api.userByName)
|
||||
|
||||
@@ -164,6 +164,13 @@ func (q *fakeQuerier) GetUserCount(_ context.Context) (int64, error) {
|
||||
return int64(len(q.users)), nil
|
||||
}
|
||||
|
||||
func (q *fakeQuerier) GetUsers(_ context.Context) ([]database.User, error) {
|
||||
q.mutex.RLock()
|
||||
defer q.mutex.RUnlock()
|
||||
|
||||
return q.users, nil
|
||||
}
|
||||
|
||||
func (q *fakeQuerier) GetWorkspacesByTemplateID(_ context.Context, arg database.GetWorkspacesByTemplateIDParams) ([]database.Workspace, error) {
|
||||
q.mutex.RLock()
|
||||
defer q.mutex.RUnlock()
|
||||
|
||||
@@ -38,6 +38,7 @@ type querier interface {
|
||||
GetUserByEmailOrUsername(ctx context.Context, arg GetUserByEmailOrUsernameParams) (User, error)
|
||||
GetUserByID(ctx context.Context, id uuid.UUID) (User, error)
|
||||
GetUserCount(ctx context.Context) (int64, error)
|
||||
GetUsers(ctx context.Context) ([]User, error)
|
||||
GetWorkspaceAgentByAuthToken(ctx context.Context, authToken uuid.UUID) (WorkspaceAgent, error)
|
||||
GetWorkspaceAgentByID(ctx context.Context, id uuid.UUID) (WorkspaceAgent, error)
|
||||
GetWorkspaceAgentByInstanceID(ctx context.Context, authInstanceID string) (WorkspaceAgent, error)
|
||||
|
||||
@@ -1851,6 +1851,46 @@ func (q *sqlQuerier) GetUserCount(ctx context.Context) (int64, error) {
|
||||
return count, err
|
||||
}
|
||||
|
||||
const getUsers = `-- name: GetUsers :many
|
||||
SELECT
|
||||
id, email, name, revoked, login_type, hashed_password, created_at, updated_at, username
|
||||
FROM
|
||||
users
|
||||
`
|
||||
|
||||
func (q *sqlQuerier) GetUsers(ctx context.Context) ([]User, error) {
|
||||
rows, err := q.db.QueryContext(ctx, getUsers)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
var items []User
|
||||
for rows.Next() {
|
||||
var i User
|
||||
if err := rows.Scan(
|
||||
&i.ID,
|
||||
&i.Email,
|
||||
&i.Name,
|
||||
&i.Revoked,
|
||||
&i.LoginType,
|
||||
&i.HashedPassword,
|
||||
&i.CreatedAt,
|
||||
&i.UpdatedAt,
|
||||
&i.Username,
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
items = append(items, i)
|
||||
}
|
||||
if err := rows.Close(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := rows.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return items, nil
|
||||
}
|
||||
|
||||
const insertUser = `-- name: InsertUser :one
|
||||
INSERT INTO
|
||||
users (
|
||||
|
||||
@@ -51,3 +51,9 @@ SET
|
||||
updated_at = $5
|
||||
WHERE
|
||||
id = $1 RETURNING *;
|
||||
|
||||
-- name: GetUsers :many
|
||||
SELECT
|
||||
*
|
||||
FROM
|
||||
users;
|
||||
|
||||
@@ -23,6 +23,25 @@ import (
|
||||
"github.com/coder/coder/cryptorand"
|
||||
)
|
||||
|
||||
// Lists all the users
|
||||
func (api *api) users(rw http.ResponseWriter, r *http.Request) {
|
||||
users, err := api.Database.GetUsers(r.Context())
|
||||
|
||||
if err != nil {
|
||||
httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{
|
||||
Message: fmt.Sprintf("get users: %s", err.Error()),
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
var res []codersdk.User
|
||||
for _, user := range users {
|
||||
res = append(res, convertUser(user))
|
||||
}
|
||||
|
||||
httpapi.Write(rw, http.StatusOK, res)
|
||||
}
|
||||
|
||||
// Returns whether the initial user has been created or not.
|
||||
func (api *api) firstUser(rw http.ResponseWriter, r *http.Request) {
|
||||
userCount, err := api.Database.GetUserCount(r.Context())
|
||||
|
||||
@@ -313,6 +313,21 @@ func TestUserByName(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
func TestGetUsers(t *testing.T) {
|
||||
t.Parallel()
|
||||
client := coderdtest.New(t, nil)
|
||||
user := coderdtest.CreateFirstUser(t, client)
|
||||
client.CreateUser(context.Background(), codersdk.CreateUserRequest{
|
||||
Email: "bruno@coder.com",
|
||||
Username: "bruno",
|
||||
Password: "password",
|
||||
OrganizationID: user.OrganizationID,
|
||||
})
|
||||
users, err := client.GetUsers(context.Background())
|
||||
require.NoError(t, err)
|
||||
require.Len(t, users, 2)
|
||||
}
|
||||
|
||||
func TestOrganizationsByUser(t *testing.T) {
|
||||
t.Parallel()
|
||||
client := coderdtest.New(t, nil)
|
||||
|
||||
@@ -136,6 +136,19 @@ func (c *Client) UpdateUserProfile(ctx context.Context, userID uuid.UUID, req Up
|
||||
return user, json.NewDecoder(res.Body).Decode(&user)
|
||||
}
|
||||
|
||||
func (c *Client) GetUsers(ctx context.Context) ([]User, error) {
|
||||
res, err := c.request(ctx, http.MethodGet, "/api/v2/users", nil)
|
||||
if err != nil {
|
||||
return []User{}, err
|
||||
}
|
||||
defer res.Body.Close()
|
||||
if res.StatusCode != http.StatusOK {
|
||||
return []User{}, readBodyAsError(res)
|
||||
}
|
||||
var users []User
|
||||
return users, json.NewDecoder(res.Body).Decode(&users)
|
||||
}
|
||||
|
||||
// CreateAPIKey generates an API key for the user ID provided.
|
||||
func (c *Client) CreateAPIKey(ctx context.Context, userID uuid.UUID) (*GenerateAPIKeyResponse, error) {
|
||||
res, err := c.request(ctx, http.MethodPost, fmt.Sprintf("/api/v2/users/%s/keys", uuidOrMe(userID)), nil)
|
||||
|
||||
Reference in New Issue
Block a user