From 411f6dcfba05ea1d3dd39a34e1865574be0bed72 Mon Sep 17 00:00:00 2001 From: Zoe Roux Date: Sun, 30 Mar 2025 20:12:56 +0200 Subject: [PATCH] Use pages for users paginations --- api/src/models/utils/descriptions.ts | 2 +- auth/docs/docs.go | 24 +++++++++++++++++++++--- auth/docs/swagger.json | 24 +++++++++++++++++++++--- auth/page.go | 28 ++++++++++++++++++++++++++++ auth/users.go | 11 +++++------ 5 files changed, 76 insertions(+), 13 deletions(-) create mode 100644 auth/page.go diff --git a/api/src/models/utils/descriptions.ts b/api/src/models/utils/descriptions.ts index 0e683805..8ace9d4d 100644 --- a/api/src/models/utils/descriptions.ts +++ b/api/src/models/utils/descriptions.ts @@ -8,7 +8,7 @@ export const desc = { `, after: comment` - Id of the cursor in the pagination. + Cursor for the pagination. You can ignore this and only use the prev/next field in the response. `, diff --git a/auth/docs/docs.go b/auth/docs/docs.go index 4540ee57..124597f8 100644 --- a/auth/docs/docs.go +++ b/auth/docs/docs.go @@ -245,9 +245,8 @@ const docTemplate = `{ "parameters": [ { "type": "string", - "format": "uuid", "description": "used for pagination.", - "name": "afterId", + "name": "after", "in": "query" } ], @@ -255,7 +254,7 @@ const docTemplate = `{ "200": { "description": "OK", "schema": { - "$ref": "#/definitions/main.User" + "$ref": "#/definitions/main.Page-main_User" } }, "422": { @@ -576,6 +575,25 @@ const docTemplate = `{ } } }, + "main.Page-main_User": { + "type": "object", + "properties": { + "items": { + "type": "array", + "items": { + "$ref": "#/definitions/main.User" + } + }, + "next": { + "type": "string", + "example": "https://kyoo.zoriya.dev/auth/users?after=aoeusth" + }, + "this": { + "type": "string", + "example": "https://kyoo.zoriya.dev/auth/users" + } + } + }, "main.RegisterDto": { "type": "object", "required": [ diff --git a/auth/docs/swagger.json b/auth/docs/swagger.json index 7b2b2d6d..2215fcf0 100644 --- a/auth/docs/swagger.json +++ b/auth/docs/swagger.json @@ -239,9 +239,8 @@ "parameters": [ { "type": "string", - "format": "uuid", "description": "used for pagination.", - "name": "afterId", + "name": "after", "in": "query" } ], @@ -249,7 +248,7 @@ "200": { "description": "OK", "schema": { - "$ref": "#/definitions/main.User" + "$ref": "#/definitions/main.Page-main_User" } }, "422": { @@ -570,6 +569,25 @@ } } }, + "main.Page-main_User": { + "type": "object", + "properties": { + "items": { + "type": "array", + "items": { + "$ref": "#/definitions/main.User" + } + }, + "next": { + "type": "string", + "example": "https://kyoo.zoriya.dev/auth/users?after=aoeusth" + }, + "this": { + "type": "string", + "example": "https://kyoo.zoriya.dev/auth/users" + } + } + }, "main.RegisterDto": { "type": "object", "required": [ diff --git a/auth/page.go b/auth/page.go new file mode 100644 index 00000000..c82dffff --- /dev/null +++ b/auth/page.go @@ -0,0 +1,28 @@ +package main + +import "net/url" + +type Page[T any] struct { + Items []T `json:"items"` + This string `json:"this" example:"https://kyoo.zoriya.dev/auth/users"` + Next *string `json:"next" example:"https://kyoo.zoriya.dev/auth/users?after=aoeusth"` +} + +func NewPage(items []User, url *url.URL, limit int32) Page[User] { + this := url.String() + + var next *string + if len(items) == int(limit) && limit > 0 { + query := url.Query() + query.Set("after", items[len(items)-1].Id.String()) + url.RawQuery = query.Encode() + nextU := url.String() + next = &nextU + } + + return Page[User]{ + Items: items, + This: this, + Next: next, + } +} diff --git a/auth/users.go b/auth/users.go index c5e8632c..7781c0ec 100644 --- a/auth/users.go +++ b/auth/users.go @@ -78,8 +78,8 @@ func MapOidc(oidc *dbc.GetUserRow) OidcHandle { // @Accept json // @Produce json // @Security Jwt[users.read] -// @Param afterId query string false "used for pagination." Format(uuid) -// @Success 200 {object} User[] +// @Param after query string false "used for pagination." +// @Success 200 {object} Page[User] // @Failure 422 {object} KError "Invalid after id" // @Router /users [get] func (h *Handler) ListUsers(c echo.Context) error { @@ -90,7 +90,7 @@ func (h *Handler) ListUsers(c echo.Context) error { ctx := context.Background() limit := int32(20) - id := c.Param("afterId") + id := c.Param("after") var users []dbc.User if id == "" { @@ -98,7 +98,7 @@ func (h *Handler) ListUsers(c echo.Context) error { } else { uid, uerr := uuid.Parse(id) if uerr != nil { - return echo.NewHTTPError(http.StatusUnprocessableEntity, "Invalid `afterId` parameter, uuid was expected") + return echo.NewHTTPError(http.StatusUnprocessableEntity, "Invalid `after` parameter, uuid was expected") } users, err = h.db.GetAllUsersAfter(ctx, dbc.GetAllUsersAfterParams{ Limit: limit, @@ -114,8 +114,7 @@ func (h *Handler) ListUsers(c echo.Context) error { for _, user := range users { ret = append(ret, MapDbUser(&user)) } - // TODO: switch to a Page - return c.JSON(200, ret) + return c.JSON(200, NewPage(ret, c.Request().URL, limit)) } // @Summary Get user