mirror of
https://github.com/hay-kot/homebox.git
synced 2025-08-04 16:50:27 +00:00
implement pagination for items
This commit is contained in:
parent
85a96cde91
commit
baa352d7ad
19 changed files with 435 additions and 327 deletions
|
@ -77,6 +77,18 @@ const docTemplate = `{
|
|||
"name": "q",
|
||||
"in": "query"
|
||||
},
|
||||
{
|
||||
"type": "integer",
|
||||
"description": "page number",
|
||||
"name": "page",
|
||||
"in": "query"
|
||||
},
|
||||
{
|
||||
"type": "integer",
|
||||
"description": "items per page",
|
||||
"name": "pageSize",
|
||||
"in": "query"
|
||||
},
|
||||
{
|
||||
"type": "array",
|
||||
"items": {
|
||||
|
@ -102,22 +114,7 @@ const docTemplate = `{
|
|||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/server.Results"
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"items": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/repo.ItemSummary"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
"$ref": "#/definitions/repo.PaginationResult-repo_ItemSummary"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -181,7 +178,7 @@ const docTemplate = `{
|
|||
],
|
||||
"responses": {
|
||||
"204": {
|
||||
"description": ""
|
||||
"description": "No Content"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -282,7 +279,7 @@ const docTemplate = `{
|
|||
],
|
||||
"responses": {
|
||||
"204": {
|
||||
"description": ""
|
||||
"description": "No Content"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -382,7 +379,7 @@ const docTemplate = `{
|
|||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": ""
|
||||
"description": "OK"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -498,7 +495,7 @@ const docTemplate = `{
|
|||
],
|
||||
"responses": {
|
||||
"204": {
|
||||
"description": ""
|
||||
"description": "No Content"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -662,7 +659,7 @@ const docTemplate = `{
|
|||
],
|
||||
"responses": {
|
||||
"204": {
|
||||
"description": ""
|
||||
"description": "No Content"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -826,7 +823,7 @@ const docTemplate = `{
|
|||
],
|
||||
"responses": {
|
||||
"204": {
|
||||
"description": ""
|
||||
"description": "No Content"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -874,7 +871,7 @@ const docTemplate = `{
|
|||
],
|
||||
"responses": {
|
||||
"204": {
|
||||
"description": ""
|
||||
"description": "No Content"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -931,7 +928,7 @@ const docTemplate = `{
|
|||
"summary": "User Logout",
|
||||
"responses": {
|
||||
"204": {
|
||||
"description": ""
|
||||
"description": "No Content"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -950,7 +947,7 @@ const docTemplate = `{
|
|||
"summary": "User Token Refresh",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": ""
|
||||
"description": "OK"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -977,7 +974,7 @@ const docTemplate = `{
|
|||
],
|
||||
"responses": {
|
||||
"204": {
|
||||
"description": ""
|
||||
"description": "No Content"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1077,7 +1074,7 @@ const docTemplate = `{
|
|||
"summary": "Deletes the user account",
|
||||
"responses": {
|
||||
"204": {
|
||||
"description": ""
|
||||
"description": "No Content"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1098,7 +1095,7 @@ const docTemplate = `{
|
|||
"summary": "Update the current user's password // TODO:",
|
||||
"responses": {
|
||||
"204": {
|
||||
"description": ""
|
||||
"description": "No Content"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1516,6 +1513,26 @@ const docTemplate = `{
|
|||
}
|
||||
}
|
||||
},
|
||||
"repo.PaginationResult-repo_ItemSummary": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"items": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/repo.ItemSummary"
|
||||
}
|
||||
},
|
||||
"page": {
|
||||
"type": "integer"
|
||||
},
|
||||
"pageSize": {
|
||||
"type": "integer"
|
||||
},
|
||||
"total": {
|
||||
"type": "integer"
|
||||
}
|
||||
}
|
||||
},
|
||||
"repo.UserOut": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
|
@ -1569,9 +1586,7 @@ const docTemplate = `{
|
|||
"server.Results": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"items": {
|
||||
"type": "any"
|
||||
}
|
||||
"items": {}
|
||||
}
|
||||
},
|
||||
"server.ValidationError": {
|
||||
|
|
|
@ -69,6 +69,18 @@
|
|||
"name": "q",
|
||||
"in": "query"
|
||||
},
|
||||
{
|
||||
"type": "integer",
|
||||
"description": "page number",
|
||||
"name": "page",
|
||||
"in": "query"
|
||||
},
|
||||
{
|
||||
"type": "integer",
|
||||
"description": "items per page",
|
||||
"name": "pageSize",
|
||||
"in": "query"
|
||||
},
|
||||
{
|
||||
"type": "array",
|
||||
"items": {
|
||||
|
@ -94,22 +106,7 @@
|
|||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/server.Results"
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"items": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/repo.ItemSummary"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
"$ref": "#/definitions/repo.PaginationResult-repo_ItemSummary"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -173,7 +170,7 @@
|
|||
],
|
||||
"responses": {
|
||||
"204": {
|
||||
"description": ""
|
||||
"description": "No Content"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -274,7 +271,7 @@
|
|||
],
|
||||
"responses": {
|
||||
"204": {
|
||||
"description": ""
|
||||
"description": "No Content"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -374,7 +371,7 @@
|
|||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": ""
|
||||
"description": "OK"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -490,7 +487,7 @@
|
|||
],
|
||||
"responses": {
|
||||
"204": {
|
||||
"description": ""
|
||||
"description": "No Content"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -654,7 +651,7 @@
|
|||
],
|
||||
"responses": {
|
||||
"204": {
|
||||
"description": ""
|
||||
"description": "No Content"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -818,7 +815,7 @@
|
|||
],
|
||||
"responses": {
|
||||
"204": {
|
||||
"description": ""
|
||||
"description": "No Content"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -866,7 +863,7 @@
|
|||
],
|
||||
"responses": {
|
||||
"204": {
|
||||
"description": ""
|
||||
"description": "No Content"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -923,7 +920,7 @@
|
|||
"summary": "User Logout",
|
||||
"responses": {
|
||||
"204": {
|
||||
"description": ""
|
||||
"description": "No Content"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -942,7 +939,7 @@
|
|||
"summary": "User Token Refresh",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": ""
|
||||
"description": "OK"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -969,7 +966,7 @@
|
|||
],
|
||||
"responses": {
|
||||
"204": {
|
||||
"description": ""
|
||||
"description": "No Content"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1069,7 +1066,7 @@
|
|||
"summary": "Deletes the user account",
|
||||
"responses": {
|
||||
"204": {
|
||||
"description": ""
|
||||
"description": "No Content"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1090,7 +1087,7 @@
|
|||
"summary": "Update the current user's password // TODO:",
|
||||
"responses": {
|
||||
"204": {
|
||||
"description": ""
|
||||
"description": "No Content"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1508,6 +1505,26 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"repo.PaginationResult-repo_ItemSummary": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"items": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/repo.ItemSummary"
|
||||
}
|
||||
},
|
||||
"page": {
|
||||
"type": "integer"
|
||||
},
|
||||
"pageSize": {
|
||||
"type": "integer"
|
||||
},
|
||||
"total": {
|
||||
"type": "integer"
|
||||
}
|
||||
}
|
||||
},
|
||||
"repo.UserOut": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
|
@ -1561,9 +1578,7 @@
|
|||
"server.Results": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"items": {
|
||||
"type": "any"
|
||||
}
|
||||
"items": {}
|
||||
}
|
||||
},
|
||||
"server.ValidationError": {
|
||||
|
|
|
@ -275,6 +275,19 @@ definitions:
|
|||
updatedAt:
|
||||
type: string
|
||||
type: object
|
||||
repo.PaginationResult-repo_ItemSummary:
|
||||
properties:
|
||||
items:
|
||||
items:
|
||||
$ref: '#/definitions/repo.ItemSummary'
|
||||
type: array
|
||||
page:
|
||||
type: integer
|
||||
pageSize:
|
||||
type: integer
|
||||
total:
|
||||
type: integer
|
||||
type: object
|
||||
repo.UserOut:
|
||||
properties:
|
||||
email:
|
||||
|
@ -310,8 +323,7 @@ definitions:
|
|||
type: object
|
||||
server.Results:
|
||||
properties:
|
||||
items:
|
||||
type: any
|
||||
items: {}
|
||||
type: object
|
||||
server.ValidationError:
|
||||
properties:
|
||||
|
@ -431,6 +443,14 @@ paths:
|
|||
in: query
|
||||
name: q
|
||||
type: string
|
||||
- description: page number
|
||||
in: query
|
||||
name: page
|
||||
type: integer
|
||||
- description: items per page
|
||||
in: query
|
||||
name: pageSize
|
||||
type: integer
|
||||
- collectionFormat: multi
|
||||
description: label Ids
|
||||
in: query
|
||||
|
@ -451,14 +471,7 @@ paths:
|
|||
"200":
|
||||
description: OK
|
||||
schema:
|
||||
allOf:
|
||||
- $ref: '#/definitions/server.Results'
|
||||
- properties:
|
||||
items:
|
||||
items:
|
||||
$ref: '#/definitions/repo.ItemSummary'
|
||||
type: array
|
||||
type: object
|
||||
$ref: '#/definitions/repo.PaginationResult-repo_ItemSummary'
|
||||
security:
|
||||
- Bearer: []
|
||||
summary: Get All Items
|
||||
|
@ -496,7 +509,7 @@ paths:
|
|||
- application/json
|
||||
responses:
|
||||
"204":
|
||||
description: ""
|
||||
description: No Content
|
||||
security:
|
||||
- Bearer: []
|
||||
summary: deletes a item
|
||||
|
@ -602,7 +615,7 @@ paths:
|
|||
type: string
|
||||
responses:
|
||||
"204":
|
||||
description: ""
|
||||
description: No Content
|
||||
security:
|
||||
- Bearer: []
|
||||
summary: retrieves an attachment for an item
|
||||
|
@ -677,7 +690,7 @@ paths:
|
|||
- application/octet-stream
|
||||
responses:
|
||||
"200":
|
||||
description: ""
|
||||
description: OK
|
||||
security:
|
||||
- Bearer: []
|
||||
summary: retrieves an attachment for an item
|
||||
|
@ -695,7 +708,7 @@ paths:
|
|||
- application/json
|
||||
responses:
|
||||
"204":
|
||||
description: ""
|
||||
description: No Content
|
||||
security:
|
||||
- Bearer: []
|
||||
summary: imports items into the database
|
||||
|
@ -754,7 +767,7 @@ paths:
|
|||
- application/json
|
||||
responses:
|
||||
"204":
|
||||
description: ""
|
||||
description: No Content
|
||||
security:
|
||||
- Bearer: []
|
||||
summary: deletes a label
|
||||
|
@ -851,7 +864,7 @@ paths:
|
|||
- application/json
|
||||
responses:
|
||||
"204":
|
||||
description: ""
|
||||
description: No Content
|
||||
security:
|
||||
- Bearer: []
|
||||
summary: deletes a location
|
||||
|
@ -918,7 +931,7 @@ paths:
|
|||
$ref: '#/definitions/v1.ChangePassword'
|
||||
responses:
|
||||
"204":
|
||||
description: ""
|
||||
description: No Content
|
||||
security:
|
||||
- Bearer: []
|
||||
summary: Updates the users password
|
||||
|
@ -954,7 +967,7 @@ paths:
|
|||
post:
|
||||
responses:
|
||||
"204":
|
||||
description: ""
|
||||
description: No Content
|
||||
security:
|
||||
- Bearer: []
|
||||
summary: User Logout
|
||||
|
@ -967,7 +980,7 @@ paths:
|
|||
This does not validate that the user still exists within the database.
|
||||
responses:
|
||||
"200":
|
||||
description: ""
|
||||
description: OK
|
||||
security:
|
||||
- Bearer: []
|
||||
summary: User Token Refresh
|
||||
|
@ -986,7 +999,7 @@ paths:
|
|||
- application/json
|
||||
responses:
|
||||
"204":
|
||||
description: ""
|
||||
description: No Content
|
||||
summary: Get the current user
|
||||
tags:
|
||||
- User
|
||||
|
@ -996,7 +1009,7 @@ paths:
|
|||
- application/json
|
||||
responses:
|
||||
"204":
|
||||
description: ""
|
||||
description: No Content
|
||||
security:
|
||||
- Bearer: []
|
||||
summary: Deletes the user account
|
||||
|
@ -1051,7 +1064,7 @@ paths:
|
|||
- application/json
|
||||
responses:
|
||||
"204":
|
||||
description: ""
|
||||
description: No Content
|
||||
security:
|
||||
- Bearer: []
|
||||
summary: 'Update the current user''s password // TODO:'
|
||||
|
|
|
@ -4,6 +4,7 @@ import (
|
|||
"encoding/csv"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strconv"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"github.com/hay-kot/homebox/backend/internal/repo"
|
||||
|
@ -24,10 +25,23 @@ func uuidList(params url.Values, key string) []uuid.UUID {
|
|||
return ids
|
||||
}
|
||||
|
||||
func intOrNegativeOne(s string) int {
|
||||
i, err := strconv.Atoi(s)
|
||||
if err != nil {
|
||||
return -1
|
||||
}
|
||||
return i
|
||||
}
|
||||
|
||||
func extractQuery(r *http.Request) repo.ItemQuery {
|
||||
params := r.URL.Query()
|
||||
|
||||
page := intOrNegativeOne(params.Get("page"))
|
||||
perPage := intOrNegativeOne(params.Get("perPage"))
|
||||
|
||||
return repo.ItemQuery{
|
||||
Page: page,
|
||||
PageSize: perPage,
|
||||
Search: params.Get("q"),
|
||||
LocationIDs: uuidList(params, "locations"),
|
||||
LabelIDs: uuidList(params, "labels"),
|
||||
|
@ -39,9 +53,11 @@ func extractQuery(r *http.Request) repo.ItemQuery {
|
|||
// @Tags Items
|
||||
// @Produce json
|
||||
// @Param q query string false "search string"
|
||||
// @Param page query int false "page number"
|
||||
// @Param pageSize query int false "items per page"
|
||||
// @Param labels query []string false "label Ids" collectionFormat(multi)
|
||||
// @Param locations query []string false "location Ids" collectionFormat(multi)
|
||||
// @Success 200 {object} server.Results{items=[]repo.ItemSummary}
|
||||
// @Success 200 {object} repo.PaginationResult[repo.ItemSummary]{}
|
||||
// @Router /v1/items [GET]
|
||||
// @Security Bearer
|
||||
func (ctrl *V1Controller) HandleItemsGetAll() http.HandlerFunc {
|
||||
|
@ -53,7 +69,7 @@ func (ctrl *V1Controller) HandleItemsGetAll() http.HandlerFunc {
|
|||
server.RespondServerError(w)
|
||||
return
|
||||
}
|
||||
server.Respond(w, http.StatusOK, server.Results{Items: items})
|
||||
server.Respond(w, http.StatusOK, items)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
12
backend/internal/repo/pagination.go
Normal file
12
backend/internal/repo/pagination.go
Normal file
|
@ -0,0 +1,12 @@
|
|||
package repo
|
||||
|
||||
type PaginationResult[T any] struct {
|
||||
Page int `json:"page"`
|
||||
PageSize int `json:"pageSize"`
|
||||
Total int `json:"total"`
|
||||
Items []T `json:"items"`
|
||||
}
|
||||
|
||||
func calculateOffset(page, pageSize int) int {
|
||||
return (page - 1) * pageSize
|
||||
}
|
|
@ -19,6 +19,8 @@ type ItemsRepository struct {
|
|||
|
||||
type (
|
||||
ItemQuery struct {
|
||||
Page int
|
||||
PageSize int
|
||||
Search string `json:"search"`
|
||||
LocationIDs []uuid.UUID `json:"locationIds"`
|
||||
LabelIDs []uuid.UUID `json:"labelIds"`
|
||||
|
@ -215,7 +217,7 @@ func (e *ItemsRepository) GetOneByGroup(ctx context.Context, gid, id uuid.UUID)
|
|||
}
|
||||
|
||||
// QueryByGroup returns a list of items that belong to a specific group based on the provided query.
|
||||
func (e *ItemsRepository) QueryByGroup(ctx context.Context, gid uuid.UUID, q ItemQuery) ([]ItemSummary, error) {
|
||||
func (e *ItemsRepository) QueryByGroup(ctx context.Context, gid uuid.UUID, q ItemQuery) (PaginationResult[ItemSummary], error) {
|
||||
qb := e.db.Item.Query().Where(item.HasGroupWith(group.ID(gid)))
|
||||
|
||||
if len(q.LabelIDs) > 0 {
|
||||
|
@ -243,9 +245,33 @@ func (e *ItemsRepository) QueryByGroup(ctx context.Context, gid uuid.UUID, q Ite
|
|||
)
|
||||
}
|
||||
|
||||
return mapItemsSummaryErr(qb.WithLabel().
|
||||
if q.Page != -1 || q.PageSize != -1 {
|
||||
qb = qb.
|
||||
Offset(calculateOffset(q.Page, q.PageSize)).
|
||||
Limit(q.PageSize)
|
||||
}
|
||||
|
||||
items, err := mapItemsSummaryErr(
|
||||
qb.WithLabel().
|
||||
WithLocation().
|
||||
All(ctx))
|
||||
All(ctx),
|
||||
)
|
||||
if err != nil {
|
||||
return PaginationResult[ItemSummary]{}, err
|
||||
}
|
||||
|
||||
count, err := qb.Count(ctx)
|
||||
if err != nil {
|
||||
return PaginationResult[ItemSummary]{}, err
|
||||
}
|
||||
|
||||
return PaginationResult[ItemSummary]{
|
||||
Page: q.Page,
|
||||
PageSize: q.PageSize,
|
||||
Total: count,
|
||||
Items: items,
|
||||
}, nil
|
||||
|
||||
}
|
||||
|
||||
// GetAll returns all the items in the database with the Labels and Locations eager loaded.
|
||||
|
|
|
@ -28,7 +28,7 @@ func (svc *ItemService) GetOne(ctx context.Context, gid uuid.UUID, id uuid.UUID)
|
|||
return svc.repo.Items.GetOneByGroup(ctx, gid, id)
|
||||
}
|
||||
|
||||
func (svc *ItemService) Query(ctx Context, q repo.ItemQuery) ([]repo.ItemSummary, error) {
|
||||
func (svc *ItemService) Query(ctx Context, q repo.ItemQuery) (repo.PaginationResult[repo.ItemSummary], error) {
|
||||
return svc.repo.Items.QueryByGroup(ctx, ctx.GID, q)
|
||||
}
|
||||
|
||||
|
|
|
@ -8,9 +8,11 @@ import {
|
|||
ItemSummary,
|
||||
ItemUpdate,
|
||||
} from "../types/data-contracts";
|
||||
import { AttachmentTypes, Results } from "../types/non-generated";
|
||||
import { AttachmentTypes, PaginationResult } from "../types/non-generated";
|
||||
|
||||
export type ItemsQuery = {
|
||||
page?: number;
|
||||
pageSize?: number;
|
||||
locations?: string[];
|
||||
labels?: string[];
|
||||
q?: string;
|
||||
|
@ -18,7 +20,7 @@ export type ItemsQuery = {
|
|||
|
||||
export class ItemsApi extends BaseAPI {
|
||||
getAll(q: ItemsQuery = {}) {
|
||||
return this.http.get<Results<ItemSummary>>({ url: route("/items", q) });
|
||||
return this.http.get<PaginationResult<ItemSummary>>({ url: route("/items", q) });
|
||||
}
|
||||
|
||||
create(item: ItemCreate) {
|
||||
|
|
|
@ -187,6 +187,7 @@ export interface LocationSummary {
|
|||
updatedAt: Date;
|
||||
}
|
||||
|
||||
|
||||
export interface UserOut {
|
||||
email: string;
|
||||
groupId: string;
|
||||
|
|
|
@ -12,3 +12,10 @@ export type Result<T> = {
|
|||
export type Results<T> = {
|
||||
items: T[];
|
||||
};
|
||||
|
||||
export interface PaginationResult<T> {
|
||||
items: T[];
|
||||
page: number;
|
||||
pageSize: number;
|
||||
total: number;
|
||||
}
|
||||
|
|
|
@ -22,6 +22,7 @@ def date_types(*names: list[str]) -> dict[re.Pattern, str]:
|
|||
|
||||
|
||||
regex_replace: dict[re.Pattern, str] = {
|
||||
re.compile(r" PaginationResultRepo"): "PaginationResult",
|
||||
re.compile(r" Repo"): " ",
|
||||
re.compile(r" Services"): " ",
|
||||
re.compile(r" V1"): " ",
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue