From 3f3f7d5e7733b69500b79d3464805fca774d9907 Mon Sep 17 00:00:00 2001 From: Bradley Nelson Date: Thu, 5 Jan 2023 21:52:15 +0000 Subject: [PATCH] suggested changes from PR --- .gitignore | 4 +- .vscode/settings.json | 2 +- backend/app/api/handlers/assetIds/redirect.go | 45 -------------- backend/app/api/handlers/v1/v1_ctrl_assets.go | 61 +++++++++++++++++++ backend/app/api/routes.go | 5 +- backend/go.sum | 4 ++ backend/internal/data/repo/repo_items.go | 39 ++++++++++-- backend/internal/data/repo/repo_items_test.go | 30 --------- frontend/lib/api/classes/assets.ts | 11 ++++ frontend/lib/api/classes/items.ts | 2 +- frontend/lib/api/user.ts | 3 + frontend/pages/a/[id].vue | 42 +++++++++++++ 12 files changed, 162 insertions(+), 86 deletions(-) delete mode 100644 backend/app/api/handlers/assetIds/redirect.go create mode 100644 backend/app/api/handlers/v1/v1_ctrl_assets.go create mode 100644 frontend/lib/api/classes/assets.ts create mode 100644 frontend/pages/a/[id].vue diff --git a/.gitignore b/.gitignore index 3eda301..05e4ebd 100644 --- a/.gitignore +++ b/.gitignore @@ -47,5 +47,5 @@ node_modules dist .pnpm-store -devData -backend/app/api/app \ No newline at end of file +backend/app/api/app +backend/app/api/__debug_bin \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json index 3c39b0e..5080f25 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -12,7 +12,7 @@ "debughandlers" ], // use ESLint to format code on save - "editor.formatOnSave": true, + "editor.formatOnSave": false, "editor.defaultFormatter": "dbaeumer.vscode-eslint", "editor.codeActionsOnSave": { "source.fixAll.eslint": true diff --git a/backend/app/api/handlers/assetIds/redirect.go b/backend/app/api/handlers/assetIds/redirect.go deleted file mode 100644 index 41d8991..0000000 --- a/backend/app/api/handlers/assetIds/redirect.go +++ /dev/null @@ -1,45 +0,0 @@ -package assetIds - -import ( - "net/http" - "strconv" - "strings" - - "github.com/rs/zerolog/log" - - "github.com/go-chi/chi/v5" - "github.com/hay-kot/homebox/backend/internal/data/repo" - "github.com/hay-kot/homebox/backend/pkgs/server" -) - -func HandleAssetRedirect(repos *repo.AllRepos) server.HandlerFunc { - return func(w http.ResponseWriter, r *http.Request) error { - // Get the asset ID from the URL - assetIdParam := chi.URLParam(r, "id") - assetIdParam = strings.ReplaceAll(assetIdParam, "-", "") // Remove dashes - // Convert the asset ID to an int64 - assetId, err := strconv.ParseInt(assetIdParam, 10, 64) - if err != nil { - return err - } - - // Get the asset from the database - itemIds, err := repos.Items.GetIDsByAssetID(r.Context(), repo.AssetID(assetId)); - if err != nil { - return err - } - // check if we got more than one item - if len(itemIds) > 1 { - log.Err(err).Msg("More than one item found for asset ID") - return server.Respond(w, http.StatusInternalServerError, "More than one item found for asset ID") - } - // check if we got any items - if len(itemIds) == 0 { - log.Err(err).Msg("No items found for asset ID") - return server.Respond(w, http.StatusNotFound, "No items found for asset ID") - } - - http.Redirect(w, r, "/item/" + itemIds[0].String(), http.StatusSeeOther) - return nil - } -} \ No newline at end of file diff --git a/backend/app/api/handlers/v1/v1_ctrl_assets.go b/backend/app/api/handlers/v1/v1_ctrl_assets.go new file mode 100644 index 0000000..82653fe --- /dev/null +++ b/backend/app/api/handlers/v1/v1_ctrl_assets.go @@ -0,0 +1,61 @@ +package v1 + +import ( + "net/http" + "strconv" + "strings" + + "github.com/go-chi/chi/v5" + "github.com/hay-kot/homebox/backend/internal/core/services" + "github.com/hay-kot/homebox/backend/internal/data/repo" + "github.com/hay-kot/homebox/backend/internal/sys/validate" + "github.com/hay-kot/homebox/backend/pkgs/server" + + "github.com/rs/zerolog/log" +) + +// HandleItemGet godocs +// @Summary Gets an item by Asset ID +// @Tags Assets +// @Produce json +// @Param id path string true "Asset ID" +// @Success 200 {object} repo.PaginationResult[repo.ItemSummary]{} +// @Router /v1/assets/{id} [GET] +// @Security Bearer +func (ctrl *V1Controller) HandleAssetGet() server.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) error { + ctx := services.NewContext(r.Context()) + assetIdParam := chi.URLParam(r, "id") + assetIdParam = strings.ReplaceAll(assetIdParam, "-", "") // Remove dashes + // Convert the asset ID to an int64 + assetId, err := strconv.ParseInt(assetIdParam, 10, 64) + if err != nil { + return err + } + pageParam := r.URL.Query().Get("page") + var page int64 = -1 + if pageParam != "" { + page, err = strconv.ParseInt(pageParam, 10, 64) + if err != nil { + return server.Respond(w, http.StatusBadRequest, "Invalid page number") + } + } + + pageSizeParam := r.URL.Query().Get("pageSize") + var pageSize int64 = -1 + if pageSizeParam != "" { + pageSize, err = strconv.ParseInt(pageSizeParam, 10, 64) + if err != nil { + return server.Respond(w, http.StatusBadRequest, "Invalid page size") + } + } + + + items, err := ctrl.repo.Items.QueryByAssetID(r.Context(), ctx.GID, repo.AssetID(assetId), int(page), int(pageSize)) + if err != nil { + log.Err(err).Msg("failed to get item") + return validate.NewRequestError(err, http.StatusInternalServerError) + } + return server.Respond(w, http.StatusOK, items) + } +} diff --git a/backend/app/api/routes.go b/backend/app/api/routes.go index 74a9e75..9c71d0f 100644 --- a/backend/app/api/routes.go +++ b/backend/app/api/routes.go @@ -10,7 +10,6 @@ import ( "path" "path/filepath" - "github.com/hay-kot/homebox/backend/app/api/handlers/assetIds" "github.com/hay-kot/homebox/backend/app/api/handlers/debughandlers" v1 "github.com/hay-kot/homebox/backend/app/api/handlers/v1" _ "github.com/hay-kot/homebox/backend/app/api/static/docs" @@ -118,14 +117,14 @@ func (a *app) mountRoutes(repos *repo.AllRepos) { a.server.Put(v1Base("/items/{id}/maintenance/{entry_id}"), v1Ctrl.HandleMaintenanceEntryUpdate(), userMW...) a.server.Delete(v1Base("/items/{id}/maintenance/{entry_id}"), v1Ctrl.HandleMaintenanceEntryDelete(), userMW...) + a.server.Get(v1Base("/asset/{id}"), v1Ctrl.HandleAssetGet(), userMW...) + a.server.Get( v1Base("/items/{id}/attachments/{attachment_id}"), v1Ctrl.HandleItemAttachmentGet(), a.mwAuthToken, a.mwRoles(RoleModeOr, authroles.RoleUser.String(), authroles.RoleAttachments.String()), ) - a.server.Get("/a/{id}", assetIds.HandleAssetRedirect(a.repos)) - a.server.NotFound(notFoundHandler()) } diff --git a/backend/go.sum b/backend/go.sum index a5ad319..72aaa74 100644 --- a/backend/go.sum +++ b/backend/go.sum @@ -72,11 +72,13 @@ github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovk github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= github.com/mattn/go-isatty v0.0.16 h1:bq3VjFmv/sOjHtdEhmkEV4x1AJtvUvOJ2PFAZ5+peKQ= github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= +github.com/mattn/go-runewidth v0.0.9 h1:Lm995f3rfxdpd6TSmuVCHVb/QhupuXlYr8sCI/QdE+0= github.com/mattn/go-sqlite3 v1.14.16 h1:yOQRA0RpS5PFz/oikGwBEqvAWhWg5ufRz4ETLjwpU1Y= github.com/mattn/go-sqlite3 v1.14.16/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg= github.com/mitchellh/go-wordwrap v1.0.1 h1:TLuKupo69TCn6TQSyGxwI1EblZZEsQ0vMlAFQflz0v0= github.com/mitchellh/go-wordwrap v1.0.1/go.mod h1:R62XHJLzvMFRBbcrT7m7WgmE1eOyTSsCt+hzestvNj0= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= +github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec= github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= @@ -88,6 +90,8 @@ github.com/rs/xid v1.4.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= github.com/rs/zerolog v1.28.0 h1:MirSo27VyNi7RJYP3078AA1+Cyzd2GB66qy3aUHvsWY= github.com/rs/zerolog v1.28.0/go.mod h1:NILgTygv/Uej1ra5XxGf82ZFSLk58MFGAUS2o6usyD0= github.com/sergi/go-diff v1.0.0 h1:Kpca3qRNrduNnOQeazBd0ysaKrUJiIuISHxogkT9RPQ= +github.com/spf13/cobra v1.6.1 h1:o94oiPyS4KD1mPy2fmcYYHHfCxLqYjJOhGsCHFZtEzA= +github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= diff --git a/backend/internal/data/repo/repo_items.go b/backend/internal/data/repo/repo_items.go index 69535c0..071f4f0 100644 --- a/backend/internal/data/repo/repo_items.go +++ b/backend/internal/data/repo/repo_items.go @@ -283,10 +283,6 @@ func (e *ItemsRepository) GetOneByGroup(ctx context.Context, gid, id uuid.UUID) return e.getOne(ctx, item.ID(id), item.HasGroupWith(group.ID(gid))) } -func (e *ItemsRepository) GetIDsByAssetID(ctx context.Context, assetID AssetID) ([]uuid.UUID, error) { - return e.db.Item.Query().Where(item.AssetID(int(assetID))).Order(ent.Desc(item.FieldCreatedAt)).IDs(ctx) -} - // 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) (PaginationResult[ItemSummary], error) { qb := e.db.Item.Query().Where( @@ -360,6 +356,41 @@ func (e *ItemsRepository) QueryByGroup(ctx context.Context, gid uuid.UUID, q Ite } + +// QueryByAssetID returns items by asset ID. If the item does not exist, an error is returned. +func (e *ItemsRepository) QueryByAssetID(ctx context.Context, gid uuid.UUID, assetID AssetID, page int, pageSize int) (PaginationResult[ItemSummary], error) { + qb := e.db.Item.Query().Where( + item.HasGroupWith(group.ID(gid)), + item.AssetID(int(assetID)), + ) + + if page != -1 || pageSize != -1 { + qb.Offset(calculateOffset(page, pageSize)). + Limit(pageSize) + } else { + page = -1 + pageSize = -1 + } + + items, err := mapItemsSummaryErr( + qb.Order(ent.Asc(item.FieldName)). + WithLabel(). + WithLocation(). + All(ctx), + ) + + if err != nil { + return PaginationResult[ItemSummary]{}, err + } + + return PaginationResult[ItemSummary]{ + Page: page, + PageSize: pageSize, + Total: len(items), + Items: items, + }, nil +} + // GetAll returns all the items in the database with the Labels and Locations eager loaded. func (e *ItemsRepository) GetAll(ctx context.Context, gid uuid.UUID) ([]ItemSummary, error) { return mapItemsSummaryErr(e.db.Item.Query(). diff --git a/backend/internal/data/repo/repo_items_test.go b/backend/internal/data/repo/repo_items_test.go index faacc18..4b958b0 100644 --- a/backend/internal/data/repo/repo_items_test.go +++ b/backend/internal/data/repo/repo_items_test.go @@ -107,36 +107,6 @@ func TestItemsRepository_GetAll(t *testing.T) { } } -func TestItemsRepository_GetIDsByAssetID(t *testing.T) { - useItems(t, 4) - - items, err := tRepos.Items.GetIDsByAssetID(context.Background(), 0) - - assert.NoError(t, err) - assert.Equal(t, 4, len(items), "Two items are returned when there are many with the same asset Id") - - item1 := useItems(t, 1)[0] - - item1Update := ItemUpdate{ - ID: item1.ID, - AssetID: 1, - Name: "note-important", - Description: "This is a note", - LocationID: item1.Location.ID, - } - - _, err = tRepos.Items.UpdateByGroup(context.Background(), tGroup.ID, item1Update) - - assert.NoError(t, err) - - items, err = tRepos.Items.GetIDsByAssetID(context.Background(), 1) - - assert.NoError(t, err) - assert.Equal(t, 1, len(items), "One item is returned when there is only one with the same asset Id") - assert.Equal(t, item1.ID, items[0], "The item returned is the one with the same asset Id") -} - - func TestItemsRepository_Create(t *testing.T) { location, err := tRepos.Locations.Create(context.Background(), tGroup.ID, locationFactory()) assert.NoError(t, err) diff --git a/frontend/lib/api/classes/assets.ts b/frontend/lib/api/classes/assets.ts new file mode 100644 index 0000000..8820bb3 --- /dev/null +++ b/frontend/lib/api/classes/assets.ts @@ -0,0 +1,11 @@ +import { BaseAPI, route } from "../base"; +import { ItemSummary } from "../types/data-contracts"; +import { PaginationResult } from "../types/non-generated"; + +export class AssetsApi extends BaseAPI { + async get(id: string, page = 1, pageSize = 50) { + return await this.http.get>({ + url: route(`/asset/${id}`, { page, pageSize }), + }); + } +} diff --git a/frontend/lib/api/classes/items.ts b/frontend/lib/api/classes/items.ts index 8522852..14cc491 100644 --- a/frontend/lib/api/classes/items.ts +++ b/frontend/lib/api/classes/items.ts @@ -76,7 +76,7 @@ export class ItemsApi extends BaseAPI { attachments: AttachmentsAPI; maintenance: MaintenanceAPI; - constructor(http: Requests, token: string) { + constructor(http: Requests, token = "") { super(http, token); this.attachments = new AttachmentsAPI(http); this.maintenance = new MaintenanceAPI(http); diff --git a/frontend/lib/api/user.ts b/frontend/lib/api/user.ts index 079f113..a64ab98 100644 --- a/frontend/lib/api/user.ts +++ b/frontend/lib/api/user.ts @@ -6,6 +6,7 @@ import { GroupApi } from "./classes/group"; import { UserApi } from "./classes/users"; import { ActionsAPI } from "./classes/actions"; import { StatsAPI } from "./classes/stats"; +import { AssetsApi } from "./classes/assets"; import { Requests } from "~~/lib/requests"; export class UserClient extends BaseAPI { @@ -16,6 +17,7 @@ export class UserClient extends BaseAPI { user: UserApi; actions: ActionsAPI; stats: StatsAPI; + assets: AssetsApi; constructor(requests: Requests, attachmentToken: string) { super(requests, attachmentToken); @@ -27,6 +29,7 @@ export class UserClient extends BaseAPI { this.user = new UserApi(requests); this.actions = new ActionsAPI(requests); this.stats = new StatsAPI(requests); + this.assets = new AssetsApi(requests); Object.freeze(this); } diff --git a/frontend/pages/a/[id].vue b/frontend/pages/a/[id].vue new file mode 100644 index 0000000..28d65d1 --- /dev/null +++ b/frontend/pages/a/[id].vue @@ -0,0 +1,42 @@ + + +