diff --git a/assets/data/api/users.ts b/assets/data/api/users.ts index 4f137d4..be8fc3c 100644 --- a/assets/data/api/users.ts +++ b/assets/data/api/users.ts @@ -1,4 +1,4 @@ -import type {User} from 'data/types/user' +import type {User, UserUpdateInput} from 'data/types/user' import api from '../api' export const getUser = async (id: string): Promise => { @@ -10,3 +10,8 @@ export const listUsers = async (): Promise => { const response = await api.get('/api/users') return response.json() } + +export const updateUser = async (id: string, input: UserUpdateInput): Promise => { + const response = await api.put('/api/users/' + encodeURIComponent(id), input) + return response.json() +} diff --git a/assets/data/types/user.ts b/assets/data/types/user.ts index 4ed7317..dc4c489 100644 --- a/assets/data/types/user.ts +++ b/assets/data/types/user.ts @@ -11,6 +11,12 @@ export type User = { TenantID: string } +export type UserUpdateInput = { + Email: string + FirstName: string + LastName: string +} + export const isAdmin = (user: User) => user.Role == Admin const Admin = 'Admin' diff --git a/assets/pages/admin/users/[id].svelte b/assets/pages/admin/users/[id].svelte new file mode 100644 index 0000000..fa93aa2 --- /dev/null +++ b/assets/pages/admin/users/[id].svelte @@ -0,0 +1,60 @@ + + +

Edit User

+ +
+
+

+ +

+

+ +

+

+ +

+
+
+ +
+
+ +
+
+ + + diff --git a/assets/pages/admin/users.svelte b/assets/pages/admin/users/index.svelte similarity index 94% rename from assets/pages/admin/users.svelte rename to assets/pages/admin/users/index.svelte index eca3b5c..74d4594 100644 --- a/assets/pages/admin/users.svelte +++ b/assets/pages/admin/users/index.svelte @@ -24,6 +24,7 @@ + @@ -36,6 +37,7 @@ {#each users as user (user.ID)} + diff --git a/server/server.go b/server/server.go index 9482d03..68772a8 100644 --- a/server/server.go +++ b/server/server.go @@ -84,4 +84,5 @@ func (s *Server) registerAPIRoutes() { api.GET("/users", s.usersListHandler) api.GET("/users/:id", s.userHandler) + api.PUT("/users/:id", s.usersUpdateHandler) } diff --git a/server/user.go b/server/user.go index 0603b9c..6835553 100644 --- a/server/user.go +++ b/server/user.go @@ -48,3 +48,30 @@ func (s *Server) userHandler(c echo.Context) error { return c.JSON(http.StatusOK, user) } + +func (s *Server) usersUpdateHandler(c echo.Context) error { + var input app.UserUpdateInput + err := (&echo.DefaultBinder{}).BindBody(c, &input) + if err != nil { + // TODO: improve error response here (and probably everywhere else too) + return echo.NewHTTPError(http.StatusBadRequest, "bad request") + } + + id := c.Param("id") + actor := app.CurrentUser(c) + if id != actor.ID && actor.Role != app.UserRoleAdmin { + return echo.NewHTTPError(http.StatusNotFound, AuthError{Error: "not found"}) + } + + updatedUser, err := db.UpdateUser(c, id, input) + if err != nil { + return echo.NewHTTPError(http.StatusInternalServerError, err) + } + + user, err := db.ConvertUser(c, updatedUser) + if err != nil { + return echo.NewHTTPError(http.StatusInternalServerError, err) + } + + return c.JSON(http.StatusOK, user) +} diff --git a/server/user_test.go b/server/user_test.go index 35dab94..09fd41f 100644 --- a/server/user_test.go +++ b/server/user_test.go @@ -113,3 +113,56 @@ func (ts *TestSuite) Test_GetUserList() { }) } } + +func (ts *TestSuite) Test_usersUpdateHandler() { + user := ts.createUserFixture(app.UserRoleBasic) + admin := ts.createUserFixture(app.UserRoleAdmin) + + tests := []struct { + name string + actor db.User + userID string + wantStatus int + }{ + { + name: "not a valid user", + userID: "xyz", + wantStatus: http.StatusUnauthorized, + }, + { + name: "a user cannot update a user", + actor: user, + userID: admin.ID, + wantStatus: http.StatusNotFound, + }, + { + name: "admin can update a user", + actor: admin, + userID: user.ID, + wantStatus: http.StatusOK, + }, + } + + for _, tt := range tests { + ts.T().Run(tt.name, func(t *testing.T) { + email := "updated@example.com" + input := app.UserUpdateInput{Email: &email} + body, status := ts.request(http.MethodPut, "/api/users/"+tt.userID, tt.actor.Email, input) + + // Assertions + ts.Equal(tt.wantStatus, status, "incorrect http status, body: \n%s", body) + + if tt.wantStatus != http.StatusOK { + return + } + + var gotUser app.User + ts.NoError(json.Unmarshal(body, &gotUser)) + ts.Equal(*input.Email, gotUser.Email, "incorrect User Email, body: \n%s", body) + + dbUser, err := db.FindUserByID(ts.ctx, gotUser.ID) + ts.NoError(err) + ts.Equal(*input.Email, dbUser.Email, "incorrect User Name in db") + }) + } +}
Edit Role Tenant First Name
Edit {user.Role} {getTenantNameFromID(user.TenantID)} {user.FirstName}