Skip to content

Commit 96fb498

Browse files
committed
Merge remote-tracking branch 'origin/main' into stevenmasley/user_assign_role_rebase
2 parents 9d47b74 + ba4c3ce commit 96fb498

File tree

27 files changed

+553
-64
lines changed

27 files changed

+553
-64
lines changed

coderd/database/databasefake/databasefake.go

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -212,6 +212,16 @@ func (q *fakeQuerier) GetUsers(_ context.Context, params database.GetUsersParams
212212
users = tmp
213213
}
214214

215+
if params.Status != "" {
216+
usersFilteredByStatus := make([]database.User, 0, len(users))
217+
for i, user := range users {
218+
if params.Status == string(user.Status) {
219+
usersFilteredByStatus = append(usersFilteredByStatus, users[i])
220+
}
221+
}
222+
users = usersFilteredByStatus
223+
}
224+
215225
if params.OffsetOpt > 0 {
216226
if int(params.OffsetOpt) > len(users)-1 {
217227
return []database.User{}, nil
@@ -225,6 +235,7 @@ func (q *fakeQuerier) GetUsers(_ context.Context, params database.GetUsersParams
225235
}
226236
users = users[:params.LimitOpt]
227237
}
238+
228239
tmp := make([]database.User, len(users))
229240
copy(tmp, users)
230241

coderd/database/queries.sql.go

Lines changed: 16 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

coderd/database/queries/users.sql

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,13 +87,25 @@ WHERE
8787
)
8888
ELSE true
8989
END
90+
-- Start filters
91+
-- Filter by name, email or username
9092
AND CASE
9193
WHEN @search :: text != '' THEN (
9294
email LIKE concat('%', @search, '%')
9395
OR username LIKE concat('%', @search, '%')
96+
)
97+
ELSE true
98+
END
99+
-- Filter by status
100+
AND CASE
101+
-- @status needs to be a text because it can be empty, If it was
102+
-- user_status enum, it would not.
103+
WHEN @status :: text != '' THEN (
104+
status = @status :: user_status
94105
)
95106
ELSE true
96107
END
108+
-- End of filters
97109
ORDER BY
98110
-- Deterministic and consistent ordering of all users, even if they share
99111
-- a timestamp. This is to ensure consistent pagination.

coderd/users.go

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -106,10 +106,11 @@ func (api *api) postFirstUser(rw http.ResponseWriter, r *http.Request) {
106106

107107
func (api *api) users(rw http.ResponseWriter, r *http.Request) {
108108
var (
109-
afterArg = r.URL.Query().Get("after_user")
110-
limitArg = r.URL.Query().Get("limit")
111-
offsetArg = r.URL.Query().Get("offset")
112-
searchName = r.URL.Query().Get("search")
109+
afterArg = r.URL.Query().Get("after_user")
110+
limitArg = r.URL.Query().Get("limit")
111+
offsetArg = r.URL.Query().Get("offset")
112+
searchName = r.URL.Query().Get("search")
113+
statusFilter = r.URL.Query().Get("status")
113114
)
114115

115116
// createdAfter is a user uuid.
@@ -152,6 +153,7 @@ func (api *api) users(rw http.ResponseWriter, r *http.Request) {
152153
OffsetOpt: int32(offset),
153154
LimitOpt: int32(pageLimit),
154155
Search: searchName,
156+
Status: statusFilter,
155157
})
156158
if err != nil {
157159
httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{

coderd/users_test.go

Lines changed: 48 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -443,19 +443,54 @@ func TestUserByName(t *testing.T) {
443443

444444
func TestGetUsers(t *testing.T) {
445445
t.Parallel()
446-
client := coderdtest.New(t, nil)
447-
user := coderdtest.CreateFirstUser(t, client)
448-
client.CreateUser(context.Background(), codersdk.CreateUserRequest{
449-
Email: "alice@email.com",
450-
Username: "alice",
451-
Password: "password",
452-
OrganizationID: user.OrganizationID,
453-
})
454-
// No params is all users
455-
users, err := client.Users(context.Background(), codersdk.UsersRequest{})
456-
require.NoError(t, err)
457-
require.Len(t, users, 2)
458-
require.Len(t, users[0].OrganizationIDs, 1)
446+
t.Run("AllUsers", func(t *testing.T) {
447+
t.Parallel()
448+
client := coderdtest.New(t, nil)
449+
user := coderdtest.CreateFirstUser(t, client)
450+
client.CreateUser(context.Background(), codersdk.CreateUserRequest{
451+
Email: "alice@email.com",
452+
Username: "alice",
453+
Password: "password",
454+
OrganizationID: user.OrganizationID,
455+
})
456+
// No params is all users
457+
users, err := client.Users(context.Background(), codersdk.UsersRequest{})
458+
require.NoError(t, err)
459+
require.Len(t, users, 2)
460+
require.Len(t, users[0].OrganizationIDs, 1)
461+
})
462+
t.Run("ActiveUsers", func(t *testing.T) {
463+
t.Parallel()
464+
client := coderdtest.New(t, nil)
465+
first := coderdtest.CreateFirstUser(t, client)
466+
active := make([]codersdk.User, 0)
467+
alice, err := client.CreateUser(context.Background(), codersdk.CreateUserRequest{
468+
Email: "alice@email.com",
469+
Username: "alice",
470+
Password: "password",
471+
OrganizationID: first.OrganizationID,
472+
})
473+
require.NoError(t, err)
474+
active = append(active, alice)
475+
476+
bruno, err := client.CreateUser(context.Background(), codersdk.CreateUserRequest{
477+
Email: "bruno@email.com",
478+
Username: "bruno",
479+
Password: "password",
480+
OrganizationID: first.OrganizationID,
481+
})
482+
require.NoError(t, err)
483+
active = append(active, bruno)
484+
485+
_, err = client.SuspendUser(context.Background(), first.UserID)
486+
require.NoError(t, err)
487+
488+
users, err := client.Users(context.Background(), codersdk.UsersRequest{
489+
Status: string(codersdk.UserStatusActive),
490+
})
491+
require.NoError(t, err)
492+
require.ElementsMatch(t, active, users)
493+
})
459494
}
460495

461496
func TestOrganizationsByUser(t *testing.T) {

codersdk/users.go

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,13 @@ import (
1414
// Me is used as a replacement for your own ID.
1515
var Me = uuid.Nil
1616

17+
type UserStatus string
18+
19+
const (
20+
UserStatusActive UserStatus = "active"
21+
UserStatusSuspended UserStatus = "suspended"
22+
)
23+
1724
type UsersRequest struct {
1825
AfterUser uuid.UUID `json:"after_user"`
1926
Search string `json:"search"`
@@ -26,15 +33,10 @@ type UsersRequest struct {
2633
// To get the next page, use offset=<limit>*<page_number>.
2734
// Offset is 0 indexed, so the first record sits at offset 0.
2835
Offset int `json:"offset"`
36+
// Filter users by status
37+
Status string `json:"status"`
2938
}
3039

31-
type UserStatus string
32-
33-
const (
34-
UserStatusActive UserStatus = "active"
35-
UserStatusSuspended UserStatus = "suspended"
36-
)
37-
3840
// User represents a user in Coder.
3941
type User struct {
4042
ID uuid.UUID `json:"id" validate:"required"`
@@ -174,6 +176,7 @@ func (c *Client) SuspendUser(ctx context.Context, userID uuid.UUID) (User, error
174176
if res.StatusCode != http.StatusOK {
175177
return User{}, readBodyAsError(res)
176178
}
179+
177180
var user User
178181
return user, json.NewDecoder(res.Body).Decode(&user)
179182
}
@@ -296,6 +299,7 @@ func (c *Client) Users(ctx context.Context, req UsersRequest) ([]User, error) {
296299
}
297300
q.Set("offset", strconv.Itoa(req.Offset))
298301
q.Set("search", req.Search)
302+
q.Set("status", req.Status)
299303
r.URL.RawQuery = q.Encode()
300304
})
301305
if err != nil {

develop.sh

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,10 @@ cd "${PROJECT_ROOT}"
88
# Run yarn install, to make sure node_modules are ready to go
99
"$PROJECT_ROOT/scripts/yarn_install.sh"
1010

11+
# Use static credentials for development
12+
export CODER_DEV_ADMIN_EMAIL=admin@coder.com
13+
export CODER_DEV_ADMIN_PASSWORD=password
14+
1115
# This is a way to run multiple processes in parallel, and have Ctrl-C work correctly
1216
# to kill both at the same time. For more details, see:
1317
# https://stackoverflow.com/questions/3004811/how-do-you-run-multiple-programs-in-parallel-from-a-bash-script

peer/conn.go

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -221,19 +221,35 @@ func (c *Conn) init() error {
221221
}
222222
}
223223
})
224+
225+
// These functions need to check if the conn is closed, because they can be
226+
// called after being closed.
224227
c.rtc.OnSignalingStateChange(func(signalState webrtc.SignalingState) {
228+
if c.isClosed() {
229+
return
230+
}
225231
c.opts.Logger.Debug(context.Background(), "signaling state updated",
226232
slog.F("state", signalState))
227233
})
228234
c.rtc.SCTP().Transport().OnStateChange(func(dtlsTransportState webrtc.DTLSTransportState) {
235+
if c.isClosed() {
236+
return
237+
}
229238
c.opts.Logger.Debug(context.Background(), "dtls transport state updated",
230239
slog.F("state", dtlsTransportState))
231240
})
232241
c.rtc.SCTP().Transport().ICETransport().OnSelectedCandidatePairChange(func(candidatePair *webrtc.ICECandidatePair) {
242+
if c.isClosed() {
243+
return
244+
}
233245
c.opts.Logger.Debug(context.Background(), "selected candidate pair changed",
234246
slog.F("local", candidatePair.Local), slog.F("remote", candidatePair.Remote))
235247
})
236248
c.rtc.OnICECandidate(func(iceCandidate *webrtc.ICECandidate) {
249+
if c.isClosed() {
250+
return
251+
}
252+
237253
if iceCandidate == nil {
238254
return
239255
}

site/htmlTemplates/index.html

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,8 @@
1010

1111
<head>
1212
<meta charset="utf-8" />
13-
<meta name="viewport" content="width=device-width, initial-scale=1" />
13+
<!-- Force desktop view on mobile viewport -->
14+
<meta name="viewport" content="width=1024" />
1415
<meta name="theme-color" content="#17172E" />
1516
<meta name="application-name" content="Coder" />
1617
<meta property="og:type" content="website" />

site/src/AppRouter.tsx

Lines changed: 19 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import { SettingsPage } from "./pages/SettingsPage/SettingsPage"
1717
import { CreateWorkspacePage } from "./pages/TemplatesPages/OrganizationPage/TemplatePage/CreateWorkspacePage"
1818
import { TemplatePage } from "./pages/TemplatesPages/OrganizationPage/TemplatePage/TemplatePage"
1919
import { TemplatesPage } from "./pages/TemplatesPages/TemplatesPage"
20+
import { CreateUserPage } from "./pages/UsersPage/CreateUserPage/CreateUserPage"
2021
import { UsersPage } from "./pages/UsersPage/UsersPage"
2122
import { WorkspacePage } from "./pages/WorkspacesPage/WorkspacesPage"
2223

@@ -83,14 +84,24 @@ export const AppRouter: React.FC = () => (
8384
/>
8485
</Route>
8586

86-
<Route
87-
path="users"
88-
element={
89-
<AuthAndFrame>
90-
<UsersPage />
91-
</AuthAndFrame>
92-
}
93-
/>
87+
<Route path="users">
88+
<Route
89+
index
90+
element={
91+
<AuthAndFrame>
92+
<UsersPage />
93+
</AuthAndFrame>
94+
}
95+
/>
96+
<Route
97+
path="create"
98+
element={
99+
<RequireAuth>
100+
<CreateUserPage />
101+
</RequireAuth>
102+
}
103+
/>
104+
</Route>
94105
<Route
95106
path="orgs"
96107
element={

0 commit comments

Comments
 (0)