From 8e1947d9712e23ba92a67bb613fd5b5e892367fe Mon Sep 17 00:00:00 2001 From: Hayden <64056131+hay-kot@users.noreply.github.com> Date: Wed, 2 Nov 2022 11:54:43 -0800 Subject: [PATCH 001/350] fix: conditionally filter parent locations (#133) --- backend/app/api/handlers/v1/query_params.go | 36 +++++++++++++++++ backend/app/api/handlers/v1/v1_ctrl_items.go | 40 +++---------------- .../app/api/handlers/v1/v1_ctrl_locations.go | 10 ++++- backend/app/api/static/docs/docs.go | 8 ++++ backend/app/api/static/docs/swagger.json | 8 ++++ backend/app/api/static/docs/swagger.yaml | 5 +++ .../internal/core/services/service_items.go | 16 ++++---- .../core/services/service_items_test.go | 3 +- backend/internal/data/repo/repo_locations.go | 18 +++++++-- .../internal/data/repo/repo_locations_test.go | 2 +- frontend/components/Item/CreateModal.vue | 2 +- frontend/layouts/default.vue | 6 ++- frontend/lib/api/classes/locations.ts | 8 +++- frontend/pages/home.vue | 2 +- frontend/pages/item/[id]/edit.vue | 2 +- frontend/pages/items.vue | 2 +- frontend/pages/location/[id].vue | 2 +- frontend/stores/locations.ts | 32 +++++++++++---- 18 files changed, 135 insertions(+), 67 deletions(-) create mode 100644 backend/app/api/handlers/v1/query_params.go diff --git a/backend/app/api/handlers/v1/query_params.go b/backend/app/api/handlers/v1/query_params.go new file mode 100644 index 0000000..0ac84d8 --- /dev/null +++ b/backend/app/api/handlers/v1/query_params.go @@ -0,0 +1,36 @@ +package v1 + +import ( + "net/url" + "strconv" + + "github.com/google/uuid" +) + +func queryUUIDList(params url.Values, key string) []uuid.UUID { + var ids []uuid.UUID + for _, id := range params[key] { + uid, err := uuid.Parse(id) + if err != nil { + continue + } + ids = append(ids, uid) + } + return ids +} + +func queryIntOrNegativeOne(s string) int { + i, err := strconv.Atoi(s) + if err != nil { + return -1 + } + return i +} + +func queryBool(s string) bool { + b, err := strconv.ParseBool(s) + if err != nil { + return false + } + return b +} diff --git a/backend/app/api/handlers/v1/v1_ctrl_items.go b/backend/app/api/handlers/v1/v1_ctrl_items.go index 732600f..f4d43d3 100644 --- a/backend/app/api/handlers/v1/v1_ctrl_items.go +++ b/backend/app/api/handlers/v1/v1_ctrl_items.go @@ -3,10 +3,7 @@ package v1 import ( "encoding/csv" "net/http" - "net/url" - "strconv" - "github.com/google/uuid" "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" @@ -27,44 +24,17 @@ import ( // @Router /v1/items [GET] // @Security Bearer func (ctrl *V1Controller) HandleItemsGetAll() server.HandlerFunc { - uuidList := func(params url.Values, key string) []uuid.UUID { - var ids []uuid.UUID - for _, id := range params[key] { - uid, err := uuid.Parse(id) - if err != nil { - continue - } - ids = append(ids, uid) - } - return ids - } - - intOrNegativeOne := func(s string) int { - i, err := strconv.Atoi(s) - if err != nil { - return -1 - } - return i - } - - getBool := func(s string) bool { - b, err := strconv.ParseBool(s) - if err != nil { - return false - } - return b - } extractQuery := func(r *http.Request) repo.ItemQuery { params := r.URL.Query() return repo.ItemQuery{ - Page: intOrNegativeOne(params.Get("page")), - PageSize: intOrNegativeOne(params.Get("perPage")), + Page: queryIntOrNegativeOne(params.Get("page")), + PageSize: queryIntOrNegativeOne(params.Get("perPage")), Search: params.Get("q"), - LocationIDs: uuidList(params, "locations"), - LabelIDs: uuidList(params, "labels"), - IncludeArchived: getBool(params.Get("includeArchived")), + LocationIDs: queryUUIDList(params, "locations"), + LabelIDs: queryUUIDList(params, "labels"), + IncludeArchived: queryBool(params.Get("includeArchived")), } } diff --git a/backend/app/api/handlers/v1/v1_ctrl_locations.go b/backend/app/api/handlers/v1/v1_ctrl_locations.go index 5e85766..12e3c29 100644 --- a/backend/app/api/handlers/v1/v1_ctrl_locations.go +++ b/backend/app/api/handlers/v1/v1_ctrl_locations.go @@ -15,13 +15,21 @@ import ( // @Summary Get All Locations // @Tags Locations // @Produce json +// @Param filterChildren query bool false "Filter locations with parents" // @Success 200 {object} server.Results{items=[]repo.LocationOutCount} // @Router /v1/locations [GET] // @Security Bearer func (ctrl *V1Controller) HandleLocationGetAll() server.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) error { user := services.UseUserCtx(r.Context()) - locations, err := ctrl.repo.Locations.GetAll(r.Context(), user.GroupID) + + q := r.URL.Query() + + filter := repo.LocationQuery{ + FilterChildren: queryBool(q.Get("filterChildren")), + } + + locations, err := ctrl.repo.Locations.GetAll(r.Context(), user.GroupID, filter) if err != nil { log.Err(err).Msg("failed to get locations") return validate.NewRequestError(err, http.StatusInternalServerError) diff --git a/backend/app/api/static/docs/docs.go b/backend/app/api/static/docs/docs.go index a4105a1..f2c877e 100644 --- a/backend/app/api/static/docs/docs.go +++ b/backend/app/api/static/docs/docs.go @@ -756,6 +756,14 @@ const docTemplate = `{ "Locations" ], "summary": "Get All Locations", + "parameters": [ + { + "type": "boolean", + "description": "Filter locations with parents", + "name": "filterChildren", + "in": "query" + } + ], "responses": { "200": { "description": "OK", diff --git a/backend/app/api/static/docs/swagger.json b/backend/app/api/static/docs/swagger.json index 911f8f2..7215fae 100644 --- a/backend/app/api/static/docs/swagger.json +++ b/backend/app/api/static/docs/swagger.json @@ -748,6 +748,14 @@ "Locations" ], "summary": "Get All Locations", + "parameters": [ + { + "type": "boolean", + "description": "Filter locations with parents", + "name": "filterChildren", + "in": "query" + } + ], "responses": { "200": { "description": "OK", diff --git a/backend/app/api/static/docs/swagger.yaml b/backend/app/api/static/docs/swagger.yaml index b4f8c78..e479583 100644 --- a/backend/app/api/static/docs/swagger.yaml +++ b/backend/app/api/static/docs/swagger.yaml @@ -960,6 +960,11 @@ paths: - Labels /v1/locations: get: + parameters: + - description: Filter locations with parents + in: query + name: filterChildren + type: boolean produces: - application/json responses: diff --git a/backend/internal/core/services/service_items.go b/backend/internal/core/services/service_items.go index 5c02724..4af80c8 100644 --- a/backend/internal/core/services/service_items.go +++ b/backend/internal/core/services/service_items.go @@ -23,7 +23,7 @@ type ItemService struct { at attachmentTokens } -func (svc *ItemService) CsvImport(ctx context.Context, gid uuid.UUID, data [][]string) (int, error) { +func (svc *ItemService) CsvImport(ctx context.Context, GID uuid.UUID, data [][]string) (int, error) { loaded := []csvRow{} // Skip first row @@ -66,7 +66,7 @@ func (svc *ItemService) CsvImport(ctx context.Context, gid uuid.UUID, data [][]s // Bootstrap the locations and labels so we can reuse the created IDs for the items locations := map[string]uuid.UUID{} - existingLocation, err := svc.repo.Locations.GetAll(ctx, gid) + existingLocation, err := svc.repo.Locations.GetAll(ctx, GID, repo.LocationQuery{}) if err != nil { return 0, err } @@ -75,7 +75,7 @@ func (svc *ItemService) CsvImport(ctx context.Context, gid uuid.UUID, data [][]s } labels := map[string]uuid.UUID{} - existingLabels, err := svc.repo.Labels.GetAll(ctx, gid) + existingLabels, err := svc.repo.Labels.GetAll(ctx, GID) if err != nil { return 0, err } @@ -87,7 +87,7 @@ func (svc *ItemService) CsvImport(ctx context.Context, gid uuid.UUID, data [][]s // Locations if _, exists := locations[row.Location]; !exists { - result, err := svc.repo.Locations.Create(ctx, gid, repo.LocationCreate{ + result, err := svc.repo.Locations.Create(ctx, GID, repo.LocationCreate{ Name: row.Location, Description: "", }) @@ -103,7 +103,7 @@ func (svc *ItemService) CsvImport(ctx context.Context, gid uuid.UUID, data [][]s if _, exists := labels[label]; exists { continue } - result, err := svc.repo.Labels.Create(ctx, gid, repo.LabelCreate{ + result, err := svc.repo.Labels.Create(ctx, GID, repo.LabelCreate{ Name: label, Description: "", }) @@ -119,7 +119,7 @@ func (svc *ItemService) CsvImport(ctx context.Context, gid uuid.UUID, data [][]s for _, row := range loaded { // Check Import Ref if row.Item.ImportRef != "" { - exists, err := svc.repo.Items.CheckRef(ctx, gid, row.Item.ImportRef) + exists, err := svc.repo.Items.CheckRef(ctx, GID, row.Item.ImportRef) if exists { continue } @@ -139,7 +139,7 @@ func (svc *ItemService) CsvImport(ctx context.Context, gid uuid.UUID, data [][]s Str("location", row.Location). Msgf("Creating Item: %s", row.Item.Name) - result, err := svc.repo.Items.Create(ctx, gid, repo.ItemCreate{ + result, err := svc.repo.Items.Create(ctx, GID, repo.ItemCreate{ ImportRef: row.Item.ImportRef, Name: row.Item.Name, Description: row.Item.Description, @@ -152,7 +152,7 @@ func (svc *ItemService) CsvImport(ctx context.Context, gid uuid.UUID, data [][]s } // Update the item with the rest of the data - _, err = svc.repo.Items.UpdateByGroup(ctx, gid, repo.ItemUpdate{ + _, err = svc.repo.Items.UpdateByGroup(ctx, GID, repo.ItemUpdate{ // Edges LocationID: locationID, LabelIDs: labelIDs, diff --git a/backend/internal/core/services/service_items_test.go b/backend/internal/core/services/service_items_test.go index 1daa0b7..105c842 100644 --- a/backend/internal/core/services/service_items_test.go +++ b/backend/internal/core/services/service_items_test.go @@ -5,6 +5,7 @@ import ( "testing" "github.com/google/uuid" + "github.com/hay-kot/homebox/backend/internal/data/repo" "github.com/stretchr/testify/assert" ) @@ -38,7 +39,7 @@ func TestItemService_CsvImport(t *testing.T) { dataCsv = append(dataCsv, newCsvRow(item)) } - allLocation, err := tRepos.Locations.GetAll(context.Background(), tGroup.ID) + allLocation, err := tRepos.Locations.GetAll(context.Background(), tGroup.ID, repo.LocationQuery{}) assert.NoError(t, err) locNames := []string{} for _, loc := range allLocation { diff --git a/backend/internal/data/repo/repo_locations.go b/backend/internal/data/repo/repo_locations.go index f7d63eb..641b3e3 100644 --- a/backend/internal/data/repo/repo_locations.go +++ b/backend/internal/data/repo/repo_locations.go @@ -2,6 +2,7 @@ package repo import ( "context" + "strings" "time" "github.com/google/uuid" @@ -90,8 +91,12 @@ func mapLocationOut(location *ent.Location) LocationOut { } } +type LocationQuery struct { + FilterChildren bool `json:"filterChildren"` +} + // GetALlWithCount returns all locations with item count field populated -func (r *LocationRepository) GetAll(ctx context.Context, groupId uuid.UUID) ([]LocationOutCount, error) { +func (r *LocationRepository) GetAll(ctx context.Context, GID uuid.UUID, filter LocationQuery) ([]LocationOutCount, error) { query := `--sql SELECT id, @@ -111,13 +116,18 @@ func (r *LocationRepository) GetAll(ctx context.Context, groupId uuid.UUID) ([]L FROM locations WHERE - locations.group_locations = ? - AND locations.location_children IS NULL + locations.group_locations = ? {{ FILTER_CHILDREN }} ORDER BY locations.name ASC ` - rows, err := r.db.Sql().QueryContext(ctx, query, groupId) + if filter.FilterChildren { + query = strings.Replace(query, "{{ FILTER_CHILDREN }}", "AND locations.location_children IS NULL", 1) + } else { + query = strings.Replace(query, "{{ FILTER_CHILDREN }}", "", 1) + } + + rows, err := r.db.Sql().QueryContext(ctx, query, GID) if err != nil { return nil, err } diff --git a/backend/internal/data/repo/repo_locations_test.go b/backend/internal/data/repo/repo_locations_test.go index d48779d..5085d3b 100644 --- a/backend/internal/data/repo/repo_locations_test.go +++ b/backend/internal/data/repo/repo_locations_test.go @@ -43,7 +43,7 @@ func TestLocationRepositoryGetAllWithCount(t *testing.T) { assert.NoError(t, err) - results, err := tRepos.Locations.GetAll(context.Background(), tGroup.ID) + results, err := tRepos.Locations.GetAll(context.Background(), tGroup.ID, LocationQuery{}) assert.NoError(t, err) for _, loc := range results { diff --git a/frontend/components/Item/CreateModal.vue b/frontend/components/Item/CreateModal.vue index 42bb6b7..2a2aec3 100644 --- a/frontend/components/Item/CreateModal.vue +++ b/frontend/components/Item/CreateModal.vue @@ -41,7 +41,7 @@ const toast = useNotifier(); const locationsStore = useLocationStore(); - const locations = computed(() => locationsStore.locations); + const locations = computed(() => locationsStore.allLocations); const labelStore = useLabelStore(); const labels = computed(() => labelStore.labels); diff --git a/frontend/layouts/default.vue b/frontend/layouts/default.vue index 5b6d488..ab21150 100644 --- a/frontend/layouts/default.vue +++ b/frontend/layouts/default.vue @@ -32,7 +32,8 @@ const rmLocationStoreObserver = defineObserver("locationStore", { handler: r => { if (r.status === 201 || r.url.match(reLocation)) { - locationStore.refresh(); + locationStore.refreshChildren(); + locationStore.refreshParents(); } console.debug("locationStore handler called by observer"); }, @@ -43,7 +44,8 @@ EventTypes.ClearStores, () => { labelStore.refresh(); - locationStore.refresh(); + locationStore.refreshChildren(); + locationStore.refreshParents(); }, "stores" ); diff --git a/frontend/lib/api/classes/locations.ts b/frontend/lib/api/classes/locations.ts index d456e91..7559c62 100644 --- a/frontend/lib/api/classes/locations.ts +++ b/frontend/lib/api/classes/locations.ts @@ -2,9 +2,13 @@ import { BaseAPI, route } from "../base"; import { LocationOutCount, LocationCreate, LocationOut, LocationUpdate } from "../types/data-contracts"; import { Results } from "../types/non-generated"; +export type LocationsQuery = { + filterChildren: boolean; +}; + export class LocationsApi extends BaseAPI { - getAll() { - return this.http.get>({ url: route("/locations") }); + getAll(q: LocationsQuery = { filterChildren: false }) { + return this.http.get>({ url: route("/locations", q) }); } create(body: LocationCreate) { diff --git a/frontend/pages/home.vue b/frontend/pages/home.vue index 1adc178..0113a0b 100644 --- a/frontend/pages/home.vue +++ b/frontend/pages/home.vue @@ -16,7 +16,7 @@ const auth = useAuthStore(); const locationStore = useLocationStore(); - const locations = computed(() => locationStore.locations); + const locations = computed(() => locationStore.parentLocations); const labelsStore = useLabelStore(); const labels = computed(() => labelsStore.labels); diff --git a/frontend/pages/item/[id]/edit.vue b/frontend/pages/item/[id]/edit.vue index ede78b8..a43abc6 100644 --- a/frontend/pages/item/[id]/edit.vue +++ b/frontend/pages/item/[id]/edit.vue @@ -18,7 +18,7 @@ const itemId = computed(() => route.params.id as string); const locationStore = useLocationStore(); - const locations = computed(() => locationStore.locations); + const locations = computed(() => locationStore.allLocations); const labelStore = useLabelStore(); const labels = computed(() => labelStore.labels); diff --git a/frontend/pages/items.vue b/frontend/pages/items.vue index a1f21a2..317a0f0 100644 --- a/frontend/pages/items.vue +++ b/frontend/pages/items.vue @@ -82,7 +82,7 @@ }); const locationsStore = useLocationStore(); - const locations = computed(() => locationsStore.locations); + const locations = computed(() => locationsStore.allLocations); const labelStore = useLabelStore(); const labels = computed(() => labelStore.labels); diff --git a/frontend/pages/location/[id].vue b/frontend/pages/location/[id].vue index d915e0b..d58a935 100644 --- a/frontend/pages/location/[id].vue +++ b/frontend/pages/location/[id].vue @@ -117,7 +117,7 @@ } const locationStore = useLocationStore(); - const locations = computed(() => locationStore.locations); + const locations = computed(() => locationStore.allLocations); // eslint-disable-next-line @typescript-eslint/no-explicit-any const parent = ref({}); diff --git a/frontend/stores/locations.ts b/frontend/stores/locations.ts index e2b2680..68295f6 100644 --- a/frontend/stores/locations.ts +++ b/frontend/stores/locations.ts @@ -3,7 +3,8 @@ import { LocationOutCount } from "~~/lib/api/types/data-contracts"; export const useLocationStore = defineStore("locations", { state: () => ({ - allLocations: null as LocationOutCount[] | null, + parents: null as LocationOutCount[] | null, + Locations: null as LocationOutCount[] | null, client: useUserApi(), }), getters: { @@ -12,21 +13,36 @@ export const useLocationStore = defineStore("locations", { * synched with the server by intercepting the API calls and updating on the * response */ - locations(state): LocationOutCount[] { - if (state.allLocations === null) { - Promise.resolve(this.refresh()); + parentLocations(state): LocationOutCount[] { + if (state.parents === null) { + Promise.resolve(this.refreshParents()); } - return state.allLocations; + return state.parents; + }, + allLocations(state): LocationOutCount[] { + if (state.Locations === null) { + Promise.resolve(this.refreshChildren()); + } + return state.Locations; }, }, actions: { - async refresh(): Promise { - const result = await this.client.locations.getAll(); + async refreshParents(): Promise { + const result = await this.client.locations.getAll({ filterChildren: true }); if (result.error) { return result; } - this.allLocations = result.data.items; + this.parents = result.data.items; + return result; + }, + async refreshChildren(): Promise { + const result = await this.client.locations.getAll({ filterChildren: false }); + if (result.error) { + return result; + } + + this.Locations = result.data.items; return result; }, }, From 976f68252dcc88cc6e1996f051a41e463d6929ba Mon Sep 17 00:00:00 2001 From: Hayden <64056131+hay-kot@users.noreply.github.com> Date: Sat, 12 Nov 2022 14:54:24 -0900 Subject: [PATCH 002/350] feat: add INR currency and sort currencies (#141) --- backend/internal/data/ent/group/group.go | 3 +- backend/internal/data/ent/migrate/schema.go | 2 +- backend/internal/data/ent/schema/group.go | 2 +- frontend/lib/data/currency.ts | 58 ++++++++++++--------- 4 files changed, 36 insertions(+), 29 deletions(-) diff --git a/backend/internal/data/ent/group/group.go b/backend/internal/data/ent/group/group.go index c90a04c..a0269b4 100644 --- a/backend/internal/data/ent/group/group.go +++ b/backend/internal/data/ent/group/group.go @@ -129,6 +129,7 @@ const ( CurrencyNok Currency = "nok" CurrencySek Currency = "sek" CurrencyDkk Currency = "dkk" + CurrencyInr Currency = "inr" ) func (c Currency) String() string { @@ -138,7 +139,7 @@ func (c Currency) String() string { // CurrencyValidator is a validator for the "currency" field enum values. It is called by the builders before save. func CurrencyValidator(c Currency) error { switch c { - case CurrencyUsd, CurrencyEur, CurrencyGbp, CurrencyJpy, CurrencyZar, CurrencyAud, CurrencyNok, CurrencySek, CurrencyDkk: + case CurrencyUsd, CurrencyEur, CurrencyGbp, CurrencyJpy, CurrencyZar, CurrencyAud, CurrencyNok, CurrencySek, CurrencyDkk, CurrencyInr: return nil default: return fmt.Errorf("group: invalid enum value for currency field: %q", c) diff --git a/backend/internal/data/ent/migrate/schema.go b/backend/internal/data/ent/migrate/schema.go index fbaeb3a..6fd2d9e 100644 --- a/backend/internal/data/ent/migrate/schema.go +++ b/backend/internal/data/ent/migrate/schema.go @@ -127,7 +127,7 @@ var ( {Name: "created_at", Type: field.TypeTime}, {Name: "updated_at", Type: field.TypeTime}, {Name: "name", Type: field.TypeString, Size: 255}, - {Name: "currency", Type: field.TypeEnum, Enums: []string{"usd", "eur", "gbp", "jpy", "zar", "aud", "nok", "sek", "dkk"}, Default: "usd"}, + {Name: "currency", Type: field.TypeEnum, Enums: []string{"usd", "eur", "gbp", "jpy", "zar", "aud", "nok", "sek", "dkk", "inr"}, Default: "usd"}, } // GroupsTable holds the schema information for the "groups" table. GroupsTable = &schema.Table{ diff --git a/backend/internal/data/ent/schema/group.go b/backend/internal/data/ent/schema/group.go index 49b358c..c7c70d0 100644 --- a/backend/internal/data/ent/schema/group.go +++ b/backend/internal/data/ent/schema/group.go @@ -27,7 +27,7 @@ func (Group) Fields() []ent.Field { NotEmpty(), field.Enum("currency"). Default("usd"). - Values("usd", "eur", "gbp", "jpy", "zar", "aud", "nok", "sek", "dkk"), + Values("usd", "eur", "gbp", "jpy", "zar", "aud", "nok", "sek", "dkk", "inr"), } } diff --git a/frontend/lib/data/currency.ts b/frontend/lib/data/currency.ts index ef44ed0..7cdbca5 100644 --- a/frontend/lib/data/currency.ts +++ b/frontend/lib/data/currency.ts @@ -1,4 +1,4 @@ -export type Codes = "USD" | "EUR" | "GBP" | "JPY" | "ZAR" | "AUD" | "NOK" | "SEK" | "DKK"; +export type Codes = "USD" | "EUR" | "GBP" | "JPY" | "ZAR" | "AUD" | "NOK" | "SEK" | "DKK" | "INR"; export type Currency = { code: Codes; @@ -9,16 +9,10 @@ export type Currency = { export const currencies: Currency[] = [ { - code: "USD", - local: "en-US", + code: "AUD", + local: "en-AU", symbol: "$", - name: "US Dollar", - }, - { - code: "EUR", - local: "de-DE", - symbol: "€", - name: "Euro", + name: "Australian Dollar", }, { code: "GBP", @@ -26,30 +20,42 @@ export const currencies: Currency[] = [ symbol: "£", name: "British Pound", }, + { + code: "DKK", + local: "da-DK", + symbol: "kr", + name: "Danish Krone", + }, + { + code: "EUR", + local: "de-DE", + symbol: "€", + name: "Euro", + }, + { + code: "INR", + local: "en-IN", + symbol: "₹", + name: "Indian Rupee", + }, { code: "JPY", local: "ja-JP", symbol: "¥", name: "Japanese Yen", }, - { - code: "ZAR", - local: "en-ZA", - symbol: "R", - name: "South African Rand", - }, - { - code: "AUD", - local: "en-AU", - symbol: "$", - name: "Australian Dollar", - }, { code: "NOK", local: "nb-NO", symbol: "kr", name: "Norwegian Krone", }, + { + code: "ZAR", + local: "en-ZA", + symbol: "R", + name: "South African Rand", + }, { code: "SEK", local: "sv-SE", @@ -57,9 +63,9 @@ export const currencies: Currency[] = [ name: "Swedish Krona", }, { - code: "DKK", - local: "da-DK", - symbol: "kr", - name: "Danish Krone", + code: "USD", + local: "en-US", + symbol: "$", + name: "US Dollar", }, ]; From 6dc2ae1bea5efeee1aa08260883a1f8fd7b1a6e2 Mon Sep 17 00:00:00 2001 From: Hayden <64056131+hay-kot@users.noreply.github.com> Date: Sun, 13 Nov 2022 14:17:55 -0900 Subject: [PATCH 003/350] feat: asset tags/ids (#142) * add schema * run db migration * bulk seed asset IDs * breaking: update runtime options * conditionally increment asset IDs * update API endpoints * fix import asset id assignment * refactor display + marshal/unmarshal * add docs page * add to form field * hide 000-000 values * update ENV vars --- .../app/api/handlers/v1/v1_ctrl_actions.go | 35 ++++++ backend/app/api/handlers/v1/v1_ctrl_items.go | 4 +- backend/app/api/main.go | 5 +- backend/app/api/routes.go | 4 +- backend/app/api/static/docs/docs.go | 39 ++++++ backend/app/api/static/docs/swagger.json | 39 ++++++ backend/app/api/static/docs/swagger.yaml | 24 ++++ backend/internal/core/services/all.go | 27 +++- .../internal/core/services/service_items.go | 61 +++++++++- backend/internal/data/ent/item.go | 13 +- backend/internal/data/ent/item/item.go | 5 + backend/internal/data/ent/item/where.go | 71 +++++++++++ backend/internal/data/ent/item_create.go | 29 +++++ backend/internal/data/ent/item_update.go | 70 +++++++++++ backend/internal/data/ent/migrate/schema.go | 18 ++- backend/internal/data/ent/mutation.go | 89 +++++++++++++- backend/internal/data/ent/runtime.go | 20 +-- backend/internal/data/ent/schema/item.go | 3 + .../20221113012312_add_asset_id_field.sql | 24 ++++ .../data/migrations/migrations/atlas.sum | 3 +- backend/internal/data/repo/asset_id_type.go | 30 +++++ .../internal/data/repo/asset_id_type_test.go | 115 ++++++++++++++++++ backend/internal/data/repo/repo_items.go | 49 +++++++- backend/internal/sys/config/conf.go | 23 ++-- docs/docs/quick-start.md | 74 +++++------ docs/docs/tips-tricks.md | 18 +++ frontend/lib/api/classes/actions.ts | 10 ++ frontend/lib/api/types/data-contracts.ts | 8 ++ frontend/lib/api/user.ts | 3 + frontend/pages/item/[id]/edit.vue | 5 + frontend/pages/item/[id]/index.vue | 14 +++ frontend/pages/profile.vue | 45 +++++++ 32 files changed, 905 insertions(+), 72 deletions(-) create mode 100644 backend/app/api/handlers/v1/v1_ctrl_actions.go create mode 100644 backend/internal/data/migrations/migrations/20221113012312_add_asset_id_field.sql create mode 100644 backend/internal/data/repo/asset_id_type.go create mode 100644 backend/internal/data/repo/asset_id_type_test.go create mode 100644 frontend/lib/api/classes/actions.ts diff --git a/backend/app/api/handlers/v1/v1_ctrl_actions.go b/backend/app/api/handlers/v1/v1_ctrl_actions.go new file mode 100644 index 0000000..37e2b72 --- /dev/null +++ b/backend/app/api/handlers/v1/v1_ctrl_actions.go @@ -0,0 +1,35 @@ +package v1 + +import ( + "net/http" + + "github.com/hay-kot/homebox/backend/internal/core/services" + "github.com/hay-kot/homebox/backend/internal/sys/validate" + "github.com/hay-kot/homebox/backend/pkgs/server" + "github.com/rs/zerolog/log" +) + +type EnsureAssetIDResult struct { + Completed int `json:"completed"` +} + +// HandleGroupInvitationsCreate godoc +// @Summary Get the current user +// @Tags Group +// @Produce json +// @Success 200 {object} EnsureAssetIDResult +// @Router /v1/actions/ensure-asset-ids [Post] +// @Security Bearer +func (ctrl *V1Controller) HandleEnsureAssetID() server.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) error { + ctx := services.NewContext(r.Context()) + + totalCompleted, err := ctrl.svc.Items.EnsureAssetID(ctx, ctx.GID) + if err != nil { + log.Err(err).Msg("failed to ensure asset id") + return validate.NewRequestError(err, http.StatusInternalServerError) + } + + return server.Respond(w, http.StatusOK, EnsureAssetIDResult{Completed: totalCompleted}) + } +} diff --git a/backend/app/api/handlers/v1/v1_ctrl_items.go b/backend/app/api/handlers/v1/v1_ctrl_items.go index f4d43d3..f6bc5ff 100644 --- a/backend/app/api/handlers/v1/v1_ctrl_items.go +++ b/backend/app/api/handlers/v1/v1_ctrl_items.go @@ -65,8 +65,8 @@ func (ctrl *V1Controller) HandleItemsCreate() server.HandlerFunc { return validate.NewRequestError(err, http.StatusInternalServerError) } - user := services.UseUserCtx(r.Context()) - item, err := ctrl.repo.Items.Create(r.Context(), user.GroupID, createData) + ctx := services.NewContext(r.Context()) + item, err := ctrl.svc.Items.Create(ctx, createData) if err != nil { log.Err(err).Msg("failed to create item") return validate.NewRequestError(err, http.StatusInternalServerError) diff --git a/backend/app/api/main.go b/backend/app/api/main.go index c62d5df..c505eea 100644 --- a/backend/app/api/main.go +++ b/backend/app/api/main.go @@ -112,7 +112,10 @@ func run(cfg *config.Config) error { app.db = c app.repos = repo.New(c, cfg.Storage.Data) - app.services = services.New(app.repos) + app.services = services.New( + app.repos, + services.WithAutoIncrementAssetID(cfg.Options.AutoIncrementAssetID), + ) // ========================================================================= // Start Server\ diff --git a/backend/app/api/routes.go b/backend/app/api/routes.go index 992e70e..cab1a14 100644 --- a/backend/app/api/routes.go +++ b/backend/app/api/routes.go @@ -51,7 +51,7 @@ func (a *app) mountRoutes(repos *repo.AllRepos) { a.services, a.repos, v1.WithMaxUploadSize(a.conf.Web.MaxUploadSize), - v1.WithRegistration(a.conf.AllowRegistration), + v1.WithRegistration(a.conf.Options.AllowRegistration), v1.WithDemoStatus(a.conf.Demo), // Disable Password Change in Demo Mode ) @@ -82,6 +82,8 @@ func (a *app) mountRoutes(repos *repo.AllRepos) { a.server.Get(v1Base("/groups"), v1Ctrl.HandleGroupGet(), a.mwAuthToken) a.server.Put(v1Base("/groups"), v1Ctrl.HandleGroupUpdate(), a.mwAuthToken) + a.server.Post(v1Base("/actions/ensure-asset-ids"), v1Ctrl.HandleEnsureAssetID(), a.mwAuthToken) + a.server.Get(v1Base("/locations"), v1Ctrl.HandleLocationGetAll(), a.mwAuthToken) a.server.Post(v1Base("/locations"), v1Ctrl.HandleLocationCreate(), a.mwAuthToken) a.server.Get(v1Base("/locations/{id}"), v1Ctrl.HandleLocationGet(), a.mwAuthToken) diff --git a/backend/app/api/static/docs/docs.go b/backend/app/api/static/docs/docs.go index f2c877e..6e02411 100644 --- a/backend/app/api/static/docs/docs.go +++ b/backend/app/api/static/docs/docs.go @@ -21,6 +21,30 @@ const docTemplate = `{ "host": "{{.Host}}", "basePath": "{{.BasePath}}", "paths": { + "/v1/actions/ensure-asset-ids": { + "post": { + "security": [ + { + "Bearer": [] + } + ], + "produces": [ + "application/json" + ], + "tags": [ + "Group" + ], + "summary": "Get the current user", + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/v1.EnsureAssetIDResult" + } + } + } + } + }, "/v1/groups": { "get": { "security": [ @@ -1326,6 +1350,10 @@ const docTemplate = `{ "archived": { "type": "boolean" }, + "assetId": { + "type": "string", + "example": "0" + }, "attachments": { "type": "array", "items": { @@ -1479,6 +1507,9 @@ const docTemplate = `{ "archived": { "type": "boolean" }, + "assetId": { + "type": "string" + }, "description": { "type": "string" }, @@ -1891,6 +1922,14 @@ const docTemplate = `{ } } }, + "v1.EnsureAssetIDResult": { + "type": "object", + "properties": { + "completed": { + "type": "integer" + } + } + }, "v1.GroupInvitation": { "type": "object", "properties": { diff --git a/backend/app/api/static/docs/swagger.json b/backend/app/api/static/docs/swagger.json index 7215fae..3be73bd 100644 --- a/backend/app/api/static/docs/swagger.json +++ b/backend/app/api/static/docs/swagger.json @@ -13,6 +13,30 @@ }, "basePath": "/api", "paths": { + "/v1/actions/ensure-asset-ids": { + "post": { + "security": [ + { + "Bearer": [] + } + ], + "produces": [ + "application/json" + ], + "tags": [ + "Group" + ], + "summary": "Get the current user", + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/v1.EnsureAssetIDResult" + } + } + } + } + }, "/v1/groups": { "get": { "security": [ @@ -1318,6 +1342,10 @@ "archived": { "type": "boolean" }, + "assetId": { + "type": "string", + "example": "0" + }, "attachments": { "type": "array", "items": { @@ -1471,6 +1499,9 @@ "archived": { "type": "boolean" }, + "assetId": { + "type": "string" + }, "description": { "type": "string" }, @@ -1883,6 +1914,14 @@ } } }, + "v1.EnsureAssetIDResult": { + "type": "object", + "properties": { + "completed": { + "type": "integer" + } + } + }, "v1.GroupInvitation": { "type": "object", "properties": { diff --git a/backend/app/api/static/docs/swagger.yaml b/backend/app/api/static/docs/swagger.yaml index e479583..72563b9 100644 --- a/backend/app/api/static/docs/swagger.yaml +++ b/backend/app/api/static/docs/swagger.yaml @@ -98,6 +98,9 @@ definitions: properties: archived: type: boolean + assetId: + example: "0" + type: string attachments: items: $ref: '#/definitions/repo.ItemAttachment' @@ -204,6 +207,8 @@ definitions: properties: archived: type: boolean + assetId: + type: string description: type: string fields: @@ -477,6 +482,11 @@ definitions: new: type: string type: object + v1.EnsureAssetIDResult: + properties: + completed: + type: integer + type: object v1.GroupInvitation: properties: expiresAt: @@ -516,6 +526,20 @@ info: title: Go API Templates version: "1.0" paths: + /v1/actions/ensure-asset-ids: + post: + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/v1.EnsureAssetIDResult' + security: + - Bearer: [] + summary: Get the current user + tags: + - Group /v1/groups: get: produces: diff --git a/backend/internal/core/services/all.go b/backend/internal/core/services/all.go index 5147b8a..25e406e 100644 --- a/backend/internal/core/services/all.go +++ b/backend/internal/core/services/all.go @@ -8,17 +8,38 @@ type AllServices struct { Items *ItemService } -func New(repos *repo.AllRepos) *AllServices { +type OptionsFunc func(*options) + +type options struct { + autoIncrementAssetID bool +} + +func WithAutoIncrementAssetID(v bool) func(*options) { + return func(o *options) { + o.autoIncrementAssetID = v + } +} + +func New(repos *repo.AllRepos, opts ...OptionsFunc) *AllServices { if repos == nil { panic("repos cannot be nil") } + options := &options{ + autoIncrementAssetID: true, + } + + for _, opt := range opts { + opt(options) + } + return &AllServices{ User: &UserService{repos}, Group: &GroupService{repos}, Items: &ItemService{ - repo: repos, - at: attachmentTokens{}, + repo: repos, + at: attachmentTokens{}, + autoIncrementAssetID: options.autoIncrementAssetID, }, } } diff --git a/backend/internal/core/services/service_items.go b/backend/internal/core/services/service_items.go index 4af80c8..5055c67 100644 --- a/backend/internal/core/services/service_items.go +++ b/backend/internal/core/services/service_items.go @@ -21,8 +21,49 @@ type ItemService struct { // at is a map of tokens to attachment IDs. This is used to store the attachment ID // for issued URLs at attachmentTokens + + autoIncrementAssetID bool } +func (svc *ItemService) Create(ctx Context, item repo.ItemCreate) (repo.ItemOut, error) { + if svc.autoIncrementAssetID { + highest, err := svc.repo.Items.GetHighestAssetID(ctx, ctx.GID) + if err != nil { + return repo.ItemOut{}, err + } + + item.AssetID = repo.AssetID(highest + 1) + } + + return svc.repo.Items.Create(ctx, ctx.GID, item) +} + +func (svc *ItemService) EnsureAssetID(ctx context.Context, GID uuid.UUID) (int, error) { + items, err := svc.repo.Items.GetAllZeroAssetID(ctx, GID) + + if err != nil { + return 0, err + } + + highest, err := svc.repo.Items.GetHighestAssetID(ctx, GID) + if err != nil { + return 0, err + } + + finished := 0 + for _, item := range items { + highest++ + + err = svc.repo.Items.SetAssetID(ctx, GID, item.ID, repo.AssetID(highest)) + if err != nil { + return 0, err + } + + finished++ + } + + return finished, nil +} func (svc *ItemService) CsvImport(ctx context.Context, GID uuid.UUID, data [][]string) (int, error) { loaded := []csvRow{} @@ -114,6 +155,14 @@ func (svc *ItemService) CsvImport(ctx context.Context, GID uuid.UUID, data [][]s } } + highest := repo.AssetID(-1) + if svc.autoIncrementAssetID { + highest, err = svc.repo.Items.GetHighestAssetID(ctx, GID) + if err != nil { + return 0, err + } + } + // Create the items var count int for _, row := range loaded { @@ -139,13 +188,20 @@ func (svc *ItemService) CsvImport(ctx context.Context, GID uuid.UUID, data [][]s Str("location", row.Location). Msgf("Creating Item: %s", row.Item.Name) - result, err := svc.repo.Items.Create(ctx, GID, repo.ItemCreate{ + data := repo.ItemCreate{ ImportRef: row.Item.ImportRef, Name: row.Item.Name, Description: row.Item.Description, LabelIDs: labelIDs, LocationID: locationID, - }) + } + + if svc.autoIncrementAssetID { + highest++ + data.AssetID = highest + } + + result, err := svc.repo.Items.Create(ctx, GID, data) if err != nil { return count, err @@ -156,6 +212,7 @@ func (svc *ItemService) CsvImport(ctx context.Context, GID uuid.UUID, data [][]s // Edges LocationID: locationID, LabelIDs: labelIDs, + AssetID: data.AssetID, // General Fields ID: result.ID, diff --git a/backend/internal/data/ent/item.go b/backend/internal/data/ent/item.go index 802acff..a780945 100644 --- a/backend/internal/data/ent/item.go +++ b/backend/internal/data/ent/item.go @@ -37,6 +37,8 @@ type Item struct { Insured bool `json:"insured,omitempty"` // Archived holds the value of the "archived" field. Archived bool `json:"archived,omitempty"` + // AssetID holds the value of the "asset_id" field. + AssetID int `json:"asset_id,omitempty"` // SerialNumber holds the value of the "serial_number" field. SerialNumber string `json:"serial_number,omitempty"` // ModelNumber holds the value of the "model_number" field. @@ -176,7 +178,7 @@ func (*Item) scanValues(columns []string) ([]any, error) { values[i] = new(sql.NullBool) case item.FieldPurchasePrice, item.FieldSoldPrice: values[i] = new(sql.NullFloat64) - case item.FieldQuantity: + case item.FieldQuantity, item.FieldAssetID: values[i] = new(sql.NullInt64) case item.FieldName, item.FieldDescription, item.FieldImportRef, item.FieldNotes, item.FieldSerialNumber, item.FieldModelNumber, item.FieldManufacturer, item.FieldWarrantyDetails, item.FieldPurchaseFrom, item.FieldSoldTo, item.FieldSoldNotes: values[i] = new(sql.NullString) @@ -265,6 +267,12 @@ func (i *Item) assignValues(columns []string, values []any) error { } else if value.Valid { i.Archived = value.Bool } + case item.FieldAssetID: + if value, ok := values[j].(*sql.NullInt64); !ok { + return fmt.Errorf("unexpected type %T for field asset_id", values[j]) + } else if value.Valid { + i.AssetID = int(value.Int64) + } case item.FieldSerialNumber: if value, ok := values[j].(*sql.NullString); !ok { return fmt.Errorf("unexpected type %T for field serial_number", values[j]) @@ -454,6 +462,9 @@ func (i *Item) String() string { builder.WriteString("archived=") builder.WriteString(fmt.Sprintf("%v", i.Archived)) builder.WriteString(", ") + builder.WriteString("asset_id=") + builder.WriteString(fmt.Sprintf("%v", i.AssetID)) + builder.WriteString(", ") builder.WriteString("serial_number=") builder.WriteString(i.SerialNumber) builder.WriteString(", ") diff --git a/backend/internal/data/ent/item/item.go b/backend/internal/data/ent/item/item.go index c2991da..ab3b43f 100644 --- a/backend/internal/data/ent/item/item.go +++ b/backend/internal/data/ent/item/item.go @@ -31,6 +31,8 @@ const ( FieldInsured = "insured" // FieldArchived holds the string denoting the archived field in the database. FieldArchived = "archived" + // FieldAssetID holds the string denoting the asset_id field in the database. + FieldAssetID = "asset_id" // FieldSerialNumber holds the string denoting the serial_number field in the database. FieldSerialNumber = "serial_number" // FieldModelNumber holds the string denoting the model_number field in the database. @@ -128,6 +130,7 @@ var Columns = []string{ FieldQuantity, FieldInsured, FieldArchived, + FieldAssetID, FieldSerialNumber, FieldModelNumber, FieldManufacturer, @@ -193,6 +196,8 @@ var ( DefaultInsured bool // DefaultArchived holds the default value on creation for the "archived" field. DefaultArchived bool + // DefaultAssetID holds the default value on creation for the "asset_id" field. + DefaultAssetID int // SerialNumberValidator is a validator for the "serial_number" field. It is called by the builders before save. SerialNumberValidator func(string) error // ModelNumberValidator is a validator for the "model_number" field. It is called by the builders before save. diff --git a/backend/internal/data/ent/item/where.go b/backend/internal/data/ent/item/where.go index 2897e35..2174432 100644 --- a/backend/internal/data/ent/item/where.go +++ b/backend/internal/data/ent/item/where.go @@ -145,6 +145,13 @@ func Archived(v bool) predicate.Item { }) } +// AssetID applies equality check predicate on the "asset_id" field. It's identical to AssetIDEQ. +func AssetID(v int) predicate.Item { + return predicate.Item(func(s *sql.Selector) { + s.Where(sql.EQ(s.C(FieldAssetID), v)) + }) +} + // SerialNumber applies equality check predicate on the "serial_number" field. It's identical to SerialNumberEQ. func SerialNumber(v string) predicate.Item { return predicate.Item(func(s *sql.Selector) { @@ -894,6 +901,70 @@ func ArchivedNEQ(v bool) predicate.Item { }) } +// AssetIDEQ applies the EQ predicate on the "asset_id" field. +func AssetIDEQ(v int) predicate.Item { + return predicate.Item(func(s *sql.Selector) { + s.Where(sql.EQ(s.C(FieldAssetID), v)) + }) +} + +// AssetIDNEQ applies the NEQ predicate on the "asset_id" field. +func AssetIDNEQ(v int) predicate.Item { + return predicate.Item(func(s *sql.Selector) { + s.Where(sql.NEQ(s.C(FieldAssetID), v)) + }) +} + +// AssetIDIn applies the In predicate on the "asset_id" field. +func AssetIDIn(vs ...int) predicate.Item { + v := make([]any, len(vs)) + for i := range v { + v[i] = vs[i] + } + return predicate.Item(func(s *sql.Selector) { + s.Where(sql.In(s.C(FieldAssetID), v...)) + }) +} + +// AssetIDNotIn applies the NotIn predicate on the "asset_id" field. +func AssetIDNotIn(vs ...int) predicate.Item { + v := make([]any, len(vs)) + for i := range v { + v[i] = vs[i] + } + return predicate.Item(func(s *sql.Selector) { + s.Where(sql.NotIn(s.C(FieldAssetID), v...)) + }) +} + +// AssetIDGT applies the GT predicate on the "asset_id" field. +func AssetIDGT(v int) predicate.Item { + return predicate.Item(func(s *sql.Selector) { + s.Where(sql.GT(s.C(FieldAssetID), v)) + }) +} + +// AssetIDGTE applies the GTE predicate on the "asset_id" field. +func AssetIDGTE(v int) predicate.Item { + return predicate.Item(func(s *sql.Selector) { + s.Where(sql.GTE(s.C(FieldAssetID), v)) + }) +} + +// AssetIDLT applies the LT predicate on the "asset_id" field. +func AssetIDLT(v int) predicate.Item { + return predicate.Item(func(s *sql.Selector) { + s.Where(sql.LT(s.C(FieldAssetID), v)) + }) +} + +// AssetIDLTE applies the LTE predicate on the "asset_id" field. +func AssetIDLTE(v int) predicate.Item { + return predicate.Item(func(s *sql.Selector) { + s.Where(sql.LTE(s.C(FieldAssetID), v)) + }) +} + // SerialNumberEQ applies the EQ predicate on the "serial_number" field. func SerialNumberEQ(v string) predicate.Item { return predicate.Item(func(s *sql.Selector) { diff --git a/backend/internal/data/ent/item_create.go b/backend/internal/data/ent/item_create.go index f4de18e..36c9720 100644 --- a/backend/internal/data/ent/item_create.go +++ b/backend/internal/data/ent/item_create.go @@ -144,6 +144,20 @@ func (ic *ItemCreate) SetNillableArchived(b *bool) *ItemCreate { return ic } +// SetAssetID sets the "asset_id" field. +func (ic *ItemCreate) SetAssetID(i int) *ItemCreate { + ic.mutation.SetAssetID(i) + return ic +} + +// SetNillableAssetID sets the "asset_id" field if the given value is not nil. +func (ic *ItemCreate) SetNillableAssetID(i *int) *ItemCreate { + if i != nil { + ic.SetAssetID(*i) + } + return ic +} + // SetSerialNumber sets the "serial_number" field. func (ic *ItemCreate) SetSerialNumber(s string) *ItemCreate { ic.mutation.SetSerialNumber(s) @@ -546,6 +560,10 @@ func (ic *ItemCreate) defaults() { v := item.DefaultArchived ic.mutation.SetArchived(v) } + if _, ok := ic.mutation.AssetID(); !ok { + v := item.DefaultAssetID + ic.mutation.SetAssetID(v) + } if _, ok := ic.mutation.LifetimeWarranty(); !ok { v := item.DefaultLifetimeWarranty ic.mutation.SetLifetimeWarranty(v) @@ -604,6 +622,9 @@ func (ic *ItemCreate) check() error { if _, ok := ic.mutation.Archived(); !ok { return &ValidationError{Name: "archived", err: errors.New(`ent: missing required field "Item.archived"`)} } + if _, ok := ic.mutation.AssetID(); !ok { + return &ValidationError{Name: "asset_id", err: errors.New(`ent: missing required field "Item.asset_id"`)} + } if v, ok := ic.mutation.SerialNumber(); ok { if err := item.SerialNumberValidator(v); err != nil { return &ValidationError{Name: "serial_number", err: fmt.Errorf(`ent: validator failed for field "Item.serial_number": %w`, err)} @@ -749,6 +770,14 @@ func (ic *ItemCreate) createSpec() (*Item, *sqlgraph.CreateSpec) { }) _node.Archived = value } + if value, ok := ic.mutation.AssetID(); ok { + _spec.Fields = append(_spec.Fields, &sqlgraph.FieldSpec{ + Type: field.TypeInt, + Value: value, + Column: item.FieldAssetID, + }) + _node.AssetID = value + } if value, ok := ic.mutation.SerialNumber(); ok { _spec.Fields = append(_spec.Fields, &sqlgraph.FieldSpec{ Type: field.TypeString, diff --git a/backend/internal/data/ent/item_update.go b/backend/internal/data/ent/item_update.go index 4307051..52970f5 100644 --- a/backend/internal/data/ent/item_update.go +++ b/backend/internal/data/ent/item_update.go @@ -135,6 +135,27 @@ func (iu *ItemUpdate) SetNillableArchived(b *bool) *ItemUpdate { return iu } +// SetAssetID sets the "asset_id" field. +func (iu *ItemUpdate) SetAssetID(i int) *ItemUpdate { + iu.mutation.ResetAssetID() + iu.mutation.SetAssetID(i) + return iu +} + +// SetNillableAssetID sets the "asset_id" field if the given value is not nil. +func (iu *ItemUpdate) SetNillableAssetID(i *int) *ItemUpdate { + if i != nil { + iu.SetAssetID(*i) + } + return iu +} + +// AddAssetID adds i to the "asset_id" field. +func (iu *ItemUpdate) AddAssetID(i int) *ItemUpdate { + iu.mutation.AddAssetID(i) + return iu +} + // SetSerialNumber sets the "serial_number" field. func (iu *ItemUpdate) SetSerialNumber(s string) *ItemUpdate { iu.mutation.SetSerialNumber(s) @@ -816,6 +837,20 @@ func (iu *ItemUpdate) sqlSave(ctx context.Context) (n int, err error) { Column: item.FieldArchived, }) } + if value, ok := iu.mutation.AssetID(); ok { + _spec.Fields.Set = append(_spec.Fields.Set, &sqlgraph.FieldSpec{ + Type: field.TypeInt, + Value: value, + Column: item.FieldAssetID, + }) + } + if value, ok := iu.mutation.AddedAssetID(); ok { + _spec.Fields.Add = append(_spec.Fields.Add, &sqlgraph.FieldSpec{ + Type: field.TypeInt, + Value: value, + Column: item.FieldAssetID, + }) + } if value, ok := iu.mutation.SerialNumber(); ok { _spec.Fields.Set = append(_spec.Fields.Set, &sqlgraph.FieldSpec{ Type: field.TypeString, @@ -1422,6 +1457,27 @@ func (iuo *ItemUpdateOne) SetNillableArchived(b *bool) *ItemUpdateOne { return iuo } +// SetAssetID sets the "asset_id" field. +func (iuo *ItemUpdateOne) SetAssetID(i int) *ItemUpdateOne { + iuo.mutation.ResetAssetID() + iuo.mutation.SetAssetID(i) + return iuo +} + +// SetNillableAssetID sets the "asset_id" field if the given value is not nil. +func (iuo *ItemUpdateOne) SetNillableAssetID(i *int) *ItemUpdateOne { + if i != nil { + iuo.SetAssetID(*i) + } + return iuo +} + +// AddAssetID adds i to the "asset_id" field. +func (iuo *ItemUpdateOne) AddAssetID(i int) *ItemUpdateOne { + iuo.mutation.AddAssetID(i) + return iuo +} + // SetSerialNumber sets the "serial_number" field. func (iuo *ItemUpdateOne) SetSerialNumber(s string) *ItemUpdateOne { iuo.mutation.SetSerialNumber(s) @@ -2133,6 +2189,20 @@ func (iuo *ItemUpdateOne) sqlSave(ctx context.Context) (_node *Item, err error) Column: item.FieldArchived, }) } + if value, ok := iuo.mutation.AssetID(); ok { + _spec.Fields.Set = append(_spec.Fields.Set, &sqlgraph.FieldSpec{ + Type: field.TypeInt, + Value: value, + Column: item.FieldAssetID, + }) + } + if value, ok := iuo.mutation.AddedAssetID(); ok { + _spec.Fields.Add = append(_spec.Fields.Add, &sqlgraph.FieldSpec{ + Type: field.TypeInt, + Value: value, + Column: item.FieldAssetID, + }) + } if value, ok := iuo.mutation.SerialNumber(); ok { _spec.Fields.Set = append(_spec.Fields.Set, &sqlgraph.FieldSpec{ Type: field.TypeString, diff --git a/backend/internal/data/ent/migrate/schema.go b/backend/internal/data/ent/migrate/schema.go index 6fd2d9e..464c3c6 100644 --- a/backend/internal/data/ent/migrate/schema.go +++ b/backend/internal/data/ent/migrate/schema.go @@ -171,6 +171,7 @@ var ( {Name: "quantity", Type: field.TypeInt, Default: 1}, {Name: "insured", Type: field.TypeBool, Default: false}, {Name: "archived", Type: field.TypeBool, Default: false}, + {Name: "asset_id", Type: field.TypeInt, Default: 0}, {Name: "serial_number", Type: field.TypeString, Nullable: true, Size: 255}, {Name: "model_number", Type: field.TypeString, Nullable: true, Size: 255}, {Name: "manufacturer", Type: field.TypeString, Nullable: true, Size: 255}, @@ -196,19 +197,19 @@ var ( ForeignKeys: []*schema.ForeignKey{ { Symbol: "items_groups_items", - Columns: []*schema.Column{ItemsColumns[23]}, + Columns: []*schema.Column{ItemsColumns[24]}, RefColumns: []*schema.Column{GroupsColumns[0]}, OnDelete: schema.Cascade, }, { Symbol: "items_items_children", - Columns: []*schema.Column{ItemsColumns[24]}, + Columns: []*schema.Column{ItemsColumns[25]}, RefColumns: []*schema.Column{ItemsColumns[0]}, OnDelete: schema.SetNull, }, { Symbol: "items_locations_items", - Columns: []*schema.Column{ItemsColumns[25]}, + Columns: []*schema.Column{ItemsColumns[26]}, RefColumns: []*schema.Column{LocationsColumns[0]}, OnDelete: schema.Cascade, }, @@ -222,23 +223,28 @@ var ( { Name: "item_manufacturer", Unique: false, - Columns: []*schema.Column{ItemsColumns[12]}, + Columns: []*schema.Column{ItemsColumns[13]}, }, { Name: "item_model_number", Unique: false, - Columns: []*schema.Column{ItemsColumns[11]}, + Columns: []*schema.Column{ItemsColumns[12]}, }, { Name: "item_serial_number", Unique: false, - Columns: []*schema.Column{ItemsColumns[10]}, + Columns: []*schema.Column{ItemsColumns[11]}, }, { Name: "item_archived", Unique: false, Columns: []*schema.Column{ItemsColumns[9]}, }, + { + Name: "item_asset_id", + Unique: false, + Columns: []*schema.Column{ItemsColumns[10]}, + }, }, } // ItemFieldsColumns holds the columns for the "item_fields" table. diff --git a/backend/internal/data/ent/mutation.go b/backend/internal/data/ent/mutation.go index 73ccbde..2f1ebdc 100644 --- a/backend/internal/data/ent/mutation.go +++ b/backend/internal/data/ent/mutation.go @@ -4134,6 +4134,8 @@ type ItemMutation struct { addquantity *int insured *bool archived *bool + asset_id *int + addasset_id *int serial_number *string model_number *string manufacturer *string @@ -4660,6 +4662,62 @@ func (m *ItemMutation) ResetArchived() { m.archived = nil } +// SetAssetID sets the "asset_id" field. +func (m *ItemMutation) SetAssetID(i int) { + m.asset_id = &i + m.addasset_id = nil +} + +// AssetID returns the value of the "asset_id" field in the mutation. +func (m *ItemMutation) AssetID() (r int, exists bool) { + v := m.asset_id + if v == nil { + return + } + return *v, true +} + +// OldAssetID returns the old "asset_id" field's value of the Item entity. +// If the Item object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *ItemMutation) OldAssetID(ctx context.Context) (v int, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldAssetID is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldAssetID requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldAssetID: %w", err) + } + return oldValue.AssetID, nil +} + +// AddAssetID adds i to the "asset_id" field. +func (m *ItemMutation) AddAssetID(i int) { + if m.addasset_id != nil { + *m.addasset_id += i + } else { + m.addasset_id = &i + } +} + +// AddedAssetID returns the value that was added to the "asset_id" field in this mutation. +func (m *ItemMutation) AddedAssetID() (r int, exists bool) { + v := m.addasset_id + if v == nil { + return + } + return *v, true +} + +// ResetAssetID resets all changes to the "asset_id" field. +func (m *ItemMutation) ResetAssetID() { + m.asset_id = nil + m.addasset_id = nil +} + // SetSerialNumber sets the "serial_number" field. func (m *ItemMutation) SetSerialNumber(s string) { m.serial_number = &s @@ -5650,7 +5708,7 @@ func (m *ItemMutation) Type() string { // order to get all numeric fields that were incremented/decremented, call // AddedFields(). func (m *ItemMutation) Fields() []string { - fields := make([]string, 0, 22) + fields := make([]string, 0, 23) if m.created_at != nil { fields = append(fields, item.FieldCreatedAt) } @@ -5678,6 +5736,9 @@ func (m *ItemMutation) Fields() []string { if m.archived != nil { fields = append(fields, item.FieldArchived) } + if m.asset_id != nil { + fields = append(fields, item.FieldAssetID) + } if m.serial_number != nil { fields = append(fields, item.FieldSerialNumber) } @@ -5743,6 +5804,8 @@ func (m *ItemMutation) Field(name string) (ent.Value, bool) { return m.Insured() case item.FieldArchived: return m.Archived() + case item.FieldAssetID: + return m.AssetID() case item.FieldSerialNumber: return m.SerialNumber() case item.FieldModelNumber: @@ -5796,6 +5859,8 @@ func (m *ItemMutation) OldField(ctx context.Context, name string) (ent.Value, er return m.OldInsured(ctx) case item.FieldArchived: return m.OldArchived(ctx) + case item.FieldAssetID: + return m.OldAssetID(ctx) case item.FieldSerialNumber: return m.OldSerialNumber(ctx) case item.FieldModelNumber: @@ -5894,6 +5959,13 @@ func (m *ItemMutation) SetField(name string, value ent.Value) error { } m.SetArchived(v) return nil + case item.FieldAssetID: + v, ok := value.(int) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetAssetID(v) + return nil case item.FieldSerialNumber: v, ok := value.(string) if !ok { @@ -5996,6 +6068,9 @@ func (m *ItemMutation) AddedFields() []string { if m.addquantity != nil { fields = append(fields, item.FieldQuantity) } + if m.addasset_id != nil { + fields = append(fields, item.FieldAssetID) + } if m.addpurchase_price != nil { fields = append(fields, item.FieldPurchasePrice) } @@ -6012,6 +6087,8 @@ func (m *ItemMutation) AddedField(name string) (ent.Value, bool) { switch name { case item.FieldQuantity: return m.AddedQuantity() + case item.FieldAssetID: + return m.AddedAssetID() case item.FieldPurchasePrice: return m.AddedPurchasePrice() case item.FieldSoldPrice: @@ -6032,6 +6109,13 @@ func (m *ItemMutation) AddField(name string, value ent.Value) error { } m.AddQuantity(v) return nil + case item.FieldAssetID: + v, ok := value.(int) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.AddAssetID(v) + return nil case item.FieldPurchasePrice: v, ok := value.(float64) if !ok { @@ -6181,6 +6265,9 @@ func (m *ItemMutation) ResetField(name string) error { case item.FieldArchived: m.ResetArchived() return nil + case item.FieldAssetID: + m.ResetAssetID() + return nil case item.FieldSerialNumber: m.ResetSerialNumber() return nil diff --git a/backend/internal/data/ent/runtime.go b/backend/internal/data/ent/runtime.go index af5dc22..25a7680 100644 --- a/backend/internal/data/ent/runtime.go +++ b/backend/internal/data/ent/runtime.go @@ -275,36 +275,40 @@ func init() { itemDescArchived := itemFields[4].Descriptor() // item.DefaultArchived holds the default value on creation for the archived field. item.DefaultArchived = itemDescArchived.Default.(bool) + // itemDescAssetID is the schema descriptor for asset_id field. + itemDescAssetID := itemFields[5].Descriptor() + // item.DefaultAssetID holds the default value on creation for the asset_id field. + item.DefaultAssetID = itemDescAssetID.Default.(int) // itemDescSerialNumber is the schema descriptor for serial_number field. - itemDescSerialNumber := itemFields[5].Descriptor() + itemDescSerialNumber := itemFields[6].Descriptor() // item.SerialNumberValidator is a validator for the "serial_number" field. It is called by the builders before save. item.SerialNumberValidator = itemDescSerialNumber.Validators[0].(func(string) error) // itemDescModelNumber is the schema descriptor for model_number field. - itemDescModelNumber := itemFields[6].Descriptor() + itemDescModelNumber := itemFields[7].Descriptor() // item.ModelNumberValidator is a validator for the "model_number" field. It is called by the builders before save. item.ModelNumberValidator = itemDescModelNumber.Validators[0].(func(string) error) // itemDescManufacturer is the schema descriptor for manufacturer field. - itemDescManufacturer := itemFields[7].Descriptor() + itemDescManufacturer := itemFields[8].Descriptor() // item.ManufacturerValidator is a validator for the "manufacturer" field. It is called by the builders before save. item.ManufacturerValidator = itemDescManufacturer.Validators[0].(func(string) error) // itemDescLifetimeWarranty is the schema descriptor for lifetime_warranty field. - itemDescLifetimeWarranty := itemFields[8].Descriptor() + itemDescLifetimeWarranty := itemFields[9].Descriptor() // item.DefaultLifetimeWarranty holds the default value on creation for the lifetime_warranty field. item.DefaultLifetimeWarranty = itemDescLifetimeWarranty.Default.(bool) // itemDescWarrantyDetails is the schema descriptor for warranty_details field. - itemDescWarrantyDetails := itemFields[10].Descriptor() + itemDescWarrantyDetails := itemFields[11].Descriptor() // item.WarrantyDetailsValidator is a validator for the "warranty_details" field. It is called by the builders before save. item.WarrantyDetailsValidator = itemDescWarrantyDetails.Validators[0].(func(string) error) // itemDescPurchasePrice is the schema descriptor for purchase_price field. - itemDescPurchasePrice := itemFields[13].Descriptor() + itemDescPurchasePrice := itemFields[14].Descriptor() // item.DefaultPurchasePrice holds the default value on creation for the purchase_price field. item.DefaultPurchasePrice = itemDescPurchasePrice.Default.(float64) // itemDescSoldPrice is the schema descriptor for sold_price field. - itemDescSoldPrice := itemFields[16].Descriptor() + itemDescSoldPrice := itemFields[17].Descriptor() // item.DefaultSoldPrice holds the default value on creation for the sold_price field. item.DefaultSoldPrice = itemDescSoldPrice.Default.(float64) // itemDescSoldNotes is the schema descriptor for sold_notes field. - itemDescSoldNotes := itemFields[17].Descriptor() + itemDescSoldNotes := itemFields[18].Descriptor() // item.SoldNotesValidator is a validator for the "sold_notes" field. It is called by the builders before save. item.SoldNotesValidator = itemDescSoldNotes.Validators[0].(func(string) error) // itemDescID is the schema descriptor for id field. diff --git a/backend/internal/data/ent/schema/item.go b/backend/internal/data/ent/schema/item.go index f7799f4..388566d 100644 --- a/backend/internal/data/ent/schema/item.go +++ b/backend/internal/data/ent/schema/item.go @@ -29,6 +29,7 @@ func (Item) Indexes() []ent.Index { index.Fields("model_number"), index.Fields("serial_number"), index.Fields("archived"), + index.Fields("asset_id"), } } @@ -48,6 +49,8 @@ func (Item) Fields() []ent.Field { Default(false), field.Bool("archived"). Default(false), + field.Int("asset_id"). + Default(0), // ------------------------------------ // item identification diff --git a/backend/internal/data/migrations/migrations/20221113012312_add_asset_id_field.sql b/backend/internal/data/migrations/migrations/20221113012312_add_asset_id_field.sql new file mode 100644 index 0000000..5bcf3ad --- /dev/null +++ b/backend/internal/data/migrations/migrations/20221113012312_add_asset_id_field.sql @@ -0,0 +1,24 @@ +-- disable the enforcement of foreign-keys constraints +PRAGMA foreign_keys = off; +-- create "new_items" table +CREATE TABLE `new_items` (`id` uuid NOT NULL, `created_at` datetime NOT NULL, `updated_at` datetime NOT NULL, `name` text NOT NULL, `description` text NULL, `import_ref` text NULL, `notes` text NULL, `quantity` integer NOT NULL DEFAULT 1, `insured` bool NOT NULL DEFAULT false, `archived` bool NOT NULL DEFAULT false, `asset_id` integer NOT NULL DEFAULT 0, `serial_number` text NULL, `model_number` text NULL, `manufacturer` text NULL, `lifetime_warranty` bool NOT NULL DEFAULT false, `warranty_expires` datetime NULL, `warranty_details` text NULL, `purchase_time` datetime NULL, `purchase_from` text NULL, `purchase_price` real NOT NULL DEFAULT 0, `sold_time` datetime NULL, `sold_to` text NULL, `sold_price` real NOT NULL DEFAULT 0, `sold_notes` text NULL, `group_items` uuid NOT NULL, `item_children` uuid NULL, `location_items` uuid NULL, PRIMARY KEY (`id`), CONSTRAINT `items_groups_items` FOREIGN KEY (`group_items`) REFERENCES `groups` (`id`) ON DELETE CASCADE, CONSTRAINT `items_items_children` FOREIGN KEY (`item_children`) REFERENCES `items` (`id`) ON DELETE SET NULL, CONSTRAINT `items_locations_items` FOREIGN KEY (`location_items`) REFERENCES `locations` (`id`) ON DELETE CASCADE); +-- copy rows from old table "items" to new temporary table "new_items" +INSERT INTO `new_items` (`id`, `created_at`, `updated_at`, `name`, `description`, `import_ref`, `notes`, `quantity`, `insured`, `archived`, `serial_number`, `model_number`, `manufacturer`, `lifetime_warranty`, `warranty_expires`, `warranty_details`, `purchase_time`, `purchase_from`, `purchase_price`, `sold_time`, `sold_to`, `sold_price`, `sold_notes`, `group_items`, `item_children`, `location_items`) SELECT `id`, `created_at`, `updated_at`, `name`, `description`, `import_ref`, `notes`, `quantity`, `insured`, `archived`, `serial_number`, `model_number`, `manufacturer`, `lifetime_warranty`, `warranty_expires`, `warranty_details`, `purchase_time`, `purchase_from`, `purchase_price`, `sold_time`, `sold_to`, `sold_price`, `sold_notes`, `group_items`, `item_children`, `location_items` FROM `items`; +-- drop "items" table after copying rows +DROP TABLE `items`; +-- rename temporary table "new_items" to "items" +ALTER TABLE `new_items` RENAME TO `items`; +-- create index "item_name" to table: "items" +CREATE INDEX `item_name` ON `items` (`name`); +-- create index "item_manufacturer" to table: "items" +CREATE INDEX `item_manufacturer` ON `items` (`manufacturer`); +-- create index "item_model_number" to table: "items" +CREATE INDEX `item_model_number` ON `items` (`model_number`); +-- create index "item_serial_number" to table: "items" +CREATE INDEX `item_serial_number` ON `items` (`serial_number`); +-- create index "item_archived" to table: "items" +CREATE INDEX `item_archived` ON `items` (`archived`); +-- create index "item_asset_id" to table: "items" +CREATE INDEX `item_asset_id` ON `items` (`asset_id`); +-- enable back the enforcement of foreign-keys constraints +PRAGMA foreign_keys = on; diff --git a/backend/internal/data/migrations/migrations/atlas.sum b/backend/internal/data/migrations/migrations/atlas.sum index 2916627..d9c72fb 100644 --- a/backend/internal/data/migrations/migrations/atlas.sum +++ b/backend/internal/data/migrations/migrations/atlas.sum @@ -1,6 +1,7 @@ -h1:i76VRMDIPdcmQtXTe9bzrgITAzLGjjVy9y8XaXIchAs= +h1:z1tbZ3fYByqxL78Z+ov8mfQVjXcwsZeEcT0i+2DZ8a8= 20220929052825_init.sql h1:ZlCqm1wzjDmofeAcSX3jE4h4VcdTNGpRg2eabztDy9Q= 20221001210956_group_invitations.sql h1:YQKJFtE39wFOcRNbZQ/d+ZlHwrcfcsZlcv/pLEYdpjw= 20221009173029_add_user_roles.sql h1:vWmzAfgEWQeGk0Vn70zfVPCcfEZth3E0JcvyKTjpYyU= 20221020043305_allow_nesting_types.sql h1:4AyJpZ7l7SSJtJAQETYY802FHJ64ufYPJTqvwdiGn3M= 20221101041931_add_archived_field.sql h1:L2WxiOh1svRn817cNURgqnEQg6DIcodZ1twK4tvxW94= +20221113012312_add_asset_id_field.sql h1:DjD7e1PS8OfxGBWic8h0nO/X6CNnHEMqQjDCaaQ3M3Q= diff --git a/backend/internal/data/repo/asset_id_type.go b/backend/internal/data/repo/asset_id_type.go new file mode 100644 index 0000000..6aecf9c --- /dev/null +++ b/backend/internal/data/repo/asset_id_type.go @@ -0,0 +1,30 @@ +package repo + +import ( + "bytes" + "fmt" + "strconv" +) + +type AssetID int + +func (aid AssetID) MarshalJSON() ([]byte, error) { + aidStr := fmt.Sprintf("%06d", aid) + aidStr = fmt.Sprintf("%s-%s", aidStr[:3], aidStr[3:]) + return []byte(fmt.Sprintf(`"%s"`, aidStr)), nil + +} + +func (aid *AssetID) UnmarshalJSON(d []byte) error { + d = bytes.Replace(d, []byte(`"`), []byte(``), -1) + d = bytes.Replace(d, []byte(`-`), []byte(``), -1) + + aidInt, err := strconv.Atoi(string(d)) + if err != nil { + return err + } + + *aid = AssetID(aidInt) + return nil + +} diff --git a/backend/internal/data/repo/asset_id_type_test.go b/backend/internal/data/repo/asset_id_type_test.go new file mode 100644 index 0000000..6a692d9 --- /dev/null +++ b/backend/internal/data/repo/asset_id_type_test.go @@ -0,0 +1,115 @@ +package repo + +import ( + "encoding/json" + "reflect" + "testing" +) + +func TestAssetID_MarshalJSON(t *testing.T) { + tests := []struct { + name string + aid AssetID + want []byte + wantErr bool + }{ + { + name: "basic test", + aid: 123, + want: []byte(`"000-123"`), + }, + { + name: "zero test", + aid: 0, + want: []byte(`"000-000"`), + }, + { + name: "large int", + aid: 123456789, + want: []byte(`"123-456789"`), + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := tt.aid.MarshalJSON() + if (err != nil) != tt.wantErr { + t.Errorf("AssetID.MarshalJSON() error = %v, wantErr %v", err, tt.wantErr) + return + } + if !reflect.DeepEqual(got, tt.want) { + t.Errorf("AssetID.MarshalJSON() = %v, want %v", got, tt.want) + } + }) + } +} + +func TestAssetID_UnmarshalJSON(t *testing.T) { + type args struct { + data []byte + } + tests := []struct { + name string + aid *AssetID + args args + want AssetID + wantErr bool + }{ + { + name: "basic test", + aid: new(AssetID), + want: 123, + args: args{ + data: []byte(`{"AssetID":"000123"}`), + }, + }, + { + name: "dashed format", + aid: new(AssetID), + want: 123, + args: args{ + data: []byte(`{"AssetID":"000-123"}`), + }, + }, + { + name: "no leading zeros", + aid: new(AssetID), + want: 123, + args: args{ + data: []byte(`{"AssetID":"123"}`), + }, + }, + { + name: "trailing zeros", + aid: new(AssetID), + want: 123000, + args: args{ + data: []byte(`{"AssetID":"000123000"}`), + }, + }, + { + name: "large int", + aid: new(AssetID), + want: 123456789, + args: args{ + data: []byte(`{"AssetID":"123456789"}`), + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + st := struct { + AssetID AssetID `json:"AssetID"` + }{} + + err := json.Unmarshal(tt.args.data, &st) + if (err != nil) != tt.wantErr { + t.Errorf("AssetID.UnmarshalJSON() error = %v, wantErr %v", err, tt.wantErr) + return + } + + if st.AssetID != tt.want { + t.Errorf("AssetID.UnmarshalJSON() = %v, want %v", st.AssetID, tt.want) + } + }) + } +} diff --git a/backend/internal/data/repo/repo_items.go b/backend/internal/data/repo/repo_items.go index 71619fa..d01c1dc 100644 --- a/backend/internal/data/repo/repo_items.go +++ b/backend/internal/data/repo/repo_items.go @@ -44,6 +44,7 @@ type ( ParentID uuid.UUID `json:"parentId" extensions:"x-nullable"` Name string `json:"name"` Description string `json:"description"` + AssetID AssetID `json:"-"` // Edges LocationID uuid.UUID `json:"locationId"` @@ -52,6 +53,7 @@ type ( ItemUpdate struct { ParentID uuid.UUID `json:"parentId" extensions:"x-nullable,x-omitempty"` ID uuid.UUID `json:"id"` + AssetID AssetID `json:"assetId"` Name string `json:"name"` Description string `json:"description"` Quantity int `json:"quantity"` @@ -107,6 +109,7 @@ type ( ItemOut struct { Parent *ItemSummary `json:"parent,omitempty" extensions:"x-nullable,x-omitempty"` ItemSummary + AssetID AssetID `json:"assetId,string"` SerialNumber string `json:"serialNumber"` ModelNumber string `json:"modelNumber"` @@ -215,6 +218,7 @@ func mapItemOut(item *ent.Item) ItemOut { return ItemOut{ Parent: parent, + AssetID: AssetID(item.AssetID), ItemSummary: mapItemSummary(item), LifetimeWarranty: item.LifetimeWarranty, WarrantyExpires: item.WarrantyExpires, @@ -359,13 +363,53 @@ func (e *ItemsRepository) GetAll(ctx context.Context, gid uuid.UUID) ([]ItemSumm All(ctx)) } +func (e *ItemsRepository) GetAllZeroAssetID(ctx context.Context, GID uuid.UUID) ([]ItemSummary, error) { + q := e.db.Item.Query().Where( + item.HasGroupWith(group.ID(GID)), + item.AssetID(0), + ).Order( + ent.Asc(item.FieldCreatedAt), + ) + + return mapItemsSummaryErr(q.All(ctx)) +} + +func (e *ItemsRepository) GetHighestAssetID(ctx context.Context, GID uuid.UUID) (AssetID, error) { + q := e.db.Item.Query().Where( + item.HasGroupWith(group.ID(GID)), + ).Order( + ent.Desc(item.FieldAssetID), + ).Limit(1) + + result, err := q.First(ctx) + if err != nil { + if ent.IsNotFound(err) { + return 0, nil + } + return 0, err + } + + return AssetID(result.AssetID), nil +} + +func (e *ItemsRepository) SetAssetID(ctx context.Context, GID uuid.UUID, ID uuid.UUID, assetID AssetID) error { + q := e.db.Item.Update().Where( + item.HasGroupWith(group.ID(GID)), + item.ID(ID), + ) + + _, err := q.SetAssetID(int(assetID)).Save(ctx) + return err +} + func (e *ItemsRepository) Create(ctx context.Context, gid uuid.UUID, data ItemCreate) (ItemOut, error) { q := e.db.Item.Create(). SetImportRef(data.ImportRef). SetName(data.Name). SetDescription(data.Description). SetGroupID(gid). - SetLocationID(data.LocationID) + SetLocationID(data.LocationID). + SetAssetID(int(data.AssetID)) if data.LabelIDs != nil && len(data.LabelIDs) > 0 { q.AddLabelIDs(data.LabelIDs...) @@ -414,7 +458,8 @@ func (e *ItemsRepository) UpdateByGroup(ctx context.Context, gid uuid.UUID, data SetInsured(data.Insured). SetWarrantyExpires(data.WarrantyExpires). SetWarrantyDetails(data.WarrantyDetails). - SetQuantity(data.Quantity) + SetQuantity(data.Quantity). + SetAssetID(int(data.AssetID)) currentLabels, err := e.db.Item.Query().Where(item.ID(data.ID)).QueryLabel().All(ctx) if err != nil { diff --git a/backend/internal/sys/config/conf.go b/backend/internal/sys/config/conf.go index 8e55756..5f212eb 100644 --- a/backend/internal/sys/config/conf.go +++ b/backend/internal/sys/config/conf.go @@ -16,15 +16,20 @@ const ( ) type Config struct { - Mode string `yaml:"mode" conf:"default:development"` // development or production - Web WebConfig `yaml:"web"` - Storage Storage `yaml:"storage"` - Log LoggerConf `yaml:"logger"` - Mailer MailerConf `yaml:"mailer"` - Swagger SwaggerConf `yaml:"swagger"` - Demo bool `yaml:"demo"` - AllowRegistration bool `yaml:"disable_registration" conf:"default:true"` - Debug DebugConf `yaml:"debug"` + Mode string `yaml:"mode" conf:"default:development"` // development or production + Web WebConfig `yaml:"web"` + Storage Storage `yaml:"storage"` + Log LoggerConf `yaml:"logger"` + Mailer MailerConf `yaml:"mailer"` + Swagger SwaggerConf `yaml:"swagger"` + Demo bool `yaml:"demo"` + Debug DebugConf `yaml:"debug"` + Options Options `yaml:"options"` +} + +type Options struct { + AllowRegistration bool `yaml:"disable_registration" conf:"default:true"` + AutoIncrementAssetID bool `yaml:"auto_increment_asset_id" conf:"default:true"` } type DebugConf struct { diff --git a/docs/docs/quick-start.md b/docs/docs/quick-start.md index f43df50..3a5fad3 100644 --- a/docs/docs/quick-start.md +++ b/docs/docs/quick-start.md @@ -37,24 +37,25 @@ volumes: ## Env Variables & Configuration -| Variable | Default | Description | -| ------------------------ | ---------------------- | ---------------------------------------------------------------------------------- | -| HBOX_MODE | production | application mode used for runtime behavior can be one of: development, production | -| HBOX_WEB_PORT | 7745 | port to run the web server on, if you're using docker do not change this | -| HBOX_WEB_HOST | | host to run the web server on, if you're using docker do not change this | -| HBOX_ALLOW_REGISTRATION | true | allow users to register themselves | -| HBOX_WEB_MAX_UPLOAD_SIZE | 10 | maximum file upload size supported in MB | -| HBOX_STORAGE_DATA | /data/ | path to the data directory, do not change this if you're using docker | -| HBOX_STORAGE_SQLITE_URL | /data/homebox.db?_fk=1 | sqlite database url, in you're using docker do not change this | -| HBOX_LOG_LEVEL | info | log level to use, can be one of: trace, debug, info, warn, error, critical | -| HBOX_LOG_FORMAT | text | log format to use, can be one of: text, json | -| HBOX_MAILER_HOST | | email host to use, if not set no email provider will be used | -| HBOX_MAILER_PORT | 587 | email port to use | -| HBOX_MAILER_USERNAME | | email user to use | -| HBOX_MAILER_PASSWORD | | email password to use | -| HBOX_MAILER_FROM | | email from address to use | -| HBOX_SWAGGER_HOST | 7745 | swagger host to use, if not set swagger will be disabled | -| HBOX_SWAGGER_SCHEMA | http | swagger schema to use, can be one of: http, https | +| Variable | Default | Description | +| ------------------------------------ | ---------------------- | ---------------------------------------------------------------------------------- | +| HBOX_MODE | production | application mode used for runtime behavior can be one of: development, production | +| HBOX_WEB_PORT | 7745 | port to run the web server on, if you're using docker do not change this | +| HBOX_WEB_HOST | | host to run the web server on, if you're using docker do not change this | +| HBOX_OPTIONS_ALLOW_REGISTRATION | true | allow users to register themselves | +| HBOX_OPTIONS_AUTO_INCREMENT_ASSET_ID | true | auto increments the asset_id field for new items | +| HBOX_WEB_MAX_UPLOAD_SIZE | 10 | maximum file upload size supported in MB | +| HBOX_STORAGE_DATA | /data/ | path to the data directory, do not change this if you're using docker | +| HBOX_STORAGE_SQLITE_URL | /data/homebox.db?_fk=1 | sqlite database url, in you're using docker do not change this | +| HBOX_LOG_LEVEL | info | log level to use, can be one of: trace, debug, info, warn, error, critical | +| HBOX_LOG_FORMAT | text | log format to use, can be one of: text, json | +| HBOX_MAILER_HOST | | email host to use, if not set no email provider will be used | +| HBOX_MAILER_PORT | 587 | email port to use | +| HBOX_MAILER_USERNAME | | email user to use | +| HBOX_MAILER_PASSWORD | | email password to use | +| HBOX_MAILER_FROM | | email from address to use | +| HBOX_SWAGGER_HOST | 7745 | swagger host to use, if not set swagger will be disabled | +| HBOX_SWAGGER_SCHEMA | http | swagger schema to use, can be one of: http, https | !!! tip "CLI Arguments" If you're deploying without docker you can use command line arguments to configure the application. Run `homebox --help` for more information. @@ -63,23 +64,26 @@ volumes: Usage: api [options] [arguments] OPTIONS - --mode/$HBOX_MODE (default: development) - --web-port/$HBOX_WEB_PORT (default: 7745) - --web-host/$HBOX_WEB_HOST - --web-max-upload-size/$HBOX_WEB_MAX_UPLOAD_SIZE (default: 10) - --storage-data/$HBOX_STORAGE_DATA (default: ./.data) - --storage-sqlite-url/$HBOX_STORAGE_SQLITE_URL (default: ./.data/homebox.db?_fk=1) - --log-level/$HBOX_LOG_LEVEL (default: info) - --log-format/$HBOX_LOG_FORMAT (default: text) - --mailer-host/$HBOX_MAILER_HOST - --mailer-port/$HBOX_MAILER_PORT - --mailer-username/$HBOX_MAILER_USERNAME - --mailer-password/$HBOX_MAILER_PASSWORD - --mailer-from/$HBOX_MAILER_FROM - --swagger-host/$HBOX_SWAGGER_HOST (default: localhost:7745) - --swagger-scheme/$HBOX_SWAGGER_SCHEME (default: http) - --demo/$HBOX_DEMO - --allow-registration/$HBOX_ALLOW_REGISTRATION (default: true) + --mode/$HBOX_MODE (default: development) + --web-port/$HBOX_WEB_PORT (default: 7745) + --web-host/$HBOX_WEB_HOST + --web-max-upload-size/$HBOX_WEB_MAX_UPLOAD_SIZE (default: 10) + --storage-data/$HBOX_STORAGE_DATA (default: ./.data) + --storage-sqlite-url/$HBOX_STORAGE_SQLITE_URL (default: ./.data/homebox.db?_fk=1) + --log-level/$HBOX_LOG_LEVEL (default: info) + --log-format/$HBOX_LOG_FORMAT (default: text) + --mailer-host/$HBOX_MAILER_HOST + --mailer-port/$HBOX_MAILER_PORT + --mailer-username/$HBOX_MAILER_USERNAME + --mailer-password/$HBOX_MAILER_PASSWORD + --mailer-from/$HBOX_MAILER_FROM + --swagger-host/$HBOX_SWAGGER_HOST (default: localhost:7745) + --swagger-scheme/$HBOX_SWAGGER_SCHEME (default: http) + --demo/$HBOX_DEMO + --debug-enabled/$HBOX_DEBUG_ENABLED (default: false) + --debug-port/$HBOX_DEBUG_PORT (default: 4000) + --options-allow-registration/$HBOX_OPTIONS_ALLOW_REGISTRATION (default: true) + --options-auto-increment-asset-id/$HBOX_OPTIONS_AUTO_INCREMENT_ASSET_ID (default: true) --help/-h display this help message ``` diff --git a/docs/docs/tips-tricks.md b/docs/docs/tips-tricks.md index 30f9f1c..75501be 100644 --- a/docs/docs/tips-tricks.md +++ b/docs/docs/tips-tricks.md @@ -14,3 +14,21 @@ Custom fields are appended to the main details section of your item. !!! tip Homebox Custom Fields also have special support for URLs. Provide a URL (`https://google.com`) and it will be automatically converted to a clickable link in the UI. Optionally, you can also use markdown syntax to add a custom text to the button. `[Google](https://google.com)` +## Managing Asset IDs + +Homebox provides the option to auto-set asset IDs, this is the default behavior. These can be used for tracking assets with printable tags or labels. You can disable this behavior via a command line flag or ENV variable. See [configuration](../quick-start#env-variables-configuration) for more details. + +Example ID: `000-001` + +Asset IDs are partially managed by Homebox, but have a flexible implementation to allow for unique use cases. ID's are non-unique at the database level so there is nothing stopping a user from manually setting duplicate IDs for various items. There are two recommended approaches to manage Asset IDs + +### 1. Auto Incrementing IDs + +This is the default behavior and likely to one to experience the most consistent behavior. Whenever creating or importing an item, that items receives the next available ID. This is the most consistent approach and is recommended for most users. + +### 2. Auto Incrementing ID's with Reset + +In some cases you may want to skip some items such as consumables, or items that are loosely tracked. In this case, we recommend that you leave auto-incrementing ID's enabled _however_ when you create a new item that you want to skip, you can go to that item and reset the ID to 0. This will remove it from the auto-incrementing sequence and the next item will receive the next available ID. + +!!! tip + If you're migrating from an older version there is a action on the users profile page to assign IDs to all items. This will assign the next available ID to all items in the order of creation. You should _only_ do this once during the migration process. You should be especially cautious of this action if you're using the reset feature described in option number 2 diff --git a/frontend/lib/api/classes/actions.ts b/frontend/lib/api/classes/actions.ts new file mode 100644 index 0000000..be892b3 --- /dev/null +++ b/frontend/lib/api/classes/actions.ts @@ -0,0 +1,10 @@ +import { BaseAPI, route } from "../base"; +import { EnsureAssetIDResult } from "../types/data-contracts"; + +export class ActionsAPI extends BaseAPI { + ensureAssetIDs() { + return this.http.post({ + url: route("/actions/ensure-asset-ids"), + }); + } +} diff --git a/frontend/lib/api/types/data-contracts.ts b/frontend/lib/api/types/data-contracts.ts index 7fd055c..9829f14 100644 --- a/frontend/lib/api/types/data-contracts.ts +++ b/frontend/lib/api/types/data-contracts.ts @@ -71,6 +71,9 @@ export interface ItemField { export interface ItemOut { archived: boolean; + + /** @example 0 */ + assetId: string; attachments: ItemAttachment[]; children: ItemSummary[]; createdAt: Date; @@ -131,6 +134,7 @@ export interface ItemSummary { export interface ItemUpdate { archived: boolean; + assetId: string; description: string; fields: ItemField[]; id: string; @@ -300,6 +304,10 @@ export interface ChangePassword { new: string; } +export interface EnsureAssetIDResult { + completed: number; +} + export interface GroupInvitation { expiresAt: Date; token: string; diff --git a/frontend/lib/api/user.ts b/frontend/lib/api/user.ts index d714bef..5c4940f 100644 --- a/frontend/lib/api/user.ts +++ b/frontend/lib/api/user.ts @@ -4,6 +4,7 @@ import { LabelsApi } from "./classes/labels"; import { LocationsApi } from "./classes/locations"; import { GroupApi } from "./classes/group"; import { UserApi } from "./classes/users"; +import { ActionsAPI } from "./classes/actions"; import { Requests } from "~~/lib/requests"; export class UserClient extends BaseAPI { @@ -12,6 +13,7 @@ export class UserClient extends BaseAPI { items: ItemsApi; group: GroupApi; user: UserApi; + actions: ActionsAPI; constructor(requests: Requests) { super(requests); @@ -21,6 +23,7 @@ export class UserClient extends BaseAPI { this.items = new ItemsApi(requests); this.group = new GroupApi(requests); this.user = new UserApi(requests); + this.actions = new ActionsAPI(requests); Object.freeze(this); } diff --git a/frontend/pages/item/[id]/edit.vue b/frontend/pages/item/[id]/edit.vue index a43abc6..0ed53d3 100644 --- a/frontend/pages/item/[id]/edit.vue +++ b/frontend/pages/item/[id]/edit.vue @@ -120,6 +120,11 @@ label: "Archived", ref: "archived", }, + { + type: "text", + label: "Asset ID", + ref: "assetId", + }, ]; const purchaseFields: FormField[] = [ diff --git a/frontend/pages/item/[id]/index.vue b/frontend/pages/item/[id]/index.vue index 03ddfc5..cf67854 100644 --- a/frontend/pages/item/[id]/index.vue +++ b/frontend/pages/item/[id]/index.vue @@ -70,6 +70,19 @@ ); }); + const assetID = computed
(() => { + if (item.value?.assetId === "000-000") { + return []; + } + + return [ + { + name: "Asset ID", + text: item.value?.assetId, + }, + ]; + }); + const itemDetails = computed
(() => { return [ { @@ -100,6 +113,7 @@ name: "Notes", text: item.value?.notes, }, + ...assetID.value, ...item.value.fields.map(field => { /** * Support Special URL Syntax diff --git a/frontend/pages/profile.vue b/frontend/pages/profile.vue index 5abb0f0..f2f32fe 100644 --- a/frontend/pages/profile.vue +++ b/frontend/pages/profile.vue @@ -163,6 +163,25 @@ passwordChange.current = ""; passwordChange.loading = false; } + + async function ensureAssetIDs() { + const { isCanceled } = await confirm.open( + "Are you sure you want to ensure all assets have an ID? This will take a while and cannot be undone." + ); + + if (isCanceled) { + return; + } + + const result = await api.actions.ensureAssetIDs(); + + if (result.error) { + notify.error("Failed to ensure asset IDs."); + return; + } + + notify.success(`${result.data.completed} assets have been updated.`); + } + + + + diff --git a/frontend/package.json b/frontend/package.json index 5c6eb39..a06db47 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -37,6 +37,8 @@ "@vueuse/nuxt": "^9.1.1", "autoprefixer": "^10.4.8", "daisyui": "^2.24.0", + "dompurify": "^2.4.1", + "markdown-it": "^13.0.1", "pinia": "^2.0.21", "postcss": "^8.4.16", "tailwindcss": "^3.1.8", diff --git a/frontend/pages/item/[id]/index.vue b/frontend/pages/item/[id]/index.vue index cf67854..e3d6afb 100644 --- a/frontend/pages/item/[id]/index.vue +++ b/frontend/pages/item/[id]/index.vue @@ -1,5 +1,5 @@ diff --git a/frontend/composables/use-api.ts b/frontend/composables/use-api.ts index 69da2ed..cd06588 100644 --- a/frontend/composables/use-api.ts +++ b/frontend/composables/use-api.ts @@ -43,5 +43,5 @@ export function useUserApi(): UserClient { requests.addResponseInterceptor(observer.handler); } - return new UserClient(requests); + return new UserClient(requests, authStore.attachmentToken); } diff --git a/frontend/lib/api/base/base-api.ts b/frontend/lib/api/base/base-api.ts index b48d10b..6db8951 100644 --- a/frontend/lib/api/base/base-api.ts +++ b/frontend/lib/api/base/base-api.ts @@ -27,9 +27,21 @@ export function parseDate(obj: T, keys: Array = []): T { export class BaseAPI { http: Requests; + attachmentToken: string; - constructor(requests: Requests) { + constructor(requests: Requests, attachmentToken = "") { this.http = requests; + this.attachmentToken = attachmentToken; + } + + // if a attachmentToken is present it will be added to URL as a query param + // this is done with a simple appending of the query param to the URL. If your + // URL already has a query param, this will not work. + authURL(url: string): string { + if (this.attachmentToken) { + return `/api/v1${url}?access_token=${this.attachmentToken}`; + } + return url; } /** diff --git a/frontend/lib/api/classes/items.ts b/frontend/lib/api/classes/items.ts index a97152d..ee18f01 100644 --- a/frontend/lib/api/classes/items.ts +++ b/frontend/lib/api/classes/items.ts @@ -1,13 +1,6 @@ import { BaseAPI, route } from "../base"; import { parseDate } from "../base/base-api"; -import { - ItemAttachmentToken, - ItemAttachmentUpdate, - ItemCreate, - ItemOut, - ItemSummary, - ItemUpdate, -} from "../types/data-contracts"; +import { ItemAttachmentUpdate, ItemCreate, ItemOut, ItemSummary, ItemUpdate } from "../types/data-contracts"; import { AttachmentTypes, PaginationResult } from "../types/non-generated"; export type ItemsQuery = { @@ -79,18 +72,6 @@ export class ItemsApi extends BaseAPI { }); } - async getAttachmentUrl(id: string, attachmentId: string): Promise { - const payload = await this.http.get({ - url: route(`/items/${id}/attachments/${attachmentId}`), - }); - - if (!payload.data) { - return ""; - } - - return route(`/items/${id}/attachments/download`, { token: payload.data.token }); - } - async deleteAttachment(id: string, attachmentId: string) { return await this.http.delete({ url: route(`/items/${id}/attachments/${attachmentId}`) }); } diff --git a/frontend/lib/api/types/data-contracts.ts b/frontend/lib/api/types/data-contracts.ts index 9829f14..464b8e7 100644 --- a/frontend/lib/api/types/data-contracts.ts +++ b/frontend/lib/api/types/data-contracts.ts @@ -324,6 +324,7 @@ export interface ItemAttachmentToken { } export interface TokenResponse { + attachmentToken: string; expiresAt: Date; token: string; } diff --git a/frontend/lib/api/user.ts b/frontend/lib/api/user.ts index 5c4940f..31538a8 100644 --- a/frontend/lib/api/user.ts +++ b/frontend/lib/api/user.ts @@ -15,8 +15,8 @@ export class UserClient extends BaseAPI { user: UserApi; actions: ActionsAPI; - constructor(requests: Requests) { - super(requests); + constructor(requests: Requests, attachmentToken: string) { + super(requests, attachmentToken); this.locations = new LocationsApi(requests); this.labels = new LabelsApi(requests); diff --git a/frontend/pages/index.vue b/frontend/pages/index.vue index 44183c6..070fa05 100644 --- a/frontend/pages/index.vue +++ b/frontend/pages/index.vue @@ -105,6 +105,7 @@ authStore.$patch({ token: data.token, expires: data.expiresAt, + attachmentToken: data.attachmentToken, }); navigateTo("/home"); diff --git a/frontend/pages/item/[id]/index.vue b/frontend/pages/item/[id]/index.vue index e3d6afb..256ff0c 100644 --- a/frontend/pages/item/[id]/index.vue +++ b/frontend/pages/item/[id]/index.vue @@ -27,17 +27,32 @@ }); type FilteredAttachments = { - photos: ItemAttachment[]; attachments: ItemAttachment[]; warranty: ItemAttachment[]; manuals: ItemAttachment[]; receipts: ItemAttachment[]; }; + type Photo = { + src: string; + }; + + const photos = computed(() => { + return ( + item.value?.attachments.reduce((acc, cur) => { + if (cur.type === "photo") { + acc.push({ + src: api.authURL(`/items/${item.value.id}/attachments/${cur.id}`), + }); + } + return acc; + }, [] as Photo[]) || [] + ); + }); + const attachments = computed(() => { if (!item.value) { return { - photos: [], attachments: [], manuals: [], warranty: [], @@ -48,8 +63,9 @@ return item.value.attachments.reduce( (acc, attachment) => { if (attachment.type === "photo") { - acc.photos.push(attachment); - } else if (attachment.type === "warranty") { + return acc; + } + if (attachment.type === "warranty") { acc.warranty.push(attachment); } else if (attachment.type === "manual") { acc.manuals.push(attachment); @@ -61,7 +77,6 @@ return acc; }, { - photos: [] as ItemAttachment[], attachments: [] as ItemAttachment[], warranty: [] as ItemAttachment[], manuals: [] as ItemAttachment[], @@ -144,7 +159,6 @@ } return ( - attachments.value.photos.length > 0 || attachments.value.attachments.length > 0 || attachments.value.warranty.length > 0 || attachments.value.manuals.length > 0 || @@ -163,10 +177,6 @@ }); }; - if (attachments.value.photos.length > 0) { - push("Photos"); - } - if (attachments.value.attachments.length > 0) { push("Attachments"); } @@ -292,10 +302,43 @@ toast.success("Item deleted"); navigateTo("/home"); } + + const refDialog = ref(); + const dialoged = reactive({ + src: "", + }); + + function openDialog(img: Photo) { + refDialog.value.showModal(); + dialoged.src = img.src; + } + + function closeDialog() { + refDialog.value.close(); + } + + const refDialogBody = ref(); + onClickOutside(refDialogBody, () => { + closeDialog(); + }); - + + diff --git a/frontend/stores/auth.ts b/frontend/stores/auth.ts index a7e641f..9b72901 100644 --- a/frontend/stores/auth.ts +++ b/frontend/stores/auth.ts @@ -6,6 +6,7 @@ import { UserOut } from "~~/lib/api/types/data-contracts"; export const useAuthStore = defineStore("auth", { state: () => ({ token: useLocalStorage("pinia/auth/token", ""), + attachmentToken: useLocalStorage("pinia/auth/attachmentToken", ""), expires: useLocalStorage("pinia/auth/expires", ""), self: null as UserOut | null, }), @@ -27,6 +28,7 @@ export const useAuthStore = defineStore("auth", { const result = await api.user.logout(); this.token = ""; + this.attachmentToken = ""; this.expires = ""; this.self = null; From d6da63187b091488866e48d74da923d5266d6537 Mon Sep 17 00:00:00 2001 From: Hayden <64056131+hay-kot@users.noreply.github.com> Date: Mon, 5 Dec 2022 12:36:32 -0900 Subject: [PATCH 014/350] feat: new homepage statistic API's (#167) * add date format and orDefault helpers * introduce new statistics calculations queries * rework statistics endpoints * code generation * fix styles on photo card * label and location aggregation endpoints * code-gen * cleanup parser and defaults * remove debug point * setup E2E Testing * linters * formatting * fmt plus name support on time series data * code gen --- .vscode/settings.json | 15 +- backend/app/api/handlers/v1/v1_ctrl_group.go | 20 -- .../app/api/handlers/v1/v1_ctrl_statistics.go | 109 ++++++++++ backend/app/api/routes.go | 3 + backend/app/api/static/docs/docs.go | 190 ++++++++++++++---- backend/app/api/static/docs/swagger.json | 190 ++++++++++++++---- backend/app/api/static/docs/swagger.yaml | 119 ++++++++--- .../internal/data/migrations/migrations.go | 1 - backend/internal/data/repo/query_helpers.go | 18 ++ backend/internal/data/repo/repo_group.go | 169 +++++++++++++++- backend/internal/data/repo/repo_group_test.go | 2 +- frontend/lib/api/__test__/user/stats.test.ts | 142 +++++++++++++ frontend/lib/api/classes/group.ts | 8 +- frontend/lib/api/classes/items.ts | 2 +- frontend/lib/api/classes/stats.ts | 42 ++++ frontend/lib/api/types/data-contracts.ts | 22 ++ frontend/lib/api/user.ts | 3 + frontend/pages/home.vue | 2 +- frontend/pages/item/[id]/index.vue | 17 +- 19 files changed, 925 insertions(+), 149 deletions(-) create mode 100644 backend/app/api/handlers/v1/v1_ctrl_statistics.go create mode 100644 backend/internal/data/repo/query_helpers.go create mode 100644 frontend/lib/api/__test__/user/stats.test.ts create mode 100644 frontend/lib/api/classes/stats.ts diff --git a/.vscode/settings.json b/.vscode/settings.json index b330533..f05ebc8 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,7 +1,4 @@ { - "editor.codeActionsOnSave": { - "source.fixAll.eslint": true - }, "yaml.schemas": { "https://squidfunk.github.io/mkdocs-material/schema.json": "mkdocs.yml" }, @@ -13,5 +10,15 @@ }, "cSpell.words": [ "debughandlers" - ] + ], + // use ESLint to format code on save + "editor.formatOnSave": true, + "editor.defaultFormatter": "dbaeumer.vscode-eslint", + "editor.codeActionsOnSave": { + "source.fixAll.eslint": true + }, + "[typescript]": { + "editor.defaultFormatter": "dbaeumer.vscode-eslint" + }, + } diff --git a/backend/app/api/handlers/v1/v1_ctrl_group.go b/backend/app/api/handlers/v1/v1_ctrl_group.go index b27622d..a3e8992 100644 --- a/backend/app/api/handlers/v1/v1_ctrl_group.go +++ b/backend/app/api/handlers/v1/v1_ctrl_group.go @@ -24,26 +24,6 @@ type ( } ) -// HandleGroupGet godoc -// @Summary Get the current user's group -// @Tags Group -// @Produce json -// @Success 200 {object} repo.GroupStatistics -// @Router /v1/groups/statistics [Get] -// @Security Bearer -func (ctrl *V1Controller) HandleGroupStatistics() server.HandlerFunc { - return func(w http.ResponseWriter, r *http.Request) error { - ctx := services.NewContext(r.Context()) - - stats, err := ctrl.repo.Groups.GroupStatistics(ctx, ctx.GID) - if err != nil { - return validate.NewRequestError(err, http.StatusInternalServerError) - } - - return server.Respond(w, http.StatusOK, stats) - } -} - // HandleGroupGet godoc // @Summary Get the current user's group // @Tags Group diff --git a/backend/app/api/handlers/v1/v1_ctrl_statistics.go b/backend/app/api/handlers/v1/v1_ctrl_statistics.go new file mode 100644 index 0000000..6c09bc6 --- /dev/null +++ b/backend/app/api/handlers/v1/v1_ctrl_statistics.go @@ -0,0 +1,109 @@ +package v1 + +import ( + "net/http" + "time" + + "github.com/hay-kot/homebox/backend/internal/core/services" + "github.com/hay-kot/homebox/backend/internal/sys/validate" + "github.com/hay-kot/homebox/backend/pkgs/server" +) + +// HandleGroupGet godoc +// @Summary Get the current user's group statistics +// @Tags Statistics +// @Produce json +// @Success 200 {object} []repo.TotalsByOrganizer +// @Router /v1/groups/statistics/locations [GET] +// @Security Bearer +func (ctrl *V1Controller) HandleGroupStatisticsLocations() server.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) error { + ctx := services.NewContext(r.Context()) + + stats, err := ctrl.repo.Groups.StatsLocationsByPurchasePrice(ctx, ctx.GID) + if err != nil { + return validate.NewRequestError(err, http.StatusInternalServerError) + } + + return server.Respond(w, http.StatusOK, stats) + } +} + +// HandleGroupGet godoc +// @Summary Get the current user's group statistics +// @Tags Statistics +// @Produce json +// @Success 200 {object} []repo.TotalsByOrganizer +// @Router /v1/groups/statistics/labels [GET] +// @Security Bearer +func (ctrl *V1Controller) HandleGroupStatisticsLabels() server.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) error { + ctx := services.NewContext(r.Context()) + + stats, err := ctrl.repo.Groups.StatsLabelsByPurchasePrice(ctx, ctx.GID) + if err != nil { + return validate.NewRequestError(err, http.StatusInternalServerError) + } + + return server.Respond(w, http.StatusOK, stats) + } +} + +// HandleGroupGet godoc +// @Summary Get the current user's group statistics +// @Tags Statistics +// @Produce json +// @Success 200 {object} repo.GroupStatistics +// @Router /v1/groups/statistics [GET] +// @Security Bearer +func (ctrl *V1Controller) HandleGroupStatistics() server.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) error { + ctx := services.NewContext(r.Context()) + + stats, err := ctrl.repo.Groups.StatsGroup(ctx, ctx.GID) + if err != nil { + return validate.NewRequestError(err, http.StatusInternalServerError) + } + + return server.Respond(w, http.StatusOK, stats) + } +} + +// HandleGroupGet godoc +// @Summary Queries the changes overtime of the purchase price over time +// @Tags Statistics +// @Produce json +// @Success 200 {object} repo.ValueOverTime +// @Param start query string false "start date" +// @Param end query string false "end date" +// @Router /v1/groups/statistics/purchase-price [GET] +// @Security Bearer +func (ctrl *V1Controller) HandleGroupStatisticsPriceOverTime() server.HandlerFunc { + parseDate := func(datestr string, defaultDate time.Time) (time.Time, error) { + if datestr == "" { + return defaultDate, nil + } + return time.Parse("2006-01-02", datestr) + } + + return func(w http.ResponseWriter, r *http.Request) error { + ctx := services.NewContext(r.Context()) + + startDate, err := parseDate(r.URL.Query().Get("start"), time.Now().AddDate(0, -1, 0)) + if err != nil { + return validate.NewRequestError(err, http.StatusBadRequest) + } + + endDate, err := parseDate(r.URL.Query().Get("end"), time.Now()) + if err != nil { + return validate.NewRequestError(err, http.StatusBadRequest) + } + + stats, err := ctrl.repo.Groups.StatsPurchasePrice(ctx, ctx.GID, startDate, endDate) + if err != nil { + return validate.NewRequestError(err, http.StatusInternalServerError) + } + + return server.Respond(w, http.StatusOK, stats) + } +} diff --git a/backend/app/api/routes.go b/backend/app/api/routes.go index e5a7948..1a42aeb 100644 --- a/backend/app/api/routes.go +++ b/backend/app/api/routes.go @@ -79,6 +79,9 @@ func (a *app) mountRoutes(repos *repo.AllRepos) { a.server.Post(v1Base("/groups/invitations"), v1Ctrl.HandleGroupInvitationsCreate(), userMW...) a.server.Get(v1Base("/groups/statistics"), v1Ctrl.HandleGroupStatistics(), userMW...) + a.server.Get(v1Base("/groups/statistics/purchase-price"), v1Ctrl.HandleGroupStatisticsPriceOverTime(), userMW...) + a.server.Get(v1Base("/groups/statistics/locations"), v1Ctrl.HandleGroupStatisticsLocations(), userMW...) + a.server.Get(v1Base("/groups/statistics/labels"), v1Ctrl.HandleGroupStatisticsLabels(), userMW...) // TODO: I don't like /groups being the URL for users a.server.Get(v1Base("/groups"), v1Ctrl.HandleGroupGet(), userMW...) diff --git a/backend/app/api/static/docs/docs.go b/backend/app/api/static/docs/docs.go index 6e8cebd..632b9e5 100644 --- a/backend/app/api/static/docs/docs.go +++ b/backend/app/api/static/docs/docs.go @@ -148,9 +148,9 @@ const docTemplate = `{ "application/json" ], "tags": [ - "Group" + "Statistics" ], - "summary": "Get the current user's group", + "summary": "Get the current user's group statistics", "responses": { "200": { "description": "OK", @@ -161,6 +161,98 @@ const docTemplate = `{ } } }, + "/v1/groups/statistics/labels": { + "get": { + "security": [ + { + "Bearer": [] + } + ], + "produces": [ + "application/json" + ], + "tags": [ + "Statistics" + ], + "summary": "Get the current user's group statistics", + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/repo.TotalsByOrganizer" + } + } + } + } + } + }, + "/v1/groups/statistics/locations": { + "get": { + "security": [ + { + "Bearer": [] + } + ], + "produces": [ + "application/json" + ], + "tags": [ + "Statistics" + ], + "summary": "Get the current user's group statistics", + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/repo.TotalsByOrganizer" + } + } + } + } + } + }, + "/v1/groups/statistics/purchase-price": { + "get": { + "security": [ + { + "Bearer": [] + } + ], + "produces": [ + "application/json" + ], + "tags": [ + "Statistics" + ], + "summary": "Queries the changes overtime of the purchase price over time", + "parameters": [ + { + "type": "string", + "description": "start date", + "name": "start", + "in": "query" + }, + { + "type": "string", + "description": "end date", + "name": "end", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/repo.ValueOverTime" + } + } + } + } + }, "/v1/items": { "get": { "security": [ @@ -449,43 +541,6 @@ const docTemplate = `{ } } }, - "/v1/items/{id}/attachments/download": { - "get": { - "security": [ - { - "Bearer": [] - } - ], - "produces": [ - "application/octet-stream" - ], - "tags": [ - "Items Attachments" - ], - "summary": "retrieves an attachment for an item", - "parameters": [ - { - "type": "string", - "description": "Item ID", - "name": "id", - "in": "path", - "required": true - }, - { - "type": "string", - "description": "Attachment token", - "name": "token", - "in": "query", - "required": true - } - ], - "responses": { - "200": { - "description": "OK" - } - } - } - }, "/v1/items/{id}/attachments/{attachment_id}": { "get": { "security": [ @@ -1237,6 +1292,9 @@ const docTemplate = `{ "repo.GroupStatistics": { "type": "object", "properties": { + "totalItemPrice": { + "type": "number" + }, "totalItems": { "type": "integer" }, @@ -1248,6 +1306,9 @@ const docTemplate = `{ }, "totalUsers": { "type": "integer" + }, + "totalWithWarranty": { + "type": "integer" } } }, @@ -1784,6 +1845,20 @@ const docTemplate = `{ } } }, + "repo.TotalsByOrganizer": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "name": { + "type": "string" + }, + "total": { + "type": "number" + } + } + }, "repo.UserOut": { "type": "object", "properties": { @@ -1821,6 +1896,43 @@ const docTemplate = `{ } } }, + "repo.ValueOverTime": { + "type": "object", + "properties": { + "end": { + "type": "string" + }, + "entries": { + "type": "array", + "items": { + "$ref": "#/definitions/repo.ValueOverTimeEntry" + } + }, + "start": { + "type": "string" + }, + "valueAtEnd": { + "type": "number" + }, + "valueAtStart": { + "type": "number" + } + } + }, + "repo.ValueOverTimeEntry": { + "type": "object", + "properties": { + "date": { + "type": "string" + }, + "name": { + "type": "string" + }, + "value": { + "type": "number" + } + } + }, "server.ErrorResponse": { "type": "object", "properties": { diff --git a/backend/app/api/static/docs/swagger.json b/backend/app/api/static/docs/swagger.json index f09cffa..69ba931 100644 --- a/backend/app/api/static/docs/swagger.json +++ b/backend/app/api/static/docs/swagger.json @@ -140,9 +140,9 @@ "application/json" ], "tags": [ - "Group" + "Statistics" ], - "summary": "Get the current user's group", + "summary": "Get the current user's group statistics", "responses": { "200": { "description": "OK", @@ -153,6 +153,98 @@ } } }, + "/v1/groups/statistics/labels": { + "get": { + "security": [ + { + "Bearer": [] + } + ], + "produces": [ + "application/json" + ], + "tags": [ + "Statistics" + ], + "summary": "Get the current user's group statistics", + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/repo.TotalsByOrganizer" + } + } + } + } + } + }, + "/v1/groups/statistics/locations": { + "get": { + "security": [ + { + "Bearer": [] + } + ], + "produces": [ + "application/json" + ], + "tags": [ + "Statistics" + ], + "summary": "Get the current user's group statistics", + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/repo.TotalsByOrganizer" + } + } + } + } + } + }, + "/v1/groups/statistics/purchase-price": { + "get": { + "security": [ + { + "Bearer": [] + } + ], + "produces": [ + "application/json" + ], + "tags": [ + "Statistics" + ], + "summary": "Queries the changes overtime of the purchase price over time", + "parameters": [ + { + "type": "string", + "description": "start date", + "name": "start", + "in": "query" + }, + { + "type": "string", + "description": "end date", + "name": "end", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/repo.ValueOverTime" + } + } + } + } + }, "/v1/items": { "get": { "security": [ @@ -441,43 +533,6 @@ } } }, - "/v1/items/{id}/attachments/download": { - "get": { - "security": [ - { - "Bearer": [] - } - ], - "produces": [ - "application/octet-stream" - ], - "tags": [ - "Items Attachments" - ], - "summary": "retrieves an attachment for an item", - "parameters": [ - { - "type": "string", - "description": "Item ID", - "name": "id", - "in": "path", - "required": true - }, - { - "type": "string", - "description": "Attachment token", - "name": "token", - "in": "query", - "required": true - } - ], - "responses": { - "200": { - "description": "OK" - } - } - } - }, "/v1/items/{id}/attachments/{attachment_id}": { "get": { "security": [ @@ -1229,6 +1284,9 @@ "repo.GroupStatistics": { "type": "object", "properties": { + "totalItemPrice": { + "type": "number" + }, "totalItems": { "type": "integer" }, @@ -1240,6 +1298,9 @@ }, "totalUsers": { "type": "integer" + }, + "totalWithWarranty": { + "type": "integer" } } }, @@ -1776,6 +1837,20 @@ } } }, + "repo.TotalsByOrganizer": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "name": { + "type": "string" + }, + "total": { + "type": "number" + } + } + }, "repo.UserOut": { "type": "object", "properties": { @@ -1813,6 +1888,43 @@ } } }, + "repo.ValueOverTime": { + "type": "object", + "properties": { + "end": { + "type": "string" + }, + "entries": { + "type": "array", + "items": { + "$ref": "#/definitions/repo.ValueOverTimeEntry" + } + }, + "start": { + "type": "string" + }, + "valueAtEnd": { + "type": "number" + }, + "valueAtStart": { + "type": "number" + } + } + }, + "repo.ValueOverTimeEntry": { + "type": "object", + "properties": { + "date": { + "type": "string" + }, + "name": { + "type": "string" + }, + "value": { + "type": "number" + } + } + }, "server.ErrorResponse": { "type": "object", "properties": { diff --git a/backend/app/api/static/docs/swagger.yaml b/backend/app/api/static/docs/swagger.yaml index 1e3b528..802fdf8 100644 --- a/backend/app/api/static/docs/swagger.yaml +++ b/backend/app/api/static/docs/swagger.yaml @@ -24,6 +24,8 @@ definitions: type: object repo.GroupStatistics: properties: + totalItemPrice: + type: number totalItems: type: integer totalLabels: @@ -32,6 +34,8 @@ definitions: type: integer totalUsers: type: integer + totalWithWarranty: + type: integer type: object repo.GroupUpdate: properties: @@ -392,6 +396,15 @@ definitions: total: type: integer type: object + repo.TotalsByOrganizer: + properties: + id: + type: string + name: + type: string + total: + type: number + type: object repo.UserOut: properties: email: @@ -416,6 +429,30 @@ definitions: name: type: string type: object + repo.ValueOverTime: + properties: + end: + type: string + entries: + items: + $ref: '#/definitions/repo.ValueOverTimeEntry' + type: array + start: + type: string + valueAtEnd: + type: number + valueAtStart: + type: number + type: object + repo.ValueOverTimeEntry: + properties: + date: + type: string + name: + type: string + value: + type: number + type: object server.ErrorResponse: properties: error: @@ -608,9 +645,64 @@ paths: $ref: '#/definitions/repo.GroupStatistics' security: - Bearer: [] - summary: Get the current user's group + summary: Get the current user's group statistics tags: - - Group + - Statistics + /v1/groups/statistics/labels: + get: + produces: + - application/json + responses: + "200": + description: OK + schema: + items: + $ref: '#/definitions/repo.TotalsByOrganizer' + type: array + security: + - Bearer: [] + summary: Get the current user's group statistics + tags: + - Statistics + /v1/groups/statistics/locations: + get: + produces: + - application/json + responses: + "200": + description: OK + schema: + items: + $ref: '#/definitions/repo.TotalsByOrganizer' + type: array + security: + - Bearer: [] + summary: Get the current user's group statistics + tags: + - Statistics + /v1/groups/statistics/purchase-price: + get: + parameters: + - description: start date + in: query + name: start + type: string + - description: end date + in: query + name: end + type: string + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/repo.ValueOverTime' + security: + - Bearer: [] + summary: Queries the changes overtime of the purchase price over time + tags: + - Statistics /v1/items: get: parameters: @@ -846,29 +938,6 @@ paths: summary: retrieves an attachment for an item tags: - Items Attachments - /v1/items/{id}/attachments/download: - get: - parameters: - - description: Item ID - in: path - name: id - required: true - type: string - - description: Attachment token - in: query - name: token - required: true - type: string - produces: - - application/octet-stream - responses: - "200": - description: OK - security: - - Bearer: [] - summary: retrieves an attachment for an item - tags: - - Items Attachments /v1/items/import: post: parameters: diff --git a/backend/internal/data/migrations/migrations.go b/backend/internal/data/migrations/migrations.go index 83354aa..5fcb8e3 100644 --- a/backend/internal/data/migrations/migrations.go +++ b/backend/internal/data/migrations/migrations.go @@ -14,7 +14,6 @@ var Files embed.FS // should be called when the migrations are no longer needed. func Write(temp string) error { err := os.MkdirAll(temp, 0755) - if err != nil { return err } diff --git a/backend/internal/data/repo/query_helpers.go b/backend/internal/data/repo/query_helpers.go new file mode 100644 index 0000000..2205d81 --- /dev/null +++ b/backend/internal/data/repo/query_helpers.go @@ -0,0 +1,18 @@ +package repo + +import "time" + +func sqliteDateFormat(t time.Time) string { + return t.Format("2006-01-02 15:04:05") +} + +// orDefault returns the value of the pointer if it is not nil, otherwise it returns the default value +// +// This is used for nullable or potentially nullable fields (or aggregates) in the database when running +// queries. If the field is null, the pointer will be nil, so we return the default value instead. +func orDefault[T any](v *T, def T) T { + if v == nil { + return def + } + return *v +} diff --git a/backend/internal/data/repo/repo_group.go b/backend/internal/data/repo/repo_group.go index 9a9ef7a..8e5ea14 100644 --- a/backend/internal/data/repo/repo_group.go +++ b/backend/internal/data/repo/repo_group.go @@ -5,10 +5,14 @@ import ( "strings" "time" + "entgo.io/ent/dialect/sql" "github.com/google/uuid" "github.com/hay-kot/homebox/backend/internal/data/ent" "github.com/hay-kot/homebox/backend/internal/data/ent/group" "github.com/hay-kot/homebox/backend/internal/data/ent/groupinvitationtoken" + "github.com/hay-kot/homebox/backend/internal/data/ent/item" + "github.com/hay-kot/homebox/backend/internal/data/ent/label" + "github.com/hay-kot/homebox/backend/internal/data/ent/location" ) type GroupRepository struct { @@ -41,11 +45,34 @@ type ( Uses int `json:"uses"` Group Group `json:"group"` } + GroupStatistics struct { - TotalUsers int `json:"totalUsers"` - TotalItems int `json:"totalItems"` - TotalLocations int `json:"totalLocations"` - TotalLabels int `json:"totalLabels"` + TotalUsers int `json:"totalUsers"` + TotalItems int `json:"totalItems"` + TotalLocations int `json:"totalLocations"` + TotalLabels int `json:"totalLabels"` + TotalItemPrice float64 `json:"totalItemPrice"` + TotalWithWarranty int `json:"totalWithWarranty"` + } + + ValueOverTimeEntry struct { + Date time.Time `json:"date"` + Value float64 `json:"value"` + Name string `json:"name"` + } + + ValueOverTime struct { + PriceAtStart float64 `json:"valueAtStart"` + PriceAtEnd float64 `json:"valueAtEnd"` + Start time.Time `json:"start"` + End time.Time `json:"end"` + Entries []ValueOverTimeEntry `json:"entries"` + } + + TotalsByOrganizer struct { + ID uuid.UUID `json:"id"` + Name string `json:"name"` + Total float64 `json:"total"` } ) @@ -76,18 +103,144 @@ func mapToGroupInvitation(g *ent.GroupInvitationToken) GroupInvitation { } } -func (r *GroupRepository) GroupStatistics(ctx context.Context, GID uuid.UUID) (GroupStatistics, error) { +func (r *GroupRepository) StatsLocationsByPurchasePrice(ctx context.Context, GID uuid.UUID) ([]TotalsByOrganizer, error) { + var v []TotalsByOrganizer + + err := r.db.Location.Query(). + Where( + location.HasGroupWith(group.ID(GID)), + ). + GroupBy(location.FieldID, location.FieldName). + Aggregate(func(sq *sql.Selector) string { + t := sql.Table(item.Table) + sq.Join(t).On(sq.C(location.FieldID), t.C(item.LocationColumn)) + + return sql.As(sql.Sum(t.C(item.FieldPurchasePrice)), "total") + }). + Scan(ctx, &v) + + if err != nil { + return nil, err + } + + return v, err +} + +func (r *GroupRepository) StatsLabelsByPurchasePrice(ctx context.Context, GID uuid.UUID) ([]TotalsByOrganizer, error) { + var v []TotalsByOrganizer + + err := r.db.Label.Query(). + Where( + label.HasGroupWith(group.ID(GID)), + ). + GroupBy(label.FieldID, label.FieldName). + Aggregate(func(sq *sql.Selector) string { + itemTable := sql.Table(item.Table) + + jt := sql.Table(label.ItemsTable) + + sq.Join(jt).On(sq.C(label.FieldID), jt.C(label.ItemsPrimaryKey[0])) + sq.Join(itemTable).On(jt.C(label.ItemsPrimaryKey[1]), itemTable.C(item.FieldID)) + + return sql.As(sql.Sum(itemTable.C(item.FieldPurchasePrice)), "total") + }). + Scan(ctx, &v) + + if err != nil { + return nil, err + } + + return v, err +} + +func (r *GroupRepository) StatsPurchasePrice(ctx context.Context, GID uuid.UUID, start, end time.Time) (*ValueOverTime, error) { + // Get the Totals for the Start and End of the Given Time Period + q := ` + SELECT + (SELECT Sum(purchase_price) + FROM items + WHERE group_items = ? + AND items.archived = false + AND items.created_at < ?) AS price_at_start, + (SELECT Sum(purchase_price) + FROM items + WHERE group_items = ? + AND items.archived = false + AND items.created_at < ?) AS price_at_end +` + stats := ValueOverTime{ + Start: start, + End: end, + } + + var maybeStart *float64 + var maybeEnd *float64 + + row := r.db.Sql().QueryRowContext(ctx, q, GID, sqliteDateFormat(start), GID, sqliteDateFormat(end)) + err := row.Scan(&maybeStart, &maybeEnd) + if err != nil { + return nil, err + } + + stats.PriceAtStart = orDefault(maybeStart, 0) + stats.PriceAtEnd = orDefault(maybeEnd, 0) + + var v []struct { + Name string `json:"name"` + CreatedAt time.Time `json:"created_at"` + PurchasePrice float64 `json:"purchase_price"` + } + + // Get Created Date and Price of all items between start and end + err = r.db.Item.Query(). + Where( + item.HasGroupWith(group.ID(GID)), + item.CreatedAtGTE(start), + item.CreatedAtLTE(end), + item.Archived(false), + ). + Select( + item.FieldName, + item.FieldCreatedAt, + item.FieldPurchasePrice, + ). + Scan(ctx, &v) + + if err != nil { + return nil, err + } + + stats.Entries = make([]ValueOverTimeEntry, len(v)) + + for i, vv := range v { + stats.Entries[i] = ValueOverTimeEntry{ + Date: vv.CreatedAt, + Value: vv.PurchasePrice, + } + } + + return &stats, nil +} + +func (r *GroupRepository) StatsGroup(ctx context.Context, GID uuid.UUID) (GroupStatistics, error) { q := ` SELECT (SELECT COUNT(*) FROM users WHERE group_users = ?) AS total_users, (SELECT COUNT(*) FROM items WHERE group_items = ? AND items.archived = false) AS total_items, (SELECT COUNT(*) FROM locations WHERE group_locations = ?) AS total_locations, - (SELECT COUNT(*) FROM labels WHERE group_labels = ?) AS total_labels + (SELECT COUNT(*) FROM labels WHERE group_labels = ?) AS total_labels, + (SELECT SUM(purchase_price) FROM items WHERE group_items = ? AND items.archived = false) AS total_item_price, + (SELECT COUNT(*) + FROM items + WHERE group_items = ? + AND items.archived = false + AND (items.lifetime_warranty = true OR items.warranty_expires > date()) + ) AS total_with_warranty ` var stats GroupStatistics - row := r.db.Sql().QueryRowContext(ctx, q, GID, GID, GID, GID) + row := r.db.Sql().QueryRowContext(ctx, q, GID, GID, GID, GID, GID, GID) - err := row.Scan(&stats.TotalUsers, &stats.TotalItems, &stats.TotalLocations, &stats.TotalLabels) + err := row.Scan(&stats.TotalUsers, &stats.TotalItems, &stats.TotalLocations, &stats.TotalLabels, &stats.TotalItemPrice, &stats.TotalWithWarranty) if err != nil { return GroupStatistics{}, err } diff --git a/backend/internal/data/repo/repo_group_test.go b/backend/internal/data/repo/repo_group_test.go index b608d16..4321fec 100644 --- a/backend/internal/data/repo/repo_group_test.go +++ b/backend/internal/data/repo/repo_group_test.go @@ -36,7 +36,7 @@ func Test_Group_GroupStatistics(t *testing.T) { useItems(t, 20) useLabels(t, 20) - stats, err := tRepos.Groups.GroupStatistics(context.Background(), tGroup.ID) + stats, err := tRepos.Groups.StatsGroup(context.Background(), tGroup.ID) assert.NoError(t, err) assert.Equal(t, 20, stats.TotalItems) diff --git a/frontend/lib/api/__test__/user/stats.test.ts b/frontend/lib/api/__test__/user/stats.test.ts new file mode 100644 index 0000000..247b024 --- /dev/null +++ b/frontend/lib/api/__test__/user/stats.test.ts @@ -0,0 +1,142 @@ +import { faker } from "@faker-js/faker"; +import { beforeAll, describe, expect, test } from "vitest"; +import { UserClient } from "../../user"; +import { factories } from "../factories"; + +type ImportObj = { + ImportRef: string; + Location: string; + Labels: string; + Quantity: string; + Name: string; + Description: string; + Insured: boolean; + SerialNumber: string; + ModelNumber: string; + Manufacturer: string; + Notes: string; + PurchaseFrom: string; + PurchasedPrice: number; + PurchasedTime: string; + LifetimeWarranty: boolean; + WarrantyExpires: string; + WarrantyDetails: string; + SoldTo: string; + SoldPrice: number; + SoldTime: string; + SoldNotes: string; +}; + +function toCsv(data: ImportObj[]): string { + const headers = Object.keys(data[0]).join("\t"); + const rows = data.map(row => { + return Object.values(row).join("\t"); + }); + return [headers, ...rows].join("\n"); +} + +function importFileGenerator(entries: number): ImportObj[] { + const imports: ImportObj[] = []; + + const pick = (arr: string[]) => arr[Math.floor(Math.random() * arr.length)]; + + const labels = faker.random.words(5).split(" ").join(";"); + const locations = faker.random.words(3).split(" "); + + const half = Math.floor(entries / 2); + + for (let i = 0; i < entries; i++) { + imports.push({ + ImportRef: faker.database.mongodbObjectId(), + Location: pick(locations), + Labels: labels, + Quantity: faker.random.numeric(1), + Name: faker.random.words(3), + Description: "", + Insured: faker.datatype.boolean(), + SerialNumber: faker.random.alphaNumeric(5), + ModelNumber: faker.random.alphaNumeric(5), + Manufacturer: faker.random.alphaNumeric(5), + Notes: "", + PurchaseFrom: faker.name.fullName(), + PurchasedPrice: faker.datatype.number(100), + PurchasedTime: faker.date.past().toDateString(), + LifetimeWarranty: half > i, + WarrantyExpires: faker.date.future().toDateString(), + WarrantyDetails: "", + SoldTo: faker.name.fullName(), + SoldPrice: faker.datatype.number(100), + SoldTime: faker.date.past().toDateString(), + SoldNotes: "", + }); + } + + return imports; +} + +describe("group related statistics tests", () => { + const TOTAL_ITEMS = 30; + + let api: UserClient | undefined; + const imports = importFileGenerator(TOTAL_ITEMS); + + beforeAll(async () => { + // -- Setup -- + const { client } = await factories.client.singleUse(); + api = client; + + const csv = toCsv(imports); + + const setupResp = await client.items.import(new Blob([csv], { type: "text/csv" })); + + expect(setupResp.status).toBe(204); + }); + + // Write to file system for debugging + // fs.writeFileSync("test.csv", csv); + test("Validate Group Statistics", async () => { + const { status, data } = await api.stats.group(); + expect(status).toBe(200); + + expect(data.totalItems).toEqual(TOTAL_ITEMS); + expect(data.totalLabels).toEqual(11); // default + new + expect(data.totalLocations).toEqual(11); // default + new + expect(data.totalUsers).toEqual(1); + expect(data.totalWithWarranty).toEqual(Math.floor(TOTAL_ITEMS / 2)); + }); + + const labelData: Record = {}; + const locationData: Record = {}; + + for (const item of imports) { + for (const label of item.Labels.split(";")) { + labelData[label] = (labelData[label] || 0) + item.PurchasedPrice; + } + + locationData[item.Location] = (locationData[item.Location] || 0) + item.PurchasedPrice; + } + + test("Validate Labels Statistics", async () => { + const { status, data } = await api.stats.labels(); + expect(status).toBe(200); + + for (const label of data) { + expect(label.total).toEqual(labelData[label.name]); + } + }); + + test("Validate Locations Statistics", async () => { + const { status, data } = await api.stats.locations(); + expect(status).toBe(200); + + for (const location of data) { + expect(location.total).toEqual(locationData[location.name]); + } + }); + + test("Validate Purchase Over Time", async () => { + const { status, data } = await api.stats.totalPriceOverTime(); + expect(status).toBe(200); + expect(data.entries.length).toEqual(TOTAL_ITEMS); + }); +}); diff --git a/frontend/lib/api/classes/group.ts b/frontend/lib/api/classes/group.ts index 9c8fefa..7468f09 100644 --- a/frontend/lib/api/classes/group.ts +++ b/frontend/lib/api/classes/group.ts @@ -1,5 +1,5 @@ import { BaseAPI, route } from "../base"; -import { Group, GroupInvitation, GroupInvitationCreate, GroupStatistics, GroupUpdate } from "../types/data-contracts"; +import { Group, GroupInvitation, GroupInvitationCreate, GroupUpdate } from "../types/data-contracts"; export class GroupApi extends BaseAPI { createInvitation(data: GroupInvitationCreate) { @@ -21,10 +21,4 @@ export class GroupApi extends BaseAPI { url: route("/groups"), }); } - - statistics() { - return this.http.get({ - url: route("/groups/statistics"), - }); - } } diff --git a/frontend/lib/api/classes/items.ts b/frontend/lib/api/classes/items.ts index ee18f01..f4fb38d 100644 --- a/frontend/lib/api/classes/items.ts +++ b/frontend/lib/api/classes/items.ts @@ -50,7 +50,7 @@ export class ItemsApi extends BaseAPI { return payload; } - import(file: File) { + import(file: File | Blob) { const formData = new FormData(); formData.append("csv", file); diff --git a/frontend/lib/api/classes/stats.ts b/frontend/lib/api/classes/stats.ts new file mode 100644 index 0000000..b605270 --- /dev/null +++ b/frontend/lib/api/classes/stats.ts @@ -0,0 +1,42 @@ +import { BaseAPI, route } from "../base"; +import { GroupStatistics, TotalsByOrganizer, ValueOverTime } from "../types/data-contracts"; + +function YYYY_DD_MM(date?: Date): string { + if (!date) { + return ""; + } + // with leading zeros + const year = date.getFullYear(); + const month = (date.getMonth() + 1).toString().padStart(2, "0"); + const day = date.getDate().toString().padStart(2, "0"); + return `${year}-${month}-${day}`; +} +export class StatsAPI extends BaseAPI { + totalPriceOverTime(start?: Date, end?: Date) { + return this.http.get({ + url: route("/groups/statistics/purchase-price", { start: YYYY_DD_MM(start), end: YYYY_DD_MM(end) }), + }); + } + + /** + * Returns ths general statistics for the group. This mostly just + * includes the totals for various group properties. + */ + group() { + return this.http.get({ + url: route("/groups/statistics"), + }); + } + + labels() { + return this.http.get({ + url: route("/groups/statistics/labels"), + }); + } + + locations() { + return this.http.get({ + url: route("/groups/statistics/locations"), + }); + } +} diff --git a/frontend/lib/api/types/data-contracts.ts b/frontend/lib/api/types/data-contracts.ts index 464b8e7..09f10e7 100644 --- a/frontend/lib/api/types/data-contracts.ts +++ b/frontend/lib/api/types/data-contracts.ts @@ -25,10 +25,12 @@ export interface Group { } export interface GroupStatistics { + totalItemPrice: number; totalItems: number; totalLabels: number; totalLocations: number; totalUsers: number; + totalWithWarranty: number; } export interface GroupUpdate { @@ -246,6 +248,12 @@ export interface PaginationResultRepoItemSummary { total: number; } +export interface TotalsByOrganizer { + id: string; + name: string; + total: number; +} + export interface UserOut { email: string; groupId: string; @@ -261,6 +269,20 @@ export interface UserUpdate { name: string; } +export interface ValueOverTime { + end: string; + entries: ValueOverTimeEntry[]; + start: string; + valueAtEnd: number; + valueAtStart: number; +} + +export interface ValueOverTimeEntry { + date: string; + name: string; + value: number; +} + export interface ServerErrorResponse { error: string; fields: Record; diff --git a/frontend/lib/api/user.ts b/frontend/lib/api/user.ts index 31538a8..079f113 100644 --- a/frontend/lib/api/user.ts +++ b/frontend/lib/api/user.ts @@ -5,6 +5,7 @@ import { LocationsApi } from "./classes/locations"; import { GroupApi } from "./classes/group"; import { UserApi } from "./classes/users"; import { ActionsAPI } from "./classes/actions"; +import { StatsAPI } from "./classes/stats"; import { Requests } from "~~/lib/requests"; export class UserClient extends BaseAPI { @@ -14,6 +15,7 @@ export class UserClient extends BaseAPI { group: GroupApi; user: UserApi; actions: ActionsAPI; + stats: StatsAPI; constructor(requests: Requests, attachmentToken: string) { super(requests, attachmentToken); @@ -24,6 +26,7 @@ export class UserClient extends BaseAPI { this.group = new GroupApi(requests); this.user = new UserApi(requests); this.actions = new ActionsAPI(requests); + this.stats = new StatsAPI(requests); Object.freeze(this); } diff --git a/frontend/pages/home.vue b/frontend/pages/home.vue index 7723d95..92edee3 100644 --- a/frontend/pages/home.vue +++ b/frontend/pages/home.vue @@ -22,7 +22,7 @@ const labels = computed(() => labelsStore.labels); const { data: statistics } = useAsyncData(async () => { - const { data } = await api.group.statistics(); + const { data } = await api.stats.group(); return data; }); diff --git a/frontend/pages/item/[id]/index.vue b/frontend/pages/item/[id]/index.vue index 256ff0c..ecafe01 100644 --- a/frontend/pages/item/[id]/index.vue +++ b/frontend/pages/item/[id]/index.vue @@ -396,9 +396,11 @@ - + -
+
@@ -465,9 +467,18 @@ - From 5bbb969763a585afdfcdecadd4a508f5afe0625d Mon Sep 17 00:00:00 2001 From: Hayden <64056131+hay-kot@users.noreply.github.com> Date: Fri, 9 Dec 2022 20:57:57 -0900 Subject: [PATCH 015/350] feat: maintenance log (#170) * remove repo for document tokens * remove schema for doc tokens * fix id template and generate cmd * schema updates * code gen * bump dependencies * fix broken migrations + add maintenance entry type * spelling * remove debug logger * implement repository layer * routes * API client * wip: maintenance log * remove depreciated call --- .../api/handlers/v1/v1_ctrl_maint_entry.go | 125 ++ backend/app/api/routes.go | 5 + backend/app/api/static/docs/docs.go | 188 ++ backend/app/api/static/docs/swagger.json | 188 ++ backend/app/api/static/docs/swagger.yaml | 117 ++ backend/app/tools/migrations/main.go | 3 + backend/go.mod | 18 +- backend/go.sum | 36 +- backend/internal/data/ent/client.go | 258 +-- backend/internal/data/ent/config.go | 2 +- backend/internal/data/ent/document.go | 20 +- .../internal/data/ent/document/document.go | 9 - backend/internal/data/ent/document/where.go | 28 - backend/internal/data/ent/document_create.go | 35 - backend/internal/data/ent/document_query.go | 109 +- backend/internal/data/ent/document_update.go | 181 -- backend/internal/data/ent/documenttoken.go | 190 -- .../data/ent/documenttoken/documenttoken.go | 85 - .../internal/data/ent/documenttoken/where.go | 498 ----- .../internal/data/ent/documenttoken_create.go | 398 ---- .../internal/data/ent/documenttoken_delete.go | 115 -- .../internal/data/ent/documenttoken_query.go | 633 ------ .../internal/data/ent/documenttoken_update.go | 542 ------ backend/internal/data/ent/ent.go | 4 +- backend/internal/data/ent/generate.go | 2 +- backend/internal/data/ent/has_id.go | 16 +- backend/internal/data/ent/hook/hook.go | 26 +- backend/internal/data/ent/item.go | 20 +- backend/internal/data/ent/item/item.go | 9 + backend/internal/data/ent/item/where.go | 28 + backend/internal/data/ent/item_create.go | 35 + backend/internal/data/ent/item_query.go | 125 +- backend/internal/data/ent/item_update.go | 181 ++ backend/internal/data/ent/maintenanceentry.go | 202 ++ .../ent/maintenanceentry/maintenanceentry.go | 82 + .../data/ent/maintenanceentry/where.go | 696 +++++++ .../data/ent/maintenanceentry_create.go | 419 ++++ .../data/ent/maintenanceentry_delete.go | 115 ++ .../data/ent/maintenanceentry_query.go | 622 ++++++ .../data/ent/maintenanceentry_update.go | 594 ++++++ backend/internal/data/ent/migrate/schema.go | 62 +- backend/internal/data/ent/mutation.go | 1706 +++++++++-------- .../internal/data/ent/predicate/predicate.go | 6 +- backend/internal/data/ent/runtime.go | 82 +- .../internal/data/ent/schema/auth_tokens.go | 6 +- backend/internal/data/ent/schema/document.go | 4 - .../data/ent/schema/document_token.go | 50 - backend/internal/data/ent/schema/item.go | 4 + .../data/ent/schema/maintenance_entry.go | 48 + .../data/ent/schema/templates/has_id.tmpl | 7 +- backend/internal/data/ent/tx.go | 6 +- .../internal/data/migrations/migrations.go | 4 +- .../20221205230404_drop_document_tokens.sql | 5 + ...20221205234214_add_maintenance_entries.sql | 2 + .../20221205234812_cascade_delete_roles.sql | 16 + .../data/migrations/migrations/atlas.sum | 5 +- backend/internal/data/repo/map_helpers.go | 19 +- .../data/repo/repo_document_tokens.go | 68 - .../data/repo/repo_document_tokens_test.go | 150 -- .../data/repo/repo_maintenance_entry.go | 136 ++ .../data/repo/repo_maintenance_entry_test.go | 65 + backend/internal/data/repo/repos_all.go | 4 +- backend/pkgs/hasher/password.go | 2 +- frontend/components/Base/Button.vue | 1 + frontend/components/Base/Card.vue | 2 +- frontend/components/Form/DatePicker.vue | 5 +- frontend/components/global/DateTime.vue | 42 +- frontend/components/global/Markdown.vue | 8 +- frontend/lib/api/__test__/user/items.test.ts | 44 +- frontend/lib/api/classes/items.ts | 94 +- frontend/lib/api/types/data-contracts.ts | 62 +- frontend/nuxt.config.ts | 20 +- frontend/package.json | 4 +- frontend/pages/home.vue | 4 +- frontend/pages/item/[id]/edit.vue | 6 +- frontend/pages/item/[id]/index.vue | 148 +- frontend/pages/item/[id]/index/log.vue | 173 ++ frontend/pnpm-lock.yaml | 1247 ++++++------ scripts/process-types.py | 1 + 79 files changed, 6320 insertions(+), 4957 deletions(-) create mode 100644 backend/app/api/handlers/v1/v1_ctrl_maint_entry.go delete mode 100644 backend/internal/data/ent/documenttoken.go delete mode 100644 backend/internal/data/ent/documenttoken/documenttoken.go delete mode 100644 backend/internal/data/ent/documenttoken/where.go delete mode 100644 backend/internal/data/ent/documenttoken_create.go delete mode 100644 backend/internal/data/ent/documenttoken_delete.go delete mode 100644 backend/internal/data/ent/documenttoken_query.go delete mode 100644 backend/internal/data/ent/documenttoken_update.go create mode 100644 backend/internal/data/ent/maintenanceentry.go create mode 100644 backend/internal/data/ent/maintenanceentry/maintenanceentry.go create mode 100644 backend/internal/data/ent/maintenanceentry/where.go create mode 100644 backend/internal/data/ent/maintenanceentry_create.go create mode 100644 backend/internal/data/ent/maintenanceentry_delete.go create mode 100644 backend/internal/data/ent/maintenanceentry_query.go create mode 100644 backend/internal/data/ent/maintenanceentry_update.go delete mode 100644 backend/internal/data/ent/schema/document_token.go create mode 100644 backend/internal/data/ent/schema/maintenance_entry.go create mode 100644 backend/internal/data/migrations/migrations/20221205230404_drop_document_tokens.sql create mode 100644 backend/internal/data/migrations/migrations/20221205234214_add_maintenance_entries.sql create mode 100644 backend/internal/data/migrations/migrations/20221205234812_cascade_delete_roles.sql delete mode 100644 backend/internal/data/repo/repo_document_tokens.go delete mode 100644 backend/internal/data/repo/repo_document_tokens_test.go create mode 100644 backend/internal/data/repo/repo_maintenance_entry.go create mode 100644 backend/internal/data/repo/repo_maintenance_entry_test.go create mode 100644 frontend/pages/item/[id]/index/log.vue diff --git a/backend/app/api/handlers/v1/v1_ctrl_maint_entry.go b/backend/app/api/handlers/v1/v1_ctrl_maint_entry.go new file mode 100644 index 0000000..3f3f1a1 --- /dev/null +++ b/backend/app/api/handlers/v1/v1_ctrl_maint_entry.go @@ -0,0 +1,125 @@ +package v1 + +import ( + "net/http" + + "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" +) + +// HandleMaintenanceGetLog godoc +// @Summary Get Maintenance Log +// @Tags Maintenance +// @Produce json +// @Success 200 {object} repo.MaintenanceLog +// @Router /v1/items/{id}/maintenance [GET] +// @Security Bearer +func (ctrl *V1Controller) HandleMaintenanceLogGet() server.HandlerFunc { + return ctrl.handleMaintenanceLog() +} + +// HandleMaintenanceEntryCreate godoc +// @Summary Create Maintenance Entry +// @Tags Maintenance +// @Produce json +// @Param payload body repo.MaintenanceEntryCreate true "Entry Data" +// @Success 200 {object} repo.MaintenanceEntry +// @Router /v1/items/{id}/maintenance [POST] +// @Security Bearer +func (ctrl *V1Controller) HandleMaintenanceEntryCreate() server.HandlerFunc { + return ctrl.handleMaintenanceLog() +} + +// HandleMaintenanceEntryDelete godoc +// @Summary Delete Maintenance Entry +// @Tags Maintenance +// @Produce json +// @Success 204 +// @Router /v1/items/{id}/maintenance/{entry_id} [DELETE] +// @Security Bearer +func (ctrl *V1Controller) HandleMaintenanceEntryDelete() server.HandlerFunc { + return ctrl.handleMaintenanceLog() +} + +// HandleMaintenanceEntryUpdate godoc +// @Summary Update Maintenance Entry +// @Tags Maintenance +// @Produce json +// @Param payload body repo.MaintenanceEntryUpdate true "Entry Data" +// @Success 200 {object} repo.MaintenanceEntry +// @Router /v1/items/{id}/maintenance/{entry_id} [PUT] +// @Security Bearer +func (ctrl *V1Controller) HandleMaintenanceEntryUpdate() server.HandlerFunc { + return ctrl.handleMaintenanceLog() +} + +func (ctrl *V1Controller) handleMaintenanceLog() server.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) error { + ctx := services.NewContext(r.Context()) + itemID, err := ctrl.routeID(r) + if err != nil { + return err + } + + switch r.Method { + case http.MethodGet: + mlog, err := ctrl.repo.MaintEntry.GetLog(ctx, itemID) + if err != nil { + log.Err(err).Msg("failed to get items") + return validate.NewRequestError(err, http.StatusInternalServerError) + } + return server.Respond(w, http.StatusOK, mlog) + case http.MethodPost: + var create repo.MaintenanceEntryCreate + err := server.Decode(r, &create) + if err != nil { + return validate.NewRequestError(err, http.StatusBadRequest) + } + + entry, err := ctrl.repo.MaintEntry.Create(ctx, itemID, create) + if err != nil { + log.Err(err).Msg("failed to create item") + return validate.NewRequestError(err, http.StatusInternalServerError) + } + + return server.Respond(w, http.StatusCreated, entry) + case http.MethodPut: + entryID, err := ctrl.routeUUID(r, "entry_id") + if err != nil { + return err + } + + var update repo.MaintenanceEntryUpdate + err = server.Decode(r, &update) + if err != nil { + return validate.NewRequestError(err, http.StatusBadRequest) + } + + entry, err := ctrl.repo.MaintEntry.Update(ctx, entryID, update) + if err != nil { + log.Err(err).Msg("failed to update item") + return validate.NewRequestError(err, http.StatusInternalServerError) + } + + return server.Respond(w, http.StatusOK, entry) + case http.MethodDelete: + entryID, err := ctrl.routeUUID(r, "entry_id") + if err != nil { + return err + } + + err = ctrl.repo.MaintEntry.Delete(ctx, entryID) + if err != nil { + log.Err(err).Msg("failed to delete item") + return validate.NewRequestError(err, http.StatusInternalServerError) + } + + return server.Respond(w, http.StatusNoContent, nil) + } + + return nil + } +} diff --git a/backend/app/api/routes.go b/backend/app/api/routes.go index 1a42aeb..61e13d4 100644 --- a/backend/app/api/routes.go +++ b/backend/app/api/routes.go @@ -112,6 +112,11 @@ func (a *app) mountRoutes(repos *repo.AllRepos) { a.server.Put(v1Base("/items/{id}/attachments/{attachment_id}"), v1Ctrl.HandleItemAttachmentUpdate(), userMW...) a.server.Delete(v1Base("/items/{id}/attachments/{attachment_id}"), v1Ctrl.HandleItemAttachmentDelete(), userMW...) + a.server.Get(v1Base("/items/{id}/maintenance"), v1Ctrl.HandleMaintenanceEntryCreate(), userMW...) + a.server.Post(v1Base("/items/{id}/maintenance"), v1Ctrl.HandleMaintenanceEntryCreate(), userMW...) + 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("/items/{id}/attachments/{attachment_id}"), v1Ctrl.HandleItemAttachmentGet(), diff --git a/backend/app/api/static/docs/docs.go b/backend/app/api/static/docs/docs.go index 632b9e5..06cb95c 100644 --- a/backend/app/api/static/docs/docs.go +++ b/backend/app/api/static/docs/docs.go @@ -657,6 +657,117 @@ const docTemplate = `{ } } }, + "/v1/items/{id}/maintenance": { + "get": { + "security": [ + { + "Bearer": [] + } + ], + "produces": [ + "application/json" + ], + "tags": [ + "Maintenance" + ], + "summary": "Get Maintenance Log", + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/repo.MaintenanceLog" + } + } + } + }, + "post": { + "security": [ + { + "Bearer": [] + } + ], + "produces": [ + "application/json" + ], + "tags": [ + "Maintenance" + ], + "summary": "Create Maintenance Entry", + "parameters": [ + { + "description": "Entry Data", + "name": "payload", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/repo.MaintenanceEntryCreate" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/repo.MaintenanceEntry" + } + } + } + } + }, + "/v1/items/{id}/maintenance/{entry_id}": { + "put": { + "security": [ + { + "Bearer": [] + } + ], + "produces": [ + "application/json" + ], + "tags": [ + "Maintenance" + ], + "summary": "Update Maintenance Entry", + "parameters": [ + { + "description": "Entry Data", + "name": "payload", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/repo.MaintenanceEntryUpdate" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/repo.MaintenanceEntry" + } + } + } + }, + "delete": { + "security": [ + { + "Bearer": [] + } + ], + "produces": [ + "application/json" + ], + "tags": [ + "Maintenance" + ], + "summary": "Delete Maintenance Entry", + "responses": { + "204": { + "description": "No Content" + } + } + } + }, "/v1/labels": { "get": { "security": [ @@ -1825,6 +1936,83 @@ const docTemplate = `{ } } }, + "repo.MaintenanceEntry": { + "type": "object", + "properties": { + "cost": { + "type": "string", + "example": "0" + }, + "date": { + "type": "string" + }, + "description": { + "type": "string" + }, + "id": { + "type": "string" + }, + "name": { + "type": "string" + } + } + }, + "repo.MaintenanceEntryCreate": { + "type": "object", + "properties": { + "cost": { + "type": "string", + "example": "0" + }, + "date": { + "type": "string" + }, + "description": { + "type": "string" + }, + "name": { + "type": "string" + } + } + }, + "repo.MaintenanceEntryUpdate": { + "type": "object", + "properties": { + "cost": { + "type": "string", + "example": "0" + }, + "date": { + "type": "string" + }, + "description": { + "type": "string" + }, + "name": { + "type": "string" + } + } + }, + "repo.MaintenanceLog": { + "type": "object", + "properties": { + "costAverage": { + "type": "number" + }, + "costTotal": { + "type": "number" + }, + "entries": { + "type": "array", + "items": { + "$ref": "#/definitions/repo.MaintenanceEntry" + } + }, + "itemId": { + "type": "string" + } + } + }, "repo.PaginationResult-repo_ItemSummary": { "type": "object", "properties": { diff --git a/backend/app/api/static/docs/swagger.json b/backend/app/api/static/docs/swagger.json index 69ba931..7e5ec85 100644 --- a/backend/app/api/static/docs/swagger.json +++ b/backend/app/api/static/docs/swagger.json @@ -649,6 +649,117 @@ } } }, + "/v1/items/{id}/maintenance": { + "get": { + "security": [ + { + "Bearer": [] + } + ], + "produces": [ + "application/json" + ], + "tags": [ + "Maintenance" + ], + "summary": "Get Maintenance Log", + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/repo.MaintenanceLog" + } + } + } + }, + "post": { + "security": [ + { + "Bearer": [] + } + ], + "produces": [ + "application/json" + ], + "tags": [ + "Maintenance" + ], + "summary": "Create Maintenance Entry", + "parameters": [ + { + "description": "Entry Data", + "name": "payload", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/repo.MaintenanceEntryCreate" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/repo.MaintenanceEntry" + } + } + } + } + }, + "/v1/items/{id}/maintenance/{entry_id}": { + "put": { + "security": [ + { + "Bearer": [] + } + ], + "produces": [ + "application/json" + ], + "tags": [ + "Maintenance" + ], + "summary": "Update Maintenance Entry", + "parameters": [ + { + "description": "Entry Data", + "name": "payload", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/repo.MaintenanceEntryUpdate" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/repo.MaintenanceEntry" + } + } + } + }, + "delete": { + "security": [ + { + "Bearer": [] + } + ], + "produces": [ + "application/json" + ], + "tags": [ + "Maintenance" + ], + "summary": "Delete Maintenance Entry", + "responses": { + "204": { + "description": "No Content" + } + } + } + }, "/v1/labels": { "get": { "security": [ @@ -1817,6 +1928,83 @@ } } }, + "repo.MaintenanceEntry": { + "type": "object", + "properties": { + "cost": { + "type": "string", + "example": "0" + }, + "date": { + "type": "string" + }, + "description": { + "type": "string" + }, + "id": { + "type": "string" + }, + "name": { + "type": "string" + } + } + }, + "repo.MaintenanceEntryCreate": { + "type": "object", + "properties": { + "cost": { + "type": "string", + "example": "0" + }, + "date": { + "type": "string" + }, + "description": { + "type": "string" + }, + "name": { + "type": "string" + } + } + }, + "repo.MaintenanceEntryUpdate": { + "type": "object", + "properties": { + "cost": { + "type": "string", + "example": "0" + }, + "date": { + "type": "string" + }, + "description": { + "type": "string" + }, + "name": { + "type": "string" + } + } + }, + "repo.MaintenanceLog": { + "type": "object", + "properties": { + "costAverage": { + "type": "number" + }, + "costTotal": { + "type": "number" + }, + "entries": { + "type": "array", + "items": { + "$ref": "#/definitions/repo.MaintenanceEntry" + } + }, + "itemId": { + "type": "string" + } + } + }, "repo.PaginationResult-repo_ItemSummary": { "type": "object", "properties": { diff --git a/backend/app/api/static/docs/swagger.yaml b/backend/app/api/static/docs/swagger.yaml index 802fdf8..3d2fe2a 100644 --- a/backend/app/api/static/docs/swagger.yaml +++ b/backend/app/api/static/docs/swagger.yaml @@ -383,6 +383,57 @@ definitions: type: string x-nullable: true type: object + repo.MaintenanceEntry: + properties: + cost: + example: "0" + type: string + date: + type: string + description: + type: string + id: + type: string + name: + type: string + type: object + repo.MaintenanceEntryCreate: + properties: + cost: + example: "0" + type: string + date: + type: string + description: + type: string + name: + type: string + type: object + repo.MaintenanceEntryUpdate: + properties: + cost: + example: "0" + type: string + date: + type: string + description: + type: string + name: + type: string + type: object + repo.MaintenanceLog: + properties: + costAverage: + type: number + costTotal: + type: number + entries: + items: + $ref: '#/definitions/repo.MaintenanceEntry' + type: array + itemId: + type: string + type: object repo.PaginationResult-repo_ItemSummary: properties: items: @@ -938,6 +989,72 @@ paths: summary: retrieves an attachment for an item tags: - Items Attachments + /v1/items/{id}/maintenance: + get: + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/repo.MaintenanceLog' + security: + - Bearer: [] + summary: Get Maintenance Log + tags: + - Maintenance + post: + parameters: + - description: Entry Data + in: body + name: payload + required: true + schema: + $ref: '#/definitions/repo.MaintenanceEntryCreate' + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/repo.MaintenanceEntry' + security: + - Bearer: [] + summary: Create Maintenance Entry + tags: + - Maintenance + /v1/items/{id}/maintenance/{entry_id}: + delete: + produces: + - application/json + responses: + "204": + description: No Content + security: + - Bearer: [] + summary: Delete Maintenance Entry + tags: + - Maintenance + put: + parameters: + - description: Entry Data + in: body + name: payload + required: true + schema: + $ref: '#/definitions/repo.MaintenanceEntryUpdate' + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/repo.MaintenanceEntry' + security: + - Bearer: [] + summary: Update Maintenance Entry + tags: + - Maintenance /v1/items/import: post: parameters: diff --git a/backend/app/tools/migrations/main.go b/backend/app/tools/migrations/main.go index a2f6624..e53e7ba 100644 --- a/backend/app/tools/migrations/main.go +++ b/backend/app/tools/migrations/main.go @@ -2,6 +2,7 @@ package main import ( "context" + "fmt" "log" "os" @@ -39,4 +40,6 @@ func main() { if err != nil { log.Fatalf("failed generating migration file: %v", err) } + + fmt.Println("Migration file generated successfully.") } diff --git a/backend/go.mod b/backend/go.mod index ab7e91b..d6eefce 100644 --- a/backend/go.mod +++ b/backend/go.mod @@ -3,7 +3,7 @@ module github.com/hay-kot/homebox/backend go 1.19 require ( - ariga.io/atlas v0.7.3-0.20221011160332-3ca609863edd + ariga.io/atlas v0.8.3 entgo.io/ent v0.11.4 github.com/ardanlabs/conf/v2 v2.2.0 github.com/go-chi/chi/v5 v5.0.7 @@ -14,7 +14,7 @@ require ( github.com/stretchr/testify v1.8.1 github.com/swaggo/http-swagger v1.3.3 github.com/swaggo/swag v1.8.8 - golang.org/x/crypto v0.0.0-20220829220503-c86fa9a7ed90 + golang.org/x/crypto v0.3.0 ) require ( @@ -30,7 +30,7 @@ require ( github.com/go-playground/locales v0.14.0 // indirect github.com/go-playground/universal-translator v0.18.0 // indirect github.com/google/go-cmp v0.5.9 // indirect - github.com/hashicorp/hcl/v2 v2.14.1 // indirect + github.com/hashicorp/hcl/v2 v2.15.0 // indirect github.com/josharian/intern v1.0.0 // indirect github.com/leodido/go-urn v1.2.1 // indirect github.com/mailru/easyjson v0.7.7 // indirect @@ -39,11 +39,11 @@ require ( github.com/mitchellh/go-wordwrap v1.0.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/swaggo/files v0.0.0-20220728132757-551d4a08d97a // indirect - github.com/zclconf/go-cty v1.11.0 // indirect - golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 // indirect - golang.org/x/net v0.0.0-20220923203811-8be639271d50 // indirect - golang.org/x/sys v0.0.0-20220919091848-fb04ddd9f9c8 // indirect - golang.org/x/text v0.3.7 // indirect - golang.org/x/tools v0.1.13-0.20220804200503-81c7dc4e4efa // indirect + github.com/zclconf/go-cty v1.12.1 // indirect + golang.org/x/mod v0.7.0 // indirect + golang.org/x/net v0.2.0 // indirect + golang.org/x/sys v0.3.0 // indirect + golang.org/x/text v0.5.0 // indirect + golang.org/x/tools v0.3.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/backend/go.sum b/backend/go.sum index 19d0437..16b6ffb 100644 --- a/backend/go.sum +++ b/backend/go.sum @@ -1,5 +1,5 @@ -ariga.io/atlas v0.7.3-0.20221011160332-3ca609863edd h1:c3F2jvvEZzsoH/KUpDNhTsCVeUPnpXaF8kADZvUSiU0= -ariga.io/atlas v0.7.3-0.20221011160332-3ca609863edd/go.mod h1:ft47uSh5hWGDCmQC9DsztZg6Xk+KagM5Ts/mZYKb9JE= +ariga.io/atlas v0.8.3 h1:nddOywkhr/62Cwa+UsGgO35lAhUYh52XGVsbFwGzWZM= +ariga.io/atlas v0.8.3/go.mod h1:T230JFcENj4ZZzMkZrXFDSkv+2kXkUgpJ5FQQ5hMcKU= entgo.io/ent v0.11.4 h1:grwVY0fp31BZ6oEo3YrXenAuv8VJmEw7F/Bi6WqeH3Q= entgo.io/ent v0.11.4/go.mod h1:fnQIXL36RYnCk/9nvG4aE7YHBFZhCycfh7wMjY5p7SE= github.com/DATA-DOG/go-sqlmock v1.5.0 h1:Shsta01QNfFxHCfpW6YH2STWB0MudeXXEWMr20OEh60= @@ -7,7 +7,6 @@ github.com/KyleBanks/depth v1.2.1 h1:5h8fQADFrWtarTdtDudMmGsC7GPbOAu6RVB3ffsVFHc github.com/KyleBanks/depth v1.2.1/go.mod h1:jzSb9d0L43HxTQfT+oSA1EEp2q+ne2uh6XgeJcm8brE= github.com/agext/levenshtein v1.2.3 h1:YB2fHEn0UJagG8T1rrWknE3ZQzWM06O8AMAatNn7lmo= github.com/agext/levenshtein v1.2.3/go.mod h1:JEDfjyjHDjOF/1e4FlBE/PkbqA9OfWu2ki2W0IB5558= -github.com/apparentlymart/go-dump v0.0.0-20180507223929-23540a00eaa3 h1:ZSTrOEhiM5J5RFxEaFvMZVEAM1KvT1YzbEOwB2EAGjA= github.com/apparentlymart/go-textseg/v13 v13.0.0 h1:Y+KvPE1NYz0xl601PVImeQfFyEy6iT90AvPUL1NNfNw= github.com/apparentlymart/go-textseg/v13 v13.0.0/go.mod h1:ZK2fH7c4NqDTLtiYLvIkEghdlcqw7yxLeM89kiTRPUo= github.com/ardanlabs/conf/v2 v2.2.0 h1:ar1+TYIYAh2Tdeg2DQroh7ruR56/vJR8BDfzDIrXgtk= @@ -47,8 +46,8 @@ github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/hashicorp/hcl/v2 v2.14.1 h1:x0BpjfZ+CYdbiz+8yZTQ+gdLO7IXvOut7Da+XJayx34= -github.com/hashicorp/hcl/v2 v2.14.1/go.mod h1:e4z5nxYlWNPdDSNYX+ph14EvWYMFm3eP0zIUqPc2jr0= +github.com/hashicorp/hcl/v2 v2.15.0 h1:CPDXO6+uORPjKflkWCCwoWc9uRp+zSIPcCQ+BrxV7m8= +github.com/hashicorp/hcl/v2 v2.15.0/go.mod h1:JRmR89jycNkrrqnMmvPDMd56n1rQJ2Q6KocSLCMCXng= github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= @@ -109,17 +108,17 @@ github.com/swaggo/http-swagger v1.3.3 h1:Hu5Z0L9ssyBLofaama21iYaF2VbWyA8jdohaaCG github.com/swaggo/http-swagger v1.3.3/go.mod h1:sE+4PjD89IxMPm77FnkDz0sdO+p5lbXzrVWT6OTVVGo= github.com/swaggo/swag v1.8.8 h1:/GgJmrJ8/c0z4R4hoEPZ5UeEhVGdvsII4JbVDLbR7Xc= github.com/swaggo/swag v1.8.8/go.mod h1:ezQVUUhly8dludpVk+/PuwJWvLLanB13ygV5Pr9enSk= -github.com/zclconf/go-cty v1.11.0 h1:726SxLdi2SDnjY+BStqB9J1hNp4+2WlzyXLuimibIe0= -github.com/zclconf/go-cty v1.11.0/go.mod h1:s9IfD1LK5ccNMSWCVFCE2rJfHiZgi7JijgeWIMfhLvA= +github.com/zclconf/go-cty v1.12.1 h1:PcupnljUm9EIvbgSHQnHhUr3fO6oFmkOrvs2BAFNXXY= +github.com/zclconf/go-cty v1.12.1/go.mod h1:s9IfD1LK5ccNMSWCVFCE2rJfHiZgi7JijgeWIMfhLvA= golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.0.0-20220829220503-c86fa9a7ed90 h1:Y/gsMcFOcR+6S6f3YeMKl5g+dZMEWqcz5Czj/GWYbkM= -golang.org/x/crypto v0.0.0-20220829220503-c86fa9a7ed90/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 h1:6zppjxzCulZykYSLyVDYbneBfbaBIQPYMevg0bEwv2s= -golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= +golang.org/x/crypto v0.3.0 h1:a06MkbcxBrEFc0w0QIZWXrH/9cCX6KJyWbBOIwAn+7A= +golang.org/x/crypto v0.3.0/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4= +golang.org/x/mod v0.7.0 h1:LapD9S96VoQRhi/GrNTqeBJFrUjs5UHCAtTlgwA5oZA= +golang.org/x/mod v0.7.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20220923203811-8be639271d50 h1:vKyz8L3zkd+xrMeIaBsQ/MNVPVFSffdaU3ZyYlBGFnI= -golang.org/x/net v0.0.0-20220923203811-8be639271d50/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= +golang.org/x/net v0.2.0 h1:sZfSu1wtKLGlWI4ZZayP0ck9Y73K1ynO6gqzTdBVdPU= +golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -127,15 +126,16 @@ golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220919091848-fb04ddd9f9c8 h1:h+EGohizhe9XlX18rfpa8k8RAc5XyaeamM+0VHRd4lc= -golang.org/x/sys v0.0.0-20220919091848-fb04ddd9f9c8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.3.0 h1:w8ZOecv6NaNa/zC8944JTU3vz4u6Lagfk4RPQxv92NQ= +golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.5.0 h1:OLmvp0KP+FVG99Ct/qFiL/Fhk4zp4QQnZ7b2U+5piUM= +golang.org/x/text v0.5.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.1.13-0.20220804200503-81c7dc4e4efa h1:uKcci2q7Qtp6nMTC/AAvfNUAldFtJuHWV9/5QWiypts= -golang.org/x/tools v0.1.13-0.20220804200503-81c7dc4e4efa/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= +golang.org/x/tools v0.3.0 h1:SrNbZl6ECOS1qFzgTdQfWXZM9XBkiA6tkFrH9YSTPHM= +golang.org/x/tools v0.3.0/go.mod h1:/rWhSS2+zyEVwoJf8YAX6L2f0ntZ7Kn/mGgAWcipA5k= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= diff --git a/backend/internal/data/ent/client.go b/backend/internal/data/ent/client.go index 67a57ef..eb66c20 100644 --- a/backend/internal/data/ent/client.go +++ b/backend/internal/data/ent/client.go @@ -15,13 +15,13 @@ import ( "github.com/hay-kot/homebox/backend/internal/data/ent/authroles" "github.com/hay-kot/homebox/backend/internal/data/ent/authtokens" "github.com/hay-kot/homebox/backend/internal/data/ent/document" - "github.com/hay-kot/homebox/backend/internal/data/ent/documenttoken" "github.com/hay-kot/homebox/backend/internal/data/ent/group" "github.com/hay-kot/homebox/backend/internal/data/ent/groupinvitationtoken" "github.com/hay-kot/homebox/backend/internal/data/ent/item" "github.com/hay-kot/homebox/backend/internal/data/ent/itemfield" "github.com/hay-kot/homebox/backend/internal/data/ent/label" "github.com/hay-kot/homebox/backend/internal/data/ent/location" + "github.com/hay-kot/homebox/backend/internal/data/ent/maintenanceentry" "github.com/hay-kot/homebox/backend/internal/data/ent/user" "entgo.io/ent/dialect" @@ -42,8 +42,6 @@ type Client struct { AuthTokens *AuthTokensClient // Document is the client for interacting with the Document builders. Document *DocumentClient - // DocumentToken is the client for interacting with the DocumentToken builders. - DocumentToken *DocumentTokenClient // Group is the client for interacting with the Group builders. Group *GroupClient // GroupInvitationToken is the client for interacting with the GroupInvitationToken builders. @@ -56,6 +54,8 @@ type Client struct { Label *LabelClient // Location is the client for interacting with the Location builders. Location *LocationClient + // MaintenanceEntry is the client for interacting with the MaintenanceEntry builders. + MaintenanceEntry *MaintenanceEntryClient // User is the client for interacting with the User builders. User *UserClient } @@ -75,13 +75,13 @@ func (c *Client) init() { c.AuthRoles = NewAuthRolesClient(c.config) c.AuthTokens = NewAuthTokensClient(c.config) c.Document = NewDocumentClient(c.config) - c.DocumentToken = NewDocumentTokenClient(c.config) c.Group = NewGroupClient(c.config) c.GroupInvitationToken = NewGroupInvitationTokenClient(c.config) c.Item = NewItemClient(c.config) c.ItemField = NewItemFieldClient(c.config) c.Label = NewLabelClient(c.config) c.Location = NewLocationClient(c.config) + c.MaintenanceEntry = NewMaintenanceEntryClient(c.config) c.User = NewUserClient(c.config) } @@ -120,13 +120,13 @@ func (c *Client) Tx(ctx context.Context) (*Tx, error) { AuthRoles: NewAuthRolesClient(cfg), AuthTokens: NewAuthTokensClient(cfg), Document: NewDocumentClient(cfg), - DocumentToken: NewDocumentTokenClient(cfg), Group: NewGroupClient(cfg), GroupInvitationToken: NewGroupInvitationTokenClient(cfg), Item: NewItemClient(cfg), ItemField: NewItemFieldClient(cfg), Label: NewLabelClient(cfg), Location: NewLocationClient(cfg), + MaintenanceEntry: NewMaintenanceEntryClient(cfg), User: NewUserClient(cfg), }, nil } @@ -151,13 +151,13 @@ func (c *Client) BeginTx(ctx context.Context, opts *sql.TxOptions) (*Tx, error) AuthRoles: NewAuthRolesClient(cfg), AuthTokens: NewAuthTokensClient(cfg), Document: NewDocumentClient(cfg), - DocumentToken: NewDocumentTokenClient(cfg), Group: NewGroupClient(cfg), GroupInvitationToken: NewGroupInvitationTokenClient(cfg), Item: NewItemClient(cfg), ItemField: NewItemFieldClient(cfg), Label: NewLabelClient(cfg), Location: NewLocationClient(cfg), + MaintenanceEntry: NewMaintenanceEntryClient(cfg), User: NewUserClient(cfg), }, nil } @@ -191,13 +191,13 @@ func (c *Client) Use(hooks ...Hook) { c.AuthRoles.Use(hooks...) c.AuthTokens.Use(hooks...) c.Document.Use(hooks...) - c.DocumentToken.Use(hooks...) c.Group.Use(hooks...) c.GroupInvitationToken.Use(hooks...) c.Item.Use(hooks...) c.ItemField.Use(hooks...) c.Label.Use(hooks...) c.Location.Use(hooks...) + c.MaintenanceEntry.Use(hooks...) c.User.Use(hooks...) } @@ -652,22 +652,6 @@ func (c *DocumentClient) QueryGroup(d *Document) *GroupQuery { return query } -// QueryDocumentTokens queries the document_tokens edge of a Document. -func (c *DocumentClient) QueryDocumentTokens(d *Document) *DocumentTokenQuery { - query := &DocumentTokenQuery{config: c.config} - query.path = func(context.Context) (fromV *sql.Selector, _ error) { - id := d.ID - step := sqlgraph.NewStep( - sqlgraph.From(document.Table, document.FieldID, id), - sqlgraph.To(documenttoken.Table, documenttoken.FieldID), - sqlgraph.Edge(sqlgraph.O2M, false, document.DocumentTokensTable, document.DocumentTokensColumn), - ) - fromV = sqlgraph.Neighbors(d.driver.Dialect(), step) - return fromV, nil - } - return query -} - // QueryAttachments queries the attachments edge of a Document. func (c *DocumentClient) QueryAttachments(d *Document) *AttachmentQuery { query := &AttachmentQuery{config: c.config} @@ -689,112 +673,6 @@ func (c *DocumentClient) Hooks() []Hook { return c.hooks.Document } -// DocumentTokenClient is a client for the DocumentToken schema. -type DocumentTokenClient struct { - config -} - -// NewDocumentTokenClient returns a client for the DocumentToken from the given config. -func NewDocumentTokenClient(c config) *DocumentTokenClient { - return &DocumentTokenClient{config: c} -} - -// Use adds a list of mutation hooks to the hooks stack. -// A call to `Use(f, g, h)` equals to `documenttoken.Hooks(f(g(h())))`. -func (c *DocumentTokenClient) Use(hooks ...Hook) { - c.hooks.DocumentToken = append(c.hooks.DocumentToken, hooks...) -} - -// Create returns a builder for creating a DocumentToken entity. -func (c *DocumentTokenClient) Create() *DocumentTokenCreate { - mutation := newDocumentTokenMutation(c.config, OpCreate) - return &DocumentTokenCreate{config: c.config, hooks: c.Hooks(), mutation: mutation} -} - -// CreateBulk returns a builder for creating a bulk of DocumentToken entities. -func (c *DocumentTokenClient) CreateBulk(builders ...*DocumentTokenCreate) *DocumentTokenCreateBulk { - return &DocumentTokenCreateBulk{config: c.config, builders: builders} -} - -// Update returns an update builder for DocumentToken. -func (c *DocumentTokenClient) Update() *DocumentTokenUpdate { - mutation := newDocumentTokenMutation(c.config, OpUpdate) - return &DocumentTokenUpdate{config: c.config, hooks: c.Hooks(), mutation: mutation} -} - -// UpdateOne returns an update builder for the given entity. -func (c *DocumentTokenClient) UpdateOne(dt *DocumentToken) *DocumentTokenUpdateOne { - mutation := newDocumentTokenMutation(c.config, OpUpdateOne, withDocumentToken(dt)) - return &DocumentTokenUpdateOne{config: c.config, hooks: c.Hooks(), mutation: mutation} -} - -// UpdateOneID returns an update builder for the given id. -func (c *DocumentTokenClient) UpdateOneID(id uuid.UUID) *DocumentTokenUpdateOne { - mutation := newDocumentTokenMutation(c.config, OpUpdateOne, withDocumentTokenID(id)) - return &DocumentTokenUpdateOne{config: c.config, hooks: c.Hooks(), mutation: mutation} -} - -// Delete returns a delete builder for DocumentToken. -func (c *DocumentTokenClient) Delete() *DocumentTokenDelete { - mutation := newDocumentTokenMutation(c.config, OpDelete) - return &DocumentTokenDelete{config: c.config, hooks: c.Hooks(), mutation: mutation} -} - -// DeleteOne returns a builder for deleting the given entity. -func (c *DocumentTokenClient) DeleteOne(dt *DocumentToken) *DocumentTokenDeleteOne { - return c.DeleteOneID(dt.ID) -} - -// DeleteOneID returns a builder for deleting the given entity by its id. -func (c *DocumentTokenClient) DeleteOneID(id uuid.UUID) *DocumentTokenDeleteOne { - builder := c.Delete().Where(documenttoken.ID(id)) - builder.mutation.id = &id - builder.mutation.op = OpDeleteOne - return &DocumentTokenDeleteOne{builder} -} - -// Query returns a query builder for DocumentToken. -func (c *DocumentTokenClient) Query() *DocumentTokenQuery { - return &DocumentTokenQuery{ - config: c.config, - } -} - -// Get returns a DocumentToken entity by its id. -func (c *DocumentTokenClient) Get(ctx context.Context, id uuid.UUID) (*DocumentToken, error) { - return c.Query().Where(documenttoken.ID(id)).Only(ctx) -} - -// GetX is like Get, but panics if an error occurs. -func (c *DocumentTokenClient) GetX(ctx context.Context, id uuid.UUID) *DocumentToken { - obj, err := c.Get(ctx, id) - if err != nil { - panic(err) - } - return obj -} - -// QueryDocument queries the document edge of a DocumentToken. -func (c *DocumentTokenClient) QueryDocument(dt *DocumentToken) *DocumentQuery { - query := &DocumentQuery{config: c.config} - query.path = func(context.Context) (fromV *sql.Selector, _ error) { - id := dt.ID - step := sqlgraph.NewStep( - sqlgraph.From(documenttoken.Table, documenttoken.FieldID, id), - sqlgraph.To(document.Table, document.FieldID), - sqlgraph.Edge(sqlgraph.M2O, true, documenttoken.DocumentTable, documenttoken.DocumentColumn), - ) - fromV = sqlgraph.Neighbors(dt.driver.Dialect(), step) - return fromV, nil - } - return query -} - -// Hooks returns the client hooks. -func (c *DocumentTokenClient) Hooks() []Hook { - return c.hooks.DocumentToken -} - // GroupClient is a client for the Group schema. type GroupClient struct { config @@ -1268,6 +1146,22 @@ func (c *ItemClient) QueryFields(i *Item) *ItemFieldQuery { return query } +// QueryMaintenanceEntries queries the maintenance_entries edge of a Item. +func (c *ItemClient) QueryMaintenanceEntries(i *Item) *MaintenanceEntryQuery { + query := &MaintenanceEntryQuery{config: c.config} + query.path = func(context.Context) (fromV *sql.Selector, _ error) { + id := i.ID + step := sqlgraph.NewStep( + sqlgraph.From(item.Table, item.FieldID, id), + sqlgraph.To(maintenanceentry.Table, maintenanceentry.FieldID), + sqlgraph.Edge(sqlgraph.O2M, false, item.MaintenanceEntriesTable, item.MaintenanceEntriesColumn), + ) + fromV = sqlgraph.Neighbors(i.driver.Dialect(), step) + return fromV, nil + } + return query +} + // QueryAttachments queries the attachments edge of a Item. func (c *ItemClient) QueryAttachments(i *Item) *AttachmentQuery { query := &AttachmentQuery{config: c.config} @@ -1671,6 +1565,112 @@ func (c *LocationClient) Hooks() []Hook { return c.hooks.Location } +// MaintenanceEntryClient is a client for the MaintenanceEntry schema. +type MaintenanceEntryClient struct { + config +} + +// NewMaintenanceEntryClient returns a client for the MaintenanceEntry from the given config. +func NewMaintenanceEntryClient(c config) *MaintenanceEntryClient { + return &MaintenanceEntryClient{config: c} +} + +// Use adds a list of mutation hooks to the hooks stack. +// A call to `Use(f, g, h)` equals to `maintenanceentry.Hooks(f(g(h())))`. +func (c *MaintenanceEntryClient) Use(hooks ...Hook) { + c.hooks.MaintenanceEntry = append(c.hooks.MaintenanceEntry, hooks...) +} + +// Create returns a builder for creating a MaintenanceEntry entity. +func (c *MaintenanceEntryClient) Create() *MaintenanceEntryCreate { + mutation := newMaintenanceEntryMutation(c.config, OpCreate) + return &MaintenanceEntryCreate{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// CreateBulk returns a builder for creating a bulk of MaintenanceEntry entities. +func (c *MaintenanceEntryClient) CreateBulk(builders ...*MaintenanceEntryCreate) *MaintenanceEntryCreateBulk { + return &MaintenanceEntryCreateBulk{config: c.config, builders: builders} +} + +// Update returns an update builder for MaintenanceEntry. +func (c *MaintenanceEntryClient) Update() *MaintenanceEntryUpdate { + mutation := newMaintenanceEntryMutation(c.config, OpUpdate) + return &MaintenanceEntryUpdate{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// UpdateOne returns an update builder for the given entity. +func (c *MaintenanceEntryClient) UpdateOne(me *MaintenanceEntry) *MaintenanceEntryUpdateOne { + mutation := newMaintenanceEntryMutation(c.config, OpUpdateOne, withMaintenanceEntry(me)) + return &MaintenanceEntryUpdateOne{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// UpdateOneID returns an update builder for the given id. +func (c *MaintenanceEntryClient) UpdateOneID(id uuid.UUID) *MaintenanceEntryUpdateOne { + mutation := newMaintenanceEntryMutation(c.config, OpUpdateOne, withMaintenanceEntryID(id)) + return &MaintenanceEntryUpdateOne{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// Delete returns a delete builder for MaintenanceEntry. +func (c *MaintenanceEntryClient) Delete() *MaintenanceEntryDelete { + mutation := newMaintenanceEntryMutation(c.config, OpDelete) + return &MaintenanceEntryDelete{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// DeleteOne returns a builder for deleting the given entity. +func (c *MaintenanceEntryClient) DeleteOne(me *MaintenanceEntry) *MaintenanceEntryDeleteOne { + return c.DeleteOneID(me.ID) +} + +// DeleteOneID returns a builder for deleting the given entity by its id. +func (c *MaintenanceEntryClient) DeleteOneID(id uuid.UUID) *MaintenanceEntryDeleteOne { + builder := c.Delete().Where(maintenanceentry.ID(id)) + builder.mutation.id = &id + builder.mutation.op = OpDeleteOne + return &MaintenanceEntryDeleteOne{builder} +} + +// Query returns a query builder for MaintenanceEntry. +func (c *MaintenanceEntryClient) Query() *MaintenanceEntryQuery { + return &MaintenanceEntryQuery{ + config: c.config, + } +} + +// Get returns a MaintenanceEntry entity by its id. +func (c *MaintenanceEntryClient) Get(ctx context.Context, id uuid.UUID) (*MaintenanceEntry, error) { + return c.Query().Where(maintenanceentry.ID(id)).Only(ctx) +} + +// GetX is like Get, but panics if an error occurs. +func (c *MaintenanceEntryClient) GetX(ctx context.Context, id uuid.UUID) *MaintenanceEntry { + obj, err := c.Get(ctx, id) + if err != nil { + panic(err) + } + return obj +} + +// QueryItem queries the item edge of a MaintenanceEntry. +func (c *MaintenanceEntryClient) QueryItem(me *MaintenanceEntry) *ItemQuery { + query := &ItemQuery{config: c.config} + query.path = func(context.Context) (fromV *sql.Selector, _ error) { + id := me.ID + step := sqlgraph.NewStep( + sqlgraph.From(maintenanceentry.Table, maintenanceentry.FieldID, id), + sqlgraph.To(item.Table, item.FieldID), + sqlgraph.Edge(sqlgraph.M2O, true, maintenanceentry.ItemTable, maintenanceentry.ItemColumn), + ) + fromV = sqlgraph.Neighbors(me.driver.Dialect(), step) + return fromV, nil + } + return query +} + +// Hooks returns the client hooks. +func (c *MaintenanceEntryClient) Hooks() []Hook { + return c.hooks.MaintenanceEntry +} + // UserClient is a client for the User schema. type UserClient struct { config diff --git a/backend/internal/data/ent/config.go b/backend/internal/data/ent/config.go index e3ce09d..9cba253 100644 --- a/backend/internal/data/ent/config.go +++ b/backend/internal/data/ent/config.go @@ -28,13 +28,13 @@ type hooks struct { AuthRoles []ent.Hook AuthTokens []ent.Hook Document []ent.Hook - DocumentToken []ent.Hook Group []ent.Hook GroupInvitationToken []ent.Hook Item []ent.Hook ItemField []ent.Hook Label []ent.Hook Location []ent.Hook + MaintenanceEntry []ent.Hook User []ent.Hook } diff --git a/backend/internal/data/ent/document.go b/backend/internal/data/ent/document.go index 0c84d7d..50c1612 100644 --- a/backend/internal/data/ent/document.go +++ b/backend/internal/data/ent/document.go @@ -36,13 +36,11 @@ type Document struct { type DocumentEdges struct { // Group holds the value of the group edge. Group *Group `json:"group,omitempty"` - // DocumentTokens holds the value of the document_tokens edge. - DocumentTokens []*DocumentToken `json:"document_tokens,omitempty"` // Attachments holds the value of the attachments edge. Attachments []*Attachment `json:"attachments,omitempty"` // loadedTypes holds the information for reporting if a // type was loaded (or requested) in eager-loading or not. - loadedTypes [3]bool + loadedTypes [2]bool } // GroupOrErr returns the Group value or an error if the edge @@ -58,19 +56,10 @@ func (e DocumentEdges) GroupOrErr() (*Group, error) { return nil, &NotLoadedError{edge: "group"} } -// DocumentTokensOrErr returns the DocumentTokens value or an error if the edge -// was not loaded in eager-loading. -func (e DocumentEdges) DocumentTokensOrErr() ([]*DocumentToken, error) { - if e.loadedTypes[1] { - return e.DocumentTokens, nil - } - return nil, &NotLoadedError{edge: "document_tokens"} -} - // AttachmentsOrErr returns the Attachments value or an error if the edge // was not loaded in eager-loading. func (e DocumentEdges) AttachmentsOrErr() ([]*Attachment, error) { - if e.loadedTypes[2] { + if e.loadedTypes[1] { return e.Attachments, nil } return nil, &NotLoadedError{edge: "attachments"} @@ -151,11 +140,6 @@ func (d *Document) QueryGroup() *GroupQuery { return (&DocumentClient{config: d.config}).QueryGroup(d) } -// QueryDocumentTokens queries the "document_tokens" edge of the Document entity. -func (d *Document) QueryDocumentTokens() *DocumentTokenQuery { - return (&DocumentClient{config: d.config}).QueryDocumentTokens(d) -} - // QueryAttachments queries the "attachments" edge of the Document entity. func (d *Document) QueryAttachments() *AttachmentQuery { return (&DocumentClient{config: d.config}).QueryAttachments(d) diff --git a/backend/internal/data/ent/document/document.go b/backend/internal/data/ent/document/document.go index bfc3881..b6a15eb 100644 --- a/backend/internal/data/ent/document/document.go +++ b/backend/internal/data/ent/document/document.go @@ -23,8 +23,6 @@ const ( FieldPath = "path" // EdgeGroup holds the string denoting the group edge name in mutations. EdgeGroup = "group" - // EdgeDocumentTokens holds the string denoting the document_tokens edge name in mutations. - EdgeDocumentTokens = "document_tokens" // EdgeAttachments holds the string denoting the attachments edge name in mutations. EdgeAttachments = "attachments" // Table holds the table name of the document in the database. @@ -36,13 +34,6 @@ const ( GroupInverseTable = "groups" // GroupColumn is the table column denoting the group relation/edge. GroupColumn = "group_documents" - // DocumentTokensTable is the table that holds the document_tokens relation/edge. - DocumentTokensTable = "document_tokens" - // DocumentTokensInverseTable is the table name for the DocumentToken entity. - // It exists in this package in order to avoid circular dependency with the "documenttoken" package. - DocumentTokensInverseTable = "document_tokens" - // DocumentTokensColumn is the table column denoting the document_tokens relation/edge. - DocumentTokensColumn = "document_document_tokens" // AttachmentsTable is the table that holds the attachments relation/edge. AttachmentsTable = "attachments" // AttachmentsInverseTable is the table name for the Attachment entity. diff --git a/backend/internal/data/ent/document/where.go b/backend/internal/data/ent/document/where.go index dc02fa4..6f1bd69 100644 --- a/backend/internal/data/ent/document/where.go +++ b/backend/internal/data/ent/document/where.go @@ -464,34 +464,6 @@ func HasGroupWith(preds ...predicate.Group) predicate.Document { }) } -// HasDocumentTokens applies the HasEdge predicate on the "document_tokens" edge. -func HasDocumentTokens() predicate.Document { - return predicate.Document(func(s *sql.Selector) { - step := sqlgraph.NewStep( - sqlgraph.From(Table, FieldID), - sqlgraph.To(DocumentTokensTable, FieldID), - sqlgraph.Edge(sqlgraph.O2M, false, DocumentTokensTable, DocumentTokensColumn), - ) - sqlgraph.HasNeighbors(s, step) - }) -} - -// HasDocumentTokensWith applies the HasEdge predicate on the "document_tokens" edge with a given conditions (other predicates). -func HasDocumentTokensWith(preds ...predicate.DocumentToken) predicate.Document { - return predicate.Document(func(s *sql.Selector) { - step := sqlgraph.NewStep( - sqlgraph.From(Table, FieldID), - sqlgraph.To(DocumentTokensInverseTable, FieldID), - sqlgraph.Edge(sqlgraph.O2M, false, DocumentTokensTable, DocumentTokensColumn), - ) - sqlgraph.HasNeighborsWith(s, step, func(s *sql.Selector) { - for _, p := range preds { - p(s) - } - }) - }) -} - // HasAttachments applies the HasEdge predicate on the "attachments" edge. func HasAttachments() predicate.Document { return predicate.Document(func(s *sql.Selector) { diff --git a/backend/internal/data/ent/document_create.go b/backend/internal/data/ent/document_create.go index b3969d6..67b5baa 100644 --- a/backend/internal/data/ent/document_create.go +++ b/backend/internal/data/ent/document_create.go @@ -13,7 +13,6 @@ import ( "github.com/google/uuid" "github.com/hay-kot/homebox/backend/internal/data/ent/attachment" "github.com/hay-kot/homebox/backend/internal/data/ent/document" - "github.com/hay-kot/homebox/backend/internal/data/ent/documenttoken" "github.com/hay-kot/homebox/backend/internal/data/ent/group" ) @@ -89,21 +88,6 @@ func (dc *DocumentCreate) SetGroup(g *Group) *DocumentCreate { return dc.SetGroupID(g.ID) } -// AddDocumentTokenIDs adds the "document_tokens" edge to the DocumentToken entity by IDs. -func (dc *DocumentCreate) AddDocumentTokenIDs(ids ...uuid.UUID) *DocumentCreate { - dc.mutation.AddDocumentTokenIDs(ids...) - return dc -} - -// AddDocumentTokens adds the "document_tokens" edges to the DocumentToken entity. -func (dc *DocumentCreate) AddDocumentTokens(d ...*DocumentToken) *DocumentCreate { - ids := make([]uuid.UUID, len(d)) - for i := range d { - ids[i] = d[i].ID - } - return dc.AddDocumentTokenIDs(ids...) -} - // AddAttachmentIDs adds the "attachments" edge to the Attachment entity by IDs. func (dc *DocumentCreate) AddAttachmentIDs(ids ...uuid.UUID) *DocumentCreate { dc.mutation.AddAttachmentIDs(ids...) @@ -309,25 +293,6 @@ func (dc *DocumentCreate) createSpec() (*Document, *sqlgraph.CreateSpec) { _node.group_documents = &nodes[0] _spec.Edges = append(_spec.Edges, edge) } - if nodes := dc.mutation.DocumentTokensIDs(); len(nodes) > 0 { - edge := &sqlgraph.EdgeSpec{ - Rel: sqlgraph.O2M, - Inverse: false, - Table: document.DocumentTokensTable, - Columns: []string{document.DocumentTokensColumn}, - Bidi: false, - Target: &sqlgraph.EdgeTarget{ - IDSpec: &sqlgraph.FieldSpec{ - Type: field.TypeUUID, - Column: documenttoken.FieldID, - }, - }, - } - for _, k := range nodes { - edge.Target.Nodes = append(edge.Target.Nodes, k) - } - _spec.Edges = append(_spec.Edges, edge) - } if nodes := dc.mutation.AttachmentsIDs(); len(nodes) > 0 { edge := &sqlgraph.EdgeSpec{ Rel: sqlgraph.O2M, diff --git a/backend/internal/data/ent/document_query.go b/backend/internal/data/ent/document_query.go index 496de53..5ef4f67 100644 --- a/backend/internal/data/ent/document_query.go +++ b/backend/internal/data/ent/document_query.go @@ -14,7 +14,6 @@ import ( "github.com/google/uuid" "github.com/hay-kot/homebox/backend/internal/data/ent/attachment" "github.com/hay-kot/homebox/backend/internal/data/ent/document" - "github.com/hay-kot/homebox/backend/internal/data/ent/documenttoken" "github.com/hay-kot/homebox/backend/internal/data/ent/group" "github.com/hay-kot/homebox/backend/internal/data/ent/predicate" ) @@ -22,16 +21,15 @@ import ( // DocumentQuery is the builder for querying Document entities. type DocumentQuery struct { config - limit *int - offset *int - unique *bool - order []OrderFunc - fields []string - predicates []predicate.Document - withGroup *GroupQuery - withDocumentTokens *DocumentTokenQuery - withAttachments *AttachmentQuery - withFKs bool + limit *int + offset *int + unique *bool + order []OrderFunc + fields []string + predicates []predicate.Document + withGroup *GroupQuery + withAttachments *AttachmentQuery + withFKs bool // intermediate query (i.e. traversal path). sql *sql.Selector path func(context.Context) (*sql.Selector, error) @@ -90,28 +88,6 @@ func (dq *DocumentQuery) QueryGroup() *GroupQuery { return query } -// QueryDocumentTokens chains the current query on the "document_tokens" edge. -func (dq *DocumentQuery) QueryDocumentTokens() *DocumentTokenQuery { - query := &DocumentTokenQuery{config: dq.config} - query.path = func(ctx context.Context) (fromU *sql.Selector, err error) { - if err := dq.prepareQuery(ctx); err != nil { - return nil, err - } - selector := dq.sqlQuery(ctx) - if err := selector.Err(); err != nil { - return nil, err - } - step := sqlgraph.NewStep( - sqlgraph.From(document.Table, document.FieldID, selector), - sqlgraph.To(documenttoken.Table, documenttoken.FieldID), - sqlgraph.Edge(sqlgraph.O2M, false, document.DocumentTokensTable, document.DocumentTokensColumn), - ) - fromU = sqlgraph.SetNeighbors(dq.driver.Dialect(), step) - return fromU, nil - } - return query -} - // QueryAttachments chains the current query on the "attachments" edge. func (dq *DocumentQuery) QueryAttachments() *AttachmentQuery { query := &AttachmentQuery{config: dq.config} @@ -310,14 +286,13 @@ func (dq *DocumentQuery) Clone() *DocumentQuery { return nil } return &DocumentQuery{ - config: dq.config, - limit: dq.limit, - offset: dq.offset, - order: append([]OrderFunc{}, dq.order...), - predicates: append([]predicate.Document{}, dq.predicates...), - withGroup: dq.withGroup.Clone(), - withDocumentTokens: dq.withDocumentTokens.Clone(), - withAttachments: dq.withAttachments.Clone(), + config: dq.config, + limit: dq.limit, + offset: dq.offset, + order: append([]OrderFunc{}, dq.order...), + predicates: append([]predicate.Document{}, dq.predicates...), + withGroup: dq.withGroup.Clone(), + withAttachments: dq.withAttachments.Clone(), // clone intermediate query. sql: dq.sql.Clone(), path: dq.path, @@ -336,17 +311,6 @@ func (dq *DocumentQuery) WithGroup(opts ...func(*GroupQuery)) *DocumentQuery { return dq } -// WithDocumentTokens tells the query-builder to eager-load the nodes that are connected to -// the "document_tokens" edge. The optional arguments are used to configure the query builder of the edge. -func (dq *DocumentQuery) WithDocumentTokens(opts ...func(*DocumentTokenQuery)) *DocumentQuery { - query := &DocumentTokenQuery{config: dq.config} - for _, opt := range opts { - opt(query) - } - dq.withDocumentTokens = query - return dq -} - // WithAttachments tells the query-builder to eager-load the nodes that are connected to // the "attachments" edge. The optional arguments are used to configure the query builder of the edge. func (dq *DocumentQuery) WithAttachments(opts ...func(*AttachmentQuery)) *DocumentQuery { @@ -432,9 +396,8 @@ func (dq *DocumentQuery) sqlAll(ctx context.Context, hooks ...queryHook) ([]*Doc nodes = []*Document{} withFKs = dq.withFKs _spec = dq.querySpec() - loadedTypes = [3]bool{ + loadedTypes = [2]bool{ dq.withGroup != nil, - dq.withDocumentTokens != nil, dq.withAttachments != nil, } ) @@ -468,13 +431,6 @@ func (dq *DocumentQuery) sqlAll(ctx context.Context, hooks ...queryHook) ([]*Doc return nil, err } } - if query := dq.withDocumentTokens; query != nil { - if err := dq.loadDocumentTokens(ctx, query, nodes, - func(n *Document) { n.Edges.DocumentTokens = []*DocumentToken{} }, - func(n *Document, e *DocumentToken) { n.Edges.DocumentTokens = append(n.Edges.DocumentTokens, e) }); err != nil { - return nil, err - } - } if query := dq.withAttachments; query != nil { if err := dq.loadAttachments(ctx, query, nodes, func(n *Document) { n.Edges.Attachments = []*Attachment{} }, @@ -514,37 +470,6 @@ func (dq *DocumentQuery) loadGroup(ctx context.Context, query *GroupQuery, nodes } return nil } -func (dq *DocumentQuery) loadDocumentTokens(ctx context.Context, query *DocumentTokenQuery, nodes []*Document, init func(*Document), assign func(*Document, *DocumentToken)) error { - fks := make([]driver.Value, 0, len(nodes)) - nodeids := make(map[uuid.UUID]*Document) - for i := range nodes { - fks = append(fks, nodes[i].ID) - nodeids[nodes[i].ID] = nodes[i] - if init != nil { - init(nodes[i]) - } - } - query.withFKs = true - query.Where(predicate.DocumentToken(func(s *sql.Selector) { - s.Where(sql.InValues(document.DocumentTokensColumn, fks...)) - })) - neighbors, err := query.All(ctx) - if err != nil { - return err - } - for _, n := range neighbors { - fk := n.document_document_tokens - if fk == nil { - return fmt.Errorf(`foreign-key "document_document_tokens" is nil for node %v`, n.ID) - } - node, ok := nodeids[*fk] - if !ok { - return fmt.Errorf(`unexpected foreign-key "document_document_tokens" returned %v for node %v`, *fk, n.ID) - } - assign(node, n) - } - return nil -} func (dq *DocumentQuery) loadAttachments(ctx context.Context, query *AttachmentQuery, nodes []*Document, init func(*Document), assign func(*Document, *Attachment)) error { fks := make([]driver.Value, 0, len(nodes)) nodeids := make(map[uuid.UUID]*Document) diff --git a/backend/internal/data/ent/document_update.go b/backend/internal/data/ent/document_update.go index 880df95..4a7ae7e 100644 --- a/backend/internal/data/ent/document_update.go +++ b/backend/internal/data/ent/document_update.go @@ -14,7 +14,6 @@ import ( "github.com/google/uuid" "github.com/hay-kot/homebox/backend/internal/data/ent/attachment" "github.com/hay-kot/homebox/backend/internal/data/ent/document" - "github.com/hay-kot/homebox/backend/internal/data/ent/documenttoken" "github.com/hay-kot/homebox/backend/internal/data/ent/group" "github.com/hay-kot/homebox/backend/internal/data/ent/predicate" ) @@ -61,21 +60,6 @@ func (du *DocumentUpdate) SetGroup(g *Group) *DocumentUpdate { return du.SetGroupID(g.ID) } -// AddDocumentTokenIDs adds the "document_tokens" edge to the DocumentToken entity by IDs. -func (du *DocumentUpdate) AddDocumentTokenIDs(ids ...uuid.UUID) *DocumentUpdate { - du.mutation.AddDocumentTokenIDs(ids...) - return du -} - -// AddDocumentTokens adds the "document_tokens" edges to the DocumentToken entity. -func (du *DocumentUpdate) AddDocumentTokens(d ...*DocumentToken) *DocumentUpdate { - ids := make([]uuid.UUID, len(d)) - for i := range d { - ids[i] = d[i].ID - } - return du.AddDocumentTokenIDs(ids...) -} - // AddAttachmentIDs adds the "attachments" edge to the Attachment entity by IDs. func (du *DocumentUpdate) AddAttachmentIDs(ids ...uuid.UUID) *DocumentUpdate { du.mutation.AddAttachmentIDs(ids...) @@ -102,27 +86,6 @@ func (du *DocumentUpdate) ClearGroup() *DocumentUpdate { return du } -// ClearDocumentTokens clears all "document_tokens" edges to the DocumentToken entity. -func (du *DocumentUpdate) ClearDocumentTokens() *DocumentUpdate { - du.mutation.ClearDocumentTokens() - return du -} - -// RemoveDocumentTokenIDs removes the "document_tokens" edge to DocumentToken entities by IDs. -func (du *DocumentUpdate) RemoveDocumentTokenIDs(ids ...uuid.UUID) *DocumentUpdate { - du.mutation.RemoveDocumentTokenIDs(ids...) - return du -} - -// RemoveDocumentTokens removes "document_tokens" edges to DocumentToken entities. -func (du *DocumentUpdate) RemoveDocumentTokens(d ...*DocumentToken) *DocumentUpdate { - ids := make([]uuid.UUID, len(d)) - for i := range d { - ids[i] = d[i].ID - } - return du.RemoveDocumentTokenIDs(ids...) -} - // ClearAttachments clears all "attachments" edges to the Attachment entity. func (du *DocumentUpdate) ClearAttachments() *DocumentUpdate { du.mutation.ClearAttachments() @@ -293,60 +256,6 @@ func (du *DocumentUpdate) sqlSave(ctx context.Context) (n int, err error) { } _spec.Edges.Add = append(_spec.Edges.Add, edge) } - if du.mutation.DocumentTokensCleared() { - edge := &sqlgraph.EdgeSpec{ - Rel: sqlgraph.O2M, - Inverse: false, - Table: document.DocumentTokensTable, - Columns: []string{document.DocumentTokensColumn}, - Bidi: false, - Target: &sqlgraph.EdgeTarget{ - IDSpec: &sqlgraph.FieldSpec{ - Type: field.TypeUUID, - Column: documenttoken.FieldID, - }, - }, - } - _spec.Edges.Clear = append(_spec.Edges.Clear, edge) - } - if nodes := du.mutation.RemovedDocumentTokensIDs(); len(nodes) > 0 && !du.mutation.DocumentTokensCleared() { - edge := &sqlgraph.EdgeSpec{ - Rel: sqlgraph.O2M, - Inverse: false, - Table: document.DocumentTokensTable, - Columns: []string{document.DocumentTokensColumn}, - Bidi: false, - Target: &sqlgraph.EdgeTarget{ - IDSpec: &sqlgraph.FieldSpec{ - Type: field.TypeUUID, - Column: documenttoken.FieldID, - }, - }, - } - for _, k := range nodes { - edge.Target.Nodes = append(edge.Target.Nodes, k) - } - _spec.Edges.Clear = append(_spec.Edges.Clear, edge) - } - if nodes := du.mutation.DocumentTokensIDs(); len(nodes) > 0 { - edge := &sqlgraph.EdgeSpec{ - Rel: sqlgraph.O2M, - Inverse: false, - Table: document.DocumentTokensTable, - Columns: []string{document.DocumentTokensColumn}, - Bidi: false, - Target: &sqlgraph.EdgeTarget{ - IDSpec: &sqlgraph.FieldSpec{ - Type: field.TypeUUID, - Column: documenttoken.FieldID, - }, - }, - } - for _, k := range nodes { - edge.Target.Nodes = append(edge.Target.Nodes, k) - } - _spec.Edges.Add = append(_spec.Edges.Add, edge) - } if du.mutation.AttachmentsCleared() { edge := &sqlgraph.EdgeSpec{ Rel: sqlgraph.O2M, @@ -449,21 +358,6 @@ func (duo *DocumentUpdateOne) SetGroup(g *Group) *DocumentUpdateOne { return duo.SetGroupID(g.ID) } -// AddDocumentTokenIDs adds the "document_tokens" edge to the DocumentToken entity by IDs. -func (duo *DocumentUpdateOne) AddDocumentTokenIDs(ids ...uuid.UUID) *DocumentUpdateOne { - duo.mutation.AddDocumentTokenIDs(ids...) - return duo -} - -// AddDocumentTokens adds the "document_tokens" edges to the DocumentToken entity. -func (duo *DocumentUpdateOne) AddDocumentTokens(d ...*DocumentToken) *DocumentUpdateOne { - ids := make([]uuid.UUID, len(d)) - for i := range d { - ids[i] = d[i].ID - } - return duo.AddDocumentTokenIDs(ids...) -} - // AddAttachmentIDs adds the "attachments" edge to the Attachment entity by IDs. func (duo *DocumentUpdateOne) AddAttachmentIDs(ids ...uuid.UUID) *DocumentUpdateOne { duo.mutation.AddAttachmentIDs(ids...) @@ -490,27 +384,6 @@ func (duo *DocumentUpdateOne) ClearGroup() *DocumentUpdateOne { return duo } -// ClearDocumentTokens clears all "document_tokens" edges to the DocumentToken entity. -func (duo *DocumentUpdateOne) ClearDocumentTokens() *DocumentUpdateOne { - duo.mutation.ClearDocumentTokens() - return duo -} - -// RemoveDocumentTokenIDs removes the "document_tokens" edge to DocumentToken entities by IDs. -func (duo *DocumentUpdateOne) RemoveDocumentTokenIDs(ids ...uuid.UUID) *DocumentUpdateOne { - duo.mutation.RemoveDocumentTokenIDs(ids...) - return duo -} - -// RemoveDocumentTokens removes "document_tokens" edges to DocumentToken entities. -func (duo *DocumentUpdateOne) RemoveDocumentTokens(d ...*DocumentToken) *DocumentUpdateOne { - ids := make([]uuid.UUID, len(d)) - for i := range d { - ids[i] = d[i].ID - } - return duo.RemoveDocumentTokenIDs(ids...) -} - // ClearAttachments clears all "attachments" edges to the Attachment entity. func (duo *DocumentUpdateOne) ClearAttachments() *DocumentUpdateOne { duo.mutation.ClearAttachments() @@ -711,60 +584,6 @@ func (duo *DocumentUpdateOne) sqlSave(ctx context.Context) (_node *Document, err } _spec.Edges.Add = append(_spec.Edges.Add, edge) } - if duo.mutation.DocumentTokensCleared() { - edge := &sqlgraph.EdgeSpec{ - Rel: sqlgraph.O2M, - Inverse: false, - Table: document.DocumentTokensTable, - Columns: []string{document.DocumentTokensColumn}, - Bidi: false, - Target: &sqlgraph.EdgeTarget{ - IDSpec: &sqlgraph.FieldSpec{ - Type: field.TypeUUID, - Column: documenttoken.FieldID, - }, - }, - } - _spec.Edges.Clear = append(_spec.Edges.Clear, edge) - } - if nodes := duo.mutation.RemovedDocumentTokensIDs(); len(nodes) > 0 && !duo.mutation.DocumentTokensCleared() { - edge := &sqlgraph.EdgeSpec{ - Rel: sqlgraph.O2M, - Inverse: false, - Table: document.DocumentTokensTable, - Columns: []string{document.DocumentTokensColumn}, - Bidi: false, - Target: &sqlgraph.EdgeTarget{ - IDSpec: &sqlgraph.FieldSpec{ - Type: field.TypeUUID, - Column: documenttoken.FieldID, - }, - }, - } - for _, k := range nodes { - edge.Target.Nodes = append(edge.Target.Nodes, k) - } - _spec.Edges.Clear = append(_spec.Edges.Clear, edge) - } - if nodes := duo.mutation.DocumentTokensIDs(); len(nodes) > 0 { - edge := &sqlgraph.EdgeSpec{ - Rel: sqlgraph.O2M, - Inverse: false, - Table: document.DocumentTokensTable, - Columns: []string{document.DocumentTokensColumn}, - Bidi: false, - Target: &sqlgraph.EdgeTarget{ - IDSpec: &sqlgraph.FieldSpec{ - Type: field.TypeUUID, - Column: documenttoken.FieldID, - }, - }, - } - for _, k := range nodes { - edge.Target.Nodes = append(edge.Target.Nodes, k) - } - _spec.Edges.Add = append(_spec.Edges.Add, edge) - } if duo.mutation.AttachmentsCleared() { edge := &sqlgraph.EdgeSpec{ Rel: sqlgraph.O2M, diff --git a/backend/internal/data/ent/documenttoken.go b/backend/internal/data/ent/documenttoken.go deleted file mode 100644 index c484a9e..0000000 --- a/backend/internal/data/ent/documenttoken.go +++ /dev/null @@ -1,190 +0,0 @@ -// Code generated by ent, DO NOT EDIT. - -package ent - -import ( - "fmt" - "strings" - "time" - - "entgo.io/ent/dialect/sql" - "github.com/google/uuid" - "github.com/hay-kot/homebox/backend/internal/data/ent/document" - "github.com/hay-kot/homebox/backend/internal/data/ent/documenttoken" -) - -// DocumentToken is the model entity for the DocumentToken schema. -type DocumentToken struct { - config `json:"-"` - // ID of the ent. - ID uuid.UUID `json:"id,omitempty"` - // CreatedAt holds the value of the "created_at" field. - CreatedAt time.Time `json:"created_at,omitempty"` - // UpdatedAt holds the value of the "updated_at" field. - UpdatedAt time.Time `json:"updated_at,omitempty"` - // Token holds the value of the "token" field. - Token []byte `json:"token,omitempty"` - // Uses holds the value of the "uses" field. - Uses int `json:"uses,omitempty"` - // ExpiresAt holds the value of the "expires_at" field. - ExpiresAt time.Time `json:"expires_at,omitempty"` - // Edges holds the relations/edges for other nodes in the graph. - // The values are being populated by the DocumentTokenQuery when eager-loading is set. - Edges DocumentTokenEdges `json:"edges"` - document_document_tokens *uuid.UUID -} - -// DocumentTokenEdges holds the relations/edges for other nodes in the graph. -type DocumentTokenEdges struct { - // Document holds the value of the document edge. - Document *Document `json:"document,omitempty"` - // loadedTypes holds the information for reporting if a - // type was loaded (or requested) in eager-loading or not. - loadedTypes [1]bool -} - -// DocumentOrErr returns the Document value or an error if the edge -// was not loaded in eager-loading, or loaded but was not found. -func (e DocumentTokenEdges) DocumentOrErr() (*Document, error) { - if e.loadedTypes[0] { - if e.Document == nil { - // Edge was loaded but was not found. - return nil, &NotFoundError{label: document.Label} - } - return e.Document, nil - } - return nil, &NotLoadedError{edge: "document"} -} - -// scanValues returns the types for scanning values from sql.Rows. -func (*DocumentToken) scanValues(columns []string) ([]any, error) { - values := make([]any, len(columns)) - for i := range columns { - switch columns[i] { - case documenttoken.FieldToken: - values[i] = new([]byte) - case documenttoken.FieldUses: - values[i] = new(sql.NullInt64) - case documenttoken.FieldCreatedAt, documenttoken.FieldUpdatedAt, documenttoken.FieldExpiresAt: - values[i] = new(sql.NullTime) - case documenttoken.FieldID: - values[i] = new(uuid.UUID) - case documenttoken.ForeignKeys[0]: // document_document_tokens - values[i] = &sql.NullScanner{S: new(uuid.UUID)} - default: - return nil, fmt.Errorf("unexpected column %q for type DocumentToken", columns[i]) - } - } - return values, nil -} - -// assignValues assigns the values that were returned from sql.Rows (after scanning) -// to the DocumentToken fields. -func (dt *DocumentToken) assignValues(columns []string, values []any) error { - if m, n := len(values), len(columns); m < n { - return fmt.Errorf("mismatch number of scan values: %d != %d", m, n) - } - for i := range columns { - switch columns[i] { - case documenttoken.FieldID: - if value, ok := values[i].(*uuid.UUID); !ok { - return fmt.Errorf("unexpected type %T for field id", values[i]) - } else if value != nil { - dt.ID = *value - } - case documenttoken.FieldCreatedAt: - if value, ok := values[i].(*sql.NullTime); !ok { - return fmt.Errorf("unexpected type %T for field created_at", values[i]) - } else if value.Valid { - dt.CreatedAt = value.Time - } - case documenttoken.FieldUpdatedAt: - if value, ok := values[i].(*sql.NullTime); !ok { - return fmt.Errorf("unexpected type %T for field updated_at", values[i]) - } else if value.Valid { - dt.UpdatedAt = value.Time - } - case documenttoken.FieldToken: - if value, ok := values[i].(*[]byte); !ok { - return fmt.Errorf("unexpected type %T for field token", values[i]) - } else if value != nil { - dt.Token = *value - } - case documenttoken.FieldUses: - if value, ok := values[i].(*sql.NullInt64); !ok { - return fmt.Errorf("unexpected type %T for field uses", values[i]) - } else if value.Valid { - dt.Uses = int(value.Int64) - } - case documenttoken.FieldExpiresAt: - if value, ok := values[i].(*sql.NullTime); !ok { - return fmt.Errorf("unexpected type %T for field expires_at", values[i]) - } else if value.Valid { - dt.ExpiresAt = value.Time - } - case documenttoken.ForeignKeys[0]: - if value, ok := values[i].(*sql.NullScanner); !ok { - return fmt.Errorf("unexpected type %T for field document_document_tokens", values[i]) - } else if value.Valid { - dt.document_document_tokens = new(uuid.UUID) - *dt.document_document_tokens = *value.S.(*uuid.UUID) - } - } - } - return nil -} - -// QueryDocument queries the "document" edge of the DocumentToken entity. -func (dt *DocumentToken) QueryDocument() *DocumentQuery { - return (&DocumentTokenClient{config: dt.config}).QueryDocument(dt) -} - -// Update returns a builder for updating this DocumentToken. -// Note that you need to call DocumentToken.Unwrap() before calling this method if this DocumentToken -// was returned from a transaction, and the transaction was committed or rolled back. -func (dt *DocumentToken) Update() *DocumentTokenUpdateOne { - return (&DocumentTokenClient{config: dt.config}).UpdateOne(dt) -} - -// Unwrap unwraps the DocumentToken entity that was returned from a transaction after it was closed, -// so that all future queries will be executed through the driver which created the transaction. -func (dt *DocumentToken) Unwrap() *DocumentToken { - _tx, ok := dt.config.driver.(*txDriver) - if !ok { - panic("ent: DocumentToken is not a transactional entity") - } - dt.config.driver = _tx.drv - return dt -} - -// String implements the fmt.Stringer. -func (dt *DocumentToken) String() string { - var builder strings.Builder - builder.WriteString("DocumentToken(") - builder.WriteString(fmt.Sprintf("id=%v, ", dt.ID)) - builder.WriteString("created_at=") - builder.WriteString(dt.CreatedAt.Format(time.ANSIC)) - builder.WriteString(", ") - builder.WriteString("updated_at=") - builder.WriteString(dt.UpdatedAt.Format(time.ANSIC)) - builder.WriteString(", ") - builder.WriteString("token=") - builder.WriteString(fmt.Sprintf("%v", dt.Token)) - builder.WriteString(", ") - builder.WriteString("uses=") - builder.WriteString(fmt.Sprintf("%v", dt.Uses)) - builder.WriteString(", ") - builder.WriteString("expires_at=") - builder.WriteString(dt.ExpiresAt.Format(time.ANSIC)) - builder.WriteByte(')') - return builder.String() -} - -// DocumentTokens is a parsable slice of DocumentToken. -type DocumentTokens []*DocumentToken - -func (dt DocumentTokens) config(cfg config) { - for _i := range dt { - dt[_i].config = cfg - } -} diff --git a/backend/internal/data/ent/documenttoken/documenttoken.go b/backend/internal/data/ent/documenttoken/documenttoken.go deleted file mode 100644 index ce05656..0000000 --- a/backend/internal/data/ent/documenttoken/documenttoken.go +++ /dev/null @@ -1,85 +0,0 @@ -// Code generated by ent, DO NOT EDIT. - -package documenttoken - -import ( - "time" - - "github.com/google/uuid" -) - -const ( - // Label holds the string label denoting the documenttoken type in the database. - Label = "document_token" - // FieldID holds the string denoting the id field in the database. - FieldID = "id" - // FieldCreatedAt holds the string denoting the created_at field in the database. - FieldCreatedAt = "created_at" - // FieldUpdatedAt holds the string denoting the updated_at field in the database. - FieldUpdatedAt = "updated_at" - // FieldToken holds the string denoting the token field in the database. - FieldToken = "token" - // FieldUses holds the string denoting the uses field in the database. - FieldUses = "uses" - // FieldExpiresAt holds the string denoting the expires_at field in the database. - FieldExpiresAt = "expires_at" - // EdgeDocument holds the string denoting the document edge name in mutations. - EdgeDocument = "document" - // Table holds the table name of the documenttoken in the database. - Table = "document_tokens" - // DocumentTable is the table that holds the document relation/edge. - DocumentTable = "document_tokens" - // DocumentInverseTable is the table name for the Document entity. - // It exists in this package in order to avoid circular dependency with the "document" package. - DocumentInverseTable = "documents" - // DocumentColumn is the table column denoting the document relation/edge. - DocumentColumn = "document_document_tokens" -) - -// Columns holds all SQL columns for documenttoken fields. -var Columns = []string{ - FieldID, - FieldCreatedAt, - FieldUpdatedAt, - FieldToken, - FieldUses, - FieldExpiresAt, -} - -// ForeignKeys holds the SQL foreign-keys that are owned by the "document_tokens" -// table and are not defined as standalone fields in the schema. -var ForeignKeys = []string{ - "document_document_tokens", -} - -// ValidColumn reports if the column name is valid (part of the table columns). -func ValidColumn(column string) bool { - for i := range Columns { - if column == Columns[i] { - return true - } - } - for i := range ForeignKeys { - if column == ForeignKeys[i] { - return true - } - } - return false -} - -var ( - // DefaultCreatedAt holds the default value on creation for the "created_at" field. - DefaultCreatedAt func() time.Time - // DefaultUpdatedAt holds the default value on creation for the "updated_at" field. - DefaultUpdatedAt func() time.Time - // UpdateDefaultUpdatedAt holds the default value on update for the "updated_at" field. - UpdateDefaultUpdatedAt func() time.Time - // TokenValidator is a validator for the "token" field. It is called by the builders before save. - TokenValidator func([]byte) error - // DefaultUses holds the default value on creation for the "uses" field. - DefaultUses int - // DefaultExpiresAt holds the default value on creation for the "expires_at" field. - DefaultExpiresAt func() time.Time - // DefaultID holds the default value on creation for the "id" field. - DefaultID func() uuid.UUID -) diff --git a/backend/internal/data/ent/documenttoken/where.go b/backend/internal/data/ent/documenttoken/where.go deleted file mode 100644 index 32dbb39..0000000 --- a/backend/internal/data/ent/documenttoken/where.go +++ /dev/null @@ -1,498 +0,0 @@ -// Code generated by ent, DO NOT EDIT. - -package documenttoken - -import ( - "time" - - "entgo.io/ent/dialect/sql" - "entgo.io/ent/dialect/sql/sqlgraph" - "github.com/google/uuid" - "github.com/hay-kot/homebox/backend/internal/data/ent/predicate" -) - -// ID filters vertices based on their ID field. -func ID(id uuid.UUID) predicate.DocumentToken { - return predicate.DocumentToken(func(s *sql.Selector) { - s.Where(sql.EQ(s.C(FieldID), id)) - }) -} - -// IDEQ applies the EQ predicate on the ID field. -func IDEQ(id uuid.UUID) predicate.DocumentToken { - return predicate.DocumentToken(func(s *sql.Selector) { - s.Where(sql.EQ(s.C(FieldID), id)) - }) -} - -// IDNEQ applies the NEQ predicate on the ID field. -func IDNEQ(id uuid.UUID) predicate.DocumentToken { - return predicate.DocumentToken(func(s *sql.Selector) { - s.Where(sql.NEQ(s.C(FieldID), id)) - }) -} - -// IDIn applies the In predicate on the ID field. -func IDIn(ids ...uuid.UUID) predicate.DocumentToken { - return predicate.DocumentToken(func(s *sql.Selector) { - v := make([]any, len(ids)) - for i := range v { - v[i] = ids[i] - } - s.Where(sql.In(s.C(FieldID), v...)) - }) -} - -// IDNotIn applies the NotIn predicate on the ID field. -func IDNotIn(ids ...uuid.UUID) predicate.DocumentToken { - return predicate.DocumentToken(func(s *sql.Selector) { - v := make([]any, len(ids)) - for i := range v { - v[i] = ids[i] - } - s.Where(sql.NotIn(s.C(FieldID), v...)) - }) -} - -// IDGT applies the GT predicate on the ID field. -func IDGT(id uuid.UUID) predicate.DocumentToken { - return predicate.DocumentToken(func(s *sql.Selector) { - s.Where(sql.GT(s.C(FieldID), id)) - }) -} - -// IDGTE applies the GTE predicate on the ID field. -func IDGTE(id uuid.UUID) predicate.DocumentToken { - return predicate.DocumentToken(func(s *sql.Selector) { - s.Where(sql.GTE(s.C(FieldID), id)) - }) -} - -// IDLT applies the LT predicate on the ID field. -func IDLT(id uuid.UUID) predicate.DocumentToken { - return predicate.DocumentToken(func(s *sql.Selector) { - s.Where(sql.LT(s.C(FieldID), id)) - }) -} - -// IDLTE applies the LTE predicate on the ID field. -func IDLTE(id uuid.UUID) predicate.DocumentToken { - return predicate.DocumentToken(func(s *sql.Selector) { - s.Where(sql.LTE(s.C(FieldID), id)) - }) -} - -// CreatedAt applies equality check predicate on the "created_at" field. It's identical to CreatedAtEQ. -func CreatedAt(v time.Time) predicate.DocumentToken { - return predicate.DocumentToken(func(s *sql.Selector) { - s.Where(sql.EQ(s.C(FieldCreatedAt), v)) - }) -} - -// UpdatedAt applies equality check predicate on the "updated_at" field. It's identical to UpdatedAtEQ. -func UpdatedAt(v time.Time) predicate.DocumentToken { - return predicate.DocumentToken(func(s *sql.Selector) { - s.Where(sql.EQ(s.C(FieldUpdatedAt), v)) - }) -} - -// Token applies equality check predicate on the "token" field. It's identical to TokenEQ. -func Token(v []byte) predicate.DocumentToken { - return predicate.DocumentToken(func(s *sql.Selector) { - s.Where(sql.EQ(s.C(FieldToken), v)) - }) -} - -// Uses applies equality check predicate on the "uses" field. It's identical to UsesEQ. -func Uses(v int) predicate.DocumentToken { - return predicate.DocumentToken(func(s *sql.Selector) { - s.Where(sql.EQ(s.C(FieldUses), v)) - }) -} - -// ExpiresAt applies equality check predicate on the "expires_at" field. It's identical to ExpiresAtEQ. -func ExpiresAt(v time.Time) predicate.DocumentToken { - return predicate.DocumentToken(func(s *sql.Selector) { - s.Where(sql.EQ(s.C(FieldExpiresAt), v)) - }) -} - -// CreatedAtEQ applies the EQ predicate on the "created_at" field. -func CreatedAtEQ(v time.Time) predicate.DocumentToken { - return predicate.DocumentToken(func(s *sql.Selector) { - s.Where(sql.EQ(s.C(FieldCreatedAt), v)) - }) -} - -// CreatedAtNEQ applies the NEQ predicate on the "created_at" field. -func CreatedAtNEQ(v time.Time) predicate.DocumentToken { - return predicate.DocumentToken(func(s *sql.Selector) { - s.Where(sql.NEQ(s.C(FieldCreatedAt), v)) - }) -} - -// CreatedAtIn applies the In predicate on the "created_at" field. -func CreatedAtIn(vs ...time.Time) predicate.DocumentToken { - v := make([]any, len(vs)) - for i := range v { - v[i] = vs[i] - } - return predicate.DocumentToken(func(s *sql.Selector) { - s.Where(sql.In(s.C(FieldCreatedAt), v...)) - }) -} - -// CreatedAtNotIn applies the NotIn predicate on the "created_at" field. -func CreatedAtNotIn(vs ...time.Time) predicate.DocumentToken { - v := make([]any, len(vs)) - for i := range v { - v[i] = vs[i] - } - return predicate.DocumentToken(func(s *sql.Selector) { - s.Where(sql.NotIn(s.C(FieldCreatedAt), v...)) - }) -} - -// CreatedAtGT applies the GT predicate on the "created_at" field. -func CreatedAtGT(v time.Time) predicate.DocumentToken { - return predicate.DocumentToken(func(s *sql.Selector) { - s.Where(sql.GT(s.C(FieldCreatedAt), v)) - }) -} - -// CreatedAtGTE applies the GTE predicate on the "created_at" field. -func CreatedAtGTE(v time.Time) predicate.DocumentToken { - return predicate.DocumentToken(func(s *sql.Selector) { - s.Where(sql.GTE(s.C(FieldCreatedAt), v)) - }) -} - -// CreatedAtLT applies the LT predicate on the "created_at" field. -func CreatedAtLT(v time.Time) predicate.DocumentToken { - return predicate.DocumentToken(func(s *sql.Selector) { - s.Where(sql.LT(s.C(FieldCreatedAt), v)) - }) -} - -// CreatedAtLTE applies the LTE predicate on the "created_at" field. -func CreatedAtLTE(v time.Time) predicate.DocumentToken { - return predicate.DocumentToken(func(s *sql.Selector) { - s.Where(sql.LTE(s.C(FieldCreatedAt), v)) - }) -} - -// UpdatedAtEQ applies the EQ predicate on the "updated_at" field. -func UpdatedAtEQ(v time.Time) predicate.DocumentToken { - return predicate.DocumentToken(func(s *sql.Selector) { - s.Where(sql.EQ(s.C(FieldUpdatedAt), v)) - }) -} - -// UpdatedAtNEQ applies the NEQ predicate on the "updated_at" field. -func UpdatedAtNEQ(v time.Time) predicate.DocumentToken { - return predicate.DocumentToken(func(s *sql.Selector) { - s.Where(sql.NEQ(s.C(FieldUpdatedAt), v)) - }) -} - -// UpdatedAtIn applies the In predicate on the "updated_at" field. -func UpdatedAtIn(vs ...time.Time) predicate.DocumentToken { - v := make([]any, len(vs)) - for i := range v { - v[i] = vs[i] - } - return predicate.DocumentToken(func(s *sql.Selector) { - s.Where(sql.In(s.C(FieldUpdatedAt), v...)) - }) -} - -// UpdatedAtNotIn applies the NotIn predicate on the "updated_at" field. -func UpdatedAtNotIn(vs ...time.Time) predicate.DocumentToken { - v := make([]any, len(vs)) - for i := range v { - v[i] = vs[i] - } - return predicate.DocumentToken(func(s *sql.Selector) { - s.Where(sql.NotIn(s.C(FieldUpdatedAt), v...)) - }) -} - -// UpdatedAtGT applies the GT predicate on the "updated_at" field. -func UpdatedAtGT(v time.Time) predicate.DocumentToken { - return predicate.DocumentToken(func(s *sql.Selector) { - s.Where(sql.GT(s.C(FieldUpdatedAt), v)) - }) -} - -// UpdatedAtGTE applies the GTE predicate on the "updated_at" field. -func UpdatedAtGTE(v time.Time) predicate.DocumentToken { - return predicate.DocumentToken(func(s *sql.Selector) { - s.Where(sql.GTE(s.C(FieldUpdatedAt), v)) - }) -} - -// UpdatedAtLT applies the LT predicate on the "updated_at" field. -func UpdatedAtLT(v time.Time) predicate.DocumentToken { - return predicate.DocumentToken(func(s *sql.Selector) { - s.Where(sql.LT(s.C(FieldUpdatedAt), v)) - }) -} - -// UpdatedAtLTE applies the LTE predicate on the "updated_at" field. -func UpdatedAtLTE(v time.Time) predicate.DocumentToken { - return predicate.DocumentToken(func(s *sql.Selector) { - s.Where(sql.LTE(s.C(FieldUpdatedAt), v)) - }) -} - -// TokenEQ applies the EQ predicate on the "token" field. -func TokenEQ(v []byte) predicate.DocumentToken { - return predicate.DocumentToken(func(s *sql.Selector) { - s.Where(sql.EQ(s.C(FieldToken), v)) - }) -} - -// TokenNEQ applies the NEQ predicate on the "token" field. -func TokenNEQ(v []byte) predicate.DocumentToken { - return predicate.DocumentToken(func(s *sql.Selector) { - s.Where(sql.NEQ(s.C(FieldToken), v)) - }) -} - -// TokenIn applies the In predicate on the "token" field. -func TokenIn(vs ...[]byte) predicate.DocumentToken { - v := make([]any, len(vs)) - for i := range v { - v[i] = vs[i] - } - return predicate.DocumentToken(func(s *sql.Selector) { - s.Where(sql.In(s.C(FieldToken), v...)) - }) -} - -// TokenNotIn applies the NotIn predicate on the "token" field. -func TokenNotIn(vs ...[]byte) predicate.DocumentToken { - v := make([]any, len(vs)) - for i := range v { - v[i] = vs[i] - } - return predicate.DocumentToken(func(s *sql.Selector) { - s.Where(sql.NotIn(s.C(FieldToken), v...)) - }) -} - -// TokenGT applies the GT predicate on the "token" field. -func TokenGT(v []byte) predicate.DocumentToken { - return predicate.DocumentToken(func(s *sql.Selector) { - s.Where(sql.GT(s.C(FieldToken), v)) - }) -} - -// TokenGTE applies the GTE predicate on the "token" field. -func TokenGTE(v []byte) predicate.DocumentToken { - return predicate.DocumentToken(func(s *sql.Selector) { - s.Where(sql.GTE(s.C(FieldToken), v)) - }) -} - -// TokenLT applies the LT predicate on the "token" field. -func TokenLT(v []byte) predicate.DocumentToken { - return predicate.DocumentToken(func(s *sql.Selector) { - s.Where(sql.LT(s.C(FieldToken), v)) - }) -} - -// TokenLTE applies the LTE predicate on the "token" field. -func TokenLTE(v []byte) predicate.DocumentToken { - return predicate.DocumentToken(func(s *sql.Selector) { - s.Where(sql.LTE(s.C(FieldToken), v)) - }) -} - -// UsesEQ applies the EQ predicate on the "uses" field. -func UsesEQ(v int) predicate.DocumentToken { - return predicate.DocumentToken(func(s *sql.Selector) { - s.Where(sql.EQ(s.C(FieldUses), v)) - }) -} - -// UsesNEQ applies the NEQ predicate on the "uses" field. -func UsesNEQ(v int) predicate.DocumentToken { - return predicate.DocumentToken(func(s *sql.Selector) { - s.Where(sql.NEQ(s.C(FieldUses), v)) - }) -} - -// UsesIn applies the In predicate on the "uses" field. -func UsesIn(vs ...int) predicate.DocumentToken { - v := make([]any, len(vs)) - for i := range v { - v[i] = vs[i] - } - return predicate.DocumentToken(func(s *sql.Selector) { - s.Where(sql.In(s.C(FieldUses), v...)) - }) -} - -// UsesNotIn applies the NotIn predicate on the "uses" field. -func UsesNotIn(vs ...int) predicate.DocumentToken { - v := make([]any, len(vs)) - for i := range v { - v[i] = vs[i] - } - return predicate.DocumentToken(func(s *sql.Selector) { - s.Where(sql.NotIn(s.C(FieldUses), v...)) - }) -} - -// UsesGT applies the GT predicate on the "uses" field. -func UsesGT(v int) predicate.DocumentToken { - return predicate.DocumentToken(func(s *sql.Selector) { - s.Where(sql.GT(s.C(FieldUses), v)) - }) -} - -// UsesGTE applies the GTE predicate on the "uses" field. -func UsesGTE(v int) predicate.DocumentToken { - return predicate.DocumentToken(func(s *sql.Selector) { - s.Where(sql.GTE(s.C(FieldUses), v)) - }) -} - -// UsesLT applies the LT predicate on the "uses" field. -func UsesLT(v int) predicate.DocumentToken { - return predicate.DocumentToken(func(s *sql.Selector) { - s.Where(sql.LT(s.C(FieldUses), v)) - }) -} - -// UsesLTE applies the LTE predicate on the "uses" field. -func UsesLTE(v int) predicate.DocumentToken { - return predicate.DocumentToken(func(s *sql.Selector) { - s.Where(sql.LTE(s.C(FieldUses), v)) - }) -} - -// ExpiresAtEQ applies the EQ predicate on the "expires_at" field. -func ExpiresAtEQ(v time.Time) predicate.DocumentToken { - return predicate.DocumentToken(func(s *sql.Selector) { - s.Where(sql.EQ(s.C(FieldExpiresAt), v)) - }) -} - -// ExpiresAtNEQ applies the NEQ predicate on the "expires_at" field. -func ExpiresAtNEQ(v time.Time) predicate.DocumentToken { - return predicate.DocumentToken(func(s *sql.Selector) { - s.Where(sql.NEQ(s.C(FieldExpiresAt), v)) - }) -} - -// ExpiresAtIn applies the In predicate on the "expires_at" field. -func ExpiresAtIn(vs ...time.Time) predicate.DocumentToken { - v := make([]any, len(vs)) - for i := range v { - v[i] = vs[i] - } - return predicate.DocumentToken(func(s *sql.Selector) { - s.Where(sql.In(s.C(FieldExpiresAt), v...)) - }) -} - -// ExpiresAtNotIn applies the NotIn predicate on the "expires_at" field. -func ExpiresAtNotIn(vs ...time.Time) predicate.DocumentToken { - v := make([]any, len(vs)) - for i := range v { - v[i] = vs[i] - } - return predicate.DocumentToken(func(s *sql.Selector) { - s.Where(sql.NotIn(s.C(FieldExpiresAt), v...)) - }) -} - -// ExpiresAtGT applies the GT predicate on the "expires_at" field. -func ExpiresAtGT(v time.Time) predicate.DocumentToken { - return predicate.DocumentToken(func(s *sql.Selector) { - s.Where(sql.GT(s.C(FieldExpiresAt), v)) - }) -} - -// ExpiresAtGTE applies the GTE predicate on the "expires_at" field. -func ExpiresAtGTE(v time.Time) predicate.DocumentToken { - return predicate.DocumentToken(func(s *sql.Selector) { - s.Where(sql.GTE(s.C(FieldExpiresAt), v)) - }) -} - -// ExpiresAtLT applies the LT predicate on the "expires_at" field. -func ExpiresAtLT(v time.Time) predicate.DocumentToken { - return predicate.DocumentToken(func(s *sql.Selector) { - s.Where(sql.LT(s.C(FieldExpiresAt), v)) - }) -} - -// ExpiresAtLTE applies the LTE predicate on the "expires_at" field. -func ExpiresAtLTE(v time.Time) predicate.DocumentToken { - return predicate.DocumentToken(func(s *sql.Selector) { - s.Where(sql.LTE(s.C(FieldExpiresAt), v)) - }) -} - -// HasDocument applies the HasEdge predicate on the "document" edge. -func HasDocument() predicate.DocumentToken { - return predicate.DocumentToken(func(s *sql.Selector) { - step := sqlgraph.NewStep( - sqlgraph.From(Table, FieldID), - sqlgraph.To(DocumentTable, FieldID), - sqlgraph.Edge(sqlgraph.M2O, true, DocumentTable, DocumentColumn), - ) - sqlgraph.HasNeighbors(s, step) - }) -} - -// HasDocumentWith applies the HasEdge predicate on the "document" edge with a given conditions (other predicates). -func HasDocumentWith(preds ...predicate.Document) predicate.DocumentToken { - return predicate.DocumentToken(func(s *sql.Selector) { - step := sqlgraph.NewStep( - sqlgraph.From(Table, FieldID), - sqlgraph.To(DocumentInverseTable, FieldID), - sqlgraph.Edge(sqlgraph.M2O, true, DocumentTable, DocumentColumn), - ) - sqlgraph.HasNeighborsWith(s, step, func(s *sql.Selector) { - for _, p := range preds { - p(s) - } - }) - }) -} - -// And groups predicates with the AND operator between them. -func And(predicates ...predicate.DocumentToken) predicate.DocumentToken { - return predicate.DocumentToken(func(s *sql.Selector) { - s1 := s.Clone().SetP(nil) - for _, p := range predicates { - p(s1) - } - s.Where(s1.P()) - }) -} - -// Or groups predicates with the OR operator between them. -func Or(predicates ...predicate.DocumentToken) predicate.DocumentToken { - return predicate.DocumentToken(func(s *sql.Selector) { - s1 := s.Clone().SetP(nil) - for i, p := range predicates { - if i > 0 { - s1.Or() - } - p(s1) - } - s.Where(s1.P()) - }) -} - -// Not applies the not operator on the given predicate. -func Not(p predicate.DocumentToken) predicate.DocumentToken { - return predicate.DocumentToken(func(s *sql.Selector) { - p(s.Not()) - }) -} diff --git a/backend/internal/data/ent/documenttoken_create.go b/backend/internal/data/ent/documenttoken_create.go deleted file mode 100644 index c937279..0000000 --- a/backend/internal/data/ent/documenttoken_create.go +++ /dev/null @@ -1,398 +0,0 @@ -// Code generated by ent, DO NOT EDIT. - -package ent - -import ( - "context" - "errors" - "fmt" - "time" - - "entgo.io/ent/dialect/sql/sqlgraph" - "entgo.io/ent/schema/field" - "github.com/google/uuid" - "github.com/hay-kot/homebox/backend/internal/data/ent/document" - "github.com/hay-kot/homebox/backend/internal/data/ent/documenttoken" -) - -// DocumentTokenCreate is the builder for creating a DocumentToken entity. -type DocumentTokenCreate struct { - config - mutation *DocumentTokenMutation - hooks []Hook -} - -// SetCreatedAt sets the "created_at" field. -func (dtc *DocumentTokenCreate) SetCreatedAt(t time.Time) *DocumentTokenCreate { - dtc.mutation.SetCreatedAt(t) - return dtc -} - -// SetNillableCreatedAt sets the "created_at" field if the given value is not nil. -func (dtc *DocumentTokenCreate) SetNillableCreatedAt(t *time.Time) *DocumentTokenCreate { - if t != nil { - dtc.SetCreatedAt(*t) - } - return dtc -} - -// SetUpdatedAt sets the "updated_at" field. -func (dtc *DocumentTokenCreate) SetUpdatedAt(t time.Time) *DocumentTokenCreate { - dtc.mutation.SetUpdatedAt(t) - return dtc -} - -// SetNillableUpdatedAt sets the "updated_at" field if the given value is not nil. -func (dtc *DocumentTokenCreate) SetNillableUpdatedAt(t *time.Time) *DocumentTokenCreate { - if t != nil { - dtc.SetUpdatedAt(*t) - } - return dtc -} - -// SetToken sets the "token" field. -func (dtc *DocumentTokenCreate) SetToken(b []byte) *DocumentTokenCreate { - dtc.mutation.SetToken(b) - return dtc -} - -// SetUses sets the "uses" field. -func (dtc *DocumentTokenCreate) SetUses(i int) *DocumentTokenCreate { - dtc.mutation.SetUses(i) - return dtc -} - -// SetNillableUses sets the "uses" field if the given value is not nil. -func (dtc *DocumentTokenCreate) SetNillableUses(i *int) *DocumentTokenCreate { - if i != nil { - dtc.SetUses(*i) - } - return dtc -} - -// SetExpiresAt sets the "expires_at" field. -func (dtc *DocumentTokenCreate) SetExpiresAt(t time.Time) *DocumentTokenCreate { - dtc.mutation.SetExpiresAt(t) - return dtc -} - -// SetNillableExpiresAt sets the "expires_at" field if the given value is not nil. -func (dtc *DocumentTokenCreate) SetNillableExpiresAt(t *time.Time) *DocumentTokenCreate { - if t != nil { - dtc.SetExpiresAt(*t) - } - return dtc -} - -// SetID sets the "id" field. -func (dtc *DocumentTokenCreate) SetID(u uuid.UUID) *DocumentTokenCreate { - dtc.mutation.SetID(u) - return dtc -} - -// SetNillableID sets the "id" field if the given value is not nil. -func (dtc *DocumentTokenCreate) SetNillableID(u *uuid.UUID) *DocumentTokenCreate { - if u != nil { - dtc.SetID(*u) - } - return dtc -} - -// SetDocumentID sets the "document" edge to the Document entity by ID. -func (dtc *DocumentTokenCreate) SetDocumentID(id uuid.UUID) *DocumentTokenCreate { - dtc.mutation.SetDocumentID(id) - return dtc -} - -// SetNillableDocumentID sets the "document" edge to the Document entity by ID if the given value is not nil. -func (dtc *DocumentTokenCreate) SetNillableDocumentID(id *uuid.UUID) *DocumentTokenCreate { - if id != nil { - dtc = dtc.SetDocumentID(*id) - } - return dtc -} - -// SetDocument sets the "document" edge to the Document entity. -func (dtc *DocumentTokenCreate) SetDocument(d *Document) *DocumentTokenCreate { - return dtc.SetDocumentID(d.ID) -} - -// Mutation returns the DocumentTokenMutation object of the builder. -func (dtc *DocumentTokenCreate) Mutation() *DocumentTokenMutation { - return dtc.mutation -} - -// Save creates the DocumentToken in the database. -func (dtc *DocumentTokenCreate) Save(ctx context.Context) (*DocumentToken, error) { - var ( - err error - node *DocumentToken - ) - dtc.defaults() - if len(dtc.hooks) == 0 { - if err = dtc.check(); err != nil { - return nil, err - } - node, err = dtc.sqlSave(ctx) - } else { - var mut Mutator = MutateFunc(func(ctx context.Context, m Mutation) (Value, error) { - mutation, ok := m.(*DocumentTokenMutation) - if !ok { - return nil, fmt.Errorf("unexpected mutation type %T", m) - } - if err = dtc.check(); err != nil { - return nil, err - } - dtc.mutation = mutation - if node, err = dtc.sqlSave(ctx); err != nil { - return nil, err - } - mutation.id = &node.ID - mutation.done = true - return node, err - }) - for i := len(dtc.hooks) - 1; i >= 0; i-- { - if dtc.hooks[i] == nil { - return nil, fmt.Errorf("ent: uninitialized hook (forgotten import ent/runtime?)") - } - mut = dtc.hooks[i](mut) - } - v, err := mut.Mutate(ctx, dtc.mutation) - if err != nil { - return nil, err - } - nv, ok := v.(*DocumentToken) - if !ok { - return nil, fmt.Errorf("unexpected node type %T returned from DocumentTokenMutation", v) - } - node = nv - } - return node, err -} - -// SaveX calls Save and panics if Save returns an error. -func (dtc *DocumentTokenCreate) SaveX(ctx context.Context) *DocumentToken { - v, err := dtc.Save(ctx) - if err != nil { - panic(err) - } - return v -} - -// Exec executes the query. -func (dtc *DocumentTokenCreate) Exec(ctx context.Context) error { - _, err := dtc.Save(ctx) - return err -} - -// ExecX is like Exec, but panics if an error occurs. -func (dtc *DocumentTokenCreate) ExecX(ctx context.Context) { - if err := dtc.Exec(ctx); err != nil { - panic(err) - } -} - -// defaults sets the default values of the builder before save. -func (dtc *DocumentTokenCreate) defaults() { - if _, ok := dtc.mutation.CreatedAt(); !ok { - v := documenttoken.DefaultCreatedAt() - dtc.mutation.SetCreatedAt(v) - } - if _, ok := dtc.mutation.UpdatedAt(); !ok { - v := documenttoken.DefaultUpdatedAt() - dtc.mutation.SetUpdatedAt(v) - } - if _, ok := dtc.mutation.Uses(); !ok { - v := documenttoken.DefaultUses - dtc.mutation.SetUses(v) - } - if _, ok := dtc.mutation.ExpiresAt(); !ok { - v := documenttoken.DefaultExpiresAt() - dtc.mutation.SetExpiresAt(v) - } - if _, ok := dtc.mutation.ID(); !ok { - v := documenttoken.DefaultID() - dtc.mutation.SetID(v) - } -} - -// check runs all checks and user-defined validators on the builder. -func (dtc *DocumentTokenCreate) check() error { - if _, ok := dtc.mutation.CreatedAt(); !ok { - return &ValidationError{Name: "created_at", err: errors.New(`ent: missing required field "DocumentToken.created_at"`)} - } - if _, ok := dtc.mutation.UpdatedAt(); !ok { - return &ValidationError{Name: "updated_at", err: errors.New(`ent: missing required field "DocumentToken.updated_at"`)} - } - if _, ok := dtc.mutation.Token(); !ok { - return &ValidationError{Name: "token", err: errors.New(`ent: missing required field "DocumentToken.token"`)} - } - if v, ok := dtc.mutation.Token(); ok { - if err := documenttoken.TokenValidator(v); err != nil { - return &ValidationError{Name: "token", err: fmt.Errorf(`ent: validator failed for field "DocumentToken.token": %w`, err)} - } - } - if _, ok := dtc.mutation.Uses(); !ok { - return &ValidationError{Name: "uses", err: errors.New(`ent: missing required field "DocumentToken.uses"`)} - } - if _, ok := dtc.mutation.ExpiresAt(); !ok { - return &ValidationError{Name: "expires_at", err: errors.New(`ent: missing required field "DocumentToken.expires_at"`)} - } - return nil -} - -func (dtc *DocumentTokenCreate) sqlSave(ctx context.Context) (*DocumentToken, error) { - _node, _spec := dtc.createSpec() - if err := sqlgraph.CreateNode(ctx, dtc.driver, _spec); err != nil { - if sqlgraph.IsConstraintError(err) { - err = &ConstraintError{msg: err.Error(), wrap: err} - } - return nil, err - } - if _spec.ID.Value != nil { - if id, ok := _spec.ID.Value.(*uuid.UUID); ok { - _node.ID = *id - } else if err := _node.ID.Scan(_spec.ID.Value); err != nil { - return nil, err - } - } - return _node, nil -} - -func (dtc *DocumentTokenCreate) createSpec() (*DocumentToken, *sqlgraph.CreateSpec) { - var ( - _node = &DocumentToken{config: dtc.config} - _spec = &sqlgraph.CreateSpec{ - Table: documenttoken.Table, - ID: &sqlgraph.FieldSpec{ - Type: field.TypeUUID, - Column: documenttoken.FieldID, - }, - } - ) - if id, ok := dtc.mutation.ID(); ok { - _node.ID = id - _spec.ID.Value = &id - } - if value, ok := dtc.mutation.CreatedAt(); ok { - _spec.SetField(documenttoken.FieldCreatedAt, field.TypeTime, value) - _node.CreatedAt = value - } - if value, ok := dtc.mutation.UpdatedAt(); ok { - _spec.SetField(documenttoken.FieldUpdatedAt, field.TypeTime, value) - _node.UpdatedAt = value - } - if value, ok := dtc.mutation.Token(); ok { - _spec.SetField(documenttoken.FieldToken, field.TypeBytes, value) - _node.Token = value - } - if value, ok := dtc.mutation.Uses(); ok { - _spec.SetField(documenttoken.FieldUses, field.TypeInt, value) - _node.Uses = value - } - if value, ok := dtc.mutation.ExpiresAt(); ok { - _spec.SetField(documenttoken.FieldExpiresAt, field.TypeTime, value) - _node.ExpiresAt = value - } - if nodes := dtc.mutation.DocumentIDs(); len(nodes) > 0 { - edge := &sqlgraph.EdgeSpec{ - Rel: sqlgraph.M2O, - Inverse: true, - Table: documenttoken.DocumentTable, - Columns: []string{documenttoken.DocumentColumn}, - Bidi: false, - Target: &sqlgraph.EdgeTarget{ - IDSpec: &sqlgraph.FieldSpec{ - Type: field.TypeUUID, - Column: document.FieldID, - }, - }, - } - for _, k := range nodes { - edge.Target.Nodes = append(edge.Target.Nodes, k) - } - _node.document_document_tokens = &nodes[0] - _spec.Edges = append(_spec.Edges, edge) - } - return _node, _spec -} - -// DocumentTokenCreateBulk is the builder for creating many DocumentToken entities in bulk. -type DocumentTokenCreateBulk struct { - config - builders []*DocumentTokenCreate -} - -// Save creates the DocumentToken entities in the database. -func (dtcb *DocumentTokenCreateBulk) Save(ctx context.Context) ([]*DocumentToken, error) { - specs := make([]*sqlgraph.CreateSpec, len(dtcb.builders)) - nodes := make([]*DocumentToken, len(dtcb.builders)) - mutators := make([]Mutator, len(dtcb.builders)) - for i := range dtcb.builders { - func(i int, root context.Context) { - builder := dtcb.builders[i] - builder.defaults() - var mut Mutator = MutateFunc(func(ctx context.Context, m Mutation) (Value, error) { - mutation, ok := m.(*DocumentTokenMutation) - if !ok { - return nil, fmt.Errorf("unexpected mutation type %T", m) - } - if err := builder.check(); err != nil { - return nil, err - } - builder.mutation = mutation - nodes[i], specs[i] = builder.createSpec() - var err error - if i < len(mutators)-1 { - _, err = mutators[i+1].Mutate(root, dtcb.builders[i+1].mutation) - } else { - spec := &sqlgraph.BatchCreateSpec{Nodes: specs} - // Invoke the actual operation on the latest mutation in the chain. - if err = sqlgraph.BatchCreate(ctx, dtcb.driver, spec); err != nil { - if sqlgraph.IsConstraintError(err) { - err = &ConstraintError{msg: err.Error(), wrap: err} - } - } - } - if err != nil { - return nil, err - } - mutation.id = &nodes[i].ID - mutation.done = true - return nodes[i], nil - }) - for i := len(builder.hooks) - 1; i >= 0; i-- { - mut = builder.hooks[i](mut) - } - mutators[i] = mut - }(i, ctx) - } - if len(mutators) > 0 { - if _, err := mutators[0].Mutate(ctx, dtcb.builders[0].mutation); err != nil { - return nil, err - } - } - return nodes, nil -} - -// SaveX is like Save, but panics if an error occurs. -func (dtcb *DocumentTokenCreateBulk) SaveX(ctx context.Context) []*DocumentToken { - v, err := dtcb.Save(ctx) - if err != nil { - panic(err) - } - return v -} - -// Exec executes the query. -func (dtcb *DocumentTokenCreateBulk) Exec(ctx context.Context) error { - _, err := dtcb.Save(ctx) - return err -} - -// ExecX is like Exec, but panics if an error occurs. -func (dtcb *DocumentTokenCreateBulk) ExecX(ctx context.Context) { - if err := dtcb.Exec(ctx); err != nil { - panic(err) - } -} diff --git a/backend/internal/data/ent/documenttoken_delete.go b/backend/internal/data/ent/documenttoken_delete.go deleted file mode 100644 index 722ec1b..0000000 --- a/backend/internal/data/ent/documenttoken_delete.go +++ /dev/null @@ -1,115 +0,0 @@ -// Code generated by ent, DO NOT EDIT. - -package ent - -import ( - "context" - "fmt" - - "entgo.io/ent/dialect/sql" - "entgo.io/ent/dialect/sql/sqlgraph" - "entgo.io/ent/schema/field" - "github.com/hay-kot/homebox/backend/internal/data/ent/documenttoken" - "github.com/hay-kot/homebox/backend/internal/data/ent/predicate" -) - -// DocumentTokenDelete is the builder for deleting a DocumentToken entity. -type DocumentTokenDelete struct { - config - hooks []Hook - mutation *DocumentTokenMutation -} - -// Where appends a list predicates to the DocumentTokenDelete builder. -func (dtd *DocumentTokenDelete) Where(ps ...predicate.DocumentToken) *DocumentTokenDelete { - dtd.mutation.Where(ps...) - return dtd -} - -// Exec executes the deletion query and returns how many vertices were deleted. -func (dtd *DocumentTokenDelete) Exec(ctx context.Context) (int, error) { - var ( - err error - affected int - ) - if len(dtd.hooks) == 0 { - affected, err = dtd.sqlExec(ctx) - } else { - var mut Mutator = MutateFunc(func(ctx context.Context, m Mutation) (Value, error) { - mutation, ok := m.(*DocumentTokenMutation) - if !ok { - return nil, fmt.Errorf("unexpected mutation type %T", m) - } - dtd.mutation = mutation - affected, err = dtd.sqlExec(ctx) - mutation.done = true - return affected, err - }) - for i := len(dtd.hooks) - 1; i >= 0; i-- { - if dtd.hooks[i] == nil { - return 0, fmt.Errorf("ent: uninitialized hook (forgotten import ent/runtime?)") - } - mut = dtd.hooks[i](mut) - } - if _, err := mut.Mutate(ctx, dtd.mutation); err != nil { - return 0, err - } - } - return affected, err -} - -// ExecX is like Exec, but panics if an error occurs. -func (dtd *DocumentTokenDelete) ExecX(ctx context.Context) int { - n, err := dtd.Exec(ctx) - if err != nil { - panic(err) - } - return n -} - -func (dtd *DocumentTokenDelete) sqlExec(ctx context.Context) (int, error) { - _spec := &sqlgraph.DeleteSpec{ - Node: &sqlgraph.NodeSpec{ - Table: documenttoken.Table, - ID: &sqlgraph.FieldSpec{ - Type: field.TypeUUID, - Column: documenttoken.FieldID, - }, - }, - } - if ps := dtd.mutation.predicates; len(ps) > 0 { - _spec.Predicate = func(selector *sql.Selector) { - for i := range ps { - ps[i](selector) - } - } - } - affected, err := sqlgraph.DeleteNodes(ctx, dtd.driver, _spec) - if err != nil && sqlgraph.IsConstraintError(err) { - err = &ConstraintError{msg: err.Error(), wrap: err} - } - return affected, err -} - -// DocumentTokenDeleteOne is the builder for deleting a single DocumentToken entity. -type DocumentTokenDeleteOne struct { - dtd *DocumentTokenDelete -} - -// Exec executes the deletion query. -func (dtdo *DocumentTokenDeleteOne) Exec(ctx context.Context) error { - n, err := dtdo.dtd.Exec(ctx) - switch { - case err != nil: - return err - case n == 0: - return &NotFoundError{documenttoken.Label} - default: - return nil - } -} - -// ExecX is like Exec, but panics if an error occurs. -func (dtdo *DocumentTokenDeleteOne) ExecX(ctx context.Context) { - dtdo.dtd.ExecX(ctx) -} diff --git a/backend/internal/data/ent/documenttoken_query.go b/backend/internal/data/ent/documenttoken_query.go deleted file mode 100644 index 58cb61b..0000000 --- a/backend/internal/data/ent/documenttoken_query.go +++ /dev/null @@ -1,633 +0,0 @@ -// Code generated by ent, DO NOT EDIT. - -package ent - -import ( - "context" - "fmt" - "math" - - "entgo.io/ent/dialect/sql" - "entgo.io/ent/dialect/sql/sqlgraph" - "entgo.io/ent/schema/field" - "github.com/google/uuid" - "github.com/hay-kot/homebox/backend/internal/data/ent/document" - "github.com/hay-kot/homebox/backend/internal/data/ent/documenttoken" - "github.com/hay-kot/homebox/backend/internal/data/ent/predicate" -) - -// DocumentTokenQuery is the builder for querying DocumentToken entities. -type DocumentTokenQuery struct { - config - limit *int - offset *int - unique *bool - order []OrderFunc - fields []string - predicates []predicate.DocumentToken - withDocument *DocumentQuery - withFKs bool - // intermediate query (i.e. traversal path). - sql *sql.Selector - path func(context.Context) (*sql.Selector, error) -} - -// Where adds a new predicate for the DocumentTokenQuery builder. -func (dtq *DocumentTokenQuery) Where(ps ...predicate.DocumentToken) *DocumentTokenQuery { - dtq.predicates = append(dtq.predicates, ps...) - return dtq -} - -// Limit adds a limit step to the query. -func (dtq *DocumentTokenQuery) Limit(limit int) *DocumentTokenQuery { - dtq.limit = &limit - return dtq -} - -// Offset adds an offset step to the query. -func (dtq *DocumentTokenQuery) Offset(offset int) *DocumentTokenQuery { - dtq.offset = &offset - return dtq -} - -// Unique configures the query builder to filter duplicate records on query. -// By default, unique is set to true, and can be disabled using this method. -func (dtq *DocumentTokenQuery) Unique(unique bool) *DocumentTokenQuery { - dtq.unique = &unique - return dtq -} - -// Order adds an order step to the query. -func (dtq *DocumentTokenQuery) Order(o ...OrderFunc) *DocumentTokenQuery { - dtq.order = append(dtq.order, o...) - return dtq -} - -// QueryDocument chains the current query on the "document" edge. -func (dtq *DocumentTokenQuery) QueryDocument() *DocumentQuery { - query := &DocumentQuery{config: dtq.config} - query.path = func(ctx context.Context) (fromU *sql.Selector, err error) { - if err := dtq.prepareQuery(ctx); err != nil { - return nil, err - } - selector := dtq.sqlQuery(ctx) - if err := selector.Err(); err != nil { - return nil, err - } - step := sqlgraph.NewStep( - sqlgraph.From(documenttoken.Table, documenttoken.FieldID, selector), - sqlgraph.To(document.Table, document.FieldID), - sqlgraph.Edge(sqlgraph.M2O, true, documenttoken.DocumentTable, documenttoken.DocumentColumn), - ) - fromU = sqlgraph.SetNeighbors(dtq.driver.Dialect(), step) - return fromU, nil - } - return query -} - -// First returns the first DocumentToken entity from the query. -// Returns a *NotFoundError when no DocumentToken was found. -func (dtq *DocumentTokenQuery) First(ctx context.Context) (*DocumentToken, error) { - nodes, err := dtq.Limit(1).All(ctx) - if err != nil { - return nil, err - } - if len(nodes) == 0 { - return nil, &NotFoundError{documenttoken.Label} - } - return nodes[0], nil -} - -// FirstX is like First, but panics if an error occurs. -func (dtq *DocumentTokenQuery) FirstX(ctx context.Context) *DocumentToken { - node, err := dtq.First(ctx) - if err != nil && !IsNotFound(err) { - panic(err) - } - return node -} - -// FirstID returns the first DocumentToken ID from the query. -// Returns a *NotFoundError when no DocumentToken ID was found. -func (dtq *DocumentTokenQuery) FirstID(ctx context.Context) (id uuid.UUID, err error) { - var ids []uuid.UUID - if ids, err = dtq.Limit(1).IDs(ctx); err != nil { - return - } - if len(ids) == 0 { - err = &NotFoundError{documenttoken.Label} - return - } - return ids[0], nil -} - -// FirstIDX is like FirstID, but panics if an error occurs. -func (dtq *DocumentTokenQuery) FirstIDX(ctx context.Context) uuid.UUID { - id, err := dtq.FirstID(ctx) - if err != nil && !IsNotFound(err) { - panic(err) - } - return id -} - -// Only returns a single DocumentToken entity found by the query, ensuring it only returns one. -// Returns a *NotSingularError when more than one DocumentToken entity is found. -// Returns a *NotFoundError when no DocumentToken entities are found. -func (dtq *DocumentTokenQuery) Only(ctx context.Context) (*DocumentToken, error) { - nodes, err := dtq.Limit(2).All(ctx) - if err != nil { - return nil, err - } - switch len(nodes) { - case 1: - return nodes[0], nil - case 0: - return nil, &NotFoundError{documenttoken.Label} - default: - return nil, &NotSingularError{documenttoken.Label} - } -} - -// OnlyX is like Only, but panics if an error occurs. -func (dtq *DocumentTokenQuery) OnlyX(ctx context.Context) *DocumentToken { - node, err := dtq.Only(ctx) - if err != nil { - panic(err) - } - return node -} - -// OnlyID is like Only, but returns the only DocumentToken ID in the query. -// Returns a *NotSingularError when more than one DocumentToken ID is found. -// Returns a *NotFoundError when no entities are found. -func (dtq *DocumentTokenQuery) OnlyID(ctx context.Context) (id uuid.UUID, err error) { - var ids []uuid.UUID - if ids, err = dtq.Limit(2).IDs(ctx); err != nil { - return - } - switch len(ids) { - case 1: - id = ids[0] - case 0: - err = &NotFoundError{documenttoken.Label} - default: - err = &NotSingularError{documenttoken.Label} - } - return -} - -// OnlyIDX is like OnlyID, but panics if an error occurs. -func (dtq *DocumentTokenQuery) OnlyIDX(ctx context.Context) uuid.UUID { - id, err := dtq.OnlyID(ctx) - if err != nil { - panic(err) - } - return id -} - -// All executes the query and returns a list of DocumentTokens. -func (dtq *DocumentTokenQuery) All(ctx context.Context) ([]*DocumentToken, error) { - if err := dtq.prepareQuery(ctx); err != nil { - return nil, err - } - return dtq.sqlAll(ctx) -} - -// AllX is like All, but panics if an error occurs. -func (dtq *DocumentTokenQuery) AllX(ctx context.Context) []*DocumentToken { - nodes, err := dtq.All(ctx) - if err != nil { - panic(err) - } - return nodes -} - -// IDs executes the query and returns a list of DocumentToken IDs. -func (dtq *DocumentTokenQuery) IDs(ctx context.Context) ([]uuid.UUID, error) { - var ids []uuid.UUID - if err := dtq.Select(documenttoken.FieldID).Scan(ctx, &ids); err != nil { - return nil, err - } - return ids, nil -} - -// IDsX is like IDs, but panics if an error occurs. -func (dtq *DocumentTokenQuery) IDsX(ctx context.Context) []uuid.UUID { - ids, err := dtq.IDs(ctx) - if err != nil { - panic(err) - } - return ids -} - -// Count returns the count of the given query. -func (dtq *DocumentTokenQuery) Count(ctx context.Context) (int, error) { - if err := dtq.prepareQuery(ctx); err != nil { - return 0, err - } - return dtq.sqlCount(ctx) -} - -// CountX is like Count, but panics if an error occurs. -func (dtq *DocumentTokenQuery) CountX(ctx context.Context) int { - count, err := dtq.Count(ctx) - if err != nil { - panic(err) - } - return count -} - -// Exist returns true if the query has elements in the graph. -func (dtq *DocumentTokenQuery) Exist(ctx context.Context) (bool, error) { - if err := dtq.prepareQuery(ctx); err != nil { - return false, err - } - return dtq.sqlExist(ctx) -} - -// ExistX is like Exist, but panics if an error occurs. -func (dtq *DocumentTokenQuery) ExistX(ctx context.Context) bool { - exist, err := dtq.Exist(ctx) - if err != nil { - panic(err) - } - return exist -} - -// Clone returns a duplicate of the DocumentTokenQuery builder, including all associated steps. It can be -// used to prepare common query builders and use them differently after the clone is made. -func (dtq *DocumentTokenQuery) Clone() *DocumentTokenQuery { - if dtq == nil { - return nil - } - return &DocumentTokenQuery{ - config: dtq.config, - limit: dtq.limit, - offset: dtq.offset, - order: append([]OrderFunc{}, dtq.order...), - predicates: append([]predicate.DocumentToken{}, dtq.predicates...), - withDocument: dtq.withDocument.Clone(), - // clone intermediate query. - sql: dtq.sql.Clone(), - path: dtq.path, - unique: dtq.unique, - } -} - -// WithDocument tells the query-builder to eager-load the nodes that are connected to -// the "document" edge. The optional arguments are used to configure the query builder of the edge. -func (dtq *DocumentTokenQuery) WithDocument(opts ...func(*DocumentQuery)) *DocumentTokenQuery { - query := &DocumentQuery{config: dtq.config} - for _, opt := range opts { - opt(query) - } - dtq.withDocument = query - return dtq -} - -// GroupBy is used to group vertices by one or more fields/columns. -// It is often used with aggregate functions, like: count, max, mean, min, sum. -// -// Example: -// -// var v []struct { -// CreatedAt time.Time `json:"created_at,omitempty"` -// Count int `json:"count,omitempty"` -// } -// -// client.DocumentToken.Query(). -// GroupBy(documenttoken.FieldCreatedAt). -// Aggregate(ent.Count()). -// Scan(ctx, &v) -func (dtq *DocumentTokenQuery) GroupBy(field string, fields ...string) *DocumentTokenGroupBy { - grbuild := &DocumentTokenGroupBy{config: dtq.config} - grbuild.fields = append([]string{field}, fields...) - grbuild.path = func(ctx context.Context) (prev *sql.Selector, err error) { - if err := dtq.prepareQuery(ctx); err != nil { - return nil, err - } - return dtq.sqlQuery(ctx), nil - } - grbuild.label = documenttoken.Label - grbuild.flds, grbuild.scan = &grbuild.fields, grbuild.Scan - return grbuild -} - -// Select allows the selection one or more fields/columns for the given query, -// instead of selecting all fields in the entity. -// -// Example: -// -// var v []struct { -// CreatedAt time.Time `json:"created_at,omitempty"` -// } -// -// client.DocumentToken.Query(). -// Select(documenttoken.FieldCreatedAt). -// Scan(ctx, &v) -func (dtq *DocumentTokenQuery) Select(fields ...string) *DocumentTokenSelect { - dtq.fields = append(dtq.fields, fields...) - selbuild := &DocumentTokenSelect{DocumentTokenQuery: dtq} - selbuild.label = documenttoken.Label - selbuild.flds, selbuild.scan = &dtq.fields, selbuild.Scan - return selbuild -} - -// Aggregate returns a DocumentTokenSelect configured with the given aggregations. -func (dtq *DocumentTokenQuery) Aggregate(fns ...AggregateFunc) *DocumentTokenSelect { - return dtq.Select().Aggregate(fns...) -} - -func (dtq *DocumentTokenQuery) prepareQuery(ctx context.Context) error { - for _, f := range dtq.fields { - if !documenttoken.ValidColumn(f) { - return &ValidationError{Name: f, err: fmt.Errorf("ent: invalid field %q for query", f)} - } - } - if dtq.path != nil { - prev, err := dtq.path(ctx) - if err != nil { - return err - } - dtq.sql = prev - } - return nil -} - -func (dtq *DocumentTokenQuery) sqlAll(ctx context.Context, hooks ...queryHook) ([]*DocumentToken, error) { - var ( - nodes = []*DocumentToken{} - withFKs = dtq.withFKs - _spec = dtq.querySpec() - loadedTypes = [1]bool{ - dtq.withDocument != nil, - } - ) - if dtq.withDocument != nil { - withFKs = true - } - if withFKs { - _spec.Node.Columns = append(_spec.Node.Columns, documenttoken.ForeignKeys...) - } - _spec.ScanValues = func(columns []string) ([]any, error) { - return (*DocumentToken).scanValues(nil, columns) - } - _spec.Assign = func(columns []string, values []any) error { - node := &DocumentToken{config: dtq.config} - nodes = append(nodes, node) - node.Edges.loadedTypes = loadedTypes - return node.assignValues(columns, values) - } - for i := range hooks { - hooks[i](ctx, _spec) - } - if err := sqlgraph.QueryNodes(ctx, dtq.driver, _spec); err != nil { - return nil, err - } - if len(nodes) == 0 { - return nodes, nil - } - if query := dtq.withDocument; query != nil { - if err := dtq.loadDocument(ctx, query, nodes, nil, - func(n *DocumentToken, e *Document) { n.Edges.Document = e }); err != nil { - return nil, err - } - } - return nodes, nil -} - -func (dtq *DocumentTokenQuery) loadDocument(ctx context.Context, query *DocumentQuery, nodes []*DocumentToken, init func(*DocumentToken), assign func(*DocumentToken, *Document)) error { - ids := make([]uuid.UUID, 0, len(nodes)) - nodeids := make(map[uuid.UUID][]*DocumentToken) - for i := range nodes { - if nodes[i].document_document_tokens == nil { - continue - } - fk := *nodes[i].document_document_tokens - if _, ok := nodeids[fk]; !ok { - ids = append(ids, fk) - } - nodeids[fk] = append(nodeids[fk], nodes[i]) - } - query.Where(document.IDIn(ids...)) - neighbors, err := query.All(ctx) - if err != nil { - return err - } - for _, n := range neighbors { - nodes, ok := nodeids[n.ID] - if !ok { - return fmt.Errorf(`unexpected foreign-key "document_document_tokens" returned %v`, n.ID) - } - for i := range nodes { - assign(nodes[i], n) - } - } - return nil -} - -func (dtq *DocumentTokenQuery) sqlCount(ctx context.Context) (int, error) { - _spec := dtq.querySpec() - _spec.Node.Columns = dtq.fields - if len(dtq.fields) > 0 { - _spec.Unique = dtq.unique != nil && *dtq.unique - } - return sqlgraph.CountNodes(ctx, dtq.driver, _spec) -} - -func (dtq *DocumentTokenQuery) sqlExist(ctx context.Context) (bool, error) { - switch _, err := dtq.FirstID(ctx); { - case IsNotFound(err): - return false, nil - case err != nil: - return false, fmt.Errorf("ent: check existence: %w", err) - default: - return true, nil - } -} - -func (dtq *DocumentTokenQuery) querySpec() *sqlgraph.QuerySpec { - _spec := &sqlgraph.QuerySpec{ - Node: &sqlgraph.NodeSpec{ - Table: documenttoken.Table, - Columns: documenttoken.Columns, - ID: &sqlgraph.FieldSpec{ - Type: field.TypeUUID, - Column: documenttoken.FieldID, - }, - }, - From: dtq.sql, - Unique: true, - } - if unique := dtq.unique; unique != nil { - _spec.Unique = *unique - } - if fields := dtq.fields; len(fields) > 0 { - _spec.Node.Columns = make([]string, 0, len(fields)) - _spec.Node.Columns = append(_spec.Node.Columns, documenttoken.FieldID) - for i := range fields { - if fields[i] != documenttoken.FieldID { - _spec.Node.Columns = append(_spec.Node.Columns, fields[i]) - } - } - } - if ps := dtq.predicates; len(ps) > 0 { - _spec.Predicate = func(selector *sql.Selector) { - for i := range ps { - ps[i](selector) - } - } - } - if limit := dtq.limit; limit != nil { - _spec.Limit = *limit - } - if offset := dtq.offset; offset != nil { - _spec.Offset = *offset - } - if ps := dtq.order; len(ps) > 0 { - _spec.Order = func(selector *sql.Selector) { - for i := range ps { - ps[i](selector) - } - } - } - return _spec -} - -func (dtq *DocumentTokenQuery) sqlQuery(ctx context.Context) *sql.Selector { - builder := sql.Dialect(dtq.driver.Dialect()) - t1 := builder.Table(documenttoken.Table) - columns := dtq.fields - if len(columns) == 0 { - columns = documenttoken.Columns - } - selector := builder.Select(t1.Columns(columns...)...).From(t1) - if dtq.sql != nil { - selector = dtq.sql - selector.Select(selector.Columns(columns...)...) - } - if dtq.unique != nil && *dtq.unique { - selector.Distinct() - } - for _, p := range dtq.predicates { - p(selector) - } - for _, p := range dtq.order { - p(selector) - } - if offset := dtq.offset; offset != nil { - // limit is mandatory for offset clause. We start - // with default value, and override it below if needed. - selector.Offset(*offset).Limit(math.MaxInt32) - } - if limit := dtq.limit; limit != nil { - selector.Limit(*limit) - } - return selector -} - -// DocumentTokenGroupBy is the group-by builder for DocumentToken entities. -type DocumentTokenGroupBy struct { - config - selector - fields []string - fns []AggregateFunc - // intermediate query (i.e. traversal path). - sql *sql.Selector - path func(context.Context) (*sql.Selector, error) -} - -// Aggregate adds the given aggregation functions to the group-by query. -func (dtgb *DocumentTokenGroupBy) Aggregate(fns ...AggregateFunc) *DocumentTokenGroupBy { - dtgb.fns = append(dtgb.fns, fns...) - return dtgb -} - -// Scan applies the group-by query and scans the result into the given value. -func (dtgb *DocumentTokenGroupBy) Scan(ctx context.Context, v any) error { - query, err := dtgb.path(ctx) - if err != nil { - return err - } - dtgb.sql = query - return dtgb.sqlScan(ctx, v) -} - -func (dtgb *DocumentTokenGroupBy) sqlScan(ctx context.Context, v any) error { - for _, f := range dtgb.fields { - if !documenttoken.ValidColumn(f) { - return &ValidationError{Name: f, err: fmt.Errorf("invalid field %q for group-by", f)} - } - } - selector := dtgb.sqlQuery() - if err := selector.Err(); err != nil { - return err - } - rows := &sql.Rows{} - query, args := selector.Query() - if err := dtgb.driver.Query(ctx, query, args, rows); err != nil { - return err - } - defer rows.Close() - return sql.ScanSlice(rows, v) -} - -func (dtgb *DocumentTokenGroupBy) sqlQuery() *sql.Selector { - selector := dtgb.sql.Select() - aggregation := make([]string, 0, len(dtgb.fns)) - for _, fn := range dtgb.fns { - aggregation = append(aggregation, fn(selector)) - } - if len(selector.SelectedColumns()) == 0 { - columns := make([]string, 0, len(dtgb.fields)+len(dtgb.fns)) - for _, f := range dtgb.fields { - columns = append(columns, selector.C(f)) - } - columns = append(columns, aggregation...) - selector.Select(columns...) - } - return selector.GroupBy(selector.Columns(dtgb.fields...)...) -} - -// DocumentTokenSelect is the builder for selecting fields of DocumentToken entities. -type DocumentTokenSelect struct { - *DocumentTokenQuery - selector - // intermediate query (i.e. traversal path). - sql *sql.Selector -} - -// Aggregate adds the given aggregation functions to the selector query. -func (dts *DocumentTokenSelect) Aggregate(fns ...AggregateFunc) *DocumentTokenSelect { - dts.fns = append(dts.fns, fns...) - return dts -} - -// Scan applies the selector query and scans the result into the given value. -func (dts *DocumentTokenSelect) Scan(ctx context.Context, v any) error { - if err := dts.prepareQuery(ctx); err != nil { - return err - } - dts.sql = dts.DocumentTokenQuery.sqlQuery(ctx) - return dts.sqlScan(ctx, v) -} - -func (dts *DocumentTokenSelect) sqlScan(ctx context.Context, v any) error { - aggregation := make([]string, 0, len(dts.fns)) - for _, fn := range dts.fns { - aggregation = append(aggregation, fn(dts.sql)) - } - switch n := len(*dts.selector.flds); { - case n == 0 && len(aggregation) > 0: - dts.sql.Select(aggregation...) - case n != 0 && len(aggregation) > 0: - dts.sql.AppendSelect(aggregation...) - } - rows := &sql.Rows{} - query, args := dts.sql.Query() - if err := dts.driver.Query(ctx, query, args, rows); err != nil { - return err - } - defer rows.Close() - return sql.ScanSlice(rows, v) -} diff --git a/backend/internal/data/ent/documenttoken_update.go b/backend/internal/data/ent/documenttoken_update.go deleted file mode 100644 index 416bc08..0000000 --- a/backend/internal/data/ent/documenttoken_update.go +++ /dev/null @@ -1,542 +0,0 @@ -// Code generated by ent, DO NOT EDIT. - -package ent - -import ( - "context" - "errors" - "fmt" - "time" - - "entgo.io/ent/dialect/sql" - "entgo.io/ent/dialect/sql/sqlgraph" - "entgo.io/ent/schema/field" - "github.com/google/uuid" - "github.com/hay-kot/homebox/backend/internal/data/ent/document" - "github.com/hay-kot/homebox/backend/internal/data/ent/documenttoken" - "github.com/hay-kot/homebox/backend/internal/data/ent/predicate" -) - -// DocumentTokenUpdate is the builder for updating DocumentToken entities. -type DocumentTokenUpdate struct { - config - hooks []Hook - mutation *DocumentTokenMutation -} - -// Where appends a list predicates to the DocumentTokenUpdate builder. -func (dtu *DocumentTokenUpdate) Where(ps ...predicate.DocumentToken) *DocumentTokenUpdate { - dtu.mutation.Where(ps...) - return dtu -} - -// SetUpdatedAt sets the "updated_at" field. -func (dtu *DocumentTokenUpdate) SetUpdatedAt(t time.Time) *DocumentTokenUpdate { - dtu.mutation.SetUpdatedAt(t) - return dtu -} - -// SetToken sets the "token" field. -func (dtu *DocumentTokenUpdate) SetToken(b []byte) *DocumentTokenUpdate { - dtu.mutation.SetToken(b) - return dtu -} - -// SetUses sets the "uses" field. -func (dtu *DocumentTokenUpdate) SetUses(i int) *DocumentTokenUpdate { - dtu.mutation.ResetUses() - dtu.mutation.SetUses(i) - return dtu -} - -// SetNillableUses sets the "uses" field if the given value is not nil. -func (dtu *DocumentTokenUpdate) SetNillableUses(i *int) *DocumentTokenUpdate { - if i != nil { - dtu.SetUses(*i) - } - return dtu -} - -// AddUses adds i to the "uses" field. -func (dtu *DocumentTokenUpdate) AddUses(i int) *DocumentTokenUpdate { - dtu.mutation.AddUses(i) - return dtu -} - -// SetExpiresAt sets the "expires_at" field. -func (dtu *DocumentTokenUpdate) SetExpiresAt(t time.Time) *DocumentTokenUpdate { - dtu.mutation.SetExpiresAt(t) - return dtu -} - -// SetNillableExpiresAt sets the "expires_at" field if the given value is not nil. -func (dtu *DocumentTokenUpdate) SetNillableExpiresAt(t *time.Time) *DocumentTokenUpdate { - if t != nil { - dtu.SetExpiresAt(*t) - } - return dtu -} - -// SetDocumentID sets the "document" edge to the Document entity by ID. -func (dtu *DocumentTokenUpdate) SetDocumentID(id uuid.UUID) *DocumentTokenUpdate { - dtu.mutation.SetDocumentID(id) - return dtu -} - -// SetNillableDocumentID sets the "document" edge to the Document entity by ID if the given value is not nil. -func (dtu *DocumentTokenUpdate) SetNillableDocumentID(id *uuid.UUID) *DocumentTokenUpdate { - if id != nil { - dtu = dtu.SetDocumentID(*id) - } - return dtu -} - -// SetDocument sets the "document" edge to the Document entity. -func (dtu *DocumentTokenUpdate) SetDocument(d *Document) *DocumentTokenUpdate { - return dtu.SetDocumentID(d.ID) -} - -// Mutation returns the DocumentTokenMutation object of the builder. -func (dtu *DocumentTokenUpdate) Mutation() *DocumentTokenMutation { - return dtu.mutation -} - -// ClearDocument clears the "document" edge to the Document entity. -func (dtu *DocumentTokenUpdate) ClearDocument() *DocumentTokenUpdate { - dtu.mutation.ClearDocument() - return dtu -} - -// Save executes the query and returns the number of nodes affected by the update operation. -func (dtu *DocumentTokenUpdate) Save(ctx context.Context) (int, error) { - var ( - err error - affected int - ) - dtu.defaults() - if len(dtu.hooks) == 0 { - if err = dtu.check(); err != nil { - return 0, err - } - affected, err = dtu.sqlSave(ctx) - } else { - var mut Mutator = MutateFunc(func(ctx context.Context, m Mutation) (Value, error) { - mutation, ok := m.(*DocumentTokenMutation) - if !ok { - return nil, fmt.Errorf("unexpected mutation type %T", m) - } - if err = dtu.check(); err != nil { - return 0, err - } - dtu.mutation = mutation - affected, err = dtu.sqlSave(ctx) - mutation.done = true - return affected, err - }) - for i := len(dtu.hooks) - 1; i >= 0; i-- { - if dtu.hooks[i] == nil { - return 0, fmt.Errorf("ent: uninitialized hook (forgotten import ent/runtime?)") - } - mut = dtu.hooks[i](mut) - } - if _, err := mut.Mutate(ctx, dtu.mutation); err != nil { - return 0, err - } - } - return affected, err -} - -// SaveX is like Save, but panics if an error occurs. -func (dtu *DocumentTokenUpdate) SaveX(ctx context.Context) int { - affected, err := dtu.Save(ctx) - if err != nil { - panic(err) - } - return affected -} - -// Exec executes the query. -func (dtu *DocumentTokenUpdate) Exec(ctx context.Context) error { - _, err := dtu.Save(ctx) - return err -} - -// ExecX is like Exec, but panics if an error occurs. -func (dtu *DocumentTokenUpdate) ExecX(ctx context.Context) { - if err := dtu.Exec(ctx); err != nil { - panic(err) - } -} - -// defaults sets the default values of the builder before save. -func (dtu *DocumentTokenUpdate) defaults() { - if _, ok := dtu.mutation.UpdatedAt(); !ok { - v := documenttoken.UpdateDefaultUpdatedAt() - dtu.mutation.SetUpdatedAt(v) - } -} - -// check runs all checks and user-defined validators on the builder. -func (dtu *DocumentTokenUpdate) check() error { - if v, ok := dtu.mutation.Token(); ok { - if err := documenttoken.TokenValidator(v); err != nil { - return &ValidationError{Name: "token", err: fmt.Errorf(`ent: validator failed for field "DocumentToken.token": %w`, err)} - } - } - return nil -} - -func (dtu *DocumentTokenUpdate) sqlSave(ctx context.Context) (n int, err error) { - _spec := &sqlgraph.UpdateSpec{ - Node: &sqlgraph.NodeSpec{ - Table: documenttoken.Table, - Columns: documenttoken.Columns, - ID: &sqlgraph.FieldSpec{ - Type: field.TypeUUID, - Column: documenttoken.FieldID, - }, - }, - } - if ps := dtu.mutation.predicates; len(ps) > 0 { - _spec.Predicate = func(selector *sql.Selector) { - for i := range ps { - ps[i](selector) - } - } - } - if value, ok := dtu.mutation.UpdatedAt(); ok { - _spec.SetField(documenttoken.FieldUpdatedAt, field.TypeTime, value) - } - if value, ok := dtu.mutation.Token(); ok { - _spec.SetField(documenttoken.FieldToken, field.TypeBytes, value) - } - if value, ok := dtu.mutation.Uses(); ok { - _spec.SetField(documenttoken.FieldUses, field.TypeInt, value) - } - if value, ok := dtu.mutation.AddedUses(); ok { - _spec.AddField(documenttoken.FieldUses, field.TypeInt, value) - } - if value, ok := dtu.mutation.ExpiresAt(); ok { - _spec.SetField(documenttoken.FieldExpiresAt, field.TypeTime, value) - } - if dtu.mutation.DocumentCleared() { - edge := &sqlgraph.EdgeSpec{ - Rel: sqlgraph.M2O, - Inverse: true, - Table: documenttoken.DocumentTable, - Columns: []string{documenttoken.DocumentColumn}, - Bidi: false, - Target: &sqlgraph.EdgeTarget{ - IDSpec: &sqlgraph.FieldSpec{ - Type: field.TypeUUID, - Column: document.FieldID, - }, - }, - } - _spec.Edges.Clear = append(_spec.Edges.Clear, edge) - } - if nodes := dtu.mutation.DocumentIDs(); len(nodes) > 0 { - edge := &sqlgraph.EdgeSpec{ - Rel: sqlgraph.M2O, - Inverse: true, - Table: documenttoken.DocumentTable, - Columns: []string{documenttoken.DocumentColumn}, - Bidi: false, - Target: &sqlgraph.EdgeTarget{ - IDSpec: &sqlgraph.FieldSpec{ - Type: field.TypeUUID, - Column: document.FieldID, - }, - }, - } - for _, k := range nodes { - edge.Target.Nodes = append(edge.Target.Nodes, k) - } - _spec.Edges.Add = append(_spec.Edges.Add, edge) - } - if n, err = sqlgraph.UpdateNodes(ctx, dtu.driver, _spec); err != nil { - if _, ok := err.(*sqlgraph.NotFoundError); ok { - err = &NotFoundError{documenttoken.Label} - } else if sqlgraph.IsConstraintError(err) { - err = &ConstraintError{msg: err.Error(), wrap: err} - } - return 0, err - } - return n, nil -} - -// DocumentTokenUpdateOne is the builder for updating a single DocumentToken entity. -type DocumentTokenUpdateOne struct { - config - fields []string - hooks []Hook - mutation *DocumentTokenMutation -} - -// SetUpdatedAt sets the "updated_at" field. -func (dtuo *DocumentTokenUpdateOne) SetUpdatedAt(t time.Time) *DocumentTokenUpdateOne { - dtuo.mutation.SetUpdatedAt(t) - return dtuo -} - -// SetToken sets the "token" field. -func (dtuo *DocumentTokenUpdateOne) SetToken(b []byte) *DocumentTokenUpdateOne { - dtuo.mutation.SetToken(b) - return dtuo -} - -// SetUses sets the "uses" field. -func (dtuo *DocumentTokenUpdateOne) SetUses(i int) *DocumentTokenUpdateOne { - dtuo.mutation.ResetUses() - dtuo.mutation.SetUses(i) - return dtuo -} - -// SetNillableUses sets the "uses" field if the given value is not nil. -func (dtuo *DocumentTokenUpdateOne) SetNillableUses(i *int) *DocumentTokenUpdateOne { - if i != nil { - dtuo.SetUses(*i) - } - return dtuo -} - -// AddUses adds i to the "uses" field. -func (dtuo *DocumentTokenUpdateOne) AddUses(i int) *DocumentTokenUpdateOne { - dtuo.mutation.AddUses(i) - return dtuo -} - -// SetExpiresAt sets the "expires_at" field. -func (dtuo *DocumentTokenUpdateOne) SetExpiresAt(t time.Time) *DocumentTokenUpdateOne { - dtuo.mutation.SetExpiresAt(t) - return dtuo -} - -// SetNillableExpiresAt sets the "expires_at" field if the given value is not nil. -func (dtuo *DocumentTokenUpdateOne) SetNillableExpiresAt(t *time.Time) *DocumentTokenUpdateOne { - if t != nil { - dtuo.SetExpiresAt(*t) - } - return dtuo -} - -// SetDocumentID sets the "document" edge to the Document entity by ID. -func (dtuo *DocumentTokenUpdateOne) SetDocumentID(id uuid.UUID) *DocumentTokenUpdateOne { - dtuo.mutation.SetDocumentID(id) - return dtuo -} - -// SetNillableDocumentID sets the "document" edge to the Document entity by ID if the given value is not nil. -func (dtuo *DocumentTokenUpdateOne) SetNillableDocumentID(id *uuid.UUID) *DocumentTokenUpdateOne { - if id != nil { - dtuo = dtuo.SetDocumentID(*id) - } - return dtuo -} - -// SetDocument sets the "document" edge to the Document entity. -func (dtuo *DocumentTokenUpdateOne) SetDocument(d *Document) *DocumentTokenUpdateOne { - return dtuo.SetDocumentID(d.ID) -} - -// Mutation returns the DocumentTokenMutation object of the builder. -func (dtuo *DocumentTokenUpdateOne) Mutation() *DocumentTokenMutation { - return dtuo.mutation -} - -// ClearDocument clears the "document" edge to the Document entity. -func (dtuo *DocumentTokenUpdateOne) ClearDocument() *DocumentTokenUpdateOne { - dtuo.mutation.ClearDocument() - return dtuo -} - -// Select allows selecting one or more fields (columns) of the returned entity. -// The default is selecting all fields defined in the entity schema. -func (dtuo *DocumentTokenUpdateOne) Select(field string, fields ...string) *DocumentTokenUpdateOne { - dtuo.fields = append([]string{field}, fields...) - return dtuo -} - -// Save executes the query and returns the updated DocumentToken entity. -func (dtuo *DocumentTokenUpdateOne) Save(ctx context.Context) (*DocumentToken, error) { - var ( - err error - node *DocumentToken - ) - dtuo.defaults() - if len(dtuo.hooks) == 0 { - if err = dtuo.check(); err != nil { - return nil, err - } - node, err = dtuo.sqlSave(ctx) - } else { - var mut Mutator = MutateFunc(func(ctx context.Context, m Mutation) (Value, error) { - mutation, ok := m.(*DocumentTokenMutation) - if !ok { - return nil, fmt.Errorf("unexpected mutation type %T", m) - } - if err = dtuo.check(); err != nil { - return nil, err - } - dtuo.mutation = mutation - node, err = dtuo.sqlSave(ctx) - mutation.done = true - return node, err - }) - for i := len(dtuo.hooks) - 1; i >= 0; i-- { - if dtuo.hooks[i] == nil { - return nil, fmt.Errorf("ent: uninitialized hook (forgotten import ent/runtime?)") - } - mut = dtuo.hooks[i](mut) - } - v, err := mut.Mutate(ctx, dtuo.mutation) - if err != nil { - return nil, err - } - nv, ok := v.(*DocumentToken) - if !ok { - return nil, fmt.Errorf("unexpected node type %T returned from DocumentTokenMutation", v) - } - node = nv - } - return node, err -} - -// SaveX is like Save, but panics if an error occurs. -func (dtuo *DocumentTokenUpdateOne) SaveX(ctx context.Context) *DocumentToken { - node, err := dtuo.Save(ctx) - if err != nil { - panic(err) - } - return node -} - -// Exec executes the query on the entity. -func (dtuo *DocumentTokenUpdateOne) Exec(ctx context.Context) error { - _, err := dtuo.Save(ctx) - return err -} - -// ExecX is like Exec, but panics if an error occurs. -func (dtuo *DocumentTokenUpdateOne) ExecX(ctx context.Context) { - if err := dtuo.Exec(ctx); err != nil { - panic(err) - } -} - -// defaults sets the default values of the builder before save. -func (dtuo *DocumentTokenUpdateOne) defaults() { - if _, ok := dtuo.mutation.UpdatedAt(); !ok { - v := documenttoken.UpdateDefaultUpdatedAt() - dtuo.mutation.SetUpdatedAt(v) - } -} - -// check runs all checks and user-defined validators on the builder. -func (dtuo *DocumentTokenUpdateOne) check() error { - if v, ok := dtuo.mutation.Token(); ok { - if err := documenttoken.TokenValidator(v); err != nil { - return &ValidationError{Name: "token", err: fmt.Errorf(`ent: validator failed for field "DocumentToken.token": %w`, err)} - } - } - return nil -} - -func (dtuo *DocumentTokenUpdateOne) sqlSave(ctx context.Context) (_node *DocumentToken, err error) { - _spec := &sqlgraph.UpdateSpec{ - Node: &sqlgraph.NodeSpec{ - Table: documenttoken.Table, - Columns: documenttoken.Columns, - ID: &sqlgraph.FieldSpec{ - Type: field.TypeUUID, - Column: documenttoken.FieldID, - }, - }, - } - id, ok := dtuo.mutation.ID() - if !ok { - return nil, &ValidationError{Name: "id", err: errors.New(`ent: missing "DocumentToken.id" for update`)} - } - _spec.Node.ID.Value = id - if fields := dtuo.fields; len(fields) > 0 { - _spec.Node.Columns = make([]string, 0, len(fields)) - _spec.Node.Columns = append(_spec.Node.Columns, documenttoken.FieldID) - for _, f := range fields { - if !documenttoken.ValidColumn(f) { - return nil, &ValidationError{Name: f, err: fmt.Errorf("ent: invalid field %q for query", f)} - } - if f != documenttoken.FieldID { - _spec.Node.Columns = append(_spec.Node.Columns, f) - } - } - } - if ps := dtuo.mutation.predicates; len(ps) > 0 { - _spec.Predicate = func(selector *sql.Selector) { - for i := range ps { - ps[i](selector) - } - } - } - if value, ok := dtuo.mutation.UpdatedAt(); ok { - _spec.SetField(documenttoken.FieldUpdatedAt, field.TypeTime, value) - } - if value, ok := dtuo.mutation.Token(); ok { - _spec.SetField(documenttoken.FieldToken, field.TypeBytes, value) - } - if value, ok := dtuo.mutation.Uses(); ok { - _spec.SetField(documenttoken.FieldUses, field.TypeInt, value) - } - if value, ok := dtuo.mutation.AddedUses(); ok { - _spec.AddField(documenttoken.FieldUses, field.TypeInt, value) - } - if value, ok := dtuo.mutation.ExpiresAt(); ok { - _spec.SetField(documenttoken.FieldExpiresAt, field.TypeTime, value) - } - if dtuo.mutation.DocumentCleared() { - edge := &sqlgraph.EdgeSpec{ - Rel: sqlgraph.M2O, - Inverse: true, - Table: documenttoken.DocumentTable, - Columns: []string{documenttoken.DocumentColumn}, - Bidi: false, - Target: &sqlgraph.EdgeTarget{ - IDSpec: &sqlgraph.FieldSpec{ - Type: field.TypeUUID, - Column: document.FieldID, - }, - }, - } - _spec.Edges.Clear = append(_spec.Edges.Clear, edge) - } - if nodes := dtuo.mutation.DocumentIDs(); len(nodes) > 0 { - edge := &sqlgraph.EdgeSpec{ - Rel: sqlgraph.M2O, - Inverse: true, - Table: documenttoken.DocumentTable, - Columns: []string{documenttoken.DocumentColumn}, - Bidi: false, - Target: &sqlgraph.EdgeTarget{ - IDSpec: &sqlgraph.FieldSpec{ - Type: field.TypeUUID, - Column: document.FieldID, - }, - }, - } - for _, k := range nodes { - edge.Target.Nodes = append(edge.Target.Nodes, k) - } - _spec.Edges.Add = append(_spec.Edges.Add, edge) - } - _node = &DocumentToken{config: dtuo.config} - _spec.Assign = _node.assignValues - _spec.ScanValues = _node.scanValues - if err = sqlgraph.UpdateNode(ctx, dtuo.driver, _spec); err != nil { - if _, ok := err.(*sqlgraph.NotFoundError); ok { - err = &NotFoundError{documenttoken.Label} - } else if sqlgraph.IsConstraintError(err) { - err = &ConstraintError{msg: err.Error(), wrap: err} - } - return nil, err - } - return _node, nil -} diff --git a/backend/internal/data/ent/ent.go b/backend/internal/data/ent/ent.go index 0731b14..adf8df3 100644 --- a/backend/internal/data/ent/ent.go +++ b/backend/internal/data/ent/ent.go @@ -14,13 +14,13 @@ import ( "github.com/hay-kot/homebox/backend/internal/data/ent/authroles" "github.com/hay-kot/homebox/backend/internal/data/ent/authtokens" "github.com/hay-kot/homebox/backend/internal/data/ent/document" - "github.com/hay-kot/homebox/backend/internal/data/ent/documenttoken" "github.com/hay-kot/homebox/backend/internal/data/ent/group" "github.com/hay-kot/homebox/backend/internal/data/ent/groupinvitationtoken" "github.com/hay-kot/homebox/backend/internal/data/ent/item" "github.com/hay-kot/homebox/backend/internal/data/ent/itemfield" "github.com/hay-kot/homebox/backend/internal/data/ent/label" "github.com/hay-kot/homebox/backend/internal/data/ent/location" + "github.com/hay-kot/homebox/backend/internal/data/ent/maintenanceentry" "github.com/hay-kot/homebox/backend/internal/data/ent/user" ) @@ -46,13 +46,13 @@ func columnChecker(table string) func(string) error { authroles.Table: authroles.ValidColumn, authtokens.Table: authtokens.ValidColumn, document.Table: document.ValidColumn, - documenttoken.Table: documenttoken.ValidColumn, group.Table: group.ValidColumn, groupinvitationtoken.Table: groupinvitationtoken.ValidColumn, item.Table: item.ValidColumn, itemfield.Table: itemfield.ValidColumn, label.Table: label.ValidColumn, location.Table: location.ValidColumn, + maintenanceentry.Table: maintenanceentry.ValidColumn, user.Table: user.ValidColumn, } check, ok := checks[table] diff --git a/backend/internal/data/ent/generate.go b/backend/internal/data/ent/generate.go index eb03ded..7b8b727 100644 --- a/backend/internal/data/ent/generate.go +++ b/backend/internal/data/ent/generate.go @@ -1,3 +1,3 @@ package ent -//go:generate go run -mod=mod entgo.io/ent/cmd/ent generate --feature sql/versioned-migration ./schema +//go:generate go run -mod=mod entgo.io/ent/cmd/ent generate --feature sql/versioned-migration ./schema --template=./schema/templates/has_id.tmpl diff --git a/backend/internal/data/ent/has_id.go b/backend/internal/data/ent/has_id.go index a6afc6a..875ba0d 100644 --- a/backend/internal/data/ent/has_id.go +++ b/backend/internal/data/ent/has_id.go @@ -8,6 +8,10 @@ func (a *Attachment) GetID() uuid.UUID { return a.ID } +func (ar *AuthRoles) GetID() int { + return ar.ID +} + func (at *AuthTokens) GetID() uuid.UUID { return at.ID } @@ -16,14 +20,14 @@ func (d *Document) GetID() uuid.UUID { return d.ID } -func (dt *DocumentToken) GetID() uuid.UUID { - return dt.ID -} - func (gr *Group) GetID() uuid.UUID { return gr.ID } +func (git *GroupInvitationToken) GetID() uuid.UUID { + return git.ID +} + func (i *Item) GetID() uuid.UUID { return i.ID } @@ -40,6 +44,10 @@ func (l *Location) GetID() uuid.UUID { return l.ID } +func (me *MaintenanceEntry) GetID() uuid.UUID { + return me.ID +} + func (u *User) GetID() uuid.UUID { return u.ID } diff --git a/backend/internal/data/ent/hook/hook.go b/backend/internal/data/ent/hook/hook.go index 49d24c3..08c9401 100644 --- a/backend/internal/data/ent/hook/hook.go +++ b/backend/internal/data/ent/hook/hook.go @@ -61,19 +61,6 @@ func (f DocumentFunc) Mutate(ctx context.Context, m ent.Mutation) (ent.Value, er return f(ctx, mv) } -// The DocumentTokenFunc type is an adapter to allow the use of ordinary -// function as DocumentToken mutator. -type DocumentTokenFunc func(context.Context, *ent.DocumentTokenMutation) (ent.Value, error) - -// Mutate calls f(ctx, m). -func (f DocumentTokenFunc) Mutate(ctx context.Context, m ent.Mutation) (ent.Value, error) { - mv, ok := m.(*ent.DocumentTokenMutation) - if !ok { - return nil, fmt.Errorf("unexpected mutation type %T. expect *ent.DocumentTokenMutation", m) - } - return f(ctx, mv) -} - // The GroupFunc type is an adapter to allow the use of ordinary // function as Group mutator. type GroupFunc func(context.Context, *ent.GroupMutation) (ent.Value, error) @@ -152,6 +139,19 @@ func (f LocationFunc) Mutate(ctx context.Context, m ent.Mutation) (ent.Value, er return f(ctx, mv) } +// The MaintenanceEntryFunc type is an adapter to allow the use of ordinary +// function as MaintenanceEntry mutator. +type MaintenanceEntryFunc func(context.Context, *ent.MaintenanceEntryMutation) (ent.Value, error) + +// Mutate calls f(ctx, m). +func (f MaintenanceEntryFunc) Mutate(ctx context.Context, m ent.Mutation) (ent.Value, error) { + mv, ok := m.(*ent.MaintenanceEntryMutation) + if !ok { + return nil, fmt.Errorf("unexpected mutation type %T. expect *ent.MaintenanceEntryMutation", m) + } + return f(ctx, mv) +} + // The UserFunc type is an adapter to allow the use of ordinary // function as User mutator. type UserFunc func(context.Context, *ent.UserMutation) (ent.Value, error) diff --git a/backend/internal/data/ent/item.go b/backend/internal/data/ent/item.go index a780945..bcca2c7 100644 --- a/backend/internal/data/ent/item.go +++ b/backend/internal/data/ent/item.go @@ -87,11 +87,13 @@ type ItemEdges struct { Location *Location `json:"location,omitempty"` // Fields holds the value of the fields edge. Fields []*ItemField `json:"fields,omitempty"` + // MaintenanceEntries holds the value of the maintenance_entries edge. + MaintenanceEntries []*MaintenanceEntry `json:"maintenance_entries,omitempty"` // Attachments holds the value of the attachments edge. Attachments []*Attachment `json:"attachments,omitempty"` // loadedTypes holds the information for reporting if a // type was loaded (or requested) in eager-loading or not. - loadedTypes [7]bool + loadedTypes [8]bool } // ParentOrErr returns the Parent value or an error if the edge @@ -160,10 +162,19 @@ func (e ItemEdges) FieldsOrErr() ([]*ItemField, error) { return nil, &NotLoadedError{edge: "fields"} } +// MaintenanceEntriesOrErr returns the MaintenanceEntries value or an error if the edge +// was not loaded in eager-loading. +func (e ItemEdges) MaintenanceEntriesOrErr() ([]*MaintenanceEntry, error) { + if e.loadedTypes[6] { + return e.MaintenanceEntries, nil + } + return nil, &NotLoadedError{edge: "maintenance_entries"} +} + // AttachmentsOrErr returns the Attachments value or an error if the edge // was not loaded in eager-loading. func (e ItemEdges) AttachmentsOrErr() ([]*Attachment, error) { - if e.loadedTypes[6] { + if e.loadedTypes[7] { return e.Attachments, nil } return nil, &NotLoadedError{edge: "attachments"} @@ -407,6 +418,11 @@ func (i *Item) QueryFields() *ItemFieldQuery { return (&ItemClient{config: i.config}).QueryFields(i) } +// QueryMaintenanceEntries queries the "maintenance_entries" edge of the Item entity. +func (i *Item) QueryMaintenanceEntries() *MaintenanceEntryQuery { + return (&ItemClient{config: i.config}).QueryMaintenanceEntries(i) +} + // QueryAttachments queries the "attachments" edge of the Item entity. func (i *Item) QueryAttachments() *AttachmentQuery { return (&ItemClient{config: i.config}).QueryAttachments(i) diff --git a/backend/internal/data/ent/item/item.go b/backend/internal/data/ent/item/item.go index ab3b43f..2cb7f6d 100644 --- a/backend/internal/data/ent/item/item.go +++ b/backend/internal/data/ent/item/item.go @@ -71,6 +71,8 @@ const ( EdgeLocation = "location" // EdgeFields holds the string denoting the fields edge name in mutations. EdgeFields = "fields" + // EdgeMaintenanceEntries holds the string denoting the maintenance_entries edge name in mutations. + EdgeMaintenanceEntries = "maintenance_entries" // EdgeAttachments holds the string denoting the attachments edge name in mutations. EdgeAttachments = "attachments" // Table holds the table name of the item in the database. @@ -109,6 +111,13 @@ const ( FieldsInverseTable = "item_fields" // FieldsColumn is the table column denoting the fields relation/edge. FieldsColumn = "item_fields" + // MaintenanceEntriesTable is the table that holds the maintenance_entries relation/edge. + MaintenanceEntriesTable = "maintenance_entries" + // MaintenanceEntriesInverseTable is the table name for the MaintenanceEntry entity. + // It exists in this package in order to avoid circular dependency with the "maintenanceentry" package. + MaintenanceEntriesInverseTable = "maintenance_entries" + // MaintenanceEntriesColumn is the table column denoting the maintenance_entries relation/edge. + MaintenanceEntriesColumn = "item_id" // AttachmentsTable is the table that holds the attachments relation/edge. AttachmentsTable = "attachments" // AttachmentsInverseTable is the table name for the Attachment entity. diff --git a/backend/internal/data/ent/item/where.go b/backend/internal/data/ent/item/where.go index 2174432..cef11f4 100644 --- a/backend/internal/data/ent/item/where.go +++ b/backend/internal/data/ent/item/where.go @@ -2300,6 +2300,34 @@ func HasFieldsWith(preds ...predicate.ItemField) predicate.Item { }) } +// HasMaintenanceEntries applies the HasEdge predicate on the "maintenance_entries" edge. +func HasMaintenanceEntries() predicate.Item { + return predicate.Item(func(s *sql.Selector) { + step := sqlgraph.NewStep( + sqlgraph.From(Table, FieldID), + sqlgraph.To(MaintenanceEntriesTable, FieldID), + sqlgraph.Edge(sqlgraph.O2M, false, MaintenanceEntriesTable, MaintenanceEntriesColumn), + ) + sqlgraph.HasNeighbors(s, step) + }) +} + +// HasMaintenanceEntriesWith applies the HasEdge predicate on the "maintenance_entries" edge with a given conditions (other predicates). +func HasMaintenanceEntriesWith(preds ...predicate.MaintenanceEntry) predicate.Item { + return predicate.Item(func(s *sql.Selector) { + step := sqlgraph.NewStep( + sqlgraph.From(Table, FieldID), + sqlgraph.To(MaintenanceEntriesInverseTable, FieldID), + sqlgraph.Edge(sqlgraph.O2M, false, MaintenanceEntriesTable, MaintenanceEntriesColumn), + ) + sqlgraph.HasNeighborsWith(s, step, func(s *sql.Selector) { + for _, p := range preds { + p(s) + } + }) + }) +} + // HasAttachments applies the HasEdge predicate on the "attachments" edge. func HasAttachments() predicate.Item { return predicate.Item(func(s *sql.Selector) { diff --git a/backend/internal/data/ent/item_create.go b/backend/internal/data/ent/item_create.go index 97938f9..4a7c5aa 100644 --- a/backend/internal/data/ent/item_create.go +++ b/backend/internal/data/ent/item_create.go @@ -17,6 +17,7 @@ import ( "github.com/hay-kot/homebox/backend/internal/data/ent/itemfield" "github.com/hay-kot/homebox/backend/internal/data/ent/label" "github.com/hay-kot/homebox/backend/internal/data/ent/location" + "github.com/hay-kot/homebox/backend/internal/data/ent/maintenanceentry" ) // ItemCreate is the builder for creating a Item entity. @@ -448,6 +449,21 @@ func (ic *ItemCreate) AddFields(i ...*ItemField) *ItemCreate { return ic.AddFieldIDs(ids...) } +// AddMaintenanceEntryIDs adds the "maintenance_entries" edge to the MaintenanceEntry entity by IDs. +func (ic *ItemCreate) AddMaintenanceEntryIDs(ids ...uuid.UUID) *ItemCreate { + ic.mutation.AddMaintenanceEntryIDs(ids...) + return ic +} + +// AddMaintenanceEntries adds the "maintenance_entries" edges to the MaintenanceEntry entity. +func (ic *ItemCreate) AddMaintenanceEntries(m ...*MaintenanceEntry) *ItemCreate { + ids := make([]uuid.UUID, len(m)) + for i := range m { + ids[i] = m[i].ID + } + return ic.AddMaintenanceEntryIDs(ids...) +} + // AddAttachmentIDs adds the "attachments" edge to the Attachment entity by IDs. func (ic *ItemCreate) AddAttachmentIDs(ids ...uuid.UUID) *ItemCreate { ic.mutation.AddAttachmentIDs(ids...) @@ -907,6 +923,25 @@ func (ic *ItemCreate) createSpec() (*Item, *sqlgraph.CreateSpec) { } _spec.Edges = append(_spec.Edges, edge) } + if nodes := ic.mutation.MaintenanceEntriesIDs(); len(nodes) > 0 { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.O2M, + Inverse: false, + Table: item.MaintenanceEntriesTable, + Columns: []string{item.MaintenanceEntriesColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: &sqlgraph.FieldSpec{ + Type: field.TypeUUID, + Column: maintenanceentry.FieldID, + }, + }, + } + for _, k := range nodes { + edge.Target.Nodes = append(edge.Target.Nodes, k) + } + _spec.Edges = append(_spec.Edges, edge) + } if nodes := ic.mutation.AttachmentsIDs(); len(nodes) > 0 { edge := &sqlgraph.EdgeSpec{ Rel: sqlgraph.O2M, diff --git a/backend/internal/data/ent/item_query.go b/backend/internal/data/ent/item_query.go index 0040a42..8891469 100644 --- a/backend/internal/data/ent/item_query.go +++ b/backend/internal/data/ent/item_query.go @@ -18,26 +18,28 @@ import ( "github.com/hay-kot/homebox/backend/internal/data/ent/itemfield" "github.com/hay-kot/homebox/backend/internal/data/ent/label" "github.com/hay-kot/homebox/backend/internal/data/ent/location" + "github.com/hay-kot/homebox/backend/internal/data/ent/maintenanceentry" "github.com/hay-kot/homebox/backend/internal/data/ent/predicate" ) // ItemQuery is the builder for querying Item entities. type ItemQuery struct { config - limit *int - offset *int - unique *bool - order []OrderFunc - fields []string - predicates []predicate.Item - withParent *ItemQuery - withChildren *ItemQuery - withGroup *GroupQuery - withLabel *LabelQuery - withLocation *LocationQuery - withFields *ItemFieldQuery - withAttachments *AttachmentQuery - withFKs bool + limit *int + offset *int + unique *bool + order []OrderFunc + fields []string + predicates []predicate.Item + withParent *ItemQuery + withChildren *ItemQuery + withGroup *GroupQuery + withLabel *LabelQuery + withLocation *LocationQuery + withFields *ItemFieldQuery + withMaintenanceEntries *MaintenanceEntryQuery + withAttachments *AttachmentQuery + withFKs bool // intermediate query (i.e. traversal path). sql *sql.Selector path func(context.Context) (*sql.Selector, error) @@ -206,6 +208,28 @@ func (iq *ItemQuery) QueryFields() *ItemFieldQuery { return query } +// QueryMaintenanceEntries chains the current query on the "maintenance_entries" edge. +func (iq *ItemQuery) QueryMaintenanceEntries() *MaintenanceEntryQuery { + query := &MaintenanceEntryQuery{config: iq.config} + query.path = func(ctx context.Context) (fromU *sql.Selector, err error) { + if err := iq.prepareQuery(ctx); err != nil { + return nil, err + } + selector := iq.sqlQuery(ctx) + if err := selector.Err(); err != nil { + return nil, err + } + step := sqlgraph.NewStep( + sqlgraph.From(item.Table, item.FieldID, selector), + sqlgraph.To(maintenanceentry.Table, maintenanceentry.FieldID), + sqlgraph.Edge(sqlgraph.O2M, false, item.MaintenanceEntriesTable, item.MaintenanceEntriesColumn), + ) + fromU = sqlgraph.SetNeighbors(iq.driver.Dialect(), step) + return fromU, nil + } + return query +} + // QueryAttachments chains the current query on the "attachments" edge. func (iq *ItemQuery) QueryAttachments() *AttachmentQuery { query := &AttachmentQuery{config: iq.config} @@ -404,18 +428,19 @@ func (iq *ItemQuery) Clone() *ItemQuery { return nil } return &ItemQuery{ - config: iq.config, - limit: iq.limit, - offset: iq.offset, - order: append([]OrderFunc{}, iq.order...), - predicates: append([]predicate.Item{}, iq.predicates...), - withParent: iq.withParent.Clone(), - withChildren: iq.withChildren.Clone(), - withGroup: iq.withGroup.Clone(), - withLabel: iq.withLabel.Clone(), - withLocation: iq.withLocation.Clone(), - withFields: iq.withFields.Clone(), - withAttachments: iq.withAttachments.Clone(), + config: iq.config, + limit: iq.limit, + offset: iq.offset, + order: append([]OrderFunc{}, iq.order...), + predicates: append([]predicate.Item{}, iq.predicates...), + withParent: iq.withParent.Clone(), + withChildren: iq.withChildren.Clone(), + withGroup: iq.withGroup.Clone(), + withLabel: iq.withLabel.Clone(), + withLocation: iq.withLocation.Clone(), + withFields: iq.withFields.Clone(), + withMaintenanceEntries: iq.withMaintenanceEntries.Clone(), + withAttachments: iq.withAttachments.Clone(), // clone intermediate query. sql: iq.sql.Clone(), path: iq.path, @@ -489,6 +514,17 @@ func (iq *ItemQuery) WithFields(opts ...func(*ItemFieldQuery)) *ItemQuery { return iq } +// WithMaintenanceEntries tells the query-builder to eager-load the nodes that are connected to +// the "maintenance_entries" edge. The optional arguments are used to configure the query builder of the edge. +func (iq *ItemQuery) WithMaintenanceEntries(opts ...func(*MaintenanceEntryQuery)) *ItemQuery { + query := &MaintenanceEntryQuery{config: iq.config} + for _, opt := range opts { + opt(query) + } + iq.withMaintenanceEntries = query + return iq +} + // WithAttachments tells the query-builder to eager-load the nodes that are connected to // the "attachments" edge. The optional arguments are used to configure the query builder of the edge. func (iq *ItemQuery) WithAttachments(opts ...func(*AttachmentQuery)) *ItemQuery { @@ -574,13 +610,14 @@ func (iq *ItemQuery) sqlAll(ctx context.Context, hooks ...queryHook) ([]*Item, e nodes = []*Item{} withFKs = iq.withFKs _spec = iq.querySpec() - loadedTypes = [7]bool{ + loadedTypes = [8]bool{ iq.withParent != nil, iq.withChildren != nil, iq.withGroup != nil, iq.withLabel != nil, iq.withLocation != nil, iq.withFields != nil, + iq.withMaintenanceEntries != nil, iq.withAttachments != nil, } ) @@ -647,6 +684,13 @@ func (iq *ItemQuery) sqlAll(ctx context.Context, hooks ...queryHook) ([]*Item, e return nil, err } } + if query := iq.withMaintenanceEntries; query != nil { + if err := iq.loadMaintenanceEntries(ctx, query, nodes, + func(n *Item) { n.Edges.MaintenanceEntries = []*MaintenanceEntry{} }, + func(n *Item, e *MaintenanceEntry) { n.Edges.MaintenanceEntries = append(n.Edges.MaintenanceEntries, e) }); err != nil { + return nil, err + } + } if query := iq.withAttachments; query != nil { if err := iq.loadAttachments(ctx, query, nodes, func(n *Item) { n.Edges.Attachments = []*Attachment{} }, @@ -864,6 +908,33 @@ func (iq *ItemQuery) loadFields(ctx context.Context, query *ItemFieldQuery, node } return nil } +func (iq *ItemQuery) loadMaintenanceEntries(ctx context.Context, query *MaintenanceEntryQuery, nodes []*Item, init func(*Item), assign func(*Item, *MaintenanceEntry)) error { + fks := make([]driver.Value, 0, len(nodes)) + nodeids := make(map[uuid.UUID]*Item) + for i := range nodes { + fks = append(fks, nodes[i].ID) + nodeids[nodes[i].ID] = nodes[i] + if init != nil { + init(nodes[i]) + } + } + query.Where(predicate.MaintenanceEntry(func(s *sql.Selector) { + s.Where(sql.InValues(item.MaintenanceEntriesColumn, fks...)) + })) + neighbors, err := query.All(ctx) + if err != nil { + return err + } + for _, n := range neighbors { + fk := n.ItemID + node, ok := nodeids[fk] + if !ok { + return fmt.Errorf(`unexpected foreign-key "item_id" returned %v for node %v`, fk, n.ID) + } + assign(node, n) + } + return nil +} func (iq *ItemQuery) loadAttachments(ctx context.Context, query *AttachmentQuery, nodes []*Item, init func(*Item), assign func(*Item, *Attachment)) error { fks := make([]driver.Value, 0, len(nodes)) nodeids := make(map[uuid.UUID]*Item) diff --git a/backend/internal/data/ent/item_update.go b/backend/internal/data/ent/item_update.go index 236f363..b7a9b79 100644 --- a/backend/internal/data/ent/item_update.go +++ b/backend/internal/data/ent/item_update.go @@ -18,6 +18,7 @@ import ( "github.com/hay-kot/homebox/backend/internal/data/ent/itemfield" "github.com/hay-kot/homebox/backend/internal/data/ent/label" "github.com/hay-kot/homebox/backend/internal/data/ent/location" + "github.com/hay-kot/homebox/backend/internal/data/ent/maintenanceentry" "github.com/hay-kot/homebox/backend/internal/data/ent/predicate" ) @@ -506,6 +507,21 @@ func (iu *ItemUpdate) AddFields(i ...*ItemField) *ItemUpdate { return iu.AddFieldIDs(ids...) } +// AddMaintenanceEntryIDs adds the "maintenance_entries" edge to the MaintenanceEntry entity by IDs. +func (iu *ItemUpdate) AddMaintenanceEntryIDs(ids ...uuid.UUID) *ItemUpdate { + iu.mutation.AddMaintenanceEntryIDs(ids...) + return iu +} + +// AddMaintenanceEntries adds the "maintenance_entries" edges to the MaintenanceEntry entity. +func (iu *ItemUpdate) AddMaintenanceEntries(m ...*MaintenanceEntry) *ItemUpdate { + ids := make([]uuid.UUID, len(m)) + for i := range m { + ids[i] = m[i].ID + } + return iu.AddMaintenanceEntryIDs(ids...) +} + // AddAttachmentIDs adds the "attachments" edge to the Attachment entity by IDs. func (iu *ItemUpdate) AddAttachmentIDs(ids ...uuid.UUID) *ItemUpdate { iu.mutation.AddAttachmentIDs(ids...) @@ -607,6 +623,27 @@ func (iu *ItemUpdate) RemoveFields(i ...*ItemField) *ItemUpdate { return iu.RemoveFieldIDs(ids...) } +// ClearMaintenanceEntries clears all "maintenance_entries" edges to the MaintenanceEntry entity. +func (iu *ItemUpdate) ClearMaintenanceEntries() *ItemUpdate { + iu.mutation.ClearMaintenanceEntries() + return iu +} + +// RemoveMaintenanceEntryIDs removes the "maintenance_entries" edge to MaintenanceEntry entities by IDs. +func (iu *ItemUpdate) RemoveMaintenanceEntryIDs(ids ...uuid.UUID) *ItemUpdate { + iu.mutation.RemoveMaintenanceEntryIDs(ids...) + return iu +} + +// RemoveMaintenanceEntries removes "maintenance_entries" edges to MaintenanceEntry entities. +func (iu *ItemUpdate) RemoveMaintenanceEntries(m ...*MaintenanceEntry) *ItemUpdate { + ids := make([]uuid.UUID, len(m)) + for i := range m { + ids[i] = m[i].ID + } + return iu.RemoveMaintenanceEntryIDs(ids...) +} + // ClearAttachments clears all "attachments" edges to the Attachment entity. func (iu *ItemUpdate) ClearAttachments() *ItemUpdate { iu.mutation.ClearAttachments() @@ -1144,6 +1181,60 @@ func (iu *ItemUpdate) sqlSave(ctx context.Context) (n int, err error) { } _spec.Edges.Add = append(_spec.Edges.Add, edge) } + if iu.mutation.MaintenanceEntriesCleared() { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.O2M, + Inverse: false, + Table: item.MaintenanceEntriesTable, + Columns: []string{item.MaintenanceEntriesColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: &sqlgraph.FieldSpec{ + Type: field.TypeUUID, + Column: maintenanceentry.FieldID, + }, + }, + } + _spec.Edges.Clear = append(_spec.Edges.Clear, edge) + } + if nodes := iu.mutation.RemovedMaintenanceEntriesIDs(); len(nodes) > 0 && !iu.mutation.MaintenanceEntriesCleared() { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.O2M, + Inverse: false, + Table: item.MaintenanceEntriesTable, + Columns: []string{item.MaintenanceEntriesColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: &sqlgraph.FieldSpec{ + Type: field.TypeUUID, + Column: maintenanceentry.FieldID, + }, + }, + } + for _, k := range nodes { + edge.Target.Nodes = append(edge.Target.Nodes, k) + } + _spec.Edges.Clear = append(_spec.Edges.Clear, edge) + } + if nodes := iu.mutation.MaintenanceEntriesIDs(); len(nodes) > 0 { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.O2M, + Inverse: false, + Table: item.MaintenanceEntriesTable, + Columns: []string{item.MaintenanceEntriesColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: &sqlgraph.FieldSpec{ + Type: field.TypeUUID, + Column: maintenanceentry.FieldID, + }, + }, + } + for _, k := range nodes { + edge.Target.Nodes = append(edge.Target.Nodes, k) + } + _spec.Edges.Add = append(_spec.Edges.Add, edge) + } if iu.mutation.AttachmentsCleared() { edge := &sqlgraph.EdgeSpec{ Rel: sqlgraph.O2M, @@ -1689,6 +1780,21 @@ func (iuo *ItemUpdateOne) AddFields(i ...*ItemField) *ItemUpdateOne { return iuo.AddFieldIDs(ids...) } +// AddMaintenanceEntryIDs adds the "maintenance_entries" edge to the MaintenanceEntry entity by IDs. +func (iuo *ItemUpdateOne) AddMaintenanceEntryIDs(ids ...uuid.UUID) *ItemUpdateOne { + iuo.mutation.AddMaintenanceEntryIDs(ids...) + return iuo +} + +// AddMaintenanceEntries adds the "maintenance_entries" edges to the MaintenanceEntry entity. +func (iuo *ItemUpdateOne) AddMaintenanceEntries(m ...*MaintenanceEntry) *ItemUpdateOne { + ids := make([]uuid.UUID, len(m)) + for i := range m { + ids[i] = m[i].ID + } + return iuo.AddMaintenanceEntryIDs(ids...) +} + // AddAttachmentIDs adds the "attachments" edge to the Attachment entity by IDs. func (iuo *ItemUpdateOne) AddAttachmentIDs(ids ...uuid.UUID) *ItemUpdateOne { iuo.mutation.AddAttachmentIDs(ids...) @@ -1790,6 +1896,27 @@ func (iuo *ItemUpdateOne) RemoveFields(i ...*ItemField) *ItemUpdateOne { return iuo.RemoveFieldIDs(ids...) } +// ClearMaintenanceEntries clears all "maintenance_entries" edges to the MaintenanceEntry entity. +func (iuo *ItemUpdateOne) ClearMaintenanceEntries() *ItemUpdateOne { + iuo.mutation.ClearMaintenanceEntries() + return iuo +} + +// RemoveMaintenanceEntryIDs removes the "maintenance_entries" edge to MaintenanceEntry entities by IDs. +func (iuo *ItemUpdateOne) RemoveMaintenanceEntryIDs(ids ...uuid.UUID) *ItemUpdateOne { + iuo.mutation.RemoveMaintenanceEntryIDs(ids...) + return iuo +} + +// RemoveMaintenanceEntries removes "maintenance_entries" edges to MaintenanceEntry entities. +func (iuo *ItemUpdateOne) RemoveMaintenanceEntries(m ...*MaintenanceEntry) *ItemUpdateOne { + ids := make([]uuid.UUID, len(m)) + for i := range m { + ids[i] = m[i].ID + } + return iuo.RemoveMaintenanceEntryIDs(ids...) +} + // ClearAttachments clears all "attachments" edges to the Attachment entity. func (iuo *ItemUpdateOne) ClearAttachments() *ItemUpdateOne { iuo.mutation.ClearAttachments() @@ -2357,6 +2484,60 @@ func (iuo *ItemUpdateOne) sqlSave(ctx context.Context) (_node *Item, err error) } _spec.Edges.Add = append(_spec.Edges.Add, edge) } + if iuo.mutation.MaintenanceEntriesCleared() { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.O2M, + Inverse: false, + Table: item.MaintenanceEntriesTable, + Columns: []string{item.MaintenanceEntriesColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: &sqlgraph.FieldSpec{ + Type: field.TypeUUID, + Column: maintenanceentry.FieldID, + }, + }, + } + _spec.Edges.Clear = append(_spec.Edges.Clear, edge) + } + if nodes := iuo.mutation.RemovedMaintenanceEntriesIDs(); len(nodes) > 0 && !iuo.mutation.MaintenanceEntriesCleared() { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.O2M, + Inverse: false, + Table: item.MaintenanceEntriesTable, + Columns: []string{item.MaintenanceEntriesColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: &sqlgraph.FieldSpec{ + Type: field.TypeUUID, + Column: maintenanceentry.FieldID, + }, + }, + } + for _, k := range nodes { + edge.Target.Nodes = append(edge.Target.Nodes, k) + } + _spec.Edges.Clear = append(_spec.Edges.Clear, edge) + } + if nodes := iuo.mutation.MaintenanceEntriesIDs(); len(nodes) > 0 { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.O2M, + Inverse: false, + Table: item.MaintenanceEntriesTable, + Columns: []string{item.MaintenanceEntriesColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: &sqlgraph.FieldSpec{ + Type: field.TypeUUID, + Column: maintenanceentry.FieldID, + }, + }, + } + for _, k := range nodes { + edge.Target.Nodes = append(edge.Target.Nodes, k) + } + _spec.Edges.Add = append(_spec.Edges.Add, edge) + } if iuo.mutation.AttachmentsCleared() { edge := &sqlgraph.EdgeSpec{ Rel: sqlgraph.O2M, diff --git a/backend/internal/data/ent/maintenanceentry.go b/backend/internal/data/ent/maintenanceentry.go new file mode 100644 index 0000000..4d0b078 --- /dev/null +++ b/backend/internal/data/ent/maintenanceentry.go @@ -0,0 +1,202 @@ +// Code generated by ent, DO NOT EDIT. + +package ent + +import ( + "fmt" + "strings" + "time" + + "entgo.io/ent/dialect/sql" + "github.com/google/uuid" + "github.com/hay-kot/homebox/backend/internal/data/ent/item" + "github.com/hay-kot/homebox/backend/internal/data/ent/maintenanceentry" +) + +// MaintenanceEntry is the model entity for the MaintenanceEntry schema. +type MaintenanceEntry struct { + config `json:"-"` + // ID of the ent. + ID uuid.UUID `json:"id,omitempty"` + // CreatedAt holds the value of the "created_at" field. + CreatedAt time.Time `json:"created_at,omitempty"` + // UpdatedAt holds the value of the "updated_at" field. + UpdatedAt time.Time `json:"updated_at,omitempty"` + // ItemID holds the value of the "item_id" field. + ItemID uuid.UUID `json:"item_id,omitempty"` + // Date holds the value of the "date" field. + Date time.Time `json:"date,omitempty"` + // Name holds the value of the "name" field. + Name string `json:"name,omitempty"` + // Description holds the value of the "description" field. + Description string `json:"description,omitempty"` + // Cost holds the value of the "cost" field. + Cost float64 `json:"cost,omitempty"` + // Edges holds the relations/edges for other nodes in the graph. + // The values are being populated by the MaintenanceEntryQuery when eager-loading is set. + Edges MaintenanceEntryEdges `json:"edges"` +} + +// MaintenanceEntryEdges holds the relations/edges for other nodes in the graph. +type MaintenanceEntryEdges struct { + // Item holds the value of the item edge. + Item *Item `json:"item,omitempty"` + // loadedTypes holds the information for reporting if a + // type was loaded (or requested) in eager-loading or not. + loadedTypes [1]bool +} + +// ItemOrErr returns the Item value or an error if the edge +// was not loaded in eager-loading, or loaded but was not found. +func (e MaintenanceEntryEdges) ItemOrErr() (*Item, error) { + if e.loadedTypes[0] { + if e.Item == nil { + // Edge was loaded but was not found. + return nil, &NotFoundError{label: item.Label} + } + return e.Item, nil + } + return nil, &NotLoadedError{edge: "item"} +} + +// scanValues returns the types for scanning values from sql.Rows. +func (*MaintenanceEntry) scanValues(columns []string) ([]any, error) { + values := make([]any, len(columns)) + for i := range columns { + switch columns[i] { + case maintenanceentry.FieldCost: + values[i] = new(sql.NullFloat64) + case maintenanceentry.FieldName, maintenanceentry.FieldDescription: + values[i] = new(sql.NullString) + case maintenanceentry.FieldCreatedAt, maintenanceentry.FieldUpdatedAt, maintenanceentry.FieldDate: + values[i] = new(sql.NullTime) + case maintenanceentry.FieldID, maintenanceentry.FieldItemID: + values[i] = new(uuid.UUID) + default: + return nil, fmt.Errorf("unexpected column %q for type MaintenanceEntry", columns[i]) + } + } + return values, nil +} + +// assignValues assigns the values that were returned from sql.Rows (after scanning) +// to the MaintenanceEntry fields. +func (me *MaintenanceEntry) assignValues(columns []string, values []any) error { + if m, n := len(values), len(columns); m < n { + return fmt.Errorf("mismatch number of scan values: %d != %d", m, n) + } + for i := range columns { + switch columns[i] { + case maintenanceentry.FieldID: + if value, ok := values[i].(*uuid.UUID); !ok { + return fmt.Errorf("unexpected type %T for field id", values[i]) + } else if value != nil { + me.ID = *value + } + case maintenanceentry.FieldCreatedAt: + if value, ok := values[i].(*sql.NullTime); !ok { + return fmt.Errorf("unexpected type %T for field created_at", values[i]) + } else if value.Valid { + me.CreatedAt = value.Time + } + case maintenanceentry.FieldUpdatedAt: + if value, ok := values[i].(*sql.NullTime); !ok { + return fmt.Errorf("unexpected type %T for field updated_at", values[i]) + } else if value.Valid { + me.UpdatedAt = value.Time + } + case maintenanceentry.FieldItemID: + if value, ok := values[i].(*uuid.UUID); !ok { + return fmt.Errorf("unexpected type %T for field item_id", values[i]) + } else if value != nil { + me.ItemID = *value + } + case maintenanceentry.FieldDate: + if value, ok := values[i].(*sql.NullTime); !ok { + return fmt.Errorf("unexpected type %T for field date", values[i]) + } else if value.Valid { + me.Date = value.Time + } + case maintenanceentry.FieldName: + if value, ok := values[i].(*sql.NullString); !ok { + return fmt.Errorf("unexpected type %T for field name", values[i]) + } else if value.Valid { + me.Name = value.String + } + case maintenanceentry.FieldDescription: + if value, ok := values[i].(*sql.NullString); !ok { + return fmt.Errorf("unexpected type %T for field description", values[i]) + } else if value.Valid { + me.Description = value.String + } + case maintenanceentry.FieldCost: + if value, ok := values[i].(*sql.NullFloat64); !ok { + return fmt.Errorf("unexpected type %T for field cost", values[i]) + } else if value.Valid { + me.Cost = value.Float64 + } + } + } + return nil +} + +// QueryItem queries the "item" edge of the MaintenanceEntry entity. +func (me *MaintenanceEntry) QueryItem() *ItemQuery { + return (&MaintenanceEntryClient{config: me.config}).QueryItem(me) +} + +// Update returns a builder for updating this MaintenanceEntry. +// Note that you need to call MaintenanceEntry.Unwrap() before calling this method if this MaintenanceEntry +// was returned from a transaction, and the transaction was committed or rolled back. +func (me *MaintenanceEntry) Update() *MaintenanceEntryUpdateOne { + return (&MaintenanceEntryClient{config: me.config}).UpdateOne(me) +} + +// Unwrap unwraps the MaintenanceEntry entity that was returned from a transaction after it was closed, +// so that all future queries will be executed through the driver which created the transaction. +func (me *MaintenanceEntry) Unwrap() *MaintenanceEntry { + _tx, ok := me.config.driver.(*txDriver) + if !ok { + panic("ent: MaintenanceEntry is not a transactional entity") + } + me.config.driver = _tx.drv + return me +} + +// String implements the fmt.Stringer. +func (me *MaintenanceEntry) String() string { + var builder strings.Builder + builder.WriteString("MaintenanceEntry(") + builder.WriteString(fmt.Sprintf("id=%v, ", me.ID)) + builder.WriteString("created_at=") + builder.WriteString(me.CreatedAt.Format(time.ANSIC)) + builder.WriteString(", ") + builder.WriteString("updated_at=") + builder.WriteString(me.UpdatedAt.Format(time.ANSIC)) + builder.WriteString(", ") + builder.WriteString("item_id=") + builder.WriteString(fmt.Sprintf("%v", me.ItemID)) + builder.WriteString(", ") + builder.WriteString("date=") + builder.WriteString(me.Date.Format(time.ANSIC)) + builder.WriteString(", ") + builder.WriteString("name=") + builder.WriteString(me.Name) + builder.WriteString(", ") + builder.WriteString("description=") + builder.WriteString(me.Description) + builder.WriteString(", ") + builder.WriteString("cost=") + builder.WriteString(fmt.Sprintf("%v", me.Cost)) + builder.WriteByte(')') + return builder.String() +} + +// MaintenanceEntries is a parsable slice of MaintenanceEntry. +type MaintenanceEntries []*MaintenanceEntry + +func (me MaintenanceEntries) config(cfg config) { + for _i := range me { + me[_i].config = cfg + } +} diff --git a/backend/internal/data/ent/maintenanceentry/maintenanceentry.go b/backend/internal/data/ent/maintenanceentry/maintenanceentry.go new file mode 100644 index 0000000..c1dcffc --- /dev/null +++ b/backend/internal/data/ent/maintenanceentry/maintenanceentry.go @@ -0,0 +1,82 @@ +// Code generated by ent, DO NOT EDIT. + +package maintenanceentry + +import ( + "time" + + "github.com/google/uuid" +) + +const ( + // Label holds the string label denoting the maintenanceentry type in the database. + Label = "maintenance_entry" + // FieldID holds the string denoting the id field in the database. + FieldID = "id" + // FieldCreatedAt holds the string denoting the created_at field in the database. + FieldCreatedAt = "created_at" + // FieldUpdatedAt holds the string denoting the updated_at field in the database. + FieldUpdatedAt = "updated_at" + // FieldItemID holds the string denoting the item_id field in the database. + FieldItemID = "item_id" + // FieldDate holds the string denoting the date field in the database. + FieldDate = "date" + // FieldName holds the string denoting the name field in the database. + FieldName = "name" + // FieldDescription holds the string denoting the description field in the database. + FieldDescription = "description" + // FieldCost holds the string denoting the cost field in the database. + FieldCost = "cost" + // EdgeItem holds the string denoting the item edge name in mutations. + EdgeItem = "item" + // Table holds the table name of the maintenanceentry in the database. + Table = "maintenance_entries" + // ItemTable is the table that holds the item relation/edge. + ItemTable = "maintenance_entries" + // ItemInverseTable is the table name for the Item entity. + // It exists in this package in order to avoid circular dependency with the "item" package. + ItemInverseTable = "items" + // ItemColumn is the table column denoting the item relation/edge. + ItemColumn = "item_id" +) + +// Columns holds all SQL columns for maintenanceentry fields. +var Columns = []string{ + FieldID, + FieldCreatedAt, + FieldUpdatedAt, + FieldItemID, + FieldDate, + FieldName, + FieldDescription, + FieldCost, +} + +// ValidColumn reports if the column name is valid (part of the table columns). +func ValidColumn(column string) bool { + for i := range Columns { + if column == Columns[i] { + return true + } + } + return false +} + +var ( + // DefaultCreatedAt holds the default value on creation for the "created_at" field. + DefaultCreatedAt func() time.Time + // DefaultUpdatedAt holds the default value on creation for the "updated_at" field. + DefaultUpdatedAt func() time.Time + // UpdateDefaultUpdatedAt holds the default value on update for the "updated_at" field. + UpdateDefaultUpdatedAt func() time.Time + // DefaultDate holds the default value on creation for the "date" field. + DefaultDate func() time.Time + // NameValidator is a validator for the "name" field. It is called by the builders before save. + NameValidator func(string) error + // DescriptionValidator is a validator for the "description" field. It is called by the builders before save. + DescriptionValidator func(string) error + // DefaultCost holds the default value on creation for the "cost" field. + DefaultCost float64 + // DefaultID holds the default value on creation for the "id" field. + DefaultID func() uuid.UUID +) diff --git a/backend/internal/data/ent/maintenanceentry/where.go b/backend/internal/data/ent/maintenanceentry/where.go new file mode 100644 index 0000000..02d9633 --- /dev/null +++ b/backend/internal/data/ent/maintenanceentry/where.go @@ -0,0 +1,696 @@ +// Code generated by ent, DO NOT EDIT. + +package maintenanceentry + +import ( + "time" + + "entgo.io/ent/dialect/sql" + "entgo.io/ent/dialect/sql/sqlgraph" + "github.com/google/uuid" + "github.com/hay-kot/homebox/backend/internal/data/ent/predicate" +) + +// ID filters vertices based on their ID field. +func ID(id uuid.UUID) predicate.MaintenanceEntry { + return predicate.MaintenanceEntry(func(s *sql.Selector) { + s.Where(sql.EQ(s.C(FieldID), id)) + }) +} + +// IDEQ applies the EQ predicate on the ID field. +func IDEQ(id uuid.UUID) predicate.MaintenanceEntry { + return predicate.MaintenanceEntry(func(s *sql.Selector) { + s.Where(sql.EQ(s.C(FieldID), id)) + }) +} + +// IDNEQ applies the NEQ predicate on the ID field. +func IDNEQ(id uuid.UUID) predicate.MaintenanceEntry { + return predicate.MaintenanceEntry(func(s *sql.Selector) { + s.Where(sql.NEQ(s.C(FieldID), id)) + }) +} + +// IDIn applies the In predicate on the ID field. +func IDIn(ids ...uuid.UUID) predicate.MaintenanceEntry { + return predicate.MaintenanceEntry(func(s *sql.Selector) { + v := make([]any, len(ids)) + for i := range v { + v[i] = ids[i] + } + s.Where(sql.In(s.C(FieldID), v...)) + }) +} + +// IDNotIn applies the NotIn predicate on the ID field. +func IDNotIn(ids ...uuid.UUID) predicate.MaintenanceEntry { + return predicate.MaintenanceEntry(func(s *sql.Selector) { + v := make([]any, len(ids)) + for i := range v { + v[i] = ids[i] + } + s.Where(sql.NotIn(s.C(FieldID), v...)) + }) +} + +// IDGT applies the GT predicate on the ID field. +func IDGT(id uuid.UUID) predicate.MaintenanceEntry { + return predicate.MaintenanceEntry(func(s *sql.Selector) { + s.Where(sql.GT(s.C(FieldID), id)) + }) +} + +// IDGTE applies the GTE predicate on the ID field. +func IDGTE(id uuid.UUID) predicate.MaintenanceEntry { + return predicate.MaintenanceEntry(func(s *sql.Selector) { + s.Where(sql.GTE(s.C(FieldID), id)) + }) +} + +// IDLT applies the LT predicate on the ID field. +func IDLT(id uuid.UUID) predicate.MaintenanceEntry { + return predicate.MaintenanceEntry(func(s *sql.Selector) { + s.Where(sql.LT(s.C(FieldID), id)) + }) +} + +// IDLTE applies the LTE predicate on the ID field. +func IDLTE(id uuid.UUID) predicate.MaintenanceEntry { + return predicate.MaintenanceEntry(func(s *sql.Selector) { + s.Where(sql.LTE(s.C(FieldID), id)) + }) +} + +// CreatedAt applies equality check predicate on the "created_at" field. It's identical to CreatedAtEQ. +func CreatedAt(v time.Time) predicate.MaintenanceEntry { + return predicate.MaintenanceEntry(func(s *sql.Selector) { + s.Where(sql.EQ(s.C(FieldCreatedAt), v)) + }) +} + +// UpdatedAt applies equality check predicate on the "updated_at" field. It's identical to UpdatedAtEQ. +func UpdatedAt(v time.Time) predicate.MaintenanceEntry { + return predicate.MaintenanceEntry(func(s *sql.Selector) { + s.Where(sql.EQ(s.C(FieldUpdatedAt), v)) + }) +} + +// ItemID applies equality check predicate on the "item_id" field. It's identical to ItemIDEQ. +func ItemID(v uuid.UUID) predicate.MaintenanceEntry { + return predicate.MaintenanceEntry(func(s *sql.Selector) { + s.Where(sql.EQ(s.C(FieldItemID), v)) + }) +} + +// Date applies equality check predicate on the "date" field. It's identical to DateEQ. +func Date(v time.Time) predicate.MaintenanceEntry { + return predicate.MaintenanceEntry(func(s *sql.Selector) { + s.Where(sql.EQ(s.C(FieldDate), v)) + }) +} + +// Name applies equality check predicate on the "name" field. It's identical to NameEQ. +func Name(v string) predicate.MaintenanceEntry { + return predicate.MaintenanceEntry(func(s *sql.Selector) { + s.Where(sql.EQ(s.C(FieldName), v)) + }) +} + +// Description applies equality check predicate on the "description" field. It's identical to DescriptionEQ. +func Description(v string) predicate.MaintenanceEntry { + return predicate.MaintenanceEntry(func(s *sql.Selector) { + s.Where(sql.EQ(s.C(FieldDescription), v)) + }) +} + +// Cost applies equality check predicate on the "cost" field. It's identical to CostEQ. +func Cost(v float64) predicate.MaintenanceEntry { + return predicate.MaintenanceEntry(func(s *sql.Selector) { + s.Where(sql.EQ(s.C(FieldCost), v)) + }) +} + +// CreatedAtEQ applies the EQ predicate on the "created_at" field. +func CreatedAtEQ(v time.Time) predicate.MaintenanceEntry { + return predicate.MaintenanceEntry(func(s *sql.Selector) { + s.Where(sql.EQ(s.C(FieldCreatedAt), v)) + }) +} + +// CreatedAtNEQ applies the NEQ predicate on the "created_at" field. +func CreatedAtNEQ(v time.Time) predicate.MaintenanceEntry { + return predicate.MaintenanceEntry(func(s *sql.Selector) { + s.Where(sql.NEQ(s.C(FieldCreatedAt), v)) + }) +} + +// CreatedAtIn applies the In predicate on the "created_at" field. +func CreatedAtIn(vs ...time.Time) predicate.MaintenanceEntry { + v := make([]any, len(vs)) + for i := range v { + v[i] = vs[i] + } + return predicate.MaintenanceEntry(func(s *sql.Selector) { + s.Where(sql.In(s.C(FieldCreatedAt), v...)) + }) +} + +// CreatedAtNotIn applies the NotIn predicate on the "created_at" field. +func CreatedAtNotIn(vs ...time.Time) predicate.MaintenanceEntry { + v := make([]any, len(vs)) + for i := range v { + v[i] = vs[i] + } + return predicate.MaintenanceEntry(func(s *sql.Selector) { + s.Where(sql.NotIn(s.C(FieldCreatedAt), v...)) + }) +} + +// CreatedAtGT applies the GT predicate on the "created_at" field. +func CreatedAtGT(v time.Time) predicate.MaintenanceEntry { + return predicate.MaintenanceEntry(func(s *sql.Selector) { + s.Where(sql.GT(s.C(FieldCreatedAt), v)) + }) +} + +// CreatedAtGTE applies the GTE predicate on the "created_at" field. +func CreatedAtGTE(v time.Time) predicate.MaintenanceEntry { + return predicate.MaintenanceEntry(func(s *sql.Selector) { + s.Where(sql.GTE(s.C(FieldCreatedAt), v)) + }) +} + +// CreatedAtLT applies the LT predicate on the "created_at" field. +func CreatedAtLT(v time.Time) predicate.MaintenanceEntry { + return predicate.MaintenanceEntry(func(s *sql.Selector) { + s.Where(sql.LT(s.C(FieldCreatedAt), v)) + }) +} + +// CreatedAtLTE applies the LTE predicate on the "created_at" field. +func CreatedAtLTE(v time.Time) predicate.MaintenanceEntry { + return predicate.MaintenanceEntry(func(s *sql.Selector) { + s.Where(sql.LTE(s.C(FieldCreatedAt), v)) + }) +} + +// UpdatedAtEQ applies the EQ predicate on the "updated_at" field. +func UpdatedAtEQ(v time.Time) predicate.MaintenanceEntry { + return predicate.MaintenanceEntry(func(s *sql.Selector) { + s.Where(sql.EQ(s.C(FieldUpdatedAt), v)) + }) +} + +// UpdatedAtNEQ applies the NEQ predicate on the "updated_at" field. +func UpdatedAtNEQ(v time.Time) predicate.MaintenanceEntry { + return predicate.MaintenanceEntry(func(s *sql.Selector) { + s.Where(sql.NEQ(s.C(FieldUpdatedAt), v)) + }) +} + +// UpdatedAtIn applies the In predicate on the "updated_at" field. +func UpdatedAtIn(vs ...time.Time) predicate.MaintenanceEntry { + v := make([]any, len(vs)) + for i := range v { + v[i] = vs[i] + } + return predicate.MaintenanceEntry(func(s *sql.Selector) { + s.Where(sql.In(s.C(FieldUpdatedAt), v...)) + }) +} + +// UpdatedAtNotIn applies the NotIn predicate on the "updated_at" field. +func UpdatedAtNotIn(vs ...time.Time) predicate.MaintenanceEntry { + v := make([]any, len(vs)) + for i := range v { + v[i] = vs[i] + } + return predicate.MaintenanceEntry(func(s *sql.Selector) { + s.Where(sql.NotIn(s.C(FieldUpdatedAt), v...)) + }) +} + +// UpdatedAtGT applies the GT predicate on the "updated_at" field. +func UpdatedAtGT(v time.Time) predicate.MaintenanceEntry { + return predicate.MaintenanceEntry(func(s *sql.Selector) { + s.Where(sql.GT(s.C(FieldUpdatedAt), v)) + }) +} + +// UpdatedAtGTE applies the GTE predicate on the "updated_at" field. +func UpdatedAtGTE(v time.Time) predicate.MaintenanceEntry { + return predicate.MaintenanceEntry(func(s *sql.Selector) { + s.Where(sql.GTE(s.C(FieldUpdatedAt), v)) + }) +} + +// UpdatedAtLT applies the LT predicate on the "updated_at" field. +func UpdatedAtLT(v time.Time) predicate.MaintenanceEntry { + return predicate.MaintenanceEntry(func(s *sql.Selector) { + s.Where(sql.LT(s.C(FieldUpdatedAt), v)) + }) +} + +// UpdatedAtLTE applies the LTE predicate on the "updated_at" field. +func UpdatedAtLTE(v time.Time) predicate.MaintenanceEntry { + return predicate.MaintenanceEntry(func(s *sql.Selector) { + s.Where(sql.LTE(s.C(FieldUpdatedAt), v)) + }) +} + +// ItemIDEQ applies the EQ predicate on the "item_id" field. +func ItemIDEQ(v uuid.UUID) predicate.MaintenanceEntry { + return predicate.MaintenanceEntry(func(s *sql.Selector) { + s.Where(sql.EQ(s.C(FieldItemID), v)) + }) +} + +// ItemIDNEQ applies the NEQ predicate on the "item_id" field. +func ItemIDNEQ(v uuid.UUID) predicate.MaintenanceEntry { + return predicate.MaintenanceEntry(func(s *sql.Selector) { + s.Where(sql.NEQ(s.C(FieldItemID), v)) + }) +} + +// ItemIDIn applies the In predicate on the "item_id" field. +func ItemIDIn(vs ...uuid.UUID) predicate.MaintenanceEntry { + v := make([]any, len(vs)) + for i := range v { + v[i] = vs[i] + } + return predicate.MaintenanceEntry(func(s *sql.Selector) { + s.Where(sql.In(s.C(FieldItemID), v...)) + }) +} + +// ItemIDNotIn applies the NotIn predicate on the "item_id" field. +func ItemIDNotIn(vs ...uuid.UUID) predicate.MaintenanceEntry { + v := make([]any, len(vs)) + for i := range v { + v[i] = vs[i] + } + return predicate.MaintenanceEntry(func(s *sql.Selector) { + s.Where(sql.NotIn(s.C(FieldItemID), v...)) + }) +} + +// DateEQ applies the EQ predicate on the "date" field. +func DateEQ(v time.Time) predicate.MaintenanceEntry { + return predicate.MaintenanceEntry(func(s *sql.Selector) { + s.Where(sql.EQ(s.C(FieldDate), v)) + }) +} + +// DateNEQ applies the NEQ predicate on the "date" field. +func DateNEQ(v time.Time) predicate.MaintenanceEntry { + return predicate.MaintenanceEntry(func(s *sql.Selector) { + s.Where(sql.NEQ(s.C(FieldDate), v)) + }) +} + +// DateIn applies the In predicate on the "date" field. +func DateIn(vs ...time.Time) predicate.MaintenanceEntry { + v := make([]any, len(vs)) + for i := range v { + v[i] = vs[i] + } + return predicate.MaintenanceEntry(func(s *sql.Selector) { + s.Where(sql.In(s.C(FieldDate), v...)) + }) +} + +// DateNotIn applies the NotIn predicate on the "date" field. +func DateNotIn(vs ...time.Time) predicate.MaintenanceEntry { + v := make([]any, len(vs)) + for i := range v { + v[i] = vs[i] + } + return predicate.MaintenanceEntry(func(s *sql.Selector) { + s.Where(sql.NotIn(s.C(FieldDate), v...)) + }) +} + +// DateGT applies the GT predicate on the "date" field. +func DateGT(v time.Time) predicate.MaintenanceEntry { + return predicate.MaintenanceEntry(func(s *sql.Selector) { + s.Where(sql.GT(s.C(FieldDate), v)) + }) +} + +// DateGTE applies the GTE predicate on the "date" field. +func DateGTE(v time.Time) predicate.MaintenanceEntry { + return predicate.MaintenanceEntry(func(s *sql.Selector) { + s.Where(sql.GTE(s.C(FieldDate), v)) + }) +} + +// DateLT applies the LT predicate on the "date" field. +func DateLT(v time.Time) predicate.MaintenanceEntry { + return predicate.MaintenanceEntry(func(s *sql.Selector) { + s.Where(sql.LT(s.C(FieldDate), v)) + }) +} + +// DateLTE applies the LTE predicate on the "date" field. +func DateLTE(v time.Time) predicate.MaintenanceEntry { + return predicate.MaintenanceEntry(func(s *sql.Selector) { + s.Where(sql.LTE(s.C(FieldDate), v)) + }) +} + +// NameEQ applies the EQ predicate on the "name" field. +func NameEQ(v string) predicate.MaintenanceEntry { + return predicate.MaintenanceEntry(func(s *sql.Selector) { + s.Where(sql.EQ(s.C(FieldName), v)) + }) +} + +// NameNEQ applies the NEQ predicate on the "name" field. +func NameNEQ(v string) predicate.MaintenanceEntry { + return predicate.MaintenanceEntry(func(s *sql.Selector) { + s.Where(sql.NEQ(s.C(FieldName), v)) + }) +} + +// NameIn applies the In predicate on the "name" field. +func NameIn(vs ...string) predicate.MaintenanceEntry { + v := make([]any, len(vs)) + for i := range v { + v[i] = vs[i] + } + return predicate.MaintenanceEntry(func(s *sql.Selector) { + s.Where(sql.In(s.C(FieldName), v...)) + }) +} + +// NameNotIn applies the NotIn predicate on the "name" field. +func NameNotIn(vs ...string) predicate.MaintenanceEntry { + v := make([]any, len(vs)) + for i := range v { + v[i] = vs[i] + } + return predicate.MaintenanceEntry(func(s *sql.Selector) { + s.Where(sql.NotIn(s.C(FieldName), v...)) + }) +} + +// NameGT applies the GT predicate on the "name" field. +func NameGT(v string) predicate.MaintenanceEntry { + return predicate.MaintenanceEntry(func(s *sql.Selector) { + s.Where(sql.GT(s.C(FieldName), v)) + }) +} + +// NameGTE applies the GTE predicate on the "name" field. +func NameGTE(v string) predicate.MaintenanceEntry { + return predicate.MaintenanceEntry(func(s *sql.Selector) { + s.Where(sql.GTE(s.C(FieldName), v)) + }) +} + +// NameLT applies the LT predicate on the "name" field. +func NameLT(v string) predicate.MaintenanceEntry { + return predicate.MaintenanceEntry(func(s *sql.Selector) { + s.Where(sql.LT(s.C(FieldName), v)) + }) +} + +// NameLTE applies the LTE predicate on the "name" field. +func NameLTE(v string) predicate.MaintenanceEntry { + return predicate.MaintenanceEntry(func(s *sql.Selector) { + s.Where(sql.LTE(s.C(FieldName), v)) + }) +} + +// NameContains applies the Contains predicate on the "name" field. +func NameContains(v string) predicate.MaintenanceEntry { + return predicate.MaintenanceEntry(func(s *sql.Selector) { + s.Where(sql.Contains(s.C(FieldName), v)) + }) +} + +// NameHasPrefix applies the HasPrefix predicate on the "name" field. +func NameHasPrefix(v string) predicate.MaintenanceEntry { + return predicate.MaintenanceEntry(func(s *sql.Selector) { + s.Where(sql.HasPrefix(s.C(FieldName), v)) + }) +} + +// NameHasSuffix applies the HasSuffix predicate on the "name" field. +func NameHasSuffix(v string) predicate.MaintenanceEntry { + return predicate.MaintenanceEntry(func(s *sql.Selector) { + s.Where(sql.HasSuffix(s.C(FieldName), v)) + }) +} + +// NameEqualFold applies the EqualFold predicate on the "name" field. +func NameEqualFold(v string) predicate.MaintenanceEntry { + return predicate.MaintenanceEntry(func(s *sql.Selector) { + s.Where(sql.EqualFold(s.C(FieldName), v)) + }) +} + +// NameContainsFold applies the ContainsFold predicate on the "name" field. +func NameContainsFold(v string) predicate.MaintenanceEntry { + return predicate.MaintenanceEntry(func(s *sql.Selector) { + s.Where(sql.ContainsFold(s.C(FieldName), v)) + }) +} + +// DescriptionEQ applies the EQ predicate on the "description" field. +func DescriptionEQ(v string) predicate.MaintenanceEntry { + return predicate.MaintenanceEntry(func(s *sql.Selector) { + s.Where(sql.EQ(s.C(FieldDescription), v)) + }) +} + +// DescriptionNEQ applies the NEQ predicate on the "description" field. +func DescriptionNEQ(v string) predicate.MaintenanceEntry { + return predicate.MaintenanceEntry(func(s *sql.Selector) { + s.Where(sql.NEQ(s.C(FieldDescription), v)) + }) +} + +// DescriptionIn applies the In predicate on the "description" field. +func DescriptionIn(vs ...string) predicate.MaintenanceEntry { + v := make([]any, len(vs)) + for i := range v { + v[i] = vs[i] + } + return predicate.MaintenanceEntry(func(s *sql.Selector) { + s.Where(sql.In(s.C(FieldDescription), v...)) + }) +} + +// DescriptionNotIn applies the NotIn predicate on the "description" field. +func DescriptionNotIn(vs ...string) predicate.MaintenanceEntry { + v := make([]any, len(vs)) + for i := range v { + v[i] = vs[i] + } + return predicate.MaintenanceEntry(func(s *sql.Selector) { + s.Where(sql.NotIn(s.C(FieldDescription), v...)) + }) +} + +// DescriptionGT applies the GT predicate on the "description" field. +func DescriptionGT(v string) predicate.MaintenanceEntry { + return predicate.MaintenanceEntry(func(s *sql.Selector) { + s.Where(sql.GT(s.C(FieldDescription), v)) + }) +} + +// DescriptionGTE applies the GTE predicate on the "description" field. +func DescriptionGTE(v string) predicate.MaintenanceEntry { + return predicate.MaintenanceEntry(func(s *sql.Selector) { + s.Where(sql.GTE(s.C(FieldDescription), v)) + }) +} + +// DescriptionLT applies the LT predicate on the "description" field. +func DescriptionLT(v string) predicate.MaintenanceEntry { + return predicate.MaintenanceEntry(func(s *sql.Selector) { + s.Where(sql.LT(s.C(FieldDescription), v)) + }) +} + +// DescriptionLTE applies the LTE predicate on the "description" field. +func DescriptionLTE(v string) predicate.MaintenanceEntry { + return predicate.MaintenanceEntry(func(s *sql.Selector) { + s.Where(sql.LTE(s.C(FieldDescription), v)) + }) +} + +// DescriptionContains applies the Contains predicate on the "description" field. +func DescriptionContains(v string) predicate.MaintenanceEntry { + return predicate.MaintenanceEntry(func(s *sql.Selector) { + s.Where(sql.Contains(s.C(FieldDescription), v)) + }) +} + +// DescriptionHasPrefix applies the HasPrefix predicate on the "description" field. +func DescriptionHasPrefix(v string) predicate.MaintenanceEntry { + return predicate.MaintenanceEntry(func(s *sql.Selector) { + s.Where(sql.HasPrefix(s.C(FieldDescription), v)) + }) +} + +// DescriptionHasSuffix applies the HasSuffix predicate on the "description" field. +func DescriptionHasSuffix(v string) predicate.MaintenanceEntry { + return predicate.MaintenanceEntry(func(s *sql.Selector) { + s.Where(sql.HasSuffix(s.C(FieldDescription), v)) + }) +} + +// DescriptionIsNil applies the IsNil predicate on the "description" field. +func DescriptionIsNil() predicate.MaintenanceEntry { + return predicate.MaintenanceEntry(func(s *sql.Selector) { + s.Where(sql.IsNull(s.C(FieldDescription))) + }) +} + +// DescriptionNotNil applies the NotNil predicate on the "description" field. +func DescriptionNotNil() predicate.MaintenanceEntry { + return predicate.MaintenanceEntry(func(s *sql.Selector) { + s.Where(sql.NotNull(s.C(FieldDescription))) + }) +} + +// DescriptionEqualFold applies the EqualFold predicate on the "description" field. +func DescriptionEqualFold(v string) predicate.MaintenanceEntry { + return predicate.MaintenanceEntry(func(s *sql.Selector) { + s.Where(sql.EqualFold(s.C(FieldDescription), v)) + }) +} + +// DescriptionContainsFold applies the ContainsFold predicate on the "description" field. +func DescriptionContainsFold(v string) predicate.MaintenanceEntry { + return predicate.MaintenanceEntry(func(s *sql.Selector) { + s.Where(sql.ContainsFold(s.C(FieldDescription), v)) + }) +} + +// CostEQ applies the EQ predicate on the "cost" field. +func CostEQ(v float64) predicate.MaintenanceEntry { + return predicate.MaintenanceEntry(func(s *sql.Selector) { + s.Where(sql.EQ(s.C(FieldCost), v)) + }) +} + +// CostNEQ applies the NEQ predicate on the "cost" field. +func CostNEQ(v float64) predicate.MaintenanceEntry { + return predicate.MaintenanceEntry(func(s *sql.Selector) { + s.Where(sql.NEQ(s.C(FieldCost), v)) + }) +} + +// CostIn applies the In predicate on the "cost" field. +func CostIn(vs ...float64) predicate.MaintenanceEntry { + v := make([]any, len(vs)) + for i := range v { + v[i] = vs[i] + } + return predicate.MaintenanceEntry(func(s *sql.Selector) { + s.Where(sql.In(s.C(FieldCost), v...)) + }) +} + +// CostNotIn applies the NotIn predicate on the "cost" field. +func CostNotIn(vs ...float64) predicate.MaintenanceEntry { + v := make([]any, len(vs)) + for i := range v { + v[i] = vs[i] + } + return predicate.MaintenanceEntry(func(s *sql.Selector) { + s.Where(sql.NotIn(s.C(FieldCost), v...)) + }) +} + +// CostGT applies the GT predicate on the "cost" field. +func CostGT(v float64) predicate.MaintenanceEntry { + return predicate.MaintenanceEntry(func(s *sql.Selector) { + s.Where(sql.GT(s.C(FieldCost), v)) + }) +} + +// CostGTE applies the GTE predicate on the "cost" field. +func CostGTE(v float64) predicate.MaintenanceEntry { + return predicate.MaintenanceEntry(func(s *sql.Selector) { + s.Where(sql.GTE(s.C(FieldCost), v)) + }) +} + +// CostLT applies the LT predicate on the "cost" field. +func CostLT(v float64) predicate.MaintenanceEntry { + return predicate.MaintenanceEntry(func(s *sql.Selector) { + s.Where(sql.LT(s.C(FieldCost), v)) + }) +} + +// CostLTE applies the LTE predicate on the "cost" field. +func CostLTE(v float64) predicate.MaintenanceEntry { + return predicate.MaintenanceEntry(func(s *sql.Selector) { + s.Where(sql.LTE(s.C(FieldCost), v)) + }) +} + +// HasItem applies the HasEdge predicate on the "item" edge. +func HasItem() predicate.MaintenanceEntry { + return predicate.MaintenanceEntry(func(s *sql.Selector) { + step := sqlgraph.NewStep( + sqlgraph.From(Table, FieldID), + sqlgraph.To(ItemTable, FieldID), + sqlgraph.Edge(sqlgraph.M2O, true, ItemTable, ItemColumn), + ) + sqlgraph.HasNeighbors(s, step) + }) +} + +// HasItemWith applies the HasEdge predicate on the "item" edge with a given conditions (other predicates). +func HasItemWith(preds ...predicate.Item) predicate.MaintenanceEntry { + return predicate.MaintenanceEntry(func(s *sql.Selector) { + step := sqlgraph.NewStep( + sqlgraph.From(Table, FieldID), + sqlgraph.To(ItemInverseTable, FieldID), + sqlgraph.Edge(sqlgraph.M2O, true, ItemTable, ItemColumn), + ) + sqlgraph.HasNeighborsWith(s, step, func(s *sql.Selector) { + for _, p := range preds { + p(s) + } + }) + }) +} + +// And groups predicates with the AND operator between them. +func And(predicates ...predicate.MaintenanceEntry) predicate.MaintenanceEntry { + return predicate.MaintenanceEntry(func(s *sql.Selector) { + s1 := s.Clone().SetP(nil) + for _, p := range predicates { + p(s1) + } + s.Where(s1.P()) + }) +} + +// Or groups predicates with the OR operator between them. +func Or(predicates ...predicate.MaintenanceEntry) predicate.MaintenanceEntry { + return predicate.MaintenanceEntry(func(s *sql.Selector) { + s1 := s.Clone().SetP(nil) + for i, p := range predicates { + if i > 0 { + s1.Or() + } + p(s1) + } + s.Where(s1.P()) + }) +} + +// Not applies the not operator on the given predicate. +func Not(p predicate.MaintenanceEntry) predicate.MaintenanceEntry { + return predicate.MaintenanceEntry(func(s *sql.Selector) { + p(s.Not()) + }) +} diff --git a/backend/internal/data/ent/maintenanceentry_create.go b/backend/internal/data/ent/maintenanceentry_create.go new file mode 100644 index 0000000..3abaa84 --- /dev/null +++ b/backend/internal/data/ent/maintenanceentry_create.go @@ -0,0 +1,419 @@ +// Code generated by ent, DO NOT EDIT. + +package ent + +import ( + "context" + "errors" + "fmt" + "time" + + "entgo.io/ent/dialect/sql/sqlgraph" + "entgo.io/ent/schema/field" + "github.com/google/uuid" + "github.com/hay-kot/homebox/backend/internal/data/ent/item" + "github.com/hay-kot/homebox/backend/internal/data/ent/maintenanceentry" +) + +// MaintenanceEntryCreate is the builder for creating a MaintenanceEntry entity. +type MaintenanceEntryCreate struct { + config + mutation *MaintenanceEntryMutation + hooks []Hook +} + +// SetCreatedAt sets the "created_at" field. +func (mec *MaintenanceEntryCreate) SetCreatedAt(t time.Time) *MaintenanceEntryCreate { + mec.mutation.SetCreatedAt(t) + return mec +} + +// SetNillableCreatedAt sets the "created_at" field if the given value is not nil. +func (mec *MaintenanceEntryCreate) SetNillableCreatedAt(t *time.Time) *MaintenanceEntryCreate { + if t != nil { + mec.SetCreatedAt(*t) + } + return mec +} + +// SetUpdatedAt sets the "updated_at" field. +func (mec *MaintenanceEntryCreate) SetUpdatedAt(t time.Time) *MaintenanceEntryCreate { + mec.mutation.SetUpdatedAt(t) + return mec +} + +// SetNillableUpdatedAt sets the "updated_at" field if the given value is not nil. +func (mec *MaintenanceEntryCreate) SetNillableUpdatedAt(t *time.Time) *MaintenanceEntryCreate { + if t != nil { + mec.SetUpdatedAt(*t) + } + return mec +} + +// SetItemID sets the "item_id" field. +func (mec *MaintenanceEntryCreate) SetItemID(u uuid.UUID) *MaintenanceEntryCreate { + mec.mutation.SetItemID(u) + return mec +} + +// SetDate sets the "date" field. +func (mec *MaintenanceEntryCreate) SetDate(t time.Time) *MaintenanceEntryCreate { + mec.mutation.SetDate(t) + return mec +} + +// SetNillableDate sets the "date" field if the given value is not nil. +func (mec *MaintenanceEntryCreate) SetNillableDate(t *time.Time) *MaintenanceEntryCreate { + if t != nil { + mec.SetDate(*t) + } + return mec +} + +// SetName sets the "name" field. +func (mec *MaintenanceEntryCreate) SetName(s string) *MaintenanceEntryCreate { + mec.mutation.SetName(s) + return mec +} + +// SetDescription sets the "description" field. +func (mec *MaintenanceEntryCreate) SetDescription(s string) *MaintenanceEntryCreate { + mec.mutation.SetDescription(s) + return mec +} + +// SetNillableDescription sets the "description" field if the given value is not nil. +func (mec *MaintenanceEntryCreate) SetNillableDescription(s *string) *MaintenanceEntryCreate { + if s != nil { + mec.SetDescription(*s) + } + return mec +} + +// SetCost sets the "cost" field. +func (mec *MaintenanceEntryCreate) SetCost(f float64) *MaintenanceEntryCreate { + mec.mutation.SetCost(f) + return mec +} + +// SetNillableCost sets the "cost" field if the given value is not nil. +func (mec *MaintenanceEntryCreate) SetNillableCost(f *float64) *MaintenanceEntryCreate { + if f != nil { + mec.SetCost(*f) + } + return mec +} + +// SetID sets the "id" field. +func (mec *MaintenanceEntryCreate) SetID(u uuid.UUID) *MaintenanceEntryCreate { + mec.mutation.SetID(u) + return mec +} + +// SetNillableID sets the "id" field if the given value is not nil. +func (mec *MaintenanceEntryCreate) SetNillableID(u *uuid.UUID) *MaintenanceEntryCreate { + if u != nil { + mec.SetID(*u) + } + return mec +} + +// SetItem sets the "item" edge to the Item entity. +func (mec *MaintenanceEntryCreate) SetItem(i *Item) *MaintenanceEntryCreate { + return mec.SetItemID(i.ID) +} + +// Mutation returns the MaintenanceEntryMutation object of the builder. +func (mec *MaintenanceEntryCreate) Mutation() *MaintenanceEntryMutation { + return mec.mutation +} + +// Save creates the MaintenanceEntry in the database. +func (mec *MaintenanceEntryCreate) Save(ctx context.Context) (*MaintenanceEntry, error) { + var ( + err error + node *MaintenanceEntry + ) + mec.defaults() + if len(mec.hooks) == 0 { + if err = mec.check(); err != nil { + return nil, err + } + node, err = mec.sqlSave(ctx) + } else { + var mut Mutator = MutateFunc(func(ctx context.Context, m Mutation) (Value, error) { + mutation, ok := m.(*MaintenanceEntryMutation) + if !ok { + return nil, fmt.Errorf("unexpected mutation type %T", m) + } + if err = mec.check(); err != nil { + return nil, err + } + mec.mutation = mutation + if node, err = mec.sqlSave(ctx); err != nil { + return nil, err + } + mutation.id = &node.ID + mutation.done = true + return node, err + }) + for i := len(mec.hooks) - 1; i >= 0; i-- { + if mec.hooks[i] == nil { + return nil, fmt.Errorf("ent: uninitialized hook (forgotten import ent/runtime?)") + } + mut = mec.hooks[i](mut) + } + v, err := mut.Mutate(ctx, mec.mutation) + if err != nil { + return nil, err + } + nv, ok := v.(*MaintenanceEntry) + if !ok { + return nil, fmt.Errorf("unexpected node type %T returned from MaintenanceEntryMutation", v) + } + node = nv + } + return node, err +} + +// SaveX calls Save and panics if Save returns an error. +func (mec *MaintenanceEntryCreate) SaveX(ctx context.Context) *MaintenanceEntry { + v, err := mec.Save(ctx) + if err != nil { + panic(err) + } + return v +} + +// Exec executes the query. +func (mec *MaintenanceEntryCreate) Exec(ctx context.Context) error { + _, err := mec.Save(ctx) + return err +} + +// ExecX is like Exec, but panics if an error occurs. +func (mec *MaintenanceEntryCreate) ExecX(ctx context.Context) { + if err := mec.Exec(ctx); err != nil { + panic(err) + } +} + +// defaults sets the default values of the builder before save. +func (mec *MaintenanceEntryCreate) defaults() { + if _, ok := mec.mutation.CreatedAt(); !ok { + v := maintenanceentry.DefaultCreatedAt() + mec.mutation.SetCreatedAt(v) + } + if _, ok := mec.mutation.UpdatedAt(); !ok { + v := maintenanceentry.DefaultUpdatedAt() + mec.mutation.SetUpdatedAt(v) + } + if _, ok := mec.mutation.Date(); !ok { + v := maintenanceentry.DefaultDate() + mec.mutation.SetDate(v) + } + if _, ok := mec.mutation.Cost(); !ok { + v := maintenanceentry.DefaultCost + mec.mutation.SetCost(v) + } + if _, ok := mec.mutation.ID(); !ok { + v := maintenanceentry.DefaultID() + mec.mutation.SetID(v) + } +} + +// check runs all checks and user-defined validators on the builder. +func (mec *MaintenanceEntryCreate) check() error { + if _, ok := mec.mutation.CreatedAt(); !ok { + return &ValidationError{Name: "created_at", err: errors.New(`ent: missing required field "MaintenanceEntry.created_at"`)} + } + if _, ok := mec.mutation.UpdatedAt(); !ok { + return &ValidationError{Name: "updated_at", err: errors.New(`ent: missing required field "MaintenanceEntry.updated_at"`)} + } + if _, ok := mec.mutation.ItemID(); !ok { + return &ValidationError{Name: "item_id", err: errors.New(`ent: missing required field "MaintenanceEntry.item_id"`)} + } + if _, ok := mec.mutation.Date(); !ok { + return &ValidationError{Name: "date", err: errors.New(`ent: missing required field "MaintenanceEntry.date"`)} + } + if _, ok := mec.mutation.Name(); !ok { + return &ValidationError{Name: "name", err: errors.New(`ent: missing required field "MaintenanceEntry.name"`)} + } + if v, ok := mec.mutation.Name(); ok { + if err := maintenanceentry.NameValidator(v); err != nil { + return &ValidationError{Name: "name", err: fmt.Errorf(`ent: validator failed for field "MaintenanceEntry.name": %w`, err)} + } + } + if v, ok := mec.mutation.Description(); ok { + if err := maintenanceentry.DescriptionValidator(v); err != nil { + return &ValidationError{Name: "description", err: fmt.Errorf(`ent: validator failed for field "MaintenanceEntry.description": %w`, err)} + } + } + if _, ok := mec.mutation.Cost(); !ok { + return &ValidationError{Name: "cost", err: errors.New(`ent: missing required field "MaintenanceEntry.cost"`)} + } + if _, ok := mec.mutation.ItemID(); !ok { + return &ValidationError{Name: "item", err: errors.New(`ent: missing required edge "MaintenanceEntry.item"`)} + } + return nil +} + +func (mec *MaintenanceEntryCreate) sqlSave(ctx context.Context) (*MaintenanceEntry, error) { + _node, _spec := mec.createSpec() + if err := sqlgraph.CreateNode(ctx, mec.driver, _spec); err != nil { + if sqlgraph.IsConstraintError(err) { + err = &ConstraintError{msg: err.Error(), wrap: err} + } + return nil, err + } + if _spec.ID.Value != nil { + if id, ok := _spec.ID.Value.(*uuid.UUID); ok { + _node.ID = *id + } else if err := _node.ID.Scan(_spec.ID.Value); err != nil { + return nil, err + } + } + return _node, nil +} + +func (mec *MaintenanceEntryCreate) createSpec() (*MaintenanceEntry, *sqlgraph.CreateSpec) { + var ( + _node = &MaintenanceEntry{config: mec.config} + _spec = &sqlgraph.CreateSpec{ + Table: maintenanceentry.Table, + ID: &sqlgraph.FieldSpec{ + Type: field.TypeUUID, + Column: maintenanceentry.FieldID, + }, + } + ) + if id, ok := mec.mutation.ID(); ok { + _node.ID = id + _spec.ID.Value = &id + } + if value, ok := mec.mutation.CreatedAt(); ok { + _spec.SetField(maintenanceentry.FieldCreatedAt, field.TypeTime, value) + _node.CreatedAt = value + } + if value, ok := mec.mutation.UpdatedAt(); ok { + _spec.SetField(maintenanceentry.FieldUpdatedAt, field.TypeTime, value) + _node.UpdatedAt = value + } + if value, ok := mec.mutation.Date(); ok { + _spec.SetField(maintenanceentry.FieldDate, field.TypeTime, value) + _node.Date = value + } + if value, ok := mec.mutation.Name(); ok { + _spec.SetField(maintenanceentry.FieldName, field.TypeString, value) + _node.Name = value + } + if value, ok := mec.mutation.Description(); ok { + _spec.SetField(maintenanceentry.FieldDescription, field.TypeString, value) + _node.Description = value + } + if value, ok := mec.mutation.Cost(); ok { + _spec.SetField(maintenanceentry.FieldCost, field.TypeFloat64, value) + _node.Cost = value + } + if nodes := mec.mutation.ItemIDs(); len(nodes) > 0 { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.M2O, + Inverse: true, + Table: maintenanceentry.ItemTable, + Columns: []string{maintenanceentry.ItemColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: &sqlgraph.FieldSpec{ + Type: field.TypeUUID, + Column: item.FieldID, + }, + }, + } + for _, k := range nodes { + edge.Target.Nodes = append(edge.Target.Nodes, k) + } + _node.ItemID = nodes[0] + _spec.Edges = append(_spec.Edges, edge) + } + return _node, _spec +} + +// MaintenanceEntryCreateBulk is the builder for creating many MaintenanceEntry entities in bulk. +type MaintenanceEntryCreateBulk struct { + config + builders []*MaintenanceEntryCreate +} + +// Save creates the MaintenanceEntry entities in the database. +func (mecb *MaintenanceEntryCreateBulk) Save(ctx context.Context) ([]*MaintenanceEntry, error) { + specs := make([]*sqlgraph.CreateSpec, len(mecb.builders)) + nodes := make([]*MaintenanceEntry, len(mecb.builders)) + mutators := make([]Mutator, len(mecb.builders)) + for i := range mecb.builders { + func(i int, root context.Context) { + builder := mecb.builders[i] + builder.defaults() + var mut Mutator = MutateFunc(func(ctx context.Context, m Mutation) (Value, error) { + mutation, ok := m.(*MaintenanceEntryMutation) + if !ok { + return nil, fmt.Errorf("unexpected mutation type %T", m) + } + if err := builder.check(); err != nil { + return nil, err + } + builder.mutation = mutation + nodes[i], specs[i] = builder.createSpec() + var err error + if i < len(mutators)-1 { + _, err = mutators[i+1].Mutate(root, mecb.builders[i+1].mutation) + } else { + spec := &sqlgraph.BatchCreateSpec{Nodes: specs} + // Invoke the actual operation on the latest mutation in the chain. + if err = sqlgraph.BatchCreate(ctx, mecb.driver, spec); err != nil { + if sqlgraph.IsConstraintError(err) { + err = &ConstraintError{msg: err.Error(), wrap: err} + } + } + } + if err != nil { + return nil, err + } + mutation.id = &nodes[i].ID + mutation.done = true + return nodes[i], nil + }) + for i := len(builder.hooks) - 1; i >= 0; i-- { + mut = builder.hooks[i](mut) + } + mutators[i] = mut + }(i, ctx) + } + if len(mutators) > 0 { + if _, err := mutators[0].Mutate(ctx, mecb.builders[0].mutation); err != nil { + return nil, err + } + } + return nodes, nil +} + +// SaveX is like Save, but panics if an error occurs. +func (mecb *MaintenanceEntryCreateBulk) SaveX(ctx context.Context) []*MaintenanceEntry { + v, err := mecb.Save(ctx) + if err != nil { + panic(err) + } + return v +} + +// Exec executes the query. +func (mecb *MaintenanceEntryCreateBulk) Exec(ctx context.Context) error { + _, err := mecb.Save(ctx) + return err +} + +// ExecX is like Exec, but panics if an error occurs. +func (mecb *MaintenanceEntryCreateBulk) ExecX(ctx context.Context) { + if err := mecb.Exec(ctx); err != nil { + panic(err) + } +} diff --git a/backend/internal/data/ent/maintenanceentry_delete.go b/backend/internal/data/ent/maintenanceentry_delete.go new file mode 100644 index 0000000..ea0ed2a --- /dev/null +++ b/backend/internal/data/ent/maintenanceentry_delete.go @@ -0,0 +1,115 @@ +// Code generated by ent, DO NOT EDIT. + +package ent + +import ( + "context" + "fmt" + + "entgo.io/ent/dialect/sql" + "entgo.io/ent/dialect/sql/sqlgraph" + "entgo.io/ent/schema/field" + "github.com/hay-kot/homebox/backend/internal/data/ent/maintenanceentry" + "github.com/hay-kot/homebox/backend/internal/data/ent/predicate" +) + +// MaintenanceEntryDelete is the builder for deleting a MaintenanceEntry entity. +type MaintenanceEntryDelete struct { + config + hooks []Hook + mutation *MaintenanceEntryMutation +} + +// Where appends a list predicates to the MaintenanceEntryDelete builder. +func (med *MaintenanceEntryDelete) Where(ps ...predicate.MaintenanceEntry) *MaintenanceEntryDelete { + med.mutation.Where(ps...) + return med +} + +// Exec executes the deletion query and returns how many vertices were deleted. +func (med *MaintenanceEntryDelete) Exec(ctx context.Context) (int, error) { + var ( + err error + affected int + ) + if len(med.hooks) == 0 { + affected, err = med.sqlExec(ctx) + } else { + var mut Mutator = MutateFunc(func(ctx context.Context, m Mutation) (Value, error) { + mutation, ok := m.(*MaintenanceEntryMutation) + if !ok { + return nil, fmt.Errorf("unexpected mutation type %T", m) + } + med.mutation = mutation + affected, err = med.sqlExec(ctx) + mutation.done = true + return affected, err + }) + for i := len(med.hooks) - 1; i >= 0; i-- { + if med.hooks[i] == nil { + return 0, fmt.Errorf("ent: uninitialized hook (forgotten import ent/runtime?)") + } + mut = med.hooks[i](mut) + } + if _, err := mut.Mutate(ctx, med.mutation); err != nil { + return 0, err + } + } + return affected, err +} + +// ExecX is like Exec, but panics if an error occurs. +func (med *MaintenanceEntryDelete) ExecX(ctx context.Context) int { + n, err := med.Exec(ctx) + if err != nil { + panic(err) + } + return n +} + +func (med *MaintenanceEntryDelete) sqlExec(ctx context.Context) (int, error) { + _spec := &sqlgraph.DeleteSpec{ + Node: &sqlgraph.NodeSpec{ + Table: maintenanceentry.Table, + ID: &sqlgraph.FieldSpec{ + Type: field.TypeUUID, + Column: maintenanceentry.FieldID, + }, + }, + } + if ps := med.mutation.predicates; len(ps) > 0 { + _spec.Predicate = func(selector *sql.Selector) { + for i := range ps { + ps[i](selector) + } + } + } + affected, err := sqlgraph.DeleteNodes(ctx, med.driver, _spec) + if err != nil && sqlgraph.IsConstraintError(err) { + err = &ConstraintError{msg: err.Error(), wrap: err} + } + return affected, err +} + +// MaintenanceEntryDeleteOne is the builder for deleting a single MaintenanceEntry entity. +type MaintenanceEntryDeleteOne struct { + med *MaintenanceEntryDelete +} + +// Exec executes the deletion query. +func (medo *MaintenanceEntryDeleteOne) Exec(ctx context.Context) error { + n, err := medo.med.Exec(ctx) + switch { + case err != nil: + return err + case n == 0: + return &NotFoundError{maintenanceentry.Label} + default: + return nil + } +} + +// ExecX is like Exec, but panics if an error occurs. +func (medo *MaintenanceEntryDeleteOne) ExecX(ctx context.Context) { + medo.med.ExecX(ctx) +} diff --git a/backend/internal/data/ent/maintenanceentry_query.go b/backend/internal/data/ent/maintenanceentry_query.go new file mode 100644 index 0000000..bcc95b5 --- /dev/null +++ b/backend/internal/data/ent/maintenanceentry_query.go @@ -0,0 +1,622 @@ +// Code generated by ent, DO NOT EDIT. + +package ent + +import ( + "context" + "fmt" + "math" + + "entgo.io/ent/dialect/sql" + "entgo.io/ent/dialect/sql/sqlgraph" + "entgo.io/ent/schema/field" + "github.com/google/uuid" + "github.com/hay-kot/homebox/backend/internal/data/ent/item" + "github.com/hay-kot/homebox/backend/internal/data/ent/maintenanceentry" + "github.com/hay-kot/homebox/backend/internal/data/ent/predicate" +) + +// MaintenanceEntryQuery is the builder for querying MaintenanceEntry entities. +type MaintenanceEntryQuery struct { + config + limit *int + offset *int + unique *bool + order []OrderFunc + fields []string + predicates []predicate.MaintenanceEntry + withItem *ItemQuery + // intermediate query (i.e. traversal path). + sql *sql.Selector + path func(context.Context) (*sql.Selector, error) +} + +// Where adds a new predicate for the MaintenanceEntryQuery builder. +func (meq *MaintenanceEntryQuery) Where(ps ...predicate.MaintenanceEntry) *MaintenanceEntryQuery { + meq.predicates = append(meq.predicates, ps...) + return meq +} + +// Limit adds a limit step to the query. +func (meq *MaintenanceEntryQuery) Limit(limit int) *MaintenanceEntryQuery { + meq.limit = &limit + return meq +} + +// Offset adds an offset step to the query. +func (meq *MaintenanceEntryQuery) Offset(offset int) *MaintenanceEntryQuery { + meq.offset = &offset + return meq +} + +// Unique configures the query builder to filter duplicate records on query. +// By default, unique is set to true, and can be disabled using this method. +func (meq *MaintenanceEntryQuery) Unique(unique bool) *MaintenanceEntryQuery { + meq.unique = &unique + return meq +} + +// Order adds an order step to the query. +func (meq *MaintenanceEntryQuery) Order(o ...OrderFunc) *MaintenanceEntryQuery { + meq.order = append(meq.order, o...) + return meq +} + +// QueryItem chains the current query on the "item" edge. +func (meq *MaintenanceEntryQuery) QueryItem() *ItemQuery { + query := &ItemQuery{config: meq.config} + query.path = func(ctx context.Context) (fromU *sql.Selector, err error) { + if err := meq.prepareQuery(ctx); err != nil { + return nil, err + } + selector := meq.sqlQuery(ctx) + if err := selector.Err(); err != nil { + return nil, err + } + step := sqlgraph.NewStep( + sqlgraph.From(maintenanceentry.Table, maintenanceentry.FieldID, selector), + sqlgraph.To(item.Table, item.FieldID), + sqlgraph.Edge(sqlgraph.M2O, true, maintenanceentry.ItemTable, maintenanceentry.ItemColumn), + ) + fromU = sqlgraph.SetNeighbors(meq.driver.Dialect(), step) + return fromU, nil + } + return query +} + +// First returns the first MaintenanceEntry entity from the query. +// Returns a *NotFoundError when no MaintenanceEntry was found. +func (meq *MaintenanceEntryQuery) First(ctx context.Context) (*MaintenanceEntry, error) { + nodes, err := meq.Limit(1).All(ctx) + if err != nil { + return nil, err + } + if len(nodes) == 0 { + return nil, &NotFoundError{maintenanceentry.Label} + } + return nodes[0], nil +} + +// FirstX is like First, but panics if an error occurs. +func (meq *MaintenanceEntryQuery) FirstX(ctx context.Context) *MaintenanceEntry { + node, err := meq.First(ctx) + if err != nil && !IsNotFound(err) { + panic(err) + } + return node +} + +// FirstID returns the first MaintenanceEntry ID from the query. +// Returns a *NotFoundError when no MaintenanceEntry ID was found. +func (meq *MaintenanceEntryQuery) FirstID(ctx context.Context) (id uuid.UUID, err error) { + var ids []uuid.UUID + if ids, err = meq.Limit(1).IDs(ctx); err != nil { + return + } + if len(ids) == 0 { + err = &NotFoundError{maintenanceentry.Label} + return + } + return ids[0], nil +} + +// FirstIDX is like FirstID, but panics if an error occurs. +func (meq *MaintenanceEntryQuery) FirstIDX(ctx context.Context) uuid.UUID { + id, err := meq.FirstID(ctx) + if err != nil && !IsNotFound(err) { + panic(err) + } + return id +} + +// Only returns a single MaintenanceEntry entity found by the query, ensuring it only returns one. +// Returns a *NotSingularError when more than one MaintenanceEntry entity is found. +// Returns a *NotFoundError when no MaintenanceEntry entities are found. +func (meq *MaintenanceEntryQuery) Only(ctx context.Context) (*MaintenanceEntry, error) { + nodes, err := meq.Limit(2).All(ctx) + if err != nil { + return nil, err + } + switch len(nodes) { + case 1: + return nodes[0], nil + case 0: + return nil, &NotFoundError{maintenanceentry.Label} + default: + return nil, &NotSingularError{maintenanceentry.Label} + } +} + +// OnlyX is like Only, but panics if an error occurs. +func (meq *MaintenanceEntryQuery) OnlyX(ctx context.Context) *MaintenanceEntry { + node, err := meq.Only(ctx) + if err != nil { + panic(err) + } + return node +} + +// OnlyID is like Only, but returns the only MaintenanceEntry ID in the query. +// Returns a *NotSingularError when more than one MaintenanceEntry ID is found. +// Returns a *NotFoundError when no entities are found. +func (meq *MaintenanceEntryQuery) OnlyID(ctx context.Context) (id uuid.UUID, err error) { + var ids []uuid.UUID + if ids, err = meq.Limit(2).IDs(ctx); err != nil { + return + } + switch len(ids) { + case 1: + id = ids[0] + case 0: + err = &NotFoundError{maintenanceentry.Label} + default: + err = &NotSingularError{maintenanceentry.Label} + } + return +} + +// OnlyIDX is like OnlyID, but panics if an error occurs. +func (meq *MaintenanceEntryQuery) OnlyIDX(ctx context.Context) uuid.UUID { + id, err := meq.OnlyID(ctx) + if err != nil { + panic(err) + } + return id +} + +// All executes the query and returns a list of MaintenanceEntries. +func (meq *MaintenanceEntryQuery) All(ctx context.Context) ([]*MaintenanceEntry, error) { + if err := meq.prepareQuery(ctx); err != nil { + return nil, err + } + return meq.sqlAll(ctx) +} + +// AllX is like All, but panics if an error occurs. +func (meq *MaintenanceEntryQuery) AllX(ctx context.Context) []*MaintenanceEntry { + nodes, err := meq.All(ctx) + if err != nil { + panic(err) + } + return nodes +} + +// IDs executes the query and returns a list of MaintenanceEntry IDs. +func (meq *MaintenanceEntryQuery) IDs(ctx context.Context) ([]uuid.UUID, error) { + var ids []uuid.UUID + if err := meq.Select(maintenanceentry.FieldID).Scan(ctx, &ids); err != nil { + return nil, err + } + return ids, nil +} + +// IDsX is like IDs, but panics if an error occurs. +func (meq *MaintenanceEntryQuery) IDsX(ctx context.Context) []uuid.UUID { + ids, err := meq.IDs(ctx) + if err != nil { + panic(err) + } + return ids +} + +// Count returns the count of the given query. +func (meq *MaintenanceEntryQuery) Count(ctx context.Context) (int, error) { + if err := meq.prepareQuery(ctx); err != nil { + return 0, err + } + return meq.sqlCount(ctx) +} + +// CountX is like Count, but panics if an error occurs. +func (meq *MaintenanceEntryQuery) CountX(ctx context.Context) int { + count, err := meq.Count(ctx) + if err != nil { + panic(err) + } + return count +} + +// Exist returns true if the query has elements in the graph. +func (meq *MaintenanceEntryQuery) Exist(ctx context.Context) (bool, error) { + if err := meq.prepareQuery(ctx); err != nil { + return false, err + } + return meq.sqlExist(ctx) +} + +// ExistX is like Exist, but panics if an error occurs. +func (meq *MaintenanceEntryQuery) ExistX(ctx context.Context) bool { + exist, err := meq.Exist(ctx) + if err != nil { + panic(err) + } + return exist +} + +// Clone returns a duplicate of the MaintenanceEntryQuery builder, including all associated steps. It can be +// used to prepare common query builders and use them differently after the clone is made. +func (meq *MaintenanceEntryQuery) Clone() *MaintenanceEntryQuery { + if meq == nil { + return nil + } + return &MaintenanceEntryQuery{ + config: meq.config, + limit: meq.limit, + offset: meq.offset, + order: append([]OrderFunc{}, meq.order...), + predicates: append([]predicate.MaintenanceEntry{}, meq.predicates...), + withItem: meq.withItem.Clone(), + // clone intermediate query. + sql: meq.sql.Clone(), + path: meq.path, + unique: meq.unique, + } +} + +// WithItem tells the query-builder to eager-load the nodes that are connected to +// the "item" edge. The optional arguments are used to configure the query builder of the edge. +func (meq *MaintenanceEntryQuery) WithItem(opts ...func(*ItemQuery)) *MaintenanceEntryQuery { + query := &ItemQuery{config: meq.config} + for _, opt := range opts { + opt(query) + } + meq.withItem = query + return meq +} + +// GroupBy is used to group vertices by one or more fields/columns. +// It is often used with aggregate functions, like: count, max, mean, min, sum. +// +// Example: +// +// var v []struct { +// CreatedAt time.Time `json:"created_at,omitempty"` +// Count int `json:"count,omitempty"` +// } +// +// client.MaintenanceEntry.Query(). +// GroupBy(maintenanceentry.FieldCreatedAt). +// Aggregate(ent.Count()). +// Scan(ctx, &v) +func (meq *MaintenanceEntryQuery) GroupBy(field string, fields ...string) *MaintenanceEntryGroupBy { + grbuild := &MaintenanceEntryGroupBy{config: meq.config} + grbuild.fields = append([]string{field}, fields...) + grbuild.path = func(ctx context.Context) (prev *sql.Selector, err error) { + if err := meq.prepareQuery(ctx); err != nil { + return nil, err + } + return meq.sqlQuery(ctx), nil + } + grbuild.label = maintenanceentry.Label + grbuild.flds, grbuild.scan = &grbuild.fields, grbuild.Scan + return grbuild +} + +// Select allows the selection one or more fields/columns for the given query, +// instead of selecting all fields in the entity. +// +// Example: +// +// var v []struct { +// CreatedAt time.Time `json:"created_at,omitempty"` +// } +// +// client.MaintenanceEntry.Query(). +// Select(maintenanceentry.FieldCreatedAt). +// Scan(ctx, &v) +func (meq *MaintenanceEntryQuery) Select(fields ...string) *MaintenanceEntrySelect { + meq.fields = append(meq.fields, fields...) + selbuild := &MaintenanceEntrySelect{MaintenanceEntryQuery: meq} + selbuild.label = maintenanceentry.Label + selbuild.flds, selbuild.scan = &meq.fields, selbuild.Scan + return selbuild +} + +// Aggregate returns a MaintenanceEntrySelect configured with the given aggregations. +func (meq *MaintenanceEntryQuery) Aggregate(fns ...AggregateFunc) *MaintenanceEntrySelect { + return meq.Select().Aggregate(fns...) +} + +func (meq *MaintenanceEntryQuery) prepareQuery(ctx context.Context) error { + for _, f := range meq.fields { + if !maintenanceentry.ValidColumn(f) { + return &ValidationError{Name: f, err: fmt.Errorf("ent: invalid field %q for query", f)} + } + } + if meq.path != nil { + prev, err := meq.path(ctx) + if err != nil { + return err + } + meq.sql = prev + } + return nil +} + +func (meq *MaintenanceEntryQuery) sqlAll(ctx context.Context, hooks ...queryHook) ([]*MaintenanceEntry, error) { + var ( + nodes = []*MaintenanceEntry{} + _spec = meq.querySpec() + loadedTypes = [1]bool{ + meq.withItem != nil, + } + ) + _spec.ScanValues = func(columns []string) ([]any, error) { + return (*MaintenanceEntry).scanValues(nil, columns) + } + _spec.Assign = func(columns []string, values []any) error { + node := &MaintenanceEntry{config: meq.config} + nodes = append(nodes, node) + node.Edges.loadedTypes = loadedTypes + return node.assignValues(columns, values) + } + for i := range hooks { + hooks[i](ctx, _spec) + } + if err := sqlgraph.QueryNodes(ctx, meq.driver, _spec); err != nil { + return nil, err + } + if len(nodes) == 0 { + return nodes, nil + } + if query := meq.withItem; query != nil { + if err := meq.loadItem(ctx, query, nodes, nil, + func(n *MaintenanceEntry, e *Item) { n.Edges.Item = e }); err != nil { + return nil, err + } + } + return nodes, nil +} + +func (meq *MaintenanceEntryQuery) loadItem(ctx context.Context, query *ItemQuery, nodes []*MaintenanceEntry, init func(*MaintenanceEntry), assign func(*MaintenanceEntry, *Item)) error { + ids := make([]uuid.UUID, 0, len(nodes)) + nodeids := make(map[uuid.UUID][]*MaintenanceEntry) + for i := range nodes { + fk := nodes[i].ItemID + if _, ok := nodeids[fk]; !ok { + ids = append(ids, fk) + } + nodeids[fk] = append(nodeids[fk], nodes[i]) + } + query.Where(item.IDIn(ids...)) + neighbors, err := query.All(ctx) + if err != nil { + return err + } + for _, n := range neighbors { + nodes, ok := nodeids[n.ID] + if !ok { + return fmt.Errorf(`unexpected foreign-key "item_id" returned %v`, n.ID) + } + for i := range nodes { + assign(nodes[i], n) + } + } + return nil +} + +func (meq *MaintenanceEntryQuery) sqlCount(ctx context.Context) (int, error) { + _spec := meq.querySpec() + _spec.Node.Columns = meq.fields + if len(meq.fields) > 0 { + _spec.Unique = meq.unique != nil && *meq.unique + } + return sqlgraph.CountNodes(ctx, meq.driver, _spec) +} + +func (meq *MaintenanceEntryQuery) sqlExist(ctx context.Context) (bool, error) { + switch _, err := meq.FirstID(ctx); { + case IsNotFound(err): + return false, nil + case err != nil: + return false, fmt.Errorf("ent: check existence: %w", err) + default: + return true, nil + } +} + +func (meq *MaintenanceEntryQuery) querySpec() *sqlgraph.QuerySpec { + _spec := &sqlgraph.QuerySpec{ + Node: &sqlgraph.NodeSpec{ + Table: maintenanceentry.Table, + Columns: maintenanceentry.Columns, + ID: &sqlgraph.FieldSpec{ + Type: field.TypeUUID, + Column: maintenanceentry.FieldID, + }, + }, + From: meq.sql, + Unique: true, + } + if unique := meq.unique; unique != nil { + _spec.Unique = *unique + } + if fields := meq.fields; len(fields) > 0 { + _spec.Node.Columns = make([]string, 0, len(fields)) + _spec.Node.Columns = append(_spec.Node.Columns, maintenanceentry.FieldID) + for i := range fields { + if fields[i] != maintenanceentry.FieldID { + _spec.Node.Columns = append(_spec.Node.Columns, fields[i]) + } + } + } + if ps := meq.predicates; len(ps) > 0 { + _spec.Predicate = func(selector *sql.Selector) { + for i := range ps { + ps[i](selector) + } + } + } + if limit := meq.limit; limit != nil { + _spec.Limit = *limit + } + if offset := meq.offset; offset != nil { + _spec.Offset = *offset + } + if ps := meq.order; len(ps) > 0 { + _spec.Order = func(selector *sql.Selector) { + for i := range ps { + ps[i](selector) + } + } + } + return _spec +} + +func (meq *MaintenanceEntryQuery) sqlQuery(ctx context.Context) *sql.Selector { + builder := sql.Dialect(meq.driver.Dialect()) + t1 := builder.Table(maintenanceentry.Table) + columns := meq.fields + if len(columns) == 0 { + columns = maintenanceentry.Columns + } + selector := builder.Select(t1.Columns(columns...)...).From(t1) + if meq.sql != nil { + selector = meq.sql + selector.Select(selector.Columns(columns...)...) + } + if meq.unique != nil && *meq.unique { + selector.Distinct() + } + for _, p := range meq.predicates { + p(selector) + } + for _, p := range meq.order { + p(selector) + } + if offset := meq.offset; offset != nil { + // limit is mandatory for offset clause. We start + // with default value, and override it below if needed. + selector.Offset(*offset).Limit(math.MaxInt32) + } + if limit := meq.limit; limit != nil { + selector.Limit(*limit) + } + return selector +} + +// MaintenanceEntryGroupBy is the group-by builder for MaintenanceEntry entities. +type MaintenanceEntryGroupBy struct { + config + selector + fields []string + fns []AggregateFunc + // intermediate query (i.e. traversal path). + sql *sql.Selector + path func(context.Context) (*sql.Selector, error) +} + +// Aggregate adds the given aggregation functions to the group-by query. +func (megb *MaintenanceEntryGroupBy) Aggregate(fns ...AggregateFunc) *MaintenanceEntryGroupBy { + megb.fns = append(megb.fns, fns...) + return megb +} + +// Scan applies the group-by query and scans the result into the given value. +func (megb *MaintenanceEntryGroupBy) Scan(ctx context.Context, v any) error { + query, err := megb.path(ctx) + if err != nil { + return err + } + megb.sql = query + return megb.sqlScan(ctx, v) +} + +func (megb *MaintenanceEntryGroupBy) sqlScan(ctx context.Context, v any) error { + for _, f := range megb.fields { + if !maintenanceentry.ValidColumn(f) { + return &ValidationError{Name: f, err: fmt.Errorf("invalid field %q for group-by", f)} + } + } + selector := megb.sqlQuery() + if err := selector.Err(); err != nil { + return err + } + rows := &sql.Rows{} + query, args := selector.Query() + if err := megb.driver.Query(ctx, query, args, rows); err != nil { + return err + } + defer rows.Close() + return sql.ScanSlice(rows, v) +} + +func (megb *MaintenanceEntryGroupBy) sqlQuery() *sql.Selector { + selector := megb.sql.Select() + aggregation := make([]string, 0, len(megb.fns)) + for _, fn := range megb.fns { + aggregation = append(aggregation, fn(selector)) + } + if len(selector.SelectedColumns()) == 0 { + columns := make([]string, 0, len(megb.fields)+len(megb.fns)) + for _, f := range megb.fields { + columns = append(columns, selector.C(f)) + } + columns = append(columns, aggregation...) + selector.Select(columns...) + } + return selector.GroupBy(selector.Columns(megb.fields...)...) +} + +// MaintenanceEntrySelect is the builder for selecting fields of MaintenanceEntry entities. +type MaintenanceEntrySelect struct { + *MaintenanceEntryQuery + selector + // intermediate query (i.e. traversal path). + sql *sql.Selector +} + +// Aggregate adds the given aggregation functions to the selector query. +func (mes *MaintenanceEntrySelect) Aggregate(fns ...AggregateFunc) *MaintenanceEntrySelect { + mes.fns = append(mes.fns, fns...) + return mes +} + +// Scan applies the selector query and scans the result into the given value. +func (mes *MaintenanceEntrySelect) Scan(ctx context.Context, v any) error { + if err := mes.prepareQuery(ctx); err != nil { + return err + } + mes.sql = mes.MaintenanceEntryQuery.sqlQuery(ctx) + return mes.sqlScan(ctx, v) +} + +func (mes *MaintenanceEntrySelect) sqlScan(ctx context.Context, v any) error { + aggregation := make([]string, 0, len(mes.fns)) + for _, fn := range mes.fns { + aggregation = append(aggregation, fn(mes.sql)) + } + switch n := len(*mes.selector.flds); { + case n == 0 && len(aggregation) > 0: + mes.sql.Select(aggregation...) + case n != 0 && len(aggregation) > 0: + mes.sql.AppendSelect(aggregation...) + } + rows := &sql.Rows{} + query, args := mes.sql.Query() + if err := mes.driver.Query(ctx, query, args, rows); err != nil { + return err + } + defer rows.Close() + return sql.ScanSlice(rows, v) +} diff --git a/backend/internal/data/ent/maintenanceentry_update.go b/backend/internal/data/ent/maintenanceentry_update.go new file mode 100644 index 0000000..af0aafd --- /dev/null +++ b/backend/internal/data/ent/maintenanceentry_update.go @@ -0,0 +1,594 @@ +// Code generated by ent, DO NOT EDIT. + +package ent + +import ( + "context" + "errors" + "fmt" + "time" + + "entgo.io/ent/dialect/sql" + "entgo.io/ent/dialect/sql/sqlgraph" + "entgo.io/ent/schema/field" + "github.com/google/uuid" + "github.com/hay-kot/homebox/backend/internal/data/ent/item" + "github.com/hay-kot/homebox/backend/internal/data/ent/maintenanceentry" + "github.com/hay-kot/homebox/backend/internal/data/ent/predicate" +) + +// MaintenanceEntryUpdate is the builder for updating MaintenanceEntry entities. +type MaintenanceEntryUpdate struct { + config + hooks []Hook + mutation *MaintenanceEntryMutation +} + +// Where appends a list predicates to the MaintenanceEntryUpdate builder. +func (meu *MaintenanceEntryUpdate) Where(ps ...predicate.MaintenanceEntry) *MaintenanceEntryUpdate { + meu.mutation.Where(ps...) + return meu +} + +// SetUpdatedAt sets the "updated_at" field. +func (meu *MaintenanceEntryUpdate) SetUpdatedAt(t time.Time) *MaintenanceEntryUpdate { + meu.mutation.SetUpdatedAt(t) + return meu +} + +// SetItemID sets the "item_id" field. +func (meu *MaintenanceEntryUpdate) SetItemID(u uuid.UUID) *MaintenanceEntryUpdate { + meu.mutation.SetItemID(u) + return meu +} + +// SetDate sets the "date" field. +func (meu *MaintenanceEntryUpdate) SetDate(t time.Time) *MaintenanceEntryUpdate { + meu.mutation.SetDate(t) + return meu +} + +// SetNillableDate sets the "date" field if the given value is not nil. +func (meu *MaintenanceEntryUpdate) SetNillableDate(t *time.Time) *MaintenanceEntryUpdate { + if t != nil { + meu.SetDate(*t) + } + return meu +} + +// SetName sets the "name" field. +func (meu *MaintenanceEntryUpdate) SetName(s string) *MaintenanceEntryUpdate { + meu.mutation.SetName(s) + return meu +} + +// SetDescription sets the "description" field. +func (meu *MaintenanceEntryUpdate) SetDescription(s string) *MaintenanceEntryUpdate { + meu.mutation.SetDescription(s) + return meu +} + +// SetNillableDescription sets the "description" field if the given value is not nil. +func (meu *MaintenanceEntryUpdate) SetNillableDescription(s *string) *MaintenanceEntryUpdate { + if s != nil { + meu.SetDescription(*s) + } + return meu +} + +// ClearDescription clears the value of the "description" field. +func (meu *MaintenanceEntryUpdate) ClearDescription() *MaintenanceEntryUpdate { + meu.mutation.ClearDescription() + return meu +} + +// SetCost sets the "cost" field. +func (meu *MaintenanceEntryUpdate) SetCost(f float64) *MaintenanceEntryUpdate { + meu.mutation.ResetCost() + meu.mutation.SetCost(f) + return meu +} + +// SetNillableCost sets the "cost" field if the given value is not nil. +func (meu *MaintenanceEntryUpdate) SetNillableCost(f *float64) *MaintenanceEntryUpdate { + if f != nil { + meu.SetCost(*f) + } + return meu +} + +// AddCost adds f to the "cost" field. +func (meu *MaintenanceEntryUpdate) AddCost(f float64) *MaintenanceEntryUpdate { + meu.mutation.AddCost(f) + return meu +} + +// SetItem sets the "item" edge to the Item entity. +func (meu *MaintenanceEntryUpdate) SetItem(i *Item) *MaintenanceEntryUpdate { + return meu.SetItemID(i.ID) +} + +// Mutation returns the MaintenanceEntryMutation object of the builder. +func (meu *MaintenanceEntryUpdate) Mutation() *MaintenanceEntryMutation { + return meu.mutation +} + +// ClearItem clears the "item" edge to the Item entity. +func (meu *MaintenanceEntryUpdate) ClearItem() *MaintenanceEntryUpdate { + meu.mutation.ClearItem() + return meu +} + +// Save executes the query and returns the number of nodes affected by the update operation. +func (meu *MaintenanceEntryUpdate) Save(ctx context.Context) (int, error) { + var ( + err error + affected int + ) + meu.defaults() + if len(meu.hooks) == 0 { + if err = meu.check(); err != nil { + return 0, err + } + affected, err = meu.sqlSave(ctx) + } else { + var mut Mutator = MutateFunc(func(ctx context.Context, m Mutation) (Value, error) { + mutation, ok := m.(*MaintenanceEntryMutation) + if !ok { + return nil, fmt.Errorf("unexpected mutation type %T", m) + } + if err = meu.check(); err != nil { + return 0, err + } + meu.mutation = mutation + affected, err = meu.sqlSave(ctx) + mutation.done = true + return affected, err + }) + for i := len(meu.hooks) - 1; i >= 0; i-- { + if meu.hooks[i] == nil { + return 0, fmt.Errorf("ent: uninitialized hook (forgotten import ent/runtime?)") + } + mut = meu.hooks[i](mut) + } + if _, err := mut.Mutate(ctx, meu.mutation); err != nil { + return 0, err + } + } + return affected, err +} + +// SaveX is like Save, but panics if an error occurs. +func (meu *MaintenanceEntryUpdate) SaveX(ctx context.Context) int { + affected, err := meu.Save(ctx) + if err != nil { + panic(err) + } + return affected +} + +// Exec executes the query. +func (meu *MaintenanceEntryUpdate) Exec(ctx context.Context) error { + _, err := meu.Save(ctx) + return err +} + +// ExecX is like Exec, but panics if an error occurs. +func (meu *MaintenanceEntryUpdate) ExecX(ctx context.Context) { + if err := meu.Exec(ctx); err != nil { + panic(err) + } +} + +// defaults sets the default values of the builder before save. +func (meu *MaintenanceEntryUpdate) defaults() { + if _, ok := meu.mutation.UpdatedAt(); !ok { + v := maintenanceentry.UpdateDefaultUpdatedAt() + meu.mutation.SetUpdatedAt(v) + } +} + +// check runs all checks and user-defined validators on the builder. +func (meu *MaintenanceEntryUpdate) check() error { + if v, ok := meu.mutation.Name(); ok { + if err := maintenanceentry.NameValidator(v); err != nil { + return &ValidationError{Name: "name", err: fmt.Errorf(`ent: validator failed for field "MaintenanceEntry.name": %w`, err)} + } + } + if v, ok := meu.mutation.Description(); ok { + if err := maintenanceentry.DescriptionValidator(v); err != nil { + return &ValidationError{Name: "description", err: fmt.Errorf(`ent: validator failed for field "MaintenanceEntry.description": %w`, err)} + } + } + if _, ok := meu.mutation.ItemID(); meu.mutation.ItemCleared() && !ok { + return errors.New(`ent: clearing a required unique edge "MaintenanceEntry.item"`) + } + return nil +} + +func (meu *MaintenanceEntryUpdate) sqlSave(ctx context.Context) (n int, err error) { + _spec := &sqlgraph.UpdateSpec{ + Node: &sqlgraph.NodeSpec{ + Table: maintenanceentry.Table, + Columns: maintenanceentry.Columns, + ID: &sqlgraph.FieldSpec{ + Type: field.TypeUUID, + Column: maintenanceentry.FieldID, + }, + }, + } + if ps := meu.mutation.predicates; len(ps) > 0 { + _spec.Predicate = func(selector *sql.Selector) { + for i := range ps { + ps[i](selector) + } + } + } + if value, ok := meu.mutation.UpdatedAt(); ok { + _spec.SetField(maintenanceentry.FieldUpdatedAt, field.TypeTime, value) + } + if value, ok := meu.mutation.Date(); ok { + _spec.SetField(maintenanceentry.FieldDate, field.TypeTime, value) + } + if value, ok := meu.mutation.Name(); ok { + _spec.SetField(maintenanceentry.FieldName, field.TypeString, value) + } + if value, ok := meu.mutation.Description(); ok { + _spec.SetField(maintenanceentry.FieldDescription, field.TypeString, value) + } + if meu.mutation.DescriptionCleared() { + _spec.ClearField(maintenanceentry.FieldDescription, field.TypeString) + } + if value, ok := meu.mutation.Cost(); ok { + _spec.SetField(maintenanceentry.FieldCost, field.TypeFloat64, value) + } + if value, ok := meu.mutation.AddedCost(); ok { + _spec.AddField(maintenanceentry.FieldCost, field.TypeFloat64, value) + } + if meu.mutation.ItemCleared() { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.M2O, + Inverse: true, + Table: maintenanceentry.ItemTable, + Columns: []string{maintenanceentry.ItemColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: &sqlgraph.FieldSpec{ + Type: field.TypeUUID, + Column: item.FieldID, + }, + }, + } + _spec.Edges.Clear = append(_spec.Edges.Clear, edge) + } + if nodes := meu.mutation.ItemIDs(); len(nodes) > 0 { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.M2O, + Inverse: true, + Table: maintenanceentry.ItemTable, + Columns: []string{maintenanceentry.ItemColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: &sqlgraph.FieldSpec{ + Type: field.TypeUUID, + Column: item.FieldID, + }, + }, + } + for _, k := range nodes { + edge.Target.Nodes = append(edge.Target.Nodes, k) + } + _spec.Edges.Add = append(_spec.Edges.Add, edge) + } + if n, err = sqlgraph.UpdateNodes(ctx, meu.driver, _spec); err != nil { + if _, ok := err.(*sqlgraph.NotFoundError); ok { + err = &NotFoundError{maintenanceentry.Label} + } else if sqlgraph.IsConstraintError(err) { + err = &ConstraintError{msg: err.Error(), wrap: err} + } + return 0, err + } + return n, nil +} + +// MaintenanceEntryUpdateOne is the builder for updating a single MaintenanceEntry entity. +type MaintenanceEntryUpdateOne struct { + config + fields []string + hooks []Hook + mutation *MaintenanceEntryMutation +} + +// SetUpdatedAt sets the "updated_at" field. +func (meuo *MaintenanceEntryUpdateOne) SetUpdatedAt(t time.Time) *MaintenanceEntryUpdateOne { + meuo.mutation.SetUpdatedAt(t) + return meuo +} + +// SetItemID sets the "item_id" field. +func (meuo *MaintenanceEntryUpdateOne) SetItemID(u uuid.UUID) *MaintenanceEntryUpdateOne { + meuo.mutation.SetItemID(u) + return meuo +} + +// SetDate sets the "date" field. +func (meuo *MaintenanceEntryUpdateOne) SetDate(t time.Time) *MaintenanceEntryUpdateOne { + meuo.mutation.SetDate(t) + return meuo +} + +// SetNillableDate sets the "date" field if the given value is not nil. +func (meuo *MaintenanceEntryUpdateOne) SetNillableDate(t *time.Time) *MaintenanceEntryUpdateOne { + if t != nil { + meuo.SetDate(*t) + } + return meuo +} + +// SetName sets the "name" field. +func (meuo *MaintenanceEntryUpdateOne) SetName(s string) *MaintenanceEntryUpdateOne { + meuo.mutation.SetName(s) + return meuo +} + +// SetDescription sets the "description" field. +func (meuo *MaintenanceEntryUpdateOne) SetDescription(s string) *MaintenanceEntryUpdateOne { + meuo.mutation.SetDescription(s) + return meuo +} + +// SetNillableDescription sets the "description" field if the given value is not nil. +func (meuo *MaintenanceEntryUpdateOne) SetNillableDescription(s *string) *MaintenanceEntryUpdateOne { + if s != nil { + meuo.SetDescription(*s) + } + return meuo +} + +// ClearDescription clears the value of the "description" field. +func (meuo *MaintenanceEntryUpdateOne) ClearDescription() *MaintenanceEntryUpdateOne { + meuo.mutation.ClearDescription() + return meuo +} + +// SetCost sets the "cost" field. +func (meuo *MaintenanceEntryUpdateOne) SetCost(f float64) *MaintenanceEntryUpdateOne { + meuo.mutation.ResetCost() + meuo.mutation.SetCost(f) + return meuo +} + +// SetNillableCost sets the "cost" field if the given value is not nil. +func (meuo *MaintenanceEntryUpdateOne) SetNillableCost(f *float64) *MaintenanceEntryUpdateOne { + if f != nil { + meuo.SetCost(*f) + } + return meuo +} + +// AddCost adds f to the "cost" field. +func (meuo *MaintenanceEntryUpdateOne) AddCost(f float64) *MaintenanceEntryUpdateOne { + meuo.mutation.AddCost(f) + return meuo +} + +// SetItem sets the "item" edge to the Item entity. +func (meuo *MaintenanceEntryUpdateOne) SetItem(i *Item) *MaintenanceEntryUpdateOne { + return meuo.SetItemID(i.ID) +} + +// Mutation returns the MaintenanceEntryMutation object of the builder. +func (meuo *MaintenanceEntryUpdateOne) Mutation() *MaintenanceEntryMutation { + return meuo.mutation +} + +// ClearItem clears the "item" edge to the Item entity. +func (meuo *MaintenanceEntryUpdateOne) ClearItem() *MaintenanceEntryUpdateOne { + meuo.mutation.ClearItem() + return meuo +} + +// Select allows selecting one or more fields (columns) of the returned entity. +// The default is selecting all fields defined in the entity schema. +func (meuo *MaintenanceEntryUpdateOne) Select(field string, fields ...string) *MaintenanceEntryUpdateOne { + meuo.fields = append([]string{field}, fields...) + return meuo +} + +// Save executes the query and returns the updated MaintenanceEntry entity. +func (meuo *MaintenanceEntryUpdateOne) Save(ctx context.Context) (*MaintenanceEntry, error) { + var ( + err error + node *MaintenanceEntry + ) + meuo.defaults() + if len(meuo.hooks) == 0 { + if err = meuo.check(); err != nil { + return nil, err + } + node, err = meuo.sqlSave(ctx) + } else { + var mut Mutator = MutateFunc(func(ctx context.Context, m Mutation) (Value, error) { + mutation, ok := m.(*MaintenanceEntryMutation) + if !ok { + return nil, fmt.Errorf("unexpected mutation type %T", m) + } + if err = meuo.check(); err != nil { + return nil, err + } + meuo.mutation = mutation + node, err = meuo.sqlSave(ctx) + mutation.done = true + return node, err + }) + for i := len(meuo.hooks) - 1; i >= 0; i-- { + if meuo.hooks[i] == nil { + return nil, fmt.Errorf("ent: uninitialized hook (forgotten import ent/runtime?)") + } + mut = meuo.hooks[i](mut) + } + v, err := mut.Mutate(ctx, meuo.mutation) + if err != nil { + return nil, err + } + nv, ok := v.(*MaintenanceEntry) + if !ok { + return nil, fmt.Errorf("unexpected node type %T returned from MaintenanceEntryMutation", v) + } + node = nv + } + return node, err +} + +// SaveX is like Save, but panics if an error occurs. +func (meuo *MaintenanceEntryUpdateOne) SaveX(ctx context.Context) *MaintenanceEntry { + node, err := meuo.Save(ctx) + if err != nil { + panic(err) + } + return node +} + +// Exec executes the query on the entity. +func (meuo *MaintenanceEntryUpdateOne) Exec(ctx context.Context) error { + _, err := meuo.Save(ctx) + return err +} + +// ExecX is like Exec, but panics if an error occurs. +func (meuo *MaintenanceEntryUpdateOne) ExecX(ctx context.Context) { + if err := meuo.Exec(ctx); err != nil { + panic(err) + } +} + +// defaults sets the default values of the builder before save. +func (meuo *MaintenanceEntryUpdateOne) defaults() { + if _, ok := meuo.mutation.UpdatedAt(); !ok { + v := maintenanceentry.UpdateDefaultUpdatedAt() + meuo.mutation.SetUpdatedAt(v) + } +} + +// check runs all checks and user-defined validators on the builder. +func (meuo *MaintenanceEntryUpdateOne) check() error { + if v, ok := meuo.mutation.Name(); ok { + if err := maintenanceentry.NameValidator(v); err != nil { + return &ValidationError{Name: "name", err: fmt.Errorf(`ent: validator failed for field "MaintenanceEntry.name": %w`, err)} + } + } + if v, ok := meuo.mutation.Description(); ok { + if err := maintenanceentry.DescriptionValidator(v); err != nil { + return &ValidationError{Name: "description", err: fmt.Errorf(`ent: validator failed for field "MaintenanceEntry.description": %w`, err)} + } + } + if _, ok := meuo.mutation.ItemID(); meuo.mutation.ItemCleared() && !ok { + return errors.New(`ent: clearing a required unique edge "MaintenanceEntry.item"`) + } + return nil +} + +func (meuo *MaintenanceEntryUpdateOne) sqlSave(ctx context.Context) (_node *MaintenanceEntry, err error) { + _spec := &sqlgraph.UpdateSpec{ + Node: &sqlgraph.NodeSpec{ + Table: maintenanceentry.Table, + Columns: maintenanceentry.Columns, + ID: &sqlgraph.FieldSpec{ + Type: field.TypeUUID, + Column: maintenanceentry.FieldID, + }, + }, + } + id, ok := meuo.mutation.ID() + if !ok { + return nil, &ValidationError{Name: "id", err: errors.New(`ent: missing "MaintenanceEntry.id" for update`)} + } + _spec.Node.ID.Value = id + if fields := meuo.fields; len(fields) > 0 { + _spec.Node.Columns = make([]string, 0, len(fields)) + _spec.Node.Columns = append(_spec.Node.Columns, maintenanceentry.FieldID) + for _, f := range fields { + if !maintenanceentry.ValidColumn(f) { + return nil, &ValidationError{Name: f, err: fmt.Errorf("ent: invalid field %q for query", f)} + } + if f != maintenanceentry.FieldID { + _spec.Node.Columns = append(_spec.Node.Columns, f) + } + } + } + if ps := meuo.mutation.predicates; len(ps) > 0 { + _spec.Predicate = func(selector *sql.Selector) { + for i := range ps { + ps[i](selector) + } + } + } + if value, ok := meuo.mutation.UpdatedAt(); ok { + _spec.SetField(maintenanceentry.FieldUpdatedAt, field.TypeTime, value) + } + if value, ok := meuo.mutation.Date(); ok { + _spec.SetField(maintenanceentry.FieldDate, field.TypeTime, value) + } + if value, ok := meuo.mutation.Name(); ok { + _spec.SetField(maintenanceentry.FieldName, field.TypeString, value) + } + if value, ok := meuo.mutation.Description(); ok { + _spec.SetField(maintenanceentry.FieldDescription, field.TypeString, value) + } + if meuo.mutation.DescriptionCleared() { + _spec.ClearField(maintenanceentry.FieldDescription, field.TypeString) + } + if value, ok := meuo.mutation.Cost(); ok { + _spec.SetField(maintenanceentry.FieldCost, field.TypeFloat64, value) + } + if value, ok := meuo.mutation.AddedCost(); ok { + _spec.AddField(maintenanceentry.FieldCost, field.TypeFloat64, value) + } + if meuo.mutation.ItemCleared() { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.M2O, + Inverse: true, + Table: maintenanceentry.ItemTable, + Columns: []string{maintenanceentry.ItemColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: &sqlgraph.FieldSpec{ + Type: field.TypeUUID, + Column: item.FieldID, + }, + }, + } + _spec.Edges.Clear = append(_spec.Edges.Clear, edge) + } + if nodes := meuo.mutation.ItemIDs(); len(nodes) > 0 { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.M2O, + Inverse: true, + Table: maintenanceentry.ItemTable, + Columns: []string{maintenanceentry.ItemColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: &sqlgraph.FieldSpec{ + Type: field.TypeUUID, + Column: item.FieldID, + }, + }, + } + for _, k := range nodes { + edge.Target.Nodes = append(edge.Target.Nodes, k) + } + _spec.Edges.Add = append(_spec.Edges.Add, edge) + } + _node = &MaintenanceEntry{config: meuo.config} + _spec.Assign = _node.assignValues + _spec.ScanValues = _node.scanValues + if err = sqlgraph.UpdateNode(ctx, meuo.driver, _spec); err != nil { + if _, ok := err.(*sqlgraph.NotFoundError); ok { + err = &NotFoundError{maintenanceentry.Label} + } else if sqlgraph.IsConstraintError(err) { + err = &ConstraintError{msg: err.Error(), wrap: err} + } + return nil, err + } + return _node, nil +} diff --git a/backend/internal/data/ent/migrate/schema.go b/backend/internal/data/ent/migrate/schema.go index 10beaa4..c700d34 100644 --- a/backend/internal/data/ent/migrate/schema.go +++ b/backend/internal/data/ent/migrate/schema.go @@ -53,7 +53,7 @@ var ( Symbol: "auth_roles_auth_tokens_roles", Columns: []*schema.Column{AuthRolesColumns[2]}, RefColumns: []*schema.Column{AuthTokensColumns[0]}, - OnDelete: schema.SetNull, + OnDelete: schema.Cascade, }, }, } @@ -110,37 +110,6 @@ var ( }, }, } - // DocumentTokensColumns holds the columns for the "document_tokens" table. - DocumentTokensColumns = []*schema.Column{ - {Name: "id", Type: field.TypeUUID}, - {Name: "created_at", Type: field.TypeTime}, - {Name: "updated_at", Type: field.TypeTime}, - {Name: "token", Type: field.TypeBytes, Unique: true}, - {Name: "uses", Type: field.TypeInt, Default: 1}, - {Name: "expires_at", Type: field.TypeTime}, - {Name: "document_document_tokens", Type: field.TypeUUID, Nullable: true}, - } - // DocumentTokensTable holds the schema information for the "document_tokens" table. - DocumentTokensTable = &schema.Table{ - Name: "document_tokens", - Columns: DocumentTokensColumns, - PrimaryKey: []*schema.Column{DocumentTokensColumns[0]}, - ForeignKeys: []*schema.ForeignKey{ - { - Symbol: "document_tokens_documents_document_tokens", - Columns: []*schema.Column{DocumentTokensColumns[6]}, - RefColumns: []*schema.Column{DocumentsColumns[0]}, - OnDelete: schema.Cascade, - }, - }, - Indexes: []*schema.Index{ - { - Name: "documenttoken_token", - Unique: false, - Columns: []*schema.Column{DocumentTokensColumns[3]}, - }, - }, - } // GroupsColumns holds the columns for the "groups" table. GroupsColumns = []*schema.Column{ {Name: "id", Type: field.TypeUUID}, @@ -349,6 +318,31 @@ var ( }, }, } + // MaintenanceEntriesColumns holds the columns for the "maintenance_entries" table. + MaintenanceEntriesColumns = []*schema.Column{ + {Name: "id", Type: field.TypeUUID}, + {Name: "created_at", Type: field.TypeTime}, + {Name: "updated_at", Type: field.TypeTime}, + {Name: "date", Type: field.TypeTime}, + {Name: "name", Type: field.TypeString, Size: 255}, + {Name: "description", Type: field.TypeString, Nullable: true, Size: 2500}, + {Name: "cost", Type: field.TypeFloat64, Default: 0}, + {Name: "item_id", Type: field.TypeUUID}, + } + // MaintenanceEntriesTable holds the schema information for the "maintenance_entries" table. + MaintenanceEntriesTable = &schema.Table{ + Name: "maintenance_entries", + Columns: MaintenanceEntriesColumns, + PrimaryKey: []*schema.Column{MaintenanceEntriesColumns[0]}, + ForeignKeys: []*schema.ForeignKey{ + { + Symbol: "maintenance_entries_items_maintenance_entries", + Columns: []*schema.Column{MaintenanceEntriesColumns[7]}, + RefColumns: []*schema.Column{ItemsColumns[0]}, + OnDelete: schema.Cascade, + }, + }, + } // UsersColumns holds the columns for the "users" table. UsersColumns = []*schema.Column{ {Name: "id", Type: field.TypeUUID}, @@ -408,13 +402,13 @@ var ( AuthRolesTable, AuthTokensTable, DocumentsTable, - DocumentTokensTable, GroupsTable, GroupInvitationTokensTable, ItemsTable, ItemFieldsTable, LabelsTable, LocationsTable, + MaintenanceEntriesTable, UsersTable, LabelItemsTable, } @@ -426,7 +420,6 @@ func init() { AuthRolesTable.ForeignKeys[0].RefTable = AuthTokensTable AuthTokensTable.ForeignKeys[0].RefTable = UsersTable DocumentsTable.ForeignKeys[0].RefTable = GroupsTable - DocumentTokensTable.ForeignKeys[0].RefTable = DocumentsTable GroupInvitationTokensTable.ForeignKeys[0].RefTable = GroupsTable ItemsTable.ForeignKeys[0].RefTable = GroupsTable ItemsTable.ForeignKeys[1].RefTable = ItemsTable @@ -435,6 +428,7 @@ func init() { LabelsTable.ForeignKeys[0].RefTable = GroupsTable LocationsTable.ForeignKeys[0].RefTable = GroupsTable LocationsTable.ForeignKeys[1].RefTable = LocationsTable + MaintenanceEntriesTable.ForeignKeys[0].RefTable = ItemsTable UsersTable.ForeignKeys[0].RefTable = GroupsTable LabelItemsTable.ForeignKeys[0].RefTable = LabelsTable LabelItemsTable.ForeignKeys[1].RefTable = ItemsTable diff --git a/backend/internal/data/ent/mutation.go b/backend/internal/data/ent/mutation.go index ea9a441..da57310 100644 --- a/backend/internal/data/ent/mutation.go +++ b/backend/internal/data/ent/mutation.go @@ -14,13 +14,13 @@ import ( "github.com/hay-kot/homebox/backend/internal/data/ent/authroles" "github.com/hay-kot/homebox/backend/internal/data/ent/authtokens" "github.com/hay-kot/homebox/backend/internal/data/ent/document" - "github.com/hay-kot/homebox/backend/internal/data/ent/documenttoken" "github.com/hay-kot/homebox/backend/internal/data/ent/group" "github.com/hay-kot/homebox/backend/internal/data/ent/groupinvitationtoken" "github.com/hay-kot/homebox/backend/internal/data/ent/item" "github.com/hay-kot/homebox/backend/internal/data/ent/itemfield" "github.com/hay-kot/homebox/backend/internal/data/ent/label" "github.com/hay-kot/homebox/backend/internal/data/ent/location" + "github.com/hay-kot/homebox/backend/internal/data/ent/maintenanceentry" "github.com/hay-kot/homebox/backend/internal/data/ent/predicate" "github.com/hay-kot/homebox/backend/internal/data/ent/user" @@ -40,13 +40,13 @@ const ( TypeAuthRoles = "AuthRoles" TypeAuthTokens = "AuthTokens" TypeDocument = "Document" - TypeDocumentToken = "DocumentToken" TypeGroup = "Group" TypeGroupInvitationToken = "GroupInvitationToken" TypeItem = "Item" TypeItemField = "ItemField" TypeLabel = "Label" TypeLocation = "Location" + TypeMaintenanceEntry = "MaintenanceEntry" TypeUser = "User" ) @@ -1587,25 +1587,22 @@ func (m *AuthTokensMutation) ResetEdge(name string) error { // DocumentMutation represents an operation that mutates the Document nodes in the graph. type DocumentMutation struct { config - op Op - typ string - id *uuid.UUID - created_at *time.Time - updated_at *time.Time - title *string - _path *string - clearedFields map[string]struct{} - group *uuid.UUID - clearedgroup bool - document_tokens map[uuid.UUID]struct{} - removeddocument_tokens map[uuid.UUID]struct{} - cleareddocument_tokens bool - attachments map[uuid.UUID]struct{} - removedattachments map[uuid.UUID]struct{} - clearedattachments bool - done bool - oldValue func(context.Context) (*Document, error) - predicates []predicate.Document + op Op + typ string + id *uuid.UUID + created_at *time.Time + updated_at *time.Time + title *string + _path *string + clearedFields map[string]struct{} + group *uuid.UUID + clearedgroup bool + attachments map[uuid.UUID]struct{} + removedattachments map[uuid.UUID]struct{} + clearedattachments bool + done bool + oldValue func(context.Context) (*Document, error) + predicates []predicate.Document } var _ ent.Mutation = (*DocumentMutation)(nil) @@ -1895,60 +1892,6 @@ func (m *DocumentMutation) ResetGroup() { m.clearedgroup = false } -// AddDocumentTokenIDs adds the "document_tokens" edge to the DocumentToken entity by ids. -func (m *DocumentMutation) AddDocumentTokenIDs(ids ...uuid.UUID) { - if m.document_tokens == nil { - m.document_tokens = make(map[uuid.UUID]struct{}) - } - for i := range ids { - m.document_tokens[ids[i]] = struct{}{} - } -} - -// ClearDocumentTokens clears the "document_tokens" edge to the DocumentToken entity. -func (m *DocumentMutation) ClearDocumentTokens() { - m.cleareddocument_tokens = true -} - -// DocumentTokensCleared reports if the "document_tokens" edge to the DocumentToken entity was cleared. -func (m *DocumentMutation) DocumentTokensCleared() bool { - return m.cleareddocument_tokens -} - -// RemoveDocumentTokenIDs removes the "document_tokens" edge to the DocumentToken entity by IDs. -func (m *DocumentMutation) RemoveDocumentTokenIDs(ids ...uuid.UUID) { - if m.removeddocument_tokens == nil { - m.removeddocument_tokens = make(map[uuid.UUID]struct{}) - } - for i := range ids { - delete(m.document_tokens, ids[i]) - m.removeddocument_tokens[ids[i]] = struct{}{} - } -} - -// RemovedDocumentTokens returns the removed IDs of the "document_tokens" edge to the DocumentToken entity. -func (m *DocumentMutation) RemovedDocumentTokensIDs() (ids []uuid.UUID) { - for id := range m.removeddocument_tokens { - ids = append(ids, id) - } - return -} - -// DocumentTokensIDs returns the "document_tokens" edge IDs in the mutation. -func (m *DocumentMutation) DocumentTokensIDs() (ids []uuid.UUID) { - for id := range m.document_tokens { - ids = append(ids, id) - } - return -} - -// ResetDocumentTokens resets all changes to the "document_tokens" edge. -func (m *DocumentMutation) ResetDocumentTokens() { - m.document_tokens = nil - m.cleareddocument_tokens = false - m.removeddocument_tokens = nil -} - // AddAttachmentIDs adds the "attachments" edge to the Attachment entity by ids. func (m *DocumentMutation) AddAttachmentIDs(ids ...uuid.UUID) { if m.attachments == nil { @@ -2172,13 +2115,10 @@ func (m *DocumentMutation) ResetField(name string) error { // AddedEdges returns all edge names that were set/added in this mutation. func (m *DocumentMutation) AddedEdges() []string { - edges := make([]string, 0, 3) + edges := make([]string, 0, 2) if m.group != nil { edges = append(edges, document.EdgeGroup) } - if m.document_tokens != nil { - edges = append(edges, document.EdgeDocumentTokens) - } if m.attachments != nil { edges = append(edges, document.EdgeAttachments) } @@ -2193,12 +2133,6 @@ func (m *DocumentMutation) AddedIDs(name string) []ent.Value { if id := m.group; id != nil { return []ent.Value{*id} } - case document.EdgeDocumentTokens: - ids := make([]ent.Value, 0, len(m.document_tokens)) - for id := range m.document_tokens { - ids = append(ids, id) - } - return ids case document.EdgeAttachments: ids := make([]ent.Value, 0, len(m.attachments)) for id := range m.attachments { @@ -2211,10 +2145,7 @@ func (m *DocumentMutation) AddedIDs(name string) []ent.Value { // RemovedEdges returns all edge names that were removed in this mutation. func (m *DocumentMutation) RemovedEdges() []string { - edges := make([]string, 0, 3) - if m.removeddocument_tokens != nil { - edges = append(edges, document.EdgeDocumentTokens) - } + edges := make([]string, 0, 2) if m.removedattachments != nil { edges = append(edges, document.EdgeAttachments) } @@ -2225,12 +2156,6 @@ func (m *DocumentMutation) RemovedEdges() []string { // the given name in this mutation. func (m *DocumentMutation) RemovedIDs(name string) []ent.Value { switch name { - case document.EdgeDocumentTokens: - ids := make([]ent.Value, 0, len(m.removeddocument_tokens)) - for id := range m.removeddocument_tokens { - ids = append(ids, id) - } - return ids case document.EdgeAttachments: ids := make([]ent.Value, 0, len(m.removedattachments)) for id := range m.removedattachments { @@ -2243,13 +2168,10 @@ func (m *DocumentMutation) RemovedIDs(name string) []ent.Value { // ClearedEdges returns all edge names that were cleared in this mutation. func (m *DocumentMutation) ClearedEdges() []string { - edges := make([]string, 0, 3) + edges := make([]string, 0, 2) if m.clearedgroup { edges = append(edges, document.EdgeGroup) } - if m.cleareddocument_tokens { - edges = append(edges, document.EdgeDocumentTokens) - } if m.clearedattachments { edges = append(edges, document.EdgeAttachments) } @@ -2262,8 +2184,6 @@ func (m *DocumentMutation) EdgeCleared(name string) bool { switch name { case document.EdgeGroup: return m.clearedgroup - case document.EdgeDocumentTokens: - return m.cleareddocument_tokens case document.EdgeAttachments: return m.clearedattachments } @@ -2288,9 +2208,6 @@ func (m *DocumentMutation) ResetEdge(name string) error { case document.EdgeGroup: m.ResetGroup() return nil - case document.EdgeDocumentTokens: - m.ResetDocumentTokens() - return nil case document.EdgeAttachments: m.ResetAttachments() return nil @@ -2298,642 +2215,6 @@ func (m *DocumentMutation) ResetEdge(name string) error { return fmt.Errorf("unknown Document edge %s", name) } -// DocumentTokenMutation represents an operation that mutates the DocumentToken nodes in the graph. -type DocumentTokenMutation struct { - config - op Op - typ string - id *uuid.UUID - created_at *time.Time - updated_at *time.Time - token *[]byte - uses *int - adduses *int - expires_at *time.Time - clearedFields map[string]struct{} - document *uuid.UUID - cleareddocument bool - done bool - oldValue func(context.Context) (*DocumentToken, error) - predicates []predicate.DocumentToken -} - -var _ ent.Mutation = (*DocumentTokenMutation)(nil) - -// documenttokenOption allows management of the mutation configuration using functional options. -type documenttokenOption func(*DocumentTokenMutation) - -// newDocumentTokenMutation creates new mutation for the DocumentToken entity. -func newDocumentTokenMutation(c config, op Op, opts ...documenttokenOption) *DocumentTokenMutation { - m := &DocumentTokenMutation{ - config: c, - op: op, - typ: TypeDocumentToken, - clearedFields: make(map[string]struct{}), - } - for _, opt := range opts { - opt(m) - } - return m -} - -// withDocumentTokenID sets the ID field of the mutation. -func withDocumentTokenID(id uuid.UUID) documenttokenOption { - return func(m *DocumentTokenMutation) { - var ( - err error - once sync.Once - value *DocumentToken - ) - m.oldValue = func(ctx context.Context) (*DocumentToken, error) { - once.Do(func() { - if m.done { - err = errors.New("querying old values post mutation is not allowed") - } else { - value, err = m.Client().DocumentToken.Get(ctx, id) - } - }) - return value, err - } - m.id = &id - } -} - -// withDocumentToken sets the old DocumentToken of the mutation. -func withDocumentToken(node *DocumentToken) documenttokenOption { - return func(m *DocumentTokenMutation) { - m.oldValue = func(context.Context) (*DocumentToken, error) { - return node, nil - } - m.id = &node.ID - } -} - -// Client returns a new `ent.Client` from the mutation. If the mutation was -// executed in a transaction (ent.Tx), a transactional client is returned. -func (m DocumentTokenMutation) Client() *Client { - client := &Client{config: m.config} - client.init() - return client -} - -// Tx returns an `ent.Tx` for mutations that were executed in transactions; -// it returns an error otherwise. -func (m DocumentTokenMutation) Tx() (*Tx, error) { - if _, ok := m.driver.(*txDriver); !ok { - return nil, errors.New("ent: mutation is not running in a transaction") - } - tx := &Tx{config: m.config} - tx.init() - return tx, nil -} - -// SetID sets the value of the id field. Note that this -// operation is only accepted on creation of DocumentToken entities. -func (m *DocumentTokenMutation) SetID(id uuid.UUID) { - m.id = &id -} - -// ID returns the ID value in the mutation. Note that the ID is only available -// if it was provided to the builder or after it was returned from the database. -func (m *DocumentTokenMutation) ID() (id uuid.UUID, exists bool) { - if m.id == nil { - return - } - return *m.id, true -} - -// IDs queries the database and returns the entity ids that match the mutation's predicate. -// That means, if the mutation is applied within a transaction with an isolation level such -// as sql.LevelSerializable, the returned ids match the ids of the rows that will be updated -// or updated by the mutation. -func (m *DocumentTokenMutation) IDs(ctx context.Context) ([]uuid.UUID, error) { - switch { - case m.op.Is(OpUpdateOne | OpDeleteOne): - id, exists := m.ID() - if exists { - return []uuid.UUID{id}, nil - } - fallthrough - case m.op.Is(OpUpdate | OpDelete): - return m.Client().DocumentToken.Query().Where(m.predicates...).IDs(ctx) - default: - return nil, fmt.Errorf("IDs is not allowed on %s operations", m.op) - } -} - -// SetCreatedAt sets the "created_at" field. -func (m *DocumentTokenMutation) SetCreatedAt(t time.Time) { - m.created_at = &t -} - -// CreatedAt returns the value of the "created_at" field in the mutation. -func (m *DocumentTokenMutation) CreatedAt() (r time.Time, exists bool) { - v := m.created_at - if v == nil { - return - } - return *v, true -} - -// OldCreatedAt returns the old "created_at" field's value of the DocumentToken entity. -// If the DocumentToken object wasn't provided to the builder, the object is fetched from the database. -// An error is returned if the mutation operation is not UpdateOne, or the database query fails. -func (m *DocumentTokenMutation) OldCreatedAt(ctx context.Context) (v time.Time, err error) { - if !m.op.Is(OpUpdateOne) { - return v, errors.New("OldCreatedAt is only allowed on UpdateOne operations") - } - if m.id == nil || m.oldValue == nil { - return v, errors.New("OldCreatedAt requires an ID field in the mutation") - } - oldValue, err := m.oldValue(ctx) - if err != nil { - return v, fmt.Errorf("querying old value for OldCreatedAt: %w", err) - } - return oldValue.CreatedAt, nil -} - -// ResetCreatedAt resets all changes to the "created_at" field. -func (m *DocumentTokenMutation) ResetCreatedAt() { - m.created_at = nil -} - -// SetUpdatedAt sets the "updated_at" field. -func (m *DocumentTokenMutation) SetUpdatedAt(t time.Time) { - m.updated_at = &t -} - -// UpdatedAt returns the value of the "updated_at" field in the mutation. -func (m *DocumentTokenMutation) UpdatedAt() (r time.Time, exists bool) { - v := m.updated_at - if v == nil { - return - } - return *v, true -} - -// OldUpdatedAt returns the old "updated_at" field's value of the DocumentToken entity. -// If the DocumentToken object wasn't provided to the builder, the object is fetched from the database. -// An error is returned if the mutation operation is not UpdateOne, or the database query fails. -func (m *DocumentTokenMutation) OldUpdatedAt(ctx context.Context) (v time.Time, err error) { - if !m.op.Is(OpUpdateOne) { - return v, errors.New("OldUpdatedAt is only allowed on UpdateOne operations") - } - if m.id == nil || m.oldValue == nil { - return v, errors.New("OldUpdatedAt requires an ID field in the mutation") - } - oldValue, err := m.oldValue(ctx) - if err != nil { - return v, fmt.Errorf("querying old value for OldUpdatedAt: %w", err) - } - return oldValue.UpdatedAt, nil -} - -// ResetUpdatedAt resets all changes to the "updated_at" field. -func (m *DocumentTokenMutation) ResetUpdatedAt() { - m.updated_at = nil -} - -// SetToken sets the "token" field. -func (m *DocumentTokenMutation) SetToken(b []byte) { - m.token = &b -} - -// Token returns the value of the "token" field in the mutation. -func (m *DocumentTokenMutation) Token() (r []byte, exists bool) { - v := m.token - if v == nil { - return - } - return *v, true -} - -// OldToken returns the old "token" field's value of the DocumentToken entity. -// If the DocumentToken object wasn't provided to the builder, the object is fetched from the database. -// An error is returned if the mutation operation is not UpdateOne, or the database query fails. -func (m *DocumentTokenMutation) OldToken(ctx context.Context) (v []byte, err error) { - if !m.op.Is(OpUpdateOne) { - return v, errors.New("OldToken is only allowed on UpdateOne operations") - } - if m.id == nil || m.oldValue == nil { - return v, errors.New("OldToken requires an ID field in the mutation") - } - oldValue, err := m.oldValue(ctx) - if err != nil { - return v, fmt.Errorf("querying old value for OldToken: %w", err) - } - return oldValue.Token, nil -} - -// ResetToken resets all changes to the "token" field. -func (m *DocumentTokenMutation) ResetToken() { - m.token = nil -} - -// SetUses sets the "uses" field. -func (m *DocumentTokenMutation) SetUses(i int) { - m.uses = &i - m.adduses = nil -} - -// Uses returns the value of the "uses" field in the mutation. -func (m *DocumentTokenMutation) Uses() (r int, exists bool) { - v := m.uses - if v == nil { - return - } - return *v, true -} - -// OldUses returns the old "uses" field's value of the DocumentToken entity. -// If the DocumentToken object wasn't provided to the builder, the object is fetched from the database. -// An error is returned if the mutation operation is not UpdateOne, or the database query fails. -func (m *DocumentTokenMutation) OldUses(ctx context.Context) (v int, err error) { - if !m.op.Is(OpUpdateOne) { - return v, errors.New("OldUses is only allowed on UpdateOne operations") - } - if m.id == nil || m.oldValue == nil { - return v, errors.New("OldUses requires an ID field in the mutation") - } - oldValue, err := m.oldValue(ctx) - if err != nil { - return v, fmt.Errorf("querying old value for OldUses: %w", err) - } - return oldValue.Uses, nil -} - -// AddUses adds i to the "uses" field. -func (m *DocumentTokenMutation) AddUses(i int) { - if m.adduses != nil { - *m.adduses += i - } else { - m.adduses = &i - } -} - -// AddedUses returns the value that was added to the "uses" field in this mutation. -func (m *DocumentTokenMutation) AddedUses() (r int, exists bool) { - v := m.adduses - if v == nil { - return - } - return *v, true -} - -// ResetUses resets all changes to the "uses" field. -func (m *DocumentTokenMutation) ResetUses() { - m.uses = nil - m.adduses = nil -} - -// SetExpiresAt sets the "expires_at" field. -func (m *DocumentTokenMutation) SetExpiresAt(t time.Time) { - m.expires_at = &t -} - -// ExpiresAt returns the value of the "expires_at" field in the mutation. -func (m *DocumentTokenMutation) ExpiresAt() (r time.Time, exists bool) { - v := m.expires_at - if v == nil { - return - } - return *v, true -} - -// OldExpiresAt returns the old "expires_at" field's value of the DocumentToken entity. -// If the DocumentToken object wasn't provided to the builder, the object is fetched from the database. -// An error is returned if the mutation operation is not UpdateOne, or the database query fails. -func (m *DocumentTokenMutation) OldExpiresAt(ctx context.Context) (v time.Time, err error) { - if !m.op.Is(OpUpdateOne) { - return v, errors.New("OldExpiresAt is only allowed on UpdateOne operations") - } - if m.id == nil || m.oldValue == nil { - return v, errors.New("OldExpiresAt requires an ID field in the mutation") - } - oldValue, err := m.oldValue(ctx) - if err != nil { - return v, fmt.Errorf("querying old value for OldExpiresAt: %w", err) - } - return oldValue.ExpiresAt, nil -} - -// ResetExpiresAt resets all changes to the "expires_at" field. -func (m *DocumentTokenMutation) ResetExpiresAt() { - m.expires_at = nil -} - -// SetDocumentID sets the "document" edge to the Document entity by id. -func (m *DocumentTokenMutation) SetDocumentID(id uuid.UUID) { - m.document = &id -} - -// ClearDocument clears the "document" edge to the Document entity. -func (m *DocumentTokenMutation) ClearDocument() { - m.cleareddocument = true -} - -// DocumentCleared reports if the "document" edge to the Document entity was cleared. -func (m *DocumentTokenMutation) DocumentCleared() bool { - return m.cleareddocument -} - -// DocumentID returns the "document" edge ID in the mutation. -func (m *DocumentTokenMutation) DocumentID() (id uuid.UUID, exists bool) { - if m.document != nil { - return *m.document, true - } - return -} - -// DocumentIDs returns the "document" edge IDs in the mutation. -// Note that IDs always returns len(IDs) <= 1 for unique edges, and you should use -// DocumentID instead. It exists only for internal usage by the builders. -func (m *DocumentTokenMutation) DocumentIDs() (ids []uuid.UUID) { - if id := m.document; id != nil { - ids = append(ids, *id) - } - return -} - -// ResetDocument resets all changes to the "document" edge. -func (m *DocumentTokenMutation) ResetDocument() { - m.document = nil - m.cleareddocument = false -} - -// Where appends a list predicates to the DocumentTokenMutation builder. -func (m *DocumentTokenMutation) Where(ps ...predicate.DocumentToken) { - m.predicates = append(m.predicates, ps...) -} - -// Op returns the operation name. -func (m *DocumentTokenMutation) Op() Op { - return m.op -} - -// Type returns the node type of this mutation (DocumentToken). -func (m *DocumentTokenMutation) Type() string { - return m.typ -} - -// Fields returns all fields that were changed during this mutation. Note that in -// order to get all numeric fields that were incremented/decremented, call -// AddedFields(). -func (m *DocumentTokenMutation) Fields() []string { - fields := make([]string, 0, 5) - if m.created_at != nil { - fields = append(fields, documenttoken.FieldCreatedAt) - } - if m.updated_at != nil { - fields = append(fields, documenttoken.FieldUpdatedAt) - } - if m.token != nil { - fields = append(fields, documenttoken.FieldToken) - } - if m.uses != nil { - fields = append(fields, documenttoken.FieldUses) - } - if m.expires_at != nil { - fields = append(fields, documenttoken.FieldExpiresAt) - } - return fields -} - -// Field returns the value of a field with the given name. The second boolean -// return value indicates that this field was not set, or was not defined in the -// schema. -func (m *DocumentTokenMutation) Field(name string) (ent.Value, bool) { - switch name { - case documenttoken.FieldCreatedAt: - return m.CreatedAt() - case documenttoken.FieldUpdatedAt: - return m.UpdatedAt() - case documenttoken.FieldToken: - return m.Token() - case documenttoken.FieldUses: - return m.Uses() - case documenttoken.FieldExpiresAt: - return m.ExpiresAt() - } - return nil, false -} - -// OldField returns the old value of the field from the database. An error is -// returned if the mutation operation is not UpdateOne, or the query to the -// database failed. -func (m *DocumentTokenMutation) OldField(ctx context.Context, name string) (ent.Value, error) { - switch name { - case documenttoken.FieldCreatedAt: - return m.OldCreatedAt(ctx) - case documenttoken.FieldUpdatedAt: - return m.OldUpdatedAt(ctx) - case documenttoken.FieldToken: - return m.OldToken(ctx) - case documenttoken.FieldUses: - return m.OldUses(ctx) - case documenttoken.FieldExpiresAt: - return m.OldExpiresAt(ctx) - } - return nil, fmt.Errorf("unknown DocumentToken field %s", name) -} - -// SetField sets the value of a field with the given name. It returns an error if -// the field is not defined in the schema, or if the type mismatched the field -// type. -func (m *DocumentTokenMutation) SetField(name string, value ent.Value) error { - switch name { - case documenttoken.FieldCreatedAt: - v, ok := value.(time.Time) - if !ok { - return fmt.Errorf("unexpected type %T for field %s", value, name) - } - m.SetCreatedAt(v) - return nil - case documenttoken.FieldUpdatedAt: - v, ok := value.(time.Time) - if !ok { - return fmt.Errorf("unexpected type %T for field %s", value, name) - } - m.SetUpdatedAt(v) - return nil - case documenttoken.FieldToken: - v, ok := value.([]byte) - if !ok { - return fmt.Errorf("unexpected type %T for field %s", value, name) - } - m.SetToken(v) - return nil - case documenttoken.FieldUses: - v, ok := value.(int) - if !ok { - return fmt.Errorf("unexpected type %T for field %s", value, name) - } - m.SetUses(v) - return nil - case documenttoken.FieldExpiresAt: - v, ok := value.(time.Time) - if !ok { - return fmt.Errorf("unexpected type %T for field %s", value, name) - } - m.SetExpiresAt(v) - return nil - } - return fmt.Errorf("unknown DocumentToken field %s", name) -} - -// AddedFields returns all numeric fields that were incremented/decremented during -// this mutation. -func (m *DocumentTokenMutation) AddedFields() []string { - var fields []string - if m.adduses != nil { - fields = append(fields, documenttoken.FieldUses) - } - return fields -} - -// AddedField returns the numeric value that was incremented/decremented on a field -// with the given name. The second boolean return value indicates that this field -// was not set, or was not defined in the schema. -func (m *DocumentTokenMutation) AddedField(name string) (ent.Value, bool) { - switch name { - case documenttoken.FieldUses: - return m.AddedUses() - } - return nil, false -} - -// AddField adds the value to the field with the given name. It returns an error if -// the field is not defined in the schema, or if the type mismatched the field -// type. -func (m *DocumentTokenMutation) AddField(name string, value ent.Value) error { - switch name { - case documenttoken.FieldUses: - v, ok := value.(int) - if !ok { - return fmt.Errorf("unexpected type %T for field %s", value, name) - } - m.AddUses(v) - return nil - } - return fmt.Errorf("unknown DocumentToken numeric field %s", name) -} - -// ClearedFields returns all nullable fields that were cleared during this -// mutation. -func (m *DocumentTokenMutation) ClearedFields() []string { - return nil -} - -// FieldCleared returns a boolean indicating if a field with the given name was -// cleared in this mutation. -func (m *DocumentTokenMutation) FieldCleared(name string) bool { - _, ok := m.clearedFields[name] - return ok -} - -// ClearField clears the value of the field with the given name. It returns an -// error if the field is not defined in the schema. -func (m *DocumentTokenMutation) ClearField(name string) error { - return fmt.Errorf("unknown DocumentToken nullable field %s", name) -} - -// ResetField resets all changes in the mutation for the field with the given name. -// It returns an error if the field is not defined in the schema. -func (m *DocumentTokenMutation) ResetField(name string) error { - switch name { - case documenttoken.FieldCreatedAt: - m.ResetCreatedAt() - return nil - case documenttoken.FieldUpdatedAt: - m.ResetUpdatedAt() - return nil - case documenttoken.FieldToken: - m.ResetToken() - return nil - case documenttoken.FieldUses: - m.ResetUses() - return nil - case documenttoken.FieldExpiresAt: - m.ResetExpiresAt() - return nil - } - return fmt.Errorf("unknown DocumentToken field %s", name) -} - -// AddedEdges returns all edge names that were set/added in this mutation. -func (m *DocumentTokenMutation) AddedEdges() []string { - edges := make([]string, 0, 1) - if m.document != nil { - edges = append(edges, documenttoken.EdgeDocument) - } - return edges -} - -// AddedIDs returns all IDs (to other nodes) that were added for the given edge -// name in this mutation. -func (m *DocumentTokenMutation) AddedIDs(name string) []ent.Value { - switch name { - case documenttoken.EdgeDocument: - if id := m.document; id != nil { - return []ent.Value{*id} - } - } - return nil -} - -// RemovedEdges returns all edge names that were removed in this mutation. -func (m *DocumentTokenMutation) RemovedEdges() []string { - edges := make([]string, 0, 1) - return edges -} - -// RemovedIDs returns all IDs (to other nodes) that were removed for the edge with -// the given name in this mutation. -func (m *DocumentTokenMutation) RemovedIDs(name string) []ent.Value { - return nil -} - -// ClearedEdges returns all edge names that were cleared in this mutation. -func (m *DocumentTokenMutation) ClearedEdges() []string { - edges := make([]string, 0, 1) - if m.cleareddocument { - edges = append(edges, documenttoken.EdgeDocument) - } - return edges -} - -// EdgeCleared returns a boolean which indicates if the edge with the given name -// was cleared in this mutation. -func (m *DocumentTokenMutation) EdgeCleared(name string) bool { - switch name { - case documenttoken.EdgeDocument: - return m.cleareddocument - } - return false -} - -// ClearEdge clears the value of the edge with the given name. It returns an error -// if that edge is not defined in the schema. -func (m *DocumentTokenMutation) ClearEdge(name string) error { - switch name { - case documenttoken.EdgeDocument: - m.ClearDocument() - return nil - } - return fmt.Errorf("unknown DocumentToken unique edge %s", name) -} - -// ResetEdge resets all changes to the edge with the given name in this mutation. -// It returns an error if the edge is not defined in the schema. -func (m *DocumentTokenMutation) ResetEdge(name string) error { - switch name { - case documenttoken.EdgeDocument: - m.ResetDocument() - return nil - } - return fmt.Errorf("unknown DocumentToken edge %s", name) -} - // GroupMutation represents an operation that mutates the Group nodes in the graph. type GroupMutation struct { config @@ -4560,58 +3841,61 @@ func (m *GroupInvitationTokenMutation) ResetEdge(name string) error { // ItemMutation represents an operation that mutates the Item nodes in the graph. type ItemMutation struct { config - op Op - typ string - id *uuid.UUID - created_at *time.Time - updated_at *time.Time - name *string - description *string - import_ref *string - notes *string - quantity *int - addquantity *int - insured *bool - archived *bool - asset_id *int - addasset_id *int - serial_number *string - model_number *string - manufacturer *string - lifetime_warranty *bool - warranty_expires *time.Time - warranty_details *string - purchase_time *time.Time - purchase_from *string - purchase_price *float64 - addpurchase_price *float64 - sold_time *time.Time - sold_to *string - sold_price *float64 - addsold_price *float64 - sold_notes *string - clearedFields map[string]struct{} - parent *uuid.UUID - clearedparent bool - children map[uuid.UUID]struct{} - removedchildren map[uuid.UUID]struct{} - clearedchildren bool - group *uuid.UUID - clearedgroup bool - label map[uuid.UUID]struct{} - removedlabel map[uuid.UUID]struct{} - clearedlabel bool - location *uuid.UUID - clearedlocation bool - fields map[uuid.UUID]struct{} - removedfields map[uuid.UUID]struct{} - clearedfields bool - attachments map[uuid.UUID]struct{} - removedattachments map[uuid.UUID]struct{} - clearedattachments bool - done bool - oldValue func(context.Context) (*Item, error) - predicates []predicate.Item + op Op + typ string + id *uuid.UUID + created_at *time.Time + updated_at *time.Time + name *string + description *string + import_ref *string + notes *string + quantity *int + addquantity *int + insured *bool + archived *bool + asset_id *int + addasset_id *int + serial_number *string + model_number *string + manufacturer *string + lifetime_warranty *bool + warranty_expires *time.Time + warranty_details *string + purchase_time *time.Time + purchase_from *string + purchase_price *float64 + addpurchase_price *float64 + sold_time *time.Time + sold_to *string + sold_price *float64 + addsold_price *float64 + sold_notes *string + clearedFields map[string]struct{} + parent *uuid.UUID + clearedparent bool + children map[uuid.UUID]struct{} + removedchildren map[uuid.UUID]struct{} + clearedchildren bool + group *uuid.UUID + clearedgroup bool + label map[uuid.UUID]struct{} + removedlabel map[uuid.UUID]struct{} + clearedlabel bool + location *uuid.UUID + clearedlocation bool + fields map[uuid.UUID]struct{} + removedfields map[uuid.UUID]struct{} + clearedfields bool + maintenance_entries map[uuid.UUID]struct{} + removedmaintenance_entries map[uuid.UUID]struct{} + clearedmaintenance_entries bool + attachments map[uuid.UUID]struct{} + removedattachments map[uuid.UUID]struct{} + clearedattachments bool + done bool + oldValue func(context.Context) (*Item, error) + predicates []predicate.Item } var _ ent.Mutation = (*ItemMutation)(nil) @@ -6074,6 +5358,60 @@ func (m *ItemMutation) ResetFields() { m.removedfields = nil } +// AddMaintenanceEntryIDs adds the "maintenance_entries" edge to the MaintenanceEntry entity by ids. +func (m *ItemMutation) AddMaintenanceEntryIDs(ids ...uuid.UUID) { + if m.maintenance_entries == nil { + m.maintenance_entries = make(map[uuid.UUID]struct{}) + } + for i := range ids { + m.maintenance_entries[ids[i]] = struct{}{} + } +} + +// ClearMaintenanceEntries clears the "maintenance_entries" edge to the MaintenanceEntry entity. +func (m *ItemMutation) ClearMaintenanceEntries() { + m.clearedmaintenance_entries = true +} + +// MaintenanceEntriesCleared reports if the "maintenance_entries" edge to the MaintenanceEntry entity was cleared. +func (m *ItemMutation) MaintenanceEntriesCleared() bool { + return m.clearedmaintenance_entries +} + +// RemoveMaintenanceEntryIDs removes the "maintenance_entries" edge to the MaintenanceEntry entity by IDs. +func (m *ItemMutation) RemoveMaintenanceEntryIDs(ids ...uuid.UUID) { + if m.removedmaintenance_entries == nil { + m.removedmaintenance_entries = make(map[uuid.UUID]struct{}) + } + for i := range ids { + delete(m.maintenance_entries, ids[i]) + m.removedmaintenance_entries[ids[i]] = struct{}{} + } +} + +// RemovedMaintenanceEntries returns the removed IDs of the "maintenance_entries" edge to the MaintenanceEntry entity. +func (m *ItemMutation) RemovedMaintenanceEntriesIDs() (ids []uuid.UUID) { + for id := range m.removedmaintenance_entries { + ids = append(ids, id) + } + return +} + +// MaintenanceEntriesIDs returns the "maintenance_entries" edge IDs in the mutation. +func (m *ItemMutation) MaintenanceEntriesIDs() (ids []uuid.UUID) { + for id := range m.maintenance_entries { + ids = append(ids, id) + } + return +} + +// ResetMaintenanceEntries resets all changes to the "maintenance_entries" edge. +func (m *ItemMutation) ResetMaintenanceEntries() { + m.maintenance_entries = nil + m.clearedmaintenance_entries = false + m.removedmaintenance_entries = nil +} + // AddAttachmentIDs adds the "attachments" edge to the Attachment entity by ids. func (m *ItemMutation) AddAttachmentIDs(ids ...uuid.UUID) { if m.attachments == nil { @@ -6752,7 +6090,7 @@ func (m *ItemMutation) ResetField(name string) error { // AddedEdges returns all edge names that were set/added in this mutation. func (m *ItemMutation) AddedEdges() []string { - edges := make([]string, 0, 7) + edges := make([]string, 0, 8) if m.parent != nil { edges = append(edges, item.EdgeParent) } @@ -6771,6 +6109,9 @@ func (m *ItemMutation) AddedEdges() []string { if m.fields != nil { edges = append(edges, item.EdgeFields) } + if m.maintenance_entries != nil { + edges = append(edges, item.EdgeMaintenanceEntries) + } if m.attachments != nil { edges = append(edges, item.EdgeAttachments) } @@ -6811,6 +6152,12 @@ func (m *ItemMutation) AddedIDs(name string) []ent.Value { ids = append(ids, id) } return ids + case item.EdgeMaintenanceEntries: + ids := make([]ent.Value, 0, len(m.maintenance_entries)) + for id := range m.maintenance_entries { + ids = append(ids, id) + } + return ids case item.EdgeAttachments: ids := make([]ent.Value, 0, len(m.attachments)) for id := range m.attachments { @@ -6823,7 +6170,7 @@ func (m *ItemMutation) AddedIDs(name string) []ent.Value { // RemovedEdges returns all edge names that were removed in this mutation. func (m *ItemMutation) RemovedEdges() []string { - edges := make([]string, 0, 7) + edges := make([]string, 0, 8) if m.removedchildren != nil { edges = append(edges, item.EdgeChildren) } @@ -6833,6 +6180,9 @@ func (m *ItemMutation) RemovedEdges() []string { if m.removedfields != nil { edges = append(edges, item.EdgeFields) } + if m.removedmaintenance_entries != nil { + edges = append(edges, item.EdgeMaintenanceEntries) + } if m.removedattachments != nil { edges = append(edges, item.EdgeAttachments) } @@ -6861,6 +6211,12 @@ func (m *ItemMutation) RemovedIDs(name string) []ent.Value { ids = append(ids, id) } return ids + case item.EdgeMaintenanceEntries: + ids := make([]ent.Value, 0, len(m.removedmaintenance_entries)) + for id := range m.removedmaintenance_entries { + ids = append(ids, id) + } + return ids case item.EdgeAttachments: ids := make([]ent.Value, 0, len(m.removedattachments)) for id := range m.removedattachments { @@ -6873,7 +6229,7 @@ func (m *ItemMutation) RemovedIDs(name string) []ent.Value { // ClearedEdges returns all edge names that were cleared in this mutation. func (m *ItemMutation) ClearedEdges() []string { - edges := make([]string, 0, 7) + edges := make([]string, 0, 8) if m.clearedparent { edges = append(edges, item.EdgeParent) } @@ -6892,6 +6248,9 @@ func (m *ItemMutation) ClearedEdges() []string { if m.clearedfields { edges = append(edges, item.EdgeFields) } + if m.clearedmaintenance_entries { + edges = append(edges, item.EdgeMaintenanceEntries) + } if m.clearedattachments { edges = append(edges, item.EdgeAttachments) } @@ -6914,6 +6273,8 @@ func (m *ItemMutation) EdgeCleared(name string) bool { return m.clearedlocation case item.EdgeFields: return m.clearedfields + case item.EdgeMaintenanceEntries: + return m.clearedmaintenance_entries case item.EdgeAttachments: return m.clearedattachments } @@ -6959,6 +6320,9 @@ func (m *ItemMutation) ResetEdge(name string) error { case item.EdgeFields: m.ResetFields() return nil + case item.EdgeMaintenanceEntries: + m.ResetMaintenanceEntries() + return nil case item.EdgeAttachments: m.ResetAttachments() return nil @@ -9400,6 +8764,758 @@ func (m *LocationMutation) ResetEdge(name string) error { return fmt.Errorf("unknown Location edge %s", name) } +// MaintenanceEntryMutation represents an operation that mutates the MaintenanceEntry nodes in the graph. +type MaintenanceEntryMutation struct { + config + op Op + typ string + id *uuid.UUID + created_at *time.Time + updated_at *time.Time + date *time.Time + name *string + description *string + cost *float64 + addcost *float64 + clearedFields map[string]struct{} + item *uuid.UUID + cleareditem bool + done bool + oldValue func(context.Context) (*MaintenanceEntry, error) + predicates []predicate.MaintenanceEntry +} + +var _ ent.Mutation = (*MaintenanceEntryMutation)(nil) + +// maintenanceentryOption allows management of the mutation configuration using functional options. +type maintenanceentryOption func(*MaintenanceEntryMutation) + +// newMaintenanceEntryMutation creates new mutation for the MaintenanceEntry entity. +func newMaintenanceEntryMutation(c config, op Op, opts ...maintenanceentryOption) *MaintenanceEntryMutation { + m := &MaintenanceEntryMutation{ + config: c, + op: op, + typ: TypeMaintenanceEntry, + clearedFields: make(map[string]struct{}), + } + for _, opt := range opts { + opt(m) + } + return m +} + +// withMaintenanceEntryID sets the ID field of the mutation. +func withMaintenanceEntryID(id uuid.UUID) maintenanceentryOption { + return func(m *MaintenanceEntryMutation) { + var ( + err error + once sync.Once + value *MaintenanceEntry + ) + m.oldValue = func(ctx context.Context) (*MaintenanceEntry, error) { + once.Do(func() { + if m.done { + err = errors.New("querying old values post mutation is not allowed") + } else { + value, err = m.Client().MaintenanceEntry.Get(ctx, id) + } + }) + return value, err + } + m.id = &id + } +} + +// withMaintenanceEntry sets the old MaintenanceEntry of the mutation. +func withMaintenanceEntry(node *MaintenanceEntry) maintenanceentryOption { + return func(m *MaintenanceEntryMutation) { + m.oldValue = func(context.Context) (*MaintenanceEntry, error) { + return node, nil + } + m.id = &node.ID + } +} + +// Client returns a new `ent.Client` from the mutation. If the mutation was +// executed in a transaction (ent.Tx), a transactional client is returned. +func (m MaintenanceEntryMutation) Client() *Client { + client := &Client{config: m.config} + client.init() + return client +} + +// Tx returns an `ent.Tx` for mutations that were executed in transactions; +// it returns an error otherwise. +func (m MaintenanceEntryMutation) Tx() (*Tx, error) { + if _, ok := m.driver.(*txDriver); !ok { + return nil, errors.New("ent: mutation is not running in a transaction") + } + tx := &Tx{config: m.config} + tx.init() + return tx, nil +} + +// SetID sets the value of the id field. Note that this +// operation is only accepted on creation of MaintenanceEntry entities. +func (m *MaintenanceEntryMutation) SetID(id uuid.UUID) { + m.id = &id +} + +// ID returns the ID value in the mutation. Note that the ID is only available +// if it was provided to the builder or after it was returned from the database. +func (m *MaintenanceEntryMutation) ID() (id uuid.UUID, exists bool) { + if m.id == nil { + return + } + return *m.id, true +} + +// IDs queries the database and returns the entity ids that match the mutation's predicate. +// That means, if the mutation is applied within a transaction with an isolation level such +// as sql.LevelSerializable, the returned ids match the ids of the rows that will be updated +// or updated by the mutation. +func (m *MaintenanceEntryMutation) IDs(ctx context.Context) ([]uuid.UUID, error) { + switch { + case m.op.Is(OpUpdateOne | OpDeleteOne): + id, exists := m.ID() + if exists { + return []uuid.UUID{id}, nil + } + fallthrough + case m.op.Is(OpUpdate | OpDelete): + return m.Client().MaintenanceEntry.Query().Where(m.predicates...).IDs(ctx) + default: + return nil, fmt.Errorf("IDs is not allowed on %s operations", m.op) + } +} + +// SetCreatedAt sets the "created_at" field. +func (m *MaintenanceEntryMutation) SetCreatedAt(t time.Time) { + m.created_at = &t +} + +// CreatedAt returns the value of the "created_at" field in the mutation. +func (m *MaintenanceEntryMutation) CreatedAt() (r time.Time, exists bool) { + v := m.created_at + if v == nil { + return + } + return *v, true +} + +// OldCreatedAt returns the old "created_at" field's value of the MaintenanceEntry entity. +// If the MaintenanceEntry object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *MaintenanceEntryMutation) OldCreatedAt(ctx context.Context) (v time.Time, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldCreatedAt is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldCreatedAt requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldCreatedAt: %w", err) + } + return oldValue.CreatedAt, nil +} + +// ResetCreatedAt resets all changes to the "created_at" field. +func (m *MaintenanceEntryMutation) ResetCreatedAt() { + m.created_at = nil +} + +// SetUpdatedAt sets the "updated_at" field. +func (m *MaintenanceEntryMutation) SetUpdatedAt(t time.Time) { + m.updated_at = &t +} + +// UpdatedAt returns the value of the "updated_at" field in the mutation. +func (m *MaintenanceEntryMutation) UpdatedAt() (r time.Time, exists bool) { + v := m.updated_at + if v == nil { + return + } + return *v, true +} + +// OldUpdatedAt returns the old "updated_at" field's value of the MaintenanceEntry entity. +// If the MaintenanceEntry object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *MaintenanceEntryMutation) OldUpdatedAt(ctx context.Context) (v time.Time, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldUpdatedAt is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldUpdatedAt requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldUpdatedAt: %w", err) + } + return oldValue.UpdatedAt, nil +} + +// ResetUpdatedAt resets all changes to the "updated_at" field. +func (m *MaintenanceEntryMutation) ResetUpdatedAt() { + m.updated_at = nil +} + +// SetItemID sets the "item_id" field. +func (m *MaintenanceEntryMutation) SetItemID(u uuid.UUID) { + m.item = &u +} + +// ItemID returns the value of the "item_id" field in the mutation. +func (m *MaintenanceEntryMutation) ItemID() (r uuid.UUID, exists bool) { + v := m.item + if v == nil { + return + } + return *v, true +} + +// OldItemID returns the old "item_id" field's value of the MaintenanceEntry entity. +// If the MaintenanceEntry object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *MaintenanceEntryMutation) OldItemID(ctx context.Context) (v uuid.UUID, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldItemID is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldItemID requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldItemID: %w", err) + } + return oldValue.ItemID, nil +} + +// ResetItemID resets all changes to the "item_id" field. +func (m *MaintenanceEntryMutation) ResetItemID() { + m.item = nil +} + +// SetDate sets the "date" field. +func (m *MaintenanceEntryMutation) SetDate(t time.Time) { + m.date = &t +} + +// Date returns the value of the "date" field in the mutation. +func (m *MaintenanceEntryMutation) Date() (r time.Time, exists bool) { + v := m.date + if v == nil { + return + } + return *v, true +} + +// OldDate returns the old "date" field's value of the MaintenanceEntry entity. +// If the MaintenanceEntry object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *MaintenanceEntryMutation) OldDate(ctx context.Context) (v time.Time, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldDate is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldDate requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldDate: %w", err) + } + return oldValue.Date, nil +} + +// ResetDate resets all changes to the "date" field. +func (m *MaintenanceEntryMutation) ResetDate() { + m.date = nil +} + +// SetName sets the "name" field. +func (m *MaintenanceEntryMutation) SetName(s string) { + m.name = &s +} + +// Name returns the value of the "name" field in the mutation. +func (m *MaintenanceEntryMutation) Name() (r string, exists bool) { + v := m.name + if v == nil { + return + } + return *v, true +} + +// OldName returns the old "name" field's value of the MaintenanceEntry entity. +// If the MaintenanceEntry object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *MaintenanceEntryMutation) OldName(ctx context.Context) (v string, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldName is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldName requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldName: %w", err) + } + return oldValue.Name, nil +} + +// ResetName resets all changes to the "name" field. +func (m *MaintenanceEntryMutation) ResetName() { + m.name = nil +} + +// SetDescription sets the "description" field. +func (m *MaintenanceEntryMutation) SetDescription(s string) { + m.description = &s +} + +// Description returns the value of the "description" field in the mutation. +func (m *MaintenanceEntryMutation) Description() (r string, exists bool) { + v := m.description + if v == nil { + return + } + return *v, true +} + +// OldDescription returns the old "description" field's value of the MaintenanceEntry entity. +// If the MaintenanceEntry object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *MaintenanceEntryMutation) OldDescription(ctx context.Context) (v string, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldDescription is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldDescription requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldDescription: %w", err) + } + return oldValue.Description, nil +} + +// ClearDescription clears the value of the "description" field. +func (m *MaintenanceEntryMutation) ClearDescription() { + m.description = nil + m.clearedFields[maintenanceentry.FieldDescription] = struct{}{} +} + +// DescriptionCleared returns if the "description" field was cleared in this mutation. +func (m *MaintenanceEntryMutation) DescriptionCleared() bool { + _, ok := m.clearedFields[maintenanceentry.FieldDescription] + return ok +} + +// ResetDescription resets all changes to the "description" field. +func (m *MaintenanceEntryMutation) ResetDescription() { + m.description = nil + delete(m.clearedFields, maintenanceentry.FieldDescription) +} + +// SetCost sets the "cost" field. +func (m *MaintenanceEntryMutation) SetCost(f float64) { + m.cost = &f + m.addcost = nil +} + +// Cost returns the value of the "cost" field in the mutation. +func (m *MaintenanceEntryMutation) Cost() (r float64, exists bool) { + v := m.cost + if v == nil { + return + } + return *v, true +} + +// OldCost returns the old "cost" field's value of the MaintenanceEntry entity. +// If the MaintenanceEntry object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *MaintenanceEntryMutation) OldCost(ctx context.Context) (v float64, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldCost is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldCost requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldCost: %w", err) + } + return oldValue.Cost, nil +} + +// AddCost adds f to the "cost" field. +func (m *MaintenanceEntryMutation) AddCost(f float64) { + if m.addcost != nil { + *m.addcost += f + } else { + m.addcost = &f + } +} + +// AddedCost returns the value that was added to the "cost" field in this mutation. +func (m *MaintenanceEntryMutation) AddedCost() (r float64, exists bool) { + v := m.addcost + if v == nil { + return + } + return *v, true +} + +// ResetCost resets all changes to the "cost" field. +func (m *MaintenanceEntryMutation) ResetCost() { + m.cost = nil + m.addcost = nil +} + +// ClearItem clears the "item" edge to the Item entity. +func (m *MaintenanceEntryMutation) ClearItem() { + m.cleareditem = true +} + +// ItemCleared reports if the "item" edge to the Item entity was cleared. +func (m *MaintenanceEntryMutation) ItemCleared() bool { + return m.cleareditem +} + +// ItemIDs returns the "item" edge IDs in the mutation. +// Note that IDs always returns len(IDs) <= 1 for unique edges, and you should use +// ItemID instead. It exists only for internal usage by the builders. +func (m *MaintenanceEntryMutation) ItemIDs() (ids []uuid.UUID) { + if id := m.item; id != nil { + ids = append(ids, *id) + } + return +} + +// ResetItem resets all changes to the "item" edge. +func (m *MaintenanceEntryMutation) ResetItem() { + m.item = nil + m.cleareditem = false +} + +// Where appends a list predicates to the MaintenanceEntryMutation builder. +func (m *MaintenanceEntryMutation) Where(ps ...predicate.MaintenanceEntry) { + m.predicates = append(m.predicates, ps...) +} + +// Op returns the operation name. +func (m *MaintenanceEntryMutation) Op() Op { + return m.op +} + +// Type returns the node type of this mutation (MaintenanceEntry). +func (m *MaintenanceEntryMutation) Type() string { + return m.typ +} + +// Fields returns all fields that were changed during this mutation. Note that in +// order to get all numeric fields that were incremented/decremented, call +// AddedFields(). +func (m *MaintenanceEntryMutation) Fields() []string { + fields := make([]string, 0, 7) + if m.created_at != nil { + fields = append(fields, maintenanceentry.FieldCreatedAt) + } + if m.updated_at != nil { + fields = append(fields, maintenanceentry.FieldUpdatedAt) + } + if m.item != nil { + fields = append(fields, maintenanceentry.FieldItemID) + } + if m.date != nil { + fields = append(fields, maintenanceentry.FieldDate) + } + if m.name != nil { + fields = append(fields, maintenanceentry.FieldName) + } + if m.description != nil { + fields = append(fields, maintenanceentry.FieldDescription) + } + if m.cost != nil { + fields = append(fields, maintenanceentry.FieldCost) + } + return fields +} + +// Field returns the value of a field with the given name. The second boolean +// return value indicates that this field was not set, or was not defined in the +// schema. +func (m *MaintenanceEntryMutation) Field(name string) (ent.Value, bool) { + switch name { + case maintenanceentry.FieldCreatedAt: + return m.CreatedAt() + case maintenanceentry.FieldUpdatedAt: + return m.UpdatedAt() + case maintenanceentry.FieldItemID: + return m.ItemID() + case maintenanceentry.FieldDate: + return m.Date() + case maintenanceentry.FieldName: + return m.Name() + case maintenanceentry.FieldDescription: + return m.Description() + case maintenanceentry.FieldCost: + return m.Cost() + } + return nil, false +} + +// OldField returns the old value of the field from the database. An error is +// returned if the mutation operation is not UpdateOne, or the query to the +// database failed. +func (m *MaintenanceEntryMutation) OldField(ctx context.Context, name string) (ent.Value, error) { + switch name { + case maintenanceentry.FieldCreatedAt: + return m.OldCreatedAt(ctx) + case maintenanceentry.FieldUpdatedAt: + return m.OldUpdatedAt(ctx) + case maintenanceentry.FieldItemID: + return m.OldItemID(ctx) + case maintenanceentry.FieldDate: + return m.OldDate(ctx) + case maintenanceentry.FieldName: + return m.OldName(ctx) + case maintenanceentry.FieldDescription: + return m.OldDescription(ctx) + case maintenanceentry.FieldCost: + return m.OldCost(ctx) + } + return nil, fmt.Errorf("unknown MaintenanceEntry field %s", name) +} + +// SetField sets the value of a field with the given name. It returns an error if +// the field is not defined in the schema, or if the type mismatched the field +// type. +func (m *MaintenanceEntryMutation) SetField(name string, value ent.Value) error { + switch name { + case maintenanceentry.FieldCreatedAt: + v, ok := value.(time.Time) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetCreatedAt(v) + return nil + case maintenanceentry.FieldUpdatedAt: + v, ok := value.(time.Time) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetUpdatedAt(v) + return nil + case maintenanceentry.FieldItemID: + v, ok := value.(uuid.UUID) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetItemID(v) + return nil + case maintenanceentry.FieldDate: + v, ok := value.(time.Time) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetDate(v) + return nil + case maintenanceentry.FieldName: + v, ok := value.(string) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetName(v) + return nil + case maintenanceentry.FieldDescription: + v, ok := value.(string) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetDescription(v) + return nil + case maintenanceentry.FieldCost: + v, ok := value.(float64) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetCost(v) + return nil + } + return fmt.Errorf("unknown MaintenanceEntry field %s", name) +} + +// AddedFields returns all numeric fields that were incremented/decremented during +// this mutation. +func (m *MaintenanceEntryMutation) AddedFields() []string { + var fields []string + if m.addcost != nil { + fields = append(fields, maintenanceentry.FieldCost) + } + return fields +} + +// AddedField returns the numeric value that was incremented/decremented on a field +// with the given name. The second boolean return value indicates that this field +// was not set, or was not defined in the schema. +func (m *MaintenanceEntryMutation) AddedField(name string) (ent.Value, bool) { + switch name { + case maintenanceentry.FieldCost: + return m.AddedCost() + } + return nil, false +} + +// AddField adds the value to the field with the given name. It returns an error if +// the field is not defined in the schema, or if the type mismatched the field +// type. +func (m *MaintenanceEntryMutation) AddField(name string, value ent.Value) error { + switch name { + case maintenanceentry.FieldCost: + v, ok := value.(float64) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.AddCost(v) + return nil + } + return fmt.Errorf("unknown MaintenanceEntry numeric field %s", name) +} + +// ClearedFields returns all nullable fields that were cleared during this +// mutation. +func (m *MaintenanceEntryMutation) ClearedFields() []string { + var fields []string + if m.FieldCleared(maintenanceentry.FieldDescription) { + fields = append(fields, maintenanceentry.FieldDescription) + } + return fields +} + +// FieldCleared returns a boolean indicating if a field with the given name was +// cleared in this mutation. +func (m *MaintenanceEntryMutation) FieldCleared(name string) bool { + _, ok := m.clearedFields[name] + return ok +} + +// ClearField clears the value of the field with the given name. It returns an +// error if the field is not defined in the schema. +func (m *MaintenanceEntryMutation) ClearField(name string) error { + switch name { + case maintenanceentry.FieldDescription: + m.ClearDescription() + return nil + } + return fmt.Errorf("unknown MaintenanceEntry nullable field %s", name) +} + +// ResetField resets all changes in the mutation for the field with the given name. +// It returns an error if the field is not defined in the schema. +func (m *MaintenanceEntryMutation) ResetField(name string) error { + switch name { + case maintenanceentry.FieldCreatedAt: + m.ResetCreatedAt() + return nil + case maintenanceentry.FieldUpdatedAt: + m.ResetUpdatedAt() + return nil + case maintenanceentry.FieldItemID: + m.ResetItemID() + return nil + case maintenanceentry.FieldDate: + m.ResetDate() + return nil + case maintenanceentry.FieldName: + m.ResetName() + return nil + case maintenanceentry.FieldDescription: + m.ResetDescription() + return nil + case maintenanceentry.FieldCost: + m.ResetCost() + return nil + } + return fmt.Errorf("unknown MaintenanceEntry field %s", name) +} + +// AddedEdges returns all edge names that were set/added in this mutation. +func (m *MaintenanceEntryMutation) AddedEdges() []string { + edges := make([]string, 0, 1) + if m.item != nil { + edges = append(edges, maintenanceentry.EdgeItem) + } + return edges +} + +// AddedIDs returns all IDs (to other nodes) that were added for the given edge +// name in this mutation. +func (m *MaintenanceEntryMutation) AddedIDs(name string) []ent.Value { + switch name { + case maintenanceentry.EdgeItem: + if id := m.item; id != nil { + return []ent.Value{*id} + } + } + return nil +} + +// RemovedEdges returns all edge names that were removed in this mutation. +func (m *MaintenanceEntryMutation) RemovedEdges() []string { + edges := make([]string, 0, 1) + return edges +} + +// RemovedIDs returns all IDs (to other nodes) that were removed for the edge with +// the given name in this mutation. +func (m *MaintenanceEntryMutation) RemovedIDs(name string) []ent.Value { + return nil +} + +// ClearedEdges returns all edge names that were cleared in this mutation. +func (m *MaintenanceEntryMutation) ClearedEdges() []string { + edges := make([]string, 0, 1) + if m.cleareditem { + edges = append(edges, maintenanceentry.EdgeItem) + } + return edges +} + +// EdgeCleared returns a boolean which indicates if the edge with the given name +// was cleared in this mutation. +func (m *MaintenanceEntryMutation) EdgeCleared(name string) bool { + switch name { + case maintenanceentry.EdgeItem: + return m.cleareditem + } + return false +} + +// ClearEdge clears the value of the edge with the given name. It returns an error +// if that edge is not defined in the schema. +func (m *MaintenanceEntryMutation) ClearEdge(name string) error { + switch name { + case maintenanceentry.EdgeItem: + m.ClearItem() + return nil + } + return fmt.Errorf("unknown MaintenanceEntry unique edge %s", name) +} + +// ResetEdge resets all changes to the edge with the given name in this mutation. +// It returns an error if the edge is not defined in the schema. +func (m *MaintenanceEntryMutation) ResetEdge(name string) error { + switch name { + case maintenanceentry.EdgeItem: + m.ResetItem() + return nil + } + return fmt.Errorf("unknown MaintenanceEntry edge %s", name) +} + // UserMutation represents an operation that mutates the User nodes in the graph. type UserMutation struct { config diff --git a/backend/internal/data/ent/predicate/predicate.go b/backend/internal/data/ent/predicate/predicate.go index 21a0a71..b1fbe67 100644 --- a/backend/internal/data/ent/predicate/predicate.go +++ b/backend/internal/data/ent/predicate/predicate.go @@ -18,9 +18,6 @@ type AuthTokens func(*sql.Selector) // Document is the predicate function for document builders. type Document func(*sql.Selector) -// DocumentToken is the predicate function for documenttoken builders. -type DocumentToken func(*sql.Selector) - // Group is the predicate function for group builders. type Group func(*sql.Selector) @@ -39,5 +36,8 @@ type Label func(*sql.Selector) // Location is the predicate function for location builders. type Location func(*sql.Selector) +// MaintenanceEntry is the predicate function for maintenanceentry builders. +type MaintenanceEntry func(*sql.Selector) + // User is the predicate function for user builders. type User func(*sql.Selector) diff --git a/backend/internal/data/ent/runtime.go b/backend/internal/data/ent/runtime.go index a9edda6..4ce9d2c 100644 --- a/backend/internal/data/ent/runtime.go +++ b/backend/internal/data/ent/runtime.go @@ -9,13 +9,13 @@ import ( "github.com/hay-kot/homebox/backend/internal/data/ent/attachment" "github.com/hay-kot/homebox/backend/internal/data/ent/authtokens" "github.com/hay-kot/homebox/backend/internal/data/ent/document" - "github.com/hay-kot/homebox/backend/internal/data/ent/documenttoken" "github.com/hay-kot/homebox/backend/internal/data/ent/group" "github.com/hay-kot/homebox/backend/internal/data/ent/groupinvitationtoken" "github.com/hay-kot/homebox/backend/internal/data/ent/item" "github.com/hay-kot/homebox/backend/internal/data/ent/itemfield" "github.com/hay-kot/homebox/backend/internal/data/ent/label" "github.com/hay-kot/homebox/backend/internal/data/ent/location" + "github.com/hay-kot/homebox/backend/internal/data/ent/maintenanceentry" "github.com/hay-kot/homebox/backend/internal/data/ent/schema" "github.com/hay-kot/homebox/backend/internal/data/ent/user" ) @@ -123,37 +123,6 @@ func init() { documentDescID := documentMixinFields0[0].Descriptor() // document.DefaultID holds the default value on creation for the id field. document.DefaultID = documentDescID.Default.(func() uuid.UUID) - documenttokenMixin := schema.DocumentToken{}.Mixin() - documenttokenMixinFields0 := documenttokenMixin[0].Fields() - _ = documenttokenMixinFields0 - documenttokenFields := schema.DocumentToken{}.Fields() - _ = documenttokenFields - // documenttokenDescCreatedAt is the schema descriptor for created_at field. - documenttokenDescCreatedAt := documenttokenMixinFields0[1].Descriptor() - // documenttoken.DefaultCreatedAt holds the default value on creation for the created_at field. - documenttoken.DefaultCreatedAt = documenttokenDescCreatedAt.Default.(func() time.Time) - // documenttokenDescUpdatedAt is the schema descriptor for updated_at field. - documenttokenDescUpdatedAt := documenttokenMixinFields0[2].Descriptor() - // documenttoken.DefaultUpdatedAt holds the default value on creation for the updated_at field. - documenttoken.DefaultUpdatedAt = documenttokenDescUpdatedAt.Default.(func() time.Time) - // documenttoken.UpdateDefaultUpdatedAt holds the default value on update for the updated_at field. - documenttoken.UpdateDefaultUpdatedAt = documenttokenDescUpdatedAt.UpdateDefault.(func() time.Time) - // documenttokenDescToken is the schema descriptor for token field. - documenttokenDescToken := documenttokenFields[0].Descriptor() - // documenttoken.TokenValidator is a validator for the "token" field. It is called by the builders before save. - documenttoken.TokenValidator = documenttokenDescToken.Validators[0].(func([]byte) error) - // documenttokenDescUses is the schema descriptor for uses field. - documenttokenDescUses := documenttokenFields[1].Descriptor() - // documenttoken.DefaultUses holds the default value on creation for the uses field. - documenttoken.DefaultUses = documenttokenDescUses.Default.(int) - // documenttokenDescExpiresAt is the schema descriptor for expires_at field. - documenttokenDescExpiresAt := documenttokenFields[2].Descriptor() - // documenttoken.DefaultExpiresAt holds the default value on creation for the expires_at field. - documenttoken.DefaultExpiresAt = documenttokenDescExpiresAt.Default.(func() time.Time) - // documenttokenDescID is the schema descriptor for id field. - documenttokenDescID := documenttokenMixinFields0[0].Descriptor() - // documenttoken.DefaultID holds the default value on creation for the id field. - documenttoken.DefaultID = documenttokenDescID.Default.(func() uuid.UUID) groupMixin := schema.Group{}.Mixin() groupMixinFields0 := groupMixin[0].Fields() _ = groupMixinFields0 @@ -462,6 +431,55 @@ func init() { locationDescID := locationMixinFields0[0].Descriptor() // location.DefaultID holds the default value on creation for the id field. location.DefaultID = locationDescID.Default.(func() uuid.UUID) + maintenanceentryMixin := schema.MaintenanceEntry{}.Mixin() + maintenanceentryMixinFields0 := maintenanceentryMixin[0].Fields() + _ = maintenanceentryMixinFields0 + maintenanceentryFields := schema.MaintenanceEntry{}.Fields() + _ = maintenanceentryFields + // maintenanceentryDescCreatedAt is the schema descriptor for created_at field. + maintenanceentryDescCreatedAt := maintenanceentryMixinFields0[1].Descriptor() + // maintenanceentry.DefaultCreatedAt holds the default value on creation for the created_at field. + maintenanceentry.DefaultCreatedAt = maintenanceentryDescCreatedAt.Default.(func() time.Time) + // maintenanceentryDescUpdatedAt is the schema descriptor for updated_at field. + maintenanceentryDescUpdatedAt := maintenanceentryMixinFields0[2].Descriptor() + // maintenanceentry.DefaultUpdatedAt holds the default value on creation for the updated_at field. + maintenanceentry.DefaultUpdatedAt = maintenanceentryDescUpdatedAt.Default.(func() time.Time) + // maintenanceentry.UpdateDefaultUpdatedAt holds the default value on update for the updated_at field. + maintenanceentry.UpdateDefaultUpdatedAt = maintenanceentryDescUpdatedAt.UpdateDefault.(func() time.Time) + // maintenanceentryDescDate is the schema descriptor for date field. + maintenanceentryDescDate := maintenanceentryFields[1].Descriptor() + // maintenanceentry.DefaultDate holds the default value on creation for the date field. + maintenanceentry.DefaultDate = maintenanceentryDescDate.Default.(func() time.Time) + // maintenanceentryDescName is the schema descriptor for name field. + maintenanceentryDescName := maintenanceentryFields[2].Descriptor() + // maintenanceentry.NameValidator is a validator for the "name" field. It is called by the builders before save. + maintenanceentry.NameValidator = func() func(string) error { + validators := maintenanceentryDescName.Validators + fns := [...]func(string) error{ + validators[0].(func(string) error), + validators[1].(func(string) error), + } + return func(name string) error { + for _, fn := range fns { + if err := fn(name); err != nil { + return err + } + } + return nil + } + }() + // maintenanceentryDescDescription is the schema descriptor for description field. + maintenanceentryDescDescription := maintenanceentryFields[3].Descriptor() + // maintenanceentry.DescriptionValidator is a validator for the "description" field. It is called by the builders before save. + maintenanceentry.DescriptionValidator = maintenanceentryDescDescription.Validators[0].(func(string) error) + // maintenanceentryDescCost is the schema descriptor for cost field. + maintenanceentryDescCost := maintenanceentryFields[4].Descriptor() + // maintenanceentry.DefaultCost holds the default value on creation for the cost field. + maintenanceentry.DefaultCost = maintenanceentryDescCost.Default.(float64) + // maintenanceentryDescID is the schema descriptor for id field. + maintenanceentryDescID := maintenanceentryMixinFields0[0].Descriptor() + // maintenanceentry.DefaultID holds the default value on creation for the id field. + maintenanceentry.DefaultID = maintenanceentryDescID.Default.(func() uuid.UUID) userMixin := schema.User{}.Mixin() userMixinFields0 := userMixin[0].Fields() _ = userMixinFields0 diff --git a/backend/internal/data/ent/schema/auth_tokens.go b/backend/internal/data/ent/schema/auth_tokens.go index e29b79a..71b22d7 100644 --- a/backend/internal/data/ent/schema/auth_tokens.go +++ b/backend/internal/data/ent/schema/auth_tokens.go @@ -4,6 +4,7 @@ import ( "time" "entgo.io/ent" + "entgo.io/ent/dialect/entsql" "entgo.io/ent/schema/edge" "entgo.io/ent/schema/field" "entgo.io/ent/schema/index" @@ -38,7 +39,10 @@ func (AuthTokens) Edges() []ent.Edge { Ref("auth_tokens"). Unique(), edge.To("roles", AuthRoles.Type). - Unique(), + Unique(). + Annotations(entsql.Annotation{ + OnDelete: entsql.Cascade, + }), } } diff --git a/backend/internal/data/ent/schema/document.go b/backend/internal/data/ent/schema/document.go index 2293c39..a2c26e2 100644 --- a/backend/internal/data/ent/schema/document.go +++ b/backend/internal/data/ent/schema/document.go @@ -38,10 +38,6 @@ func (Document) Edges() []ent.Edge { Ref("documents"). Required(). Unique(), - edge.To("document_tokens", DocumentToken.Type). - Annotations(entsql.Annotation{ - OnDelete: entsql.Cascade, - }), edge.To("attachments", Attachment.Type). Annotations(entsql.Annotation{ OnDelete: entsql.Cascade, diff --git a/backend/internal/data/ent/schema/document_token.go b/backend/internal/data/ent/schema/document_token.go deleted file mode 100644 index c5ec72f..0000000 --- a/backend/internal/data/ent/schema/document_token.go +++ /dev/null @@ -1,50 +0,0 @@ -package schema - -import ( - "time" - - "entgo.io/ent" - "entgo.io/ent/schema/edge" - "entgo.io/ent/schema/field" - "entgo.io/ent/schema/index" - "github.com/hay-kot/homebox/backend/internal/data/ent/schema/mixins" -) - -// DocumentToken holds the schema definition for the DocumentToken entity. -type DocumentToken struct { - ent.Schema -} - -func (DocumentToken) Mixin() []ent.Mixin { - return []ent.Mixin{ - mixins.BaseMixin{}, - } -} - -// Fields of the DocumentToken. -func (DocumentToken) Fields() []ent.Field { - return []ent.Field{ - field.Bytes("token"). - NotEmpty(). - Unique(), - field.Int("uses"). - Default(1), - field.Time("expires_at"). - Default(func() time.Time { return time.Now().Add(time.Minute * 10) }), - } -} - -// Edges of the DocumentToken. -func (DocumentToken) Edges() []ent.Edge { - return []ent.Edge{ - edge.From("document", Document.Type). - Ref("document_tokens"). - Unique(), - } -} - -func (DocumentToken) Indexes() []ent.Index { - return []ent.Index{ - index.Fields("token"), - } -} diff --git a/backend/internal/data/ent/schema/item.go b/backend/internal/data/ent/schema/item.go index 388566d..5180f27 100644 --- a/backend/internal/data/ent/schema/item.go +++ b/backend/internal/data/ent/schema/item.go @@ -116,6 +116,10 @@ func (Item) Edges() []ent.Edge { Annotations(entsql.Annotation{ OnDelete: entsql.Cascade, }), + edge.To("maintenance_entries", MaintenanceEntry.Type). + Annotations(entsql.Annotation{ + OnDelete: entsql.Cascade, + }), edge.To("attachments", Attachment.Type). Annotations(entsql.Annotation{ OnDelete: entsql.Cascade, diff --git a/backend/internal/data/ent/schema/maintenance_entry.go b/backend/internal/data/ent/schema/maintenance_entry.go new file mode 100644 index 0000000..7fd9643 --- /dev/null +++ b/backend/internal/data/ent/schema/maintenance_entry.go @@ -0,0 +1,48 @@ +package schema + +import ( + "time" + + "entgo.io/ent" + "entgo.io/ent/schema/edge" + "entgo.io/ent/schema/field" + "github.com/google/uuid" + "github.com/hay-kot/homebox/backend/internal/data/ent/schema/mixins" +) + +type MaintenanceEntry struct { + ent.Schema +} + +func (MaintenanceEntry) Mixin() []ent.Mixin { + return []ent.Mixin{ + mixins.BaseMixin{}, + } +} + +func (MaintenanceEntry) Fields() []ent.Field { + return []ent.Field{ + field.UUID("item_id", uuid.UUID{}), + field.Time("date"). + Default(time.Now), + field.String("name"). + MaxLen(255). + NotEmpty(), + field.String("description"). + MaxLen(2500). + Optional(), + field.Float("cost"). + Default(0.0), + } +} + +// Edges of the ItemField. +func (MaintenanceEntry) Edges() []ent.Edge { + return []ent.Edge{ + edge.From("item", Item.Type). + Field("item_id"). + Ref("maintenance_entries"). + Required(). + Unique(), + } +} diff --git a/backend/internal/data/ent/schema/templates/has_id.tmpl b/backend/internal/data/ent/schema/templates/has_id.tmpl index 42b0cd8..cc6e30a 100644 --- a/backend/internal/data/ent/schema/templates/has_id.tmpl +++ b/backend/internal/data/ent/schema/templates/has_id.tmpl @@ -9,8 +9,13 @@ import "github.com/google/uuid" {{/* Loop over all nodes and implement the "HasID" interface */}} {{ range $n := $.Nodes }} + {{ if not $n.ID }} + {{/* If the node doesn't have an ID field, we skip it. */}} + {{ continue }} + {{ end }} + {{/* The "HasID" interface is implemented by the "ID" method. */}} {{ $receiver := $n.Receiver }} - func ({{ $receiver }} *{{ $n.Name }}) GetID() uuid.UUID { + func ({{ $receiver }} *{{ $n.Name }}) GetID() {{ $n.ID.Type }} { return {{ $receiver }}.ID } {{ end }} diff --git a/backend/internal/data/ent/tx.go b/backend/internal/data/ent/tx.go index 8af5b01..0703ce5 100644 --- a/backend/internal/data/ent/tx.go +++ b/backend/internal/data/ent/tx.go @@ -20,8 +20,6 @@ type Tx struct { AuthTokens *AuthTokensClient // Document is the client for interacting with the Document builders. Document *DocumentClient - // DocumentToken is the client for interacting with the DocumentToken builders. - DocumentToken *DocumentTokenClient // Group is the client for interacting with the Group builders. Group *GroupClient // GroupInvitationToken is the client for interacting with the GroupInvitationToken builders. @@ -34,6 +32,8 @@ type Tx struct { Label *LabelClient // Location is the client for interacting with the Location builders. Location *LocationClient + // MaintenanceEntry is the client for interacting with the MaintenanceEntry builders. + MaintenanceEntry *MaintenanceEntryClient // User is the client for interacting with the User builders. User *UserClient @@ -171,13 +171,13 @@ func (tx *Tx) init() { tx.AuthRoles = NewAuthRolesClient(tx.config) tx.AuthTokens = NewAuthTokensClient(tx.config) tx.Document = NewDocumentClient(tx.config) - tx.DocumentToken = NewDocumentTokenClient(tx.config) tx.Group = NewGroupClient(tx.config) tx.GroupInvitationToken = NewGroupInvitationTokenClient(tx.config) tx.Item = NewItemClient(tx.config) tx.ItemField = NewItemFieldClient(tx.config) tx.Label = NewLabelClient(tx.config) tx.Location = NewLocationClient(tx.config) + tx.MaintenanceEntry = NewMaintenanceEntryClient(tx.config) tx.User = NewUserClient(tx.config) } diff --git a/backend/internal/data/migrations/migrations.go b/backend/internal/data/migrations/migrations.go index 5fcb8e3..fba84c5 100644 --- a/backend/internal/data/migrations/migrations.go +++ b/backend/internal/data/migrations/migrations.go @@ -6,7 +6,7 @@ import ( "path/filepath" ) -// go:embed all:migrations +//go:embed all:migrations var Files embed.FS // Write writes the embedded migrations to a temporary directory. @@ -18,7 +18,7 @@ func Write(temp string) error { return err } - fsDir, err := Files.ReadDir(".") + fsDir, err := Files.ReadDir("migrations") if err != nil { return err } diff --git a/backend/internal/data/migrations/migrations/20221205230404_drop_document_tokens.sql b/backend/internal/data/migrations/migrations/20221205230404_drop_document_tokens.sql new file mode 100644 index 0000000..e130abe --- /dev/null +++ b/backend/internal/data/migrations/migrations/20221205230404_drop_document_tokens.sql @@ -0,0 +1,5 @@ +-- disable the enforcement of foreign-keys constraints +PRAGMA foreign_keys = off; +DROP TABLE `document_tokens`; +-- enable back the enforcement of foreign-keys constraints +PRAGMA foreign_keys = on; \ No newline at end of file diff --git a/backend/internal/data/migrations/migrations/20221205234214_add_maintenance_entries.sql b/backend/internal/data/migrations/migrations/20221205234214_add_maintenance_entries.sql new file mode 100644 index 0000000..2491ec4 --- /dev/null +++ b/backend/internal/data/migrations/migrations/20221205234214_add_maintenance_entries.sql @@ -0,0 +1,2 @@ +-- create "maintenance_entries" table +CREATE TABLE `maintenance_entries` (`id` uuid NOT NULL, `created_at` datetime NOT NULL, `updated_at` datetime NOT NULL, `date` datetime NOT NULL, `name` text NOT NULL, `description` text NULL, `cost` real NOT NULL DEFAULT 0, `item_id` uuid NOT NULL, PRIMARY KEY (`id`), CONSTRAINT `maintenance_entries_items_maintenance_entries` FOREIGN KEY (`item_id`) REFERENCES `items` (`id`) ON DELETE CASCADE); diff --git a/backend/internal/data/migrations/migrations/20221205234812_cascade_delete_roles.sql b/backend/internal/data/migrations/migrations/20221205234812_cascade_delete_roles.sql new file mode 100644 index 0000000..8a37c11 --- /dev/null +++ b/backend/internal/data/migrations/migrations/20221205234812_cascade_delete_roles.sql @@ -0,0 +1,16 @@ +-- disable the enforcement of foreign-keys constraints +PRAGMA foreign_keys = off; +-- create "new_auth_roles" table +CREATE TABLE `new_auth_roles` (`id` integer NOT NULL PRIMARY KEY AUTOINCREMENT, `role` text NOT NULL DEFAULT 'user', `auth_tokens_roles` uuid NULL, CONSTRAINT `auth_roles_auth_tokens_roles` FOREIGN KEY (`auth_tokens_roles`) REFERENCES `auth_tokens` (`id`) ON DELETE CASCADE); +-- copy rows from old table "auth_roles" to new temporary table "new_auth_roles" +INSERT INTO `new_auth_roles` (`id`, `role`, `auth_tokens_roles`) SELECT `id`, `role`, `auth_tokens_roles` FROM `auth_roles`; +-- drop "auth_roles" table after copying rows +DROP TABLE `auth_roles`; +-- rename temporary table "new_auth_roles" to "auth_roles" +ALTER TABLE `new_auth_roles` RENAME TO `auth_roles`; +-- create index "auth_roles_auth_tokens_roles_key" to table: "auth_roles" +CREATE UNIQUE INDEX `auth_roles_auth_tokens_roles_key` ON `auth_roles` (`auth_tokens_roles`); +-- delete where tokens is null +DELETE FROM `auth_roles` WHERE `auth_tokens_roles` IS NULL; +-- enable back the enforcement of foreign-keys constraints +PRAGMA foreign_keys = on; diff --git a/backend/internal/data/migrations/migrations/atlas.sum b/backend/internal/data/migrations/migrations/atlas.sum index 0c9927b..5de79cc 100644 --- a/backend/internal/data/migrations/migrations/atlas.sum +++ b/backend/internal/data/migrations/migrations/atlas.sum @@ -1,4 +1,4 @@ -h1:oo2QbYbKkbf4oTfkRXqo9XGPp8S76j33WQvDZITv5s8= +h1:dn3XsqwgjCxEtpLXmHlt2ALRwg2cZB6m8lg2faxeLXM= 20220929052825_init.sql h1:ZlCqm1wzjDmofeAcSX3jE4h4VcdTNGpRg2eabztDy9Q= 20221001210956_group_invitations.sql h1:YQKJFtE39wFOcRNbZQ/d+ZlHwrcfcsZlcv/pLEYdpjw= 20221009173029_add_user_roles.sql h1:vWmzAfgEWQeGk0Vn70zfVPCcfEZth3E0JcvyKTjpYyU= @@ -6,3 +6,6 @@ h1:oo2QbYbKkbf4oTfkRXqo9XGPp8S76j33WQvDZITv5s8= 20221101041931_add_archived_field.sql h1:L2WxiOh1svRn817cNURgqnEQg6DIcodZ1twK4tvxW94= 20221113012312_add_asset_id_field.sql h1:DjD7e1PS8OfxGBWic8h0nO/X6CNnHEMqQjDCaaQ3M3Q= 20221203053132_add_token_roles.sql h1:wFTIh+KBoHfLfy/L0ZmJz4cNXKHdACG9ZK/yvVKjF0M= +20221205230404_drop_document_tokens.sql h1:9dCbNFcjtsT6lEhkxCn/vYaGRmQrl1LefdEJgvkfhGg= +20221205234214_add_maintenance_entries.sql h1:B56VzCuDsed1k3/sYUoKlOkP90DcdLufxFK0qYvoafU= +20221205234812_cascade_delete_roles.sql h1:VIiaImR48nCHF3uFbOYOX1E79Ta5HsUBetGaSAbh9Gk= diff --git a/backend/internal/data/repo/map_helpers.go b/backend/internal/data/repo/map_helpers.go index a9c0bca..9404cb0 100644 --- a/backend/internal/data/repo/map_helpers.go +++ b/backend/internal/data/repo/map_helpers.go @@ -16,17 +16,16 @@ func mapTErrFunc[T any, Y any](fn func(T) Y) func(T, error) (Y, error) { } } -// TODO: Future Usage -// func mapEachFunc[T any, Y any](fn func(T) Y) func([]T) []Y { -// return func(items []T) []Y { -// result := make([]Y, len(items)) -// for i, item := range items { -// result[i] = fn(item) -// } +func mapTEachFunc[T any, Y any](fn func(T) Y) func([]T) []Y { + return func(items []T) []Y { + result := make([]Y, len(items)) + for i, item := range items { + result[i] = fn(item) + } -// return result -// } -// } + return result + } +} func mapTEachErrFunc[T any, Y any](fn func(T) Y) func([]T, error) ([]Y, error) { return func(items []T, err error) ([]Y, error) { diff --git a/backend/internal/data/repo/repo_document_tokens.go b/backend/internal/data/repo/repo_document_tokens.go deleted file mode 100644 index 018ea61..0000000 --- a/backend/internal/data/repo/repo_document_tokens.go +++ /dev/null @@ -1,68 +0,0 @@ -package repo - -import ( - "context" - "time" - - "github.com/google/uuid" - "github.com/hay-kot/homebox/backend/internal/data/ent" - "github.com/hay-kot/homebox/backend/internal/data/ent/documenttoken" -) - -// DocumentTokensRepository is a repository for Document entity -type DocumentTokensRepository struct { - db *ent.Client -} - -type ( - DocumentToken struct { - ID uuid.UUID `json:"-"` - TokenHash []byte `json:"tokenHash"` - ExpiresAt time.Time `json:"expiresAt"` - DocumentID uuid.UUID `json:"documentId"` - } - - DocumentTokenCreate struct { - TokenHash []byte `json:"tokenHash"` - DocumentID uuid.UUID `json:"documentId"` - ExpiresAt time.Time `json:"expiresAt"` - } -) - -var ( - mapDocumentTokenErr = mapTErrFunc(mapDocumentToken) -) - -func mapDocumentToken(e *ent.DocumentToken) DocumentToken { - return DocumentToken{ - ID: e.ID, - TokenHash: e.Token, - ExpiresAt: e.ExpiresAt, - DocumentID: e.Edges.Document.ID, - } -} - -func (r *DocumentTokensRepository) Create(ctx context.Context, data DocumentTokenCreate) (DocumentToken, error) { - result, err := r.db.DocumentToken.Create(). - SetDocumentID(data.DocumentID). - SetToken(data.TokenHash). - SetExpiresAt(data.ExpiresAt). - Save(ctx) - - if err != nil { - return DocumentToken{}, err - } - - return mapDocumentTokenErr(r.db.DocumentToken.Query(). - Where(documenttoken.ID(result.ID)). - WithDocument(). - Only(ctx)) -} - -func (r *DocumentTokensRepository) PurgeExpiredTokens(ctx context.Context) (int, error) { - return r.db.DocumentToken.Delete().Where(documenttoken.ExpiresAtLT(time.Now())).Exec(ctx) -} - -func (r *DocumentTokensRepository) Delete(ctx context.Context, id uuid.UUID) error { - return r.db.DocumentToken.DeleteOneID(id).Exec(ctx) -} diff --git a/backend/internal/data/repo/repo_document_tokens_test.go b/backend/internal/data/repo/repo_document_tokens_test.go deleted file mode 100644 index 6646eca..0000000 --- a/backend/internal/data/repo/repo_document_tokens_test.go +++ /dev/null @@ -1,150 +0,0 @@ -package repo - -import ( - "context" - "testing" - "time" - - "github.com/google/uuid" - "github.com/hay-kot/homebox/backend/internal/data/ent" - "github.com/hay-kot/homebox/backend/internal/data/ent/documenttoken" - "github.com/stretchr/testify/assert" -) - -func TestDocumentTokensRepository_Create(t *testing.T) { - entities := useDocs(t, 1) - doc := entities[0] - expires := fk.Time() - - type args struct { - ctx context.Context - data DocumentTokenCreate - } - tests := []struct { - name string - args args - want *ent.DocumentToken - wantErr bool - }{ - { - name: "create document token", - args: args{ - ctx: context.Background(), - data: DocumentTokenCreate{ - DocumentID: doc.ID, - TokenHash: []byte("token"), - ExpiresAt: expires, - }, - }, - want: &ent.DocumentToken{ - Edges: ent.DocumentTokenEdges{ - Document: &ent.Document{ - ID: doc.ID, - }, - }, - Token: []byte("token"), - ExpiresAt: expires, - }, - wantErr: false, - }, - { - name: "create document token with empty token", - args: args{ - ctx: context.Background(), - data: DocumentTokenCreate{ - DocumentID: doc.ID, - TokenHash: []byte(""), - ExpiresAt: expires, - }, - }, - want: nil, - wantErr: true, - }, - { - name: "create document token with empty document id", - args: args{ - ctx: context.Background(), - data: DocumentTokenCreate{ - DocumentID: uuid.Nil, - TokenHash: []byte("token"), - ExpiresAt: expires, - }, - }, - want: nil, - wantErr: true, - }, - } - - ids := make([]uuid.UUID, 0, len(tests)) - - t.Cleanup(func() { - for _, id := range ids { - _ = tRepos.DocTokens.Delete(context.Background(), id) - } - }) - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - - got, err := tRepos.DocTokens.Create(tt.args.ctx, tt.args.data) - if (err != nil) != tt.wantErr { - t.Errorf("DocumentTokensRepository.Create() error = %v, wantErr %v", err, tt.wantErr) - return - } - if tt.wantErr { - return - } - - assert.Equal(t, tt.want.Token, got.TokenHash) - assert.WithinDuration(t, tt.want.ExpiresAt, got.ExpiresAt, time.Duration(1)*time.Second) - assert.Equal(t, tt.want.Edges.Document.ID, got.DocumentID) - }) - - } -} - -func useDocTokens(t *testing.T, num int) []DocumentToken { - entity := useDocs(t, 1)[0] - - results := make([]DocumentToken, 0, num) - - ids := make([]uuid.UUID, 0, num) - t.Cleanup(func() { - for _, id := range ids { - _ = tRepos.DocTokens.Delete(context.Background(), id) - } - }) - - for i := 0; i < num; i++ { - e, err := tRepos.DocTokens.Create(context.Background(), DocumentTokenCreate{ - DocumentID: entity.ID, - TokenHash: []byte(fk.Str(10)), - ExpiresAt: fk.Time(), - }) - - assert.NoError(t, err) - results = append(results, e) - ids = append(ids, e.ID) - } - - return results -} - -func TestDocumentTokensRepository_PurgeExpiredTokens(t *testing.T) { - entities := useDocTokens(t, 2) - - // set expired token - tRepos.DocTokens.db.DocumentToken.Update(). - Where(documenttoken.ID(entities[0].ID)). - SetExpiresAt(time.Now().Add(-time.Hour)). - ExecX(context.Background()) - - count, err := tRepos.DocTokens.PurgeExpiredTokens(context.Background()) - assert.NoError(t, err) - assert.Equal(t, 1, count) - - all, err := tRepos.DocTokens.db.DocumentToken.Query().All(context.Background()) - assert.NoError(t, err) - assert.Len(t, all, 1) - assert.Equal(t, entities[1].ID, all[0].ID) -} diff --git a/backend/internal/data/repo/repo_maintenance_entry.go b/backend/internal/data/repo/repo_maintenance_entry.go new file mode 100644 index 0000000..175bd7e --- /dev/null +++ b/backend/internal/data/repo/repo_maintenance_entry.go @@ -0,0 +1,136 @@ +package repo + +import ( + "context" + "time" + + "github.com/google/uuid" + "github.com/hay-kot/homebox/backend/internal/data/ent" + "github.com/hay-kot/homebox/backend/internal/data/ent/maintenanceentry" +) + +// MaintenanceEntryRepository is a repository for maintenance entries that are +// associated with an item in the database. An entry represents a maintenance event +// that has been performed on an item. +type MaintenanceEntryRepository struct { + db *ent.Client +} +type ( + MaintenanceEntryCreate struct { + Date time.Time `json:"date"` + Name string `json:"name"` + Description string `json:"description"` + Cost float64 `json:"cost,string"` + } + + MaintenanceEntry struct { + ID uuid.UUID `json:"id"` + Date time.Time `json:"date"` + Name string `json:"name"` + Description string `json:"description"` + Cost float64 `json:"cost,string"` + } + + MaintenanceEntryUpdate struct { + Date time.Time `json:"date"` + Name string `json:"name"` + Description string `json:"description"` + Cost float64 `json:"cost,string"` + } + + MaintenanceLog struct { + ItemID uuid.UUID `json:"itemId"` + CostAverage float64 `json:"costAverage"` + CostTotal float64 `json:"costTotal"` + Entries []MaintenanceEntry `json:"entries"` + } +) + +var ( + mapMaintenanceEntryErr = mapTErrFunc(mapMaintenanceEntry) + mapEachMaintenanceEntry = mapTEachFunc(mapMaintenanceEntry) +) + +func mapMaintenanceEntry(entry *ent.MaintenanceEntry) MaintenanceEntry { + return MaintenanceEntry{ + ID: entry.ID, + Date: entry.Date, + Name: entry.Name, + Description: entry.Description, + Cost: entry.Cost, + } +} + +func (r *MaintenanceEntryRepository) Create(ctx context.Context, itemID uuid.UUID, input MaintenanceEntryCreate) (MaintenanceEntry, error) { + item, err := r.db.MaintenanceEntry.Create(). + SetItemID(itemID). + SetDate(input.Date). + SetName(input.Name). + SetDescription(input.Description). + SetCost(input.Cost). + Save(ctx) + + return mapMaintenanceEntryErr(item, err) +} + +func (r *MaintenanceEntryRepository) Update(ctx context.Context, ID uuid.UUID, input MaintenanceEntryUpdate) (MaintenanceEntry, error) { + item, err := r.db.MaintenanceEntry.UpdateOneID(ID). + SetDate(input.Date). + SetName(input.Name). + SetDescription(input.Description). + SetCost(input.Cost). + Save(ctx) + + return mapMaintenanceEntryErr(item, err) +} + +func (r *MaintenanceEntryRepository) GetLog(ctx context.Context, itemID uuid.UUID) (MaintenanceLog, error) { + log := MaintenanceLog{ + ItemID: itemID, + } + + entries, err := r.db.MaintenanceEntry.Query(). + Where(maintenanceentry.ItemID(itemID)). + Order(ent.Desc(maintenanceentry.FieldDate)). + All(ctx) + + if err != nil { + return MaintenanceLog{}, err + } + + log.Entries = mapEachMaintenanceEntry(entries) + + var maybeTotal *float64 + var maybeAverage *float64 + + q := ` +SELECT + SUM(cost_total) AS total_of_totals, + AVG(cost_total) AS avg_of_averages +FROM + ( + SELECT + strftime('%m-%Y', date) AS my, + SUM(cost) AS cost_total + FROM + maintenance_entries + WHERE + item_id = ? + GROUP BY + my + )` + + row := r.db.Sql().QueryRowContext(ctx, q, itemID) + err = row.Scan(&maybeTotal, &maybeAverage) + if err != nil { + return MaintenanceLog{}, err + } + + log.CostAverage = orDefault(maybeAverage, 0) + log.CostTotal = orDefault(maybeTotal, 0) + return log, nil +} + +func (r *MaintenanceEntryRepository) Delete(ctx context.Context, ID uuid.UUID) error { + return r.db.MaintenanceEntry.DeleteOneID(ID).Exec(ctx) +} diff --git a/backend/internal/data/repo/repo_maintenance_entry_test.go b/backend/internal/data/repo/repo_maintenance_entry_test.go new file mode 100644 index 0000000..8babefc --- /dev/null +++ b/backend/internal/data/repo/repo_maintenance_entry_test.go @@ -0,0 +1,65 @@ +package repo + +import ( + "context" + "testing" + "time" + + "github.com/stretchr/testify/assert" +) + +func TestMaintenanceEntryRepository_GetLog(t *testing.T) { + item := useItems(t, 1)[0] + + // Create 10 maintenance entries for the item + created := make([]MaintenanceEntryCreate, 10) + + lastMonth := time.Now().AddDate(0, -1, 0) + thisMonth := time.Now() + + for i := 0; i < 10; i++ { + dt := lastMonth + if i%2 == 0 { + dt = thisMonth + } + + created[i] = MaintenanceEntryCreate{ + Date: dt, + Name: "Maintenance", + Description: "Maintenance description", + Cost: 10, + } + } + + for _, entry := range created { + _, err := tRepos.MaintEntry.Create(context.Background(), item.ID, entry) + if err != nil { + t.Fatalf("failed to create maintenance entry: %v", err) + } + } + + // Get the log for the item + log, err := tRepos.MaintEntry.GetLog(context.Background(), item.ID) + + if err != nil { + t.Fatalf("failed to get maintenance log: %v", err) + } + + assert.Equal(t, item.ID, log.ItemID) + assert.Equal(t, 10, len(log.Entries)) + + // Calculate the average cost + var total float64 + + for _, entry := range log.Entries { + total += entry.Cost + } + + assert.Equal(t, total, log.CostTotal, "total cost should be equal to the sum of all entries") + assert.Equal(t, total/2, log.CostAverage, "average cost should be the average of the two months") + + for _, entry := range log.Entries { + err := tRepos.MaintEntry.Delete(context.Background(), entry.ID) + assert.NoError(t, err) + } +} diff --git a/backend/internal/data/repo/repos_all.go b/backend/internal/data/repo/repos_all.go index e726e88..40748cb 100644 --- a/backend/internal/data/repo/repos_all.go +++ b/backend/internal/data/repo/repos_all.go @@ -11,8 +11,8 @@ type AllRepos struct { Labels *LabelRepository Items *ItemsRepository Docs *DocumentRepository - DocTokens *DocumentTokensRepository Attachments *AttachmentRepo + MaintEntry *MaintenanceEntryRepository } func New(db *ent.Client, root string) *AllRepos { @@ -24,7 +24,7 @@ func New(db *ent.Client, root string) *AllRepos { Labels: &LabelRepository{db}, Items: &ItemsRepository{db}, Docs: &DocumentRepository{db, root}, - DocTokens: &DocumentTokensRepository{db}, Attachments: &AttachmentRepo{db}, + MaintEntry: &MaintenanceEntryRepository{db}, } } diff --git a/backend/pkgs/hasher/password.go b/backend/pkgs/hasher/password.go index 64e88b2..1be8251 100644 --- a/backend/pkgs/hasher/password.go +++ b/backend/pkgs/hasher/password.go @@ -13,7 +13,7 @@ func init() { disableHas := os.Getenv("UNSAFE_DISABLE_PASSWORD_PROJECTION") == "yes_i_am_sure" if disableHas { - fmt.Println("WARNING: Password projection is disabled. This is unsafe in production.") + fmt.Println("WARNING: Password protection is disabled. This is unsafe in production.") enabled = false } } diff --git a/frontend/components/Base/Button.vue b/frontend/components/Base/Button.vue index 1fcb2f2..915ef51 100644 --- a/frontend/components/Base/Button.vue +++ b/frontend/components/Base/Button.vue @@ -9,6 +9,7 @@ 'btn-sm': size === 'sm', 'btn-lg': size === 'lg', }" + :style="upper ? '' : 'text-transform: none'" >
-

{{ month }} {{ year }}

-
diff --git a/frontend/components/global/DateTime.vue b/frontend/components/global/DateTime.vue index 940ee3e..4f349e3 100644 --- a/frontend/components/global/DateTime.vue +++ b/frontend/components/global/DateTime.vue @@ -3,12 +3,37 @@ diff --git a/frontend/lib/api/__test__/user/items.test.ts b/frontend/lib/api/__test__/user/items.test.ts index 7837e50..1a5f94e 100644 --- a/frontend/lib/api/__test__/user/items.test.ts +++ b/frontend/lib/api/__test__/user/items.test.ts @@ -33,6 +33,7 @@ describe("user should be able to create an item and add an attachment", () => { const [location, cleanup] = await useLocation(api); const { response, data: item } = await api.items.create({ + parentId: null, name: "test-item", labelIds: [], description: "test-description", @@ -43,7 +44,7 @@ describe("user should be able to create an item and add an attachment", () => { // Add attachment { const testFile = new Blob(["test"], { type: "text/plain" }); - const { response } = await api.items.addAttachment(item.id, testFile, "test.txt", AttachmentTypes.Attachment); + const { response } = await api.items.attachments.add(item.id, testFile, "test.txt", AttachmentTypes.Attachment); expect(response.status).toBe(201); } @@ -54,7 +55,7 @@ describe("user should be able to create an item and add an attachment", () => { expect(data.attachments).toHaveLength(1); expect(data.attachments[0].document.title).toBe("test.txt"); - const resp = await api.items.deleteAttachment(data.id, data.attachments[0].id); + const resp = await api.items.attachments.delete(data.id, data.attachments[0].id); expect(resp.response.status).toBe(204); api.items.delete(item.id); @@ -66,6 +67,7 @@ describe("user should be able to create an item and add an attachment", () => { const [location, cleanup] = await useLocation(api); const { response, data: item } = await api.items.create({ + parentId: null, name: faker.vehicle.model(), labelIds: [], description: faker.lorem.paragraph(1), @@ -82,6 +84,7 @@ describe("user should be able to create an item and add an attachment", () => { // Add fields const itemUpdate = { + parentId: null, ...item, locationId: item.location.id, labelIds: item.labels.map(l => l.id), @@ -113,4 +116,41 @@ describe("user should be able to create an item and add an attachment", () => { cleanup(); }); + + test("users should be able to create and few maintenance logs for an item", async () => { + const api = await sharedUserClient(); + const [location, cleanup] = await useLocation(api); + const { response, data: item } = await api.items.create({ + parentId: null, + name: faker.vehicle.model(), + labelIds: [], + description: faker.lorem.paragraph(1), + locationId: location.id, + }); + expect(response.status).toBe(201); + + const maintenanceEntries = []; + for (let i = 0; i < 5; i++) { + const { response, data } = await api.items.maintenance.create(item.id, { + name: faker.vehicle.model(), + description: faker.lorem.paragraph(1), + date: faker.date.past(1), + cost: faker.datatype.number(100).toString(), + }); + + expect(response.status).toBe(201); + maintenanceEntries.push(data); + } + + // Log + { + const { response, data } = await api.items.maintenance.getLog(item.id); + expect(response.status).toBe(200); + expect(data.entries).toHaveLength(maintenanceEntries.length); + expect(data.costAverage).toBeGreaterThan(0); + expect(data.costTotal).toBeGreaterThan(0); + } + + cleanup(); + }); }); diff --git a/frontend/lib/api/classes/items.ts b/frontend/lib/api/classes/items.ts index f4fb38d..8522852 100644 --- a/frontend/lib/api/classes/items.ts +++ b/frontend/lib/api/classes/items.ts @@ -1,7 +1,18 @@ import { BaseAPI, route } from "../base"; import { parseDate } from "../base/base-api"; -import { ItemAttachmentUpdate, ItemCreate, ItemOut, ItemSummary, ItemUpdate } from "../types/data-contracts"; +import { + ItemAttachmentUpdate, + ItemCreate, + ItemOut, + ItemSummary, + ItemUpdate, + MaintenanceEntry, + MaintenanceEntryCreate, + MaintenanceEntryUpdate, + MaintenanceLog, +} from "../types/data-contracts"; import { AttachmentTypes, PaginationResult } from "../types/non-generated"; +import { Requests } from "~~/lib/requests"; export type ItemsQuery = { includeArchived?: boolean; @@ -12,7 +23,65 @@ export type ItemsQuery = { q?: string; }; +export class AttachmentsAPI extends BaseAPI { + add(id: string, file: File | Blob, filename: string, type: AttachmentTypes) { + const formData = new FormData(); + formData.append("file", file); + formData.append("type", type); + formData.append("name", filename); + + return this.http.post({ + url: route(`/items/${id}/attachments`), + data: formData, + }); + } + + delete(id: string, attachmentId: string) { + return this.http.delete({ url: route(`/items/${id}/attachments/${attachmentId}`) }); + } + + update(id: string, attachmentId: string, data: ItemAttachmentUpdate) { + return this.http.put({ + url: route(`/items/${id}/attachments/${attachmentId}`), + body: data, + }); + } +} + +export class MaintenanceAPI extends BaseAPI { + getLog(itemId: string) { + return this.http.get({ url: route(`/items/${itemId}/maintenance`) }); + } + + create(itemId: string, data: MaintenanceEntryCreate) { + return this.http.post({ + url: route(`/items/${itemId}/maintenance`), + body: data, + }); + } + + delete(itemId: string, entryId: string) { + return this.http.delete({ url: route(`/items/${itemId}/maintenance/${entryId}`) }); + } + + update(itemId: string, entryId: string, data: MaintenanceEntryUpdate) { + return this.http.put({ + url: route(`/items/${itemId}/maintenance/${entryId}`), + body: data, + }); + } +} + export class ItemsApi extends BaseAPI { + attachments: AttachmentsAPI; + maintenance: MaintenanceAPI; + + constructor(http: Requests, token: string) { + super(http, token); + this.attachments = new AttachmentsAPI(http); + this.maintenance = new MaintenanceAPI(http); + } + getAll(q: ItemsQuery = {}) { return this.http.get>({ url: route("/items", q) }); } @@ -59,27 +128,4 @@ export class ItemsApi extends BaseAPI { data: formData, }); } - - addAttachment(id: string, file: File | Blob, filename: string, type: AttachmentTypes) { - const formData = new FormData(); - formData.append("file", file); - formData.append("type", type); - formData.append("name", filename); - - return this.http.post({ - url: route(`/items/${id}/attachments`), - data: formData, - }); - } - - async deleteAttachment(id: string, attachmentId: string) { - return await this.http.delete({ url: route(`/items/${id}/attachments/${attachmentId}`) }); - } - - async updateAttachment(id: string, attachmentId: string, data: ItemAttachmentUpdate) { - return await this.http.put({ - url: route(`/items/${id}/attachments/${attachmentId}`), - body: data, - }); - } } diff --git a/frontend/lib/api/types/data-contracts.ts b/frontend/lib/api/types/data-contracts.ts index 09f10e7..e313175 100644 --- a/frontend/lib/api/types/data-contracts.ts +++ b/frontend/lib/api/types/data-contracts.ts @@ -54,7 +54,6 @@ export interface ItemAttachmentUpdate { export interface ItemCreate { description: string; labelIds: string[]; - /** Edges */ locationId: string; name: string; @@ -73,8 +72,7 @@ export interface ItemField { export interface ItemOut { archived: boolean; - - /** @example 0 */ + /** @example "0" */ assetId: string; attachments: ItemAttachment[]; children: ItemSummary[]; @@ -84,33 +82,26 @@ export interface ItemOut { id: string; insured: boolean; labels: LabelSummary[]; - /** Warranty */ lifetimeWarranty: boolean; - /** Edges */ location: LocationSummary | null; manufacturer: string; modelNumber: string; name: string; - /** Extras */ notes: string; parent: ItemSummary | null; purchaseFrom: string; - - /** @example 0 */ + /** @example "0" */ purchasePrice: string; - /** Purchase */ purchaseTime: Date; quantity: number; serialNumber: string; soldNotes: string; - - /** @example 0 */ + /** @example "0" */ soldPrice: string; - /** Sold */ soldTime: Date; soldTo: string; @@ -126,7 +117,6 @@ export interface ItemSummary { id: string; insured: boolean; labels: LabelSummary[]; - /** Edges */ location: LocationSummary | null; name: string; @@ -142,35 +132,27 @@ export interface ItemUpdate { id: string; insured: boolean; labelIds: string[]; - /** Warranty */ lifetimeWarranty: boolean; - /** Edges */ locationId: string; manufacturer: string; modelNumber: string; name: string; - /** Extras */ notes: string; parentId: string | null; purchaseFrom: string; - - /** @example 0 */ + /** @example "0" */ purchasePrice: string; - /** Purchase */ purchaseTime: Date; quantity: number; - /** Identifications */ serialNumber: string; soldNotes: string; - - /** @example 0 */ + /** @example "0" */ soldPrice: string; - /** Sold */ soldTime: Date; soldTo: string; @@ -241,6 +223,38 @@ export interface LocationUpdate { parentId: string | null; } +export interface MaintenanceEntry { + /** @example "0" */ + cost: string; + date: Date; + description: string; + id: string; + name: string; +} + +export interface MaintenanceEntryCreate { + /** @example "0" */ + cost: string; + date: Date; + description: string; + name: string; +} + +export interface MaintenanceEntryUpdate { + /** @example "0" */ + cost: string; + date: Date; + description: string; + name: string; +} + +export interface MaintenanceLog { + costAverage: number; + costTotal: number; + entries: MaintenanceEntry[]; + itemId: string; +} + export interface PaginationResultRepoItemSummary { items: ItemSummary[]; page: number; @@ -278,7 +292,7 @@ export interface ValueOverTime { } export interface ValueOverTimeEntry { - date: string; + date: Date; name: string; value: number; } diff --git a/frontend/nuxt.config.ts b/frontend/nuxt.config.ts index 6019d26..6f4638b 100644 --- a/frontend/nuxt.config.ts +++ b/frontend/nuxt.config.ts @@ -1,20 +1,16 @@ -import { defineNuxtConfig } from "nuxt"; +import { defineNuxtConfig } from "nuxt/config"; // https://v3.nuxtjs.org/api/configuration/nuxt.config export default defineNuxtConfig({ - target: "static", ssr: false, modules: ["@nuxtjs/tailwindcss", "@pinia/nuxt", "@vueuse/nuxt"], - meta: { - title: "Homebox", - link: [{ rel: "icon", type: "image/x-icon", href: "/favicon.svg" }], - }, - vite: { - server: { - proxy: { - "/api": "http://localhost:7745", - }, + nitro: { + devProxy: { + "/api": { + target: "http://localhost:7745/api", + changeOrigin: true, + } }, - plugins: [], }, + plugins: [], }); diff --git a/frontend/package.json b/frontend/package.json index a06db47..973e383 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -21,7 +21,7 @@ "eslint-plugin-prettier": "^4.2.1", "eslint-plugin-vue": "^9.4.0", "isomorphic-fetch": "^3.0.0", - "nuxt": "3.0.0-rc.11", + "nuxt": "3.0.0", "prettier": "^2.7.1", "typescript": "^4.8.3", "vite-plugin-eslint": "^1.8.1", @@ -29,7 +29,7 @@ }, "dependencies": { "@iconify/vue": "^3.2.1", - "@nuxtjs/tailwindcss": "^5.3.2", + "@nuxtjs/tailwindcss": "^6.1.3", "@pinia/nuxt": "^0.4.1", "@tailwindcss/aspect-ratio": "^0.4.0", "@tailwindcss/forms": "^0.5.2", diff --git a/frontend/pages/home.vue b/frontend/pages/home.vue index 92edee3..e8a52e7 100644 --- a/frontend/pages/home.vue +++ b/frontend/pages/home.vue @@ -46,7 +46,7 @@ const importDialog = ref(false); const importCsv = ref(null); const importLoading = ref(false); - const importRef = ref(null); + const importRef = ref(); whenever( () => !importDialog.value, () => { @@ -120,7 +120,7 @@
- +
diff --git a/frontend/pages/item/[id]/index/log.vue b/frontend/pages/item/[id]/index/log.vue index ab701db..b0f06b2 100644 --- a/frontend/pages/item/[id]/index/log.vue +++ b/frontend/pages/item/[id]/index/log.vue @@ -1,5 +1,6 @@ @@ -95,16 +112,32 @@ -
- - - Log Maintenance - -
-
-
+
+
+ + + Back + + + + Log Maintenance + +
+
+ +
+
@@ -137,37 +170,6 @@
-
-
-
-
{{ stat.title }}
-
{{ stat.value }}
-
{{ stat.subtitle }}
-
-
-
- - diff --git a/frontend/pages/items.vue b/frontend/pages/items.vue index 317a0f0..34d453d 100644 --- a/frontend/pages/items.vue +++ b/frontend/pages/items.vue @@ -135,7 +135,7 @@
diff --git a/frontend/pages/profile.vue b/frontend/pages/profile.vue index f2f32fe..fe3c3ef 100644 --- a/frontend/pages/profile.vue +++ b/frontend/pages/profile.vue @@ -22,8 +22,6 @@ if (group.value) { group.value.currency = currency.value.code; } - - console.log(group.value); }); const currencyExample = computed(() => { diff --git a/frontend/pnpm-lock.yaml b/frontend/pnpm-lock.yaml index 8013d1b..1de5c6f 100644 --- a/frontend/pnpm-lock.yaml +++ b/frontend/pnpm-lock.yaml @@ -13,6 +13,7 @@ specifiers: '@typescript-eslint/parser': ^5.36.2 '@vueuse/nuxt': ^9.1.1 autoprefixer: ^10.4.8 + chart.js: ^4.0.1 daisyui: ^2.24.0 dompurify: ^2.4.1 eslint: ^8.23.0 @@ -30,6 +31,7 @@ specifiers: vite-plugin-eslint: ^1.8.1 vitest: ^0.22.1 vue: ^3.2.38 + vue-chartjs: ^4.1.2 dependencies: '@iconify/vue': 3.2.1_vue@3.2.45 @@ -40,6 +42,7 @@ dependencies: '@tailwindcss/typography': 0.5.8_tailwindcss@3.2.4 '@vueuse/nuxt': 9.6.0_nuxt@3.0.0+vue@3.2.45 autoprefixer: 10.4.13_postcss@8.4.19 + chart.js: 4.0.1 daisyui: 2.43.0_2lwn2upnx27dqeg6hqdu7sq75m dompurify: 2.4.1 markdown-it: 13.0.1 @@ -47,6 +50,7 @@ dependencies: postcss: 8.4.19 tailwindcss: 3.2.4_postcss@8.4.19 vue: 3.2.45 + vue-chartjs: 4.1.2_chart.js@4.0.1+vue@3.2.45 devDependencies: '@faker-js/faker': 7.6.0 @@ -1750,6 +1754,11 @@ packages: /chardet/0.7.0: resolution: {integrity: sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==} + /chart.js/4.0.1: + resolution: {integrity: sha512-5/8/9eBivwBZK81mKvmIwTb2Pmw4D/5h1RK9fBWZLLZ8mCJ+kfYNmV9rMrGoa5Hgy2/wVDBMLSUDudul2/9ihA==} + engines: {pnpm: ^7.0.0} + dev: false + /check-error/1.0.2: resolution: {integrity: sha512-BrgHpW9NURQgzoNyjfq0Wu6VFO6D7IZEmJNdtgNqpzGG8RuNFHt2jQxWlAs4HMe119chBnv+34syEZtc6IhLtA==} dev: true @@ -6412,6 +6421,16 @@ packages: dependencies: ufo: 1.0.1 + /vue-chartjs/4.1.2_chart.js@4.0.1+vue@3.2.45: + resolution: {integrity: sha512-QSggYjeFv/L4jFSBQpX8NzrAvX0B+Ha6nDgxkTG8tEXxYOOTwKI4phRLe+B4f+REnkmg7hgPY24R0cixZJyXBg==} + peerDependencies: + chart.js: ^3.7.0 + vue: ^3.0.0-0 || ^2.6.0 + dependencies: + chart.js: 4.0.1 + vue: 3.2.45 + dev: false + /vue-demi/0.13.11_vue@3.2.45: resolution: {integrity: sha512-IR8HoEEGM65YY3ZJYAjMlKygDQn25D5ajNFNoKh9RSDMQtlzCxtfQjdQgv9jjK+m3377SsJXY8ysq8kLCZL25A==} engines: {node: '>=12'} diff --git a/frontend/tailwind.config.js b/frontend/tailwind.config.js index fc78c6c..acb95bb 100644 --- a/frontend/tailwind.config.js +++ b/frontend/tailwind.config.js @@ -4,6 +4,52 @@ module.exports = { theme: { extend: {}, }, + daisyui: { + themes: [ + { + homebox: { + primary: "#5C7F67", + secondary: "#ECF4E7", + accent: "#FFDA56", + neutral: "#2C2E27", + "base-100": "#FFFFFF", + info: "#3ABFF8", + success: "#36D399", + warning: "#FBBD23", + error: "#F87272", + }, + }, + "light", + "dark", + "cupcake", + "bumblebee", + "emerald", + "corporate", + "synthwave", + "retro", + "cyberpunk", + "valentine", + "halloween", + "garden", + "forest", + "aqua", + "lofi", + "pastel", + "fantasy", + "wireframe", + "black", + "luxury", + "dracula", + "cmyk", + "autumn", + "business", + "acid", + "lemonade", + "night", + "coffee", + "winter", + ], + }, variants: { extend: {}, }, From 58d6f9a28c13a060fcad19385abe2712825cea2e Mon Sep 17 00:00:00 2001 From: Hayden <64056131+hay-kot@users.noreply.github.com> Date: Thu, 29 Dec 2022 21:18:49 -0800 Subject: [PATCH 020/350] Fix/mobile-layouts (#192) * partial fix for location card spacing * update header on mobile --- frontend/components/Location/Card.vue | 9 ++++++++- frontend/layouts/default.vue | 23 ++++++++++++++++------- 2 files changed, 24 insertions(+), 8 deletions(-) diff --git a/frontend/components/Location/Card.vue b/frontend/components/Location/Card.vue index 335a0f9..d0b6543 100644 --- a/frontend/components/Location/Card.vue +++ b/frontend/components/Location/Card.vue @@ -19,7 +19,14 @@ {{ location.name }} - {{ count }} + + {{ count }} + diff --git a/frontend/layouts/default.vue b/frontend/layouts/default.vue index 8dff148..ef1a8ca 100644 --- a/frontend/layouts/default.vue +++ b/frontend/layouts/default.vue @@ -13,14 +13,23 @@
-
- - - +
+
From 891d41b75f759348d9cae3839134656894e2922c Mon Sep 17 00:00:00 2001 From: Hayden <64056131+hay-kot@users.noreply.github.com> Date: Sun, 1 Jan 2023 13:50:48 -0800 Subject: [PATCH 021/350] feat: new-card-design (#196) * card option 1 * UI updates for item card * fix test error * fix pagination issues on backend * add integer support * remove date from cards * implement pagination for search page * resolve search state problems * other fixes * fix broken datetime * attempt to fix scroll behavior --- backend/app/api/handlers/v1/v1_ctrl_items.go | 7 + backend/go.mod | 8 +- backend/go.sum | 42 ++++-- backend/internal/data/repo/repo_items.go | 20 +-- .../data/repo/repo_maintenance_entry_test.go | 23 +++- frontend/components/Chart/Line.vue | 1 - frontend/components/Item/Card.vue | 80 ++++++++--- frontend/components/global/DateTime.vue | 71 ++-------- frontend/composables/use-formatters.ts | 60 +++++++- frontend/composables/use-route-params.ts | 38 +++-- frontend/layouts/default.vue | 3 + frontend/lib/api/base/base-api.ts | 10 ++ frontend/nuxt.config.ts | 1 - frontend/package.json | 4 +- frontend/pages/items.vue | 130 +++++++++++++++--- frontend/pages/label/[id].vue | 2 +- frontend/pages/location/[id].vue | 2 +- frontend/plugins/scroll.client.ts | 7 + frontend/pnpm-lock.yaml | 26 ++++ 19 files changed, 393 insertions(+), 142 deletions(-) create mode 100644 frontend/plugins/scroll.client.ts diff --git a/backend/app/api/handlers/v1/v1_ctrl_items.go b/backend/app/api/handlers/v1/v1_ctrl_items.go index ea961f3..d7be19d 100644 --- a/backend/app/api/handlers/v1/v1_ctrl_items.go +++ b/backend/app/api/handlers/v1/v1_ctrl_items.go @@ -1,6 +1,8 @@ package v1 import ( + "database/sql" + "errors" "net/http" "github.com/hay-kot/homebox/backend/internal/core/services" @@ -41,6 +43,11 @@ func (ctrl *V1Controller) HandleItemsGetAll() server.HandlerFunc { ctx := services.NewContext(r.Context()) items, err := ctrl.repo.Items.QueryByGroup(ctx, ctx.GID, extractQuery(r)) if err != nil { + if errors.Is(err, sql.ErrNoRows) { + return server.Respond(w, http.StatusOK, repo.PaginationResult[repo.ItemSummary]{ + Items: []repo.ItemSummary{}, + }) + } log.Err(err).Msg("failed to get items") return validate.NewRequestError(err, http.StatusInternalServerError) } diff --git a/backend/go.mod b/backend/go.mod index 3f1f50f..275b010 100644 --- a/backend/go.mod +++ b/backend/go.mod @@ -35,15 +35,15 @@ require ( github.com/leodido/go-urn v1.2.1 // indirect github.com/mailru/easyjson v0.7.7 // indirect github.com/mattn/go-colorable v0.1.13 // indirect - github.com/mattn/go-isatty v0.0.16 // indirect + github.com/mattn/go-isatty v0.0.17 // indirect github.com/mitchellh/go-wordwrap v1.0.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect - github.com/swaggo/files v0.0.0-20220728132757-551d4a08d97a // indirect + github.com/swaggo/files v1.0.0 // indirect github.com/zclconf/go-cty v1.12.1 // indirect golang.org/x/mod v0.7.0 // indirect - golang.org/x/net v0.3.0 // indirect + golang.org/x/net v0.4.0 // indirect golang.org/x/sys v0.3.0 // indirect golang.org/x/text v0.5.0 // indirect - golang.org/x/tools v0.3.0 // indirect + golang.org/x/tools v0.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/backend/go.sum b/backend/go.sum index a5ad319..243b47a 100644 --- a/backend/go.sum +++ b/backend/go.sum @@ -70,13 +70,16 @@ github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= 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-isatty v0.0.17 h1:BTarxUcIeDqL27Mc+vyvdWYSL28zpIhv3RoTdsLMPng= +github.com/mattn/go-isatty v0.0.17/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 +91,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= @@ -98,40 +103,61 @@ github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/ github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= -github.com/swaggo/files v0.0.0-20220728132757-551d4a08d97a h1:kAe4YSu0O0UFn1DowNo2MY5p6xzqtJ/wQ7LZynSvGaY= -github.com/swaggo/files v0.0.0-20220728132757-551d4a08d97a/go.mod h1:lKJPbtWzJ9JhsTN1k1gZgleJWY/cqq0psdoMmaThG3w= +github.com/swaggo/files v1.0.0 h1:1gGXVIeUFCS/dta17rnP0iOpr6CXFwKD7EO5ID233e4= +github.com/swaggo/files v1.0.0/go.mod h1:N59U6URJLyU1PQgFqPM7wXLMhJx7QAolnvfQkqO13kc= github.com/swaggo/http-swagger v1.3.3 h1:Hu5Z0L9ssyBLofaama21iYaF2VbWyA8jdohaaCGpHsc= github.com/swaggo/http-swagger v1.3.3/go.mod h1:sE+4PjD89IxMPm77FnkDz0sdO+p5lbXzrVWT6OTVVGo= github.com/swaggo/swag v1.8.9 h1:kHtaBe/Ob9AZzAANfcn5c6RyCke9gG9QpH0jky0I/sA= github.com/swaggo/swag v1.8.9/go.mod h1:ezQVUUhly8dludpVk+/PuwJWvLLanB13ygV5Pr9enSk= +github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= github.com/zclconf/go-cty v1.12.1 h1:PcupnljUm9EIvbgSHQnHhUr3fO6oFmkOrvs2BAFNXXY= github.com/zclconf/go-cty v1.12.1/go.mod h1:s9IfD1LK5ccNMSWCVFCE2rJfHiZgi7JijgeWIMfhLvA= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.4.0 h1:UVQgzMY87xqpKNgb+kDsll2Igd33HszWHFLmpaRMq/8= golang.org/x/crypto v0.4.0/go.mod h1:3quD/ATkf6oY+rnes5c3ExXTbLc8mueNue5/DoinL80= +golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/mod v0.7.0 h1:LapD9S96VoQRhi/GrNTqeBJFrUjs5UHCAtTlgwA5oZA= golang.org/x/mod v0.7.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= -golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.3.0 h1:VWL6FNY2bEEmsGVKabSlHu5Irp34xmMRoqb/9lF9lxk= -golang.org/x/net v0.3.0/go.mod h1:MBQ8lrhLObU/6UmLb4fmbmk5OcyYmqtbGd/9yIeKjEE= +golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY= +golang.org/x/net v0.4.0 h1:Q5QPcMlvfxFTAPV0+07Xz/MpK9NTXu2VDUuy0FeMfaU= +golang.org/x/net v0.4.0/go.mod h1:MBQ8lrhLObU/6UmLb4fmbmk5OcyYmqtbGd/9yIeKjEE= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.3.0 h1:w8ZOecv6NaNa/zC8944JTU3vz4u6Lagfk4RPQxv92NQ= golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.5.0 h1:OLmvp0KP+FVG99Ct/qFiL/Fhk4zp4QQnZ7b2U+5piUM= golang.org/x/text v0.5.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.3.0 h1:SrNbZl6ECOS1qFzgTdQfWXZM9XBkiA6tkFrH9YSTPHM= -golang.org/x/tools v0.3.0/go.mod h1:/rWhSS2+zyEVwoJf8YAX6L2f0ntZ7Kn/mGgAWcipA5k= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= +golang.org/x/tools v0.4.0 h1:7mTAgkunk3fr4GAloyyCasadO6h9zSsQZbwvcaIciV4= +golang.org/x/tools v0.4.0/go.mod h1:UE5sM2OK9E/d67R0ANs2xJizIymRP5gJU295PvKXxjQ= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= diff --git a/backend/internal/data/repo/repo_items.go b/backend/internal/data/repo/repo_items.go index d8a3904..e22f30a 100644 --- a/backend/internal/data/repo/repo_items.go +++ b/backend/internal/data/repo/repo_items.go @@ -326,23 +326,23 @@ func (e *ItemsRepository) QueryByGroup(ctx context.Context, gid uuid.UUID, q Ite ) } + count, err := qb.Count(ctx) + if err != nil { + return PaginationResult[ItemSummary]{}, err + } + + qb = qb.Order(ent.Asc(item.FieldName)). + WithLabel(). + WithLocation() + if q.Page != -1 || q.PageSize != -1 { qb = qb. Offset(calculateOffset(q.Page, q.PageSize)). Limit(q.PageSize) } - items, err := mapItemsSummaryErr( - qb.Order(ent.Asc(item.FieldName)). - WithLabel(). - WithLocation(). - All(ctx), - ) - if err != nil { - return PaginationResult[ItemSummary]{}, err - } + items, err := mapItemsSummaryErr(qb.All(ctx)) - count, err := qb.Count(ctx) if err != nil { return PaginationResult[ItemSummary]{}, err } diff --git a/backend/internal/data/repo/repo_maintenance_entry_test.go b/backend/internal/data/repo/repo_maintenance_entry_test.go index 8babefc..e3df3d0 100644 --- a/backend/internal/data/repo/repo_maintenance_entry_test.go +++ b/backend/internal/data/repo/repo_maintenance_entry_test.go @@ -8,14 +8,35 @@ import ( "github.com/stretchr/testify/assert" ) +// get the previous month from the current month, accounts for errors when run +// near the beginning or end of the month/year +func getPrevMonth(now time.Time) time.Time { + t := now.AddDate(0, -1, 0) + + // avoid infinite loop + max := 15 + for t.Month() == now.Month() { + println("month is the same") + t = t.AddDate(0, 0, -1) + println(t.String()) + + max-- + if max == 0 { + panic("max exceeded") + } + } + + return t +} + func TestMaintenanceEntryRepository_GetLog(t *testing.T) { item := useItems(t, 1)[0] // Create 10 maintenance entries for the item created := make([]MaintenanceEntryCreate, 10) - lastMonth := time.Now().AddDate(0, -1, 0) thisMonth := time.Now() + lastMonth := getPrevMonth(thisMonth) for i := 0; i < 10; i++ { dt := lastMonth diff --git a/frontend/components/Chart/Line.vue b/frontend/components/Chart/Line.vue index c74e759..c36ef93 100644 --- a/frontend/components/Chart/Line.vue +++ b/frontend/components/Chart/Line.vue @@ -56,7 +56,6 @@ const calcWidth = ref(0); function resize() { - console.log("resize", el.value?.offsetHeight, el.value?.offsetWidth); calcHeight.value = el.value?.offsetHeight || 0; calcWidth.value = el.value?.offsetWidth || 0; } diff --git a/frontend/components/Item/Card.vue b/frontend/components/Item/Card.vue index 7dfb62a..96b3b04 100644 --- a/frontend/components/Item/Card.vue +++ b/frontend/components/Item/Card.vue @@ -1,22 +1,38 @@ + + + + diff --git a/frontend/lib/api/types/data-contracts.ts b/frontend/lib/api/types/data-contracts.ts index 7e4fd54..a36df88 100644 --- a/frontend/lib/api/types/data-contracts.ts +++ b/frontend/lib/api/types/data-contracts.ts @@ -96,7 +96,7 @@ export interface ItemOut { /** @example "0" */ purchasePrice: string; /** Purchase */ - purchaseTime: Date; + purchaseTime: string; quantity: number; serialNumber: string; soldNotes: string; @@ -148,7 +148,7 @@ export interface ItemUpdate { /** @example "0" */ purchasePrice: string; /** Purchase */ - purchaseTime: Date; + purchaseTime: string; quantity: number; /** Identifications */ serialNumber: string; @@ -228,7 +228,7 @@ export interface LocationUpdate { export interface MaintenanceEntry { /** @example "0" */ cost: string; - date: Date; + date: string; description: string; id: string; name: string; @@ -237,7 +237,7 @@ export interface MaintenanceEntry { export interface MaintenanceEntryCreate { /** @example "0" */ cost: string; - date: Date; + date: string; description: string; name: string; } @@ -245,7 +245,7 @@ export interface MaintenanceEntryCreate { export interface MaintenanceEntryUpdate { /** @example "0" */ cost: string; - date: Date; + date: string; description: string; name: string; } @@ -257,7 +257,7 @@ export interface MaintenanceLog { itemId: string; } -export interface PaginationResultRepoItemSummary { +export interface PaginationResultItemSummary { items: ItemSummary[]; page: number; pageSize: number; @@ -294,7 +294,7 @@ export interface ValueOverTime { } export interface ValueOverTimeEntry { - date: Date; + date: string; name: string; value: number; } @@ -347,13 +347,13 @@ export interface EnsureAssetIDResult { } export interface GroupInvitation { - expiresAt: Date; + expiresAt: string; token: string; uses: number; } export interface GroupInvitationCreate { - expiresAt: Date; + expiresAt: string; uses: number; } @@ -363,6 +363,6 @@ export interface ItemAttachmentToken { export interface TokenResponse { attachmentToken: string; - expiresAt: Date; + expiresAt: string; token: string; } diff --git a/frontend/pages/item/[id]/index.vue b/frontend/pages/item/[id]/index.vue index 4bcef2c..42c4995 100644 --- a/frontend/pages/item/[id]/index.vue +++ b/frontend/pages/item/[id]/index.vue @@ -30,6 +30,15 @@ refresh(); }); + const lastRoute = ref(route.fullPath); + watchEffect(() => { + if (lastRoute.value.endsWith("edit")) { + refresh(); + } + + lastRoute.value = route.fullPath; + }); + type FilteredAttachments = { attachments: ItemAttachment[]; warranty: ItemAttachment[]; @@ -325,6 +334,30 @@ onClickOutside(refDialogBody, () => { closeDialog(); }); + + const currentPath = computed(() => { + return route.path; + }); + + const tabs = computed(() => { + return [ + { + id: "details", + name: "Details", + to: `/item/${itemId.value}`, + }, + { + id: "log", + name: "Log", + to: `/item/${itemId.value}/log`, + }, + { + id: "edit", + name: "Edit", + to: `/item/${itemId.value}/edit`, + }, + ]; + }); diff --git a/scripts/process-types/main.go b/scripts/process-types/main.go index 59c2530..06e3ec7 100644 --- a/scripts/process-types/main.go +++ b/scripts/process-types/main.go @@ -2,7 +2,6 @@ package main import ( "fmt" - "io/ioutil" "os" "regexp" ) @@ -31,7 +30,7 @@ func main() { } text := "/* post-processed by ./scripts/process-types.go */\n" - data, err := ioutil.ReadFile(path) + data, err := os.ReadFile(path) if err != nil { fmt.Println(err) os.Exit(1) @@ -63,7 +62,7 @@ func main() { text = regex.ReplaceAllString(text, replace) } - err = ioutil.WriteFile(path, []byte(text), 0644) + err = os.WriteFile(path, []byte(text), 0644) if err != nil { fmt.Println(err) os.Exit(1) From 91d0c588d906e6008ca0c6ff993f8fd2fd43ee90 Mon Sep 17 00:00:00 2001 From: Hayden <64056131+hay-kot@users.noreply.github.com> Date: Sat, 21 Jan 2023 21:15:23 -0900 Subject: [PATCH 037/350] refactor: refactor item page UI (#235) * fix generated types * fix tailwind auto-complete * force lowercase buttons * add title and change style for items page * add copy button support for item details * empty state for log * fix duplicate padding * add option for create without closing the current dialog. * hide purchase price is not set * invert toggle for edit mode * update styles on item cards * add edit support for maintenance logs --- .vscode/settings.json | 9 +++ frontend/assets/css/main.css | 4 + frontend/components/Item/Card.vue | 18 ++--- frontend/components/Item/CreateModal.vue | 51 ++++++++----- frontend/components/global/CopyText.vue | 24 +++++- .../global/DetailsSection/DetailsSection.vue | 14 +++- .../components/global/DetailsSection/types.ts | 1 + frontend/composables/use-preferences.ts | 6 +- frontend/lib/api/types/data-contracts.ts | 14 ++-- frontend/pages/item/[id]/index.vue | 27 +++++-- frontend/pages/item/[id]/index/edit.vue | 20 ++--- frontend/pages/item/[id]/index/log.vue | 73 ++++++++++++++++++- frontend/pages/profile.vue | 4 +- 13 files changed, 197 insertions(+), 68 deletions(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index 5080f25..d0ae55d 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -21,4 +21,13 @@ "editor.defaultFormatter": "dbaeumer.vscode-eslint" }, "eslint.format.enable": true, + "css.validate": false, + "tailwindCSS.includeLanguages": { + "vue": "html", + "vue-html": "html" + }, + "editor.quickSuggestions": { + "strings": true + }, + "tailwindCSS.experimental.configFile": "./frontend/tailwind.config.js" } diff --git a/frontend/assets/css/main.css b/frontend/assets/css/main.css index a3c199c..d83faf6 100644 --- a/frontend/assets/css/main.css +++ b/frontend/assets/css/main.css @@ -1,3 +1,7 @@ .text-no-transform { text-transform: none !important; +} + +.btn { + text-transform: none !important; } \ No newline at end of file diff --git a/frontend/components/Item/Card.vue b/frontend/components/Item/Card.vue index 6ca1704..c0b5b88 100644 --- a/frontend/components/Item/Card.vue +++ b/frontend/components/Item/Card.vue @@ -1,22 +1,18 @@