diff --git a/api/_build/openapi.yaml b/api/_build/openapi.yaml index 720b686..e2c7409 100644 --- a/api/_build/openapi.yaml +++ b/api/_build/openapi.yaml @@ -407,6 +407,30 @@ paths: description: User or Title not found '500': description: Internal server error + delete: + operationId: deleteUserTitle + summary: Delete a usertitle + description: User deleting title from list of watched + parameters: + - name: user_id + in: path + description: ID of the user to assign the title to + required: true + schema: + type: integer + format: int64 + example: 123 + responses: + '200': + description: Title successfully deleted + '401': + description: Unauthorized — missing or invalid auth token + '403': + description: Forbidden — user not allowed to delete title + '404': + description: User or Title not found + '500': + description: Internal server error components: parameters: cursor: @@ -557,6 +581,7 @@ components: additionalProperties: type: number format: double + additionalProperties: true required: - id - title_names diff --git a/api/api.gen.go b/api/api.gen.go index cb5c1ae..6af01d0 100644 --- a/api/api.gen.go +++ b/api/api.gen.go @@ -16,12 +16,6 @@ import ( openapi_types "github.com/oapi-codegen/runtime/types" ) -// Defines values for ImageStorageType. -const ( - Local ImageStorageType = "local" - S3 ImageStorageType = "s3" -) - // Defines values for ReleaseSeason. const ( Fall ReleaseSeason = "fall" @@ -30,6 +24,12 @@ const ( Winter ReleaseSeason = "winter" ) +// Defines values for StorageType. +const ( + Local StorageType = "local" + S3 StorageType = "s3" +) + // Defines values for TitleSort. const ( Id TitleSort = "id" @@ -65,15 +65,15 @@ type Image struct { ImagePath *string `json:"image_path,omitempty"` // StorageType Image storage type - StorageType *ImageStorageType `json:"storage_type,omitempty"` + StorageType *StorageType `json:"storage_type,omitempty"` } -// ImageStorageType Image storage type -type ImageStorageType string - // ReleaseSeason Title release season type ReleaseSeason string +// StorageType Image storage type +type StorageType string + // Studio defines model for Studio. type Studio struct { Description *string `json:"description,omitempty"` @@ -156,6 +156,18 @@ type UserTitle struct { UserId int64 `json:"user_id"` } +// UserTitleMini defines model for UserTitleMini. +type UserTitleMini struct { + Ctime *time.Time `json:"ctime,omitempty"` + Rate *int32 `json:"rate,omitempty"` + ReviewId *int64 `json:"review_id,omitempty"` + + // Status User's title status + Status UserTitleStatus `json:"status"` + TitleId int64 `json:"title_id"` + UserId int64 `json:"user_id"` +} + // UserTitleStatus User's title status type UserTitleStatus string @@ -225,21 +237,30 @@ type GetUsersUserIdTitlesParams struct { Fields *string `form:"fields,omitempty" json:"fields,omitempty"` } +// UpdateUserTitleJSONBody defines parameters for UpdateUserTitle. +type UpdateUserTitleJSONBody struct { + Rate *int32 `json:"rate,omitempty"` + + // Status User's title status + Status *UserTitleStatus `json:"status,omitempty"` + TitleId int64 `json:"title_id"` +} + // AddUserTitleJSONBody defines parameters for AddUserTitle. type AddUserTitleJSONBody struct { - Ctime *time.Time `json:"ctime,omitempty"` - Rate *int32 `json:"rate,omitempty"` - ReviewId *int64 `json:"review_id,omitempty"` + Rate *int32 `json:"rate,omitempty"` // Status User's title status Status UserTitleStatus `json:"status"` TitleId int64 `json:"title_id"` - UserId int64 `json:"user_id"` } // UpdateUserJSONRequestBody defines body for UpdateUser for application/json ContentType. type UpdateUserJSONRequestBody UpdateUserJSONBody +// UpdateUserTitleJSONRequestBody defines body for UpdateUserTitle for application/json ContentType. +type UpdateUserTitleJSONRequestBody UpdateUserTitleJSONBody + // AddUserTitleJSONRequestBody defines body for AddUserTitle for application/json ContentType. type AddUserTitleJSONRequestBody AddUserTitleJSONBody @@ -499,9 +520,15 @@ type ServerInterface interface { // Partially update a user account // (PATCH /users/{user_id}) UpdateUser(c *gin.Context, userId int64) + // Delete a usertitle + // (DELETE /users/{user_id}/titles) + DeleteUserTitle(c *gin.Context, userId int64) // Get user titles // (GET /users/{user_id}/titles) GetUsersUserIdTitles(c *gin.Context, userId string, params GetUsersUserIdTitlesParams) + // Update a usertitle + // (PATCH /users/{user_id}/titles) + UpdateUserTitle(c *gin.Context, userId int64) // Add a title to a user // (POST /users/{user_id}/titles) AddUserTitle(c *gin.Context, userId int64) @@ -716,6 +743,30 @@ func (siw *ServerInterfaceWrapper) UpdateUser(c *gin.Context) { siw.Handler.UpdateUser(c, userId) } +// DeleteUserTitle operation middleware +func (siw *ServerInterfaceWrapper) DeleteUserTitle(c *gin.Context) { + + var err error + + // ------------- Path parameter "user_id" ------------- + var userId int64 + + err = runtime.BindStyledParameterWithOptions("simple", "user_id", c.Param("user_id"), &userId, runtime.BindStyledParameterOptions{Explode: false, Required: true}) + if err != nil { + siw.ErrorHandler(c, fmt.Errorf("Invalid format for parameter user_id: %w", err), http.StatusBadRequest) + return + } + + for _, middleware := range siw.HandlerMiddlewares { + middleware(c) + if c.IsAborted() { + return + } + } + + siw.Handler.DeleteUserTitle(c, userId) +} + // GetUsersUserIdTitles operation middleware func (siw *ServerInterfaceWrapper) GetUsersUserIdTitles(c *gin.Context) { @@ -839,6 +890,30 @@ func (siw *ServerInterfaceWrapper) GetUsersUserIdTitles(c *gin.Context) { siw.Handler.GetUsersUserIdTitles(c, userId, params) } +// UpdateUserTitle operation middleware +func (siw *ServerInterfaceWrapper) UpdateUserTitle(c *gin.Context) { + + var err error + + // ------------- Path parameter "user_id" ------------- + var userId int64 + + err = runtime.BindStyledParameterWithOptions("simple", "user_id", c.Param("user_id"), &userId, runtime.BindStyledParameterOptions{Explode: false, Required: true}) + if err != nil { + siw.ErrorHandler(c, fmt.Errorf("Invalid format for parameter user_id: %w", err), http.StatusBadRequest) + return + } + + for _, middleware := range siw.HandlerMiddlewares { + middleware(c) + if c.IsAborted() { + return + } + } + + siw.Handler.UpdateUserTitle(c, userId) +} + // AddUserTitle operation middleware func (siw *ServerInterfaceWrapper) AddUserTitle(c *gin.Context) { @@ -894,7 +969,9 @@ func RegisterHandlersWithOptions(router gin.IRouter, si ServerInterface, options router.GET(options.BaseURL+"/titles/:title_id", wrapper.GetTitlesTitleId) router.GET(options.BaseURL+"/users/:user_id", wrapper.GetUsersUserId) router.PATCH(options.BaseURL+"/users/:user_id", wrapper.UpdateUser) + router.DELETE(options.BaseURL+"/users/:user_id/titles", wrapper.DeleteUserTitle) router.GET(options.BaseURL+"/users/:user_id/titles", wrapper.GetUsersUserIdTitles) + router.PATCH(options.BaseURL+"/users/:user_id/titles", wrapper.UpdateUserTitle) router.POST(options.BaseURL+"/users/:user_id/titles", wrapper.AddUserTitle) } @@ -1110,6 +1187,54 @@ func (response UpdateUser500Response) VisitUpdateUserResponse(w http.ResponseWri return nil } +type DeleteUserTitleRequestObject struct { + UserId int64 `json:"user_id"` +} + +type DeleteUserTitleResponseObject interface { + VisitDeleteUserTitleResponse(w http.ResponseWriter) error +} + +type DeleteUserTitle200Response struct { +} + +func (response DeleteUserTitle200Response) VisitDeleteUserTitleResponse(w http.ResponseWriter) error { + w.WriteHeader(200) + return nil +} + +type DeleteUserTitle401Response struct { +} + +func (response DeleteUserTitle401Response) VisitDeleteUserTitleResponse(w http.ResponseWriter) error { + w.WriteHeader(401) + return nil +} + +type DeleteUserTitle403Response struct { +} + +func (response DeleteUserTitle403Response) VisitDeleteUserTitleResponse(w http.ResponseWriter) error { + w.WriteHeader(403) + return nil +} + +type DeleteUserTitle404Response struct { +} + +func (response DeleteUserTitle404Response) VisitDeleteUserTitleResponse(w http.ResponseWriter) error { + w.WriteHeader(404) + return nil +} + +type DeleteUserTitle500Response struct { +} + +func (response DeleteUserTitle500Response) VisitDeleteUserTitleResponse(w http.ResponseWriter) error { + w.WriteHeader(500) + return nil +} + type GetUsersUserIdTitlesRequestObject struct { UserId string `json:"user_id"` Params GetUsersUserIdTitlesParams @@ -1163,6 +1288,64 @@ func (response GetUsersUserIdTitles500Response) VisitGetUsersUserIdTitlesRespons return nil } +type UpdateUserTitleRequestObject struct { + UserId int64 `json:"user_id"` + Body *UpdateUserTitleJSONRequestBody +} + +type UpdateUserTitleResponseObject interface { + VisitUpdateUserTitleResponse(w http.ResponseWriter) error +} + +type UpdateUserTitle200JSONResponse UserTitleMini + +func (response UpdateUserTitle200JSONResponse) VisitUpdateUserTitleResponse(w http.ResponseWriter) error { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(200) + + return json.NewEncoder(w).Encode(response) +} + +type UpdateUserTitle400Response struct { +} + +func (response UpdateUserTitle400Response) VisitUpdateUserTitleResponse(w http.ResponseWriter) error { + w.WriteHeader(400) + return nil +} + +type UpdateUserTitle401Response struct { +} + +func (response UpdateUserTitle401Response) VisitUpdateUserTitleResponse(w http.ResponseWriter) error { + w.WriteHeader(401) + return nil +} + +type UpdateUserTitle403Response struct { +} + +func (response UpdateUserTitle403Response) VisitUpdateUserTitleResponse(w http.ResponseWriter) error { + w.WriteHeader(403) + return nil +} + +type UpdateUserTitle404Response struct { +} + +func (response UpdateUserTitle404Response) VisitUpdateUserTitleResponse(w http.ResponseWriter) error { + w.WriteHeader(404) + return nil +} + +type UpdateUserTitle500Response struct { +} + +func (response UpdateUserTitle500Response) VisitUpdateUserTitleResponse(w http.ResponseWriter) error { + w.WriteHeader(500) + return nil +} + type AddUserTitleRequestObject struct { UserId int64 `json:"user_id"` Body *AddUserTitleJSONRequestBody @@ -1172,16 +1355,7 @@ type AddUserTitleResponseObject interface { VisitAddUserTitleResponse(w http.ResponseWriter) error } -type AddUserTitle200JSONResponse struct { - Ctime *time.Time `json:"ctime,omitempty"` - Rate *int32 `json:"rate,omitempty"` - ReviewId *int64 `json:"review_id,omitempty"` - - // Status User's title status - Status UserTitleStatus `json:"status"` - TitleId int64 `json:"title_id"` - UserId int64 `json:"user_id"` -} +type AddUserTitle200JSONResponse UserTitleMini func (response AddUserTitle200JSONResponse) VisitAddUserTitleResponse(w http.ResponseWriter) error { w.Header().Set("Content-Type", "application/json") @@ -1252,9 +1426,15 @@ type StrictServerInterface interface { // Partially update a user account // (PATCH /users/{user_id}) UpdateUser(ctx context.Context, request UpdateUserRequestObject) (UpdateUserResponseObject, error) + // Delete a usertitle + // (DELETE /users/{user_id}/titles) + DeleteUserTitle(ctx context.Context, request DeleteUserTitleRequestObject) (DeleteUserTitleResponseObject, error) // Get user titles // (GET /users/{user_id}/titles) GetUsersUserIdTitles(ctx context.Context, request GetUsersUserIdTitlesRequestObject) (GetUsersUserIdTitlesResponseObject, error) + // Update a usertitle + // (PATCH /users/{user_id}/titles) + UpdateUserTitle(ctx context.Context, request UpdateUserTitleRequestObject) (UpdateUserTitleResponseObject, error) // Add a title to a user // (POST /users/{user_id}/titles) AddUserTitle(ctx context.Context, request AddUserTitleRequestObject) (AddUserTitleResponseObject, error) @@ -1390,6 +1570,33 @@ func (sh *strictHandler) UpdateUser(ctx *gin.Context, userId int64) { } } +// DeleteUserTitle operation middleware +func (sh *strictHandler) DeleteUserTitle(ctx *gin.Context, userId int64) { + var request DeleteUserTitleRequestObject + + request.UserId = userId + + handler := func(ctx *gin.Context, request interface{}) (interface{}, error) { + return sh.ssi.DeleteUserTitle(ctx, request.(DeleteUserTitleRequestObject)) + } + for _, middleware := range sh.middlewares { + handler = middleware(handler, "DeleteUserTitle") + } + + response, err := handler(ctx, request) + + if err != nil { + ctx.Error(err) + ctx.Status(http.StatusInternalServerError) + } else if validResponse, ok := response.(DeleteUserTitleResponseObject); ok { + if err := validResponse.VisitDeleteUserTitleResponse(ctx.Writer); err != nil { + ctx.Error(err) + } + } else if response != nil { + ctx.Error(fmt.Errorf("unexpected response type: %T", response)) + } +} + // GetUsersUserIdTitles operation middleware func (sh *strictHandler) GetUsersUserIdTitles(ctx *gin.Context, userId string, params GetUsersUserIdTitlesParams) { var request GetUsersUserIdTitlesRequestObject @@ -1418,6 +1625,41 @@ func (sh *strictHandler) GetUsersUserIdTitles(ctx *gin.Context, userId string, p } } +// UpdateUserTitle operation middleware +func (sh *strictHandler) UpdateUserTitle(ctx *gin.Context, userId int64) { + var request UpdateUserTitleRequestObject + + request.UserId = userId + + var body UpdateUserTitleJSONRequestBody + if err := ctx.ShouldBindJSON(&body); err != nil { + ctx.Status(http.StatusBadRequest) + ctx.Error(err) + return + } + request.Body = &body + + handler := func(ctx *gin.Context, request interface{}) (interface{}, error) { + return sh.ssi.UpdateUserTitle(ctx, request.(UpdateUserTitleRequestObject)) + } + for _, middleware := range sh.middlewares { + handler = middleware(handler, "UpdateUserTitle") + } + + response, err := handler(ctx, request) + + if err != nil { + ctx.Error(err) + ctx.Status(http.StatusInternalServerError) + } else if validResponse, ok := response.(UpdateUserTitleResponseObject); ok { + if err := validResponse.VisitUpdateUserTitleResponse(ctx.Writer); err != nil { + ctx.Error(err) + } + } else if response != nil { + ctx.Error(fmt.Errorf("unexpected response type: %T", response)) + } +} + // AddUserTitle operation middleware func (sh *strictHandler) AddUserTitle(ctx *gin.Context, userId int64) { var request AddUserTitleRequestObject diff --git a/api/openapi.yaml b/api/openapi.yaml index 7da26f8..23f2058 100644 --- a/api/openapi.yaml +++ b/api/openapi.yaml @@ -21,4 +21,3 @@ components: $ref: "./parameters/_index.yaml" schemas: $ref: "./schemas/_index.yaml" - \ No newline at end of file diff --git a/api/paths/users-id-titles.yaml b/api/paths/users-id-titles.yaml index 4f11ab6..2cff448 100644 --- a/api/paths/users-id-titles.yaml +++ b/api/paths/users-id-titles.yaml @@ -187,5 +187,33 @@ patch: description: Forbidden — user not allowed to update title '404': description: User or Title not found + '500': + description: Internal server error + +delete: + summary: Delete a usertitle + description: User deleting title from list of watched + operationId: deleteUserTitle + parameters: + - name: user_id + in: path + required: true + schema: + type: integer + format: int64 + description: ID of the user to assign the title to + example: 123 + + responses: + '200': + description: Title successfully deleted + # '400': + # description: Invalid request body (missing fields, invalid types, etc.) + '401': + description: Unauthorized — missing or invalid auth token + '403': + description: Forbidden — user not allowed to delete title + '404': + description: User or Title not found '500': description: Internal server error \ No newline at end of file diff --git a/deploy/api_gen.ps1 b/deploy/api_gen.ps1 new file mode 100644 index 0000000..c8966b7 --- /dev/null +++ b/deploy/api_gen.ps1 @@ -0,0 +1,4 @@ +cd ./api +openapi-format .\openapi.yaml --output .\_build\openapi.yaml --yaml +cd .. +oapi-codegen --config=api\oapi-codegen.yaml api\_build\openapi.yaml diff --git a/modules/backend/handlers/common.go b/modules/backend/handlers/common.go index 2cf2283..f820db6 100644 --- a/modules/backend/handlers/common.go +++ b/modules/backend/handlers/common.go @@ -1,7 +1,6 @@ package handlers import ( - "context" "encoding/json" "fmt" oapi "nyanimedb/api" @@ -17,11 +16,11 @@ func NewServer(db *sqlc.Queries) Server { return Server{db: db} } -func sql2StorageType(s *sqlc.StorageTypeT) (*oapi.ImageStorageType, error) { +func sql2StorageType(s *sqlc.StorageTypeT) (*oapi.StorageType, error) { if s == nil { return nil, nil } - var t oapi.ImageStorageType + var t oapi.StorageType switch *s { case sqlc.StorageTypeTLocal: t = oapi.Local @@ -33,7 +32,7 @@ func sql2StorageType(s *sqlc.StorageTypeT) (*oapi.ImageStorageType, error) { return &t, nil } -func (s Server) mapTitle(ctx context.Context, title sqlc.GetTitleByIDRow) (oapi.Title, error) { +func (s Server) mapTitle(title sqlc.GetTitleByIDRow) (oapi.Title, error) { oapi_title := oapi.Title{ EpisodesAired: title.EpisodesAired, diff --git a/modules/backend/handlers/titles.go b/modules/backend/handlers/titles.go index c67177f..03553fd 100644 --- a/modules/backend/handlers/titles.go +++ b/modules/backend/handlers/titles.go @@ -144,7 +144,7 @@ func (s Server) GetTitlesTitleId(ctx context.Context, request oapi.GetTitlesTitl return oapi.GetTitlesTitleId500Response{}, nil } - oapi_title, err = s.mapTitle(ctx, sqlc_title) + oapi_title, err = s.mapTitle(sqlc_title) if err != nil { log.Errorf("%v", err) return oapi.GetTitlesTitleId500Response{}, nil @@ -238,7 +238,7 @@ func (s Server) GetTitles(ctx context.Context, request oapi.GetTitlesRequestObje // _title.TitleStorageType = string(s) // } - t, err := s.mapTitle(ctx, _title) + t, err := s.mapTitle(_title) if err != nil { log.Errorf("%v", err) return oapi.GetTitles500Response{}, nil diff --git a/modules/backend/handlers/users.go b/modules/backend/handlers/users.go index 1881f36..7af705e 100644 --- a/modules/backend/handlers/users.go +++ b/modules/backend/handlers/users.go @@ -2,6 +2,7 @@ package handlers import ( "context" + "errors" "fmt" oapi "nyanimedb/api" sqlc "nyanimedb/sql" @@ -9,24 +10,12 @@ import ( "time" "github.com/jackc/pgx/v5" + "github.com/jackc/pgx/v5/pgconn" "github.com/jackc/pgx/v5/pgtype" "github.com/oapi-codegen/runtime/types" log "github.com/sirupsen/logrus" ) -// type Server struct { -// db *sqlc.Queries -// } - -// func NewServer(db *sqlc.Queries) Server { -// return Server{db: db} -// } - -// func parseInt64(s string) (int32, error) { -// i, err := strconv.ParseInt(s, 10, 64) -// return int32(i), err -// } - func mapUser(u sqlc.GetUserByIDRow) (oapi.User, error) { i := oapi.Image{ Id: u.AvatarID, @@ -202,7 +191,7 @@ func (s Server) mapUsertitle(ctx context.Context, t sqlc.SearchUserTitlesRow) (o // StudioImagePath: title.StudioImagePath, } - oapi_title, err := s.mapTitle(ctx, _title) + oapi_title, err := s.mapTitle(_title) if err != nil { return oapi_usertitle, fmt.Errorf("mapUsertitle: %v", err) } @@ -368,19 +357,26 @@ func (s Server) AddUserTitle(ctx context.Context, request oapi.AddUserTitleReque } params := sqlc.InsertUserTitleParams{ - UserID: request.UserId, - TitleID: request.Body.TitleId, - Status: *status, - Rate: request.Body.Rate, - ReviewID: request.Body.ReviewId, + UserID: request.UserId, + TitleID: request.Body.TitleId, + Status: *status, + Rate: request.Body.Rate, } user_title, err := s.db.InsertUserTitle(ctx, params) if err != nil { - log.Errorf("%v", err) - return oapi.AddUserTitle500Response{}, nil + var pgErr *pgconn.PgError + if errors.As(err, &pgErr) { + // fmt.Println(pgErr.Message) // => syntax error at end of input + // fmt.Println(pgErr.Code) // => 42601 + if pgErr.Code == "23505" { //duplicate key value + return oapi.AddUserTitle409Response{}, nil + } + } else { + log.Errorf("%v", err) + return oapi.AddUserTitle500Response{}, nil + } } - oapi_status, err := sql2usertitlestatus(user_title.Status) if err != nil { log.Errorf("%v", err) @@ -406,3 +402,13 @@ func (s Server) AddUserTitle(ctx context.Context, request oapi.AddUserTitleReque return oapi.AddUserTitle200JSONResponse(oapi_usertitle), nil } + +// DeleteUserTitle implements oapi.StrictServerInterface. +func (s Server) DeleteUserTitle(ctx context.Context, request oapi.DeleteUserTitleRequestObject) (oapi.DeleteUserTitleResponseObject, error) { + panic("unimplemented") +} + +// UpdateUserTitle implements oapi.StrictServerInterface. +func (s Server) UpdateUserTitle(ctx context.Context, request oapi.UpdateUserTitleRequestObject) (oapi.UpdateUserTitleResponseObject, error) { + panic("unimplemented") +} diff --git a/modules/backend/queries.sql b/modules/backend/queries.sql index 0146b25..ef6e26d 100644 --- a/modules/backend/queries.sql +++ b/modules/backend/queries.sql @@ -461,21 +461,13 @@ VALUES ( ) RETURNING user_id, title_id, status, rate, review_id, ctime; --- -- name: UpdateUserTitle :one --- UPDATE usertitles --- SET --- status = COALESCE(sqlc.narg('status'), status), --- rate = COALESCE(sqlc.narg('rate'), rate), --- review_id = COALESCE(sqlc.narg('review_id'), review_id) --- WHERE user_id = $1 AND title_id = $2 --- RETURNING *; - --- -- name: DeleteUserTitle :exec --- DELETE FROM usertitles --- WHERE user_id = $1 AND ($2::int IS NULL OR title_id = $2); - --- -- name: ListTags :many --- SELECT tag_id, tag_names --- FROM tags --- ORDER BY tag_id --- LIMIT $1 OFFSET $2; \ No newline at end of file +-- name: UpdateUserTitle :one +-- Fails with sql.ErrNoRows if (user_id, title_id) not found +UPDATE usertitles +SET + status = COALESCE(sqlc.narg('status')::usertitle_status_t, status), + rate = COALESCE(sqlc.narg('rate')::int, rate) +WHERE + user_id = sqlc.arg('user_id') + AND title_id = sqlc.arg('title_id') +RETURNING *; \ No newline at end of file diff --git a/sql/migrations/000001_init.up.sql b/sql/migrations/000001_init.up.sql index f8781de..3499fe2 100644 --- a/sql/migrations/000001_init.up.sql +++ b/sql/migrations/000001_init.up.sql @@ -179,6 +179,6 @@ END; $$ LANGUAGE plpgsql; CREATE TRIGGER set_ctime_on_update -AFTER UPDATE ON usertitles +BEFORE UPDATE ON usertitles FOR EACH ROW EXECUTE FUNCTION set_ctime(); \ No newline at end of file diff --git a/sql/queries.sql.go b/sql/queries.sql.go index a46da86..89b16c9 100644 --- a/sql/queries.sql.go +++ b/sql/queries.sql.go @@ -925,3 +925,41 @@ func (q *Queries) UpdateUser(ctx context.Context, arg UpdateUserParams) (UpdateU ) return i, err } + +const updateUserTitle = `-- name: UpdateUserTitle :one +UPDATE usertitles +SET + status = COALESCE($1::usertitle_status_t, status), + rate = COALESCE($2::int, rate) +WHERE + user_id = $3 + AND title_id = $4 +RETURNING user_id, title_id, status, rate, review_id, ctime +` + +type UpdateUserTitleParams struct { + Status NullUsertitleStatusT `json:"status"` + Rate *int32 `json:"rate"` + UserID int64 `json:"user_id"` + TitleID int64 `json:"title_id"` +} + +// Fails with sql.ErrNoRows if (user_id, title_id) not found +func (q *Queries) UpdateUserTitle(ctx context.Context, arg UpdateUserTitleParams) (Usertitle, error) { + row := q.db.QueryRow(ctx, updateUserTitle, + arg.Status, + arg.Rate, + arg.UserID, + arg.TitleID, + ) + var i Usertitle + err := row.Scan( + &i.UserID, + &i.TitleID, + &i.Status, + &i.Rate, + &i.ReviewID, + &i.Ctime, + ) + return i, err +}