feat: UpdateUser implemented
This commit is contained in:
parent
cfb2523cfd
commit
e999534b3f
4 changed files with 193 additions and 33 deletions
|
|
@ -24,3 +24,79 @@ get:
|
||||||
description: Request params are not correct
|
description: Request params are not correct
|
||||||
'500':
|
'500':
|
||||||
description: Unknown server error
|
description: Unknown server error
|
||||||
|
|
||||||
|
patch:
|
||||||
|
summary: Partially update a user account
|
||||||
|
description: |
|
||||||
|
Update selected user profile fields (excluding password).
|
||||||
|
Password updates must be done via the dedicated auth-service (`/auth/`).
|
||||||
|
Fields not provided in the request body remain unchanged.
|
||||||
|
operationId: updateUser
|
||||||
|
parameters:
|
||||||
|
- name: user_id
|
||||||
|
in: path
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
type: integer
|
||||||
|
format: int64
|
||||||
|
description: User ID (primary key)
|
||||||
|
example: 123
|
||||||
|
requestBody:
|
||||||
|
required: true
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
avatar_id:
|
||||||
|
type: integer
|
||||||
|
format: int64
|
||||||
|
nullable: true
|
||||||
|
description: ID of the user avatar (references `images.id`); set to `null` to remove avatar
|
||||||
|
example: 42
|
||||||
|
mail:
|
||||||
|
type: string
|
||||||
|
format: email
|
||||||
|
pattern: '^[a-zA-Z0-9._-]+@[a-zA-Z0-9._-]+\\.[a-zA-Z0-9_-]+$'
|
||||||
|
description: User email (must be unique and valid)
|
||||||
|
example: john.doe.updated@example.com
|
||||||
|
nickname:
|
||||||
|
type: string
|
||||||
|
pattern: '^[a-zA-Z0-9_-]{3,16}$'
|
||||||
|
description: Username (alphanumeric + `_` or `-`, 3–16 chars)
|
||||||
|
maxLength: 16
|
||||||
|
minLength: 3
|
||||||
|
example: john_doe_43
|
||||||
|
disp_name:
|
||||||
|
type: string
|
||||||
|
description: Display name
|
||||||
|
maxLength: 32
|
||||||
|
example: John Smith
|
||||||
|
user_desc:
|
||||||
|
type: string
|
||||||
|
description: User description / bio
|
||||||
|
maxLength: 512
|
||||||
|
example: Just a curious developer.
|
||||||
|
additionalProperties: false
|
||||||
|
description: Only provided fields are updated. Omitted fields remain unchanged.
|
||||||
|
responses:
|
||||||
|
'200':
|
||||||
|
description: User updated successfully. Returns updated user representation (excluding sensitive fields).
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '../schemas/User.yaml'
|
||||||
|
'400':
|
||||||
|
description: Invalid input (e.g., validation failed, nickname/email conflict, malformed JSON)
|
||||||
|
'401':
|
||||||
|
description: Unauthorized — missing or invalid authentication token
|
||||||
|
'403':
|
||||||
|
description: Forbidden — user is not allowed to modify this resource (e.g., not own profile & no admin rights)
|
||||||
|
'404':
|
||||||
|
description: User not found
|
||||||
|
'409':
|
||||||
|
description: Conflict — e.g., requested `nickname` or `mail` already taken by another user
|
||||||
|
'422':
|
||||||
|
description: Unprocessable Entity — semantic errors not caught by schema (e.g., invalid `avatar_id`)
|
||||||
|
'500':
|
||||||
|
description: Unknown server error
|
||||||
|
|
|
||||||
|
|
@ -33,7 +33,7 @@ func mapUser(u sqlc.GetUserByIDRow) oapi.User {
|
||||||
CreationDate: &u.CreationDate,
|
CreationDate: &u.CreationDate,
|
||||||
DispName: u.DispName,
|
DispName: u.DispName,
|
||||||
Id: &u.ID,
|
Id: &u.ID,
|
||||||
Mail: (*types.Email)(u.Mail),
|
Mail: StringToEmail(u.Mail),
|
||||||
Nickname: u.Nickname,
|
Nickname: u.Nickname,
|
||||||
UserDesc: u.UserDesc,
|
UserDesc: u.UserDesc,
|
||||||
}
|
}
|
||||||
|
|
@ -270,3 +270,49 @@ func (s Server) GetUsersUserIdTitles(ctx context.Context, request oapi.GetUsersU
|
||||||
|
|
||||||
return oapi.GetUsersUserIdTitles200JSONResponse{Cursor: new_cursor, Data: oapi_usertitles}, nil
|
return oapi.GetUsersUserIdTitles200JSONResponse{Cursor: new_cursor, Data: oapi_usertitles}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func EmailToStringPtr(e *types.Email) *string {
|
||||||
|
if e == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
s := string(*e)
|
||||||
|
return &s
|
||||||
|
}
|
||||||
|
|
||||||
|
func StringToEmail(e *string) *types.Email {
|
||||||
|
if e == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
s := types.Email(*e)
|
||||||
|
return &s
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdateUser implements oapi.StrictServerInterface.
|
||||||
|
func (s Server) UpdateUser(ctx context.Context, request oapi.UpdateUserRequestObject) (oapi.UpdateUserResponseObject, error) {
|
||||||
|
|
||||||
|
params := sqlc.UpdateUserParams{
|
||||||
|
AvatarID: request.Body.AvatarId,
|
||||||
|
DispName: request.Body.DispName,
|
||||||
|
UserDesc: request.Body.UserDesc,
|
||||||
|
Mail: EmailToStringPtr(request.Body.Mail),
|
||||||
|
UserID: request.UserId,
|
||||||
|
}
|
||||||
|
|
||||||
|
user, err := s.db.UpdateUser(ctx, params)
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf("%v", err)
|
||||||
|
return oapi.UpdateUser500Response{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
oapi_user := oapi.User{ // maybe its possible to make one sqlc type and use one map func iinstead of this shit
|
||||||
|
AvatarId: user.AvatarID,
|
||||||
|
CreationDate: &user.CreationDate,
|
||||||
|
DispName: user.DispName,
|
||||||
|
Id: &user.ID,
|
||||||
|
Mail: StringToEmail(user.Mail),
|
||||||
|
Nickname: user.Nickname,
|
||||||
|
UserDesc: user.UserDesc,
|
||||||
|
}
|
||||||
|
|
||||||
|
return oapi.UpdateUser200JSONResponse(oapi_user), nil
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -58,15 +58,15 @@ RETURNING id, tag_names;
|
||||||
-- VALUES ($1, $2, $3, $4, $5, $6, $7)
|
-- VALUES ($1, $2, $3, $4, $5, $6, $7)
|
||||||
-- RETURNING user_id, avatar_id, nickname, disp_name, user_desc, creation_date;
|
-- RETURNING user_id, avatar_id, nickname, disp_name, user_desc, creation_date;
|
||||||
|
|
||||||
-- -- name: UpdateUser :one
|
-- name: UpdateUser :one
|
||||||
-- UPDATE users
|
UPDATE users
|
||||||
-- SET
|
SET
|
||||||
-- avatar_id = COALESCE(sqlc.narg('avatar_id'), avatar_id),
|
avatar_id = COALESCE(sqlc.narg('avatar_id'), avatar_id),
|
||||||
-- disp_name = COALESCE(sqlc.narg('disp_name'), disp_name),
|
disp_name = COALESCE(sqlc.narg('disp_name'), disp_name),
|
||||||
-- user_desc = COALESCE(sqlc.narg('user_desc'), user_desc),
|
user_desc = COALESCE(sqlc.narg('user_desc'), user_desc),
|
||||||
-- passhash = COALESCE(sqlc.narg('passhash'), passhash)
|
mail = COALESCE(sqlc.narg('mail'), mail)
|
||||||
-- WHERE user_id = sqlc.arg('user_id')
|
WHERE id = sqlc.arg('user_id')
|
||||||
-- RETURNING user_id, avatar_id, nickname, disp_name, user_desc, creation_date;
|
RETURNING id, avatar_id, nickname, disp_name, user_desc, creation_date, mail;
|
||||||
|
|
||||||
-- -- name: DeleteUser :exec
|
-- -- name: DeleteUser :exec
|
||||||
-- DELETE FROM users
|
-- DELETE FROM users
|
||||||
|
|
|
||||||
|
|
@ -114,9 +114,6 @@ func (q *Queries) GetStudioByID(ctx context.Context, studioID int64) (Studio, er
|
||||||
|
|
||||||
const getTitleByID = `-- name: GetTitleByID :one
|
const getTitleByID = `-- name: GetTitleByID :one
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
SELECT
|
SELECT
|
||||||
t.id, t.title_names, t.studio_id, t.poster_id, t.title_status, t.rating, t.rating_count, t.release_year, t.release_season, t.season, t.episodes_aired, t.episodes_all, t.episodes_len,
|
t.id, t.title_names, t.studio_id, t.poster_id, t.title_status, t.rating, t.rating_count, t.release_year, t.release_season, t.season, t.episodes_aired, t.episodes_all, t.episodes_len,
|
||||||
i.storage_type::text as title_storage_type,
|
i.storage_type::text as title_storage_type,
|
||||||
|
|
@ -167,26 +164,6 @@ type GetTitleByIDRow struct {
|
||||||
StudioImagePath *string `json:"studio_image_path"`
|
StudioImagePath *string `json:"studio_image_path"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// -- name: ListUsers :many
|
|
||||||
// SELECT user_id, avatar_id, passhash, mail, nickname, disp_name, user_desc, creation_date
|
|
||||||
// FROM users
|
|
||||||
// ORDER BY user_id
|
|
||||||
// LIMIT $1 OFFSET $2;
|
|
||||||
// -- name: CreateUser :one
|
|
||||||
// INSERT INTO users (avatar_id, passhash, mail, nickname, disp_name, user_desc, creation_date)
|
|
||||||
// VALUES ($1, $2, $3, $4, $5, $6, $7)
|
|
||||||
// RETURNING user_id, avatar_id, nickname, disp_name, user_desc, creation_date;
|
|
||||||
// -- name: UpdateUser :one
|
|
||||||
// UPDATE users
|
|
||||||
// SET
|
|
||||||
//
|
|
||||||
// avatar_id = COALESCE(sqlc.narg('avatar_id'), avatar_id),
|
|
||||||
// disp_name = COALESCE(sqlc.narg('disp_name'), disp_name),
|
|
||||||
// user_desc = COALESCE(sqlc.narg('user_desc'), user_desc),
|
|
||||||
// passhash = COALESCE(sqlc.narg('passhash'), passhash)
|
|
||||||
//
|
|
||||||
// WHERE user_id = sqlc.arg('user_id')
|
|
||||||
// RETURNING user_id, avatar_id, nickname, disp_name, user_desc, creation_date;
|
|
||||||
// -- name: DeleteUser :exec
|
// -- name: DeleteUser :exec
|
||||||
// DELETE FROM users
|
// DELETE FROM users
|
||||||
// WHERE user_id = $1;
|
// WHERE user_id = $1;
|
||||||
|
|
@ -784,3 +761,64 @@ func (q *Queries) SearchUserTitles(ctx context.Context, arg SearchUserTitlesPara
|
||||||
}
|
}
|
||||||
return items, nil
|
return items, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const updateUser = `-- name: UpdateUser :one
|
||||||
|
|
||||||
|
|
||||||
|
UPDATE users
|
||||||
|
SET
|
||||||
|
avatar_id = COALESCE($1, avatar_id),
|
||||||
|
disp_name = COALESCE($2, disp_name),
|
||||||
|
user_desc = COALESCE($3, user_desc),
|
||||||
|
mail = COALESCE($4, mail)
|
||||||
|
WHERE id = $5
|
||||||
|
RETURNING id, avatar_id, nickname, disp_name, user_desc, creation_date, mail
|
||||||
|
`
|
||||||
|
|
||||||
|
type UpdateUserParams struct {
|
||||||
|
AvatarID *int64 `json:"avatar_id"`
|
||||||
|
DispName *string `json:"disp_name"`
|
||||||
|
UserDesc *string `json:"user_desc"`
|
||||||
|
Mail *string `json:"mail"`
|
||||||
|
UserID int64 `json:"user_id"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type UpdateUserRow struct {
|
||||||
|
ID int64 `json:"id"`
|
||||||
|
AvatarID *int64 `json:"avatar_id"`
|
||||||
|
Nickname string `json:"nickname"`
|
||||||
|
DispName *string `json:"disp_name"`
|
||||||
|
UserDesc *string `json:"user_desc"`
|
||||||
|
CreationDate time.Time `json:"creation_date"`
|
||||||
|
Mail *string `json:"mail"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// -- name: ListUsers :many
|
||||||
|
// SELECT user_id, avatar_id, passhash, mail, nickname, disp_name, user_desc, creation_date
|
||||||
|
// FROM users
|
||||||
|
// ORDER BY user_id
|
||||||
|
// LIMIT $1 OFFSET $2;
|
||||||
|
// -- name: CreateUser :one
|
||||||
|
// INSERT INTO users (avatar_id, passhash, mail, nickname, disp_name, user_desc, creation_date)
|
||||||
|
// VALUES ($1, $2, $3, $4, $5, $6, $7)
|
||||||
|
// RETURNING user_id, avatar_id, nickname, disp_name, user_desc, creation_date;
|
||||||
|
func (q *Queries) UpdateUser(ctx context.Context, arg UpdateUserParams) (UpdateUserRow, error) {
|
||||||
|
row := q.db.QueryRow(ctx, updateUser,
|
||||||
|
arg.AvatarID,
|
||||||
|
arg.DispName,
|
||||||
|
arg.UserDesc,
|
||||||
|
arg.Mail,
|
||||||
|
arg.UserID,
|
||||||
|
)
|
||||||
|
var i UpdateUserRow
|
||||||
|
err := row.Scan(
|
||||||
|
&i.ID,
|
||||||
|
&i.AvatarID,
|
||||||
|
&i.Nickname,
|
||||||
|
&i.DispName,
|
||||||
|
&i.UserDesc,
|
||||||
|
&i.CreationDate,
|
||||||
|
&i.Mail,
|
||||||
|
)
|
||||||
|
return i, err
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue