Compare commits

..

3 Commits

Author SHA1 Message Date
Hayden
e4ebceeeb6 remove charts.js from libs 2023-12-01 11:55:32 -06:00
Hayden
51e3ad11c4 automatically detect image types and set primary photo if first 2023-12-01 11:45:20 -06:00
Hayden
3f455cb574 fix error when item doesn't have photo attachment 2023-12-01 11:36:29 -06:00
79 changed files with 471 additions and 1154 deletions

View File

@@ -10,7 +10,7 @@ jobs:
- uses: actions/checkout@v4
- name: Set up Go
uses: actions/setup-go@v5
uses: actions/setup-go@v4
with:
go-version: "1.20"

View File

@@ -44,7 +44,7 @@ jobs:
repo-token: ${{ secrets.GITHUB_TOKEN }}
- name: Set up Go
uses: actions/setup-go@v5
uses: actions/setup-go@v4
with:
go-version: "1.20"

View File

@@ -22,7 +22,7 @@ jobs:
steps:
- uses: actions/checkout@v4
- name: Set up Go
uses: actions/setup-go@v5
uses: actions/setup-go@v4
with:
go-version: "1.20"

View File

@@ -27,7 +27,7 @@ jobs:
fetch-depth: 0
- name: Set up Go
uses: actions/setup-go@v5
uses: actions/setup-go@v4
- uses: pnpm/action-setup@v2
with:

View File

