Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit63d1465

Browse files
feat: Add update profile endpoint (#916)
1 parentdb9d5b7 commit63d1465

File tree

8 files changed

+264
-0
lines changed

8 files changed

+264
-0
lines changed

‎coderd/coderd.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,7 @@ func New(options *Options) (http.Handler, func()) {
150150
r.Route("/{user}",func(r chi.Router) {
151151
r.Use(httpmw.ExtractUserParam(options.Database))
152152
r.Get("/",api.userByName)
153+
r.Put("/profile",api.putUserProfile)
153154
r.Get("/organizations",api.organizationsByUser)
154155
r.Post("/organizations",api.postOrganizationsByUser)
155156
r.Post("/keys",api.postAPIKey)

‎coderd/database/databasefake/databasefake.go

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1050,6 +1050,23 @@ func (q *fakeQuerier) InsertUser(_ context.Context, arg database.InsertUserParam
10501050
returnuser,nil
10511051
}
10521052

1053+
func (q*fakeQuerier)UpdateUserProfile(_ context.Context,arg database.UpdateUserProfileParams) (database.User,error) {
1054+
q.mutex.Lock()
1055+
deferq.mutex.Unlock()
1056+
1057+
forindex,user:=rangeq.users {
1058+
ifuser.ID!=arg.ID {
1059+
continue
1060+
}
1061+
user.Name=arg.Name
1062+
user.Email=arg.Email
1063+
user.Username=arg.Username
1064+
q.users[index]=user
1065+
returnuser,nil
1066+
}
1067+
return database.User{},sql.ErrNoRows
1068+
}
1069+
10531070
func (q*fakeQuerier)InsertWorkspace(_ context.Context,arg database.InsertWorkspaceParams) (database.Workspace,error) {
10541071
q.mutex.Lock()
10551072
deferq.mutex.Unlock()

‎coderd/database/querier.go

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more aboutcustomizing how changed files appear on GitHub.

‎coderd/database/queries.sql.go

Lines changed: 43 additions & 0 deletions
Some generated files are not rendered by default. Learn more aboutcustomizing how changed files appear on GitHub.

‎coderd/database/queries/users.sql

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,3 +40,14 @@ INSERT INTO
4040
)
4141
VALUES
4242
($1, $2, $3, $4, FALSE, $5, $6, $7, $8) RETURNING*;
43+
44+
-- name: UpdateUserProfile :one
45+
UPDATE
46+
users
47+
SET
48+
email= $2,
49+
"name"= $3,
50+
username= $4,
51+
updated_at= $5
52+
WHERE
53+
id= $1 RETURNING*;

‎coderd/users.go

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -270,6 +270,70 @@ func (*api) userByName(rw http.ResponseWriter, r *http.Request) {
270270
render.JSON(rw,r,convertUser(user))
271271
}
272272

273+
func (api*api)putUserProfile(rw http.ResponseWriter,r*http.Request) {
274+
user:=httpmw.UserParam(r)
275+
276+
varparams codersdk.UpdateUserProfileRequest
277+
if!httpapi.Read(rw,r,&params) {
278+
return
279+
}
280+
281+
ifparams.Name==nil {
282+
params.Name=&user.Name
283+
}
284+
285+
existentUser,err:=api.Database.GetUserByEmailOrUsername(r.Context(), database.GetUserByEmailOrUsernameParams{
286+
Email:params.Email,
287+
Username:params.Username,
288+
})
289+
isDifferentUser:=existentUser.ID!=user.ID
290+
291+
iferr==nil&&isDifferentUser {
292+
responseErrors:= []httpapi.Error{}
293+
ifexistentUser.Email==params.Email {
294+
responseErrors=append(responseErrors, httpapi.Error{
295+
Field:"email",
296+
Code:"exists",
297+
})
298+
}
299+
ifexistentUser.Username==params.Username {
300+
responseErrors=append(responseErrors, httpapi.Error{
301+
Field:"username",
302+
Code:"exists",
303+
})
304+
}
305+
httpapi.Write(rw,http.StatusConflict, httpapi.Response{
306+
Message:fmt.Sprintf("user already exists"),
307+
Errors:responseErrors,
308+
})
309+
return
310+
}
311+
if!errors.Is(err,sql.ErrNoRows)&&isDifferentUser {
312+
httpapi.Write(rw,http.StatusInternalServerError, httpapi.Response{
313+
Message:fmt.Sprintf("get user: %s",err),
314+
})
315+
return
316+
}
317+
318+
updatedUserProfile,err:=api.Database.UpdateUserProfile(r.Context(), database.UpdateUserProfileParams{
319+
ID:user.ID,
320+
Name:*params.Name,
321+
Email:params.Email,
322+
Username:params.Username,
323+
UpdatedAt:database.Now(),
324+
})
325+
326+
iferr!=nil {
327+
httpapi.Write(rw,http.StatusInternalServerError, httpapi.Response{
328+
Message:fmt.Sprintf("patch user: %s",err.Error()),
329+
})
330+
return
331+
}
332+
333+
render.Status(r,http.StatusOK)
334+
render.JSON(rw,r,convertUser(updatedUserProfile))
335+
}
336+
273337
// Returns organizations the parameterized user has access to.
274338
func (api*api)organizationsByUser(rw http.ResponseWriter,r*http.Request) {
275339
user:=httpmw.UserParam(r)
@@ -872,5 +936,6 @@ func convertUser(user database.User) codersdk.User {
872936
Email:user.Email,
873937
CreatedAt:user.CreatedAt,
874938
Username:user.Username,
939+
Name:user.Name,
875940
}
876941
}

‎coderd/users_test.go

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -200,6 +200,111 @@ func TestPostUsers(t *testing.T) {
200200
})
201201
}
202202

203+
funcTestUpdateUserProfile(t*testing.T) {
204+
t.Parallel()
205+
t.Run("UserNotFound",func(t*testing.T) {
206+
t.Parallel()
207+
client:=coderdtest.New(t,nil)
208+
coderdtest.CreateFirstUser(t,client)
209+
_,err:=client.UpdateUserProfile(context.Background(),uuid.New(), codersdk.UpdateUserProfileRequest{
210+
Username:"newusername",
211+
Email:"newemail@coder.com",
212+
})
213+
varapiErr*codersdk.Error
214+
require.ErrorAs(t,err,&apiErr)
215+
// Right now, we are raising a BAD request error because we don't support a
216+
// user accessing other users info
217+
require.Equal(t,http.StatusBadRequest,apiErr.StatusCode())
218+
})
219+
220+
t.Run("ConflictingEmail",func(t*testing.T) {
221+
t.Parallel()
222+
client:=coderdtest.New(t,nil)
223+
user:=coderdtest.CreateFirstUser(t,client)
224+
existentUser,_:=client.CreateUser(context.Background(), codersdk.CreateUserRequest{
225+
Email:"bruno@coder.com",
226+
Username:"bruno",
227+
Password:"password",
228+
OrganizationID:user.OrganizationID,
229+
})
230+
_,err:=client.UpdateUserProfile(context.Background(),codersdk.Me, codersdk.UpdateUserProfileRequest{
231+
Username:"newusername",
232+
Email:existentUser.Email,
233+
})
234+
varapiErr*codersdk.Error
235+
require.ErrorAs(t,err,&apiErr)
236+
require.Equal(t,http.StatusConflict,apiErr.StatusCode())
237+
})
238+
239+
t.Run("ConflictingUsername",func(t*testing.T) {
240+
t.Parallel()
241+
client:=coderdtest.New(t,nil)
242+
user:=coderdtest.CreateFirstUser(t,client)
243+
existentUser,_:=client.CreateUser(context.Background(), codersdk.CreateUserRequest{
244+
Email:"bruno@coder.com",
245+
Username:"bruno",
246+
Password:"password",
247+
OrganizationID:user.OrganizationID,
248+
})
249+
_,err:=client.UpdateUserProfile(context.Background(),codersdk.Me, codersdk.UpdateUserProfileRequest{
250+
Username:existentUser.Username,
251+
Email:"newemail@coder.com",
252+
})
253+
varapiErr*codersdk.Error
254+
require.ErrorAs(t,err,&apiErr)
255+
require.Equal(t,http.StatusConflict,apiErr.StatusCode())
256+
})
257+
258+
t.Run("UpdateUsernameAndEmail",func(t*testing.T) {
259+
t.Parallel()
260+
client:=coderdtest.New(t,nil)
261+
coderdtest.CreateFirstUser(t,client)
262+
userProfile,err:=client.UpdateUserProfile(context.Background(),codersdk.Me, codersdk.UpdateUserProfileRequest{
263+
Username:"newusername",
264+
Email:"newemail@coder.com",
265+
})
266+
require.NoError(t,err)
267+
require.Equal(t,userProfile.Username,"newusername")
268+
require.Equal(t,userProfile.Email,"newemail@coder.com")
269+
})
270+
271+
t.Run("UpdateUsername",func(t*testing.T) {
272+
t.Parallel()
273+
client:=coderdtest.New(t,nil)
274+
coderdtest.CreateFirstUser(t,client)
275+
me,_:=client.User(context.Background(),codersdk.Me)
276+
userProfile,err:=client.UpdateUserProfile(context.Background(),codersdk.Me, codersdk.UpdateUserProfileRequest{
277+
Username:me.Username,
278+
Email:"newemail@coder.com",
279+
})
280+
require.NoError(t,err)
281+
require.Equal(t,userProfile.Username,me.Username)
282+
require.Equal(t,userProfile.Email,"newemail@coder.com")
283+
})
284+
285+
t.Run("KeepUserName",func(t*testing.T) {
286+
t.Parallel()
287+
client:=coderdtest.New(t,nil)
288+
coderdtest.CreateFirstUser(t,client)
289+
me,_:=client.User(context.Background(),codersdk.Me)
290+
newName:="New Name"
291+
firstProfile,_:=client.UpdateUserProfile(context.Background(),codersdk.Me, codersdk.UpdateUserProfileRequest{
292+
Username:me.Username,
293+
Email:me.Email,
294+
Name:&newName,
295+
})
296+
t.Log(firstProfile)
297+
userProfile,err:=client.UpdateUserProfile(context.Background(),codersdk.Me, codersdk.UpdateUserProfileRequest{
298+
Username:"newusername",
299+
Email:"newemail@coder.com",
300+
})
301+
require.NoError(t,err)
302+
require.Equal(t,userProfile.Username,"newusername")
303+
require.Equal(t,userProfile.Email,"newemail@coder.com")
304+
require.Equal(t,userProfile.Name,newName)
305+
})
306+
}
307+
203308
funcTestUserByName(t*testing.T) {
204309
t.Parallel()
205310
client:=coderdtest.New(t,nil)

‎codersdk/users.go

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ type User struct {
1919
Emailstring`json:"email" validate:"required"`
2020
CreatedAt time.Time`json:"created_at" validate:"required"`
2121
Usernamestring`json:"username" validate:"required"`
22+
Namestring`json:"name"`
2223
}
2324

2425
typeCreateFirstUserRequeststruct {
@@ -41,6 +42,12 @@ type CreateUserRequest struct {
4142
OrganizationID uuid.UUID`json:"organization_id" validate:"required"`
4243
}
4344

45+
typeUpdateUserProfileRequeststruct {
46+
Emailstring`json:"email" validate:"required,email"`
47+
Usernamestring`json:"username" validate:"required,username"`
48+
Name*string`json:"name"`
49+
}
50+
4451
// LoginWithPasswordRequest enables callers to authenticate with email and password.
4552
typeLoginWithPasswordRequeststruct {
4653
Emailstring`json:"email" validate:"required,email"`
@@ -115,6 +122,20 @@ func (c *Client) CreateUser(ctx context.Context, req CreateUserRequest) (User, e
115122
returnuser,json.NewDecoder(res.Body).Decode(&user)
116123
}
117124

125+
// UpdateUserProfile enables callers to update profile information
126+
func (c*Client)UpdateUserProfile(ctx context.Context,userID uuid.UUID,reqUpdateUserProfileRequest) (User,error) {
127+
res,err:=c.request(ctx,http.MethodPut,fmt.Sprintf("/api/v2/users/%s/profile",uuidOrMe(userID)),req)
128+
iferr!=nil {
129+
returnUser{},err
130+
}
131+
deferres.Body.Close()
132+
ifres.StatusCode!=http.StatusOK {
133+
returnUser{},readBodyAsError(res)
134+
}
135+
varuserUser
136+
returnuser,json.NewDecoder(res.Body).Decode(&user)
137+
}
138+
118139
// CreateAPIKey generates an API key for the user ID provided.
119140
func (c*Client)CreateAPIKey(ctx context.Context,userID uuid.UUID) (*GenerateAPIKeyResponse,error) {
120141
res,err:=c.request(ctx,http.MethodPost,fmt.Sprintf("/api/v2/users/%s/keys",uuidOrMe(userID)),nil)

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp