From b81c94f2da7bfabd74ed8dd24d7e6f38e7d5573a Mon Sep 17 00:00:00 2001 From: Zoe Roux Date: Sat, 29 Mar 2025 20:05:09 +0100 Subject: [PATCH] Cleanup doc of users routes --- auth/docs/docs.go | 115 ++++++++++++++++++++++------------------- auth/docs/swagger.json | 115 ++++++++++++++++++++++------------------- auth/users.go | 51 +++++++++--------- 3 files changed, 150 insertions(+), 131 deletions(-) diff --git a/auth/docs/docs.go b/auth/docs/docs.go index 284074e5..4540ee57 100644 --- a/auth/docs/docs.go +++ b/auth/docs/docs.go @@ -159,14 +159,14 @@ const docTemplate = `{ "$ref": "#/definitions/main.Session" } }, - "403": { - "description": "Invalid jwt token (or expired)", + "401": { + "description": "Missing jwt token", "schema": { "$ref": "#/definitions/main.KError" } }, - "422": { - "description": "Invalid session id", + "403": { + "description": "Invalid jwt token (or expired)", "schema": { "$ref": "#/definitions/main.KError" } @@ -258,9 +258,11 @@ const docTemplate = `{ "$ref": "#/definitions/main.User" } }, - "400": { + "422": { "description": "Invalid after id", - "schema": {} + "schema": { + "$ref": "#/definitions/main.KError" + } } } }, @@ -279,6 +281,7 @@ const docTemplate = `{ "parameters": [ { "type": "string", + "example": "android", "description": "The device the created session will be used on", "name": "device", "in": "query" @@ -296,16 +299,20 @@ const docTemplate = `{ "201": { "description": "Created", "schema": { - "$ref": "#/definitions/dbc.Session" + "$ref": "#/definitions/main.SessionWToken" } }, - "400": { - "description": "Invalid register body", - "schema": {} - }, "409": { "description": "Duplicated email or username", - "schema": {} + "schema": { + "$ref": "#/definitions/main.KError" + } + }, + "422": { + "description": "Invalid register body", + "schema": { + "$ref": "#/definitions/main.KError" + } } } } @@ -334,11 +341,15 @@ const docTemplate = `{ }, "401": { "description": "Missing jwt token", - "schema": {} + "schema": { + "$ref": "#/definitions/main.KError" + } }, "403": { "description": "Invalid jwt token (or expired)", - "schema": {} + "schema": { + "$ref": "#/definitions/main.KError" + } } } }, @@ -405,7 +416,15 @@ const docTemplate = `{ }, "404": { "description": "No user with the given id found", - "schema": {} + "schema": { + "$ref": "#/definitions/main.KError" + } + }, + "422": { + "description": "Invalid id (not a uuid)", + "schema": { + "$ref": "#/definitions/main.KError" + } } } }, @@ -446,39 +465,15 @@ const docTemplate = `{ }, "404": { "description": "Invalid user id", - "schema": {} + "schema": { + "$ref": "#/definitions/main.KError" + } } } } } }, "definitions": { - "dbc.Session": { - "type": "object", - "properties": { - "createdDate": { - "type": "string" - }, - "device": { - "type": "string" - }, - "id": { - "type": "string" - }, - "lastUsed": { - "type": "string" - }, - "pk": { - "type": "integer" - }, - "token": { - "type": "string" - }, - "userPk": { - "type": "integer" - } - } - }, "main.JwkSet": { "type": "object", "properties": { @@ -565,16 +560,19 @@ const docTemplate = `{ "properties": { "id": { "description": "Id of this oidc handle.", - "type": "string" + "type": "string", + "example": "e05089d6-9179-4b5b-a63e-94dd5fc2a397" }, "profileUrl": { "description": "Link to the profile of the user on the external service. Null if unknown or irrelevant.", "type": "string", - "format": "url" + "format": "url", + "example": "https://myanimelist.net/profile/zoriya" }, "username": { "description": "Username of the user on the external service.", - "type": "string" + "type": "string", + "example": "zoriya" } } }, @@ -589,15 +587,18 @@ const docTemplate = `{ "email": { "description": "Valid email that could be used for forgotten password requests. Can be used for login.", "type": "string", - "format": "email" + "format": "email", + "example": "kyoo@zoriya.dev" }, "password": { "description": "Password to use.", - "type": "string" + "type": "string", + "example": "password1234" }, "username": { "description": "Username of the new account, can't contain @ signs. Can be used for login.", - "type": "string" + "type": "string", + "example": "zoriya" } } }, @@ -663,24 +664,31 @@ const docTemplate = `{ "type": "object", "additionalProperties": { "type": "string" + }, + "example": { + "isAdmin": " true" } }, "createdDate": { "description": "When was this account created?", - "type": "string" + "type": "string", + "example": "2025-03-29T18:20:05.267Z" }, "email": { "description": "Email of the user. Can be used as a login.", "type": "string", - "format": "email" + "format": "email", + "example": "kyoo@zoriya.dev" }, "id": { "description": "Id of the user.", - "type": "string" + "type": "string", + "example": "e05089d6-9179-4b5b-a63e-94dd5fc2a397" }, "lastSeen": { "description": "When was the last time this account made any authorized request?", - "type": "string" + "type": "string", + "example": "2025-03-29T18:20:05.267Z" }, "oidc": { "description": "List of other login method available for this user. Access tokens wont be returned here.", @@ -691,7 +699,8 @@ const docTemplate = `{ }, "username": { "description": "Username of the user. Can be used as a login.", - "type": "string" + "type": "string", + "example": "zoriya" } } } diff --git a/auth/docs/swagger.json b/auth/docs/swagger.json index ec7b927e..7b2b2d6d 100644 --- a/auth/docs/swagger.json +++ b/auth/docs/swagger.json @@ -153,14 +153,14 @@ "$ref": "#/definitions/main.Session" } }, - "403": { - "description": "Invalid jwt token (or expired)", + "401": { + "description": "Missing jwt token", "schema": { "$ref": "#/definitions/main.KError" } }, - "422": { - "description": "Invalid session id", + "403": { + "description": "Invalid jwt token (or expired)", "schema": { "$ref": "#/definitions/main.KError" } @@ -252,9 +252,11 @@ "$ref": "#/definitions/main.User" } }, - "400": { + "422": { "description": "Invalid after id", - "schema": {} + "schema": { + "$ref": "#/definitions/main.KError" + } } } }, @@ -273,6 +275,7 @@ "parameters": [ { "type": "string", + "example": "android", "description": "The device the created session will be used on", "name": "device", "in": "query" @@ -290,16 +293,20 @@ "201": { "description": "Created", "schema": { - "$ref": "#/definitions/dbc.Session" + "$ref": "#/definitions/main.SessionWToken" } }, - "400": { - "description": "Invalid register body", - "schema": {} - }, "409": { "description": "Duplicated email or username", - "schema": {} + "schema": { + "$ref": "#/definitions/main.KError" + } + }, + "422": { + "description": "Invalid register body", + "schema": { + "$ref": "#/definitions/main.KError" + } } } } @@ -328,11 +335,15 @@ }, "401": { "description": "Missing jwt token", - "schema": {} + "schema": { + "$ref": "#/definitions/main.KError" + } }, "403": { "description": "Invalid jwt token (or expired)", - "schema": {} + "schema": { + "$ref": "#/definitions/main.KError" + } } } }, @@ -399,7 +410,15 @@ }, "404": { "description": "No user with the given id found", - "schema": {} + "schema": { + "$ref": "#/definitions/main.KError" + } + }, + "422": { + "description": "Invalid id (not a uuid)", + "schema": { + "$ref": "#/definitions/main.KError" + } } } }, @@ -440,39 +459,15 @@ }, "404": { "description": "Invalid user id", - "schema": {} + "schema": { + "$ref": "#/definitions/main.KError" + } } } } } }, "definitions": { - "dbc.Session": { - "type": "object", - "properties": { - "createdDate": { - "type": "string" - }, - "device": { - "type": "string" - }, - "id": { - "type": "string" - }, - "lastUsed": { - "type": "string" - }, - "pk": { - "type": "integer" - }, - "token": { - "type": "string" - }, - "userPk": { - "type": "integer" - } - } - }, "main.JwkSet": { "type": "object", "properties": { @@ -559,16 +554,19 @@ "properties": { "id": { "description": "Id of this oidc handle.", - "type": "string" + "type": "string", + "example": "e05089d6-9179-4b5b-a63e-94dd5fc2a397" }, "profileUrl": { "description": "Link to the profile of the user on the external service. Null if unknown or irrelevant.", "type": "string", - "format": "url" + "format": "url", + "example": "https://myanimelist.net/profile/zoriya" }, "username": { "description": "Username of the user on the external service.", - "type": "string" + "type": "string", + "example": "zoriya" } } }, @@ -583,15 +581,18 @@ "email": { "description": "Valid email that could be used for forgotten password requests. Can be used for login.", "type": "string", - "format": "email" + "format": "email", + "example": "kyoo@zoriya.dev" }, "password": { "description": "Password to use.", - "type": "string" + "type": "string", + "example": "password1234" }, "username": { "description": "Username of the new account, can't contain @ signs. Can be used for login.", - "type": "string" + "type": "string", + "example": "zoriya" } } }, @@ -657,24 +658,31 @@ "type": "object", "additionalProperties": { "type": "string" + }, + "example": { + "isAdmin": " true" } }, "createdDate": { "description": "When was this account created?", - "type": "string" + "type": "string", + "example": "2025-03-29T18:20:05.267Z" }, "email": { "description": "Email of the user. Can be used as a login.", "type": "string", - "format": "email" + "format": "email", + "example": "kyoo@zoriya.dev" }, "id": { "description": "Id of the user.", - "type": "string" + "type": "string", + "example": "e05089d6-9179-4b5b-a63e-94dd5fc2a397" }, "lastSeen": { "description": "When was the last time this account made any authorized request?", - "type": "string" + "type": "string", + "example": "2025-03-29T18:20:05.267Z" }, "oidc": { "description": "List of other login method available for this user. Access tokens wont be returned here.", @@ -685,7 +693,8 @@ }, "username": { "description": "Username of the user. Can be used as a login.", - "type": "string" + "type": "string", + "example": "zoriya" } } } diff --git a/auth/users.go b/auth/users.go index 31ee799c..c5e8632c 100644 --- a/auth/users.go +++ b/auth/users.go @@ -18,37 +18,37 @@ type User struct { // Primary key in database Pk int32 `json:"-"` // Id of the user. - Id uuid.UUID `json:"id"` + Id uuid.UUID `json:"id" example:"e05089d6-9179-4b5b-a63e-94dd5fc2a397"` // Username of the user. Can be used as a login. - Username string `json:"username"` + Username string `json:"username" example:"zoriya"` // Email of the user. Can be used as a login. - Email string `json:"email" format:"email"` + Email string `json:"email" format:"email" example:"kyoo@zoriya.dev"` // When was this account created? - CreatedDate time.Time `json:"createdDate"` + CreatedDate time.Time `json:"createdDate" example:"2025-03-29T18:20:05.267Z"` // When was the last time this account made any authorized request? - LastSeen time.Time `json:"lastSeen"` + LastSeen time.Time `json:"lastSeen" example:"2025-03-29T18:20:05.267Z"` // List of custom claims JWT created via get /jwt will have - Claims jwt.MapClaims `json:"claims"` + Claims jwt.MapClaims `json:"claims" example:"isAdmin: true"` // List of other login method available for this user. Access tokens wont be returned here. Oidc map[string]OidcHandle `json:"oidc,omitempty"` } type OidcHandle struct { // Id of this oidc handle. - Id string `json:"id"` + Id string `json:"id" example:"e05089d6-9179-4b5b-a63e-94dd5fc2a397"` // Username of the user on the external service. - Username string `json:"username"` + Username string `json:"username" example:"zoriya"` // Link to the profile of the user on the external service. Null if unknown or irrelevant. - ProfileUrl *string `json:"profileUrl" format:"url"` + ProfileUrl *string `json:"profileUrl" format:"url" example:"https://myanimelist.net/profile/zoriya"` } type RegisterDto struct { // Username of the new account, can't contain @ signs. Can be used for login. - Username string `json:"username" validate:"required,excludes=@"` + Username string `json:"username" validate:"required,excludes=@" example:"zoriya"` // Valid email that could be used for forgotten password requests. Can be used for login. - Email string `json:"email" validate:"required,email" format:"email"` + Email string `json:"email" validate:"required,email" format:"email" example:"kyoo@zoriya.dev"` // Password to use. - Password string `json:"password" validate:"required"` + Password string `json:"password" validate:"required" example:"password1234"` } func MapDbUser(user *dbc.User) User { @@ -80,7 +80,7 @@ func MapOidc(oidc *dbc.GetUserRow) OidcHandle { // @Security Jwt[users.read] // @Param afterId query string false "used for pagination." Format(uuid) // @Success 200 {object} User[] -// @Failure 400 {object} problem.Problem "Invalid after id" +// @Failure 422 {object} KError "Invalid after id" // @Router /users [get] func (h *Handler) ListUsers(c echo.Context) error { err := CheckPermissions(c, []string{"user.read"}) @@ -98,7 +98,7 @@ func (h *Handler) ListUsers(c echo.Context) error { } else { uid, uerr := uuid.Parse(id) if uerr != nil { - return echo.NewHTTPError(400, "Invalid `afterId` parameter, uuid was expected") + return echo.NewHTTPError(http.StatusUnprocessableEntity, "Invalid `afterId` parameter, uuid was expected") } users, err = h.db.GetAllUsersAfter(ctx, dbc.GetAllUsersAfterParams{ Limit: limit, @@ -125,7 +125,8 @@ func (h *Handler) ListUsers(c echo.Context) error { // @Security Jwt[users.read] // @Param id path string true "The id of the user" Format(uuid) // @Success 200 {object} User -// @Failure 404 {object} problem.Problem "No user with the given id found" +// @Failure 404 {object} KError "No user with the given id found" +// @Failure 422 {object} KError "Invalid id (not a uuid)" // @Router /users/{id} [get] func (h *Handler) GetUser(c echo.Context) error { err := CheckPermissions(c, []string{"user.read"}) @@ -135,7 +136,7 @@ func (h *Handler) GetUser(c echo.Context) error { id, err := uuid.Parse(c.Param("id")) if err != nil { - return echo.NewHTTPError(400, "Invalid id") + return echo.NewHTTPError(http.StatusUnprocessableEntity, "Invalid id") } dbuser, err := h.db.GetUser(context.Background(), id) if err != nil { @@ -158,8 +159,8 @@ func (h *Handler) GetUser(c echo.Context) error { // @Produce json // @Security Jwt // @Success 200 {object} User -// @Failure 401 {object} problem.Problem "Missing jwt token" -// @Failure 403 {object} problem.Problem "Invalid jwt token (or expired)" +// @Failure 401 {object} KError "Missing jwt token" +// @Failure 403 {object} KError "Invalid jwt token (or expired)" // @Router /users/me [get] func (h *Handler) GetMe(c echo.Context) error { id, err := GetCurrentUserId(c) @@ -186,17 +187,17 @@ func (h *Handler) GetMe(c echo.Context) error { // @Tags users // @Accept json // @Produce json -// @Param device query string false "The device the created session will be used on" +// @Param device query string false "The device the created session will be used on" Example(android) // @Param user body RegisterDto false "Registration informations" -// @Success 201 {object} dbc.Session -// @Failure 400 {object} problem.Problem "Invalid register body" -// @Success 409 {object} problem.Problem "Duplicated email or username" +// @Success 201 {object} SessionWToken +// @Success 409 {object} KError "Duplicated email or username" +// @Failure 422 {object} KError "Invalid register body" // @Router /users [post] func (h *Handler) Register(c echo.Context) error { var req RegisterDto err := c.Bind(&req) if err != nil { - return echo.NewHTTPError(http.StatusBadRequest, err.Error()) + return echo.NewHTTPError(http.StatusUnprocessableEntity, err.Error()) } if err = c.Validate(&req); err != nil { return err @@ -230,8 +231,8 @@ func (h *Handler) Register(c echo.Context) error { // @Security Jwt[users.delete] // @Param id path string false "User id of the user to delete" Format(uuid) // @Success 200 {object} User -// @Failure 404 {object} problem.Problem "Invalid id format" -// @Failure 404 {object} problem.Problem "Invalid user id" +// @Failure 404 {object} KError "Invalid id format" +// @Failure 404 {object} KError "Invalid user id" // @Router /users/{id} [delete] func (h *Handler) DeleteUser(c echo.Context) error { uid, err := uuid.Parse(c.Param("id"))