suggested changes from PR

This commit is contained in:
Bradley Nelson 2023-01-05 21:52:15 +00:00
parent 82a63d1ed3
commit 3f3f7d5e77
No known key found for this signature in database
GPG key ID: EB209B0420B6230F
12 changed files with 162 additions and 86 deletions

2
.gitignore vendored
View file

@ -47,5 +47,5 @@ node_modules
dist
.pnpm-store
devData
backend/app/api/app
backend/app/api/__debug_bin

View file

@ -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

View file

@ -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
}
}

View file

@ -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)
}
}

View file

@ -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())
}

View file

@ -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=

View file

@ -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().

View file

@ -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)

View file

@ -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<PaginationResult<ItemSummary>>({
url: route(`/asset/${id}`, { page, pageSize }),
});
}
}

View file

@ -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);

View file

@ -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);
}

42
frontend/pages/a/[id].vue Normal file
View file

@ -0,0 +1,42 @@
<script setup lang="ts">
definePageMeta({
middleware: ["auth"],
});
const route = useRoute();
const api = useUserApi();
const toast = useNotifier();
const assetId = computed<string>(() => route.params.id as string);
const { pending, data: items } = useLazyAsyncData(`asset/${assetId.value}`, async () => {
const { data, error } = await api.assets.get(assetId.value);
if (error) {
toast.error("Failed to load asset");
navigateTo("/home");
return;
}
switch (data.total) {
case 0:
toast.error("Asset not found");
navigateTo("/home");
break;
case 1:
navigateTo(`/item/${data.items[0].id}`);
break;
default:
return data.items;
}
});
</script>
<template>
<BaseContainer>
<section v-if="!pending">
<BaseSectionHeader class="mb-5"> This Asset Id is associated with multiple items</BaseSectionHeader>
<div class="grid gap-2 grid-cols-1 sm:grid-cols-2">
<ItemCard v-for="item in items" :key="item.id" :item="item" />
</div>
</section>
</BaseContainer>
</template>