@@ -16,7 +16,7 @@
"editor.formatOnSave": false,
"editor.defaultFormatter": "dbaeumer.vscode-eslint",
"editor.codeActionsOnSave": {
"source.fixAll.eslint": "explicit"
"source.fixAll.eslint": true
},
"[typescript]": {
"editor.defaultFormatter": "dbaeumer.vscode-eslint"

View File

@@ -32,7 +32,7 @@ FROM alpine:latest
ENV HBOX_MODE=production
ENV HBOX_STORAGE_DATA=/data/
ENV HBOX_STORAGE_SQLITE_URL=/data/homebox.db?_pragma=busy_timeout=2000&_pragma=journal_mode=WAL&_fk=1
ENV HBOX_STORAGE_SQLITE_URL=/data/homebox.db?_fk=1
RUN apk --no-cache add ca-certificates
RUN mkdir /app

View File

@@ -1,73 +0,0 @@
run:
timeout: 10m
skip-dirs:
- internal/data/ent.*
linters-settings:
goconst:
min-len: 5
min-occurrences: 5
exhaustive:
default-signifies-exhaustive: true
revive:
ignore-generated-header: false
severity: warning
confidence: 3
depguard:
rules:
main:
deny:
- pkg: io/util
desc: |
Deprecated: As of Go 1.16, the same functionality is now provided by
package io or package os, and those implementations should be
preferred in new code. See the specific function documentation for
details.
gocritic:
enabled-checks:
- ruleguard
testifylint:
enable-all: true
tagalign:
order:
- json
- schema
- yaml
- yml
- toml
- validate
linters:
disable-all: true
enable:
- asciicheck
- bodyclose
- depguard
- dogsled
- errcheck
- errorlint
- exhaustive
- exportloopref
- gochecknoinits
- goconst
- gocritic
- gocyclo
- goprintffuncname
- gosimple
- govet
- ineffassign
- misspell
- nakedret
- revive
- staticcheck
- stylecheck
- tagalign
- testifylint
- typecheck
- typecheck
- unconvert
- unused
- whitespace
- zerologlint
- sqlclosecheck
issues:
exclude-use-default: false
fix: true

View File

@@ -15,7 +15,7 @@ func (a *app) SetupDemo() {
,Office,IOT;Home Assistant; Z-Wave,1,Zooz 110v Power Switch,"Zooz Z-Wave Plus Power Switch ZEN15 for 110V AC Units, Sump Pumps, Humidifiers, and More",,,ZEN15,Zooz,,Amazon,39.95,10/13/2021,,,,,,,
,Downstairs,IOT;Home Assistant; Z-Wave,1,Ecolink Z-Wave PIR Motion Sensor,"Ecolink Z-Wave PIR Motion Detector Pet Immune, White (PIRZWAVE2.5-ECO)",,,PIRZWAVE2.5-ECO,Ecolink,,Amazon,35.58,10/21/2020,,,,,,,
,Entry,IOT;Home Assistant; Z-Wave,1,Yale Security Touchscreen Deadbolt,"Yale Security YRD226-ZW2-619 YRD226ZW2619 Touchscreen Deadbolt, Satin Nickel",,,YRD226ZW2619,Yale,,Amazon,120.39,10/14/2020,,,,,,,
,Kitchen,IOT;Home Assistant; Z-Wave,1,Smart Rocker Light Dimmer,"UltraPro Z-Wave Smart Rocker Light Dimmer with QuickFit and SimpleWire, 3-Way Ready, Compatible with Alexa, Google Assistant, ZWave Hub Required, Repeater/Range Extender, White Paddle Only, 39351",,,39351,Honeywell,,Amazon,65.98,09/30/0202,,,,,,,
,Kitchen,IOT;Home Assistant; Z-Wave,1,Smart Rocker Light Dimmer,"UltraPro Z-Wave Smart Rocker Light Dimmer with QuickFit and SimpleWire, 3-Way Ready, Compatible with Alexa, Google Assistant, ZWave Hub Required, Repeater/Range Extender, White Paddle Only, 39351",,,39351,Honeywell,,Amazon,65.98,09/30/0202,,,,,,,
`
registration := services.UserRegistration{

View File

@@ -1,4 +1,3 @@
// Package debughandlers provides handlers for debugging.
package debughandlers
import (

View File

@@ -1,4 +1,3 @@
// Package v1 provides the API handlers for version 1 of the API.
package v1
import (
@@ -75,7 +74,7 @@ type (
BuildTime string `json:"buildTime"`
}
APISummary struct {
ApiSummary struct {
Healthy bool `json:"health"`
Versions []string `json:"versions"`
Title string `json:"title"`
@@ -86,7 +85,7 @@ type (
}
)
func BaseURLFunc(prefix string) func(s string) string {
func BaseUrlFunc(prefix string) func(s string) string {
return func(s string) string {
return prefix + "/v1" + s
}
@@ -116,7 +115,7 @@ func NewControllerV1(svc *services.AllServices, repos *repo.AllRepos, bus *event
// @Router /v1/status [GET]
func (ctrl *V1Controller) HandleBase(ready ReadyFunc, build Build) errchain.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) error {
return server.JSON(w, http.StatusOK, APISummary{
return server.JSON(w, http.StatusOK, ApiSummary{
Healthy: ready(),
Title: "Homebox",
Message: "Track, Manage, and Organize your Things",

View File

@@ -27,10 +27,10 @@ import (
func (ctrl *V1Controller) HandleAssetGet() errchain.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) error {
ctx := services.NewContext(r.Context())
assetIDParam := chi.URLParam(r, "id")
assetIDParam = strings.ReplaceAll(assetIDParam, "-", "") // Remove dashes
assetIdParam := chi.URLParam(r, "id")
assetIdParam = strings.ReplaceAll(assetIdParam, "-", "") // Remove dashes
// Convert the asset ID to an int64
assetID, err := strconv.ParseInt(assetIDParam, 10, 64)
assetId, err := strconv.ParseInt(assetIdParam, 10, 64)
if err != nil {
return err
}
@@ -52,7 +52,7 @@ func (ctrl *V1Controller) HandleAssetGet() errchain.HandlerFunc {
}
}
items, err := ctrl.repo.Items.QueryByAssetID(r.Context(), ctx.GID, repo.AssetID(assetID), int(page), int(pageSize))
items, err := ctrl.repo.Items.QueryByAssetID(r.Context(), ctx.GID, repo.AssetID(assetId), int(page), int(pageSize))
if err != nil {
log.Err(err).Msg("failed to get item")
return validate.NewRequestError(err, http.StatusInternalServerError)

View File

@@ -58,25 +58,6 @@ func GetCookies(r *http.Request) (*CookieContents, error) {
}, nil
}
// AuthProvider is an interface that can be implemented by any authentication provider.
// to extend authentication methods for the API.
type AuthProvider interface {
// Name returns the name of the authentication provider. This should be a unique name.
// that is URL friendly.
//
// Example: "local", "ldap"
Name() string
// Authenticate is called when a user attempts to login to the API. The implementation
// should return an error if the user cannot be authenticated. If an error is returned
// the API controller will return a vague error message to the user.
//
// Authenticate should do the following:
//
// 1. Ensure that the user exists within the database (either create, or get)
// 2. On successful authentication, they must set the user cookies.
Authenticate(w http.ResponseWriter, r *http.Request) (services.UserAuthTokenDetail, error)
}
// HandleAuthLogin godoc
//
// @Summary User Login
@@ -85,42 +66,53 @@ type AuthProvider interface {
// @Accept application/json
// @Param username formData string false "string" example(admin@admin.com)
// @Param password formData string false "string" example(admin)
// @Param payload body LoginForm true "Login Data"
// @Param provider query string false "auth provider"
// @Param payload body LoginForm true "Login Data"
// @Produce json
// @Success 200 {object} TokenResponse
// @Router /v1/users/login [POST]
func (ctrl *V1Controller) HandleAuthLogin(ps ...AuthProvider) errchain.HandlerFunc {
if len(ps) == 0 {
panic("no auth providers provided")
}
providers := make(map[string]AuthProvider)
for _, p := range ps {
log.Info().Str("name", p.Name()).Msg("registering auth provider")
providers[p.Name()] = p
}
func (ctrl *V1Controller) HandleAuthLogin() errchain.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) error {
// Extract provider query
provider := r.URL.Query().Get("provider")
if provider == "" {
provider = "local"
loginForm := &LoginForm{}
switch r.Header.Get("Content-Type") {
case "application/x-www-form-urlencoded":
err := r.ParseForm()
if err != nil {
return errors.New("failed to parse form")
}
loginForm.Username = r.PostFormValue("username")
loginForm.Password = r.PostFormValue("password")
loginForm.StayLoggedIn = r.PostFormValue("stayLoggedIn") == "true"
case "application/json":
err := server.Decode(r, loginForm)
if err != nil {
log.Err(err).Msg("failed to decode login form")
return errors.New("failed to decode login form")
}
default:
return server.JSON(w, http.StatusBadRequest, errors.New("invalid content type"))
}
// Get the provider
p, ok := providers[provider]
if !ok {
return validate.NewRequestError(errors.New("invalid auth provider"), http.StatusBadRequest)
if loginForm.Username == "" || loginForm.Password == "" {
return validate.NewFieldErrors(
validate.FieldError{
Field: "username",
Error: "username or password is empty",
},
validate.FieldError{
Field: "password",
Error: "username or password is empty",
},
)
}
newToken, err := p.Authenticate(w, r)
newToken, err := ctrl.svc.User.Login(r.Context(), strings.ToLower(loginForm.Username), loginForm.Password, loginForm.StayLoggedIn)
if err != nil {
log.Err(err).Msg("failed to authenticate")
return server.JSON(w, http.StatusInternalServerError, err.Error())
return validate.NewRequestError(errors.New("authentication failed"), http.StatusInternalServerError)
}
ctrl.setCookies(w, noPort(r.Host), newToken.Raw, newToken.ExpiresAt, true)
ctrl.setCookies(w, noPort(r.Host), newToken.Raw, newToken.ExpiresAt, loginForm.StayLoggedIn)
return server.JSON(w, http.StatusOK, TokenResponse{
Token: "Bearer " + newToken.Raw,
ExpiresAt: newToken.ExpiresAt,
@@ -153,7 +145,7 @@ func (ctrl *V1Controller) HandleAuthLogout() errchain.HandlerFunc {
}
}
// HandleAuthRefresh godoc
// HandleAuthLogout godoc
//
// @Summary User Token Refresh
// @Description handleAuthRefresh returns a handler that will issue a new token from an existing token.

View File

@@ -12,7 +12,7 @@ import (
type (
GroupInvitationCreate struct {
Uses int `json:"uses" validate:"required,min=1,max=100"`
Uses int `json:"uses" validate:"required,min=1,max=100"`
ExpiresAt time.Time `json:"expiresAt"`
}

View File

@@ -233,6 +233,7 @@ func (ctrl *V1Controller) HandleGetAllCustomFieldValues() errchain.HandlerFunc {
}
return adapters.Query(fn, http.StatusOK)
}
// HandleItemsImport godocs

View File

@@ -39,6 +39,7 @@ func (ctrl *V1Controller) HandleItemAttachmentCreate() errchain.HandlerFunc {
if err != nil {
log.Err(err).Msg("failed to parse multipart form")
return validate.NewRequestError(errors.New("failed to parse multipart form"), http.StatusBadRequest)
}
errs := validate.NewFieldErrors()

View File

@@ -10,7 +10,7 @@ import (
"github.com/hay-kot/httpkit/errchain"
)
// HandleLocationTreeQuery godoc
// HandleLocationTreeQuery
//
// @Summary Get Locations Tree
// @Tags Locations
@@ -28,7 +28,7 @@ func (ctrl *V1Controller) HandleLocationTreeQuery() errchain.HandlerFunc {
return adapters.Query(fn, http.StatusOK)
}
// HandleLocationGetAll godoc
// HandleLocationGetAll
//
// @Summary Get All Locations
// @Tags Locations
@@ -46,7 +46,7 @@ func (ctrl *V1Controller) HandleLocationGetAll() errchain.HandlerFunc {
return adapters.Query(fn, http.StatusOK)
}
// HandleLocationCreate godoc
// HandleLocationCreate
//
// @Summary Create Location
// @Tags Locations
@@ -64,7 +64,7 @@ func (ctrl *V1Controller) HandleLocationCreate() errchain.HandlerFunc {
return adapters.Action(fn, http.StatusCreated)
}
// HandleLocationDelete godoc
// HandleLocationDelete
//
// @Summary Delete Location
// @Tags Locations
@@ -83,7 +83,7 @@ func (ctrl *V1Controller) HandleLocationDelete() errchain.HandlerFunc {
return adapters.CommandID("id", fn, http.StatusNoContent)
}
// HandleLocationGet godoc
// HandleLocationGet
//
// @Summary Get Location
// @Tags Locations
@@ -101,7 +101,7 @@ func (ctrl *V1Controller) HandleLocationGet() errchain.HandlerFunc {
return adapters.CommandID("id", fn, http.StatusOK)
}
// HandleLocationUpdate godoc
// HandleLocationUpdate
//
// @Summary Update Location
// @Tags Locations

View File

@@ -10,7 +10,7 @@ import (
"github.com/hay-kot/httpkit/errchain"
)
// HandleMaintenanceLogGet godoc
// HandleMaintenanceGetLog godoc
//
// @Summary Get Maintenance Log
// @Tags Maintenance

View File

@@ -12,7 +12,7 @@ import (
"github.com/hay-kot/httpkit/server"
)
// HandleGroupStatisticsLocations godoc
// HandleGroupGet godoc
//
// @Summary Get Location Statistics
// @Tags Statistics

View File

@@ -78,12 +78,12 @@ func run(cfg *config.Config) error {
log.Fatal().Err(err).Msg("failed to create data directory")
}
c, err := ent.Open("sqlite3", cfg.Storage.SqliteURL)
c, err := ent.Open("sqlite3", cfg.Storage.SqliteUrl)
if err != nil {
log.Fatal().
Err(err).
Str("driver", "sqlite").
Str("url", cfg.Storage.SqliteURL).
Str("url", cfg.Storage.SqliteUrl).
Msg("failed opening connection to sqlite")
}
defer func(c *ent.Client) {
@@ -116,7 +116,7 @@ func run(cfg *config.Config) error {
log.Fatal().
Err(err).
Str("driver", "sqlite").
Str("url", cfg.Storage.SqliteURL).
Str("url", cfg.Storage.SqliteUrl).
Msg("failed creating schema resources")
}

View File

@@ -9,7 +9,6 @@ import (
v1 "github.com/hay-kot/homebox/backend/app/api/handlers/v1"
"github.com/hay-kot/homebox/backend/internal/core/services"
"github.com/hay-kot/homebox/backend/internal/data/ent"
"github.com/hay-kot/homebox/backend/internal/sys/validate"
"github.com/hay-kot/httpkit/errchain"
)
@@ -131,7 +130,7 @@ func (a *app) mwAuthToken(next errchain.Handler) errchain.Handler {
}
if requestToken == "" {
return validate.NewRequestError(errors.New("authorization header or query is required"), http.StatusUnauthorized)
return validate.NewRequestError(errors.New("Authorization header or query is required"), http.StatusUnauthorized)
}
requestToken = strings.TrimPrefix(requestToken, "Bearer ")
@@ -141,11 +140,7 @@ func (a *app) mwAuthToken(next errchain.Handler) errchain.Handler {
usr, err := a.services.User.GetSelf(r.Context(), requestToken)
// Check the database for the token
if err != nil {
if ent.IsNotFound(err) {
return validate.NewRequestError(errors.New("valid authorization token is required"), http.StatusUnauthorized)
}
return err
return validate.NewRequestError(errors.New("valid authorization header is required"), http.StatusUnauthorized)
}
r = r.WithContext(services.SetUserCtx(r.Context(), &usr, requestToken))

View File

@@ -1,2 +0,0 @@
// Package providers provides a authentication abstraction for the backend.
package providers

View File

@@ -1,55 +0,0 @@
package providers
import (
"errors"
"net/http"
"github.com/hay-kot/homebox/backend/internal/sys/validate"
"github.com/hay-kot/httpkit/server"
"github.com/rs/zerolog/log"
)
type LoginForm struct {
Username string `json:"username"`
Password string `json:"password"`
StayLoggedIn bool `json:"stayLoggedIn"`
}
func getLoginForm(r *http.Request) (LoginForm, error) {
loginForm := LoginForm{}
switch r.Header.Get("Content-Type") {
case "application/x-www-form-urlencoded":
err := r.ParseForm()
if err != nil {
return loginForm, errors.New("failed to parse form")
}
loginForm.Username = r.PostFormValue("username")
loginForm.Password = r.PostFormValue("password")
loginForm.StayLoggedIn = r.PostFormValue("stayLoggedIn") == "true"
case "application/json":
err := server.Decode(r, &loginForm)
if err != nil {
log.Err(err).Msg("failed to decode login form")
return loginForm, errors.New("failed to decode login form")
}
default:
return loginForm, errors.New("invalid content type")
}
if loginForm.Username == "" || loginForm.Password == "" {
return loginForm, validate.NewFieldErrors(
validate.FieldError{
Field: "username",
Error: "username or password is empty",
},
validate.FieldError{
Field: "password",
Error: "username or password is empty",
},
)
}
return loginForm, nil
}

View File

@@ -1,30 +0,0 @@
package providers
import (
"net/http"
"github.com/hay-kot/homebox/backend/internal/core/services"
)
type LocalProvider struct {
service *services.UserService
}
func NewLocalProvider(service *services.UserService) *LocalProvider {
return &LocalProvider{
service: service,
}
}
func (p *LocalProvider) Name() string {
return "local"
}
func (p *LocalProvider) Authenticate(w http.ResponseWriter, r *http.Request) (services.UserAuthTokenDetail, error) {
loginForm, err := getLoginForm(r)
if err != nil {
return services.UserAuthTokenDetail{}, err
}
return p.service.Login(r.Context(), loginForm.Username, loginForm.Password, loginForm.StayLoggedIn)
}

View File

@@ -12,7 +12,6 @@ import (
"github.com/go-chi/chi/v5"
"github.com/hay-kot/homebox/backend/app/api/handlers/debughandlers"
v1 "github.com/hay-kot/homebox/backend/app/api/handlers/v1"
"github.com/hay-kot/homebox/backend/app/api/providers"
_ "github.com/hay-kot/homebox/backend/app/api/static/docs"
"github.com/hay-kot/homebox/backend/internal/data/ent/authroles"
"github.com/hay-kot/homebox/backend/internal/data/repo"
@@ -47,7 +46,7 @@ func (a *app) mountRoutes(r *chi.Mux, chain *errchain.ErrChain, repos *repo.AllR
// =========================================================================
// API Version 1
v1Base := v1.BaseURLFunc(prefix)
v1Base := v1.BaseUrlFunc(prefix)
v1Ctrl := v1.NewControllerV1(
a.services,
@@ -64,12 +63,8 @@ func (a *app) mountRoutes(r *chi.Mux, chain *errchain.ErrChain, repos *repo.AllR
BuildTime: buildTime,
})))
providers := []v1.AuthProvider{
providers.NewLocalProvider(a.services.User),
}
r.Post(v1Base("/users/register"), chain.ToHandlerFunc(v1Ctrl.HandleUserRegistration()))
r.Post(v1Base("/users/login"), chain.ToHandlerFunc(v1Ctrl.HandleAuthLogin(providers...)))
r.Post(v1Base("/users/login"), chain.ToHandlerFunc(v1Ctrl.HandleAuthLogin()))
userMW := []errchain.Middleware{
a.mwAuthToken,
@@ -183,7 +178,7 @@ func notFoundHandler() errchain.HandlerFunc {
if err != nil {
return err
}
defer func() { _ = f.Close() }()
defer f.Close()
stat, _ := f.Stat()
if stat.IsDir() {

View File

@@ -1,6 +1,6 @@
module github.com/hay-kot/homebox/backend
go 1.21
go 1.20
require (
ariga.io/atlas v0.15.0
@@ -9,10 +9,10 @@ require (
github.com/containrrr/shoutrrr v0.8.0
github.com/go-chi/chi/v5 v5.0.10
github.com/go-playground/validator/v10 v10.16.0
github.com/gocarina/gocsv v0.0.0-20231116093920-b87c2d0e983a
github.com/gocarina/gocsv v0.0.0-20230616125104-99d496ca653d
github.com/google/uuid v1.4.0
github.com/gorilla/schema v1.2.1
github.com/hay-kot/httpkit v0.0.5
github.com/hay-kot/httpkit v0.0.3
github.com/mattn/go-sqlite3 v1.14.18
github.com/olahol/melody v1.1.4
github.com/pkg/errors v0.9.1
@@ -22,7 +22,7 @@ require (
github.com/swaggo/swag v1.16.2
github.com/yeqown/go-qrcode/v2 v2.2.2
github.com/yeqown/go-qrcode/writer/standard v1.2.2
golang.org/x/crypto v0.16.0
golang.org/x/crypto v0.15.0
modernc.org/sqlite v1.27.0
)
@@ -62,7 +62,7 @@ require (
golang.org/x/image v0.14.0 // indirect
golang.org/x/mod v0.14.0 // indirect
golang.org/x/net v0.18.0 // indirect
golang.org/x/sys v0.15.0 // indirect
golang.org/x/sys v0.14.0 // indirect
golang.org/x/text v0.14.0 // indirect
golang.org/x/tools v0.15.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect

View File

@@ -3,7 +3,6 @@ ariga.io/atlas v0.15.0/go.mod h1:isZrlzJ5cpoCoKFoY9knZug7Lq4pP1cm8g3XciLZ0Pw=
entgo.io/ent v0.12.5 h1:KREM5E4CSoej4zeGa88Ou/gfturAnpUv0mzAjch1sj4=
entgo.io/ent v0.12.5/go.mod h1:Y3JVAjtlIk8xVZYSn3t3mf8xlZIn5SAOXZQxD6kKI+Q=
github.com/DATA-DOG/go-sqlmock v1.5.0 h1:Shsta01QNfFxHCfpW6YH2STWB0MudeXXEWMr20OEh60=
github.com/DATA-DOG/go-sqlmock v1.5.0/go.mod h1:f/Ixk793poVmq4qj/V1dPUg2JEAKC73Q5eFN3EC/SaM=
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=
@@ -30,7 +29,6 @@ github.com/gabriel-vasile/mimetype v1.4.3/go.mod h1:d8uq/6HKRL6CGdk+aubisF/M5GcP
github.com/go-chi/chi/v5 v5.0.10 h1:rLz5avzKpjqxrYwXNfmjkrYYXOyLJd37pz53UFHC6vk=
github.com/go-chi/chi/v5 v5.0.10/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8=
github.com/go-logr/logr v1.2.3 h1:2DntVwHkVopvECVRSlL5PSo9eG+cAkDCuckLubN+rq0=
github.com/go-logr/logr v1.2.3/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
github.com/go-openapi/inflect v0.19.0 h1:9jCH9scKIbHeV9m12SmPilScz6krDxKRasNNSNPXu/4=
github.com/go-openapi/inflect v0.19.0/go.mod h1:lHpZVlpIQqLyKwJ4N+YSc9hchQy/i12fJykb83CRBH4=
github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=
@@ -49,7 +47,6 @@ github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+
github.com/go-openapi/swag v0.22.4 h1:QLMzNJnMGPRNDCbySlcj1x01tzU8/9LTTL9hZZZogBU=
github.com/go-openapi/swag v0.22.4/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14=
github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s=
github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA=
github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
@@ -57,21 +54,17 @@ github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91
github.com/go-playground/validator/v10 v10.16.0 h1:x+plE831WK4vaKHO/jpgUGsvLKIqRRkz6M78GuJAfGE=
github.com/go-playground/validator/v10 v10.16.0/go.mod h1:9iXMNT7sEkjXb0I+enO7QXmzG6QCsPWY4zveKFVRSyU=
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI=
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls=
github.com/go-test/deep v1.0.3 h1:ZrJSEWsXzPOxaZnFteGEfooLba+ju3FYIbOrS+rQd68=
github.com/go-test/deep v1.0.3/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA=
github.com/gocarina/gocsv v0.0.0-20231116093920-b87c2d0e983a h1:RYfmiM0zluBJOiPDJseKLEN4BapJ42uSi9SZBQ2YyiA=
github.com/gocarina/gocsv v0.0.0-20231116093920-b87c2d0e983a/go.mod h1:5YoVOkjYAQumqlV356Hj3xeYh4BdZuLE0/nRkf2NKkI=
github.com/gocarina/gocsv v0.0.0-20230616125104-99d496ca653d h1:KbPOUXFUDJxwZ04vbmDOc3yuruGvVO+LOa7cVER3yWw=
github.com/gocarina/gocsv v0.0.0-20230616125104-99d496ca653d/go.mod h1:5YoVOkjYAQumqlV356Hj3xeYh4BdZuLE0/nRkf2NKkI=
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 h1:DACJavvAHhabrF08vX0COfcOBJRhZ8lUbR+ZWIs0Y5g=
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k=
github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg=
github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/pprof v0.0.0-20221118152302-e6195bd50e26 h1:Xim43kblpZXfIBQsbuBVKCudVG457BR2GZFIz3uw3hQ=
github.com/google/pprof v0.0.0-20221118152302-e6195bd50e26/go.mod h1:dDKJzRmX4S37WGHujM7tX//fmj1uioxKzKxz3lo4HJo=
github.com/google/uuid v1.4.0 h1:MtMxsa51/r9yyhkyLsVeVt0B+BGQZzpQiTQ4eHZ8bc4=
github.com/google/uuid v1.4.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/gorilla/schema v1.2.1 h1:tjDxcmdb+siIqkTNoV+qRH2mjYdr2hHe5MKXbp61ziM=
@@ -80,10 +73,9 @@ github.com/gorilla/websocket v1.5.1 h1:gmztn0JnHVt9JZquRuzLw3g4wouNVzKL15iLr/zn/
github.com/gorilla/websocket v1.5.1/go.mod h1:x3kM2JMyaluk02fnUJpQuwD2dCS5NDG2ZHL0uE0tcaY=
github.com/hashicorp/hcl/v2 v2.19.1 h1://i05Jqznmb2EXqa39Nsvyan2o5XyMowW5fnCKW5RPI=
github.com/hashicorp/hcl/v2 v2.19.1/go.mod h1:ThLC89FV4p9MPW804KVbe/cEXoQ8NZEh+JtMeeGErHE=
github.com/hay-kot/httpkit v0.0.5 h1:jy0sWtYJ0CtKzKNd6VUv5dLBTUgo6hk6i4RP6aCxw/g=
github.com/hay-kot/httpkit v0.0.5/go.mod h1:1s/OJwWRyH6tBtTw76jTp6kwBYvjswziXaokPQH7eKQ=
github.com/hay-kot/httpkit v0.0.3 h1:QYq01J5Jrn+ie0s1ptavNSEyydkOHqsrw4RLp+2LeJQ=
github.com/hay-kot/httpkit v0.0.3/go.mod h1:1s/OJwWRyH6tBtTw76jTp6kwBYvjswziXaokPQH7eKQ=
github.com/jarcoal/httpmock v1.3.0 h1:2RJ8GP0IIaWwcC9Fp2BmVi8Kog3v2Hn7VXM3fTd+nuc=
github.com/jarcoal/httpmock v1.3.0/go.mod h1:3yb8rc4BI7TCBhFY8ng0gjuLKJNquuDNiPaZjnENuYg=
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/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNUXsshfwJMBgNA0RU6/i7WVaAegv3PtuIHPMs=
@@ -91,13 +83,11 @@ github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:C
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/kylelemons/godebug v0.0.0-20170820004349-d65d576e9348 h1:MtvEpTB6LX3vkb4ax0b5D2DHbNAUsen0Gx5wZoq3lV4=
github.com/kylelemons/godebug v0.0.0-20170820004349-d65d576e9348/go.mod h1:B69LEHPfb2qLo0BaaOLcbitczOKLWTsrBG9LczfCD4k=
github.com/leodido/go-urn v1.2.4 h1:XlAE/cm/ms7TE/VMVoduSpNBoyc2dOxHs5MZSwAN63Q=
github.com/leodido/go-urn v1.2.4/go.mod h1:7ZrI8mTSeBSHl/UaRyKQW1qZeMgak41ANeCNaVckg+4=
github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
@@ -119,9 +109,7 @@ github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLA
github.com/olahol/melody v1.1.4 h1:RQHfKZkQmDxI0+SLZRNBCn4LiXdqxLKRGSkT8Dyoe/E=
github.com/olahol/melody v1.1.4/go.mod h1:GgkTl6Y7yWj/HtfD48Q5vLKPVoZOH+Qqgfa7CvJgJM4=
github.com/onsi/ginkgo/v2 v2.9.2 h1:BA2GMJOtfGAfagzYtrAlufIP0lq6QERkFmHLMLPwFSU=
github.com/onsi/ginkgo/v2 v2.9.2/go.mod h1:WHcJJG2dIlcCqVfBAwUCrJxSPFb6v4azBwgxeMeDuts=
github.com/onsi/gomega v1.27.6 h1:ENqfyGeS5AX/rlXDd/ETokDz93u0YufY1Pgxuy/PvWE=
github.com/onsi/gomega v1.27.6/go.mod h1:PIQNjfQwkP3aQAH7lf7j87O/5FiNr+ZR8+ipb+qQlhg=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
@@ -134,7 +122,6 @@ github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg=
github.com/rs/zerolog v1.31.0 h1:FcTR3NnLWW+NnTwwhFWiJSZr4ECLpqCm6QsEnyvbV4A=
github.com/rs/zerolog v1.31.0/go.mod h1:/7mN4D5sKwJLZQ2b/znpjC3/GQWY/xaDXUM0kKWRHss=
github.com/sergi/go-diff v1.0.0 h1:Kpca3qRNrduNnOQeazBd0ysaKrUJiIuISHxogkT9RPQ=
github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
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=
@@ -163,8 +150,8 @@ github.com/zclconf/go-cty v1.14.1 h1:t9fyA35fwjjUMcmL5hLER+e/rEPqrbCK1/OSE4SI9KA
github.com/zclconf/go-cty v1.14.1/go.mod h1:VvMs5i0vgZdhYawQNq5kePSpLAoz8u1xvZgrPIxfnZE=
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.16.0 h1:mMMrFzRSCF0GvB7Ne27XVtVAaXLrPmgPC7/v0tkwHaY=
golang.org/x/crypto v0.16.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4=
golang.org/x/crypto v0.15.0 h1:frVn1TEaCEaZcn3Tmd7Y2b5KKPaZ+I32Q2OA3kYp5TA=
golang.org/x/crypto v0.15.0/go.mod h1:4ChreQoLWfG3xLDer1WdlH5NdlQ3+mwnQq1YTKY+72g=
golang.org/x/image v0.14.0 h1:tNgSxAFe3jC4uYqvZdTr84SZoM1KfwdC9SKIFrLjFn4=
golang.org/x/image v0.14.0/go.mod h1:HUYqC05R2ZcZ3ejNQsIHQDQiwWM4JBqmm6MKANTp4LE=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
@@ -179,7 +166,6 @@ golang.org/x/net v0.18.0/go.mod h1:/czyP5RqHAH4odGYxBJ1qz0+CE5WZ+2j1YgoEo8F2jQ=
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/sync v0.5.0 h1:60k92dhOjHxJkrqnwsfl8KuaHbn/5dl0lUPUklKo3qE=
golang.org/x/sync v0.5.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
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-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
@@ -189,8 +175,8 @@ golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc=
golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.14.0 h1:Vz7Qs629MkJkGyHxUlRHizWJRG2j8fbQKjELVSNhy7Q=
golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
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.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
@@ -207,7 +193,6 @@ golang.org/x/tools v0.15.0 h1:zdAyfUGbYmuVokhzVmghFl2ZJh5QhcfebBgmVPFYA+8=
golang.org/x/tools v0.15.0/go.mod h1:hpksKq4dtpQWS1uQ61JkdqWM3LscIS6Slf+VVkm+wQk=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w=
google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
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=
@@ -227,9 +212,7 @@ modernc.org/cc/v3 v3.41.0/go.mod h1:Ni4zjJYJ04CDOhG7dn640WGfwBzfE0ecX8TyMB0Fv0Y=
modernc.org/ccgo/v3 v3.16.15 h1:KbDR3ZAVU+wiLyMESPtbtE/Add4elztFyfsWoNTgxS0=
modernc.org/ccgo/v3 v3.16.15/go.mod h1:yT7B+/E2m43tmMOT51GMoM98/MtHIcQQSleGnddkUNI=
modernc.org/ccorpus v1.11.6 h1:J16RXiiqiCgua6+ZvQot4yUuUy8zxgqbqEEUuGPlISk=
modernc.org/ccorpus v1.11.6/go.mod h1:2gEUTrWqdpH2pXsmTM1ZkjeSrUWDpjMu2T6m29L/ErQ=
modernc.org/httpfs v1.0.6 h1:AAgIpFZRXuYnkjftxTAZwMIiwEqAfk8aVB2/oA6nAeM=
modernc.org/httpfs v1.0.6/go.mod h1:7dosgurJGp0sPaRanU53W4xZYKh14wfzX420oZADeHM=
modernc.org/libc v1.34.4 h1:r9+5s4wNeoCsB8CuJE67UB4N07ernbvrcry9O3MLWtQ=
modernc.org/libc v1.34.4/go.mod h1:YAXkAZ8ktnkCKaN9sw/UDeUVkGYJ/YquGO4FTi5nmHE=
modernc.org/mathutil v1.6.0 h1:fRe9+AmYlaej+64JsEEhoWuAYBkOtQiMEU7n/XgfYi4=
@@ -243,8 +226,6 @@ modernc.org/sqlite v1.27.0/go.mod h1:Qxpazz0zH8Z1xCFyi5GSL3FzbtZ3fvbjmywNogldEW0
modernc.org/strutil v1.2.0 h1:agBi9dp1I+eOnxXeiZawM8F4LawKv4NzGWSaLfyeNZA=
modernc.org/strutil v1.2.0/go.mod h1:/mdcBmfOibveCTBxUl5B5l6W+TTH1FXPLHZE6bTosX0=
modernc.org/tcl v1.15.2 h1:C4ybAYCGJw968e+Me18oW55kD/FexcHbqH2xak1ROSY=
modernc.org/tcl v1.15.2/go.mod h1:3+k/ZaEbKrC8ePv8zJWPtBSW0V7Gg9g8rkmhI1Kfs3c=
modernc.org/token v1.1.0 h1:Xl7Ap9dKaEs5kLoOQeQmPWevfnk/DM5qcLcYlA8ys6Y=
modernc.org/token v1.1.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM=
modernc.org/z v1.7.3 h1:zDJf6iHjrnB+WRD88stbXokugjyc0/pB91ri1gO6LZY=
modernc.org/z v1.7.3/go.mod h1:Ipv4tsdxZRbQyLq9Q1M6gdbkxYzdlrciF2Hi/lS7nWE=

View File

@@ -1,4 +1,3 @@
// Package services provides the core business logic for the application.
package services
import (

View File

@@ -62,7 +62,7 @@ func TestMain(m *testing.M) {
tClient = client
tRepos = repo.New(tClient, tbus, os.TempDir()+"/homebox")
tSvc = New(tRepos)
defer func() { _ = client.Close() }()
defer client.Close()
bootstrap()
tCtx = Context{

View File

@@ -1,4 +1,4 @@
// Package eventbus provides an interface for event bus.
// / Package eventbus provides an interface for event bus.
package eventbus
import (

View File

@@ -1,4 +1,3 @@
// Package reporting provides a way to import CSV files into the database.
package reporting
import (

View File

@@ -152,7 +152,7 @@ func (s *IOSheet) Read(data io.Reader) error {
return nil
}
// ReadItems writes the sheet to a writer.
// Write writes the sheet to a writer.
func (s *IOSheet) ReadItems(ctx context.Context, items []repo.ItemOut, GID uuid.UUID, repos *repo.AllRepos) error {
s.Rows = make([]ExportTSVRow, len(items))
@@ -162,9 +162,9 @@ func (s *IOSheet) ReadItems(ctx context.Context, items []repo.ItemOut, GID uuid.
item := items[i]
// TODO: Support fetching nested locations
locID := item.Location.ID
locId := item.Location.ID
locPaths, err := repos.Locations.PathForLoc(context.Background(), GID, locID)
locPaths, err := repos.Locations.PathForLoc(context.Background(), GID, locId)
if err != nil {
log.Error().Err(err).Msg("could not get location path")
return err
@@ -252,7 +252,7 @@ func (s *IOSheet) ReadItems(ctx context.Context, items []repo.ItemOut, GID uuid.
return nil
}
// TSV writes the current sheet to a writer in TSV format.
// Writes the current sheet to a writer in TSV format.
func (s *IOSheet) TSV() ([][]string, error) {
memcsv := make([][]string, len(s.Rows)+1)

View File

@@ -9,7 +9,6 @@ import (
"github.com/hay-kot/homebox/backend/internal/data/repo"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
var (
@@ -104,9 +103,9 @@ func TestSheet_Read(t *testing.T) {
switch {
case tt.wantErr:
require.Error(t, err)
assert.Error(t, err)
default:
require.NoError(t, err)
assert.NoError(t, err)
assert.ElementsMatch(t, tt.want, sheet.Rows)
}
})

View File

@@ -32,7 +32,7 @@ func (svc *ItemService) Create(ctx Context, item repo.ItemCreate) (repo.ItemOut,
return repo.ItemOut{}, err
}
item.AssetID = highest + 1
item.AssetID = repo.AssetID(highest + 1)
}
return svc.repo.Items.Create(ctx, ctx.GID, item)
@@ -53,7 +53,7 @@ func (svc *ItemService) EnsureAssetID(ctx context.Context, GID uuid.UUID) (int,
for _, item := range items {
highest++
err = svc.repo.Items.SetAssetID(ctx, GID, item.ID, highest)
err = svc.repo.Items.SetAssetID(ctx, GID, item.ID, repo.AssetID(highest))
if err != nil {
return 0, err
}

View File

@@ -12,8 +12,8 @@ import (
"github.com/rs/zerolog/log"
)
func (svc *ItemService) AttachmentPath(ctx context.Context, attachmentID uuid.UUID) (*ent.Document, error) {
attachment, err := svc.repo.Attachments.Get(ctx, attachmentID)
func (svc *ItemService) AttachmentPath(ctx context.Context, attachmentId uuid.UUID) (*ent.Document, error) {
attachment, err := svc.repo.Attachments.Get(ctx, attachmentId)
if err != nil {
return nil, err
}
@@ -21,7 +21,7 @@ func (svc *ItemService) AttachmentPath(ctx context.Context, attachmentID uuid.UU
return attachment.Edges.Document, nil
}
func (svc *ItemService) AttachmentUpdate(ctx Context, itemID uuid.UUID, data *repo.ItemAttachmentUpdate) (repo.ItemOut, error) {
func (svc *ItemService) AttachmentUpdate(ctx Context, itemId uuid.UUID, data *repo.ItemAttachmentUpdate) (repo.ItemOut, error) {
// Update Attachment
attachment, err := svc.repo.Attachments.Update(ctx, data.ID, data)
if err != nil {
@@ -35,15 +35,15 @@ func (svc *ItemService) AttachmentUpdate(ctx Context, itemID uuid.UUID, data *re
return repo.ItemOut{}, err
}
return svc.repo.Items.GetOneByGroup(ctx, ctx.GID, itemID)
return svc.repo.Items.GetOneByGroup(ctx, ctx.GID, itemId)
}
// AttachmentAdd adds an attachment to an item by creating an entry in the Documents table and linking it to the Attachment
// Table and Items table. The file provided via the reader is stored on the file system based on the provided
// relative path during construction of the service.
func (svc *ItemService) AttachmentAdd(ctx Context, itemID uuid.UUID, filename string, attachmentType attachment.Type, file io.Reader) (repo.ItemOut, error) {
func (svc *ItemService) AttachmentAdd(ctx Context, itemId uuid.UUID, filename string, attachmentType attachment.Type, file io.Reader) (repo.ItemOut, error) {
// Get the Item
_, err := svc.repo.Items.GetOneByGroup(ctx, ctx.GID, itemID)
_, err := svc.repo.Items.GetOneByGroup(ctx, ctx.GID, itemId)
if err != nil {
return repo.ItemOut{}, err
}
@@ -56,29 +56,29 @@ func (svc *ItemService) AttachmentAdd(ctx Context, itemID uuid.UUID, filename st
}
// Create the attachment
_, err = svc.repo.Attachments.Create(ctx, itemID, doc.ID, attachmentType)
_, err = svc.repo.Attachments.Create(ctx, itemId, doc.ID, attachmentType)
if err != nil {
log.Err(err).Msg("failed to create attachment")
return repo.ItemOut{}, err
}
return svc.repo.Items.GetOneByGroup(ctx, ctx.GID, itemID)
return svc.repo.Items.GetOneByGroup(ctx, ctx.GID, itemId)
}
func (svc *ItemService) AttachmentDelete(ctx context.Context, gid, itemID, attachmentID uuid.UUID) error {
func (svc *ItemService) AttachmentDelete(ctx context.Context, gid, itemId, attachmentId uuid.UUID) error {
// Get the Item
_, err := svc.repo.Items.GetOneByGroup(ctx, gid, itemID)
_, err := svc.repo.Items.GetOneByGroup(ctx, gid, itemId)
if err != nil {
return err
}
attachment, err := svc.repo.Attachments.Get(ctx, attachmentID)
attachment, err := svc.repo.Attachments.Get(ctx, attachmentId)
if err != nil {
return err
}
// Delete the attachment
err = svc.repo.Attachments.Delete(ctx, attachmentID)
err = svc.repo.Attachments.Delete(ctx, attachmentId)
if err != nil {
return err
}

View File

@@ -9,7 +9,6 @@ import (
"github.com/hay-kot/homebox/backend/internal/data/repo"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestItemService_AddAttachment(t *testing.T) {
@@ -24,7 +23,7 @@ func TestItemService_AddAttachment(t *testing.T) {
Description: "test",
Name: "test",
})
require.NoError(t, err)
assert.NoError(t, err)
assert.NotNil(t, loc)
itmC := repo.ItemCreate{
@@ -34,11 +33,11 @@ func TestItemService_AddAttachment(t *testing.T) {
}
itm, err := svc.repo.Items.Create(context.Background(), tGroup.ID, itmC)
require.NoError(t, err)
assert.NoError(t, err)
assert.NotNil(t, itm)
t.Cleanup(func() {
err := svc.repo.Items.Delete(context.Background(), itm.ID)
require.NoError(t, err)
assert.NoError(t, err)
})
contents := fk.Str(1000)
@@ -46,7 +45,7 @@ func TestItemService_AddAttachment(t *testing.T) {
// Setup
afterAttachment, err := svc.AttachmentAdd(tCtx, itm.ID, "testfile.txt", "attachment", reader)
require.NoError(t, err)
assert.NoError(t, err)
assert.NotNil(t, afterAttachment)
// Check that the file exists
@@ -57,6 +56,6 @@ func TestItemService_AddAttachment(t *testing.T) {
// Check that the file contents are correct
bts, err := os.ReadFile(storedPath)
require.NoError(t, err)
assert.NoError(t, err)
assert.Equal(t, contents, string(bts))
}

View File

@@ -16,7 +16,7 @@ var (
oneWeek = time.Hour * 24 * 7
ErrorInvalidLogin = errors.New("invalid username or password")
ErrorInvalidToken = errors.New("invalid token")
ErrorTokenIDMismatch = errors.New("token id mismatch")
ErrorTokenIdMismatch = errors.New("token id mismatch")
)
type UserService struct {
@@ -134,13 +134,13 @@ func (svc *UserService) UpdateSelf(ctx context.Context, ID uuid.UUID, data repo.
return repo.UserOut{}, err
}
return svc.repos.Users.GetOneID(ctx, ID)
return svc.repos.Users.GetOneId(ctx, ID)
}
// ============================================================================
// User Authentication
func (svc *UserService) createSessionToken(ctx context.Context, userID uuid.UUID, extendedSession bool) (UserAuthTokenDetail, error) {
func (svc *UserService) createSessionToken(ctx context.Context, userId uuid.UUID, extendedSession bool) (UserAuthTokenDetail, error) {
attachmentToken := hasher.GenerateToken()
expiresAt := time.Now().Add(oneWeek)
@@ -149,7 +149,7 @@ func (svc *UserService) createSessionToken(ctx context.Context, userID uuid.UUID
}
attachmentData := repo.UserAuthTokenCreate{
UserID: userID,
UserID: userId,
TokenHash: attachmentToken.Hash,
ExpiresAt: expiresAt,
}
@@ -161,7 +161,7 @@ func (svc *UserService) createSessionToken(ctx context.Context, userID uuid.UUID
userToken := hasher.GenerateToken()
data := repo.UserAuthTokenCreate{
UserID: userID,
UserID: userId,
TokenHash: userToken.Hash,
ExpiresAt: expiresAt,
}
@@ -218,7 +218,7 @@ func (svc *UserService) DeleteSelf(ctx context.Context, ID uuid.UUID) error {
}
func (svc *UserService) ChangePassword(ctx Context, current string, new string) (ok bool) {
usr, err := svc.repos.Users.GetOneID(ctx, ctx.UID)
usr, err := svc.repos.Users.GetOneId(ctx, ctx.UID)
if err != nil {
return false
}

View File

@@ -1,4 +1,3 @@
// Package migrations provides a way to embed the migrations into the binary.
package migrations
import (

View File

@@ -54,7 +54,7 @@ func TestMain(m *testing.M) {
tClient = client
tRepos = New(tClient, tbus, os.TempDir())
defer func() { _ = client.Close() }()
defer client.Close()
bootstrap()

View File

@@ -11,7 +11,6 @@ import (
"github.com/google/uuid"
"github.com/hay-kot/homebox/backend/internal/data/ent"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func useDocs(t *testing.T, num int) []DocumentOut {
@@ -26,7 +25,7 @@ func useDocs(t *testing.T, num int) []DocumentOut {
Content: bytes.NewReader([]byte(fk.Str(10))),
})
require.NoError(t, err)
assert.NoError(t, err)
assert.NotNil(t, doc)
results = append(results, doc)
ids = append(ids, doc.ID)
@@ -81,31 +80,31 @@ func TestDocumentRepository_CreateUpdateDelete(t *testing.T) {
t.Run(tt.name, func(t *testing.T) {
// Create Document
got, err := r.Create(tt.args.ctx, tt.args.gid, tt.args.doc)
require.NoError(t, err)
assert.NoError(t, err)
assert.Equal(t, tt.title, got.Title)
assert.Equal(t, fmt.Sprintf("%s/%s/documents", temp, tt.args.gid), filepath.Dir(got.Path))
ensureRead := func() {
// Read Document
bts, err := os.ReadFile(got.Path)
require.NoError(t, err)
assert.NoError(t, err)
assert.Equal(t, tt.content, string(bts))
}
ensureRead()
// Update Document
got, err = r.Rename(tt.args.ctx, got.ID, "__"+tt.title+"__")
require.NoError(t, err)
assert.NoError(t, err)
assert.Equal(t, "__"+tt.title+"__", got.Title)
ensureRead()
// Delete Document
err = r.Delete(tt.args.ctx, got.ID)
require.NoError(t, err)
assert.NoError(t, err)
_, err = os.Stat(got.Path)
require.Error(t, err)
assert.Error(t, err)
})
}
}

View File

@@ -5,30 +5,29 @@ import (
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func Test_Group_Create(t *testing.T) {
g, err := tRepos.Groups.GroupCreate(context.Background(), "test")
require.NoError(t, err)
assert.NoError(t, err)
assert.Equal(t, "test", g.Name)
// Get by ID
foundGroup, err := tRepos.Groups.GroupByID(context.Background(), g.ID)
require.NoError(t, err)
assert.NoError(t, err)
assert.Equal(t, g.ID, foundGroup.ID)
}
func Test_Group_Update(t *testing.T) {
g, err := tRepos.Groups.GroupCreate(context.Background(), "test")
require.NoError(t, err)
assert.NoError(t, err)
g, err = tRepos.Groups.GroupUpdate(context.Background(), g.ID, GroupUpdate{
Name: "test2",
Currency: "eur",
})
require.NoError(t, err)
assert.NoError(t, err)
assert.Equal(t, "test2", g.Name)
assert.Equal(t, "EUR", g.Currency)
}
@@ -39,7 +38,7 @@ func Test_Group_GroupStatistics(t *testing.T) {
stats, err := tRepos.Groups.StatsGroup(context.Background(), tGroup.ID)
require.NoError(t, err)
assert.NoError(t, err)
assert.Equal(t, 20, stats.TotalItems)
assert.Equal(t, 20, stats.TotalLabels)
assert.Equal(t, 1, stats.TotalUsers)

View File

@@ -51,31 +51,31 @@ func ToItemAttachment(attachment *ent.Attachment) ItemAttachment {
}
}
func (r *AttachmentRepo) Create(ctx context.Context, itemID, docID uuid.UUID, typ attachment.Type) (*ent.Attachment, error) {
func (r *AttachmentRepo) Create(ctx context.Context, itemId, docId uuid.UUID, typ attachment.Type) (*ent.Attachment, error) {
bldr := r.db.Attachment.Create().
SetType(typ).
SetDocumentID(docID).
SetItemID(itemID)
SetDocumentID(docId).
SetItemID(itemId)
// Autoset primary to true if this is the first attachment
// that is of type photo
if typ == attachment.TypePhoto {
cnt, err := r.db.Attachment.Query().
Where(
attachment.HasItemWith(item.ID(itemID)),
attachment.TypeEQ(typ),
).
Count(ctx)
if err != nil {
return nil, err
}
// Autoset primary to true if this is the first attachment
// that is of type photo
if typ == attachment.TypePhoto {
cnt, err := r.db.Attachment.Query().
Where(
attachment.HasItemWith(item.ID(itemId)),
attachment.TypeEQ(typ),
).
Count(ctx)
if err != nil {
return nil, err
}
if cnt == 0 {
bldr = bldr.SetPrimary(true)
}
}
if cnt == 0 {
bldr = bldr.SetPrimary(true)
}
}
return bldr.Save(ctx)
return bldr.Save(ctx)
}
func (r *AttachmentRepo) Get(ctx context.Context, id uuid.UUID) (*ent.Attachment, error) {
@@ -87,11 +87,11 @@ func (r *AttachmentRepo) Get(ctx context.Context, id uuid.UUID) (*ent.Attachment
Only(ctx)
}
func (r *AttachmentRepo) Update(ctx context.Context, itemID uuid.UUID, data *ItemAttachmentUpdate) (*ent.Attachment, error) {
func (r *AttachmentRepo) Update(ctx context.Context, itemId uuid.UUID, data *ItemAttachmentUpdate) (*ent.Attachment, error) {
// TODO: execute within Tx
typ := attachment.Type(data.Type)
bldr := r.db.Attachment.UpdateOneID(itemID).
bldr := r.db.Attachment.UpdateOneID(itemId).
SetType(typ)
// Primary only applies to photos
@@ -109,7 +109,7 @@ func (r *AttachmentRepo) Update(ctx context.Context, itemID uuid.UUID, data *Ite
// Ensure all other attachments are not primary
err = r.db.Attachment.Update().
Where(
attachment.HasItemWith(item.ID(itemID)),
attachment.HasItemWith(item.ID(itemId)),
attachment.IDNEQ(itm.ID),
).
SetPrimary(false).

View File

@@ -8,7 +8,6 @@ import (
"github.com/hay-kot/homebox/backend/internal/data/ent"
"github.com/hay-kot/homebox/backend/internal/data/ent/attachment"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestAttachmentRepo_Create(t *testing.T) {
@@ -24,8 +23,8 @@ func TestAttachmentRepo_Create(t *testing.T) {
type args struct {
ctx context.Context
itemID uuid.UUID
docID uuid.UUID
itemId uuid.UUID
docId uuid.UUID
typ attachment.Type
}
tests := []struct {
@@ -38,8 +37,8 @@ func TestAttachmentRepo_Create(t *testing.T) {
name: "create attachment",
args: args{
ctx: context.Background(),
itemID: item.ID,
docID: doc.ID,
itemId: item.ID,
docId: doc.ID,
typ: attachment.TypePhoto,
},
want: &ent.Attachment{
@@ -50,8 +49,8 @@ func TestAttachmentRepo_Create(t *testing.T) {
name: "create attachment with invalid item id",
args: args{
ctx: context.Background(),
itemID: uuid.New(),
docID: doc.ID,
itemId: uuid.New(),
docId: doc.ID,
typ: "blarg",
},
wantErr: true,
@@ -59,7 +58,7 @@ func TestAttachmentRepo_Create(t *testing.T) {
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := tRepos.Attachments.Create(tt.args.ctx, tt.args.itemID, tt.args.docID, tt.args.typ)
got, err := tRepos.Attachments.Create(tt.args.ctx, tt.args.itemId, tt.args.docId, tt.args.typ)
if (err != nil) != tt.wantErr {
t.Errorf("AttachmentRepo.Create() error = %v, wantErr %v", err, tt.wantErr)
return
@@ -72,9 +71,9 @@ func TestAttachmentRepo_Create(t *testing.T) {
assert.Equal(t, tt.want.Type, got.Type)
withItems, err := tRepos.Attachments.Get(tt.args.ctx, got.ID)
require.NoError(t, err)
assert.Equal(t, tt.args.itemID, withItems.Edges.Item.ID)
assert.Equal(t, tt.args.docID, withItems.Edges.Document.ID)
assert.NoError(t, err)
assert.Equal(t, tt.args.itemId, withItems.Edges.Item.ID)
assert.Equal(t, tt.args.docId, withItems.Edges.Document.ID)
ids = append(ids, got.ID)
})
@@ -97,7 +96,7 @@ func useAttachments(t *testing.T, n int) []*ent.Attachment {
attachments := make([]*ent.Attachment, n)
for i := 0; i < n; i++ {
attachment, err := tRepos.Attachments.Create(context.Background(), item.ID, doc.ID, attachment.TypePhoto)
require.NoError(t, err)
assert.NoError(t, err)
attachments[i] = attachment
ids = append(ids, attachment.ID)
@@ -115,10 +114,10 @@ func TestAttachmentRepo_Update(t *testing.T) {
Type: string(typ),
})
require.NoError(t, err)
assert.NoError(t, err)
updated, err := tRepos.Attachments.Get(context.Background(), entity.ID)
require.NoError(t, err)
assert.NoError(t, err)
assert.Equal(t, typ, updated.Type)
})
}
@@ -128,8 +127,8 @@ func TestAttachmentRepo_Delete(t *testing.T) {
entity := useAttachments(t, 1)[0]
err := tRepos.Attachments.Delete(context.Background(), entity.ID)
require.NoError(t, err)
assert.NoError(t, err)
_, err = tRepos.Attachments.Get(context.Background(), entity.ID)
require.Error(t, err)
assert.Error(t, err)
}

View File

@@ -55,8 +55,8 @@ type (
ItemCreate struct {
ImportRef string `json:"-"`
ParentID uuid.UUID `json:"parentId" extensions:"x-nullable"`
Name string `json:"name" validate:"required,min=1,max=255"`
ParentID uuid.UUID `json:"parentId" extensions:"x-nullable"`
Name string `json:"name" validate:"required,min=1,max=255"`
Description string `json:"description" validate:"max=1000"`
AssetID AssetID `json:"-"`
@@ -66,7 +66,7 @@ type (
}
ItemUpdate struct {
ParentID uuid.UUID `json:"parentId" extensions:"x-nullable,x-omitempty"`
ParentID uuid.UUID `json:"parentId" extensions:"x-nullable,x-omitempty"`
ID uuid.UUID `json:"id"`
AssetID AssetID `json:"assetId"`
Name string `json:"name"`
@@ -108,7 +108,7 @@ type (
ItemPatch struct {
ID uuid.UUID `json:"id"`
Quantity *int `json:"quantity,omitempty" extensions:"x-nullable,x-omitempty"`
ImportRef *string `json:"-,omitempty" extensions:"x-nullable,x-omitempty"`
ImportRef *string `json:"-,omitempty" extensions:"x-nullable,x-omitempty"`
}
ItemSummary struct {
@@ -276,9 +276,9 @@ func mapItemOut(item *ent.Item) ItemOut {
}
}
func (e *ItemsRepository) publishMutationEvent(GID uuid.UUID) {
if e.bus != nil {
e.bus.Publish(eventbus.EventItemMutation, eventbus.GroupMutationEvent{GID: GID})
func (r *ItemsRepository) publishMutationEvent(GID uuid.UUID) {
if r.bus != nil {
r.bus.Publish(eventbus.EventItemMutation, eventbus.GroupMutationEvent{GID: GID})
}
}

View File

@@ -22,7 +22,7 @@ func useItems(t *testing.T, len int) []ItemOut {
t.Helper()
location, err := tRepos.Locations.Create(context.Background(), tGroup.ID, locationFactory())
require.NoError(t, err)
assert.NoError(t, err)
items := make([]ItemOut, len)
for i := 0; i < len; i++ {
@@ -30,7 +30,7 @@ func useItems(t *testing.T, len int) []ItemOut {
itm.LocationID = location.ID
item, err := tRepos.Items.Create(context.Background(), tGroup.ID, itm)
require.NoError(t, err)
assert.NoError(t, err)
items[i] = item
}
@@ -61,22 +61,23 @@ func TestItemsRepository_RecursiveRelationships(t *testing.T) {
// Append Parent ID
_, err := tRepos.Items.UpdateByGroup(context.Background(), tGroup.ID, update)
require.NoError(t, err)
assert.NoError(t, err)
// Check Parent ID
updated, err := tRepos.Items.GetOne(context.Background(), child.ID)
require.NoError(t, err)
assert.NoError(t, err)
assert.Equal(t, parent.ID, updated.Parent.ID)
// Remove Parent ID
update.ParentID = uuid.Nil
_, err = tRepos.Items.UpdateByGroup(context.Background(), tGroup.ID, update)
require.NoError(t, err)
assert.NoError(t, err)
// Check Parent ID
updated, err = tRepos.Items.GetOne(context.Background(), child.ID)
require.NoError(t, err)
assert.NoError(t, err)
assert.Nil(t, updated.Parent)
}
}
@@ -85,7 +86,7 @@ func TestItemsRepository_GetOne(t *testing.T) {
for _, item := range entity {
result, err := tRepos.Items.GetOne(context.Background(), item.ID)
require.NoError(t, err)
assert.NoError(t, err)
assert.Equal(t, item.ID, result.ID)
}
}
@@ -95,9 +96,9 @@ func TestItemsRepository_GetAll(t *testing.T) {
expected := useItems(t, length)
results, err := tRepos.Items.GetAll(context.Background(), tGroup.ID)
require.NoError(t, err)
assert.NoError(t, err)
assert.Len(t, results, length)
assert.Equal(t, length, len(results))
for _, item := range results {
for _, expectedItem := range expected {
@@ -112,23 +113,23 @@ func TestItemsRepository_GetAll(t *testing.T) {
func TestItemsRepository_Create(t *testing.T) {
location, err := tRepos.Locations.Create(context.Background(), tGroup.ID, locationFactory())
require.NoError(t, err)
assert.NoError(t, err)
itm := itemFactory()
itm.LocationID = location.ID
result, err := tRepos.Items.Create(context.Background(), tGroup.ID, itm)
require.NoError(t, err)
assert.NoError(t, err)
assert.NotEmpty(t, result.ID)
// Cleanup - Also deletes item
err = tRepos.Locations.delete(context.Background(), location.ID)
require.NoError(t, err)
assert.NoError(t, err)
}
func TestItemsRepository_Create_Location(t *testing.T) {
location, err := tRepos.Locations.Create(context.Background(), tGroup.ID, locationFactory())
require.NoError(t, err)
assert.NoError(t, err)
assert.NotEmpty(t, location.ID)
item := itemFactory()
@@ -136,18 +137,18 @@ func TestItemsRepository_Create_Location(t *testing.T) {
// Create Resource
result, err := tRepos.Items.Create(context.Background(), tGroup.ID, item)
require.NoError(t, err)
assert.NoError(t, err)
assert.NotEmpty(t, result.ID)
// Get Resource
foundItem, err := tRepos.Items.GetOne(context.Background(), result.ID)
require.NoError(t, err)
assert.NoError(t, err)
assert.Equal(t, result.ID, foundItem.ID)
assert.Equal(t, location.ID, foundItem.Location.ID)
// Cleanup - Also deletes item
err = tRepos.Locations.delete(context.Background(), location.ID)
require.NoError(t, err)
assert.NoError(t, err)
}
func TestItemsRepository_Delete(t *testing.T) {
@@ -155,11 +156,11 @@ func TestItemsRepository_Delete(t *testing.T) {
for _, item := range entities {
err := tRepos.Items.Delete(context.Background(), item.ID)
require.NoError(t, err)
assert.NoError(t, err)
}
results, err := tRepos.Items.GetAll(context.Background(), tGroup.ID)
require.NoError(t, err)
assert.NoError(t, err)
assert.Empty(t, results)
}
@@ -212,7 +213,7 @@ func TestItemsRepository_Update_Labels(t *testing.T) {
}
updated, err := tRepos.Items.UpdateByGroup(context.Background(), tGroup.ID, updateData)
require.NoError(t, err)
assert.NoError(t, err)
assert.Len(t, tt.want, len(updated.Labels))
for _, label := range updated.Labels {
@@ -249,10 +250,10 @@ func TestItemsRepository_Update(t *testing.T) {
}
updatedEntity, err := tRepos.Items.UpdateByGroup(context.Background(), tGroup.ID, updateData)
require.NoError(t, err)
assert.NoError(t, err)
got, err := tRepos.Items.GetOne(context.Background(), updatedEntity.ID)
require.NoError(t, err)
assert.NoError(t, err)
assert.Equal(t, updateData.ID, got.ID)
assert.Equal(t, updateData.Name, got.Name)
@@ -262,10 +263,10 @@ func TestItemsRepository_Update(t *testing.T) {
assert.Equal(t, updateData.Manufacturer, got.Manufacturer)
// assert.Equal(t, updateData.PurchaseTime, got.PurchaseTime)
assert.Equal(t, updateData.PurchaseFrom, got.PurchaseFrom)
assert.InDelta(t, updateData.PurchasePrice, got.PurchasePrice, 0.01)
assert.Equal(t, updateData.PurchasePrice, got.PurchasePrice)
// assert.Equal(t, updateData.SoldTime, got.SoldTime)
assert.Equal(t, updateData.SoldTo, got.SoldTo)
assert.InDelta(t, updateData.SoldPrice, got.SoldPrice, 0.01)
assert.Equal(t, updateData.SoldPrice, got.SoldPrice)
assert.Equal(t, updateData.SoldNotes, got.SoldNotes)
assert.Equal(t, updateData.Notes, got.Notes)
// assert.Equal(t, updateData.WarrantyExpires, got.WarrantyExpires)
@@ -274,15 +275,15 @@ func TestItemsRepository_Update(t *testing.T) {
}
func TestItemRepository_GetAllCustomFields(t *testing.T) {
const FieldsCount = 5
const FIELDS_COUNT = 5
entity := useItems(t, 1)[0]
fields := make([]ItemField, FieldsCount)
names := make([]string, FieldsCount)
values := make([]string, FieldsCount)
fields := make([]ItemField, FIELDS_COUNT)
names := make([]string, FIELDS_COUNT)
values := make([]string, FIELDS_COUNT)
for i := 0; i < FieldsCount; i++ {
for i := 0; i < FIELDS_COUNT; i++ {
name := fk.Str(10)
fields[i] = ItemField{
Name: name,
@@ -305,7 +306,7 @@ func TestItemRepository_GetAllCustomFields(t *testing.T) {
// Test getting all fields
{
results, err := tRepos.Items.GetAllCustomFieldNames(context.Background(), tGroup.ID)
require.NoError(t, err)
assert.NoError(t, err)
assert.ElementsMatch(t, names, results)
}
@@ -313,7 +314,7 @@ func TestItemRepository_GetAllCustomFields(t *testing.T) {
{
results, err := tRepos.Items.GetAllCustomFieldValues(context.Background(), tUser.GroupID, names[0])
require.NoError(t, err)
assert.NoError(t, err)
assert.ElementsMatch(t, values[:1], results)
}
}

View File

@@ -19,14 +19,14 @@ type LabelRepository struct {
type (
LabelCreate struct {
Name string `json:"name" validate:"required,min=1,max=255"`
Name string `json:"name" validate:"required,min=1,max=255"`
Description string `json:"description" validate:"max=255"`
Color string `json:"color"`
}
LabelUpdate struct {
ID uuid.UUID `json:"id"`
Name string `json:"name" validate:"required,min=1,max=255"`
Name string `json:"name" validate:"required,min=1,max=255"`
Description string `json:"description" validate:"max=255"`
Color string `json:"color"`
}
@@ -87,28 +87,28 @@ func (r *LabelRepository) GetOneByGroup(ctx context.Context, gid, ld uuid.UUID)
return r.getOne(ctx, label.ID(ld), label.HasGroupWith(group.ID(gid)))
}
func (r *LabelRepository) GetAll(ctx context.Context, groupID uuid.UUID) ([]LabelSummary, error) {
func (r *LabelRepository) GetAll(ctx context.Context, groupId uuid.UUID) ([]LabelSummary, error) {
return mapLabelsOut(r.db.Label.Query().
Where(label.HasGroupWith(group.ID(groupID))).
Where(label.HasGroupWith(group.ID(groupId))).
Order(ent.Asc(label.FieldName)).
WithGroup().
All(ctx),
)
}
func (r *LabelRepository) Create(ctx context.Context, groupID uuid.UUID, data LabelCreate) (LabelOut, error) {
func (r *LabelRepository) Create(ctx context.Context, groupdId uuid.UUID, data LabelCreate) (LabelOut, error) {
label, err := r.db.Label.Create().
SetName(data.Name).
SetDescription(data.Description).
SetColor(data.Color).
SetGroupID(groupID).
SetGroupID(groupdId).
Save(ctx)
if err != nil {
return LabelOut{}, err
}
label.Edges.Group = &ent.Group{ID: groupID} // bootstrap group ID
r.publishMutationEvent(groupID)
label.Edges.Group = &ent.Group{ID: groupdId} // bootstrap group ID
r.publishMutationEvent(groupdId)
return mapLabelOut(label), err
}

View File

@@ -5,7 +5,6 @@ import (
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func labelFactory() LabelCreate {
@@ -23,7 +22,7 @@ func useLabels(t *testing.T, len int) []LabelOut {
itm := labelFactory()
item, err := tRepos.Labels.Create(context.Background(), tGroup.ID, itm)
require.NoError(t, err)
assert.NoError(t, err)
labels[i] = item
}
@@ -42,7 +41,7 @@ func TestLabelRepository_Get(t *testing.T) {
// Get by ID
foundLoc, err := tRepos.Labels.GetOne(context.Background(), label.ID)
require.NoError(t, err)
assert.NoError(t, err)
assert.Equal(t, label.ID, foundLoc.ID)
}
@@ -50,26 +49,26 @@ func TestLabelRepositoryGetAll(t *testing.T) {
useLabels(t, 10)
all, err := tRepos.Labels.GetAll(context.Background(), tGroup.ID)
require.NoError(t, err)
assert.NoError(t, err)
assert.Len(t, all, 10)
}
func TestLabelRepository_Create(t *testing.T) {
loc, err := tRepos.Labels.Create(context.Background(), tGroup.ID, labelFactory())
require.NoError(t, err)
assert.NoError(t, err)
// Get by ID
foundLoc, err := tRepos.Labels.GetOne(context.Background(), loc.ID)
require.NoError(t, err)
assert.NoError(t, err)
assert.Equal(t, loc.ID, foundLoc.ID)
err = tRepos.Labels.delete(context.Background(), loc.ID)
require.NoError(t, err)
assert.NoError(t, err)
}
func TestLabelRepository_Update(t *testing.T) {
loc, err := tRepos.Labels.Create(context.Background(), tGroup.ID, labelFactory())
require.NoError(t, err)
assert.NoError(t, err)
updateData := LabelUpdate{
ID: loc.ID,
@@ -78,26 +77,26 @@ func TestLabelRepository_Update(t *testing.T) {
}
update, err := tRepos.Labels.UpdateByGroup(context.Background(), tGroup.ID, updateData)
require.NoError(t, err)
assert.NoError(t, err)
foundLoc, err := tRepos.Labels.GetOne(context.Background(), loc.ID)
require.NoError(t, err)
assert.NoError(t, err)
assert.Equal(t, update.ID, foundLoc.ID)
assert.Equal(t, update.Name, foundLoc.Name)
assert.Equal(t, update.Description, foundLoc.Description)
err = tRepos.Labels.delete(context.Background(), loc.ID)
require.NoError(t, err)
assert.NoError(t, err)
}
func TestLabelRepository_Delete(t *testing.T) {
loc, err := tRepos.Labels.Create(context.Background(), tGroup.ID, labelFactory())
require.NoError(t, err)
assert.NoError(t, err)
err = tRepos.Labels.delete(context.Background(), loc.ID)
require.NoError(t, err)
assert.NoError(t, err)
_, err = tRepos.Labels.GetOne(context.Background(), loc.ID)
require.Error(t, err)
assert.Error(t, err)
}

View File

@@ -21,12 +21,12 @@ type LocationRepository struct {
type (
LocationCreate struct {
Name string `json:"name"`
ParentID uuid.UUID `json:"parentId" extensions:"x-nullable"`
ParentID uuid.UUID `json:"parentId" extensions:"x-nullable"`
Description string `json:"description"`
}
LocationUpdate struct {
ParentID uuid.UUID `json:"parentId" extensions:"x-nullable"`
ParentID uuid.UUID `json:"parentId" extensions:"x-nullable"`
ID uuid.UUID `json:"id"`
Name string `json:"name"`
Description string `json:"description"`
@@ -99,7 +99,7 @@ type LocationQuery struct {
FilterChildren bool `json:"filterChildren" schema:"filterChildren"`
}
// GetAll returns all locations with item count field populated
// GetALlWithCount returns all locations with item count field populated
func (r *LocationRepository) GetAll(ctx context.Context, GID uuid.UUID, filter LocationQuery) ([]LocationOutCount, error) {
query := `--sql
SELECT
@@ -135,7 +135,6 @@ func (r *LocationRepository) GetAll(ctx context.Context, GID uuid.UUID, filter L
if err != nil {
return nil, err
}
defer func() { _ = rows.Close() }()
list := []LocationOutCount{}
for rows.Next() {
@@ -265,7 +264,7 @@ type LocationPath struct {
Name string `json:"name"`
}
func (r *LocationRepository) PathForLoc(ctx context.Context, GID, locID uuid.UUID) ([]LocationPath, error) {
func (lr *LocationRepository) PathForLoc(ctx context.Context, GID, locID uuid.UUID) ([]LocationPath, error) {
query := `WITH RECURSIVE location_path AS (
SELECT id, name, location_children
FROM locations
@@ -282,11 +281,10 @@ func (r *LocationRepository) PathForLoc(ctx context.Context, GID, locID uuid.UUI
SELECT id, name
FROM location_path`
rows, err := r.db.Sql().QueryContext(ctx, query, locID, GID)
rows, err := lr.db.Sql().QueryContext(ctx, query, locID, GID)
if err != nil {
return nil, err
}
defer func() { _ = rows.Close() }()
var locations []LocationPath
@@ -311,7 +309,7 @@ func (r *LocationRepository) PathForLoc(ctx context.Context, GID, locID uuid.UUI
return locations, nil
}
func (r *LocationRepository) Tree(ctx context.Context, GID uuid.UUID, tq TreeQuery) ([]TreeItem, error) {
func (lr *LocationRepository) Tree(ctx context.Context, GID uuid.UUID, tq TreeQuery) ([]TreeItem, error) {
query := `
WITH recursive location_tree(id, NAME, parent_id, level, node_type) AS
(
@@ -393,11 +391,11 @@ func (r *LocationRepository) Tree(ctx context.Context, GID uuid.UUID, tq TreeQue
query = strings.ReplaceAll(query, "{{ WITH_ITEMS_FROM }}", "")
}
rows, err := r.db.Sql().QueryContext(ctx, query, GID)
rows, err := lr.db.Sql().QueryContext(ctx, query, GID)
if err != nil {
return nil, err
}
defer func() { _ = rows.Close() }()
defer rows.Close()
var locations []FlatTreeItem
for rows.Next() {

View File

@@ -8,7 +8,6 @@ import (
"github.com/google/uuid"
"github.com/hay-kot/homebox/backend/internal/data/ent"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func locationFactory() LocationCreate {
@@ -25,7 +24,7 @@ func useLocations(t *testing.T, len int) []LocationOut {
for i := 0; i < len; i++ {
loc, err := tRepos.Locations.Create(context.Background(), tGroup.ID, locationFactory())
require.NoError(t, err)
assert.NoError(t, err)
out[i] = loc
}
@@ -43,15 +42,15 @@ func useLocations(t *testing.T, len int) []LocationOut {
func TestLocationRepository_Get(t *testing.T) {
loc, err := tRepos.Locations.Create(context.Background(), tGroup.ID, locationFactory())
require.NoError(t, err)
assert.NoError(t, err)
// Get by ID
foundLoc, err := tRepos.Locations.Get(context.Background(), loc.ID)
require.NoError(t, err)
assert.NoError(t, err)
assert.Equal(t, loc.ID, foundLoc.ID)
err = tRepos.Locations.delete(context.Background(), loc.ID)
require.NoError(t, err)
assert.NoError(t, err)
}
func TestLocationRepositoryGetAllWithCount(t *testing.T) {
@@ -64,10 +63,10 @@ func TestLocationRepositoryGetAllWithCount(t *testing.T) {
LocationID: result.ID,
})
require.NoError(t, err)
assert.NoError(t, err)
results, err := tRepos.Locations.GetAll(context.Background(), tGroup.ID, LocationQuery{})
require.NoError(t, err)
assert.NoError(t, err)
for _, loc := range results {
if loc.ID == result.ID {
@@ -81,11 +80,11 @@ func TestLocationRepository_Create(t *testing.T) {
// Get by ID
foundLoc, err := tRepos.Locations.Get(context.Background(), loc.ID)
require.NoError(t, err)
assert.NoError(t, err)
assert.Equal(t, loc.ID, foundLoc.ID)
err = tRepos.Locations.delete(context.Background(), loc.ID)
require.NoError(t, err)
assert.NoError(t, err)
}
func TestLocationRepository_Update(t *testing.T) {
@@ -98,27 +97,27 @@ func TestLocationRepository_Update(t *testing.T) {
}
update, err := tRepos.Locations.UpdateByGroup(context.Background(), tGroup.ID, updateData.ID, updateData)
require.NoError(t, err)
assert.NoError(t, err)
foundLoc, err := tRepos.Locations.Get(context.Background(), loc.ID)
require.NoError(t, err)
assert.NoError(t, err)
assert.Equal(t, update.ID, foundLoc.ID)
assert.Equal(t, update.Name, foundLoc.Name)
assert.Equal(t, update.Description, foundLoc.Description)
err = tRepos.Locations.delete(context.Background(), loc.ID)
require.NoError(t, err)
assert.NoError(t, err)
}
func TestLocationRepository_Delete(t *testing.T) {
loc := useLocations(t, 1)[0]
err := tRepos.Locations.delete(context.Background(), loc.ID)
require.NoError(t, err)
assert.NoError(t, err)
_, err = tRepos.Locations.Get(context.Background(), loc.ID)
require.Error(t, err)
assert.Error(t, err)
}
func TestItemRepository_TreeQuery(t *testing.T) {
@@ -131,18 +130,18 @@ func TestItemRepository_TreeQuery(t *testing.T) {
Name: locs[0].Name,
Description: locs[0].Description,
})
require.NoError(t, err)
assert.NoError(t, err)
locations, err := tRepos.Locations.Tree(context.Background(), tGroup.ID, TreeQuery{WithItems: true})
require.NoError(t, err)
assert.NoError(t, err)
assert.Len(t, locations, 2)
assert.Equal(t, 2, len(locations))
// Check roots
for _, loc := range locations {
if loc.ID == locs[1].ID {
assert.Len(t, loc.Children, 1)
assert.Equal(t, 1, len(loc.Children))
}
}
}
@@ -158,15 +157,15 @@ func TestLocationRepository_PathForLoc(t *testing.T) {
Name: locs[i].Name,
Description: locs[i].Description,
})
require.NoError(t, err)
assert.NoError(t, err)
}
last := locs[0]
path, err := tRepos.Locations.PathForLoc(context.Background(), tGroup.ID, last.ID)
require.NoError(t, err)
assert.Len(t, path, 3)
assert.NoError(t, err)
assert.Equal(t, 3, len(path))
// Check path and order
for i, loc := range path {

View File

@@ -23,7 +23,7 @@ type MaintenanceEntryRepository struct {
type MaintenanceEntryCreate struct {
CompletedDate types.Date `json:"completedDate"`
ScheduledDate types.Date `json:"scheduledDate"`
Name string `json:"name" validate:"required"`
Name string `json:"name" validate:"required"`
Description string `json:"description"`
Cost float64 `json:"cost,string"`
}
@@ -152,6 +152,7 @@ func (r *MaintenanceEntryRepository) GetLog(ctx context.Context, groupID, itemID
maintenanceentry.DateNotNil(),
maintenanceentry.DateNEQ(time.Time{}),
))
} else if query.Scheduled {
q = q.Where(maintenanceentry.And(
maintenanceentry.Or(

View File

@@ -7,7 +7,6 @@ import (
"github.com/hay-kot/homebox/backend/internal/data/types"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
// get the previous month from the current month, accounts for errors when run
@@ -68,7 +67,7 @@ func TestMaintenanceEntryRepository_GetLog(t *testing.T) {
}
assert.Equal(t, item.ID, log.ItemID)
assert.Len(t, log.Entries, 10)
assert.Equal(t, 10, len(log.Entries))
// Calculate the average cost
var total float64
@@ -77,11 +76,11 @@ func TestMaintenanceEntryRepository_GetLog(t *testing.T) {
total += entry.Cost
}
assert.InDelta(t, total, log.CostTotal, .001, "total cost should be equal to the sum of all entries")
assert.InDelta(t, total/2, log.CostAverage, 001, "average cost should be the average of the two months")
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)
require.NoError(t, err)
assert.NoError(t, err)
}
}

View File

@@ -35,15 +35,15 @@ func NewNotifierRepository(db *ent.Client) *NotifierRepository {
type (
NotifierCreate struct {
Name string `json:"name" validate:"required,min=1,max=255"`
Name string `json:"name" validate:"required,min=1,max=255"`
IsActive bool `json:"isActive"`
URL string `json:"url" validate:"required,shoutrrr"`
URL string `json:"url" validate:"required,shoutrrr"`
}
NotifierUpdate struct {
Name string `json:"name" validate:"required,min=1,max=255"`
Name string `json:"name" validate:"required,min=1,max=255"`
IsActive bool `json:"isActive"`
URL *string `json:"url" validate:"omitempty,shoutrrr" extensions:"x-nullable"`
URL *string `json:"url" validate:"omitempty,shoutrrr" extensions:"x-nullable" `
}
NotifierOut struct {

View File

@@ -71,7 +71,7 @@ func (r *TokenRepository) GetRoles(ctx context.Context, token string) (*set.Set[
return &roleSet, nil
}
// CreateToken Creates a token for a user
// Creates a token for a user
func (r *TokenRepository) CreateToken(ctx context.Context, createToken UserAuthTokenCreate, roles ...authroles.Role) (UserAuthToken, error) {
dbToken, err := r.db.AuthTokens.Create().
SetToken(createToken.TokenHash).

View File

@@ -7,15 +7,15 @@ import (
"github.com/hay-kot/homebox/backend/pkgs/hasher"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestAuthTokenRepo_CreateToken(t *testing.T) {
asrt := assert.New(t)
ctx := context.Background()
user := userFactory()
userOut, err := tRepos.Users.Create(ctx, user)
require.NoError(t, err)
asrt.NoError(err)
expiresAt := time.Now().Add(time.Hour)
@@ -27,22 +27,23 @@ func TestAuthTokenRepo_CreateToken(t *testing.T) {
UserID: userOut.ID,
})
require.NoError(t, err)
assert.Equal(t, userOut.ID, token.UserID)
assert.Equal(t, expiresAt, token.ExpiresAt)
asrt.NoError(err)
asrt.Equal(userOut.ID, token.UserID)
asrt.Equal(expiresAt, token.ExpiresAt)
// Cleanup
require.NoError(t, tRepos.Users.Delete(ctx, userOut.ID))
asrt.NoError(tRepos.Users.Delete(ctx, userOut.ID))
_, err = tRepos.AuthTokens.DeleteAll(ctx)
require.NoError(t, err)
asrt.NoError(err)
}
func TestAuthTokenRepo_DeleteToken(t *testing.T) {
asrt := assert.New(t)
ctx := context.Background()
user := userFactory()
userOut, err := tRepos.Users.Create(ctx, user)
require.NoError(t, err)
asrt.NoError(err)
expiresAt := time.Now().Add(time.Hour)
@@ -53,14 +54,15 @@ func TestAuthTokenRepo_DeleteToken(t *testing.T) {
ExpiresAt: expiresAt,
UserID: userOut.ID,
})
require.NoError(t, err)
asrt.NoError(err)
// Delete token
err = tRepos.AuthTokens.DeleteToken(ctx, []byte(generatedToken.Raw))
require.NoError(t, err)
asrt.NoError(err)
}
func TestAuthTokenRepo_GetUserByToken(t *testing.T) {
assert := assert.New(t)
ctx := context.Background()
user := userFactory()
@@ -75,23 +77,24 @@ func TestAuthTokenRepo_GetUserByToken(t *testing.T) {
UserID: userOut.ID,
})
require.NoError(t, err)
assert.NoError(err)
// Get User from token
foundUser, err := tRepos.AuthTokens.GetUserFromToken(ctx, token.TokenHash)
require.NoError(t, err)
assert.Equal(t, userOut.ID, foundUser.ID)
assert.Equal(t, userOut.Name, foundUser.Name)
assert.Equal(t, userOut.Email, foundUser.Email)
assert.NoError(err)
assert.Equal(userOut.ID, foundUser.ID)
assert.Equal(userOut.Name, foundUser.Name)
assert.Equal(userOut.Email, foundUser.Email)
// Cleanup
require.NoError(t, tRepos.Users.Delete(ctx, userOut.ID))
assert.NoError(tRepos.Users.Delete(ctx, userOut.ID))
_, err = tRepos.AuthTokens.DeleteAll(ctx)
require.NoError(t, err)
assert.NoError(err)
}
func TestAuthTokenRepo_PurgeExpiredTokens(t *testing.T) {
assert := assert.New(t)
ctx := context.Background()
user := userFactory()
@@ -109,26 +112,27 @@ func TestAuthTokenRepo_PurgeExpiredTokens(t *testing.T) {
UserID: userOut.ID,
})
require.NoError(t, err)
assert.NotNil(t, createdToken)
assert.NoError(err)
assert.NotNil(createdToken)
createdTokens = append(createdTokens, createdToken)
}
// Purge expired tokens
tokensDeleted, err := tRepos.AuthTokens.PurgeExpiredTokens(ctx)
require.NoError(t, err)
assert.Equal(t, 5, tokensDeleted)
assert.NoError(err)
assert.Equal(5, tokensDeleted)
// Check if tokens are deleted
for _, token := range createdTokens {
_, err := tRepos.AuthTokens.GetUserFromToken(ctx, token.TokenHash)
require.Error(t, err)
assert.Error(err)
}
// Cleanup
require.NoError(t, tRepos.Users.Delete(ctx, userOut.ID))
assert.NoError(tRepos.Users.Delete(ctx, userOut.ID))
_, err = tRepos.AuthTokens.DeleteAll(ctx)
require.NoError(t, err)
assert.NoError(err)
}

View File

@@ -60,32 +60,32 @@ func mapUserOut(user *ent.User) UserOut {
}
}
func (r *UserRepository) GetOneID(ctx context.Context, ID uuid.UUID) (UserOut, error) {
return mapUserOutErr(r.db.User.Query().
Where(user.ID(ID)).
func (e *UserRepository) GetOneId(ctx context.Context, id uuid.UUID) (UserOut, error) {
return mapUserOutErr(e.db.User.Query().
Where(user.ID(id)).
WithGroup().
Only(ctx))
}
func (r *UserRepository) GetOneEmail(ctx context.Context, email string) (UserOut, error) {
return mapUserOutErr(r.db.User.Query().
func (e *UserRepository) GetOneEmail(ctx context.Context, email string) (UserOut, error) {
return mapUserOutErr(e.db.User.Query().
Where(user.EmailEqualFold(email)).
WithGroup().
Only(ctx),
)
}
func (r *UserRepository) GetAll(ctx context.Context) ([]UserOut, error) {
return mapUsersOutErr(r.db.User.Query().WithGroup().All(ctx))
func (e *UserRepository) GetAll(ctx context.Context) ([]UserOut, error) {
return mapUsersOutErr(e.db.User.Query().WithGroup().All(ctx))
}
func (r *UserRepository) Create(ctx context.Context, usr UserCreate) (UserOut, error) {
func (e *UserRepository) Create(ctx context.Context, usr UserCreate) (UserOut, error) {
role := user.RoleUser
if usr.IsOwner {
role = user.RoleOwner
}
entUser, err := r.db.User.
entUser, err := e.db.User.
Create().
SetName(usr.Name).
SetEmail(usr.Email).
@@ -98,11 +98,11 @@ func (r *UserRepository) Create(ctx context.Context, usr UserCreate) (UserOut, e
return UserOut{}, err
}
return r.GetOneID(ctx, entUser.ID)
return e.GetOneId(ctx, entUser.ID)
}
func (r *UserRepository) Update(ctx context.Context, ID uuid.UUID, data UserUpdate) error {
q := r.db.User.Update().
func (e *UserRepository) Update(ctx context.Context, ID uuid.UUID, data UserUpdate) error {
q := e.db.User.Update().
Where(user.ID(ID)).
SetName(data.Name).
SetEmail(data.Email)
@@ -111,18 +111,18 @@ func (r *UserRepository) Update(ctx context.Context, ID uuid.UUID, data UserUpda
return err
}
func (r *UserRepository) Delete(ctx context.Context, id uuid.UUID) error {
_, err := r.db.User.Delete().Where(user.ID(id)).Exec(ctx)
func (e *UserRepository) Delete(ctx context.Context, id uuid.UUID) error {
_, err := e.db.User.Delete().Where(user.ID(id)).Exec(ctx)
return err
}
func (r *UserRepository) DeleteAll(ctx context.Context) error {
_, err := r.db.User.Delete().Exec(ctx)
func (e *UserRepository) DeleteAll(ctx context.Context) error {
_, err := e.db.User.Delete().Exec(ctx)
return err
}
func (r *UserRepository) GetSuperusers(ctx context.Context) ([]*ent.User, error) {
users, err := r.db.User.Query().Where(user.IsSuperuser(true)).All(ctx)
func (e *UserRepository) GetSuperusers(ctx context.Context) ([]*ent.User, error) {
users, err := e.db.User.Query().Where(user.IsSuperuser(true)).All(ctx)
if err != nil {
return nil, err
}

View File

@@ -5,7 +5,6 @@ import (
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func userFactory() UserCreate {
@@ -24,18 +23,18 @@ func TestUserRepo_GetOneEmail(t *testing.T) {
ctx := context.Background()
_, err := tRepos.Users.Create(ctx, user)
require.NoError(t, err)
assert.NoError(err)
foundUser, err := tRepos.Users.GetOneEmail(ctx, user.Email)
assert.NotNil(foundUser)
require.NoError(t, err)
assert.Nil(err)
assert.Equal(user.Email, foundUser.Email)
assert.Equal(user.Name, foundUser.Name)
// Cleanup
err = tRepos.Users.DeleteAll(ctx)
require.NoError(t, err)
assert.NoError(err)
}
func TestUserRepo_GetOneId(t *testing.T) {
@@ -44,16 +43,16 @@ func TestUserRepo_GetOneId(t *testing.T) {
ctx := context.Background()
userOut, _ := tRepos.Users.Create(ctx, user)
foundUser, err := tRepos.Users.GetOneID(ctx, userOut.ID)
foundUser, err := tRepos.Users.GetOneId(ctx, userOut.ID)
assert.NotNil(foundUser)
require.NoError(t, err)
assert.Nil(err)
assert.Equal(user.Email, foundUser.Email)
assert.Equal(user.Name, foundUser.Name)
// Cleanup
err = tRepos.Users.DeleteAll(ctx)
require.NoError(t, err)
assert.NoError(err)
}
func TestUserRepo_GetAll(t *testing.T) {
@@ -77,7 +76,7 @@ func TestUserRepo_GetAll(t *testing.T) {
// Validate
allUsers, err := tRepos.Users.GetAll(ctx)
require.NoError(t, err)
assert.NoError(t, err)
assert.Equal(t, len(created), len(allUsers))
for _, usr := range created {
@@ -97,12 +96,12 @@ func TestUserRepo_GetAll(t *testing.T) {
// Cleanup
err = tRepos.Users.DeleteAll(ctx)
require.NoError(t, err)
assert.NoError(t, err)
}
func TestUserRepo_Update(t *testing.T) {
user, err := tRepos.Users.Create(context.Background(), userFactory())
require.NoError(t, err)
assert.NoError(t, err)
updateData := UserUpdate{
Name: fk.Str(10),
@@ -111,11 +110,11 @@ func TestUserRepo_Update(t *testing.T) {
// Update
err = tRepos.Users.Update(context.Background(), user.ID, updateData)
require.NoError(t, err)
assert.NoError(t, err)
// Validate
updated, err := tRepos.Users.GetOneID(context.Background(), user.ID)
require.NoError(t, err)
updated, err := tRepos.Users.GetOneId(context.Background(), user.ID)
assert.NoError(t, err)
assert.NotEqual(t, user.Name, updated.Name)
assert.NotEqual(t, user.Email, updated.Email)
}
@@ -132,12 +131,12 @@ func TestUserRepo_Delete(t *testing.T) {
ctx := context.Background()
allUsers, _ := tRepos.Users.GetAll(ctx)
assert.NotEmpty(t, allUsers)
assert.Greater(t, len(allUsers), 0)
err := tRepos.Users.DeleteAll(ctx)
require.NoError(t, err)
assert.NoError(t, err)
allUsers, _ = tRepos.Users.GetAll(ctx)
assert.Empty(t, allUsers)
assert.Equal(t, len(allUsers), 0)
}
func TestUserRepo_GetSuperusers(t *testing.T) {
@@ -161,7 +160,7 @@ func TestUserRepo_GetSuperusers(t *testing.T) {
ctx := context.Background()
superUsers, err := tRepos.Users.GetSuperusers(ctx)
require.NoError(t, err)
assert.NoError(t, err)
for _, usr := range superUsers {
assert.True(t, usr.IsSuperuser)
@@ -169,5 +168,5 @@ func TestUserRepo_GetSuperusers(t *testing.T) {
// Cleanup
err = tRepos.Users.DeleteAll(ctx)
require.NoError(t, err)
assert.NoError(t, err)
}

View File

@@ -1,4 +1,3 @@
// Package repo provides the data access layer for the application.
package repo
import (

View File

@@ -1,4 +1,3 @@
// Package types provides custom types for the application.
package types
import (

View File

@@ -1,4 +1,3 @@
// Package config provides the configuration for the application.
package config
import (
@@ -17,7 +16,7 @@ const (
type Config struct {
conf.Version
Mode string `yaml:"mode" conf:"default:development"` // development or production
Mode string `yaml:"mode" conf:"default:development"` // development or production
Web WebConfig `yaml:"web"`
Storage Storage `yaml:"storage"`
Log LoggerConf `yaml:"logger"`
@@ -28,13 +27,13 @@ type Config struct {
}
type Options struct {
AllowRegistration bool `yaml:"disable_registration" conf:"default:true"`
AllowRegistration bool `yaml:"disable_registration" conf:"default:true"`
AutoIncrementAssetID bool `yaml:"auto_increment_asset_id" conf:"default:true"`
}
type DebugConf struct {
Enabled bool `yaml:"enabled" conf:"default:false"`
Port string `yaml:"port" conf:"default:4000"`
Port string `yaml:"port" conf:"default:4000"`
}
type WebConfig struct {

View File

@@ -6,6 +6,6 @@ const (
type Storage struct {
// Data is the path to the root directory
Data string `yaml:"data" conf:"default:./.data"`
SqliteURL string `yaml:"sqlite-url" conf:"default:./.data/homebox.db?_pragma=busy_timeout=999&_pragma=journal_mode=WAL&_fk=1"`
Data string `yaml:"data" conf:"default:./.data"`
SqliteUrl string `yaml:"sqlite-url" conf:"default:./.data/homebox.db?_pragma=busy_timeout=1000&_pragma=journal_mode=WAL&_fk=1"`
}

View File

@@ -1,4 +1,3 @@
// Package validate provides a wrapper around the go-playground/validator package
package validate
import (
@@ -9,7 +8,7 @@ import (
var validate *validator.Validate
func init() { // nolint
func init() {
validate = validator.New()
err := validate.RegisterValidation("shoutrrr", func(fl validator.FieldLevel) bool {
@@ -53,16 +52,17 @@ func init() { // nolint
if err != nil {
panic(err)
}
}
// Check a struct for validation errors and returns any errors the occur. This
// Checks a struct for validation errors and returns any errors the occur. This
// wraps the validate.Struct() function and provides some error wrapping. When
// a validator.ValidationErrors is returned, it is wrapped transformed into a
// FieldErrors array and returned.
func Check(val any) error {
err := validate.Struct(val)
if err != nil {
verrors, ok := err.(validator.ValidationErrors) // nolint - we know it's a validator.ValidationErrors
verrors, ok := err.(validator.ValidationErrors)
if !ok {
return err
}

View File

@@ -1,2 +0,0 @@
// Package mid provides web middleware.
package mid

View File

@@ -44,7 +44,7 @@ func Errors(svr *server.Server, log zerolog.Logger) errchain.ErrorHandler {
case validate.IsFieldError(err):
code = http.StatusUnprocessableEntity
fieldErrors := err.(validate.FieldErrors) // nolint
fieldErrors := err.(validate.FieldErrors)
resp.Error = "Validation Error"
resp.Fields = map[string]string{}
@@ -52,7 +52,7 @@ func Errors(svr *server.Server, log zerolog.Logger) errchain.ErrorHandler {
resp.Fields[fieldError.Field] = fieldError.Error
}
case validate.IsRequestError(err):
requestError := err.(*validate.RequestError) // nolint
requestError := err.(*validate.RequestError)
resp.Error = requestError.Error()
if requestError.Status == 0 {

View File

@@ -1,4 +1,4 @@
// Package cgofreesqlite package provides a CGO free implementation of the sqlite3 driver. This wraps the
// sqlite package provides a CGO free implementation of the sqlite3 driver. This wraps the
// modernc.org/sqlite driver and adds the PRAGMA foreign_keys = ON; statement to the connection
// initialization as well as registering the driver with the sql package as "sqlite3" for compatibility
// with entgo.io
@@ -35,6 +35,6 @@ func (d CGOFreeSqliteDriver) Open(name string) (conn driver.Conn, err error) {
return conn, err
}
func init() { //nolint:gochecknoinits
func init() {
sql.Register("sqlite3", CGOFreeSqliteDriver{Driver: &sqlite.Driver{}})
}

View File

@@ -1,4 +1,3 @@
// Package faker provides a simple interface for generating fake data for testing.
package faker
import (

View File

@@ -1,2 +0,0 @@
// Package hasher provides a simple interface for hashing and verifying passwords.
package hasher

View File

@@ -9,7 +9,7 @@ import (
var enabled = true
func init() { // nolint: gochecknoinits
func init() {
disableHas := os.Getenv("UNSAFE_DISABLE_PASSWORD_PROJECTION") == "yes_i_am_sure"
if disableHas {

View File

@@ -1,4 +1,3 @@
// Package mailer provides a simple mailer for sending emails.
package mailer
import (

View File

@@ -5,7 +5,7 @@ import (
"os"
"testing"
"github.com/stretchr/testify/require"
"github.com/stretchr/testify/assert"
)
const (
@@ -59,5 +59,5 @@ func Test_Mailer(t *testing.T) {
err = mailer.Send(msg)
require.NoError(t, err)
assert.Nil(t, err)
}

View File

@@ -1,4 +1,3 @@
// Package pathlib provides a way to safely create a file path without overwriting any existing files.
package pathlib
import (
@@ -15,7 +14,7 @@ var dirReader dirReaderFunc = func(directory string) []string {
if err != nil {
return nil
}
defer func() { _ = f.Close() }()
defer f.Close()
names, err := f.Readdirnames(-1)
if err != nil {

View File

@@ -1,4 +1,3 @@
// Package set provides a simple set implementation.
package set
type key interface {

View File

@@ -1 +1 @@
mkdocs-material==9.5.2
mkdocs-material==9.4.14

View File

@@ -11,7 +11,6 @@
});
const md = new MarkdownIt({
breaks: true,
html: true,
linkify: true,
typographer: true,

View File

@@ -36,6 +36,9 @@ export function useUserApi(): UserClient {
if (r.status === 401) {
console.error("unauthorized request, invalidating session");
authCtx.invalidateSession();
if (window.location.pathname !== "/") {
window.location.href = "/";
}
}
});

View File

@@ -71,7 +71,6 @@ class AuthContext implements IAuthContext {
}
isAuthorized() {
console.debug("isAuthorized", this.token);
return this.token;
}

View File

@@ -4,7 +4,6 @@ export default defineNuxtRouteMiddleware(async () => {
if (!ctx.isAuthorized()) {
if (window.location.pathname !== "/") {
console.debug("[middleware/auth] isAuthorized returned false, redirecting to /");
return navigateTo("/");
}
}
@@ -14,7 +13,6 @@ export default defineNuxtRouteMiddleware(async () => {
const { data, error } = await api.user.self();
if (error) {
if (window.location.pathname !== "/") {
console.debug("[middleware/user] user is null and fetch failed, redirecting to /");
return navigateTo("/");
}
}

View File

@@ -15,9 +15,6 @@ export default defineNuxtConfig({
},
css: ["@/assets/css/main.css"],
pwa: {
workbox: {
navigateFallbackDenylist: [/^\/api/],
},
injectRegister: "script",
injectManifest: {
swSrc: "sw.js",

View File

@@ -31,7 +31,7 @@
"prettier": "^2.7.1",
"typescript": "^5.0.0",
"vite-plugin-eslint": "^1.8.1",
"vitest": "^1.0.0"
"vitest": "^0.34.0"
},
"dependencies": {
"@headlessui/vue": "^1.7.9",
@@ -48,7 +48,7 @@
"dompurify": "^3.0.0",
"h3": "^1.7.1",
"http-proxy": "^1.18.1",
"markdown-it": "^14.0.0",
"markdown-it": "^13.0.1",
"pinia": "^2.0.21",
"postcss": "^8.4.16",
"tailwindcss": "^3.1.8",

View File

@@ -23,11 +23,7 @@
const labelStore = useLabelStore();
const labels = computed(() => labelStore.labels);
const {
data: nullableItem,
refresh,
pending: requestPending,
} = useAsyncData(async () => {
const { data: nullableItem, refresh } = useAsyncData(async () => {
const { data, error } = await api.items.get(itemId.value);
if (error) {
toast.error("Failed to load item");
@@ -454,7 +450,7 @@
Delete
</BaseButton>
</div>
<div v-if="!requestPending" class="space-y-6">
<div class="space-y-6">
<BaseCard class="overflow-visible">
<template #title> Edit Details </template>
<template #title-actions>

697
frontend/pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff