Compare commits

..

11 Commits

Author SHA1 Message Date
Hayden
7c240a563b attempt to fix scroll behavior 2023-01-01 12:46:59 -09:00
Hayden
59ebf0af73 fix broken datetime 2022-12-31 16:57:30 -09:00
Hayden
25e01d9606 other fixes 2022-12-31 15:40:31 -09:00
Hayden
b5b65af6b4 resolve search state problems 2022-12-31 12:46:58 -09:00
Hayden
412977ea6c implement pagination for search page 2022-12-31 11:44:41 -09:00
Hayden
9f0b09be52 remove date from cards 2022-12-31 11:44:15 -09:00
Hayden
824a1d0bc4 add integer support 2022-12-31 11:44:05 -09:00
Hayden
c379a73a63 fix pagination issues on backend 2022-12-31 11:43:55 -09:00
Hayden
188bd054e5 fix test error 2022-12-31 10:19:44 -09:00
Hayden
d26118a515 UI updates for item card 2022-12-30 22:06:45 -09:00
Hayden
149f16a600 card option 1 2022-12-30 20:32:19 -09:00
333 changed files with 14872 additions and 26516 deletions

31
.github/dependabot.yml vendored Normal file
View File

@@ -0,0 +1,31 @@
version: 2
updates:
# Fetch and update latest `npm` packages
- package-ecosystem: npm
directory: "/frontend"
schedule:
interval: daily
time: "00:00"
open-pull-requests-limit: 10
reviewers:
- hay-kot
assignees:
- hay-kot
commit-message:
prefix: fix
prefix-development: chore
include: scope
- package-ecosystem: gomod
directory: backend
schedule:
interval: daily
time: "00:00"
open-pull-requests-limit: 10
reviewers:
- hay-kot
assignees:
- hay-kot
commit-message:
prefix: fix
prefix-development: chore
include: scope

View File

@@ -7,12 +7,12 @@ jobs:
Go:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v2
- name: Set up Go
uses: actions/setup-go@v4
uses: actions/setup-go@v2
with:
go-version: "1.20"
go-version: 1.19
- name: Install Task
uses: arduino/setup-task@v1

View File

@@ -4,37 +4,11 @@ on:
workflow_call:
jobs:
lint:
name: Lint
Frontend:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v3
with:
fetch-depth: 0
- uses: pnpm/action-setup@v2.2.4
with:
version: 6.0.2
- name: Install dependencies
run: pnpm install --shamefully-hoist
working-directory: frontend
- name: Run Lint
run: pnpm run lint:ci
working-directory: frontend
- name: Run Typecheck
run: pnpm run typecheck
working-directory: frontend
integration-tests:
name: Integration Tests
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v3
uses: actions/checkout@v2
with:
fetch-depth: 0
@@ -44,15 +18,15 @@ jobs:
repo-token: ${{ secrets.GITHUB_TOKEN }}
- name: Set up Go
uses: actions/setup-go@v4
uses: actions/setup-go@v2
with:
go-version: "1.20"
go-version: 1.19
- uses: actions/setup-node@v3
with:
node-version: 18
- uses: pnpm/action-setup@v2.2.4
- uses: pnpm/action-setup@v2.2.2
with:
version: 6.0.2
@@ -60,5 +34,9 @@ jobs:
run: pnpm install
working-directory: frontend
- name: Run linter 👀
run: pnpm lint
working-directory: "frontend"
- name: Run Integration Tests
run: task test:ci

View File

@@ -20,22 +20,22 @@ jobs:
name: "Publish Homebox"
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v2
- name: Set up Go
uses: actions/setup-go@v4
uses: actions/setup-go@v2
with:
go-version: "1.20"
go-version: 1.19
- name: Set up QEMU
id: qemu
uses: docker/setup-qemu-action@v2
uses: docker/setup-qemu-action@v1
with:
image: tonistiigi/binfmt:latest
platforms: all
- name: install buildx
id: buildx
uses: docker/setup-buildx-action@v2
uses: docker/setup-buildx-action@v1
with:
install: true

View File

@@ -1,4 +1,4 @@
name: Publish Dockers
name: Build Nightly
on:
push:
@@ -12,17 +12,31 @@ env:
FLY_API_TOKEN: ${{ secrets.FLY_API_TOKEN }}
jobs:
backend-tests:
name: "Backend Server Tests"
uses: hay-kot/homebox/.github/workflows/partial-backend.yaml@main
frontend-tests:
name: "Frontend and End-to-End Tests"
uses: hay-kot/homebox/.github/workflows/partial-frontend.yaml@main
deploy:
name: "Deploy Nightly to Fly.io"
runs-on: ubuntu-latest
needs:
- backend-tests
- frontend-tests
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v2
- uses: superfly/flyctl-actions/setup-flyctl@master
- run: flyctl deploy --remote-only
publish-nightly:
name: "Publish Nightly"
if: github.event_name != 'release'
needs:
- backend-tests
- frontend-tests
uses: hay-kot/homebox/.github/workflows/partial-publish.yaml@main
with:
tag: nightly
@@ -32,6 +46,9 @@ jobs:
publish-tag:
name: "Publish Tag"
if: github.event_name == 'release'
needs:
- backend-tests
- frontend-tests
uses: hay-kot/homebox/.github/workflows/partial-publish.yaml@main
with:
release: true
@@ -46,7 +63,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout main
uses: actions/checkout@v3
uses: actions/checkout@v2
- name: Deploy docs
uses: mhausenblas/mkdocs-deploy-gh-pages@master

View File

@@ -12,4 +12,4 @@ jobs:
frontend-tests:
name: "Frontend and End-to-End Tests"
uses: ./.github/workflows/partial-frontend.yaml
uses: ./.github/workflows/partial-frontend.yaml

View File

@@ -1,51 +0,0 @@
name: Publish Release
on:
push:
tags:
- v*
env:
FLY_API_TOKEN: ${{ secrets.FLY_API_TOKEN }}
jobs:
backend-tests:
name: "Backend Server Tests"
uses: hay-kot/homebox/.github/workflows/partial-backend.yaml@main
frontend-tests:
name: "Frontend and End-to-End Tests"
uses: hay-kot/homebox/.github/workflows/partial-frontend.yaml@main
goreleaser:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v3
with:
fetch-depth: 0
- name: Set up Go
uses: actions/setup-go@v3
- uses: pnpm/action-setup@v2
with:
version: 7.30.1
- name: Build Frontend and Copy to Backend
working-directory: frontend
run: |
pnpm install --shamefully-hoist
pnpm run build
cp -r ./.output/public ../backend/app/api/static/
- name: Run GoReleaser
uses: goreleaser/goreleaser-action@v4
with:
workdir: "backend"
distribution: goreleaser
version: latest
args: release --clean
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

9
.gitignore vendored
View File

@@ -45,12 +45,3 @@ node_modules
.output
.env
dist
.pnpm-store
backend/app/api/app
backend/app/api/__debug_bin
dist/
# Nuxt Publish Dir
backend/app/api/static/public/*
!backend/app/api/static/public/.gitkeep

View File

@@ -1,33 +0,0 @@
---
# yaml-language-server: $schema=https://hay-kot.github.io/scaffold/schema.json
messages:
pre: |
# Ent Model Generation
With Boilerplate!
post: |
Complete!
questions:
- name: "model"
prompt:
message: "What is the name of the model? (PascalCase)"
required: true
- name: "by_group"
prompt:
confirm: "Include a Group Edge? (group_id -> id)"
required: true
rewrites:
- from: 'templates/model.go'
to: 'backend/internal/data/ent/schema/{{ lower .Scaffold.model }}.go'
inject:
- name: "Insert Groups Edge"
path: 'backend/internal/data/ent/schema/group.go'
at: // $scaffold_edge
template: |
{{- if .Scaffold.by_group -}}
owned("{{ lower .Scaffold.model }}s", {{ .Scaffold.model }}.Type),
{{- end -}}

View File

@@ -1,40 +0,0 @@
package schema
import (
"entgo.io/ent"
"github.com/hay-kot/homebox/backend/internal/data/ent/schema/mixins"
)
type {{ .Scaffold.model }} struct {
ent.Schema
}
func ({{ .Scaffold.model }}) Mixin() []ent.Mixin {
return []ent.Mixin{
mixins.BaseMixin{},
{{- if .Scaffold.by_group }}
GroupMixin{ref: "{{ snakecase .Scaffold.model }}s"},
{{- end }}
}
}
// Fields of the {{ .Scaffold.model }}.
func ({{ .Scaffold.model }}) Fields() []ent.Field {
return []ent.Field{
// field.String("name").
}
}
// Edges of the {{ .Scaffold.model }}.
func ({{ .Scaffold.model }}) Edges() []ent.Edge {
return []ent.Edge{
// edge.From("group", Group.Type).
}
}
func ({{ .Scaffold.model }}) Indexes() []ent.Index {
return []ent.Index{
// index.Fields("token"),
}
}

47
.vscode/launch.json vendored
View File

@@ -1,47 +0,0 @@
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"compounds": [
{
"name": "Full Stack",
"configurations": ["Launch Backend", "Launch Frontend"],
"stopAll": true
}
],
"configurations": [
{
"name": "Launch Backend",
"type": "go",
"request": "launch",
"mode": "debug",
"program": "${workspaceRoot}/backend/app/api/",
"args": [],
"env": {
"HBOX_DEMO": "true",
"HBOX_LOG_LEVEL": "debug",
"HBOX_DEBUG_ENABLED": "true",
"HBOX_STORAGE_DATA": "${workspaceRoot}/backend/.data",
"HBOX_STORAGE_SQLITE_URL": "${workspaceRoot}/backend/.data/homebox.db?_fk=1"
},
},
{
"name": "Launch Frontend",
"type": "node",
"request": "launch",
"runtimeExecutable": "pnpm",
"runtimeArgs": [
"run",
"dev"
],
"cwd": "${workspaceFolder}/frontend",
"serverReadyAction": {
"action": "debugWithChrome",
"pattern": "Local: http://localhost:([0-9]+)",
"uriFormat": "http://localhost:%s",
"webRoot": "${workspaceFolder}/frontend"
}
}
]
}

14
.vscode/settings.json vendored
View File

@@ -9,11 +9,10 @@
"README.md": "LICENSE, SECURITY.md"
},
"cSpell.words": [
"debughandlers",
"Homebox"
"debughandlers"
],
// use ESLint to format code on save
"editor.formatOnSave": false,
"editor.formatOnSave": true,
"editor.defaultFormatter": "dbaeumer.vscode-eslint",
"editor.codeActionsOnSave": {
"source.fixAll.eslint": true
@@ -22,13 +21,4 @@
"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"
}

View File

@@ -48,10 +48,4 @@ start command `task: ui:dev`
1. The frontend is a Vue 3 app with Nuxt.js that uses Tailwind and DaisyUI for styling.
2. We're using Vitest for our automated testing. you can run these with `task ui:watch`.
3. Tests require the API server to be running and in some cases the first run will fail due to a race condition. If this happens just run the tests again and they should pass.
## Publishing Release
Create a new tag in github with the version number vX.X.X. This will trigger a new release to be created.
Test -> Goreleaser -> Publish Release -> Trigger Docker Builds -> Deploy Docs + Fly.io Demo
3. Tests require the API server to be running and in some cases the first run will fail due to a race condition. If this happens just run the tests again and they should pass.

View File

@@ -22,7 +22,7 @@ COPY ./backend .
RUN go get -d -v ./...
RUN rm -rf ./app/api/public
COPY --from=frontend-builder /app/.output/public ./app/api/static/public
RUN CGO_ENABLED=0 GOOS=linux go build \
RUN CGO_ENABLED=1 GOOS=linux go build \
-ldflags "-s -w -X main.commit=$COMMIT -X main.buildTime=$BUILD_TIME -X main.version=$VERSION" \
-o /go/bin/api \
-v ./app/api/*.go
@@ -41,10 +41,9 @@ COPY --from=builder /go/bin/api /app
RUN chmod +x /app/api
LABEL Name=homebox Version=0.0.1
LABEL org.opencontainers.image.source="https://github.com/hay-kot/homebox"
EXPOSE 7745
WORKDIR /app
VOLUME [ "/data" ]
ENTRYPOINT [ "/app/api" ]
CMD [ "/data/config.yml" ]
CMD [ "/data/config.yml" ]

View File

@@ -16,13 +16,10 @@
[Configuration & Docker Compose](https://hay-kot.github.io/homebox/quick-start)
```bash
docker run -d \
--name homebox \
--restart unless-stopped \
--publish 3100:7745 \
--env TZ=Europe/Bucharest \
--volume /path/to/data/folder/:/data \
ghcr.io/hay-kot/homebox:latest
docker run --name=homebox \
--restart=always \
--publish=3100:7745 \
ghcr.io/hay-kot/homebox:latest
```
## Credits

View File

@@ -1,8 +1,7 @@
version: "3"
env:
HBOX_STORAGE_SQLITE_URL: .data/homebox.db?_pragma=busy_timeout=1000&_pragma=journal_mode=WAL&_fk=1
HBOX_OPTIONS_ALLOW_REGISTRATION: true
HBOX_STORAGE_SQLITE_URL: .data/homebox.db?_fk=1
UNSAFE_DISABLE_PASSWORD_PROJECTION: "yes_i_am_sure"
tasks:
setup:
@@ -27,47 +26,46 @@ tasks:
--modular \
--path ./backend/app/api/static/docs/swagger.json \
--output ./frontend/lib/api/types
- go run ./backend/app/tools/typegen/main.go ./frontend/lib/api/types/data-contracts.ts
- cp ./backend/app/api/static/docs/swagger.json docs/docs/api/openapi-2.0.json
- python3 ./scripts/process-types.py ./frontend/lib/api/types/data-contracts.ts
sources:
- "./backend/app/api/**/*"
- "./backend/internal/data/**"
- "./backend/internal/core/services/**/*"
- "./backend/app/tools/typegen/main.go"
- "./backend/internal/services/**/*"
- "./scripts/process-types.py"
generates:
- "./frontend/lib/api/types/data-contracts.ts"
- "./backend/internal/data/ent/schema"
- "./backend/app/api/static/docs/swagger.json"
- "./backend/app/api/static/docs/swagger.yaml"
go:run:
desc: Starts the backend api server (depends on generate task)
dir: backend
deps:
- generate
cmds:
- go run ./app/api/ {{ .CLI_ARGS }}
- cd backend && go run ./app/api/ {{ .CLI_ARGS }}
silent: false
go:test:
desc: Runs all go tests using gotestsum - supports passing gotestsum args
dir: backend
cmds:
- gotestsum {{ .CLI_ARGS }} ./...
- cd backend && gotestsum {{ .CLI_ARGS }} ./...
go:coverage:
desc: Runs all go tests with -race flag and generates a coverage report
dir: backend
cmds:
- go test -race -coverprofile=coverage.out -covermode=atomic ./app/... ./internal/... ./pkgs/... -v -cover
- cd backend && go test -race -coverprofile=coverage.out -covermode=atomic ./app/... ./internal/... ./pkgs/... -v -cover
silent: true
go:tidy:
desc: Runs go mod tidy on the backend
dir: backend
cmds:
- go mod tidy
- cd backend && go mod tidy
go:lint:
desc: Runs golangci-lint
dir: backend
cmds:
- golangci-lint run ./...
- cd backend && golangci-lint run ./...
go:all:
desc: Runs all go test and lint related tasks
@@ -78,19 +76,19 @@ tasks:
go:build:
desc: Builds the backend binary
dir: backend
cmds:
- go build -o ../build/backend ./app/api
- cd backend && go build -o ../build/backend ./app/api
db:generate:
desc: Run Entgo.io Code Generation
dir: backend/internal/
cmds:
- |
go generate ./... \
cd backend/internal/ && go generate ./... \
--template=./data/ent/schema/templates/has_id.tmpl
sources:
- "./backend/internal/data/ent/schema/**/*"
generates:
- "./backend/internal/ent/"
db:migration:
desc: Runs the database diff engine to generate a SQL migration files
@@ -101,27 +99,13 @@ tasks:
ui:watch:
desc: Starts the vitest test runner in watch mode
dir: frontend
cmds:
- pnpm run test:watch
- cd frontend && pnpm run test:watch
ui:dev:
desc: Run frontend development server
dir: frontend
cmds:
- pnpm dev
ui:fix:
desc: Runs prettier and eslint on the frontend
dir: frontend
cmds:
- pnpm run lint:fix
ui:check:
desc: Runs type checking
dir: frontend
cmds:
- pnpm run typecheck
- cd frontend && pnpm dev
test:ci:
desc: Runs end-to-end test on a live server (only for use in CI)
@@ -131,12 +115,3 @@ tasks:
- sleep 5
- cd frontend && pnpm run test:ci
silent: true
pr:
desc: Runs all tasks required for a PR
cmds:
- task: generate
- task: go:all
- task: ui:check
- task: ui:fix
- task: test:ci

2
backend/.gitignore vendored
View File

@@ -1,2 +0,0 @@
dist/

View File

@@ -1,54 +0,0 @@
# This is an example .goreleaser.yml file with some sensible defaults.
# Make sure to check the documentation at https://goreleaser.com
before:
hooks:
# you may remove this if you don't need go generate
- go generate ./...
builds:
- main: ./app/api
env:
- CGO_ENABLED=0
goos:
- linux
- windows
- darwin
goarch:
- amd64
- "386"
- arm
- arm64
ignore:
- goos: windows
goarch: arm
- goos: windows
goarch: "386"
archives:
- format: tar.gz
# this name template makes the OS and Arch compatible with the results of uname.
name_template: >-
{{ .ProjectName }}_
{{- title .Os }}_
{{- if eq .Arch "amd64" }}x86_64
{{- else if eq .Arch "386" }}i386
{{- else }}{{ .Arch }}{{ end }}
{{- if .Arm }}v{{ .Arm }}{{ end }}
# use zip for windows archives
format_overrides:
- goos: windows
format: zip
checksum:
name_template: 'checksums.txt'
snapshot:
name_template: "{{ incpatch .Version }}-next"
changelog:
sort: asc
filters:
exclude:
- '^docs:'
- '^test:'
# The lines beneath this are called `modelines`. See `:help modeline`
# Feel free to remove those if you don't want/use them.
# yaml-language-server: $schema=https://goreleaser.com/static/schema.json
# vim: set ts=2 sw=2 tw=0 fo=cnqoj

View File

@@ -8,7 +8,7 @@ import (
"github.com/hay-kot/homebox/backend/internal/data/repo"
"github.com/hay-kot/homebox/backend/internal/sys/config"
"github.com/hay-kot/homebox/backend/pkgs/mailer"
"github.com/hay-kot/safeserve/server"
"github.com/hay-kot/homebox/backend/pkgs/server"
)
type app struct {
@@ -37,11 +37,8 @@ func new(conf *config.Config) *app {
}
func (a *app) startBgTask(t time.Duration, fn func()) {
timer := time.NewTimer(t)
for {
timer.Reset(t)
a.server.Background(fn)
<-timer.C
time.Sleep(t)
}
}

View File

@@ -2,6 +2,7 @@ package main
import (
"context"
"encoding/csv"
"strings"
"github.com/hay-kot/homebox/backend/internal/core/services"
@@ -9,7 +10,7 @@ import (
)
func (a *app) SetupDemo() {
csvText := `HB.import_ref,HB.location,HB.labels,HB.quantity,HB.name,HB.description,HB.insured,HB.serial_number,HB.model_number,HB.manufacturer,HB.notes,HB.purchase_from,HB.purchase_price,HB.purchase_time,HB.lifetime_warranty,HB.warranty_expires,HB.warranty_details,HB.sold_to,HB.sold_price,HB.sold_time,HB.sold_notes
csvText := `Import Ref,Location,Labels,Quantity,Name,Description,Insured,Serial Number,Model Number,Manufacturer,Notes,Purchase From,Purchased Price,Purchased Time,Lifetime Warranty,Warranty Expires,Warranty Details,Sold To,Sold Price,Sold Time,Sold Notes
,Garage,IOT;Home Assistant; Z-Wave,1,Zooz Universal Relay ZEN17,"Zooz 700 Series Z-Wave Universal Relay ZEN17 for Awnings, Garage Doors, Sprinklers, and More | 2 NO-C-NC Relays (20A, 10A) | Signal Repeater | Hub Required (Compatible with SmartThings and Hubitat)",,,ZEN17,Zooz,,Amazon,39.95,10/13/2021,,,,,,,
,Living Room,IOT;Home Assistant; Z-Wave,1,Zooz Motion Sensor,"Zooz Z-Wave Plus S2 Motion Sensor ZSE18 with Magnetic Mount, Works with Vera and SmartThings",,,ZSE18,Zooz,,Amazon,29.95,10/15/2021,,,,,,,
,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,,,,,,,
@@ -18,14 +19,16 @@ func (a *app) SetupDemo() {
,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{
Email: "demo@example.com",
Name: "Demo",
Password: "demo",
}
var (
registration = services.UserRegistration{
Email: "demo@example.com",
Name: "Demo",
Password: "demo",
}
)
// First check if we've already setup a demo user and skip if so
_, err := a.services.User.Login(context.Background(), registration.Email, registration.Password, false)
_, err := a.services.User.Login(context.Background(), registration.Email, registration.Password)
if err == nil {
return
}
@@ -36,10 +39,20 @@ func (a *app) SetupDemo() {
log.Fatal().Msg("Failed to setup demo")
}
token, _ := a.services.User.Login(context.Background(), registration.Email, registration.Password, false)
token, _ := a.services.User.Login(context.Background(), registration.Email, registration.Password)
self, _ := a.services.User.GetSelf(context.Background(), token.Raw)
_, err = a.services.Items.CsvImport(context.Background(), self.GroupID, strings.NewReader(csvText))
// Read CSV Text
reader := csv.NewReader(strings.NewReader(csvText))
reader.Comma = ','
records, err := reader.ReadAll()
if err != nil {
log.Err(err).Msg("Failed to read CSV")
log.Fatal().Msg("Failed to setup demo")
}
_, err = a.services.Items.CsvImport(context.Background(), self.GroupID, records)
if err != nil {
log.Err(err).Msg("Failed to import CSV")
log.Fatal().Msg("Failed to setup demo")

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.5 KiB

View File

@@ -5,26 +5,9 @@ import (
"github.com/hay-kot/homebox/backend/internal/core/services"
"github.com/hay-kot/homebox/backend/internal/data/repo"
"github.com/hay-kot/safeserve/errchain"
"github.com/hay-kot/safeserve/server"
"github.com/hay-kot/homebox/backend/pkgs/server"
)
type Results[T any] struct {
Items []T `json:"items"`
}
func WrapResults[T any](items []T) Results[T] {
return Results[T]{Items: items}
}
type Wrapped struct {
Item interface{} `json:"item"`
}
func Wrap(v any) Wrapped {
return Wrapped{Item: v}
}
func WithMaxUploadSize(maxUploadSize int64) func(*V1Controller) {
return func(ctrl *V1Controller) {
ctrl.maxUploadSize = maxUploadSize
@@ -61,13 +44,12 @@ type (
}
ApiSummary struct {
Healthy bool `json:"health"`
Versions []string `json:"versions"`
Title string `json:"title"`
Message string `json:"message"`
Build Build `json:"build"`
Demo bool `json:"demo"`
AllowRegistration bool `json:"allowRegistration"`
Healthy bool `json:"health"`
Versions []string `json:"versions"`
Title string `json:"title"`
Message string `json:"message"`
Build Build `json:"build"`
Demo bool `json:"demo"`
}
)
@@ -92,21 +74,19 @@ func NewControllerV1(svc *services.AllServices, repos *repo.AllRepos, options ..
}
// HandleBase godoc
//
// @Summary Application Info
// @Tags Base
// @Produce json
// @Success 200 {object} ApiSummary
// @Router /v1/status [GET]
func (ctrl *V1Controller) HandleBase(ready ReadyFunc, build Build) errchain.HandlerFunc {
// @Summary Retrieves the basic information about the API
// @Tags Base
// @Produce json
// @Success 200 {object} ApiSummary
// @Router /v1/status [GET]
func (ctrl *V1Controller) HandleBase(ready ReadyFunc, build Build) server.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) error {
return server.JSON(w, http.StatusOK, ApiSummary{
Healthy: ready(),
Title: "Homebox",
Message: "Track, Manage, and Organize your Things",
Build: build,
Demo: ctrl.isDemo,
AllowRegistration: ctrl.allowRegistration,
return server.Respond(w, http.StatusOK, ApiSummary{
Healthy: ready(),
Title: "Go API Template",
Message: "Welcome to the Go API Template Application!",
Build: build,
Demo: ctrl.isDemo,
})
}
}

View File

@@ -21,7 +21,7 @@ func (ctrl *V1Controller) routeID(r *http.Request) (uuid.UUID, error) {
func (ctrl *V1Controller) routeUUID(r *http.Request, key string) (uuid.UUID, error) {
ID, err := uuid.Parse(chi.URLParam(r, key))
if err != nil {
return uuid.Nil, validate.NewRouteKeyError(key)
return uuid.Nil, validate.NewInvalidRouteKeyError(key)
}
return ID, nil
}

View File

@@ -1,70 +1,35 @@
package v1
import (
"context"
"net/http"
"github.com/google/uuid"
"github.com/hay-kot/homebox/backend/internal/core/services"
"github.com/hay-kot/homebox/backend/internal/sys/validate"
"github.com/hay-kot/safeserve/errchain"
"github.com/hay-kot/safeserve/server"
"github.com/hay-kot/homebox/backend/pkgs/server"
"github.com/rs/zerolog/log"
)
type ActionAmountResult struct {
type EnsureAssetIDResult struct {
Completed int `json:"completed"`
}
func actionHandlerFactory(ref string, fn func(context.Context, uuid.UUID) (int, error)) errchain.HandlerFunc {
// 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 := fn(ctx, ctx.GID)
totalCompleted, err := ctrl.svc.Items.EnsureAssetID(ctx, ctx.GID)
if err != nil {
log.Err(err).Str("action_ref", ref).Msg("failed to run action")
log.Err(err).Msg("failed to ensure asset id")
return validate.NewRequestError(err, http.StatusInternalServerError)
}
return server.JSON(w, http.StatusOK, ActionAmountResult{Completed: totalCompleted})
return server.Respond(w, http.StatusOK, EnsureAssetIDResult{Completed: totalCompleted})
}
}
// HandleEnsureAssetID godoc
//
// @Summary Ensure Asset IDs
// @Description Ensures all items in the database have an asset ID
// @Tags Actions
// @Produce json
// @Success 200 {object} ActionAmountResult
// @Router /v1/actions/ensure-asset-ids [Post]
// @Security Bearer
func (ctrl *V1Controller) HandleEnsureAssetID() errchain.HandlerFunc {
return actionHandlerFactory("ensure asset IDs", ctrl.svc.Items.EnsureAssetID)
}
// HandleEnsureImportRefs godoc
//
// @Summary Ensures Import Refs
// @Description Ensures all items in the database have an import ref
// @Tags Actions
// @Produce json
// @Success 200 {object} ActionAmountResult
// @Router /v1/actions/ensure-import-refs [Post]
// @Security Bearer
func (ctrl *V1Controller) HandleEnsureImportRefs() errchain.HandlerFunc {
return actionHandlerFactory("ensure import refs", ctrl.svc.Items.EnsureImportRef)
}
// HandleItemDateZeroOut godoc
//
// @Summary Zero Out Time Fields
// @Description Resets all item date fields to the beginning of the day
// @Tags Actions
// @Produce json
// @Success 200 {object} ActionAmountResult
// @Router /v1/actions/zero-item-time-fields [Post]
// @Security Bearer
func (ctrl *V1Controller) HandleItemDateZeroOut() errchain.HandlerFunc {
return actionHandlerFactory("zero out date time", ctrl.repo.Items.ZeroOutTimeFields)
}

View File

@@ -1,62 +0,0 @@
package v1
import (
"net/http"
"strconv"
"strings"
"github.com/go-chi/chi/v5"
"github.com/hay-kot/homebox/backend/internal/core/services"
"github.com/hay-kot/homebox/backend/internal/data/repo"
"github.com/hay-kot/homebox/backend/internal/sys/validate"
"github.com/hay-kot/safeserve/errchain"
"github.com/hay-kot/safeserve/server"
"github.com/rs/zerolog/log"
)
// HandleAssetGet godocs
//
// @Summary Get Item by Asset ID
// @Tags Items
// @Produce json
// @Param id path string true "Asset ID"
// @Success 200 {object} repo.PaginationResult[repo.ItemSummary]{}
// @Router /v1/assets/{id} [GET]
// @Security Bearer
func (ctrl *V1Controller) HandleAssetGet() 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
// Convert the asset ID to an int64
assetId, err := strconv.ParseInt(assetIdParam, 10, 64)
if err != nil {
return err
}
pageParam := r.URL.Query().Get("page")
var page int64 = -1
if pageParam != "" {
page, err = strconv.ParseInt(pageParam, 10, 64)
if err != nil {
return server.JSON(w, http.StatusBadRequest, "Invalid page number")
}
}
pageSizeParam := r.URL.Query().Get("pageSize")
var pageSize int64 = -1
if pageSizeParam != "" {
pageSize, err = strconv.ParseInt(pageSizeParam, 10, 64)
if err != nil {
return server.JSON(w, http.StatusBadRequest, "Invalid page size")
}
}
items, err := ctrl.repo.Items.QueryByAssetID(r.Context(), ctx.GID, repo.AssetID(assetId), int(page), int(pageSize))
if err != nil {
log.Err(err).Msg("failed to get item")
return validate.NewRequestError(err, http.StatusInternalServerError)
}
return server.JSON(w, http.StatusOK, items)
}
}

View File

@@ -3,13 +3,11 @@ package v1
import (
"errors"
"net/http"
"strings"
"time"
"github.com/hay-kot/homebox/backend/internal/core/services"
"github.com/hay-kot/homebox/backend/internal/sys/validate"
"github.com/hay-kot/safeserve/errchain"
"github.com/hay-kot/safeserve/server"
"github.com/hay-kot/homebox/backend/pkgs/server"
"github.com/rs/zerolog/log"
)
@@ -21,46 +19,42 @@ type (
}
LoginForm struct {
Username string `json:"username"`
Password string `json:"password"`
StayLoggedIn bool `json:"stayLoggedIn"`
Username string `json:"username"`
Password string `json:"password"`
}
)
// HandleAuthLogin godoc
//
// @Summary User Login
// @Tags Authentication
// @Accept x-www-form-urlencoded
// @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"
// @Produce json
// @Success 200 {object} TokenResponse
// @Router /v1/users/login [POST]
func (ctrl *V1Controller) HandleAuthLogin() errchain.HandlerFunc {
// @Summary User Login
// @Tags Authentication
// @Accept x-www-form-urlencoded
// @Accept application/json
// @Param username formData string false "string" example(admin@admin.com)
// @Param password formData string false "string" example(admin)
// @Produce json
// @Success 200 {object} TokenResponse
// @Router /v1/users/login [POST]
func (ctrl *V1Controller) HandleAuthLogin() server.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) error {
loginForm := &LoginForm{}
switch r.Header.Get("Content-Type") {
case "application/x-www-form-urlencoded":
case server.ContentFormUrlEncoded:
err := r.ParseForm()
if err != nil {
return errors.New("failed to parse form")
return server.Respond(w, http.StatusBadRequest, server.Wrap(err))
}
loginForm.Username = r.PostFormValue("username")
loginForm.Password = r.PostFormValue("password")
loginForm.StayLoggedIn = r.PostFormValue("stayLoggedIn") == "true"
case "application/json":
case server.ContentJSON:
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"))
return server.Respond(w, http.StatusBadRequest, errors.New("invalid content type"))
}
if loginForm.Username == "" || loginForm.Password == "" {
@@ -76,12 +70,13 @@ func (ctrl *V1Controller) HandleAuthLogin() errchain.HandlerFunc {
)
}
newToken, err := ctrl.svc.User.Login(r.Context(), strings.ToLower(loginForm.Username), loginForm.Password, loginForm.StayLoggedIn)
newToken, err := ctrl.svc.User.Login(r.Context(), loginForm.Username, loginForm.Password)
if err != nil {
return validate.NewRequestError(errors.New("authentication failed"), http.StatusInternalServerError)
}
return server.JSON(w, http.StatusOK, TokenResponse{
return server.Respond(w, http.StatusOK, TokenResponse{
Token: "Bearer " + newToken.Raw,
ExpiresAt: newToken.ExpiresAt,
AttachmentToken: newToken.AttachmentToken,
@@ -90,13 +85,12 @@ func (ctrl *V1Controller) HandleAuthLogin() errchain.HandlerFunc {
}
// HandleAuthLogout godoc
//
// @Summary User Logout
// @Tags Authentication
// @Success 204
// @Router /v1/users/logout [POST]
// @Security Bearer
func (ctrl *V1Controller) HandleAuthLogout() errchain.HandlerFunc {
// @Summary User Logout
// @Tags Authentication
// @Success 204
// @Router /v1/users/logout [POST]
// @Security Bearer
func (ctrl *V1Controller) HandleAuthLogout() server.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) error {
token := services.UseTokenCtx(r.Context())
if token == "" {
@@ -108,20 +102,19 @@ func (ctrl *V1Controller) HandleAuthLogout() errchain.HandlerFunc {
return validate.NewRequestError(err, http.StatusInternalServerError)
}
return server.JSON(w, http.StatusNoContent, nil)
return server.Respond(w, http.StatusNoContent, nil)
}
}
// HandleAuthLogout godoc
//
// @Summary User Token Refresh
// @Description handleAuthRefresh returns a handler that will issue a new token from an existing token.
// @Description This does not validate that the user still exists within the database.
// @Tags Authentication
// @Success 200
// @Router /v1/users/refresh [GET]
// @Security Bearer
func (ctrl *V1Controller) HandleAuthRefresh() errchain.HandlerFunc {
// @Summary User Token Refresh
// @Description handleAuthRefresh returns a handler that will issue a new token from an existing token.
// @Description This does not validate that the user still exists within the database.
// @Tags Authentication
// @Success 200
// @Router /v1/users/refresh [GET]
// @Security Bearer
func (ctrl *V1Controller) HandleAuthRefresh() server.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) error {
requestToken := services.UseTokenCtx(r.Context())
if requestToken == "" {
@@ -133,6 +126,6 @@ func (ctrl *V1Controller) HandleAuthRefresh() errchain.HandlerFunc {
return validate.NewUnauthorizedError()
}
return server.JSON(w, http.StatusOK, newToken)
return server.Respond(w, http.StatusOK, newToken)
}
}

View File

@@ -6,13 +6,14 @@ import (
"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/web/adapters"
"github.com/hay-kot/safeserve/errchain"
"github.com/hay-kot/homebox/backend/internal/sys/validate"
"github.com/hay-kot/homebox/backend/pkgs/server"
"github.com/rs/zerolog/log"
)
type (
GroupInvitationCreate struct {
Uses int `json:"uses" validate:"required,min=1,max=100"`
Uses int `json:"uses"`
ExpiresAt time.Time `json:"expiresAt"`
}
@@ -24,65 +25,93 @@ type (
)
// HandleGroupGet godoc
//
// @Summary Get Group
// @Tags Group
// @Produce json
// @Success 200 {object} repo.Group
// @Router /v1/groups [Get]
// @Security Bearer
func (ctrl *V1Controller) HandleGroupGet() errchain.HandlerFunc {
fn := func(r *http.Request) (repo.Group, error) {
auth := services.NewContext(r.Context())
return ctrl.repo.Groups.GroupByID(auth, auth.GID)
}
return adapters.Command(fn, http.StatusOK)
// @Summary Get the current user's group
// @Tags Group
// @Produce json
// @Success 200 {object} repo.Group
// @Router /v1/groups [Get]
// @Security Bearer
func (ctrl *V1Controller) HandleGroupGet() server.HandlerFunc {
return ctrl.handleGroupGeneral()
}
// HandleGroupUpdate godoc
//
// @Summary Update Group
// @Tags Group
// @Produce json
// @Param payload body repo.GroupUpdate true "User Data"
// @Success 200 {object} repo.Group
// @Router /v1/groups [Put]
// @Security Bearer
func (ctrl *V1Controller) HandleGroupUpdate() errchain.HandlerFunc {
fn := func(r *http.Request, body repo.GroupUpdate) (repo.Group, error) {
auth := services.NewContext(r.Context())
return ctrl.svc.Group.UpdateGroup(auth, body)
}
// @Summary Updates some fields of the current users group
// @Tags Group
// @Produce json
// @Param payload body repo.GroupUpdate true "User Data"
// @Success 200 {object} repo.Group
// @Router /v1/groups [Put]
// @Security Bearer
func (ctrl *V1Controller) HandleGroupUpdate() server.HandlerFunc {
return ctrl.handleGroupGeneral()
}
return adapters.Action(fn, http.StatusOK)
func (ctrl *V1Controller) handleGroupGeneral() server.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) error {
ctx := services.NewContext(r.Context())
switch r.Method {
case http.MethodGet:
group, err := ctrl.repo.Groups.GroupByID(ctx, ctx.GID)
if err != nil {
log.Err(err).Msg("failed to get group")
return validate.NewRequestError(err, http.StatusInternalServerError)
}
return server.Respond(w, http.StatusOK, group)
case http.MethodPut:
data := repo.GroupUpdate{}
if err := server.Decode(r, &data); err != nil {
return validate.NewRequestError(err, http.StatusBadRequest)
}
group, err := ctrl.svc.Group.UpdateGroup(ctx, data)
if err != nil {
log.Err(err).Msg("failed to update group")
return validate.NewRequestError(err, http.StatusInternalServerError)
}
return server.Respond(w, http.StatusOK, group)
}
return nil
}
}
// HandleGroupInvitationsCreate godoc
//
// @Summary Create Group Invitation
// @Tags Group
// @Produce json
// @Param payload body GroupInvitationCreate true "User Data"
// @Success 200 {object} GroupInvitation
// @Router /v1/groups/invitations [Post]
// @Security Bearer
func (ctrl *V1Controller) HandleGroupInvitationsCreate() errchain.HandlerFunc {
fn := func(r *http.Request, body GroupInvitationCreate) (GroupInvitation, error) {
if body.ExpiresAt.IsZero() {
body.ExpiresAt = time.Now().Add(time.Hour * 24)
// @Summary Get the current user
// @Tags Group
// @Produce json
// @Param payload body GroupInvitationCreate true "User Data"
// @Success 200 {object} GroupInvitation
// @Router /v1/groups/invitations [Post]
// @Security Bearer
func (ctrl *V1Controller) HandleGroupInvitationsCreate() server.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) error {
data := GroupInvitationCreate{}
if err := server.Decode(r, &data); err != nil {
log.Err(err).Msg("failed to decode user registration data")
return validate.NewRequestError(err, http.StatusBadRequest)
}
auth := services.NewContext(r.Context())
if data.ExpiresAt.IsZero() {
data.ExpiresAt = time.Now().Add(time.Hour * 24)
}
token, err := ctrl.svc.Group.NewInvitation(auth, body.Uses, body.ExpiresAt)
ctx := services.NewContext(r.Context())
return GroupInvitation{
token, err := ctrl.svc.Group.NewInvitation(ctx, data.Uses, data.ExpiresAt)
if err != nil {
log.Err(err).Msg("failed to create new token")
return validate.NewRequestError(err, http.StatusInternalServerError)
}
return server.Respond(w, http.StatusCreated, GroupInvitation{
Token: token,
ExpiresAt: body.ExpiresAt,
Uses: body.Uses,
}, err
ExpiresAt: data.ExpiresAt,
Uses: data.Uses,
})
}
return adapters.Action(fn, http.StatusCreated)
}

View File

@@ -2,222 +2,176 @@ package v1
import (
"database/sql"
"encoding/csv"
"errors"
"net/http"
"strings"
"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"
"github.com/hay-kot/homebox/backend/internal/web/adapters"
"github.com/hay-kot/safeserve/errchain"
"github.com/hay-kot/safeserve/server"
"github.com/hay-kot/homebox/backend/pkgs/server"
"github.com/rs/zerolog/log"
)
// HandleItemsGetAll godoc
//
// @Summary Query All Items
// @Tags Items
// @Produce json
// @Param q query string false "search string"
// @Param page query int false "page number"
// @Param pageSize query int false "items per page"
// @Param labels query []string false "label Ids" collectionFormat(multi)
// @Param locations query []string false "location Ids" collectionFormat(multi)
// @Success 200 {object} repo.PaginationResult[repo.ItemSummary]{}
// @Router /v1/items [GET]
// @Security Bearer
func (ctrl *V1Controller) HandleItemsGetAll() errchain.HandlerFunc {
// @Summary Get All Items
// @Tags Items
// @Produce json
// @Param q query string false "search string"
// @Param page query int false "page number"
// @Param pageSize query int false "items per page"
// @Param labels query []string false "label Ids" collectionFormat(multi)
// @Param locations query []string false "location Ids" collectionFormat(multi)
// @Success 200 {object} repo.PaginationResult[repo.ItemSummary]{}
// @Router /v1/items [GET]
// @Security Bearer
func (ctrl *V1Controller) HandleItemsGetAll() server.HandlerFunc {
extractQuery := func(r *http.Request) repo.ItemQuery {
params := r.URL.Query()
filterFieldItems := func(raw []string) []repo.FieldQuery {
var items []repo.FieldQuery
for _, v := range raw {
parts := strings.SplitN(v, "=", 2)
if len(parts) == 2 {
items = append(items, repo.FieldQuery{
Name: parts[0],
Value: parts[1],
})
}
}
return items
}
v := repo.ItemQuery{
return repo.ItemQuery{
Page: queryIntOrNegativeOne(params.Get("page")),
PageSize: queryIntOrNegativeOne(params.Get("pageSize")),
Search: params.Get("q"),
LocationIDs: queryUUIDList(params, "locations"),
LabelIDs: queryUUIDList(params, "labels"),
IncludeArchived: queryBool(params.Get("includeArchived")),
Fields: filterFieldItems(params["fields"]),
}
if strings.HasPrefix(v.Search, "#") {
aidStr := strings.TrimPrefix(v.Search, "#")
aid, ok := repo.ParseAssetID(aidStr)
if ok {
v.Search = ""
v.AssetID = aid
}
}
return v
}
return func(w http.ResponseWriter, r *http.Request) error {
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.JSON(w, http.StatusOK, repo.PaginationResult[repo.ItemSummary]{
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)
}
return server.JSON(w, http.StatusOK, items)
return server.Respond(w, http.StatusOK, items)
}
}
// HandleItemsCreate godoc
//
// @Summary Create Item
// @Tags Items
// @Produce json
// @Param payload body repo.ItemCreate true "Item Data"
// @Success 201 {object} repo.ItemSummary
// @Router /v1/items [POST]
// @Security Bearer
func (ctrl *V1Controller) HandleItemsCreate() errchain.HandlerFunc {
fn := func(r *http.Request, body repo.ItemCreate) (repo.ItemOut, error) {
return ctrl.svc.Items.Create(services.NewContext(r.Context()), body)
}
// @Summary Create a new item
// @Tags Items
// @Produce json
// @Param payload body repo.ItemCreate true "Item Data"
// @Success 200 {object} repo.ItemSummary
// @Router /v1/items [POST]
// @Security Bearer
func (ctrl *V1Controller) HandleItemsCreate() server.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) error {
createData := repo.ItemCreate{}
if err := server.Decode(r, &createData); err != nil {
log.Err(err).Msg("failed to decode request body")
return validate.NewRequestError(err, http.StatusInternalServerError)
}
return adapters.Action(fn, http.StatusCreated)
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)
}
return server.Respond(w, http.StatusCreated, item)
}
}
// HandleItemGet godocs
//
// @Summary Get Item
// @Tags Items
// @Produce json
// @Param id path string true "Item ID"
// @Success 200 {object} repo.ItemOut
// @Router /v1/items/{id} [GET]
// @Security Bearer
func (ctrl *V1Controller) HandleItemGet() errchain.HandlerFunc {
fn := func(r *http.Request, ID uuid.UUID) (repo.ItemOut, error) {
auth := services.NewContext(r.Context())
return ctrl.repo.Items.GetOneByGroup(auth, auth.GID, ID)
}
return adapters.CommandID("id", fn, http.StatusOK)
// @Summary Gets a item and fields
// @Tags Items
// @Produce json
// @Param id path string true "Item ID"
// @Success 200 {object} repo.ItemOut
// @Router /v1/items/{id} [GET]
// @Security Bearer
func (ctrl *V1Controller) HandleItemGet() server.HandlerFunc {
return ctrl.handleItemsGeneral()
}
// HandleItemDelete godocs
//
// @Summary Delete Item
// @Tags Items
// @Produce json
// @Param id path string true "Item ID"
// @Success 204
// @Router /v1/items/{id} [DELETE]
// @Security Bearer
func (ctrl *V1Controller) HandleItemDelete() errchain.HandlerFunc {
fn := func(r *http.Request, ID uuid.UUID) (any, error) {
auth := services.NewContext(r.Context())
err := ctrl.repo.Items.DeleteByGroup(auth, auth.GID, ID)
return nil, err
}
return adapters.CommandID("id", fn, http.StatusNoContent)
// @Summary deletes a item
// @Tags Items
// @Produce json
// @Param id path string true "Item ID"
// @Success 204
// @Router /v1/items/{id} [DELETE]
// @Security Bearer
func (ctrl *V1Controller) HandleItemDelete() server.HandlerFunc {
return ctrl.handleItemsGeneral()
}
// HandleItemUpdate godocs
//
// @Summary Update Item
// @Tags Items
// @Produce json
// @Param id path string true "Item ID"
// @Param payload body repo.ItemUpdate true "Item Data"
// @Success 200 {object} repo.ItemOut
// @Router /v1/items/{id} [PUT]
// @Security Bearer
func (ctrl *V1Controller) HandleItemUpdate() errchain.HandlerFunc {
fn := func(r *http.Request, ID uuid.UUID, body repo.ItemUpdate) (repo.ItemOut, error) {
auth := services.NewContext(r.Context())
body.ID = ID
return ctrl.repo.Items.UpdateByGroup(auth, auth.GID, body)
}
return adapters.ActionID("id", fn, http.StatusOK)
// @Summary updates a item
// @Tags Items
// @Produce json
// @Param id path string true "Item ID"
// @Param payload body repo.ItemUpdate true "Item Data"
// @Success 200 {object} repo.ItemOut
// @Router /v1/items/{id} [PUT]
// @Security Bearer
func (ctrl *V1Controller) HandleItemUpdate() server.HandlerFunc {
return ctrl.handleItemsGeneral()
}
// HandleGetAllCustomFieldNames godocs
//
// @Summary Get All Custom Field Names
// @Tags Items
// @Produce json
// @Success 200
// @Router /v1/items/fields [GET]
// @Success 200 {object} []string
// @Security Bearer
func (ctrl *V1Controller) HandleGetAllCustomFieldNames() errchain.HandlerFunc {
fn := func(r *http.Request) ([]string, error) {
auth := services.NewContext(r.Context())
return ctrl.repo.Items.GetAllCustomFieldNames(auth, auth.GID)
func (ctrl *V1Controller) handleItemsGeneral() server.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) error {
ctx := services.NewContext(r.Context())
ID, err := ctrl.routeID(r)
if err != nil {
return err
}
switch r.Method {
case http.MethodGet:
items, err := ctrl.repo.Items.GetOneByGroup(r.Context(), ctx.GID, ID)
if err != nil {
log.Err(err).Msg("failed to get item")
return validate.NewRequestError(err, http.StatusInternalServerError)
}
return server.Respond(w, http.StatusOK, items)
case http.MethodDelete:
err = ctrl.repo.Items.DeleteByGroup(r.Context(), ctx.GID, ID)
if err != nil {
log.Err(err).Msg("failed to delete item")
return validate.NewRequestError(err, http.StatusInternalServerError)
}
return server.Respond(w, http.StatusNoContent, nil)
case http.MethodPut:
body := repo.ItemUpdate{}
if err := server.Decode(r, &body); err != nil {
log.Err(err).Msg("failed to decode request body")
return validate.NewRequestError(err, http.StatusInternalServerError)
}
body.ID = ID
result, err := ctrl.repo.Items.UpdateByGroup(r.Context(), ctx.GID, body)
if err != nil {
log.Err(err).Msg("failed to update item")
return validate.NewRequestError(err, http.StatusInternalServerError)
}
return server.Respond(w, http.StatusOK, result)
}
return nil
}
return adapters.Command(fn, http.StatusOK)
}
// HandleGetAllCustomFieldValues godocs
//
// @Summary Get All Custom Field Values
// @Tags Items
// @Produce json
// @Success 200
// @Router /v1/items/fields/values [GET]
// @Success 200 {object} []string
// @Security Bearer
func (ctrl *V1Controller) HandleGetAllCustomFieldValues() errchain.HandlerFunc {
type query struct {
Field string `schema:"field" validate:"required"`
}
fn := func(r *http.Request, q query) ([]string, error) {
auth := services.NewContext(r.Context())
return ctrl.repo.Items.GetAllCustomFieldValues(auth, auth.GID, q.Field)
}
return adapters.Action(fn, http.StatusOK)
}
// HandleItemsImport godocs
//
// @Summary Import Items
// @Tags Items
// @Produce json
// @Success 204
// @Param csv formData file true "Image to upload"
// @Router /v1/items/import [Post]
// @Security Bearer
func (ctrl *V1Controller) HandleItemsImport() errchain.HandlerFunc {
// @Summary imports items into the database
// @Tags Items
// @Produce json
// @Success 204
// @Param csv formData file true "Image to upload"
// @Router /v1/items/import [Post]
// @Security Bearer
func (ctrl *V1Controller) HandleItemsImport() server.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) error {
err := r.ParseMultipartForm(ctrl.maxUploadSize << 20)
if err != nil {
log.Err(err).Msg("failed to parse multipart form")
@@ -230,38 +184,20 @@ func (ctrl *V1Controller) HandleItemsImport() errchain.HandlerFunc {
return validate.NewRequestError(err, http.StatusInternalServerError)
}
data, err := services.ReadCsv(file)
if err != nil {
log.Err(err).Msg("failed to read csv")
return validate.NewRequestError(err, http.StatusInternalServerError)
}
user := services.UseUserCtx(r.Context())
_, err = ctrl.svc.Items.CsvImport(r.Context(), user.GroupID, file)
_, err = ctrl.svc.Items.CsvImport(r.Context(), user.GroupID, data)
if err != nil {
log.Err(err).Msg("failed to import items")
return validate.NewRequestError(err, http.StatusInternalServerError)
}
return server.JSON(w, http.StatusNoContent, nil)
}
}
// HandleItemsExport godocs
//
// @Summary Export Items
// @Tags Items
// @Success 200 {string} string "text/csv"
// @Router /v1/items/export [GET]
// @Security Bearer
func (ctrl *V1Controller) HandleItemsExport() errchain.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) error {
ctx := services.NewContext(r.Context())
csvData, err := ctrl.svc.Items.ExportTSV(r.Context(), ctx.GID)
if err != nil {
log.Err(err).Msg("failed to export items")
return validate.NewRequestError(err, http.StatusInternalServerError)
}
w.Header().Set("Content-Type", "text/tsv")
w.Header().Set("Content-Disposition", "attachment;filename=homebox-items.tsv")
writer := csv.NewWriter(w)
return writer.WriteAll(csvData)
return server.Respond(w, http.StatusNoContent, nil)
}
}

View File

@@ -8,8 +8,7 @@ import (
"github.com/hay-kot/homebox/backend/internal/data/ent/attachment"
"github.com/hay-kot/homebox/backend/internal/data/repo"
"github.com/hay-kot/homebox/backend/internal/sys/validate"
"github.com/hay-kot/safeserve/errchain"
"github.com/hay-kot/safeserve/server"
"github.com/hay-kot/homebox/backend/pkgs/server"
"github.com/rs/zerolog/log"
)
@@ -19,20 +18,19 @@ type (
}
)
// HandleItemAttachmentCreate godocs
//
// @Summary Create Item Attachment
// @Tags Items Attachments
// @Produce json
// @Param id path string true "Item ID"
// @Param file formData file true "File attachment"
// @Param type formData string true "Type of file"
// @Param name formData string true "name of the file including extension"
// @Success 200 {object} repo.ItemOut
// @Failure 422 {object} mid.ErrorResponse
// @Router /v1/items/{id}/attachments [POST]
// @Security Bearer
func (ctrl *V1Controller) HandleItemAttachmentCreate() errchain.HandlerFunc {
// HandleItemsImport godocs
// @Summary imports items into the database
// @Tags Items Attachments
// @Produce json
// @Param id path string true "Item ID"
// @Param file formData file true "File attachment"
// @Param type formData string true "Type of file"
// @Param name formData string true "name of the file including extension"
// @Success 200 {object} repo.ItemOut
// @Failure 422 {object} server.ErrorResponse
// @Router /v1/items/{id}/attachments [POST]
// @Security Bearer
func (ctrl *V1Controller) HandleItemAttachmentCreate() server.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) error {
err := r.ParseMultipartForm(ctrl.maxUploadSize << 20)
if err != nil {
@@ -62,7 +60,7 @@ func (ctrl *V1Controller) HandleItemAttachmentCreate() errchain.HandlerFunc {
}
if !errs.Nil() {
return server.JSON(w, http.StatusUnprocessableEntity, errs)
return server.Respond(w, http.StatusUnprocessableEntity, errs)
}
attachmentType := r.FormValue("type")
@@ -84,53 +82,51 @@ func (ctrl *V1Controller) HandleItemAttachmentCreate() errchain.HandlerFunc {
attachment.Type(attachmentType),
file,
)
if err != nil {
log.Err(err).Msg("failed to add attachment")
return validate.NewRequestError(err, http.StatusInternalServerError)
}
return server.JSON(w, http.StatusCreated, item)
return server.Respond(w, http.StatusCreated, item)
}
}
// HandleItemAttachmentGet godocs
//
// @Summary Get Item Attachment
// @Tags Items Attachments
// @Produce application/octet-stream
// @Param id path string true "Item ID"
// @Param attachment_id path string true "Attachment ID"
// @Success 200 {object} ItemAttachmentToken
// @Router /v1/items/{id}/attachments/{attachment_id} [GET]
// @Security Bearer
func (ctrl *V1Controller) HandleItemAttachmentGet() errchain.HandlerFunc {
// @Summary retrieves an attachment for an item
// @Tags Items Attachments
// @Produce application/octet-stream
// @Param id path string true "Item ID"
// @Param attachment_id path string true "Attachment ID"
// @Success 200 {object} ItemAttachmentToken
// @Router /v1/items/{id}/attachments/{attachment_id} [GET]
// @Security Bearer
func (ctrl *V1Controller) HandleItemAttachmentGet() server.HandlerFunc {
return ctrl.handleItemAttachmentsHandler
}
// HandleItemAttachmentDelete godocs
//
// @Summary Delete Item Attachment
// @Tags Items Attachments
// @Param id path string true "Item ID"
// @Param attachment_id path string true "Attachment ID"
// @Success 204
// @Router /v1/items/{id}/attachments/{attachment_id} [DELETE]
// @Security Bearer
func (ctrl *V1Controller) HandleItemAttachmentDelete() errchain.HandlerFunc {
// @Summary retrieves an attachment for an item
// @Tags Items Attachments
// @Param id path string true "Item ID"
// @Param attachment_id path string true "Attachment ID"
// @Success 204
// @Router /v1/items/{id}/attachments/{attachment_id} [DELETE]
// @Security Bearer
func (ctrl *V1Controller) HandleItemAttachmentDelete() server.HandlerFunc {
return ctrl.handleItemAttachmentsHandler
}
// HandleItemAttachmentUpdate godocs
//
// @Summary Update Item Attachment
// @Tags Items Attachments
// @Param id path string true "Item ID"
// @Param attachment_id path string true "Attachment ID"
// @Param payload body repo.ItemAttachmentUpdate true "Attachment Update"
// @Success 200 {object} repo.ItemOut
// @Router /v1/items/{id}/attachments/{attachment_id} [PUT]
// @Security Bearer
func (ctrl *V1Controller) HandleItemAttachmentUpdate() errchain.HandlerFunc {
// @Summary retrieves an attachment for an item
// @Tags Items Attachments
// @Param id path string true "Item ID"
// @Param attachment_id path string true "Attachment ID"
// @Param payload body repo.ItemAttachmentUpdate true "Attachment Update"
// @Success 200 {object} repo.ItemOut
// @Router /v1/items/{id}/attachments/{attachment_id} [PUT]
// @Security Bearer
func (ctrl *V1Controller) HandleItemAttachmentUpdate() server.HandlerFunc {
return ctrl.handleItemAttachmentsHandler
}
@@ -165,7 +161,7 @@ func (ctrl *V1Controller) handleItemAttachmentsHandler(w http.ResponseWriter, r
return validate.NewRequestError(err, http.StatusInternalServerError)
}
return server.JSON(w, http.StatusNoContent, nil)
return server.Respond(w, http.StatusNoContent, nil)
// Update Attachment Handler
case http.MethodPut:
@@ -183,7 +179,7 @@ func (ctrl *V1Controller) handleItemAttachmentsHandler(w http.ResponseWriter, r
return validate.NewRequestError(err, http.StatusInternalServerError)
}
return server.JSON(w, http.StatusOK, val)
return server.Respond(w, http.StatusOK, val)
}
return nil

View File

@@ -3,100 +3,141 @@ package v1
import (
"net/http"
"github.com/google/uuid"
"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/data/repo"
"github.com/hay-kot/homebox/backend/internal/web/adapters"
"github.com/hay-kot/safeserve/errchain"
"github.com/hay-kot/homebox/backend/internal/sys/validate"
"github.com/hay-kot/homebox/backend/pkgs/server"
"github.com/rs/zerolog/log"
)
// HandleLabelsGetAll godoc
//
// @Summary Get All Labels
// @Tags Labels
// @Produce json
// @Success 200 {object} []repo.LabelOut
// @Router /v1/labels [GET]
// @Security Bearer
func (ctrl *V1Controller) HandleLabelsGetAll() errchain.HandlerFunc {
fn := func(r *http.Request) ([]repo.LabelSummary, error) {
auth := services.NewContext(r.Context())
return ctrl.repo.Labels.GetAll(auth, auth.GID)
// @Summary Get All Labels
// @Tags Labels
// @Produce json
// @Success 200 {object} server.Results{items=[]repo.LabelOut}
// @Router /v1/labels [GET]
// @Security Bearer
func (ctrl *V1Controller) HandleLabelsGetAll() server.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) error {
user := services.UseUserCtx(r.Context())
labels, err := ctrl.repo.Labels.GetAll(r.Context(), user.GroupID)
if err != nil {
log.Err(err).Msg("error getting labels")
return validate.NewRequestError(err, http.StatusInternalServerError)
}
return server.Respond(w, http.StatusOK, server.Results{Items: labels})
}
return adapters.Command(fn, http.StatusOK)
}
// HandleLabelsCreate godoc
//
// @Summary Create Label
// @Tags Labels
// @Produce json
// @Param payload body repo.LabelCreate true "Label Data"
// @Success 200 {object} repo.LabelSummary
// @Router /v1/labels [POST]
// @Security Bearer
func (ctrl *V1Controller) HandleLabelsCreate() errchain.HandlerFunc {
fn := func(r *http.Request, data repo.LabelCreate) (repo.LabelOut, error) {
auth := services.NewContext(r.Context())
return ctrl.repo.Labels.Create(auth, auth.GID, data)
}
// @Summary Create a new label
// @Tags Labels
// @Produce json
// @Param payload body repo.LabelCreate true "Label Data"
// @Success 200 {object} repo.LabelSummary
// @Router /v1/labels [POST]
// @Security Bearer
func (ctrl *V1Controller) HandleLabelsCreate() server.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) error {
createData := repo.LabelCreate{}
if err := server.Decode(r, &createData); err != nil {
log.Err(err).Msg("error decoding label create data")
return validate.NewRequestError(err, http.StatusInternalServerError)
}
return adapters.Action(fn, http.StatusCreated)
user := services.UseUserCtx(r.Context())
label, err := ctrl.repo.Labels.Create(r.Context(), user.GroupID, createData)
if err != nil {
log.Err(err).Msg("error creating label")
return validate.NewRequestError(err, http.StatusInternalServerError)
}
return server.Respond(w, http.StatusCreated, label)
}
}
// HandleLabelDelete godocs
//
// @Summary Delete Label
// @Tags Labels
// @Produce json
// @Param id path string true "Label ID"
// @Success 204
// @Router /v1/labels/{id} [DELETE]
// @Security Bearer
func (ctrl *V1Controller) HandleLabelDelete() errchain.HandlerFunc {
fn := func(r *http.Request, ID uuid.UUID) (any, error) {
auth := services.NewContext(r.Context())
err := ctrl.repo.Labels.DeleteByGroup(auth, auth.GID, ID)
return nil, err
}
return adapters.CommandID("id", fn, http.StatusNoContent)
// @Summary deletes a label
// @Tags Labels
// @Produce json
// @Param id path string true "Label ID"
// @Success 204
// @Router /v1/labels/{id} [DELETE]
// @Security Bearer
func (ctrl *V1Controller) HandleLabelDelete() server.HandlerFunc {
return ctrl.handleLabelsGeneral()
}
// HandleLabelGet godocs
//
// @Summary Get Label
// @Tags Labels
// @Produce json
// @Param id path string true "Label ID"
// @Success 200 {object} repo.LabelOut
// @Router /v1/labels/{id} [GET]
// @Security Bearer
func (ctrl *V1Controller) HandleLabelGet() errchain.HandlerFunc {
fn := func(r *http.Request, ID uuid.UUID) (repo.LabelOut, error) {
auth := services.NewContext(r.Context())
return ctrl.repo.Labels.GetOneByGroup(auth, auth.GID, ID)
}
return adapters.CommandID("id", fn, http.StatusOK)
// @Summary Gets a label and fields
// @Tags Labels
// @Produce json
// @Param id path string true "Label ID"
// @Success 200 {object} repo.LabelOut
// @Router /v1/labels/{id} [GET]
// @Security Bearer
func (ctrl *V1Controller) HandleLabelGet() server.HandlerFunc {
return ctrl.handleLabelsGeneral()
}
// HandleLabelUpdate godocs
//
// @Summary Update Label
// @Tags Labels
// @Produce json
// @Param id path string true "Label ID"
// @Success 200 {object} repo.LabelOut
// @Router /v1/labels/{id} [PUT]
// @Security Bearer
func (ctrl *V1Controller) HandleLabelUpdate() errchain.HandlerFunc {
fn := func(r *http.Request, ID uuid.UUID, data repo.LabelUpdate) (repo.LabelOut, error) {
auth := services.NewContext(r.Context())
data.ID = ID
return ctrl.repo.Labels.UpdateByGroup(auth, auth.GID, data)
}
return adapters.ActionID("id", fn, http.StatusOK)
// @Summary updates a label
// @Tags Labels
// @Produce json
// @Param id path string true "Label ID"
// @Success 200 {object} repo.LabelOut
// @Router /v1/labels/{id} [PUT]
// @Security Bearer
func (ctrl *V1Controller) HandleLabelUpdate() server.HandlerFunc {
return ctrl.handleLabelsGeneral()
}
func (ctrl *V1Controller) handleLabelsGeneral() server.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) error {
ctx := services.NewContext(r.Context())
ID, err := ctrl.routeID(r)
if err != nil {
return err
}
switch r.Method {
case http.MethodGet:
labels, err := ctrl.repo.Labels.GetOneByGroup(r.Context(), ctx.GID, ID)
if err != nil {
if ent.IsNotFound(err) {
log.Err(err).
Str("id", ID.String()).
Msg("label not found")
return validate.NewRequestError(err, http.StatusNotFound)
}
log.Err(err).Msg("error getting label")
return validate.NewRequestError(err, http.StatusInternalServerError)
}
return server.Respond(w, http.StatusOK, labels)
case http.MethodDelete:
err = ctrl.repo.Labels.DeleteByGroup(ctx, ctx.GID, ID)
if err != nil {
log.Err(err).Msg("error deleting label")
return validate.NewRequestError(err, http.StatusInternalServerError)
}
return server.Respond(w, http.StatusNoContent, nil)
case http.MethodPut:
body := repo.LabelUpdate{}
if err := server.Decode(r, &body); err != nil {
return validate.NewRequestError(err, http.StatusInternalServerError)
}
body.ID = ID
result, err := ctrl.repo.Labels.UpdateByGroup(ctx, ctx.GID, body)
if err != nil {
return validate.NewRequestError(err, http.StatusInternalServerError)
}
return server.Respond(w, http.StatusOK, result)
}
return nil
}
}

View File

@@ -3,120 +3,154 @@ package v1
import (
"net/http"
"github.com/google/uuid"
"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/data/repo"
"github.com/hay-kot/homebox/backend/internal/web/adapters"
"github.com/hay-kot/safeserve/errchain"
"github.com/hay-kot/homebox/backend/internal/sys/validate"
"github.com/hay-kot/homebox/backend/pkgs/server"
"github.com/rs/zerolog/log"
)
// HandleLocationTreeQuery
//
// @Summary Get Locations Tree
// @Tags Locations
// @Produce json
// @Param withItems query bool false "include items in response tree"
// @Success 200 {object} []repo.TreeItem
// @Router /v1/locations/tree [GET]
// @Security Bearer
func (ctrl *V1Controller) HandleLocationTreeQuery() errchain.HandlerFunc {
fn := func(r *http.Request, query repo.TreeQuery) ([]repo.TreeItem, error) {
auth := services.NewContext(r.Context())
return ctrl.repo.Locations.Tree(auth, auth.GID, query)
}
// HandleLocationGetAll godoc
// @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())
return adapters.Query(fn, http.StatusOK)
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)
}
return server.Respond(w, http.StatusOK, server.Results{Items: locations})
}
}
// HandleLocationGetAll
//
// @Summary Get All Locations
// @Tags Locations
// @Produce json
// @Param filterChildren query bool false "Filter locations with parents"
// @Success 200 {object} []repo.LocationOutCount
// @Router /v1/locations [GET]
// @Security Bearer
func (ctrl *V1Controller) HandleLocationGetAll() errchain.HandlerFunc {
fn := func(r *http.Request, q repo.LocationQuery) ([]repo.LocationOutCount, error) {
auth := services.NewContext(r.Context())
return ctrl.repo.Locations.GetAll(auth, auth.GID, q)
}
// HandleLocationCreate godoc
// @Summary Create a new location
// @Tags Locations
// @Produce json
// @Param payload body repo.LocationCreate true "Location Data"
// @Success 200 {object} repo.LocationSummary
// @Router /v1/locations [POST]
// @Security Bearer
func (ctrl *V1Controller) HandleLocationCreate() server.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) error {
createData := repo.LocationCreate{}
if err := server.Decode(r, &createData); err != nil {
log.Err(err).Msg("failed to decode location create data")
return validate.NewRequestError(err, http.StatusInternalServerError)
}
return adapters.Query(fn, http.StatusOK)
user := services.UseUserCtx(r.Context())
location, err := ctrl.repo.Locations.Create(r.Context(), user.GroupID, createData)
if err != nil {
log.Err(err).Msg("failed to create location")
return validate.NewRequestError(err, http.StatusInternalServerError)
}
return server.Respond(w, http.StatusCreated, location)
}
}
// HandleLocationCreate
//
// @Summary Create Location
// @Tags Locations
// @Produce json
// @Param payload body repo.LocationCreate true "Location Data"
// @Success 200 {object} repo.LocationSummary
// @Router /v1/locations [POST]
// @Security Bearer
func (ctrl *V1Controller) HandleLocationCreate() errchain.HandlerFunc {
fn := func(r *http.Request, createData repo.LocationCreate) (repo.LocationOut, error) {
auth := services.NewContext(r.Context())
return ctrl.repo.Locations.Create(auth, auth.GID, createData)
}
return adapters.Action(fn, http.StatusCreated)
// HandleLocationDelete godocs
// @Summary deletes a location
// @Tags Locations
// @Produce json
// @Param id path string true "Location ID"
// @Success 204
// @Router /v1/locations/{id} [DELETE]
// @Security Bearer
func (ctrl *V1Controller) HandleLocationDelete() server.HandlerFunc {
return ctrl.handleLocationGeneral()
}
// HandleLocationDelete
//
// @Summary Delete Location
// @Tags Locations
// @Produce json
// @Param id path string true "Location ID"
// @Success 204
// @Router /v1/locations/{id} [DELETE]
// @Security Bearer
func (ctrl *V1Controller) HandleLocationDelete() errchain.HandlerFunc {
fn := func(r *http.Request, ID uuid.UUID) (any, error) {
auth := services.NewContext(r.Context())
err := ctrl.repo.Locations.DeleteByGroup(auth, auth.GID, ID)
return nil, err
}
return adapters.CommandID("id", fn, http.StatusNoContent)
// HandleLocationGet godocs
// @Summary Gets a location and fields
// @Tags Locations
// @Produce json
// @Param id path string true "Location ID"
// @Success 200 {object} repo.LocationOut
// @Router /v1/locations/{id} [GET]
// @Security Bearer
func (ctrl *V1Controller) HandleLocationGet() server.HandlerFunc {
return ctrl.handleLocationGeneral()
}
// HandleLocationGet
//
// @Summary Get Location
// @Tags Locations
// @Produce json
// @Param id path string true "Location ID"
// @Success 200 {object} repo.LocationOut
// @Router /v1/locations/{id} [GET]
// @Security Bearer
func (ctrl *V1Controller) HandleLocationGet() errchain.HandlerFunc {
fn := func(r *http.Request, ID uuid.UUID) (repo.LocationOut, error) {
auth := services.NewContext(r.Context())
return ctrl.repo.Locations.GetOneByGroup(auth, auth.GID, ID)
}
return adapters.CommandID("id", fn, http.StatusOK)
// HandleLocationUpdate godocs
// @Summary updates a location
// @Tags Locations
// @Produce json
// @Param id path string true "Location ID"
// @Param payload body repo.LocationUpdate true "Location Data"
// @Success 200 {object} repo.LocationOut
// @Router /v1/locations/{id} [PUT]
// @Security Bearer
func (ctrl *V1Controller) HandleLocationUpdate() server.HandlerFunc {
return ctrl.handleLocationGeneral()
}
// HandleLocationUpdate
//
// @Summary Update Location
// @Tags Locations
// @Produce json
// @Param id path string true "Location ID"
// @Param payload body repo.LocationUpdate true "Location Data"
// @Success 200 {object} repo.LocationOut
// @Router /v1/locations/{id} [PUT]
// @Security Bearer
func (ctrl *V1Controller) HandleLocationUpdate() errchain.HandlerFunc {
fn := func(r *http.Request, ID uuid.UUID, body repo.LocationUpdate) (repo.LocationOut, error) {
auth := services.NewContext(r.Context())
body.ID = ID
return ctrl.repo.Locations.UpdateByGroup(auth, auth.GID, ID, body)
}
func (ctrl *V1Controller) handleLocationGeneral() server.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) error {
ctx := services.NewContext(r.Context())
ID, err := ctrl.routeID(r)
if err != nil {
return err
}
return adapters.ActionID("id", fn, http.StatusOK)
switch r.Method {
case http.MethodGet:
location, err := ctrl.repo.Locations.GetOneByGroup(r.Context(), ctx.GID, ID)
if err != nil {
l := log.Err(err).
Str("ID", ID.String()).
Str("GID", ctx.GID.String())
if ent.IsNotFound(err) {
l.Msg("location not found")
return validate.NewRequestError(err, http.StatusNotFound)
}
l.Msg("failed to get location")
return validate.NewRequestError(err, http.StatusInternalServerError)
}
return server.Respond(w, http.StatusOK, location)
case http.MethodPut:
body := repo.LocationUpdate{}
if err := server.Decode(r, &body); err != nil {
log.Err(err).Msg("failed to decode location update data")
return validate.NewRequestError(err, http.StatusInternalServerError)
}
body.ID = ID
result, err := ctrl.repo.Locations.UpdateOneByGroup(r.Context(), ctx.GID, ID, body)
if err != nil {
log.Err(err).Msg("failed to update location")
return validate.NewRequestError(err, http.StatusInternalServerError)
}
return server.Respond(w, http.StatusOK, result)
case http.MethodDelete:
err = ctrl.repo.Locations.DeleteByGroup(r.Context(), ctx.GID, ID)
if err != nil {
log.Err(err).Msg("failed to delete location")
return validate.NewRequestError(err, http.StatusInternalServerError)
}
return server.Respond(w, http.StatusNoContent, nil)
}
return nil
}
}

View File

@@ -3,80 +3,123 @@ package v1
import (
"net/http"
"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/web/adapters"
"github.com/hay-kot/safeserve/errchain"
"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() errchain.HandlerFunc {
fn := func(r *http.Request, ID uuid.UUID, q repo.MaintenanceLogQuery) (repo.MaintenanceLog, error) {
auth := services.NewContext(r.Context())
return ctrl.repo.MaintEntry.GetLog(auth, auth.GID, ID, q)
}
return adapters.QueryID("id", fn, http.StatusOK)
// @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 201 {object} repo.MaintenanceEntry
// @Router /v1/items/{id}/maintenance [POST]
// @Security Bearer
func (ctrl *V1Controller) HandleMaintenanceEntryCreate() errchain.HandlerFunc {
fn := func(r *http.Request, itemID uuid.UUID, body repo.MaintenanceEntryCreate) (repo.MaintenanceEntry, error) {
auth := services.NewContext(r.Context())
return ctrl.repo.MaintEntry.Create(auth, itemID, body)
}
return adapters.ActionID("id", fn, http.StatusCreated)
// @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() errchain.HandlerFunc {
fn := func(r *http.Request, entryID uuid.UUID) (any, error) {
auth := services.NewContext(r.Context())
err := ctrl.repo.MaintEntry.Delete(auth, entryID)
return nil, err
}
return adapters.CommandID("entry_id", fn, http.StatusNoContent)
// @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() errchain.HandlerFunc {
fn := func(r *http.Request, entryID uuid.UUID, body repo.MaintenanceEntryUpdate) (repo.MaintenanceEntry, error) {
auth := services.NewContext(r.Context())
return ctrl.repo.MaintEntry.Update(auth, entryID, body)
}
return adapters.ActionID("entry_id", fn, http.StatusOK)
// @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
}
}

View File

@@ -1,105 +0,0 @@
package v1
import (
"net/http"
"github.com/containrrr/shoutrrr"
"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/web/adapters"
"github.com/hay-kot/safeserve/errchain"
)
// HandleGetUserNotifiers godoc
//
// @Summary Get Notifiers
// @Tags Notifiers
// @Produce json
// @Success 200 {object} []repo.NotifierOut
// @Router /v1/notifiers [GET]
// @Security Bearer
func (ctrl *V1Controller) HandleGetUserNotifiers() errchain.HandlerFunc {
fn := func(r *http.Request, _ struct{}) ([]repo.NotifierOut, error) {
user := services.UseUserCtx(r.Context())
return ctrl.repo.Notifiers.GetByUser(r.Context(), user.ID)
}
return adapters.Query(fn, http.StatusOK)
}
// HandleCreateNotifier godoc
//
// @Summary Create Notifier
// @Tags Notifiers
// @Produce json
// @Param payload body repo.NotifierCreate true "Notifier Data"
// @Success 200 {object} repo.NotifierOut
// @Router /v1/notifiers [POST]
// @Security Bearer
func (ctrl *V1Controller) HandleCreateNotifier() errchain.HandlerFunc {
fn := func(r *http.Request, in repo.NotifierCreate) (repo.NotifierOut, error) {
auth := services.NewContext(r.Context())
return ctrl.repo.Notifiers.Create(auth, auth.GID, auth.UID, in)
}
return adapters.Action(fn, http.StatusCreated)
}
// HandleDeleteNotifier godocs
//
// @Summary Delete a Notifier
// @Tags Notifiers
// @Param id path string true "Notifier ID"
// @Success 204
// @Router /v1/notifiers/{id} [DELETE]
// @Security Bearer
func (ctrl *V1Controller) HandleDeleteNotifier() errchain.HandlerFunc {
fn := func(r *http.Request, ID uuid.UUID) (any, error) {
auth := services.NewContext(r.Context())
return nil, ctrl.repo.Notifiers.Delete(auth, auth.UID, ID)
}
return adapters.CommandID("id", fn, http.StatusNoContent)
}
// HandleUpdateNotifier godocs
//
// @Summary Update Notifier
// @Tags Notifiers
// @Param id path string true "Notifier ID"
// @Param payload body repo.NotifierUpdate true "Notifier Data"
// @Success 200 {object} repo.NotifierOut
// @Router /v1/notifiers/{id} [PUT]
// @Security Bearer
func (ctrl *V1Controller) HandleUpdateNotifier() errchain.HandlerFunc {
fn := func(r *http.Request, ID uuid.UUID, in repo.NotifierUpdate) (repo.NotifierOut, error) {
auth := services.NewContext(r.Context())
return ctrl.repo.Notifiers.Update(auth, auth.UID, ID, in)
}
return adapters.ActionID("id", fn, http.StatusOK)
}
// HandlerNotifierTest godoc
//
// @Summary Test Notifier
// @Tags Notifiers
// @Produce json
// @Param id path string true "Notifier ID"
// @Param url query string true "URL"
// @Success 204
// @Router /v1/notifiers/test [POST]
// @Security Bearer
func (ctrl *V1Controller) HandlerNotifierTest() errchain.HandlerFunc {
type body struct {
URL string `json:"url" validate:"required"`
}
fn := func(r *http.Request, q body) (any, error) {
err := shoutrrr.Send(q.URL, "Test message from Homebox")
return nil, err
}
return adapters.Action(fn, http.StatusOK)
}

View File

@@ -1,66 +0,0 @@
package v1
import (
"bytes"
"image/png"
"io"
"net/http"
"github.com/hay-kot/homebox/backend/internal/web/adapters"
"github.com/hay-kot/safeserve/errchain"
"github.com/yeqown/go-qrcode/v2"
"github.com/yeqown/go-qrcode/writer/standard"
_ "embed"
)
//go:embed assets/QRIcon.png
var qrcodeLogo []byte
// HandleGenerateQRCode godoc
//
// @Summary Create QR Code
// @Tags Items
// @Produce json
// @Param data query string false "data to be encoded into qrcode"
// @Success 200 {string} string "image/jpeg"
// @Router /v1/qrcode [GET]
// @Security Bearer
func (ctrl *V1Controller) HandleGenerateQRCode() errchain.HandlerFunc {
type query struct {
// 4,296 characters is the maximum length of a QR code
Data string `schema:"data" validate:"required,max=4296"`
}
return func(w http.ResponseWriter, r *http.Request) error {
q, err := adapters.DecodeQuery[query](r)
if err != nil {
return err
}
image, err := png.Decode(bytes.NewReader(qrcodeLogo))
if err != nil {
panic(err)
}
qrc, err := qrcode.New(q.Data)
if err != nil {
return err
}
toWriteCloser := struct {
io.Writer
io.Closer
}{
Writer: w,
Closer: io.NopCloser(nil),
}
qrwriter := standard.NewWithWriter(toWriteCloser, standard.WithLogoImage(image))
// Return the QR code as a jpeg image
w.Header().Set("Content-Type", "image/jpeg")
w.Header().Set("Content-Disposition", "attachment; filename=qrcode.jpg")
return qrc.Save(qrwriter)
}
}

View File

@@ -1,32 +0,0 @@
package v1
import (
"net/http"
"github.com/hay-kot/homebox/backend/internal/core/services"
"github.com/hay-kot/safeserve/errchain"
)
// HandleBillOfMaterialsExport godoc
//
// @Summary Export Bill of Materials
// @Tags Reporting
// @Produce json
// @Success 200 {string} string "text/csv"
// @Router /v1/reporting/bill-of-materials [GET]
// @Security Bearer
func (ctrl *V1Controller) HandleBillOfMaterialsExport() errchain.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) error {
actor := services.UseUserCtx(r.Context())
csv, err := ctrl.svc.Items.ExportBillOfMaterialsTSV(r.Context(), actor.GroupID)
if err != nil {
return err
}
w.Header().Set("Content-Type", "text/tsv")
w.Header().Set("Content-Disposition", "attachment; filename=bill-of-materials.tsv")
_, err = w.Write(csv)
return err
}
}

View File

@@ -5,75 +5,80 @@ import (
"time"
"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/internal/web/adapters"
"github.com/hay-kot/safeserve/errchain"
"github.com/hay-kot/safeserve/server"
"github.com/hay-kot/homebox/backend/pkgs/server"
)
// HandleGroupGet godoc
//
// @Summary Get Location Statistics
// @Tags Statistics
// @Produce json
// @Success 200 {object} []repo.TotalsByOrganizer
// @Router /v1/groups/statistics/locations [GET]
// @Security Bearer
func (ctrl *V1Controller) HandleGroupStatisticsLocations() errchain.HandlerFunc {
fn := func(r *http.Request) ([]repo.TotalsByOrganizer, error) {
auth := services.NewContext(r.Context())
return ctrl.repo.Groups.StatsLocationsByPurchasePrice(auth, auth.GID)
}
// @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())
return adapters.Command(fn, http.StatusOK)
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)
}
}
// HandleGroupStatisticsLabels godoc
//
// @Summary Get Label Statistics
// @Tags Statistics
// @Produce json
// @Success 200 {object} []repo.TotalsByOrganizer
// @Router /v1/groups/statistics/labels [GET]
// @Security Bearer
func (ctrl *V1Controller) HandleGroupStatisticsLabels() errchain.HandlerFunc {
fn := func(r *http.Request) ([]repo.TotalsByOrganizer, error) {
auth := services.NewContext(r.Context())
return ctrl.repo.Groups.StatsLabelsByPurchasePrice(auth, auth.GID)
}
// 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())
return adapters.Command(fn, http.StatusOK)
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)
}
}
// HandleGroupStatistics godoc
//
// @Summary Get Group Statistics
// @Tags Statistics
// @Produce json
// @Success 200 {object} repo.GroupStatistics
// @Router /v1/groups/statistics [GET]
// @Security Bearer
func (ctrl *V1Controller) HandleGroupStatistics() errchain.HandlerFunc {
fn := func(r *http.Request) (repo.GroupStatistics, error) {
auth := services.NewContext(r.Context())
return ctrl.repo.Groups.StatsGroup(auth, auth.GID)
}
// 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())
return adapters.Command(fn, http.StatusOK)
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)
}
}
// HandleGroupStatisticsPriceOverTime godoc
//
// @Summary Get Purchase Price Statistics
// @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() errchain.HandlerFunc {
// 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
@@ -99,6 +104,6 @@ func (ctrl *V1Controller) HandleGroupStatisticsPriceOverTime() errchain.HandlerF
return validate.NewRequestError(err, http.StatusInternalServerError)
}
return server.JSON(w, http.StatusOK, stats)
return server.Respond(w, http.StatusOK, stats)
}
}

View File

@@ -1,27 +1,24 @@
package v1
import (
"fmt"
"net/http"
"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"
"github.com/hay-kot/safeserve/errchain"
"github.com/hay-kot/safeserve/server"
"github.com/hay-kot/homebox/backend/pkgs/server"
"github.com/rs/zerolog/log"
)
// HandleUserRegistration godoc
//
// @Summary Register New User
// @Tags User
// @Produce json
// @Param payload body services.UserRegistration true "User Data"
// @Success 204
// @Router /v1/users/register [Post]
func (ctrl *V1Controller) HandleUserRegistration() errchain.HandlerFunc {
// HandleUserSelf godoc
// @Summary Get the current user
// @Tags User
// @Produce json
// @Param payload body services.UserRegistration true "User Data"
// @Success 204
// @Router /v1/users/register [Post]
func (ctrl *V1Controller) HandleUserRegistration() server.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) error {
regData := services.UserRegistration{}
@@ -31,7 +28,7 @@ func (ctrl *V1Controller) HandleUserRegistration() errchain.HandlerFunc {
}
if !ctrl.allowRegistration && regData.GroupToken == "" {
return validate.NewRequestError(fmt.Errorf("user registration disabled"), http.StatusForbidden)
return validate.NewRequestError(nil, http.StatusForbidden)
}
_, err := ctrl.svc.User.RegisterUser(r.Context(), regData)
@@ -40,19 +37,18 @@ func (ctrl *V1Controller) HandleUserRegistration() errchain.HandlerFunc {
return validate.NewRequestError(err, http.StatusInternalServerError)
}
return server.JSON(w, http.StatusNoContent, nil)
return server.Respond(w, http.StatusNoContent, nil)
}
}
// HandleUserSelf godoc
//
// @Summary Get User Self
// @Tags User
// @Produce json
// @Success 200 {object} Wrapped{item=repo.UserOut}
// @Router /v1/users/self [GET]
// @Security Bearer
func (ctrl *V1Controller) HandleUserSelf() errchain.HandlerFunc {
// @Summary Get the current user
// @Tags User
// @Produce json
// @Success 200 {object} server.Result{item=repo.UserOut}
// @Router /v1/users/self [GET]
// @Security Bearer
func (ctrl *V1Controller) HandleUserSelf() server.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) error {
token := services.UseTokenCtx(r.Context())
usr, err := ctrl.svc.User.GetSelf(r.Context(), token)
@@ -61,20 +57,19 @@ func (ctrl *V1Controller) HandleUserSelf() errchain.HandlerFunc {
return validate.NewRequestError(err, http.StatusInternalServerError)
}
return server.JSON(w, http.StatusOK, Wrap(usr))
return server.Respond(w, http.StatusOK, server.Wrap(usr))
}
}
// HandleUserSelfUpdate godoc
//
// @Summary Update Account
// @Tags User
// @Produce json
// @Param payload body repo.UserUpdate true "User Data"
// @Success 200 {object} Wrapped{item=repo.UserUpdate}
// @Router /v1/users/self [PUT]
// @Security Bearer
func (ctrl *V1Controller) HandleUserSelfUpdate() errchain.HandlerFunc {
// @Summary Update the current user
// @Tags User
// @Produce json
// @Param payload body repo.UserUpdate true "User Data"
// @Success 200 {object} server.Result{item=repo.UserUpdate}
// @Router /v1/users/self [PUT]
// @Security Bearer
func (ctrl *V1Controller) HandleUserSelfUpdate() server.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) error {
updateData := repo.UserUpdate{}
if err := server.Decode(r, &updateData); err != nil {
@@ -84,23 +79,23 @@ func (ctrl *V1Controller) HandleUserSelfUpdate() errchain.HandlerFunc {
actor := services.UseUserCtx(r.Context())
newData, err := ctrl.svc.User.UpdateSelf(r.Context(), actor.ID, updateData)
if err != nil {
return validate.NewRequestError(err, http.StatusInternalServerError)
}
return server.JSON(w, http.StatusOK, Wrap(newData))
return server.Respond(w, http.StatusOK, server.Wrap(newData))
}
}
// HandleUserSelfDelete godoc
//
// @Summary Delete Account
// @Tags User
// @Produce json
// @Success 204
// @Router /v1/users/self [DELETE]
// @Security Bearer
func (ctrl *V1Controller) HandleUserSelfDelete() errchain.HandlerFunc {
// @Summary Deletes the user account
// @Tags User
// @Produce json
// @Success 204
// @Router /v1/users/self [DELETE]
// @Security Bearer
func (ctrl *V1Controller) HandleUserSelfDelete() server.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) error {
if ctrl.isDemo {
return validate.NewRequestError(nil, http.StatusForbidden)
@@ -111,7 +106,7 @@ func (ctrl *V1Controller) HandleUserSelfDelete() errchain.HandlerFunc {
return validate.NewRequestError(err, http.StatusInternalServerError)
}
return server.JSON(w, http.StatusNoContent, nil)
return server.Respond(w, http.StatusNoContent, nil)
}
}
@@ -123,14 +118,13 @@ type (
)
// HandleUserSelfChangePassword godoc
//
// @Summary Change Password
// @Tags User
// @Success 204
// @Param payload body ChangePassword true "Password Payload"
// @Router /v1/users/change-password [PUT]
// @Security Bearer
func (ctrl *V1Controller) HandleUserSelfChangePassword() errchain.HandlerFunc {
// @Summary Updates the users password
// @Tags User
// @Success 204
// @Param payload body ChangePassword true "Password Payload"
// @Router /v1/users/change-password [PUT]
// @Security Bearer
func (ctrl *V1Controller) HandleUserSelfChangePassword() server.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) error {
if ctrl.isDemo {
return validate.NewRequestError(nil, http.StatusForbidden)
@@ -149,6 +143,6 @@ func (ctrl *V1Controller) HandleUserSelfChangePassword() errchain.HandlerFunc {
return validate.NewRequestError(err, http.StatusInternalServerError)
}
return server.JSON(w, http.StatusNoContent, nil)
return server.Respond(w, http.StatusNoContent, nil)
}
}

View File

@@ -2,7 +2,6 @@ package main
import (
"context"
"fmt"
"net/http"
"os"
"path/filepath"
@@ -10,9 +9,6 @@ import (
atlas "ariga.io/atlas/sql/migrate"
"entgo.io/ent/dialect/sql/schema"
"github.com/go-chi/chi/v5"
"github.com/go-chi/chi/v5/middleware"
"github.com/hay-kot/homebox/backend/app/api/static/docs"
"github.com/hay-kot/homebox/backend/internal/core/services"
"github.com/hay-kot/homebox/backend/internal/data/ent"
@@ -20,13 +16,9 @@ import (
"github.com/hay-kot/homebox/backend/internal/data/repo"
"github.com/hay-kot/homebox/backend/internal/sys/config"
"github.com/hay-kot/homebox/backend/internal/web/mid"
"github.com/hay-kot/safeserve/errchain"
"github.com/hay-kot/safeserve/server"
"github.com/rs/zerolog"
"github.com/hay-kot/homebox/backend/pkgs/server"
_ "github.com/mattn/go-sqlite3"
"github.com/rs/zerolog/log"
"github.com/rs/zerolog/pkgerrors"
_ "github.com/hay-kot/homebox/backend/pkgs/cgofreesqlite"
)
var (
@@ -35,9 +27,9 @@ var (
buildTime = "now"
)
// @title Homebox API
// @title Go API Templates
// @version 1.0
// @description Track, Manage, and Organize your Things.
// @description This is a simple Rest API Server Template that implements some basic User and Authentication patterns to help you get started and bootstrap your next project!.
// @contact.name Don't
// @license.name MIT
// @BasePath /api
@@ -46,8 +38,6 @@ var (
// @name Authorization
// @description "Type 'Bearer TOKEN' to correctly set the API Key"
func main() {
zerolog.ErrorStackMarshaler = pkgerrors.MarshalStack
cfg, err := config.New()
if err != nil {
panic(err)
@@ -67,7 +57,7 @@ func run(cfg *config.Config) error {
// =========================================================================
// Initialize Database & Repos
err := os.MkdirAll(cfg.Storage.Data, 0o755)
err := os.MkdirAll(cfg.Storage.Data, 0755)
if err != nil {
log.Fatal().Err(err).Msg("failed to create data directory")
}
@@ -128,27 +118,26 @@ func run(cfg *config.Config) error {
)
// =========================================================================
// Start Server
// Start Server\
logger := log.With().Caller().Logger()
router := chi.NewMux()
router.Use(
middleware.RequestID,
middleware.RealIP,
mid.Logger(logger),
middleware.Recoverer,
middleware.StripSlashes,
)
chain := errchain.New(mid.Errors(app.server, logger))
app.mountRoutes(router, chain, app.repos)
mwLogger := mid.Logger(logger)
if app.conf.Mode == config.ModeDevelopment {
mwLogger = mid.SugarLogger(logger)
}
app.server = server.NewServer(
server.WithHost(app.conf.Web.Host),
server.WithPort(app.conf.Web.Port),
server.WithMiddleware(
mwLogger,
mid.Errors(logger),
mid.Panic(app.conf.Mode == config.ModeDevelopment),
),
)
app.mountRoutes(app.repos)
log.Info().Msgf("Starting HTTP Server on %s:%s", app.server.Host, app.server.Port)
// =========================================================================
@@ -170,19 +159,6 @@ func run(cfg *config.Config) error {
Msg("failed to purge expired invitations")
}
})
go app.startBgTask(time.Duration(1)*time.Hour, func() {
now := time.Now()
if now.Hour() == 8 {
fmt.Println("run notifiers")
err := app.services.BackgroundService.SendNotifiersToday(context.Background())
if err != nil {
log.Error().
Err(err).
Msg("failed to send notifiers")
}
}
})
// TODO: Remove through external API that does setup
if cfg.Demo {
@@ -199,5 +175,5 @@ func run(cfg *config.Config) error {
}()
}
return app.server.Start(router)
return app.server.Start()
}

View File

@@ -4,19 +4,20 @@ import (
"context"
"errors"
"net/http"
"net/url"
"strings"
"github.com/hay-kot/homebox/backend/internal/core/services"
"github.com/hay-kot/homebox/backend/internal/sys/validate"
"github.com/hay-kot/safeserve/errchain"
"github.com/hay-kot/homebox/backend/pkgs/server"
)
type tokenHasKey struct {
key string
}
var hashedToken = tokenHasKey{key: "hashedToken"}
var (
hashedToken = tokenHasKey{key: "hashedToken"}
)
type RoleMode int
@@ -30,9 +31,9 @@ const (
// the required roles, a 403 Forbidden will be returned.
//
// WARNING: This middleware _MUST_ be called after mwAuthToken or else it will panic
func (a *app) mwRoles(rm RoleMode, required ...string) errchain.Middleware {
return func(next errchain.Handler) errchain.Handler {
return errchain.HandlerFunc(func(w http.ResponseWriter, r *http.Request) error {
func (a *app) mwRoles(rm RoleMode, required ...string) server.Middleware {
return func(next server.Handler) server.Handler {
return server.HandlerFunc(func(w http.ResponseWriter, r *http.Request) error {
ctx := r.Context()
maybeToken := ctx.Value(hashedToken)
@@ -69,45 +70,6 @@ func (a *app) mwRoles(rm RoleMode, required ...string) errchain.Middleware {
}
}
type KeyFunc func(r *http.Request) (string, error)
func getBearer(r *http.Request) (string, error) {
auth := r.Header.Get("Authorization")
if auth == "" {
return "", errors.New("authorization header is required")
}
return auth, nil
}
func getQuery(r *http.Request) (string, error) {
token := r.URL.Query().Get("access_token")
if token == "" {
return "", errors.New("access_token query is required")
}
token, err := url.QueryUnescape(token)
if err != nil {
return "", errors.New("access_token query is required")
}
return token, nil
}
func getCookie(r *http.Request) (string, error) {
cookie, err := r.Cookie("hb.auth.token")
if err != nil {
return "", errors.New("access_token cookie is required")
}
token, err := url.QueryUnescape(cookie.Value)
if err != nil {
return "", errors.New("access_token cookie is required")
}
return token, nil
}
// mwAuthToken is a middleware that will check the database for a stateful token
// and attach it's user to the request context, or return an appropriate error.
// Authorization support is by token via Headers or Query Parameter
@@ -115,26 +77,15 @@ func getCookie(r *http.Request) (string, error) {
// Example:
// - header = "Bearer 1234567890"
// - query = "?access_token=1234567890"
// - cookie = hb.auth.token = 1234567890
func (a *app) mwAuthToken(next errchain.Handler) errchain.Handler {
return errchain.HandlerFunc(func(w http.ResponseWriter, r *http.Request) error {
keyFuncs := [...]KeyFunc{
getBearer,
getCookie,
getQuery,
}
var requestToken string
for _, keyFunc := range keyFuncs {
token, err := keyFunc(r)
if err == nil {
requestToken = token
break
}
}
func (a *app) mwAuthToken(next server.Handler) server.Handler {
return server.HandlerFunc(func(w http.ResponseWriter, r *http.Request) error {
requestToken := r.Header.Get("Authorization")
if requestToken == "" {
return validate.NewRequestError(errors.New("Authorization header or query is required"), http.StatusUnauthorized)
// check for query param
requestToken = r.URL.Query().Get("access_token")
if requestToken == "" {
return validate.NewRequestError(errors.New("Authorization header or query is required"), http.StatusUnauthorized)
}
}
requestToken = strings.TrimPrefix(requestToken, "Bearer ")
@@ -142,9 +93,10 @@ func (a *app) mwAuthToken(next errchain.Handler) errchain.Handler {
r = r.WithContext(context.WithValue(r.Context(), hashedToken, requestToken))
usr, err := a.services.User.GetSelf(r.Context(), requestToken)
// Check the database for the token
if err != nil {
return validate.NewRequestError(errors.New("valid authorization header is required"), http.StatusUnauthorized)
return validate.NewRequestError(errors.New("Authorization header is required"), http.StatusUnauthorized)
}
r = r.WithContext(services.SetUserCtx(r.Context(), &usr, requestToken))

View File

@@ -10,13 +10,12 @@ import (
"path"
"path/filepath"
"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/static/docs"
"github.com/hay-kot/homebox/backend/internal/data/ent/authroles"
"github.com/hay-kot/homebox/backend/internal/data/repo"
"github.com/hay-kot/safeserve/errchain"
"github.com/hay-kot/homebox/backend/pkgs/server"
httpSwagger "github.com/swaggo/http-swagger" // http-swagger middleware
)
@@ -37,12 +36,12 @@ func (a *app) debugRouter() *http.ServeMux {
}
// registerRoutes registers all the routes for the API
func (a *app) mountRoutes(r *chi.Mux, chain *errchain.ErrChain, repos *repo.AllRepos) {
func (a *app) mountRoutes(repos *repo.AllRepos) {
registerMimes()
r.Get("/swagger/*", httpSwagger.Handler(
a.server.Get("/swagger/*", server.ToHandler(httpSwagger.Handler(
httpSwagger.URL(fmt.Sprintf("%s://%s/swagger/doc.json", a.conf.Swagger.Scheme, a.conf.Swagger.Host)),
))
)))
// =========================================================================
// API Version 1
@@ -57,103 +56,74 @@ func (a *app) mountRoutes(r *chi.Mux, chain *errchain.ErrChain, repos *repo.AllR
v1.WithDemoStatus(a.conf.Demo), // Disable Password Change in Demo Mode
)
r.Get(v1Base("/status"), chain.ToHandlerFunc(v1Ctrl.HandleBase(func() bool { return true }, v1.Build{
a.server.Get(v1Base("/status"), v1Ctrl.HandleBase(func() bool { return true }, v1.Build{
Version: version,
Commit: commit,
BuildTime: buildTime,
})))
}))
r.Post(v1Base("/users/register"), chain.ToHandlerFunc(v1Ctrl.HandleUserRegistration()))
r.Post(v1Base("/users/login"), chain.ToHandlerFunc(v1Ctrl.HandleAuthLogin()))
a.server.Post(v1Base("/users/register"), v1Ctrl.HandleUserRegistration())
a.server.Post(v1Base("/users/login"), v1Ctrl.HandleAuthLogin())
userMW := []errchain.Middleware{
userMW := []server.Middleware{
a.mwAuthToken,
a.mwRoles(RoleModeOr, authroles.RoleUser.String()),
}
r.Get(v1Base("/users/self"), chain.ToHandlerFunc(v1Ctrl.HandleUserSelf(), userMW...))
r.Put(v1Base("/users/self"), chain.ToHandlerFunc(v1Ctrl.HandleUserSelfUpdate(), userMW...))
r.Delete(v1Base("/users/self"), chain.ToHandlerFunc(v1Ctrl.HandleUserSelfDelete(), userMW...))
r.Post(v1Base("/users/logout"), chain.ToHandlerFunc(v1Ctrl.HandleAuthLogout(), userMW...))
r.Get(v1Base("/users/refresh"), chain.ToHandlerFunc(v1Ctrl.HandleAuthRefresh(), userMW...))
r.Put(v1Base("/users/self/change-password"), chain.ToHandlerFunc(v1Ctrl.HandleUserSelfChangePassword(), userMW...))
a.server.Get(v1Base("/users/self"), v1Ctrl.HandleUserSelf(), userMW...)
a.server.Put(v1Base("/users/self"), v1Ctrl.HandleUserSelfUpdate(), userMW...)
a.server.Delete(v1Base("/users/self"), v1Ctrl.HandleUserSelfDelete(), userMW...)
a.server.Post(v1Base("/users/logout"), v1Ctrl.HandleAuthLogout(), userMW...)
a.server.Get(v1Base("/users/refresh"), v1Ctrl.HandleAuthRefresh(), userMW...)
a.server.Put(v1Base("/users/self/change-password"), v1Ctrl.HandleUserSelfChangePassword(), userMW...)
r.Post(v1Base("/groups/invitations"), chain.ToHandlerFunc(v1Ctrl.HandleGroupInvitationsCreate(), userMW...))
r.Get(v1Base("/groups/statistics"), chain.ToHandlerFunc(v1Ctrl.HandleGroupStatistics(), userMW...))
r.Get(v1Base("/groups/statistics/purchase-price"), chain.ToHandlerFunc(v1Ctrl.HandleGroupStatisticsPriceOverTime(), userMW...))
r.Get(v1Base("/groups/statistics/locations"), chain.ToHandlerFunc(v1Ctrl.HandleGroupStatisticsLocations(), userMW...))
r.Get(v1Base("/groups/statistics/labels"), chain.ToHandlerFunc(v1Ctrl.HandleGroupStatisticsLabels(), userMW...))
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
r.Get(v1Base("/groups"), chain.ToHandlerFunc(v1Ctrl.HandleGroupGet(), userMW...))
r.Put(v1Base("/groups"), chain.ToHandlerFunc(v1Ctrl.HandleGroupUpdate(), userMW...))
a.server.Get(v1Base("/groups"), v1Ctrl.HandleGroupGet(), userMW...)
a.server.Put(v1Base("/groups"), v1Ctrl.HandleGroupUpdate(), userMW...)
r.Post(v1Base("/actions/ensure-asset-ids"), chain.ToHandlerFunc(v1Ctrl.HandleEnsureAssetID(), userMW...))
r.Post(v1Base("/actions/zero-item-time-fields"), chain.ToHandlerFunc(v1Ctrl.HandleItemDateZeroOut(), userMW...))
r.Post(v1Base("/actions/ensure-import-refs"), chain.ToHandlerFunc(v1Ctrl.HandleEnsureImportRefs(), userMW...))
a.server.Post(v1Base("/actions/ensure-asset-ids"), v1Ctrl.HandleEnsureAssetID(), userMW...)
r.Get(v1Base("/locations"), chain.ToHandlerFunc(v1Ctrl.HandleLocationGetAll(), userMW...))
r.Post(v1Base("/locations"), chain.ToHandlerFunc(v1Ctrl.HandleLocationCreate(), userMW...))
r.Get(v1Base("/locations/tree"), chain.ToHandlerFunc(v1Ctrl.HandleLocationTreeQuery(), userMW...))
r.Get(v1Base("/locations/{id}"), chain.ToHandlerFunc(v1Ctrl.HandleLocationGet(), userMW...))
r.Put(v1Base("/locations/{id}"), chain.ToHandlerFunc(v1Ctrl.HandleLocationUpdate(), userMW...))
r.Delete(v1Base("/locations/{id}"), chain.ToHandlerFunc(v1Ctrl.HandleLocationDelete(), userMW...))
a.server.Get(v1Base("/locations"), v1Ctrl.HandleLocationGetAll(), userMW...)
a.server.Post(v1Base("/locations"), v1Ctrl.HandleLocationCreate(), userMW...)
a.server.Get(v1Base("/locations/{id}"), v1Ctrl.HandleLocationGet(), userMW...)
a.server.Put(v1Base("/locations/{id}"), v1Ctrl.HandleLocationUpdate(), userMW...)
a.server.Delete(v1Base("/locations/{id}"), v1Ctrl.HandleLocationDelete(), userMW...)
r.Get(v1Base("/labels"), chain.ToHandlerFunc(v1Ctrl.HandleLabelsGetAll(), userMW...))
r.Post(v1Base("/labels"), chain.ToHandlerFunc(v1Ctrl.HandleLabelsCreate(), userMW...))
r.Get(v1Base("/labels/{id}"), chain.ToHandlerFunc(v1Ctrl.HandleLabelGet(), userMW...))
r.Put(v1Base("/labels/{id}"), chain.ToHandlerFunc(v1Ctrl.HandleLabelUpdate(), userMW...))
r.Delete(v1Base("/labels/{id}"), chain.ToHandlerFunc(v1Ctrl.HandleLabelDelete(), userMW...))
a.server.Get(v1Base("/labels"), v1Ctrl.HandleLabelsGetAll(), userMW...)
a.server.Post(v1Base("/labels"), v1Ctrl.HandleLabelsCreate(), userMW...)
a.server.Get(v1Base("/labels/{id}"), v1Ctrl.HandleLabelGet(), userMW...)
a.server.Put(v1Base("/labels/{id}"), v1Ctrl.HandleLabelUpdate(), userMW...)
a.server.Delete(v1Base("/labels/{id}"), v1Ctrl.HandleLabelDelete(), userMW...)
r.Get(v1Base("/items"), chain.ToHandlerFunc(v1Ctrl.HandleItemsGetAll(), userMW...))
r.Post(v1Base("/items"), chain.ToHandlerFunc(v1Ctrl.HandleItemsCreate(), userMW...))
r.Post(v1Base("/items/import"), chain.ToHandlerFunc(v1Ctrl.HandleItemsImport(), userMW...))
r.Get(v1Base("/items/export"), chain.ToHandlerFunc(v1Ctrl.HandleItemsExport(), userMW...))
r.Get(v1Base("/items/fields"), chain.ToHandlerFunc(v1Ctrl.HandleGetAllCustomFieldNames(), userMW...))
r.Get(v1Base("/items/fields/values"), chain.ToHandlerFunc(v1Ctrl.HandleGetAllCustomFieldValues(), userMW...))
a.server.Get(v1Base("/items"), v1Ctrl.HandleItemsGetAll(), userMW...)
a.server.Post(v1Base("/items/import"), v1Ctrl.HandleItemsImport(), userMW...)
a.server.Post(v1Base("/items"), v1Ctrl.HandleItemsCreate(), userMW...)
a.server.Get(v1Base("/items/{id}"), v1Ctrl.HandleItemGet(), userMW...)
a.server.Put(v1Base("/items/{id}"), v1Ctrl.HandleItemUpdate(), userMW...)
a.server.Delete(v1Base("/items/{id}"), v1Ctrl.HandleItemDelete(), userMW...)
r.Get(v1Base("/items/{id}"), chain.ToHandlerFunc(v1Ctrl.HandleItemGet(), userMW...))
r.Put(v1Base("/items/{id}"), chain.ToHandlerFunc(v1Ctrl.HandleItemUpdate(), userMW...))
r.Delete(v1Base("/items/{id}"), chain.ToHandlerFunc(v1Ctrl.HandleItemDelete(), userMW...))
a.server.Post(v1Base("/items/{id}/attachments"), v1Ctrl.HandleItemAttachmentCreate(), userMW...)
a.server.Put(v1Base("/items/{id}/attachments/{attachment_id}"), v1Ctrl.HandleItemAttachmentUpdate(), userMW...)
a.server.Delete(v1Base("/items/{id}/attachments/{attachment_id}"), v1Ctrl.HandleItemAttachmentDelete(), userMW...)
r.Post(v1Base("/items/{id}/attachments"), chain.ToHandlerFunc(v1Ctrl.HandleItemAttachmentCreate(), userMW...))
r.Put(v1Base("/items/{id}/attachments/{attachment_id}"), chain.ToHandlerFunc(v1Ctrl.HandleItemAttachmentUpdate(), userMW...))
r.Delete(v1Base("/items/{id}/attachments/{attachment_id}"), chain.ToHandlerFunc(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...)
r.Get(v1Base("/items/{id}/maintenance"), chain.ToHandlerFunc(v1Ctrl.HandleMaintenanceLogGet(), userMW...))
r.Post(v1Base("/items/{id}/maintenance"), chain.ToHandlerFunc(v1Ctrl.HandleMaintenanceEntryCreate(), userMW...))
r.Put(v1Base("/items/{id}/maintenance/{entry_id}"), chain.ToHandlerFunc(v1Ctrl.HandleMaintenanceEntryUpdate(), userMW...))
r.Delete(v1Base("/items/{id}/maintenance/{entry_id}"), chain.ToHandlerFunc(v1Ctrl.HandleMaintenanceEntryDelete(), userMW...))
r.Get(v1Base("/asset/{id}"), chain.ToHandlerFunc(v1Ctrl.HandleAssetGet(), userMW...))
// Notifiers
r.Get(v1Base("/notifiers"), chain.ToHandlerFunc(v1Ctrl.HandleGetUserNotifiers(), userMW...))
r.Post(v1Base("/notifiers"), chain.ToHandlerFunc(v1Ctrl.HandleCreateNotifier(), userMW...))
r.Put(v1Base("/notifiers/{id}"), chain.ToHandlerFunc(v1Ctrl.HandleUpdateNotifier(), userMW...))
r.Delete(v1Base("/notifiers/{id}"), chain.ToHandlerFunc(v1Ctrl.HandleDeleteNotifier(), userMW...))
r.Post(v1Base("/notifiers/test"), chain.ToHandlerFunc(v1Ctrl.HandlerNotifierTest(), userMW...))
// Asset-Like endpoints
assetMW := []errchain.Middleware{
a.mwAuthToken,
a.mwRoles(RoleModeOr, authroles.RoleUser.String(), authroles.RoleAttachments.String()),
}
r.Get(
v1Base("/qrcode"),
chain.ToHandlerFunc(v1Ctrl.HandleGenerateQRCode(), assetMW...),
)
r.Get(
a.server.Get(
v1Base("/items/{id}/attachments/{attachment_id}"),
chain.ToHandlerFunc(v1Ctrl.HandleItemAttachmentGet(), assetMW...),
v1Ctrl.HandleItemAttachmentGet(),
a.mwAuthToken, a.mwRoles(RoleModeOr, authroles.RoleUser.String(), authroles.RoleAttachments.String()),
)
// Reporting Services
r.Get(v1Base("/reporting/bill-of-materials"), chain.ToHandlerFunc(v1Ctrl.HandleBillOfMaterialsExport(), userMW...))
r.NotFound(chain.ToHandlerFunc(notFoundHandler()))
a.server.NotFound(notFoundHandler())
}
func registerMimes() {
@@ -170,7 +140,7 @@ func registerMimes() {
// notFoundHandler perform the main logic around handling the internal SPA embed and ensuring that
// the client side routing is handled correctly.
func notFoundHandler() errchain.HandlerFunc {
func notFoundHandler() server.HandlerFunc {
tryRead := func(fs embed.FS, prefix, requestedPath string, w http.ResponseWriter) error {
f, err := fs.Open(path.Join(prefix, requestedPath))
if err != nil {

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -1,14 +1,5 @@
basePath: /api
definitions:
mid.ErrorResponse:
properties:
error:
type: string
fields:
additionalProperties:
type: string
type: object
type: object
repo.DocumentOut:
properties:
id:
@@ -76,7 +67,6 @@ definitions:
repo.ItemCreate:
properties:
description:
maxLength: 1000
type: string
labelIds:
items:
@@ -86,14 +76,10 @@ definitions:
description: Edges
type: string
name:
maxLength: 255
minLength: 1
type: string
parentId:
type: string
x-nullable: true
required:
- name
type: object
repo.ItemField:
properties:
@@ -107,6 +93,8 @@ definitions:
type: integer
textValue:
type: string
timeValue:
type: string
type:
type: string
type: object
@@ -287,7 +275,6 @@ definitions:
warrantyDetails:
type: string
warrantyExpires:
description: Sold
type: string
type: object
repo.LabelCreate:
@@ -295,14 +282,9 @@ definitions:
color:
type: string
description:
maxLength: 255
type: string
name:
maxLength: 255
minLength: 1
type: string
required:
- name
type: object
repo.LabelOut:
properties:
@@ -340,9 +322,6 @@ definitions:
type: string
name:
type: string
parentId:
type: string
x-nullable: true
type: object
repo.LocationOut:
properties:
@@ -409,55 +388,41 @@ definitions:
type: object
repo.MaintenanceEntry:
properties:
completedDate:
description: Sold
type: string
cost:
example: "0"
type: string
date:
type: string
description:
type: string
id:
type: string
name:
type: string
scheduledDate:
description: Sold
type: string
type: object
repo.MaintenanceEntryCreate:
properties:
completedDate:
description: Sold
type: string
cost:
example: "0"
type: string
date:
type: string
description:
type: string
name:
type: string
scheduledDate:
description: Sold
type: string
required:
- name
type: object
repo.MaintenanceEntryUpdate:
properties:
completedDate:
description: Sold
type: string
cost:
example: "0"
type: string
date:
type: string
description:
type: string
name:
type: string
scheduledDate:
description: Sold
type: string
type: object
repo.MaintenanceLog:
properties:
@@ -472,51 +437,6 @@ definitions:
itemId:
type: string
type: object
repo.NotifierCreate:
properties:
isActive:
type: boolean
name:
maxLength: 255
minLength: 1
type: string
url:
type: string
required:
- name
- url
type: object
repo.NotifierOut:
properties:
createdAt:
type: string
groupId:
type: string
id:
type: string
isActive:
type: boolean
name:
type: string
updatedAt:
type: string
userId:
type: string
type: object
repo.NotifierUpdate:
properties:
isActive:
type: boolean
name:
maxLength: 255
minLength: 1
type: string
url:
type: string
x-nullable: true
required:
- name
type: object
repo.PaginationResult-repo_ItemSummary:
properties:
items:
@@ -539,19 +459,6 @@ definitions:
total:
type: number
type: object
repo.TreeItem:
properties:
children:
items:
$ref: '#/definitions/repo.TreeItem'
type: array
id:
type: string
name:
type: string
type:
type: string
type: object
repo.UserOut:
properties:
email:
@@ -600,6 +507,28 @@ definitions:
value:
type: number
type: object
server.ErrorResponse:
properties:
error:
type: string
fields:
additionalProperties:
type: string
type: object
type: object
server.Result:
properties:
details: {}
error:
type: boolean
item: {}
message:
type: string
type: object
server.Results:
properties:
items: {}
type: object
services.UserRegistration:
properties:
email:
@@ -611,15 +540,8 @@ definitions:
token:
type: string
type: object
v1.ActionAmountResult:
properties:
completed:
type: integer
type: object
v1.ApiSummary:
properties:
allowRegistration:
type: boolean
build:
$ref: '#/definitions/v1.Build'
demo:
@@ -651,6 +573,11 @@ definitions:
new:
type: string
type: object
v1.EnsureAssetIDResult:
properties:
completed:
type: integer
type: object
v1.GroupInvitation:
properties:
expiresAt:
@@ -665,26 +592,13 @@ definitions:
expiresAt:
type: string
uses:
maximum: 100
minimum: 1
type: integer
required:
- uses
type: object
v1.ItemAttachmentToken:
properties:
token:
type: string
type: object
v1.LoginForm:
properties:
password:
type: string
stayLoggedIn:
type: boolean
username:
type: string
type: object
v1.TokenResponse:
properties:
attachmentToken:
@@ -694,84 +608,31 @@ definitions:
token:
type: string
type: object
v1.Wrapped:
properties:
item: {}
type: object
info:
contact:
name: Don't
description: Track, Manage, and Organize your Things.
description: This is a simple Rest API Server Template that implements some basic
User and Authentication patterns to help you get started and bootstrap your next
project!.
license:
name: MIT
title: Homebox API
title: Go API Templates
version: "1.0"
paths:
/v1/actions/ensure-asset-ids:
post:
description: Ensures all items in the database have an asset ID
produces:
- application/json
responses:
"200":
description: OK
schema:
$ref: '#/definitions/v1.ActionAmountResult'
$ref: '#/definitions/v1.EnsureAssetIDResult'
security:
- Bearer: []
summary: Ensure Asset IDs
summary: Get the current user
tags:
- Actions
/v1/actions/ensure-import-refs:
post:
description: Ensures all items in the database have an import ref
produces:
- application/json
responses:
"200":
description: OK
schema:
$ref: '#/definitions/v1.ActionAmountResult'
security:
- Bearer: []
summary: Ensures Import Refs
tags:
- Actions
/v1/actions/zero-item-time-fields:
post:
description: Resets all item date fields to the beginning of the day
produces:
- application/json
responses:
"200":
description: OK
schema:
$ref: '#/definitions/v1.ActionAmountResult'
security:
- Bearer: []
summary: Zero Out Time Fields
tags:
- Actions
/v1/assets/{id}:
get:
parameters:
- description: Asset ID
in: path
name: id
required: true
type: string
produces:
- application/json
responses:
"200":
description: OK
schema:
$ref: '#/definitions/repo.PaginationResult-repo_ItemSummary'
security:
- Bearer: []
summary: Get Item by Asset ID
tags:
- Items
- Group
/v1/groups:
get:
produces:
@@ -783,7 +644,7 @@ paths:
$ref: '#/definitions/repo.Group'
security:
- Bearer: []
summary: Get Group
summary: Get the current user's group
tags:
- Group
put:
@@ -803,7 +664,7 @@ paths:
$ref: '#/definitions/repo.Group'
security:
- Bearer: []
summary: Update Group
summary: Updates some fields of the current users group
tags:
- Group
/v1/groups/invitations:
@@ -824,7 +685,7 @@ paths:
$ref: '#/definitions/v1.GroupInvitation'
security:
- Bearer: []
summary: Create Group Invitation
summary: Get the current user
tags:
- Group
/v1/groups/statistics:
@@ -838,7 +699,7 @@ paths:
$ref: '#/definitions/repo.GroupStatistics'
security:
- Bearer: []
summary: Get Group Statistics
summary: Get the current user's group statistics
tags:
- Statistics
/v1/groups/statistics/labels:
@@ -854,7 +715,7 @@ paths:
type: array
security:
- Bearer: []
summary: Get Label Statistics
summary: Get the current user's group statistics
tags:
- Statistics
/v1/groups/statistics/locations:
@@ -870,7 +731,7 @@ paths:
type: array
security:
- Bearer: []
summary: Get Location Statistics
summary: Get the current user's group statistics
tags:
- Statistics
/v1/groups/statistics/purchase-price:
@@ -893,7 +754,7 @@ paths:
$ref: '#/definitions/repo.ValueOverTime'
security:
- Bearer: []
summary: Get Purchase Price Statistics
summary: Queries the changes overtime of the purchase price over time
tags:
- Statistics
/v1/items:
@@ -934,7 +795,7 @@ paths:
$ref: '#/definitions/repo.PaginationResult-repo_ItemSummary'
security:
- Bearer: []
summary: Query All Items
summary: Get All Items
tags:
- Items
post:
@@ -948,13 +809,13 @@ paths:
produces:
- application/json
responses:
"201":
description: Created
"200":
description: OK
schema:
$ref: '#/definitions/repo.ItemSummary'
security:
- Bearer: []
summary: Create Item
summary: Create a new item
tags:
- Items
/v1/items/{id}:
@@ -972,7 +833,7 @@ paths:
description: No Content
security:
- Bearer: []
summary: Delete Item
summary: deletes a item
tags:
- Items
get:
@@ -991,7 +852,7 @@ paths:
$ref: '#/definitions/repo.ItemOut'
security:
- Bearer: []
summary: Get Item
summary: Gets a item and fields
tags:
- Items
put:
@@ -1016,7 +877,7 @@ paths:
$ref: '#/definitions/repo.ItemOut'
security:
- Bearer: []
summary: Update Item
summary: updates a item
tags:
- Items
/v1/items/{id}/attachments:
@@ -1052,10 +913,10 @@ paths:
"422":
description: Unprocessable Entity
schema:
$ref: '#/definitions/mid.ErrorResponse'
$ref: '#/definitions/server.ErrorResponse'
security:
- Bearer: []
summary: Create Item Attachment
summary: imports items into the database
tags:
- Items Attachments
/v1/items/{id}/attachments/{attachment_id}:
@@ -1076,7 +937,7 @@ paths:
description: No Content
security:
- Bearer: []
summary: Delete Item Attachment
summary: retrieves an attachment for an item
tags:
- Items Attachments
get:
@@ -1100,7 +961,7 @@ paths:
$ref: '#/definitions/v1.ItemAttachmentToken'
security:
- Bearer: []
summary: Get Item Attachment
summary: retrieves an attachment for an item
tags:
- Items Attachments
put:
@@ -1128,7 +989,7 @@ paths:
$ref: '#/definitions/repo.ItemOut'
security:
- Bearer: []
summary: Update Item Attachment
summary: retrieves an attachment for an item
tags:
- Items Attachments
/v1/items/{id}/maintenance:
@@ -1156,8 +1017,8 @@ paths:
produces:
- application/json
responses:
"201":
description: Created
"200":
description: OK
schema:
$ref: '#/definitions/repo.MaintenanceEntry'
security:
@@ -1197,50 +1058,6 @@ paths:
summary: Update Maintenance Entry
tags:
- Maintenance
/v1/items/export:
get:
responses:
"200":
description: text/csv
schema:
type: string
security:
- Bearer: []
summary: Export Items
tags:
- Items
/v1/items/fields:
get:
produces:
- application/json
responses:
"200":
description: OK
schema:
items:
type: string
type: array
security:
- Bearer: []
summary: Get All Custom Field Names
tags:
- Items
/v1/items/fields/values:
get:
produces:
- application/json
responses:
"200":
description: OK
schema:
items:
type: string
type: array
security:
- Bearer: []
summary: Get All Custom Field Values
tags:
- Items
/v1/items/import:
post:
parameters:
@@ -1256,7 +1073,7 @@ paths:
description: No Content
security:
- Bearer: []
summary: Import Items
summary: imports items into the database
tags:
- Items
/v1/labels:
@@ -1267,9 +1084,14 @@ paths:
"200":
description: OK
schema:
items:
$ref: '#/definitions/repo.LabelOut'
type: array
allOf:
- $ref: '#/definitions/server.Results'
- properties:
items:
items:
$ref: '#/definitions/repo.LabelOut'
type: array
type: object
security:
- Bearer: []
summary: Get All Labels
@@ -1292,7 +1114,7 @@ paths:
$ref: '#/definitions/repo.LabelSummary'
security:
- Bearer: []
summary: Create Label
summary: Create a new label
tags:
- Labels
/v1/labels/{id}:
@@ -1310,7 +1132,7 @@ paths:
description: No Content
security:
- Bearer: []
summary: Delete Label
summary: deletes a label
tags:
- Labels
get:
@@ -1329,7 +1151,7 @@ paths:
$ref: '#/definitions/repo.LabelOut'
security:
- Bearer: []
summary: Get Label
summary: Gets a label and fields
tags:
- Labels
put:
@@ -1348,7 +1170,7 @@ paths:
$ref: '#/definitions/repo.LabelOut'
security:
- Bearer: []
summary: Update Label
summary: updates a label
tags:
- Labels
/v1/locations:
@@ -1364,9 +1186,14 @@ paths:
"200":
description: OK
schema:
items:
$ref: '#/definitions/repo.LocationOutCount'
type: array
allOf:
- $ref: '#/definitions/server.Results'
- properties:
items:
items:
$ref: '#/definitions/repo.LocationOutCount'
type: array
type: object
security:
- Bearer: []
summary: Get All Locations
@@ -1389,7 +1216,7 @@ paths:
$ref: '#/definitions/repo.LocationSummary'
security:
- Bearer: []
summary: Create Location
summary: Create a new location
tags:
- Locations
/v1/locations/{id}:
@@ -1407,7 +1234,7 @@ paths:
description: No Content
security:
- Bearer: []
summary: Delete Location
summary: deletes a location
tags:
- Locations
get:
@@ -1426,7 +1253,7 @@ paths:
$ref: '#/definitions/repo.LocationOut'
security:
- Bearer: []
summary: Get Location
summary: Gets a location and fields
tags:
- Locations
put:
@@ -1451,161 +1278,9 @@ paths:
$ref: '#/definitions/repo.LocationOut'
security:
- Bearer: []
summary: Update Location
summary: updates a location
tags:
- Locations
/v1/locations/tree:
get:
parameters:
- description: include items in response tree
in: query
name: withItems
type: boolean
produces:
- application/json
responses:
"200":
description: OK
schema:
items:
$ref: '#/definitions/repo.TreeItem'
type: array
security:
- Bearer: []
summary: Get Locations Tree
tags:
- Locations
/v1/notifiers:
get:
produces:
- application/json
responses:
"200":
description: OK
schema:
items:
$ref: '#/definitions/repo.NotifierOut'
type: array
security:
- Bearer: []
summary: Get Notifiers
tags:
- Notifiers
post:
parameters:
- description: Notifier Data
in: body
name: payload
required: true
schema:
$ref: '#/definitions/repo.NotifierCreate'
produces:
- application/json
responses:
"200":
description: OK
schema:
$ref: '#/definitions/repo.NotifierOut'
security:
- Bearer: []
summary: Create Notifier
tags:
- Notifiers
/v1/notifiers/{id}:
delete:
parameters:
- description: Notifier ID
in: path
name: id
required: true
type: string
responses:
"204":
description: No Content
security:
- Bearer: []
summary: Delete a Notifier
tags:
- Notifiers
put:
parameters:
- description: Notifier ID
in: path
name: id
required: true
type: string
- description: Notifier Data
in: body
name: payload
required: true
schema:
$ref: '#/definitions/repo.NotifierUpdate'
responses:
"200":
description: OK
schema:
$ref: '#/definitions/repo.NotifierOut'
security:
- Bearer: []
summary: Update Notifier
tags:
- Notifiers
/v1/notifiers/test:
post:
parameters:
- description: Notifier ID
in: path
name: id
required: true
type: string
- description: URL
in: query
name: url
required: true
type: string
produces:
- application/json
responses:
"204":
description: No Content
security:
- Bearer: []
summary: Test Notifier
tags:
- Notifiers
/v1/qrcode:
get:
parameters:
- description: data to be encoded into qrcode
in: query
name: data
type: string
produces:
- application/json
responses:
"200":
description: image/jpeg
schema:
type: string
security:
- Bearer: []
summary: Create QR Code
tags:
- Items
/v1/reporting/bill-of-materials:
get:
produces:
- application/json
responses:
"200":
description: text/csv
schema:
type: string
security:
- Bearer: []
summary: Export Bill of Materials
tags:
- Reporting
/v1/status:
get:
produces:
@@ -1615,7 +1290,7 @@ paths:
description: OK
schema:
$ref: '#/definitions/v1.ApiSummary'
summary: Application Info
summary: Retrieves the basic information about the API
tags:
- Base
/v1/users/change-password:
@@ -1632,7 +1307,7 @@ paths:
description: No Content
security:
- Bearer: []
summary: Change Password
summary: Updates the users password
tags:
- User
/v1/users/login:
@@ -1651,12 +1326,6 @@ paths:
in: formData
name: password
type: string
- description: Login Data
in: body
name: payload
required: true
schema:
$ref: '#/definitions/v1.LoginForm'
produces:
- application/json
responses:
@@ -1704,7 +1373,7 @@ paths:
responses:
"204":
description: No Content
summary: Register New User
summary: Get the current user
tags:
- User
/v1/users/self:
@@ -1716,7 +1385,7 @@ paths:
description: No Content
security:
- Bearer: []
summary: Delete Account
summary: Deletes the user account
tags:
- User
get:
@@ -1727,14 +1396,14 @@ paths:
description: OK
schema:
allOf:
- $ref: '#/definitions/v1.Wrapped'
- $ref: '#/definitions/server.Result'
- properties:
item:
$ref: '#/definitions/repo.UserOut'
type: object
security:
- Bearer: []
summary: Get User Self
summary: Get the current user
tags:
- User
put:
@@ -1752,14 +1421,14 @@ paths:
description: OK
schema:
allOf:
- $ref: '#/definitions/v1.Wrapped'
- $ref: '#/definitions/server.Result'
- properties:
item:
$ref: '#/definitions/repo.UserUpdate'
type: object
security:
- Bearer: []
summary: Update Account
summary: Update the current user
tags:
- User
securityDefinitions:

View File

@@ -1,80 +0,0 @@
package main
import (
"fmt"
"os"
"regexp"
)
type ReReplace struct {
Regex *regexp.Regexp
Text string
}
func NewReReplace(regex string, replace string) ReReplace {
return ReReplace{
Regex: regexp.MustCompile(regex),
Text: replace,
}
}
func NewReDate(dateStr string) ReReplace {
return ReReplace{
Regex: regexp.MustCompile(fmt.Sprintf(`%s: string`, dateStr)),
Text: fmt.Sprintf(`%s: Date | string`, dateStr),
}
}
func main() {
if len(os.Args) != 2 {
fmt.Println("Please provide a file path as an argument")
os.Exit(1)
}
path := os.Args[1]
fmt.Printf("Processing %s\n", path)
if _, err := os.Stat(path); os.IsNotExist(err) {
fmt.Printf("File %s does not exist\n", path)
os.Exit(1)
}
text := "/* post-processed by ./scripts/process-types.go */\n"
data, err := os.ReadFile(path)
if err != nil {
fmt.Println(err)
os.Exit(1)
}
text += string(data)
replaces := [...]ReReplace{
NewReReplace(` Repo`, " "),
NewReReplace(` PaginationResultRepo`, " PaginationResult"),
NewReReplace(` Services`, " "),
NewReReplace(` V1`, " "),
NewReReplace(`\?:`, ":"),
NewReDate("createdAt"),
NewReDate("updatedAt"),
NewReDate("soldTime"),
NewReDate("purchaseTime"),
NewReDate("warrantyExpires"),
NewReDate("expiresAt"),
NewReDate("date"),
NewReDate("completedDate"),
NewReDate("scheduledDate"),
}
for _, replace := range replaces {
fmt.Printf("Replacing '%v' -> '%s'\n", replace.Regex, replace.Text)
text = replace.Regex.ReplaceAllString(text, replace.Text)
}
err = os.WriteFile(path, []byte(text), 0644)
if err != nil {
fmt.Println(err)
os.Exit(1)
}
os.Exit(0)
}

View File

@@ -1,28 +1,20 @@
module github.com/hay-kot/homebox/backend
go 1.20
go 1.19
require (
ariga.io/atlas v0.10.0
entgo.io/ent v0.11.10
github.com/ardanlabs/conf/v3 v3.1.5
github.com/containrrr/shoutrrr v0.7.1
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.8
github.com/go-playground/validator/v10 v10.12.0
github.com/gocarina/gocsv v0.0.0-20230226133904-70c27cb2918a
github.com/go-playground/validator/v10 v10.11.1
github.com/google/uuid v1.3.0
github.com/gorilla/schema v1.2.0
github.com/hay-kot/safeserve v0.0.1
github.com/mattn/go-sqlite3 v1.14.16
github.com/pkg/errors v0.9.1
github.com/rs/zerolog v1.29.0
github.com/stretchr/testify v1.8.2
github.com/swaggo/http-swagger v1.3.4
github.com/swaggo/swag v1.8.11
github.com/yeqown/go-qrcode/v2 v2.2.1
github.com/yeqown/go-qrcode/writer/standard v1.2.1
golang.org/x/crypto v0.7.0
modernc.org/sqlite v1.21.0
github.com/rs/zerolog v1.28.0
github.com/stretchr/testify v1.8.1
github.com/swaggo/http-swagger v1.3.3
github.com/swaggo/swag v1.8.9
golang.org/x/crypto v0.4.0
)
require (
@@ -30,45 +22,28 @@ require (
github.com/agext/levenshtein v1.2.3 // indirect
github.com/apparentlymart/go-textseg/v13 v13.0.0 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/dustin/go-humanize v1.0.0 // indirect
github.com/fatih/color v1.13.0 // indirect
github.com/fogleman/gg v1.3.0 // indirect
github.com/go-openapi/inflect v0.19.0 // indirect
github.com/go-openapi/jsonpointer v0.19.5 // indirect
github.com/go-openapi/jsonreference v0.20.0 // indirect
github.com/go-openapi/spec v0.20.7 // indirect
github.com/go-openapi/swag v0.22.3 // indirect
github.com/go-playground/locales v0.14.1 // indirect
github.com/go-playground/universal-translator v0.18.1 // indirect
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 // indirect
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.15.0 // indirect
github.com/josharian/intern v1.0.0 // indirect
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 // indirect
github.com/leodido/go-urn v1.2.2 // indirect
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.17 // indirect
github.com/mitchellh/go-wordwrap v1.0.1 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
github.com/swaggo/files v1.0.0 // indirect
github.com/yeqown/reedsolomon v1.0.0 // indirect
github.com/zclconf/go-cty v1.12.1 // indirect
golang.org/x/image v0.0.0-20200927104501-e162460cd6b5 // indirect
golang.org/x/mod v0.8.0 // indirect
golang.org/x/net v0.8.0 // indirect
golang.org/x/sys v0.6.0 // indirect
golang.org/x/text v0.8.0 // indirect
golang.org/x/tools v0.6.1-0.20230222164832-25d2519c8696 // indirect
golang.org/x/mod v0.7.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.4.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
lukechampine.com/uint128 v1.2.0 // indirect
modernc.org/cc/v3 v3.40.0 // indirect
modernc.org/ccgo/v3 v3.16.13 // indirect
modernc.org/libc v1.22.3 // indirect
modernc.org/mathutil v1.5.0 // indirect
modernc.org/memory v1.5.0 // indirect
modernc.org/opt v0.1.3 // indirect
modernc.org/strutil v1.1.3 // indirect
modernc.org/token v1.0.1 // indirect
)

File diff suppressed because it is too large Load Diff

View File

@@ -1,14 +1,11 @@
package services
import (
"github.com/hay-kot/homebox/backend/internal/data/repo"
)
import "github.com/hay-kot/homebox/backend/internal/data/repo"
type AllServices struct {
User *UserService
Group *GroupService
Items *ItemService
BackgroundService *BackgroundService
User *UserService
Group *GroupService
Items *ItemService
}
type OptionsFunc func(*options)
@@ -43,6 +40,5 @@ func New(repos *repo.AllRepos, opts ...OptionsFunc) *AllServices {
repo: repos,
autoIncrementAssetID: options.autoIncrementAssetID,
},
BackgroundService: &BackgroundService{repos},
}
}

View File

@@ -3,8 +3,10 @@ package services
import (
"context"
"log"
"math/rand"
"os"
"testing"
"time"
"github.com/hay-kot/homebox/backend/internal/data/ent"
"github.com/hay-kot/homebox/backend/internal/data/repo"
@@ -47,6 +49,8 @@ func bootstrap() {
}
func TestMain(m *testing.M) {
rand.Seed(int64(time.Now().Unix()))
client, err := ent.Open("sqlite3", "file:ent?mode=memory&cache=shared&_fk=1")
if err != nil {
log.Fatalf("failed opening connection to sqlite: %v", err)

View File

@@ -1,5 +0,0 @@
HB.location,HB.name,HB.quantity,HB.description,HB.field.Custom Field 1,HB.field.Custom Field 2,HB.field.Custom Field 3
loc,Item 1,1,Description 1,Value 1[1],Value 1[2],Value 1[3]
loc,Item 2,2,Description 2,Value 2[1],Value 2[2],Value 2[3]
loc,Item 3,3,Description 3,Value 3[1],Value 3[2],Value 3[3]
1 HB.location HB.name HB.quantity HB.description HB.field.Custom Field 1 HB.field.Custom Field 2 HB.field.Custom Field 3
2 loc Item 1 1 Description 1 Value 1[1] Value 1[2] Value 1[3]
3 loc Item 2 2 Description 2 Value 2[1] Value 2[2] Value 2[3]
4 loc Item 3 3 Description 3 Value 3[1] Value 3[2] Value 3[3]

View File

@@ -1,4 +0,0 @@
HB.location,HB.name,HB.quantity,HB.description
loc,Item 1,1,Description 1
loc,Item 2,2,Description 2
loc,Item 3,3,Description 3
1 HB.location HB.name HB.quantity HB.description
2 loc Item 1 1 Description 1
3 loc Item 2 2 Description 2
4 loc Item 3 3 Description 3

View File

@@ -1,4 +0,0 @@
HB.name,HB.asset_id,HB.location,HB.labels
Item 1,1,Path / To / Location 1,L1 ; L2 ; L3
Item 2,000-002,Path /To/ Location 2,L1;L2;L3
Item 3,1000-003,Path / To /Location 3 , L1;L2; L3
1 HB.name HB.asset_id HB.location HB.labels
2 Item 1 1 Path / To / Location 1 L1 ; L2 ; L3
3 Item 2 000-002 Path /To/ Location 2 L1;L2;L3
4 Item 3 1000-003 Path / To /Location 3 L1;L2; L3

View File

@@ -1,42 +0,0 @@
package reporting
import (
"github.com/gocarina/gocsv"
"github.com/hay-kot/homebox/backend/internal/data/repo"
"github.com/hay-kot/homebox/backend/internal/data/types"
)
// =================================================================================================
type BillOfMaterialsEntry struct {
PurchaseDate types.Date `csv:"Purchase Date"`
Name string `csv:"Name"`
Description string `csv:"Description"`
Manufacturer string `csv:"Manufacturer"`
SerialNumber string `csv:"Serial Number"`
ModelNumber string `csv:"Model Number"`
Quantity int `csv:"Quantity"`
Price float64 `csv:"Price"`
TotalPrice float64 `csv:"Total Price"`
}
// BillOfMaterialsTSV returns a byte slice of the Bill of Materials for a given GID in TSV format
// See BillOfMaterialsEntry for the format of the output
func BillOfMaterialsTSV(entities []repo.ItemOut) ([]byte, error) {
bomEntries := make([]BillOfMaterialsEntry, len(entities))
for i, entity := range entities {
bomEntries[i] = BillOfMaterialsEntry{
PurchaseDate: entity.PurchaseTime,
Name: entity.Name,
Description: entity.Description,
Manufacturer: entity.Manufacturer,
SerialNumber: entity.SerialNumber,
ModelNumber: entity.ModelNumber,
Quantity: entity.Quantity,
Price: entity.PurchasePrice,
TotalPrice: entity.PurchasePrice * float64(entity.Quantity),
}
}
return gocsv.MarshalBytes(&bomEntries)
}

View File

@@ -1,93 +0,0 @@
package reporting
import (
"bytes"
"encoding/csv"
"errors"
"io"
"strings"
)
var (
ErrNoHomeboxHeaders = errors.New("no headers found")
ErrMissingRequiredHeaders = errors.New("missing required headers `HB.location` or `HB.name`")
)
// determineSeparator determines the separator used in the CSV file
// It returns the separator as a rune and an error if it could not be determined
//
// It is assumed that the first row is the header row and that the separator is the same
// for all rows.
//
// Supported separators are `,` and `\t`
func determineSeparator(data []byte) (rune, error) {
// First row
firstRow := bytes.Split(data, []byte("\n"))[0]
// find first comma or /t
comma := bytes.IndexByte(firstRow, ',')
tab := bytes.IndexByte(firstRow, '\t')
switch {
case comma == -1 && tab == -1:
return 0, errors.New("could not determine separator")
case tab > comma:
return '\t', nil
default:
return ',', nil
}
}
// readRawCsv reads a CSV file and returns the raw data as a 2D string array
// It determines the separator used in the CSV file and returns an error if
// it could not be determined
func readRawCsv(r io.Reader) ([][]string, error) {
data, err := io.ReadAll(r)
if err != nil {
return nil, err
}
reader := csv.NewReader(bytes.NewReader(data))
// Determine separator
sep, err := determineSeparator(data)
if err != nil {
return nil, err
}
reader.Comma = sep
return reader.ReadAll()
}
// parseHeaders parses the homebox headers from the CSV file and returns a map of the headers
// and their column index as well as a list of the field headers (HB.field.*) in the order
// they appear in the CSV file
//
// It returns an error if no homebox headers are found
func parseHeaders(headers []string) (hbHeaders map[string]int, fieldHeaders []string, err error) {
hbHeaders = map[string]int{} // initialize map
for col, h := range headers {
if strings.HasPrefix(h, "HB.field.") {
fieldHeaders = append(fieldHeaders, h)
}
if strings.HasPrefix(h, "HB.") {
hbHeaders[h] = col
}
}
required := []string{"HB.location", "HB.name"}
for _, h := range required {
if _, ok := hbHeaders[h]; !ok {
return nil, nil, ErrMissingRequiredHeaders
}
}
if len(hbHeaders) == 0 {
return nil, nil, ErrNoHomeboxHeaders
}
return hbHeaders, fieldHeaders, nil
}

View File

@@ -1,85 +0,0 @@
package reporting
import (
"strings"
"github.com/hay-kot/homebox/backend/internal/data/repo"
"github.com/hay-kot/homebox/backend/internal/data/types"
)
type ExportItemFields struct {
Name string
Value string
}
type ExportTSVRow struct {
ImportRef string `csv:"HB.import_ref"`
Location LocationString `csv:"HB.location"`
LabelStr LabelString `csv:"HB.labels"`
AssetID repo.AssetID `csv:"HB.asset_id"`
Archived bool `csv:"HB.archived"`
Name string `csv:"HB.name"`
Quantity int `csv:"HB.quantity"`
Description string `csv:"HB.description"`
Insured bool `csv:"HB.insured"`
Notes string `csv:"HB.notes"`
PurchasePrice float64 `csv:"HB.purchase_price"`
PurchaseFrom string `csv:"HB.purchase_from"`
PurchaseTime types.Date `csv:"HB.purchase_time"`
Manufacturer string `csv:"HB.manufacturer"`
ModelNumber string `csv:"HB.model_number"`
SerialNumber string `csv:"HB.serial_number"`
LifetimeWarranty bool `csv:"HB.lifetime_warranty"`
WarrantyExpires types.Date `csv:"HB.warranty_expires"`
WarrantyDetails string `csv:"HB.warranty_details"`
SoldTo string `csv:"HB.sold_to"`
SoldPrice float64 `csv:"HB.sold_price"`
SoldTime types.Date `csv:"HB.sold_time"`
SoldNotes string `csv:"HB.sold_notes"`
Fields []ExportItemFields `csv:"-"`
}
// ============================================================================
// LabelString is a string slice that is used to represent a list of labels.
//
// For example, a list of labels "Important; Work" would be represented as a
// LabelString with the following values:
//
// LabelString{"Important", "Work"}
type LabelString []string
func parseLabelString(s string) LabelString {
v, _ := parseSeparatedString(s, ";")
return v
}
func (ls LabelString) String() string {
return strings.Join(ls, "; ")
}
// ============================================================================
// LocationString is a string slice that is used to represent a location
// hierarchy.
//
// For example, a location hierarchy of "Home / Bedroom / Desk" would be
// represented as a LocationString with the following values:
//
// LocationString{"Home", "Bedroom", "Desk"}
type LocationString []string
func parseLocationString(s string) LocationString {
v, _ := parseSeparatedString(s, "/")
return v
}
func (csf LocationString) String() string {
return strings.Join(csf, " / ")
}

View File

@@ -1,310 +0,0 @@
package reporting
import (
"fmt"
"io"
"reflect"
"sort"
"strconv"
"strings"
"github.com/hay-kot/homebox/backend/internal/data/repo"
"github.com/hay-kot/homebox/backend/internal/data/types"
"github.com/rs/zerolog/log"
)
// IOSheet is the representation of a CSV/TSV sheet that is used for importing/exporting
// items from homebox. It is used to read/write the data from/to a CSV/TSV file given
// the standard format of the file.
//
// See ExportTSVRow for the format of the data in the sheet.
type IOSheet struct {
headers []string
custom []int
index map[string]int
Rows []ExportTSVRow
}
func (s *IOSheet) indexHeaders() {
s.index = make(map[string]int)
for i, h := range s.headers {
if strings.HasPrefix(h, "HB.field") {
s.custom = append(s.custom, i)
}
if strings.HasPrefix(h, "HB.") {
s.index[h] = i
}
}
}
func (s *IOSheet) GetColumn(str string) (col int, ok bool) {
if s.index == nil {
s.indexHeaders()
}
col, ok = s.index[str]
return
}
// Read reads a CSV/TSV and populates the "Rows" field with the data from the sheet
// Custom Fields are supported via the `HB.field.*` headers. The `HB.field.*` the "Name"
// of the field is the part after the `HB.field.` prefix. Additionally, Custom Fields with
// no value are excluded from the row.Fields slice, this includes empty strings.
//
// Note That
// - the first row is assumed to be the header
// - at least 1 row of data is required
// - rows and columns must be rectangular (i.e. all rows must have the same number of columns)
func (s *IOSheet) Read(data io.Reader) error {
sheet, err := readRawCsv(data)
if err != nil {
return err
}
if len(sheet) < 2 {
return fmt.Errorf("sheet must have at least 1 row of data (header + 1)")
}
s.headers = sheet[0]
s.Rows = make([]ExportTSVRow, len(sheet)-1)
for i, row := range sheet[1:] {
if len(row) != len(s.headers) {
return fmt.Errorf("row has %d columns, expected %d", len(row), len(s.headers))
}
rowData := ExportTSVRow{}
st := reflect.TypeOf(ExportTSVRow{})
for i := 0; i < st.NumField(); i++ {
field := st.Field(i)
tag := field.Tag.Get("csv")
if tag == "" || tag == "-" {
continue
}
col, ok := s.GetColumn(tag)
if !ok {
continue
}
val := row[col]
var v interface{}
switch field.Type {
case reflect.TypeOf(""):
v = val
case reflect.TypeOf(int(0)):
v = parseInt(val)
case reflect.TypeOf(bool(false)):
v = parseBool(val)
case reflect.TypeOf(float64(0)):
v = parseFloat(val)
// Custom Types
case reflect.TypeOf(types.Date{}):
v = types.DateFromString(val)
case reflect.TypeOf(repo.AssetID(0)):
v, _ = repo.ParseAssetID(val)
case reflect.TypeOf(LocationString{}):
v = parseLocationString(val)
case reflect.TypeOf(LabelString{}):
v = parseLabelString(val)
}
log.Debug().
Str("tag", tag).
Interface("val", v).
Str("type", fmt.Sprintf("%T", v)).
Msg("parsed value")
// Nil values are not allowed at the moment. This may change.
if v == nil {
return fmt.Errorf("could not convert %q to %s", val, field.Type)
}
ptrField := reflect.ValueOf(&rowData).Elem().Field(i)
ptrField.Set(reflect.ValueOf(v))
}
for _, col := range s.custom {
colName := strings.TrimPrefix(s.headers[col], "HB.field.")
customVal := row[col]
if customVal == "" {
continue
}
rowData.Fields = append(rowData.Fields, ExportItemFields{
Name: colName,
Value: customVal,
})
}
s.Rows[i] = rowData
}
return nil
}
// Write writes the sheet to a writer.
func (s *IOSheet) ReadItems(items []repo.ItemOut) {
s.Rows = make([]ExportTSVRow, len(items))
extraHeaders := map[string]struct{}{}
for i := range items {
item := items[i]
// TODO: Support fetching nested locations
locString := LocationString{item.Location.Name}
labelString := make([]string, len(item.Labels))
for i, l := range item.Labels {
labelString[i] = l.Name
}
customFields := make([]ExportItemFields, len(item.Fields))
for i, f := range item.Fields {
extraHeaders[f.Name] = struct{}{}
customFields[i] = ExportItemFields{
Name: f.Name,
Value: f.TextValue,
}
}
s.Rows[i] = ExportTSVRow{
// fill struct
Location: locString,
LabelStr: labelString,
ImportRef: item.ImportRef,
AssetID: item.AssetID,
Name: item.Name,
Quantity: item.Quantity,
Description: item.Description,
Insured: item.Insured,
Archived: item.Archived,
PurchasePrice: item.PurchasePrice,
PurchaseFrom: item.PurchaseFrom,
PurchaseTime: item.PurchaseTime,
Manufacturer: item.Manufacturer,
ModelNumber: item.ModelNumber,
SerialNumber: item.SerialNumber,
LifetimeWarranty: item.LifetimeWarranty,
WarrantyExpires: item.WarrantyExpires,
WarrantyDetails: item.WarrantyDetails,
SoldTo: item.SoldTo,
SoldTime: item.SoldTime,
SoldPrice: item.SoldPrice,
SoldNotes: item.SoldNotes,
Fields: customFields,
}
}
// Extract and sort additional headers for deterministic output
customHeaders := make([]string, 0, len(extraHeaders))
for k := range extraHeaders {
customHeaders = append(customHeaders, k)
}
sort.Strings(customHeaders)
st := reflect.TypeOf(ExportTSVRow{})
// Write headers
for i := 0; i < st.NumField(); i++ {
field := st.Field(i)
tag := field.Tag.Get("csv")
if tag == "" || tag == "-" {
continue
}
s.headers = append(s.headers, tag)
}
for _, h := range customHeaders {
s.headers = append(s.headers, "HB.field."+h)
}
}
// Writes the current sheet to a writer in TSV format.
func (s *IOSheet) TSV() ([][]string, error) {
memcsv := make([][]string, len(s.Rows)+1)
memcsv[0] = s.headers
// use struct tags in rows to dertmine column order
for i, row := range s.Rows {
rowIdx := i + 1
memcsv[rowIdx] = make([]string, len(s.headers))
st := reflect.TypeOf(row)
for i := 0; i < st.NumField(); i++ {
field := st.Field(i)
tag := field.Tag.Get("csv")
if tag == "" || tag == "-" {
continue
}
col, ok := s.GetColumn(tag)
if !ok {
continue
}
val := reflect.ValueOf(row).Field(i)
var v string
switch field.Type {
case reflect.TypeOf(""):
v = val.String()
case reflect.TypeOf(int(0)):
v = strconv.Itoa(int(val.Int()))
case reflect.TypeOf(bool(false)):
v = strconv.FormatBool(val.Bool())
case reflect.TypeOf(float64(0)):
v = strconv.FormatFloat(val.Float(), 'f', -1, 64)
// Custom Types
case reflect.TypeOf(types.Date{}):
v = val.Interface().(types.Date).String()
case reflect.TypeOf(repo.AssetID(0)):
v = val.Interface().(repo.AssetID).String()
case reflect.TypeOf(LocationString{}):
v = val.Interface().(LocationString).String()
case reflect.TypeOf(LabelString{}):
v = val.Interface().(LabelString).String()
default:
log.Debug().Str("type", field.Type.String()).Msg("unknown type")
}
memcsv[rowIdx][col] = v
}
for _, f := range row.Fields {
col, ok := s.GetColumn("HB.field." + f.Name)
if !ok {
continue
}
memcsv[i+1][col] = f.Value
}
}
return memcsv, nil
}

View File

@@ -1,226 +0,0 @@
package reporting
import (
"bytes"
"reflect"
"testing"
_ "embed"
"github.com/hay-kot/homebox/backend/internal/data/repo"
"github.com/stretchr/testify/assert"
)
var (
//go:embed .testdata/import/minimal.csv
minimalImportCSV []byte
//go:embed .testdata/import/fields.csv
customFieldImportCSV []byte
//go:embed .testdata/import/types.csv
customTypesImportCSV []byte
//go:embed .testdata/import.csv
CSVData_Comma []byte
//go:embed .testdata/import.tsv
CSVData_Tab []byte
)
func TestSheet_Read(t *testing.T) {
tests := []struct {
name string
data []byte
want []ExportTSVRow
wantErr bool
}{
{
name: "minimal import",
data: minimalImportCSV,
want: []ExportTSVRow{
{Location: LocationString{"loc"}, Name: "Item 1", Quantity: 1, Description: "Description 1"},
{Location: LocationString{"loc"}, Name: "Item 2", Quantity: 2, Description: "Description 2"},
{Location: LocationString{"loc"}, Name: "Item 3", Quantity: 3, Description: "Description 3"},
},
},
{
name: "custom field import",
data: customFieldImportCSV,
want: []ExportTSVRow{
{
Location: LocationString{"loc"}, Name: "Item 1", Quantity: 1, Description: "Description 1",
Fields: []ExportItemFields{
{Name: "Custom Field 1", Value: "Value 1[1]"},
{Name: "Custom Field 2", Value: "Value 1[2]"},
{Name: "Custom Field 3", Value: "Value 1[3]"},
},
},
{
Location: LocationString{"loc"}, Name: "Item 2", Quantity: 2, Description: "Description 2",
Fields: []ExportItemFields{
{Name: "Custom Field 1", Value: "Value 2[1]"},
{Name: "Custom Field 2", Value: "Value 2[2]"},
{Name: "Custom Field 3", Value: "Value 2[3]"},
},
},
{
Location: LocationString{"loc"}, Name: "Item 3", Quantity: 3, Description: "Description 3",
Fields: []ExportItemFields{
{Name: "Custom Field 1", Value: "Value 3[1]"},
{Name: "Custom Field 2", Value: "Value 3[2]"},
{Name: "Custom Field 3", Value: "Value 3[3]"},
},
},
},
},
{
name: "custom types import",
data: customTypesImportCSV,
want: []ExportTSVRow{
{
Name: "Item 1",
AssetID: repo.AssetID(1),
Location: LocationString{"Path", "To", "Location 1"},
LabelStr: LabelString{"L1", "L2", "L3"},
},
{
Name: "Item 2",
AssetID: repo.AssetID(2),
Location: LocationString{"Path", "To", "Location 2"},
LabelStr: LabelString{"L1", "L2", "L3"},
},
{
Name: "Item 3",
AssetID: repo.AssetID(1000003),
Location: LocationString{"Path", "To", "Location 3"},
LabelStr: LabelString{"L1", "L2", "L3"},
},
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
reader := bytes.NewReader(tt.data)
sheet := &IOSheet{}
err := sheet.Read(reader)
switch {
case tt.wantErr:
assert.Error(t, err)
default:
assert.NoError(t, err)
assert.ElementsMatch(t, tt.want, sheet.Rows)
}
})
}
}
func Test_parseHeaders(t *testing.T) {
tests := []struct {
name string
rawHeaders []string
wantHbHeaders map[string]int
wantFieldHeaders []string
wantErr bool
}{
{
name: "no hombox headers",
rawHeaders: []string{"Header 1", "Header 2", "Header 3"},
wantHbHeaders: nil,
wantFieldHeaders: nil,
wantErr: true,
},
{
name: "field headers only",
rawHeaders: []string{"HB.location", "HB.name", "HB.field.1", "HB.field.2", "HB.field.3"},
wantHbHeaders: map[string]int{
"HB.location": 0,
"HB.name": 1,
"HB.field.1": 2,
"HB.field.2": 3,
"HB.field.3": 4,
},
wantFieldHeaders: []string{"HB.field.1", "HB.field.2", "HB.field.3"},
wantErr: false,
},
{
name: "mixed headers",
rawHeaders: []string{"Header 1", "HB.name", "Header 2", "HB.field.2", "Header 3", "HB.field.3", "HB.location"},
wantHbHeaders: map[string]int{
"HB.name": 1,
"HB.field.2": 3,
"HB.field.3": 5,
"HB.location": 6,
},
wantFieldHeaders: []string{"HB.field.2", "HB.field.3"},
wantErr: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
gotHbHeaders, gotFieldHeaders, err := parseHeaders(tt.rawHeaders)
if (err != nil) != tt.wantErr {
t.Errorf("parseHeaders() error = %v, wantErr %v", err, tt.wantErr)
return
}
if !reflect.DeepEqual(gotHbHeaders, tt.wantHbHeaders) {
t.Errorf("parseHeaders() gotHbHeaders = %v, want %v", gotHbHeaders, tt.wantHbHeaders)
}
if !reflect.DeepEqual(gotFieldHeaders, tt.wantFieldHeaders) {
t.Errorf("parseHeaders() gotFieldHeaders = %v, want %v", gotFieldHeaders, tt.wantFieldHeaders)
}
})
}
}
func Test_determineSeparator(t *testing.T) {
type args struct {
data []byte
}
tests := []struct {
name string
args args
want rune
wantErr bool
}{
{
name: "comma",
args: args{
data: CSVData_Comma,
},
want: ',',
wantErr: false,
},
{
name: "tab",
args: args{
data: CSVData_Tab,
},
want: '\t',
wantErr: false,
},
{
name: "invalid",
args: args{
data: []byte("a;b;c"),
},
want: 0,
wantErr: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := determineSeparator(tt.args.data)
if (err != nil) != tt.wantErr {
t.Errorf("determineSeparator() error = %v, wantErr %v", err, tt.wantErr)
return
}
if got != tt.want {
t.Errorf("determineSeparator() = %v, want %v", got, tt.want)
}
})
}
}

View File

@@ -1,38 +0,0 @@
package reporting
import (
"strconv"
"strings"
)
func parseSeparatedString(s string, sep string) ([]string, error) {
list := strings.Split(s, sep)
csf := make([]string, 0, len(list))
for _, s := range list {
trimmed := strings.TrimSpace(s)
if trimmed != "" {
csf = append(csf, trimmed)
}
}
return csf, nil
}
func parseFloat(s string) float64 {
if s == "" {
return 0
}
f, _ := strconv.ParseFloat(s, 64)
return f
}
func parseBool(s string) bool {
b, _ := strconv.ParseBool(s)
return b
}
func parseInt(s string) int {
i, _ := strconv.Atoi(s)
return i
}

View File

@@ -1,65 +0,0 @@
package reporting
import (
"reflect"
"testing"
)
func Test_parseSeparatedString(t *testing.T) {
type args struct {
s string
sep string
}
tests := []struct {
name string
args args
want []string
wantErr bool
}{
{
name: "comma",
args: args{
s: "a,b,c",
sep: ",",
},
want: []string{"a", "b", "c"},
wantErr: false,
},
{
name: "trimmed comma",
args: args{
s: "a, b, c",
sep: ",",
},
want: []string{"a", "b", "c"},
},
{
name: "excessive whitespace",
args: args{
s: " a, b, c ",
sep: ",",
},
want: []string{"a", "b", "c"},
},
{
name: "empty",
args: args{
s: "",
sep: ",",
},
want: []string{},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := parseSeparatedString(tt.args.s, tt.args.sep)
if (err != nil) != tt.wantErr {
t.Errorf("parseSeparatedString() error = %v, wantErr %v", err, tt.wantErr)
return
}
if !reflect.DeepEqual(got, tt.want) {
t.Errorf("parseSeparatedString() = %v, want %v", got, tt.want)
}
})
}
}

View File

@@ -1,81 +0,0 @@
package services
import (
"context"
"strings"
"time"
"github.com/containrrr/shoutrrr"
"github.com/hay-kot/homebox/backend/internal/data/repo"
"github.com/hay-kot/homebox/backend/internal/data/types"
"github.com/rs/zerolog/log"
)
type BackgroundService struct {
repos *repo.AllRepos
}
func (svc *BackgroundService) SendNotifiersToday(ctx context.Context) error {
// Get All Groups
groups, err := svc.repos.Groups.GetAllGroups(ctx)
if err != nil {
return err
}
today := types.DateFromTime(time.Now())
for i := range groups {
group := groups[i]
entries, err := svc.repos.MaintEntry.GetScheduled(ctx, group.ID, today)
if err != nil {
return err
}
if len(entries) == 0 {
log.Debug().
Str("group_name", group.Name).
Str("group_id", group.ID.String()).
Msg("No scheduled maintenance for today")
continue
}
notifiers, err := svc.repos.Notifiers.GetByGroup(ctx, group.ID)
if err != nil {
return err
}
urls := make([]string, len(notifiers))
for i := range notifiers {
urls[i] = notifiers[i].URL
}
bldr := strings.Builder{}
bldr.WriteString("Homebox Maintenance for (")
bldr.WriteString(today.String())
bldr.WriteString("):\n")
for i := range entries {
entry := entries[i]
bldr.WriteString(" - ")
bldr.WriteString(entry.Name)
bldr.WriteString("\n")
}
var sendErrs []error
for i := range urls {
err := shoutrrr.Send(urls[i], bldr.String())
if err != nil {
sendErrs = append(sendErrs, err)
}
}
if len(sendErrs) > 0 {
return sendErrs[0]
}
}
return nil
}

View File

@@ -3,13 +3,10 @@ package services
import (
"context"
"errors"
"fmt"
"io"
"strings"
"github.com/google/uuid"
"github.com/hay-kot/homebox/backend/internal/core/services/reporting"
"github.com/hay-kot/homebox/backend/internal/data/repo"
"github.com/rs/zerolog/log"
)
var (
@@ -40,6 +37,7 @@ func (svc *ItemService) Create(ctx Context, item repo.ItemCreate) (repo.ItemOut,
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
}
@@ -63,290 +61,190 @@ func (svc *ItemService) EnsureAssetID(ctx context.Context, GID uuid.UUID) (int,
return finished, nil
}
func (svc *ItemService) CsvImport(ctx context.Context, GID uuid.UUID, data [][]string) (int, error) {
loaded := []csvRow{}
func (svc *ItemService) EnsureImportRef(ctx context.Context, GID uuid.UUID) (int, error) {
ids, err := svc.repo.Items.GetAllZeroImportRef(ctx, GID)
if err != nil {
return 0, err
}
finished := 0
for _, itemID := range ids {
ref := uuid.New().String()[0:8]
err = svc.repo.Items.Patch(ctx, GID, itemID, repo.ItemPatch{ImportRef: &ref})
if err != nil {
return 0, err
// Skip first row
for _, row := range data[1:] {
// Skip empty rows
if len(row) == 0 {
continue
}
finished++
}
return finished, nil
}
func serializeLocation[T ~[]string](location T) string {
return strings.Join(location, "/")
}
// CsvImport imports items from a CSV file. using the standard defined format.
//
// CsvImport applies the following rules/operations
//
// 1. If the item does not exist, it is created.
// 2. If the item has a ImportRef and it exists it is skipped
// 3. Locations and Labels are created if they do not exist.
func (svc *ItemService) CsvImport(ctx context.Context, GID uuid.UUID, data io.Reader) (int, error) {
sheet := reporting.IOSheet{}
err := sheet.Read(data)
if err != nil {
return 0, err
}
// ========================================
// Labels
labelMap := make(map[string]uuid.UUID)
{
labels, err := svc.repo.Labels.GetAll(ctx, GID)
if err != nil {
return 0, err
if len(row) != NumOfCols {
return 0, ErrInvalidCsv
}
for _, label := range labels {
labelMap[label.Name] = label.ID
r := newCsvRow(row)
loaded = append(loaded, r)
}
// validate rows
var errMap = map[int][]error{}
var hasErr bool
for i, r := range loaded {
errs := r.validate()
if len(errs) > 0 {
hasErr = true
lineNum := i + 2
errMap[lineNum] = errs
}
}
// ========================================
// Locations
locationMap := make(map[string]uuid.UUID)
{
locations, err := svc.repo.Locations.Tree(ctx, GID, repo.TreeQuery{WithItems: false})
if err != nil {
return 0, err
}
// Traverse the tree and build a map of location full paths to IDs
// where the full path is the location name joined by slashes.
var traverse func(location *repo.TreeItem, path []string)
traverse = func(location *repo.TreeItem, path []string) {
path = append(path, location.Name)
locationMap[serializeLocation(path)] = location.ID
for _, child := range location.Children {
traverse(child, path)
if hasErr {
for lineNum, errs := range errMap {
for _, err := range errs {
log.Error().Err(err).Int("line", lineNum).Msg("csv import error")
}
}
}
for _, location := range locations {
traverse(&location, []string{})
// 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, repo.LocationQuery{})
if err != nil {
return 0, err
}
for _, loc := range existingLocation {
locations[loc.Name] = loc.ID
}
labels := map[string]uuid.UUID{}
existingLabels, err := svc.repo.Labels.GetAll(ctx, GID)
if err != nil {
return 0, err
}
for _, label := range existingLabels {
labels[label.Name] = label.ID
}
for _, row := range loaded {
// Locations
if _, exists := locations[row.Location]; !exists {
result, err := svc.repo.Locations.Create(ctx, GID, repo.LocationCreate{
Name: row.Location,
Description: "",
})
if err != nil {
return 0, err
}
locations[row.Location] = result.ID
}
// Labels
for _, label := range row.getLabels() {
if _, exists := labels[label]; exists {
continue
}
result, err := svc.repo.Labels.Create(ctx, GID, repo.LabelCreate{
Name: label,
Description: "",
})
if err != nil {
return 0, err
}
labels[label] = result.ID
}
}
// ========================================
// Import items
// Asset ID Pre-Check
highestAID := repo.AssetID(-1)
highest := repo.AssetID(-1)
if svc.autoIncrementAssetID {
highestAID, err = svc.repo.Items.GetHighestAssetID(ctx, GID)
highest, err = svc.repo.Items.GetHighestAssetID(ctx, GID)
if err != nil {
return 0, err
}
}
finished := 0
for i := range sheet.Rows {
row := sheet.Rows[i]
createRequired := true
// ========================================
// Preflight check for existing item
if row.ImportRef != "" {
exists, err := svc.repo.Items.CheckRef(ctx, GID, row.ImportRef)
if err != nil {
return 0, fmt.Errorf("error checking for existing item with ref %q: %w", row.ImportRef, err)
}
// Create the items
var count int
for _, row := range loaded {
// Check Import Ref
if row.Item.ImportRef != "" {
exists, err := svc.repo.Items.CheckRef(ctx, GID, row.Item.ImportRef)
if exists {
createRequired = false
continue
}
}
// ========================================
// Pre-Create Labels as necessary
labelIds := make([]uuid.UUID, len(row.LabelStr))
for j := range row.LabelStr {
label := row.LabelStr[j]
id, ok := labelMap[label]
if !ok {
newLabel, err := svc.repo.Labels.Create(ctx, GID, repo.LabelCreate{Name: label})
if err != nil {
return 0, err
}
id = newLabel.ID
}
labelIds[j] = id
labelMap[label] = id
}
// ========================================
// Pre-Create Locations as necessary
path := serializeLocation(row.Location)
locationID, ok := locationMap[path]
if !ok { // Traverse the path of LocationStr and check each path element to see if it exists already, if not create it.
paths := []string{}
for i, pathElement := range row.Location {
paths = append(paths, pathElement)
path := serializeLocation(paths)
locationID, ok = locationMap[path]
if !ok {
parentID := uuid.Nil
// Get the parent ID
if i > 0 {
parentPath := serializeLocation(row.Location[:i])
parentID = locationMap[parentPath]
}
newLocation, err := svc.repo.Locations.Create(ctx, GID, repo.LocationCreate{
ParentID: parentID,
Name: pathElement,
})
if err != nil {
return 0, err
}
locationID = newLocation.ID
}
locationMap[path] = locationID
}
locationID, ok = locationMap[path]
if !ok {
return 0, errors.New("failed to create location")
}
}
var effAID repo.AssetID
if svc.autoIncrementAssetID && row.AssetID.Nil() {
effAID = highestAID + 1
highestAID++
} else {
effAID = row.AssetID
}
// ========================================
// Create Item
var item repo.ItemOut
switch {
case createRequired:
newItem := repo.ItemCreate{
ImportRef: row.ImportRef,
Name: row.Name,
Description: row.Description,
AssetID: effAID,
LocationID: locationID,
LabelIDs: labelIds,
}
item, err = svc.repo.Items.Create(ctx, GID, newItem)
if err != nil {
return 0, err
}
default:
item, err = svc.repo.Items.GetByRef(ctx, GID, row.ImportRef)
if err != nil {
return 0, err
log.Err(err).Msg("error checking import ref")
}
}
if item.ID == uuid.Nil {
panic("item ID is nil on import - this should never happen")
locationID := locations[row.Location]
labelIDs := []uuid.UUID{}
for _, label := range row.getLabels() {
labelIDs = append(labelIDs, labels[label])
}
fields := make([]repo.ItemField, len(row.Fields))
for i := range row.Fields {
fields[i] = repo.ItemField{
Name: row.Fields[i].Name,
Type: "text",
TextValue: row.Fields[i].Value,
}
log.Info().
Str("name", row.Item.Name).
Str("location", row.Location).
Msgf("Creating Item: %s", row.Item.Name)
data := repo.ItemCreate{
ImportRef: row.Item.ImportRef,
Name: row.Item.Name,
Description: row.Item.Description,
LabelIDs: labelIDs,
LocationID: locationID,
}
updateItem := repo.ItemUpdate{
ID: item.ID,
LabelIDs: labelIds,
LocationID: locationID,
Name: row.Name,
Description: row.Description,
AssetID: effAID,
Insured: row.Insured,
Quantity: row.Quantity,
Archived: row.Archived,
PurchasePrice: row.PurchasePrice,
PurchaseFrom: row.PurchaseFrom,
PurchaseTime: row.PurchaseTime,
Manufacturer: row.Manufacturer,
ModelNumber: row.ModelNumber,
SerialNumber: row.SerialNumber,
LifetimeWarranty: row.LifetimeWarranty,
WarrantyExpires: row.WarrantyExpires,
WarrantyDetails: row.WarrantyDetails,
SoldTo: row.SoldTo,
SoldTime: row.SoldTime,
SoldPrice: row.SoldPrice,
SoldNotes: row.SoldNotes,
Notes: row.Notes,
Fields: fields,
if svc.autoIncrementAssetID {
highest++
data.AssetID = highest
}
item, err = svc.repo.Items.UpdateByGroup(ctx, GID, updateItem)
result, err := svc.repo.Items.Create(ctx, GID, data)
if err != nil {
return 0, err
return count, err
}
finished++
// Update the item with the rest of the data
_, err = svc.repo.Items.UpdateByGroup(ctx, GID, repo.ItemUpdate{
// Edges
LocationID: locationID,
LabelIDs: labelIDs,
AssetID: data.AssetID,
// General Fields
ID: result.ID,
Name: result.Name,
Description: result.Description,
Insured: row.Item.Insured,
Notes: row.Item.Notes,
Quantity: row.Item.Quantity,
// Identifies the item as imported
SerialNumber: row.Item.SerialNumber,
ModelNumber: row.Item.ModelNumber,
Manufacturer: row.Item.Manufacturer,
// Purchase
PurchaseFrom: row.Item.PurchaseFrom,
PurchasePrice: row.Item.PurchasePrice,
PurchaseTime: row.Item.PurchaseTime,
// Warranty
LifetimeWarranty: row.Item.LifetimeWarranty,
WarrantyExpires: row.Item.WarrantyExpires,
WarrantyDetails: row.Item.WarrantyDetails,
SoldTo: row.Item.SoldTo,
SoldPrice: row.Item.SoldPrice,
SoldTime: row.Item.SoldTime,
SoldNotes: row.Item.SoldNotes,
})
if err != nil {
return count, err
}
count++
}
return finished, nil
}
func (svc *ItemService) ExportTSV(ctx context.Context, GID uuid.UUID) ([][]string, error) {
items, err := svc.repo.Items.GetAll(ctx, GID)
if err != nil {
return nil, err
}
sheet := reporting.IOSheet{}
sheet.ReadItems(items)
return sheet.TSV()
}
func (svc *ItemService) ExportBillOfMaterialsTSV(ctx context.Context, GID uuid.UUID) ([]byte, error) {
items, err := svc.repo.Items.GetAll(ctx, GID)
if err != nil {
return nil, err
}
return reporting.BillOfMaterialsTSV(items)
return count, nil
}

View File

@@ -58,4 +58,5 @@ func TestItemService_AddAttachment(t *testing.T) {
bts, err := os.ReadFile(storedPath)
assert.NoError(t, err)
assert.Equal(t, contents, string(bts))
}

View File

@@ -0,0 +1,159 @@
package services
import (
"bytes"
"encoding/csv"
"errors"
"io"
"strconv"
"strings"
"time"
"github.com/hay-kot/homebox/backend/internal/data/repo"
)
func determineSeparator(data []byte) (rune, error) {
// First row
firstRow := bytes.Split(data, []byte("\n"))[0]
// find first comma or /t
comma := bytes.IndexByte(firstRow, ',')
tab := bytes.IndexByte(firstRow, '\t')
switch {
case comma == -1 && tab == -1:
return 0, errors.New("could not determine separator")
case tab > comma:
return '\t', nil
default:
return ',', nil
}
}
func ReadCsv(r io.Reader) ([][]string, error) {
data, err := io.ReadAll(r)
if err != nil {
return nil, err
}
reader := csv.NewReader(bytes.NewReader(data))
// Determine separator
sep, err := determineSeparator(data)
if err != nil {
return nil, err
}
reader.Comma = sep
return reader.ReadAll()
}
var ErrInvalidCsv = errors.New("invalid csv")
const NumOfCols = 21
func parseFloat(s string) float64 {
if s == "" {
return 0
}
f, _ := strconv.ParseFloat(s, 64)
return f
}
func parseDate(s string) time.Time {
if s == "" {
return time.Time{}
}
p, _ := time.Parse("01/02/2006", s)
return p
}
func parseBool(s string) bool {
switch strings.ToLower(s) {
case "true", "yes", "1":
return true
default:
return false
}
}
func parseInt(s string) int {
i, _ := strconv.Atoi(s)
return i
}
type csvRow struct {
Item repo.ItemOut
Location string
LabelStr string
}
func newCsvRow(row []string) csvRow {
return csvRow{
Location: row[1],
LabelStr: row[2],
Item: repo.ItemOut{
ItemSummary: repo.ItemSummary{
ImportRef: row[0],
Quantity: parseInt(row[3]),
Name: row[4],
Description: row[5],
Insured: parseBool(row[6]),
PurchasePrice: parseFloat(row[12]),
},
SerialNumber: row[7],
ModelNumber: row[8],
Manufacturer: row[9],
Notes: row[10],
PurchaseFrom: row[11],
PurchaseTime: parseDate(row[13]),
LifetimeWarranty: parseBool(row[14]),
WarrantyExpires: parseDate(row[15]),
WarrantyDetails: row[16],
SoldTo: row[17],
SoldPrice: parseFloat(row[18]),
SoldTime: parseDate(row[19]),
SoldNotes: row[20],
},
}
}
func (c csvRow) getLabels() []string {
split := strings.Split(c.LabelStr, ";")
// Trim each
for i, s := range split {
split[i] = strings.TrimSpace(s)
}
// Remove empty
for i, s := range split {
if s == "" {
split = append(split[:i], split[i+1:]...)
}
}
return split
}
func (c csvRow) validate() []error {
var errs []error
add := func(err error) {
errs = append(errs, err)
}
required := func(s string, name string) {
if s == "" {
add(errors.New(name + " is required"))
}
}
required(c.Location, "Location")
required(c.Item.Name, "Name")
return errs
}

View File

@@ -0,0 +1,164 @@
package services
import (
"bytes"
_ "embed"
"encoding/csv"
"fmt"
"reflect"
"testing"
"time"
"github.com/stretchr/testify/assert"
)
//go:embed testdata/import.csv
var CSVData_Comma []byte
//go:embed testdata/import.tsv
var CSVData_Tab []byte
func loadcsv() [][]string {
reader := csv.NewReader(bytes.NewReader(CSVData_Comma))
records, err := reader.ReadAll()
if err != nil {
panic(err)
}
return records
}
func Test_CorrectDateParsing(t *testing.T) {
t.Parallel()
expected := []time.Time{
time.Date(2021, 10, 13, 0, 0, 0, 0, time.UTC),
time.Date(2021, 10, 15, 0, 0, 0, 0, time.UTC),
time.Date(2021, 10, 13, 0, 0, 0, 0, time.UTC),
time.Date(2020, 10, 21, 0, 0, 0, 0, time.UTC),
time.Date(2020, 10, 14, 0, 0, 0, 0, time.UTC),
time.Date(2020, 9, 30, 0, 0, 0, 0, time.UTC),
}
records := loadcsv()
for i, record := range records {
if i == 0 {
continue
}
entity := newCsvRow(record)
expected := expected[i-1]
assert.Equal(t, expected, entity.Item.PurchaseTime, fmt.Sprintf("Failed on row %d", i))
assert.Equal(t, expected, entity.Item.WarrantyExpires, fmt.Sprintf("Failed on row %d", i))
assert.Equal(t, expected, entity.Item.SoldTime, fmt.Sprintf("Failed on row %d", i))
}
}
func Test_csvRow_getLabels(t *testing.T) {
type fields struct {
LabelStr string
}
tests := []struct {
name string
fields fields
want []string
}{
{
name: "basic test",
fields: fields{
LabelStr: "IOT;Home Assistant;Z-Wave",
},
want: []string{"IOT", "Home Assistant", "Z-Wave"},
},
{
name: "no labels",
fields: fields{
LabelStr: "",
},
want: []string{},
},
{
name: "single label",
fields: fields{
LabelStr: "IOT",
},
want: []string{"IOT"},
},
{
name: "trailing semicolon",
fields: fields{
LabelStr: "IOT;",
},
want: []string{"IOT"},
},
{
name: "whitespace",
fields: fields{
LabelStr: " IOT; Home Assistant; Z-Wave ",
},
want: []string{"IOT", "Home Assistant", "Z-Wave"},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
c := csvRow{
LabelStr: tt.fields.LabelStr,
}
if got := c.getLabels(); !reflect.DeepEqual(got, tt.want) {
t.Errorf("csvRow.getLabels() = %v, want %v", got, tt.want)
}
})
}
}
func Test_determineSeparator(t *testing.T) {
type args struct {
data []byte
}
tests := []struct {
name string
args args
want rune
wantErr bool
}{
{
name: "comma",
args: args{
data: CSVData_Comma,
},
want: ',',
wantErr: false,
},
{
name: "tab",
args: args{
data: CSVData_Tab,
},
want: '\t',
wantErr: false,
},
{
name: "invalid",
args: args{
data: []byte("a;b;c"),
},
want: 0,
wantErr: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := determineSeparator(tt.args.data)
if (err != nil) != tt.wantErr {
t.Errorf("determineSeparator() error = %v, wantErr %v", err, tt.wantErr)
return
}
if got != tt.want {
t.Errorf("determineSeparator() = %v, want %v", got, tt.want)
}
})
}
}

View File

@@ -0,0 +1,78 @@
package services
import (
"context"
"testing"
"github.com/google/uuid"
"github.com/hay-kot/homebox/backend/internal/data/repo"
"github.com/stretchr/testify/assert"
)
func TestItemService_CsvImport(t *testing.T) {
data := loadcsv()
svc := &ItemService{
repo: tRepos,
}
count, err := svc.CsvImport(context.Background(), tGroup.ID, data)
assert.Equal(t, 6, count)
assert.NoError(t, err)
// Check import refs are deduplicated
count, err = svc.CsvImport(context.Background(), tGroup.ID, data)
assert.Equal(t, 0, count)
assert.NoError(t, err)
items, err := svc.repo.Items.GetAll(context.Background(), tGroup.ID)
assert.NoError(t, err)
t.Cleanup(func() {
for _, item := range items {
err := svc.repo.Items.Delete(context.Background(), item.ID)
assert.NoError(t, err)
}
})
assert.Equal(t, len(items), 6)
dataCsv := []csvRow{}
for _, item := range data {
dataCsv = append(dataCsv, newCsvRow(item))
}
allLocation, err := tRepos.Locations.GetAll(context.Background(), tGroup.ID, repo.LocationQuery{})
assert.NoError(t, err)
locNames := []string{}
for _, loc := range allLocation {
locNames = append(locNames, loc.Name)
}
allLabels, err := tRepos.Labels.GetAll(context.Background(), tGroup.ID)
assert.NoError(t, err)
labelNames := []string{}
for _, label := range allLabels {
labelNames = append(labelNames, label.Name)
}
ids := []uuid.UUID{}
t.Cleanup((func() {
for _, id := range ids {
err := svc.repo.Items.Delete(context.Background(), id)
assert.NoError(t, err)
}
}))
for _, item := range items {
assert.Contains(t, locNames, item.Location.Name)
for _, label := range item.Labels {
assert.Contains(t, labelNames, label.Name)
}
for _, csvRow := range dataCsv {
if csvRow.Item.Name == item.Name {
assert.Equal(t, csvRow.Item.Description, item.Description)
assert.Equal(t, csvRow.Item.Quantity, item.Quantity)
assert.Equal(t, csvRow.Item.Insured, item.Insured)
}
}
}
}

View File

@@ -51,25 +51,21 @@ func (svc *UserService) RegisterUser(ctx context.Context, data UserRegistration)
Msg("Registering new user")
var (
err error
group repo.Group
token repo.GroupInvitation
// creatingGroup is true if the user is creating a new group.
creatingGroup = false
err error
group repo.Group
token repo.GroupInvitation
isOwner = false
)
switch data.GroupToken {
case "":
log.Debug().Msg("creating new group")
creatingGroup = true
isOwner = true
group, err = svc.repos.Groups.GroupCreate(ctx, "Home")
if err != nil {
log.Err(err).Msg("Failed to create group")
return repo.UserOut{}, err
}
default:
log.Debug().Msg("joining existing group")
token, err = svc.repos.Groups.InvitationGet(ctx, hasher.HashToken(data.GroupToken))
if err != nil {
log.Err(err).Msg("Failed to get invitation token")
@@ -85,7 +81,7 @@ func (svc *UserService) RegisterUser(ctx context.Context, data UserRegistration)
Password: hashed,
IsSuperuser: false,
GroupID: group.ID,
IsOwner: creatingGroup,
IsOwner: isOwner,
}
usr, err := svc.repos.Users.Create(ctx, usrCreate)
@@ -93,24 +89,21 @@ func (svc *UserService) RegisterUser(ctx context.Context, data UserRegistration)
return repo.UserOut{}, err
}
// Create the default labels and locations for the group.
if creatingGroup {
for _, label := range defaultLabels() {
_, err := svc.repos.Labels.Create(ctx, usr.GroupID, label)
if err != nil {
return repo.UserOut{}, err
}
}
for _, location := range defaultLocations() {
_, err := svc.repos.Locations.Create(ctx, usr.GroupID, location)
if err != nil {
return repo.UserOut{}, err
}
for _, label := range defaultLabels() {
_, err := svc.repos.Labels.Create(ctx, group.ID, label)
if err != nil {
return repo.UserOut{}, err
}
}
// Decrement the invitation token if it was used.
for _, location := range defaultLocations() {
_, err := svc.repos.Locations.Create(ctx, group.ID, location)
if err != nil {
return repo.UserOut{}, err
}
}
// Decrement the invitation token if it was used
if token.ID != uuid.Nil {
err = svc.repos.Groups.InvitationUpdate(ctx, token.ID, token.Uses-1)
if err != nil {
@@ -140,18 +133,13 @@ func (svc *UserService) UpdateSelf(ctx context.Context, ID uuid.UUID, data repo.
// ============================================================================
// 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) (UserAuthTokenDetail, error) {
attachmentToken := hasher.GenerateToken()
expiresAt := time.Now().Add(oneWeek)
if extendedSession {
expiresAt = time.Now().Add(oneWeek * 4)
}
attachmentData := repo.UserAuthTokenCreate{
UserID: userId,
TokenHash: attachmentToken.Hash,
ExpiresAt: expiresAt,
ExpiresAt: time.Now().Add(oneWeek),
}
_, err := svc.repos.AuthTokens.CreateToken(ctx, attachmentData, authroles.RoleAttachments)
@@ -163,7 +151,7 @@ func (svc *UserService) createSessionToken(ctx context.Context, userId uuid.UUID
data := repo.UserAuthTokenCreate{
UserID: userId,
TokenHash: userToken.Hash,
ExpiresAt: expiresAt,
ExpiresAt: time.Now().Add(oneWeek),
}
created, err := svc.repos.AuthTokens.CreateToken(ctx, data, authroles.RoleUser)
@@ -178,8 +166,9 @@ func (svc *UserService) createSessionToken(ctx context.Context, userId uuid.UUID
}, nil
}
func (svc *UserService) Login(ctx context.Context, username, password string, extendedSession bool) (UserAuthTokenDetail, error) {
func (svc *UserService) Login(ctx context.Context, username, password string) (UserAuthTokenDetail, error) {
usr, err := svc.repos.Users.GetOneEmail(ctx, username)
if err != nil {
// SECURITY: Perform hash to ensure response times are the same
hasher.CheckPasswordHash("not-a-real-password", "not-a-real-password")
@@ -190,7 +179,7 @@ func (svc *UserService) Login(ctx context.Context, username, password string, ex
return UserAuthTokenDetail{}, ErrorInvalidLogin
}
return svc.createSessionToken(ctx, usr.ID, extendedSession)
return svc.createSessionToken(ctx, usr.ID)
}
func (svc *UserService) Logout(ctx context.Context, token string) error {
@@ -203,11 +192,12 @@ func (svc *UserService) RenewToken(ctx context.Context, token string) (UserAuthT
hash := hasher.HashToken(token)
dbToken, err := svc.repos.AuthTokens.GetUserFromToken(ctx, hash)
if err != nil {
return UserAuthTokenDetail{}, ErrorInvalidToken
}
return svc.createSessionToken(ctx, dbToken.ID, false)
return svc.createSessionToken(ctx, dbToken.ID)
}
// DeleteSelf deletes the user that is currently logged based of the provided UUID

View File

@@ -144,19 +144,19 @@ func (a *Attachment) assignValues(columns []string, values []any) error {
// QueryItem queries the "item" edge of the Attachment entity.
func (a *Attachment) QueryItem() *ItemQuery {
return NewAttachmentClient(a.config).QueryItem(a)
return (&AttachmentClient{config: a.config}).QueryItem(a)
}
// QueryDocument queries the "document" edge of the Attachment entity.
func (a *Attachment) QueryDocument() *DocumentQuery {
return NewAttachmentClient(a.config).QueryDocument(a)
return (&AttachmentClient{config: a.config}).QueryDocument(a)
}
// Update returns a builder for updating this Attachment.
// Note that you need to call Attachment.Unwrap() before calling this method if this Attachment
// was returned from a transaction, and the transaction was committed or rolled back.
func (a *Attachment) Update() *AttachmentUpdateOne {
return NewAttachmentClient(a.config).UpdateOne(a)
return (&AttachmentClient{config: a.config}).UpdateOne(a)
}
// Unwrap unwraps the Attachment entity that was returned from a transaction after it was closed,
@@ -189,3 +189,9 @@ func (a *Attachment) String() string {
// Attachments is a parsable slice of Attachment.
type Attachments []*Attachment
func (a Attachments) config(cfg config) {
for _i := range a {
a[_i].config = cfg
}
}

View File

@@ -13,157 +13,251 @@ import (
// ID filters vertices based on their ID field.
func ID(id uuid.UUID) predicate.Attachment {
return predicate.Attachment(sql.FieldEQ(FieldID, id))
return predicate.Attachment(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.Attachment {
return predicate.Attachment(sql.FieldEQ(FieldID, id))
return predicate.Attachment(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.Attachment {
return predicate.Attachment(sql.FieldNEQ(FieldID, id))
return predicate.Attachment(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.Attachment {
return predicate.Attachment(sql.FieldIn(FieldID, ids...))
return predicate.Attachment(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.Attachment {
return predicate.Attachment(sql.FieldNotIn(FieldID, ids...))
return predicate.Attachment(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.Attachment {
return predicate.Attachment(sql.FieldGT(FieldID, id))
return predicate.Attachment(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.Attachment {
return predicate.Attachment(sql.FieldGTE(FieldID, id))
return predicate.Attachment(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.Attachment {
return predicate.Attachment(sql.FieldLT(FieldID, id))
return predicate.Attachment(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.Attachment {
return predicate.Attachment(sql.FieldLTE(FieldID, id))
return predicate.Attachment(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.Attachment {
return predicate.Attachment(sql.FieldEQ(FieldCreatedAt, v))
return predicate.Attachment(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.Attachment {
return predicate.Attachment(sql.FieldEQ(FieldUpdatedAt, v))
return predicate.Attachment(func(s *sql.Selector) {
s.Where(sql.EQ(s.C(FieldUpdatedAt), v))
})
}
// CreatedAtEQ applies the EQ predicate on the "created_at" field.
func CreatedAtEQ(v time.Time) predicate.Attachment {
return predicate.Attachment(sql.FieldEQ(FieldCreatedAt, v))
return predicate.Attachment(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.Attachment {
return predicate.Attachment(sql.FieldNEQ(FieldCreatedAt, v))
return predicate.Attachment(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.Attachment {
return predicate.Attachment(sql.FieldIn(FieldCreatedAt, vs...))
v := make([]any, len(vs))
for i := range v {
v[i] = vs[i]
}
return predicate.Attachment(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.Attachment {
return predicate.Attachment(sql.FieldNotIn(FieldCreatedAt, vs...))
v := make([]any, len(vs))
for i := range v {
v[i] = vs[i]
}
return predicate.Attachment(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.Attachment {
return predicate.Attachment(sql.FieldGT(FieldCreatedAt, v))
return predicate.Attachment(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.Attachment {
return predicate.Attachment(sql.FieldGTE(FieldCreatedAt, v))
return predicate.Attachment(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.Attachment {
return predicate.Attachment(sql.FieldLT(FieldCreatedAt, v))
return predicate.Attachment(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.Attachment {
return predicate.Attachment(sql.FieldLTE(FieldCreatedAt, v))
return predicate.Attachment(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.Attachment {
return predicate.Attachment(sql.FieldEQ(FieldUpdatedAt, v))
return predicate.Attachment(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.Attachment {
return predicate.Attachment(sql.FieldNEQ(FieldUpdatedAt, v))
return predicate.Attachment(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.Attachment {
return predicate.Attachment(sql.FieldIn(FieldUpdatedAt, vs...))
v := make([]any, len(vs))
for i := range v {
v[i] = vs[i]
}
return predicate.Attachment(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.Attachment {
return predicate.Attachment(sql.FieldNotIn(FieldUpdatedAt, vs...))
v := make([]any, len(vs))
for i := range v {
v[i] = vs[i]
}
return predicate.Attachment(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.Attachment {
return predicate.Attachment(sql.FieldGT(FieldUpdatedAt, v))
return predicate.Attachment(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.Attachment {
return predicate.Attachment(sql.FieldGTE(FieldUpdatedAt, v))
return predicate.Attachment(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.Attachment {
return predicate.Attachment(sql.FieldLT(FieldUpdatedAt, v))
return predicate.Attachment(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.Attachment {
return predicate.Attachment(sql.FieldLTE(FieldUpdatedAt, v))
return predicate.Attachment(func(s *sql.Selector) {
s.Where(sql.LTE(s.C(FieldUpdatedAt), v))
})
}
// TypeEQ applies the EQ predicate on the "type" field.
func TypeEQ(v Type) predicate.Attachment {
return predicate.Attachment(sql.FieldEQ(FieldType, v))
return predicate.Attachment(func(s *sql.Selector) {
s.Where(sql.EQ(s.C(FieldType), v))
})
}
// TypeNEQ applies the NEQ predicate on the "type" field.
func TypeNEQ(v Type) predicate.Attachment {
return predicate.Attachment(sql.FieldNEQ(FieldType, v))
return predicate.Attachment(func(s *sql.Selector) {
s.Where(sql.NEQ(s.C(FieldType), v))
})
}
// TypeIn applies the In predicate on the "type" field.
func TypeIn(vs ...Type) predicate.Attachment {
return predicate.Attachment(sql.FieldIn(FieldType, vs...))
v := make([]any, len(vs))
for i := range v {
v[i] = vs[i]
}
return predicate.Attachment(func(s *sql.Selector) {
s.Where(sql.In(s.C(FieldType), v...))
})
}
// TypeNotIn applies the NotIn predicate on the "type" field.
func TypeNotIn(vs ...Type) predicate.Attachment {
return predicate.Attachment(sql.FieldNotIn(FieldType, vs...))
v := make([]any, len(vs))
for i := range v {
v[i] = vs[i]
}
return predicate.Attachment(func(s *sql.Selector) {
s.Where(sql.NotIn(s.C(FieldType), v...))
})
}
// HasItem applies the HasEdge predicate on the "item" edge.
@@ -171,6 +265,7 @@ func HasItem() predicate.Attachment {
return predicate.Attachment(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)
@@ -198,6 +293,7 @@ func HasDocument() predicate.Attachment {
return predicate.Attachment(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)

View File

@@ -108,8 +108,50 @@ func (ac *AttachmentCreate) Mutation() *AttachmentMutation {
// Save creates the Attachment in the database.
func (ac *AttachmentCreate) Save(ctx context.Context) (*Attachment, error) {
var (
err error
node *Attachment
)
ac.defaults()
return withHooks[*Attachment, AttachmentMutation](ctx, ac.sqlSave, ac.mutation, ac.hooks)
if len(ac.hooks) == 0 {
if err = ac.check(); err != nil {
return nil, err
}
node, err = ac.sqlSave(ctx)
} else {
var mut Mutator = MutateFunc(func(ctx context.Context, m Mutation) (Value, error) {
mutation, ok := m.(*AttachmentMutation)
if !ok {
return nil, fmt.Errorf("unexpected mutation type %T", m)
}
if err = ac.check(); err != nil {
return nil, err
}
ac.mutation = mutation
if node, err = ac.sqlSave(ctx); err != nil {
return nil, err
}
mutation.id = &node.ID
mutation.done = true
return node, err
})
for i := len(ac.hooks) - 1; i >= 0; i-- {
if ac.hooks[i] == nil {
return nil, fmt.Errorf("ent: uninitialized hook (forgotten import ent/runtime?)")
}
mut = ac.hooks[i](mut)
}
v, err := mut.Mutate(ctx, ac.mutation)
if err != nil {
return nil, err
}
nv, ok := v.(*Attachment)
if !ok {
return nil, fmt.Errorf("unexpected node type %T returned from AttachmentMutation", v)
}
node = nv
}
return node, err
}
// SaveX calls Save and panics if Save returns an error.
@@ -180,9 +222,6 @@ func (ac *AttachmentCreate) check() error {
}
func (ac *AttachmentCreate) sqlSave(ctx context.Context) (*Attachment, error) {
if err := ac.check(); err != nil {
return nil, err
}
_node, _spec := ac.createSpec()
if err := sqlgraph.CreateNode(ctx, ac.driver, _spec); err != nil {
if sqlgraph.IsConstraintError(err) {
@@ -197,15 +236,19 @@ func (ac *AttachmentCreate) sqlSave(ctx context.Context) (*Attachment, error) {
return nil, err
}
}
ac.mutation.id = &_node.ID
ac.mutation.done = true
return _node, nil
}
func (ac *AttachmentCreate) createSpec() (*Attachment, *sqlgraph.CreateSpec) {
var (
_node = &Attachment{config: ac.config}
_spec = sqlgraph.NewCreateSpec(attachment.Table, sqlgraph.NewFieldSpec(attachment.FieldID, field.TypeUUID))
_spec = &sqlgraph.CreateSpec{
Table: attachment.Table,
ID: &sqlgraph.FieldSpec{
Type: field.TypeUUID,
Column: attachment.FieldID,
},
}
)
if id, ok := ac.mutation.ID(); ok {
_node.ID = id
@@ -231,7 +274,10 @@ func (ac *AttachmentCreate) createSpec() (*Attachment, *sqlgraph.CreateSpec) {
Columns: []string{attachment.ItemColumn},
Bidi: false,
Target: &sqlgraph.EdgeTarget{
IDSpec: sqlgraph.NewFieldSpec(item.FieldID, field.TypeUUID),
IDSpec: &sqlgraph.FieldSpec{
Type: field.TypeUUID,
Column: item.FieldID,
},
},
}
for _, k := range nodes {
@@ -248,7 +294,10 @@ func (ac *AttachmentCreate) createSpec() (*Attachment, *sqlgraph.CreateSpec) {
Columns: []string{attachment.DocumentColumn},
Bidi: false,
Target: &sqlgraph.EdgeTarget{
IDSpec: sqlgraph.NewFieldSpec(document.FieldID, field.TypeUUID),
IDSpec: &sqlgraph.FieldSpec{
Type: field.TypeUUID,
Column: document.FieldID,
},
},
}
for _, k := range nodes {

View File

@@ -4,6 +4,7 @@ package ent
import (
"context"
"fmt"
"entgo.io/ent/dialect/sql"
"entgo.io/ent/dialect/sql/sqlgraph"
@@ -27,7 +28,34 @@ func (ad *AttachmentDelete) Where(ps ...predicate.Attachment) *AttachmentDelete
// Exec executes the deletion query and returns how many vertices were deleted.
func (ad *AttachmentDelete) Exec(ctx context.Context) (int, error) {
return withHooks[int, AttachmentMutation](ctx, ad.sqlExec, ad.mutation, ad.hooks)
var (
err error
affected int
)
if len(ad.hooks) == 0 {
affected, err = ad.sqlExec(ctx)
} else {
var mut Mutator = MutateFunc(func(ctx context.Context, m Mutation) (Value, error) {
mutation, ok := m.(*AttachmentMutation)
if !ok {
return nil, fmt.Errorf("unexpected mutation type %T", m)
}
ad.mutation = mutation
affected, err = ad.sqlExec(ctx)
mutation.done = true
return affected, err
})
for i := len(ad.hooks) - 1; i >= 0; i-- {
if ad.hooks[i] == nil {
return 0, fmt.Errorf("ent: uninitialized hook (forgotten import ent/runtime?)")
}
mut = ad.hooks[i](mut)
}
if _, err := mut.Mutate(ctx, ad.mutation); err != nil {
return 0, err
}
}
return affected, err
}
// ExecX is like Exec, but panics if an error occurs.
@@ -40,7 +68,15 @@ func (ad *AttachmentDelete) ExecX(ctx context.Context) int {
}
func (ad *AttachmentDelete) sqlExec(ctx context.Context) (int, error) {
_spec := sqlgraph.NewDeleteSpec(attachment.Table, sqlgraph.NewFieldSpec(attachment.FieldID, field.TypeUUID))
_spec := &sqlgraph.DeleteSpec{
Node: &sqlgraph.NodeSpec{
Table: attachment.Table,
ID: &sqlgraph.FieldSpec{
Type: field.TypeUUID,
Column: attachment.FieldID,
},
},
}
if ps := ad.mutation.predicates; len(ps) > 0 {
_spec.Predicate = func(selector *sql.Selector) {
for i := range ps {
@@ -52,7 +88,6 @@ func (ad *AttachmentDelete) sqlExec(ctx context.Context) (int, error) {
if err != nil && sqlgraph.IsConstraintError(err) {
err = &ConstraintError{msg: err.Error(), wrap: err}
}
ad.mutation.done = true
return affected, err
}
@@ -61,12 +96,6 @@ type AttachmentDeleteOne struct {
ad *AttachmentDelete
}
// Where appends a list predicates to the AttachmentDelete builder.
func (ado *AttachmentDeleteOne) Where(ps ...predicate.Attachment) *AttachmentDeleteOne {
ado.ad.mutation.Where(ps...)
return ado
}
// Exec executes the deletion query.
func (ado *AttachmentDeleteOne) Exec(ctx context.Context) error {
n, err := ado.ad.Exec(ctx)
@@ -82,7 +111,5 @@ func (ado *AttachmentDeleteOne) Exec(ctx context.Context) error {
// ExecX is like Exec, but panics if an error occurs.
func (ado *AttachmentDeleteOne) ExecX(ctx context.Context) {
if err := ado.Exec(ctx); err != nil {
panic(err)
}
ado.ad.ExecX(ctx)
}

View File

@@ -20,9 +20,11 @@ import (
// AttachmentQuery is the builder for querying Attachment entities.
type AttachmentQuery struct {
config
ctx *QueryContext
limit *int
offset *int
unique *bool
order []OrderFunc
inters []Interceptor
fields []string
predicates []predicate.Attachment
withItem *ItemQuery
withDocument *DocumentQuery
@@ -38,26 +40,26 @@ func (aq *AttachmentQuery) Where(ps ...predicate.Attachment) *AttachmentQuery {
return aq
}
// Limit the number of records to be returned by this query.
// Limit adds a limit step to the query.
func (aq *AttachmentQuery) Limit(limit int) *AttachmentQuery {
aq.ctx.Limit = &limit
aq.limit = &limit
return aq
}
// Offset to start from.
// Offset adds an offset step to the query.
func (aq *AttachmentQuery) Offset(offset int) *AttachmentQuery {
aq.ctx.Offset = &offset
aq.offset = &offset
return aq
}
// 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 (aq *AttachmentQuery) Unique(unique bool) *AttachmentQuery {
aq.ctx.Unique = &unique
aq.unique = &unique
return aq
}
// Order specifies how the records should be ordered.
// Order adds an order step to the query.
func (aq *AttachmentQuery) Order(o ...OrderFunc) *AttachmentQuery {
aq.order = append(aq.order, o...)
return aq
@@ -65,7 +67,7 @@ func (aq *AttachmentQuery) Order(o ...OrderFunc) *AttachmentQuery {
// QueryItem chains the current query on the "item" edge.
func (aq *AttachmentQuery) QueryItem() *ItemQuery {
query := (&ItemClient{config: aq.config}).Query()
query := &ItemQuery{config: aq.config}
query.path = func(ctx context.Context) (fromU *sql.Selector, err error) {
if err := aq.prepareQuery(ctx); err != nil {
return nil, err
@@ -87,7 +89,7 @@ func (aq *AttachmentQuery) QueryItem() *ItemQuery {
// QueryDocument chains the current query on the "document" edge.
func (aq *AttachmentQuery) QueryDocument() *DocumentQuery {
query := (&DocumentClient{config: aq.config}).Query()
query := &DocumentQuery{config: aq.config}
query.path = func(ctx context.Context) (fromU *sql.Selector, err error) {
if err := aq.prepareQuery(ctx); err != nil {
return nil, err
@@ -110,7 +112,7 @@ func (aq *AttachmentQuery) QueryDocument() *DocumentQuery {
// First returns the first Attachment entity from the query.
// Returns a *NotFoundError when no Attachment was found.
func (aq *AttachmentQuery) First(ctx context.Context) (*Attachment, error) {
nodes, err := aq.Limit(1).All(setContextOp(ctx, aq.ctx, "First"))
nodes, err := aq.Limit(1).All(ctx)
if err != nil {
return nil, err
}
@@ -133,7 +135,7 @@ func (aq *AttachmentQuery) FirstX(ctx context.Context) *Attachment {
// Returns a *NotFoundError when no Attachment ID was found.
func (aq *AttachmentQuery) FirstID(ctx context.Context) (id uuid.UUID, err error) {
var ids []uuid.UUID
if ids, err = aq.Limit(1).IDs(setContextOp(ctx, aq.ctx, "FirstID")); err != nil {
if ids, err = aq.Limit(1).IDs(ctx); err != nil {
return
}
if len(ids) == 0 {
@@ -156,7 +158,7 @@ func (aq *AttachmentQuery) FirstIDX(ctx context.Context) uuid.UUID {
// Returns a *NotSingularError when more than one Attachment entity is found.
// Returns a *NotFoundError when no Attachment entities are found.
func (aq *AttachmentQuery) Only(ctx context.Context) (*Attachment, error) {
nodes, err := aq.Limit(2).All(setContextOp(ctx, aq.ctx, "Only"))
nodes, err := aq.Limit(2).All(ctx)
if err != nil {
return nil, err
}
@@ -184,7 +186,7 @@ func (aq *AttachmentQuery) OnlyX(ctx context.Context) *Attachment {
// Returns a *NotFoundError when no entities are found.
func (aq *AttachmentQuery) OnlyID(ctx context.Context) (id uuid.UUID, err error) {
var ids []uuid.UUID
if ids, err = aq.Limit(2).IDs(setContextOp(ctx, aq.ctx, "OnlyID")); err != nil {
if ids, err = aq.Limit(2).IDs(ctx); err != nil {
return
}
switch len(ids) {
@@ -209,12 +211,10 @@ func (aq *AttachmentQuery) OnlyIDX(ctx context.Context) uuid.UUID {
// All executes the query and returns a list of Attachments.
func (aq *AttachmentQuery) All(ctx context.Context) ([]*Attachment, error) {
ctx = setContextOp(ctx, aq.ctx, "All")
if err := aq.prepareQuery(ctx); err != nil {
return nil, err
}
qr := querierAll[[]*Attachment, *AttachmentQuery]()
return withInterceptors[[]*Attachment](ctx, aq, qr, aq.inters)
return aq.sqlAll(ctx)
}
// AllX is like All, but panics if an error occurs.
@@ -227,12 +227,9 @@ func (aq *AttachmentQuery) AllX(ctx context.Context) []*Attachment {
}
// IDs executes the query and returns a list of Attachment IDs.
func (aq *AttachmentQuery) IDs(ctx context.Context) (ids []uuid.UUID, err error) {
if aq.ctx.Unique == nil && aq.path != nil {
aq.Unique(true)
}
ctx = setContextOp(ctx, aq.ctx, "IDs")
if err = aq.Select(attachment.FieldID).Scan(ctx, &ids); err != nil {
func (aq *AttachmentQuery) IDs(ctx context.Context) ([]uuid.UUID, error) {
var ids []uuid.UUID
if err := aq.Select(attachment.FieldID).Scan(ctx, &ids); err != nil {
return nil, err
}
return ids, nil
@@ -249,11 +246,10 @@ func (aq *AttachmentQuery) IDsX(ctx context.Context) []uuid.UUID {
// Count returns the count of the given query.
func (aq *AttachmentQuery) Count(ctx context.Context) (int, error) {
ctx = setContextOp(ctx, aq.ctx, "Count")
if err := aq.prepareQuery(ctx); err != nil {
return 0, err
}
return withInterceptors[int](ctx, aq, querierCount[*AttachmentQuery](), aq.inters)
return aq.sqlCount(ctx)
}
// CountX is like Count, but panics if an error occurs.
@@ -267,15 +263,10 @@ func (aq *AttachmentQuery) CountX(ctx context.Context) int {
// Exist returns true if the query has elements in the graph.
func (aq *AttachmentQuery) Exist(ctx context.Context) (bool, error) {
ctx = setContextOp(ctx, aq.ctx, "Exist")
switch _, err := aq.FirstID(ctx); {
case IsNotFound(err):
return false, nil
case err != nil:
return false, fmt.Errorf("ent: check existence: %w", err)
default:
return true, nil
if err := aq.prepareQuery(ctx); err != nil {
return false, err
}
return aq.sqlExist(ctx)
}
// ExistX is like Exist, but panics if an error occurs.
@@ -295,22 +286,23 @@ func (aq *AttachmentQuery) Clone() *AttachmentQuery {
}
return &AttachmentQuery{
config: aq.config,
ctx: aq.ctx.Clone(),
limit: aq.limit,
offset: aq.offset,
order: append([]OrderFunc{}, aq.order...),
inters: append([]Interceptor{}, aq.inters...),
predicates: append([]predicate.Attachment{}, aq.predicates...),
withItem: aq.withItem.Clone(),
withDocument: aq.withDocument.Clone(),
// clone intermediate query.
sql: aq.sql.Clone(),
path: aq.path,
sql: aq.sql.Clone(),
path: aq.path,
unique: aq.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 (aq *AttachmentQuery) WithItem(opts ...func(*ItemQuery)) *AttachmentQuery {
query := (&ItemClient{config: aq.config}).Query()
query := &ItemQuery{config: aq.config}
for _, opt := range opts {
opt(query)
}
@@ -321,7 +313,7 @@ func (aq *AttachmentQuery) WithItem(opts ...func(*ItemQuery)) *AttachmentQuery {
// 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 (aq *AttachmentQuery) WithDocument(opts ...func(*DocumentQuery)) *AttachmentQuery {
query := (&DocumentClient{config: aq.config}).Query()
query := &DocumentQuery{config: aq.config}
for _, opt := range opts {
opt(query)
}
@@ -344,11 +336,16 @@ func (aq *AttachmentQuery) WithDocument(opts ...func(*DocumentQuery)) *Attachmen
// Aggregate(ent.Count()).
// Scan(ctx, &v)
func (aq *AttachmentQuery) GroupBy(field string, fields ...string) *AttachmentGroupBy {
aq.ctx.Fields = append([]string{field}, fields...)
grbuild := &AttachmentGroupBy{build: aq}
grbuild.flds = &aq.ctx.Fields
grbuild := &AttachmentGroupBy{config: aq.config}
grbuild.fields = append([]string{field}, fields...)
grbuild.path = func(ctx context.Context) (prev *sql.Selector, err error) {
if err := aq.prepareQuery(ctx); err != nil {
return nil, err
}
return aq.sqlQuery(ctx), nil
}
grbuild.label = attachment.Label
grbuild.scan = grbuild.Scan
grbuild.flds, grbuild.scan = &grbuild.fields, grbuild.Scan
return grbuild
}
@@ -365,11 +362,11 @@ func (aq *AttachmentQuery) GroupBy(field string, fields ...string) *AttachmentGr
// Select(attachment.FieldCreatedAt).
// Scan(ctx, &v)
func (aq *AttachmentQuery) Select(fields ...string) *AttachmentSelect {
aq.ctx.Fields = append(aq.ctx.Fields, fields...)
sbuild := &AttachmentSelect{AttachmentQuery: aq}
sbuild.label = attachment.Label
sbuild.flds, sbuild.scan = &aq.ctx.Fields, sbuild.Scan
return sbuild
aq.fields = append(aq.fields, fields...)
selbuild := &AttachmentSelect{AttachmentQuery: aq}
selbuild.label = attachment.Label
selbuild.flds, selbuild.scan = &aq.fields, selbuild.Scan
return selbuild
}
// Aggregate returns a AttachmentSelect configured with the given aggregations.
@@ -378,17 +375,7 @@ func (aq *AttachmentQuery) Aggregate(fns ...AggregateFunc) *AttachmentSelect {
}
func (aq *AttachmentQuery) prepareQuery(ctx context.Context) error {
for _, inter := range aq.inters {
if inter == nil {
return fmt.Errorf("ent: uninitialized interceptor (forgotten import ent/runtime?)")
}
if trv, ok := inter.(Traverser); ok {
if err := trv.Traverse(ctx, aq); err != nil {
return err
}
}
}
for _, f := range aq.ctx.Fields {
for _, f := range aq.fields {
if !attachment.ValidColumn(f) {
return &ValidationError{Name: f, err: fmt.Errorf("ent: invalid field %q for query", f)}
}
@@ -465,9 +452,6 @@ func (aq *AttachmentQuery) loadItem(ctx context.Context, query *ItemQuery, nodes
}
nodeids[fk] = append(nodeids[fk], nodes[i])
}
if len(ids) == 0 {
return nil
}
query.Where(item.IDIn(ids...))
neighbors, err := query.All(ctx)
if err != nil {
@@ -497,9 +481,6 @@ func (aq *AttachmentQuery) loadDocument(ctx context.Context, query *DocumentQuer
}
nodeids[fk] = append(nodeids[fk], nodes[i])
}
if len(ids) == 0 {
return nil
}
query.Where(document.IDIn(ids...))
neighbors, err := query.All(ctx)
if err != nil {
@@ -519,22 +500,41 @@ func (aq *AttachmentQuery) loadDocument(ctx context.Context, query *DocumentQuer
func (aq *AttachmentQuery) sqlCount(ctx context.Context) (int, error) {
_spec := aq.querySpec()
_spec.Node.Columns = aq.ctx.Fields
if len(aq.ctx.Fields) > 0 {
_spec.Unique = aq.ctx.Unique != nil && *aq.ctx.Unique
_spec.Node.Columns = aq.fields
if len(aq.fields) > 0 {
_spec.Unique = aq.unique != nil && *aq.unique
}
return sqlgraph.CountNodes(ctx, aq.driver, _spec)
}
func (aq *AttachmentQuery) querySpec() *sqlgraph.QuerySpec {
_spec := sqlgraph.NewQuerySpec(attachment.Table, attachment.Columns, sqlgraph.NewFieldSpec(attachment.FieldID, field.TypeUUID))
_spec.From = aq.sql
if unique := aq.ctx.Unique; unique != nil {
_spec.Unique = *unique
} else if aq.path != nil {
_spec.Unique = true
func (aq *AttachmentQuery) sqlExist(ctx context.Context) (bool, error) {
switch _, err := aq.FirstID(ctx); {
case IsNotFound(err):
return false, nil
case err != nil:
return false, fmt.Errorf("ent: check existence: %w", err)
default:
return true, nil
}
if fields := aq.ctx.Fields; len(fields) > 0 {
}
func (aq *AttachmentQuery) querySpec() *sqlgraph.QuerySpec {
_spec := &sqlgraph.QuerySpec{
Node: &sqlgraph.NodeSpec{
Table: attachment.Table,
Columns: attachment.Columns,
ID: &sqlgraph.FieldSpec{
Type: field.TypeUUID,
Column: attachment.FieldID,
},
},
From: aq.sql,
Unique: true,
}
if unique := aq.unique; unique != nil {
_spec.Unique = *unique
}
if fields := aq.fields; len(fields) > 0 {
_spec.Node.Columns = make([]string, 0, len(fields))
_spec.Node.Columns = append(_spec.Node.Columns, attachment.FieldID)
for i := range fields {
@@ -550,10 +550,10 @@ func (aq *AttachmentQuery) querySpec() *sqlgraph.QuerySpec {
}
}
}
if limit := aq.ctx.Limit; limit != nil {
if limit := aq.limit; limit != nil {
_spec.Limit = *limit
}
if offset := aq.ctx.Offset; offset != nil {
if offset := aq.offset; offset != nil {
_spec.Offset = *offset
}
if ps := aq.order; len(ps) > 0 {
@@ -569,7 +569,7 @@ func (aq *AttachmentQuery) querySpec() *sqlgraph.QuerySpec {
func (aq *AttachmentQuery) sqlQuery(ctx context.Context) *sql.Selector {
builder := sql.Dialect(aq.driver.Dialect())
t1 := builder.Table(attachment.Table)
columns := aq.ctx.Fields
columns := aq.fields
if len(columns) == 0 {
columns = attachment.Columns
}
@@ -578,7 +578,7 @@ func (aq *AttachmentQuery) sqlQuery(ctx context.Context) *sql.Selector {
selector = aq.sql
selector.Select(selector.Columns(columns...)...)
}
if aq.ctx.Unique != nil && *aq.ctx.Unique {
if aq.unique != nil && *aq.unique {
selector.Distinct()
}
for _, p := range aq.predicates {
@@ -587,12 +587,12 @@ func (aq *AttachmentQuery) sqlQuery(ctx context.Context) *sql.Selector {
for _, p := range aq.order {
p(selector)
}
if offset := aq.ctx.Offset; offset != nil {
if offset := aq.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 := aq.ctx.Limit; limit != nil {
if limit := aq.limit; limit != nil {
selector.Limit(*limit)
}
return selector
@@ -600,8 +600,13 @@ func (aq *AttachmentQuery) sqlQuery(ctx context.Context) *sql.Selector {
// AttachmentGroupBy is the group-by builder for Attachment entities.
type AttachmentGroupBy struct {
config
selector
build *AttachmentQuery
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.
@@ -610,46 +615,58 @@ func (agb *AttachmentGroupBy) Aggregate(fns ...AggregateFunc) *AttachmentGroupBy
return agb
}
// Scan applies the selector query and scans the result into the given value.
// Scan applies the group-by query and scans the result into the given value.
func (agb *AttachmentGroupBy) Scan(ctx context.Context, v any) error {
ctx = setContextOp(ctx, agb.build.ctx, "GroupBy")
if err := agb.build.prepareQuery(ctx); err != nil {
query, err := agb.path(ctx)
if err != nil {
return err
}
return scanWithInterceptors[*AttachmentQuery, *AttachmentGroupBy](ctx, agb.build, agb, agb.build.inters, v)
agb.sql = query
return agb.sqlScan(ctx, v)
}
func (agb *AttachmentGroupBy) sqlScan(ctx context.Context, root *AttachmentQuery, v any) error {
selector := root.sqlQuery(ctx).Select()
aggregation := make([]string, 0, len(agb.fns))
for _, fn := range agb.fns {
aggregation = append(aggregation, fn(selector))
}
if len(selector.SelectedColumns()) == 0 {
columns := make([]string, 0, len(*agb.flds)+len(agb.fns))
for _, f := range *agb.flds {
columns = append(columns, selector.C(f))
func (agb *AttachmentGroupBy) sqlScan(ctx context.Context, v any) error {
for _, f := range agb.fields {
if !attachment.ValidColumn(f) {
return &ValidationError{Name: f, err: fmt.Errorf("invalid field %q for group-by", f)}
}
columns = append(columns, aggregation...)
selector.Select(columns...)
}
selector.GroupBy(selector.Columns(*agb.flds...)...)
selector := agb.sqlQuery()
if err := selector.Err(); err != nil {
return err
}
rows := &sql.Rows{}
query, args := selector.Query()
if err := agb.build.driver.Query(ctx, query, args, rows); err != nil {
if err := agb.driver.Query(ctx, query, args, rows); err != nil {
return err
}
defer rows.Close()
return sql.ScanSlice(rows, v)
}
func (agb *AttachmentGroupBy) sqlQuery() *sql.Selector {
selector := agb.sql.Select()
aggregation := make([]string, 0, len(agb.fns))
for _, fn := range agb.fns {
aggregation = append(aggregation, fn(selector))
}
if len(selector.SelectedColumns()) == 0 {
columns := make([]string, 0, len(agb.fields)+len(agb.fns))
for _, f := range agb.fields {
columns = append(columns, selector.C(f))
}
columns = append(columns, aggregation...)
selector.Select(columns...)
}
return selector.GroupBy(selector.Columns(agb.fields...)...)
}
// AttachmentSelect is the builder for selecting fields of Attachment entities.
type AttachmentSelect struct {
*AttachmentQuery
selector
// intermediate query (i.e. traversal path).
sql *sql.Selector
}
// Aggregate adds the given aggregation functions to the selector query.
@@ -660,27 +677,26 @@ func (as *AttachmentSelect) Aggregate(fns ...AggregateFunc) *AttachmentSelect {
// Scan applies the selector query and scans the result into the given value.
func (as *AttachmentSelect) Scan(ctx context.Context, v any) error {
ctx = setContextOp(ctx, as.ctx, "Select")
if err := as.prepareQuery(ctx); err != nil {
return err
}
return scanWithInterceptors[*AttachmentQuery, *AttachmentSelect](ctx, as.AttachmentQuery, as, as.inters, v)
as.sql = as.AttachmentQuery.sqlQuery(ctx)
return as.sqlScan(ctx, v)
}
func (as *AttachmentSelect) sqlScan(ctx context.Context, root *AttachmentQuery, v any) error {
selector := root.sqlQuery(ctx)
func (as *AttachmentSelect) sqlScan(ctx context.Context, v any) error {
aggregation := make([]string, 0, len(as.fns))
for _, fn := range as.fns {
aggregation = append(aggregation, fn(selector))
aggregation = append(aggregation, fn(as.sql))
}
switch n := len(*as.selector.flds); {
case n == 0 && len(aggregation) > 0:
selector.Select(aggregation...)
as.sql.Select(aggregation...)
case n != 0 && len(aggregation) > 0:
selector.AppendSelect(aggregation...)
as.sql.AppendSelect(aggregation...)
}
rows := &sql.Rows{}
query, args := selector.Query()
query, args := as.sql.Query()
if err := as.driver.Query(ctx, query, args, rows); err != nil {
return err
}

View File

@@ -92,8 +92,41 @@ func (au *AttachmentUpdate) ClearDocument() *AttachmentUpdate {
// Save executes the query and returns the number of nodes affected by the update operation.
func (au *AttachmentUpdate) Save(ctx context.Context) (int, error) {
var (
err error
affected int
)
au.defaults()
return withHooks[int, AttachmentMutation](ctx, au.sqlSave, au.mutation, au.hooks)
if len(au.hooks) == 0 {
if err = au.check(); err != nil {
return 0, err
}
affected, err = au.sqlSave(ctx)
} else {
var mut Mutator = MutateFunc(func(ctx context.Context, m Mutation) (Value, error) {
mutation, ok := m.(*AttachmentMutation)
if !ok {
return nil, fmt.Errorf("unexpected mutation type %T", m)
}
if err = au.check(); err != nil {
return 0, err
}
au.mutation = mutation
affected, err = au.sqlSave(ctx)
mutation.done = true
return affected, err
})
for i := len(au.hooks) - 1; i >= 0; i-- {
if au.hooks[i] == nil {
return 0, fmt.Errorf("ent: uninitialized hook (forgotten import ent/runtime?)")
}
mut = au.hooks[i](mut)
}
if _, err := mut.Mutate(ctx, au.mutation); err != nil {
return 0, err
}
}
return affected, err
}
// SaveX is like Save, but panics if an error occurs.
@@ -143,10 +176,16 @@ func (au *AttachmentUpdate) check() error {
}
func (au *AttachmentUpdate) sqlSave(ctx context.Context) (n int, err error) {
if err := au.check(); err != nil {
return n, err
_spec := &sqlgraph.UpdateSpec{
Node: &sqlgraph.NodeSpec{
Table: attachment.Table,
Columns: attachment.Columns,
ID: &sqlgraph.FieldSpec{
Type: field.TypeUUID,
Column: attachment.FieldID,
},
},
}
_spec := sqlgraph.NewUpdateSpec(attachment.Table, attachment.Columns, sqlgraph.NewFieldSpec(attachment.FieldID, field.TypeUUID))
if ps := au.mutation.predicates; len(ps) > 0 {
_spec.Predicate = func(selector *sql.Selector) {
for i := range ps {
@@ -168,7 +207,10 @@ func (au *AttachmentUpdate) sqlSave(ctx context.Context) (n int, err error) {
Columns: []string{attachment.ItemColumn},
Bidi: false,
Target: &sqlgraph.EdgeTarget{
IDSpec: sqlgraph.NewFieldSpec(item.FieldID, field.TypeUUID),
IDSpec: &sqlgraph.FieldSpec{
Type: field.TypeUUID,
Column: item.FieldID,
},
},
}
_spec.Edges.Clear = append(_spec.Edges.Clear, edge)
@@ -181,7 +223,10 @@ func (au *AttachmentUpdate) sqlSave(ctx context.Context) (n int, err error) {
Columns: []string{attachment.ItemColumn},
Bidi: false,
Target: &sqlgraph.EdgeTarget{
IDSpec: sqlgraph.NewFieldSpec(item.FieldID, field.TypeUUID),
IDSpec: &sqlgraph.FieldSpec{
Type: field.TypeUUID,
Column: item.FieldID,
},
},
}
for _, k := range nodes {
@@ -197,7 +242,10 @@ func (au *AttachmentUpdate) sqlSave(ctx context.Context) (n int, err error) {
Columns: []string{attachment.DocumentColumn},
Bidi: false,
Target: &sqlgraph.EdgeTarget{
IDSpec: sqlgraph.NewFieldSpec(document.FieldID, field.TypeUUID),
IDSpec: &sqlgraph.FieldSpec{
Type: field.TypeUUID,
Column: document.FieldID,
},
},
}
_spec.Edges.Clear = append(_spec.Edges.Clear, edge)
@@ -210,7 +258,10 @@ func (au *AttachmentUpdate) sqlSave(ctx context.Context) (n int, err error) {
Columns: []string{attachment.DocumentColumn},
Bidi: false,
Target: &sqlgraph.EdgeTarget{
IDSpec: sqlgraph.NewFieldSpec(document.FieldID, field.TypeUUID),
IDSpec: &sqlgraph.FieldSpec{
Type: field.TypeUUID,
Column: document.FieldID,
},
},
}
for _, k := range nodes {
@@ -226,7 +277,6 @@ func (au *AttachmentUpdate) sqlSave(ctx context.Context) (n int, err error) {
}
return 0, err
}
au.mutation.done = true
return n, nil
}
@@ -297,12 +347,6 @@ func (auo *AttachmentUpdateOne) ClearDocument() *AttachmentUpdateOne {
return auo
}
// Where appends a list predicates to the AttachmentUpdate builder.
func (auo *AttachmentUpdateOne) Where(ps ...predicate.Attachment) *AttachmentUpdateOne {
auo.mutation.Where(ps...)
return auo
}
// Select allows selecting one or more fields (columns) of the returned entity.
// The default is selecting all fields defined in the entity schema.
func (auo *AttachmentUpdateOne) Select(field string, fields ...string) *AttachmentUpdateOne {
@@ -312,8 +356,47 @@ func (auo *AttachmentUpdateOne) Select(field string, fields ...string) *Attachme
// Save executes the query and returns the updated Attachment entity.
func (auo *AttachmentUpdateOne) Save(ctx context.Context) (*Attachment, error) {
var (
err error
node *Attachment
)
auo.defaults()
return withHooks[*Attachment, AttachmentMutation](ctx, auo.sqlSave, auo.mutation, auo.hooks)
if len(auo.hooks) == 0 {
if err = auo.check(); err != nil {
return nil, err
}
node, err = auo.sqlSave(ctx)
} else {
var mut Mutator = MutateFunc(func(ctx context.Context, m Mutation) (Value, error) {
mutation, ok := m.(*AttachmentMutation)
if !ok {
return nil, fmt.Errorf("unexpected mutation type %T", m)
}
if err = auo.check(); err != nil {
return nil, err
}
auo.mutation = mutation
node, err = auo.sqlSave(ctx)
mutation.done = true
return node, err
})
for i := len(auo.hooks) - 1; i >= 0; i-- {
if auo.hooks[i] == nil {
return nil, fmt.Errorf("ent: uninitialized hook (forgotten import ent/runtime?)")
}
mut = auo.hooks[i](mut)
}
v, err := mut.Mutate(ctx, auo.mutation)
if err != nil {
return nil, err
}
nv, ok := v.(*Attachment)
if !ok {
return nil, fmt.Errorf("unexpected node type %T returned from AttachmentMutation", v)
}
node = nv
}
return node, err
}
// SaveX is like Save, but panics if an error occurs.
@@ -363,10 +446,16 @@ func (auo *AttachmentUpdateOne) check() error {
}
func (auo *AttachmentUpdateOne) sqlSave(ctx context.Context) (_node *Attachment, err error) {
if err := auo.check(); err != nil {
return _node, err
_spec := &sqlgraph.UpdateSpec{
Node: &sqlgraph.NodeSpec{
Table: attachment.Table,
Columns: attachment.Columns,
ID: &sqlgraph.FieldSpec{
Type: field.TypeUUID,
Column: attachment.FieldID,
},
},
}
_spec := sqlgraph.NewUpdateSpec(attachment.Table, attachment.Columns, sqlgraph.NewFieldSpec(attachment.FieldID, field.TypeUUID))
id, ok := auo.mutation.ID()
if !ok {
return nil, &ValidationError{Name: "id", err: errors.New(`ent: missing "Attachment.id" for update`)}
@@ -405,7 +494,10 @@ func (auo *AttachmentUpdateOne) sqlSave(ctx context.Context) (_node *Attachment,
Columns: []string{attachment.ItemColumn},
Bidi: false,
Target: &sqlgraph.EdgeTarget{
IDSpec: sqlgraph.NewFieldSpec(item.FieldID, field.TypeUUID),
IDSpec: &sqlgraph.FieldSpec{
Type: field.TypeUUID,
Column: item.FieldID,
},
},
}
_spec.Edges.Clear = append(_spec.Edges.Clear, edge)
@@ -418,7 +510,10 @@ func (auo *AttachmentUpdateOne) sqlSave(ctx context.Context) (_node *Attachment,
Columns: []string{attachment.ItemColumn},
Bidi: false,
Target: &sqlgraph.EdgeTarget{
IDSpec: sqlgraph.NewFieldSpec(item.FieldID, field.TypeUUID),
IDSpec: &sqlgraph.FieldSpec{
Type: field.TypeUUID,
Column: item.FieldID,
},
},
}
for _, k := range nodes {
@@ -434,7 +529,10 @@ func (auo *AttachmentUpdateOne) sqlSave(ctx context.Context) (_node *Attachment,
Columns: []string{attachment.DocumentColumn},
Bidi: false,
Target: &sqlgraph.EdgeTarget{
IDSpec: sqlgraph.NewFieldSpec(document.FieldID, field.TypeUUID),
IDSpec: &sqlgraph.FieldSpec{
Type: field.TypeUUID,
Column: document.FieldID,
},
},
}
_spec.Edges.Clear = append(_spec.Edges.Clear, edge)
@@ -447,7 +545,10 @@ func (auo *AttachmentUpdateOne) sqlSave(ctx context.Context) (_node *Attachment,
Columns: []string{attachment.DocumentColumn},
Bidi: false,
Target: &sqlgraph.EdgeTarget{
IDSpec: sqlgraph.NewFieldSpec(document.FieldID, field.TypeUUID),
IDSpec: &sqlgraph.FieldSpec{
Type: field.TypeUUID,
Column: document.FieldID,
},
},
}
for _, k := range nodes {
@@ -466,6 +567,5 @@ func (auo *AttachmentUpdateOne) sqlSave(ctx context.Context) (_node *Attachment,
}
return nil, err
}
auo.mutation.done = true
return _node, nil
}

View File

@@ -99,14 +99,14 @@ func (ar *AuthRoles) assignValues(columns []string, values []any) error {
// QueryToken queries the "token" edge of the AuthRoles entity.
func (ar *AuthRoles) QueryToken() *AuthTokensQuery {
return NewAuthRolesClient(ar.config).QueryToken(ar)
return (&AuthRolesClient{config: ar.config}).QueryToken(ar)
}
// Update returns a builder for updating this AuthRoles.
// Note that you need to call AuthRoles.Unwrap() before calling this method if this AuthRoles
// was returned from a transaction, and the transaction was committed or rolled back.
func (ar *AuthRoles) Update() *AuthRolesUpdateOne {
return NewAuthRolesClient(ar.config).UpdateOne(ar)
return (&AuthRolesClient{config: ar.config}).UpdateOne(ar)
}
// Unwrap unwraps the AuthRoles entity that was returned from a transaction after it was closed,
@@ -133,3 +133,9 @@ func (ar *AuthRoles) String() string {
// AuthRolesSlice is a parsable slice of AuthRoles.
type AuthRolesSlice []*AuthRoles
func (ar AuthRolesSlice) config(cfg config) {
for _i := range ar {
ar[_i].config = cfg
}
}

View File

@@ -10,67 +10,109 @@ import (
// ID filters vertices based on their ID field.
func ID(id int) predicate.AuthRoles {
return predicate.AuthRoles(sql.FieldEQ(FieldID, id))
return predicate.AuthRoles(func(s *sql.Selector) {
s.Where(sql.EQ(s.C(FieldID), id))
})
}
// IDEQ applies the EQ predicate on the ID field.
func IDEQ(id int) predicate.AuthRoles {
return predicate.AuthRoles(sql.FieldEQ(FieldID, id))
return predicate.AuthRoles(func(s *sql.Selector) {
s.Where(sql.EQ(s.C(FieldID), id))
})
}
// IDNEQ applies the NEQ predicate on the ID field.
func IDNEQ(id int) predicate.AuthRoles {
return predicate.AuthRoles(sql.FieldNEQ(FieldID, id))
return predicate.AuthRoles(func(s *sql.Selector) {
s.Where(sql.NEQ(s.C(FieldID), id))
})
}
// IDIn applies the In predicate on the ID field.
func IDIn(ids ...int) predicate.AuthRoles {
return predicate.AuthRoles(sql.FieldIn(FieldID, ids...))
return predicate.AuthRoles(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 ...int) predicate.AuthRoles {
return predicate.AuthRoles(sql.FieldNotIn(FieldID, ids...))
return predicate.AuthRoles(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 int) predicate.AuthRoles {
return predicate.AuthRoles(sql.FieldGT(FieldID, id))
return predicate.AuthRoles(func(s *sql.Selector) {
s.Where(sql.GT(s.C(FieldID), id))
})
}
// IDGTE applies the GTE predicate on the ID field.
func IDGTE(id int) predicate.AuthRoles {
return predicate.AuthRoles(sql.FieldGTE(FieldID, id))
return predicate.AuthRoles(func(s *sql.Selector) {
s.Where(sql.GTE(s.C(FieldID), id))
})
}
// IDLT applies the LT predicate on the ID field.
func IDLT(id int) predicate.AuthRoles {
return predicate.AuthRoles(sql.FieldLT(FieldID, id))
return predicate.AuthRoles(func(s *sql.Selector) {
s.Where(sql.LT(s.C(FieldID), id))
})
}
// IDLTE applies the LTE predicate on the ID field.
func IDLTE(id int) predicate.AuthRoles {
return predicate.AuthRoles(sql.FieldLTE(FieldID, id))
return predicate.AuthRoles(func(s *sql.Selector) {
s.Where(sql.LTE(s.C(FieldID), id))
})
}
// RoleEQ applies the EQ predicate on the "role" field.
func RoleEQ(v Role) predicate.AuthRoles {
return predicate.AuthRoles(sql.FieldEQ(FieldRole, v))
return predicate.AuthRoles(func(s *sql.Selector) {
s.Where(sql.EQ(s.C(FieldRole), v))
})
}
// RoleNEQ applies the NEQ predicate on the "role" field.
func RoleNEQ(v Role) predicate.AuthRoles {
return predicate.AuthRoles(sql.FieldNEQ(FieldRole, v))
return predicate.AuthRoles(func(s *sql.Selector) {
s.Where(sql.NEQ(s.C(FieldRole), v))
})
}
// RoleIn applies the In predicate on the "role" field.
func RoleIn(vs ...Role) predicate.AuthRoles {
return predicate.AuthRoles(sql.FieldIn(FieldRole, vs...))
v := make([]any, len(vs))
for i := range v {
v[i] = vs[i]
}
return predicate.AuthRoles(func(s *sql.Selector) {
s.Where(sql.In(s.C(FieldRole), v...))
})
}
// RoleNotIn applies the NotIn predicate on the "role" field.
func RoleNotIn(vs ...Role) predicate.AuthRoles {
return predicate.AuthRoles(sql.FieldNotIn(FieldRole, vs...))
v := make([]any, len(vs))
for i := range v {
v[i] = vs[i]
}
return predicate.AuthRoles(func(s *sql.Selector) {
s.Where(sql.NotIn(s.C(FieldRole), v...))
})
}
// HasToken applies the HasEdge predicate on the "token" edge.
@@ -78,6 +120,7 @@ func HasToken() predicate.AuthRoles {
return predicate.AuthRoles(func(s *sql.Selector) {
step := sqlgraph.NewStep(
sqlgraph.From(Table, FieldID),
sqlgraph.To(TokenTable, FieldID),
sqlgraph.Edge(sqlgraph.O2O, true, TokenTable, TokenColumn),
)
sqlgraph.HasNeighbors(s, step)

View File

@@ -61,8 +61,50 @@ func (arc *AuthRolesCreate) Mutation() *AuthRolesMutation {
// Save creates the AuthRoles in the database.
func (arc *AuthRolesCreate) Save(ctx context.Context) (*AuthRoles, error) {
var (
err error
node *AuthRoles
)
arc.defaults()
return withHooks[*AuthRoles, AuthRolesMutation](ctx, arc.sqlSave, arc.mutation, arc.hooks)
if len(arc.hooks) == 0 {
if err = arc.check(); err != nil {
return nil, err
}
node, err = arc.sqlSave(ctx)
} else {
var mut Mutator = MutateFunc(func(ctx context.Context, m Mutation) (Value, error) {
mutation, ok := m.(*AuthRolesMutation)
if !ok {
return nil, fmt.Errorf("unexpected mutation type %T", m)
}
if err = arc.check(); err != nil {
return nil, err
}
arc.mutation = mutation
if node, err = arc.sqlSave(ctx); err != nil {
return nil, err
}
mutation.id = &node.ID
mutation.done = true
return node, err
})
for i := len(arc.hooks) - 1; i >= 0; i-- {
if arc.hooks[i] == nil {
return nil, fmt.Errorf("ent: uninitialized hook (forgotten import ent/runtime?)")
}
mut = arc.hooks[i](mut)
}
v, err := mut.Mutate(ctx, arc.mutation)
if err != nil {
return nil, err
}
nv, ok := v.(*AuthRoles)
if !ok {
return nil, fmt.Errorf("unexpected node type %T returned from AuthRolesMutation", v)
}
node = nv
}
return node, err
}
// SaveX calls Save and panics if Save returns an error.
@@ -109,9 +151,6 @@ func (arc *AuthRolesCreate) check() error {
}
func (arc *AuthRolesCreate) sqlSave(ctx context.Context) (*AuthRoles, error) {
if err := arc.check(); err != nil {
return nil, err
}
_node, _spec := arc.createSpec()
if err := sqlgraph.CreateNode(ctx, arc.driver, _spec); err != nil {
if sqlgraph.IsConstraintError(err) {
@@ -121,15 +160,19 @@ func (arc *AuthRolesCreate) sqlSave(ctx context.Context) (*AuthRoles, error) {
}
id := _spec.ID.Value.(int64)
_node.ID = int(id)
arc.mutation.id = &_node.ID
arc.mutation.done = true
return _node, nil
}
func (arc *AuthRolesCreate) createSpec() (*AuthRoles, *sqlgraph.CreateSpec) {
var (
_node = &AuthRoles{config: arc.config}
_spec = sqlgraph.NewCreateSpec(authroles.Table, sqlgraph.NewFieldSpec(authroles.FieldID, field.TypeInt))
_spec = &sqlgraph.CreateSpec{
Table: authroles.Table,
ID: &sqlgraph.FieldSpec{
Type: field.TypeInt,
Column: authroles.FieldID,
},
}
)
if value, ok := arc.mutation.Role(); ok {
_spec.SetField(authroles.FieldRole, field.TypeEnum, value)
@@ -143,7 +186,10 @@ func (arc *AuthRolesCreate) createSpec() (*AuthRoles, *sqlgraph.CreateSpec) {
Columns: []string{authroles.TokenColumn},
Bidi: false,
Target: &sqlgraph.EdgeTarget{
IDSpec: sqlgraph.NewFieldSpec(authtokens.FieldID, field.TypeUUID),
IDSpec: &sqlgraph.FieldSpec{
Type: field.TypeUUID,
Column: authtokens.FieldID,
},
},
}
for _, k := range nodes {

View File

@@ -4,6 +4,7 @@ package ent
import (
"context"
"fmt"
"entgo.io/ent/dialect/sql"
"entgo.io/ent/dialect/sql/sqlgraph"
@@ -27,7 +28,34 @@ func (ard *AuthRolesDelete) Where(ps ...predicate.AuthRoles) *AuthRolesDelete {
// Exec executes the deletion query and returns how many vertices were deleted.
func (ard *AuthRolesDelete) Exec(ctx context.Context) (int, error) {
return withHooks[int, AuthRolesMutation](ctx, ard.sqlExec, ard.mutation, ard.hooks)
var (
err error
affected int
)
if len(ard.hooks) == 0 {
affected, err = ard.sqlExec(ctx)
} else {
var mut Mutator = MutateFunc(func(ctx context.Context, m Mutation) (Value, error) {
mutation, ok := m.(*AuthRolesMutation)
if !ok {
return nil, fmt.Errorf("unexpected mutation type %T", m)
}
ard.mutation = mutation
affected, err = ard.sqlExec(ctx)
mutation.done = true
return affected, err
})
for i := len(ard.hooks) - 1; i >= 0; i-- {
if ard.hooks[i] == nil {
return 0, fmt.Errorf("ent: uninitialized hook (forgotten import ent/runtime?)")
}
mut = ard.hooks[i](mut)
}
if _, err := mut.Mutate(ctx, ard.mutation); err != nil {
return 0, err
}
}
return affected, err
}
// ExecX is like Exec, but panics if an error occurs.
@@ -40,7 +68,15 @@ func (ard *AuthRolesDelete) ExecX(ctx context.Context) int {
}
func (ard *AuthRolesDelete) sqlExec(ctx context.Context) (int, error) {
_spec := sqlgraph.NewDeleteSpec(authroles.Table, sqlgraph.NewFieldSpec(authroles.FieldID, field.TypeInt))
_spec := &sqlgraph.DeleteSpec{
Node: &sqlgraph.NodeSpec{
Table: authroles.Table,
ID: &sqlgraph.FieldSpec{
Type: field.TypeInt,
Column: authroles.FieldID,
},
},
}
if ps := ard.mutation.predicates; len(ps) > 0 {
_spec.Predicate = func(selector *sql.Selector) {
for i := range ps {
@@ -52,7 +88,6 @@ func (ard *AuthRolesDelete) sqlExec(ctx context.Context) (int, error) {
if err != nil && sqlgraph.IsConstraintError(err) {
err = &ConstraintError{msg: err.Error(), wrap: err}
}
ard.mutation.done = true
return affected, err
}
@@ -61,12 +96,6 @@ type AuthRolesDeleteOne struct {
ard *AuthRolesDelete
}
// Where appends a list predicates to the AuthRolesDelete builder.
func (ardo *AuthRolesDeleteOne) Where(ps ...predicate.AuthRoles) *AuthRolesDeleteOne {
ardo.ard.mutation.Where(ps...)
return ardo
}
// Exec executes the deletion query.
func (ardo *AuthRolesDeleteOne) Exec(ctx context.Context) error {
n, err := ardo.ard.Exec(ctx)
@@ -82,7 +111,5 @@ func (ardo *AuthRolesDeleteOne) Exec(ctx context.Context) error {
// ExecX is like Exec, but panics if an error occurs.
func (ardo *AuthRolesDeleteOne) ExecX(ctx context.Context) {
if err := ardo.Exec(ctx); err != nil {
panic(err)
}
ardo.ard.ExecX(ctx)
}

View File

@@ -19,9 +19,11 @@ import (
// AuthRolesQuery is the builder for querying AuthRoles entities.
type AuthRolesQuery struct {
config
ctx *QueryContext
limit *int
offset *int
unique *bool
order []OrderFunc
inters []Interceptor
fields []string
predicates []predicate.AuthRoles
withToken *AuthTokensQuery
withFKs bool
@@ -36,26 +38,26 @@ func (arq *AuthRolesQuery) Where(ps ...predicate.AuthRoles) *AuthRolesQuery {
return arq
}
// Limit the number of records to be returned by this query.
// Limit adds a limit step to the query.
func (arq *AuthRolesQuery) Limit(limit int) *AuthRolesQuery {
arq.ctx.Limit = &limit
arq.limit = &limit
return arq
}
// Offset to start from.
// Offset adds an offset step to the query.
func (arq *AuthRolesQuery) Offset(offset int) *AuthRolesQuery {
arq.ctx.Offset = &offset
arq.offset = &offset
return arq
}
// 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 (arq *AuthRolesQuery) Unique(unique bool) *AuthRolesQuery {
arq.ctx.Unique = &unique
arq.unique = &unique
return arq
}
// Order specifies how the records should be ordered.
// Order adds an order step to the query.
func (arq *AuthRolesQuery) Order(o ...OrderFunc) *AuthRolesQuery {
arq.order = append(arq.order, o...)
return arq
@@ -63,7 +65,7 @@ func (arq *AuthRolesQuery) Order(o ...OrderFunc) *AuthRolesQuery {
// QueryToken chains the current query on the "token" edge.
func (arq *AuthRolesQuery) QueryToken() *AuthTokensQuery {
query := (&AuthTokensClient{config: arq.config}).Query()
query := &AuthTokensQuery{config: arq.config}
query.path = func(ctx context.Context) (fromU *sql.Selector, err error) {
if err := arq.prepareQuery(ctx); err != nil {
return nil, err
@@ -86,7 +88,7 @@ func (arq *AuthRolesQuery) QueryToken() *AuthTokensQuery {
// First returns the first AuthRoles entity from the query.
// Returns a *NotFoundError when no AuthRoles was found.
func (arq *AuthRolesQuery) First(ctx context.Context) (*AuthRoles, error) {
nodes, err := arq.Limit(1).All(setContextOp(ctx, arq.ctx, "First"))
nodes, err := arq.Limit(1).All(ctx)
if err != nil {
return nil, err
}
@@ -109,7 +111,7 @@ func (arq *AuthRolesQuery) FirstX(ctx context.Context) *AuthRoles {
// Returns a *NotFoundError when no AuthRoles ID was found.
func (arq *AuthRolesQuery) FirstID(ctx context.Context) (id int, err error) {
var ids []int
if ids, err = arq.Limit(1).IDs(setContextOp(ctx, arq.ctx, "FirstID")); err != nil {
if ids, err = arq.Limit(1).IDs(ctx); err != nil {
return
}
if len(ids) == 0 {
@@ -132,7 +134,7 @@ func (arq *AuthRolesQuery) FirstIDX(ctx context.Context) int {
// Returns a *NotSingularError when more than one AuthRoles entity is found.
// Returns a *NotFoundError when no AuthRoles entities are found.
func (arq *AuthRolesQuery) Only(ctx context.Context) (*AuthRoles, error) {
nodes, err := arq.Limit(2).All(setContextOp(ctx, arq.ctx, "Only"))
nodes, err := arq.Limit(2).All(ctx)
if err != nil {
return nil, err
}
@@ -160,7 +162,7 @@ func (arq *AuthRolesQuery) OnlyX(ctx context.Context) *AuthRoles {
// Returns a *NotFoundError when no entities are found.
func (arq *AuthRolesQuery) OnlyID(ctx context.Context) (id int, err error) {
var ids []int
if ids, err = arq.Limit(2).IDs(setContextOp(ctx, arq.ctx, "OnlyID")); err != nil {
if ids, err = arq.Limit(2).IDs(ctx); err != nil {
return
}
switch len(ids) {
@@ -185,12 +187,10 @@ func (arq *AuthRolesQuery) OnlyIDX(ctx context.Context) int {
// All executes the query and returns a list of AuthRolesSlice.
func (arq *AuthRolesQuery) All(ctx context.Context) ([]*AuthRoles, error) {
ctx = setContextOp(ctx, arq.ctx, "All")
if err := arq.prepareQuery(ctx); err != nil {
return nil, err
}
qr := querierAll[[]*AuthRoles, *AuthRolesQuery]()
return withInterceptors[[]*AuthRoles](ctx, arq, qr, arq.inters)
return arq.sqlAll(ctx)
}
// AllX is like All, but panics if an error occurs.
@@ -203,12 +203,9 @@ func (arq *AuthRolesQuery) AllX(ctx context.Context) []*AuthRoles {
}
// IDs executes the query and returns a list of AuthRoles IDs.
func (arq *AuthRolesQuery) IDs(ctx context.Context) (ids []int, err error) {
if arq.ctx.Unique == nil && arq.path != nil {
arq.Unique(true)
}
ctx = setContextOp(ctx, arq.ctx, "IDs")
if err = arq.Select(authroles.FieldID).Scan(ctx, &ids); err != nil {
func (arq *AuthRolesQuery) IDs(ctx context.Context) ([]int, error) {
var ids []int
if err := arq.Select(authroles.FieldID).Scan(ctx, &ids); err != nil {
return nil, err
}
return ids, nil
@@ -225,11 +222,10 @@ func (arq *AuthRolesQuery) IDsX(ctx context.Context) []int {
// Count returns the count of the given query.
func (arq *AuthRolesQuery) Count(ctx context.Context) (int, error) {
ctx = setContextOp(ctx, arq.ctx, "Count")
if err := arq.prepareQuery(ctx); err != nil {
return 0, err
}
return withInterceptors[int](ctx, arq, querierCount[*AuthRolesQuery](), arq.inters)
return arq.sqlCount(ctx)
}
// CountX is like Count, but panics if an error occurs.
@@ -243,15 +239,10 @@ func (arq *AuthRolesQuery) CountX(ctx context.Context) int {
// Exist returns true if the query has elements in the graph.
func (arq *AuthRolesQuery) Exist(ctx context.Context) (bool, error) {
ctx = setContextOp(ctx, arq.ctx, "Exist")
switch _, err := arq.FirstID(ctx); {
case IsNotFound(err):
return false, nil
case err != nil:
return false, fmt.Errorf("ent: check existence: %w", err)
default:
return true, nil
if err := arq.prepareQuery(ctx); err != nil {
return false, err
}
return arq.sqlExist(ctx)
}
// ExistX is like Exist, but panics if an error occurs.
@@ -271,21 +262,22 @@ func (arq *AuthRolesQuery) Clone() *AuthRolesQuery {
}
return &AuthRolesQuery{
config: arq.config,
ctx: arq.ctx.Clone(),
limit: arq.limit,
offset: arq.offset,
order: append([]OrderFunc{}, arq.order...),
inters: append([]Interceptor{}, arq.inters...),
predicates: append([]predicate.AuthRoles{}, arq.predicates...),
withToken: arq.withToken.Clone(),
// clone intermediate query.
sql: arq.sql.Clone(),
path: arq.path,
sql: arq.sql.Clone(),
path: arq.path,
unique: arq.unique,
}
}
// WithToken tells the query-builder to eager-load the nodes that are connected to
// the "token" edge. The optional arguments are used to configure the query builder of the edge.
func (arq *AuthRolesQuery) WithToken(opts ...func(*AuthTokensQuery)) *AuthRolesQuery {
query := (&AuthTokensClient{config: arq.config}).Query()
query := &AuthTokensQuery{config: arq.config}
for _, opt := range opts {
opt(query)
}
@@ -308,11 +300,16 @@ func (arq *AuthRolesQuery) WithToken(opts ...func(*AuthTokensQuery)) *AuthRolesQ
// Aggregate(ent.Count()).
// Scan(ctx, &v)
func (arq *AuthRolesQuery) GroupBy(field string, fields ...string) *AuthRolesGroupBy {
arq.ctx.Fields = append([]string{field}, fields...)
grbuild := &AuthRolesGroupBy{build: arq}
grbuild.flds = &arq.ctx.Fields
grbuild := &AuthRolesGroupBy{config: arq.config}
grbuild.fields = append([]string{field}, fields...)
grbuild.path = func(ctx context.Context) (prev *sql.Selector, err error) {
if err := arq.prepareQuery(ctx); err != nil {
return nil, err
}
return arq.sqlQuery(ctx), nil
}
grbuild.label = authroles.Label
grbuild.scan = grbuild.Scan
grbuild.flds, grbuild.scan = &grbuild.fields, grbuild.Scan
return grbuild
}
@@ -329,11 +326,11 @@ func (arq *AuthRolesQuery) GroupBy(field string, fields ...string) *AuthRolesGro
// Select(authroles.FieldRole).
// Scan(ctx, &v)
func (arq *AuthRolesQuery) Select(fields ...string) *AuthRolesSelect {
arq.ctx.Fields = append(arq.ctx.Fields, fields...)
sbuild := &AuthRolesSelect{AuthRolesQuery: arq}
sbuild.label = authroles.Label
sbuild.flds, sbuild.scan = &arq.ctx.Fields, sbuild.Scan
return sbuild
arq.fields = append(arq.fields, fields...)
selbuild := &AuthRolesSelect{AuthRolesQuery: arq}
selbuild.label = authroles.Label
selbuild.flds, selbuild.scan = &arq.fields, selbuild.Scan
return selbuild
}
// Aggregate returns a AuthRolesSelect configured with the given aggregations.
@@ -342,17 +339,7 @@ func (arq *AuthRolesQuery) Aggregate(fns ...AggregateFunc) *AuthRolesSelect {
}
func (arq *AuthRolesQuery) prepareQuery(ctx context.Context) error {
for _, inter := range arq.inters {
if inter == nil {
return fmt.Errorf("ent: uninitialized interceptor (forgotten import ent/runtime?)")
}
if trv, ok := inter.(Traverser); ok {
if err := trv.Traverse(ctx, arq); err != nil {
return err
}
}
}
for _, f := range arq.ctx.Fields {
for _, f := range arq.fields {
if !authroles.ValidColumn(f) {
return &ValidationError{Name: f, err: fmt.Errorf("ent: invalid field %q for query", f)}
}
@@ -422,9 +409,6 @@ func (arq *AuthRolesQuery) loadToken(ctx context.Context, query *AuthTokensQuery
}
nodeids[fk] = append(nodeids[fk], nodes[i])
}
if len(ids) == 0 {
return nil
}
query.Where(authtokens.IDIn(ids...))
neighbors, err := query.All(ctx)
if err != nil {
@@ -444,22 +428,41 @@ func (arq *AuthRolesQuery) loadToken(ctx context.Context, query *AuthTokensQuery
func (arq *AuthRolesQuery) sqlCount(ctx context.Context) (int, error) {
_spec := arq.querySpec()
_spec.Node.Columns = arq.ctx.Fields
if len(arq.ctx.Fields) > 0 {
_spec.Unique = arq.ctx.Unique != nil && *arq.ctx.Unique
_spec.Node.Columns = arq.fields
if len(arq.fields) > 0 {
_spec.Unique = arq.unique != nil && *arq.unique
}
return sqlgraph.CountNodes(ctx, arq.driver, _spec)
}
func (arq *AuthRolesQuery) querySpec() *sqlgraph.QuerySpec {
_spec := sqlgraph.NewQuerySpec(authroles.Table, authroles.Columns, sqlgraph.NewFieldSpec(authroles.FieldID, field.TypeInt))
_spec.From = arq.sql
if unique := arq.ctx.Unique; unique != nil {
_spec.Unique = *unique
} else if arq.path != nil {
_spec.Unique = true
func (arq *AuthRolesQuery) sqlExist(ctx context.Context) (bool, error) {
switch _, err := arq.FirstID(ctx); {
case IsNotFound(err):
return false, nil
case err != nil:
return false, fmt.Errorf("ent: check existence: %w", err)
default:
return true, nil
}
if fields := arq.ctx.Fields; len(fields) > 0 {
}
func (arq *AuthRolesQuery) querySpec() *sqlgraph.QuerySpec {
_spec := &sqlgraph.QuerySpec{
Node: &sqlgraph.NodeSpec{
Table: authroles.Table,
Columns: authroles.Columns,
ID: &sqlgraph.FieldSpec{
Type: field.TypeInt,
Column: authroles.FieldID,
},
},
From: arq.sql,
Unique: true,
}
if unique := arq.unique; unique != nil {
_spec.Unique = *unique
}
if fields := arq.fields; len(fields) > 0 {
_spec.Node.Columns = make([]string, 0, len(fields))
_spec.Node.Columns = append(_spec.Node.Columns, authroles.FieldID)
for i := range fields {
@@ -475,10 +478,10 @@ func (arq *AuthRolesQuery) querySpec() *sqlgraph.QuerySpec {
}
}
}
if limit := arq.ctx.Limit; limit != nil {
if limit := arq.limit; limit != nil {
_spec.Limit = *limit
}
if offset := arq.ctx.Offset; offset != nil {
if offset := arq.offset; offset != nil {
_spec.Offset = *offset
}
if ps := arq.order; len(ps) > 0 {
@@ -494,7 +497,7 @@ func (arq *AuthRolesQuery) querySpec() *sqlgraph.QuerySpec {
func (arq *AuthRolesQuery) sqlQuery(ctx context.Context) *sql.Selector {
builder := sql.Dialect(arq.driver.Dialect())
t1 := builder.Table(authroles.Table)
columns := arq.ctx.Fields
columns := arq.fields
if len(columns) == 0 {
columns = authroles.Columns
}
@@ -503,7 +506,7 @@ func (arq *AuthRolesQuery) sqlQuery(ctx context.Context) *sql.Selector {
selector = arq.sql
selector.Select(selector.Columns(columns...)...)
}
if arq.ctx.Unique != nil && *arq.ctx.Unique {
if arq.unique != nil && *arq.unique {
selector.Distinct()
}
for _, p := range arq.predicates {
@@ -512,12 +515,12 @@ func (arq *AuthRolesQuery) sqlQuery(ctx context.Context) *sql.Selector {
for _, p := range arq.order {
p(selector)
}
if offset := arq.ctx.Offset; offset != nil {
if offset := arq.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 := arq.ctx.Limit; limit != nil {
if limit := arq.limit; limit != nil {
selector.Limit(*limit)
}
return selector
@@ -525,8 +528,13 @@ func (arq *AuthRolesQuery) sqlQuery(ctx context.Context) *sql.Selector {
// AuthRolesGroupBy is the group-by builder for AuthRoles entities.
type AuthRolesGroupBy struct {
config
selector
build *AuthRolesQuery
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.
@@ -535,46 +543,58 @@ func (argb *AuthRolesGroupBy) Aggregate(fns ...AggregateFunc) *AuthRolesGroupBy
return argb
}
// Scan applies the selector query and scans the result into the given value.
// Scan applies the group-by query and scans the result into the given value.
func (argb *AuthRolesGroupBy) Scan(ctx context.Context, v any) error {
ctx = setContextOp(ctx, argb.build.ctx, "GroupBy")
if err := argb.build.prepareQuery(ctx); err != nil {
query, err := argb.path(ctx)
if err != nil {
return err
}
return scanWithInterceptors[*AuthRolesQuery, *AuthRolesGroupBy](ctx, argb.build, argb, argb.build.inters, v)
argb.sql = query
return argb.sqlScan(ctx, v)
}
func (argb *AuthRolesGroupBy) sqlScan(ctx context.Context, root *AuthRolesQuery, v any) error {
selector := root.sqlQuery(ctx).Select()
aggregation := make([]string, 0, len(argb.fns))
for _, fn := range argb.fns {
aggregation = append(aggregation, fn(selector))
}
if len(selector.SelectedColumns()) == 0 {
columns := make([]string, 0, len(*argb.flds)+len(argb.fns))
for _, f := range *argb.flds {
columns = append(columns, selector.C(f))
func (argb *AuthRolesGroupBy) sqlScan(ctx context.Context, v any) error {
for _, f := range argb.fields {
if !authroles.ValidColumn(f) {
return &ValidationError{Name: f, err: fmt.Errorf("invalid field %q for group-by", f)}
}
columns = append(columns, aggregation...)
selector.Select(columns...)
}
selector.GroupBy(selector.Columns(*argb.flds...)...)
selector := argb.sqlQuery()
if err := selector.Err(); err != nil {
return err
}
rows := &sql.Rows{}
query, args := selector.Query()
if err := argb.build.driver.Query(ctx, query, args, rows); err != nil {
if err := argb.driver.Query(ctx, query, args, rows); err != nil {
return err
}
defer rows.Close()
return sql.ScanSlice(rows, v)
}
func (argb *AuthRolesGroupBy) sqlQuery() *sql.Selector {
selector := argb.sql.Select()
aggregation := make([]string, 0, len(argb.fns))
for _, fn := range argb.fns {
aggregation = append(aggregation, fn(selector))
}
if len(selector.SelectedColumns()) == 0 {
columns := make([]string, 0, len(argb.fields)+len(argb.fns))
for _, f := range argb.fields {
columns = append(columns, selector.C(f))
}
columns = append(columns, aggregation...)
selector.Select(columns...)
}
return selector.GroupBy(selector.Columns(argb.fields...)...)
}
// AuthRolesSelect is the builder for selecting fields of AuthRoles entities.
type AuthRolesSelect struct {
*AuthRolesQuery
selector
// intermediate query (i.e. traversal path).
sql *sql.Selector
}
// Aggregate adds the given aggregation functions to the selector query.
@@ -585,27 +605,26 @@ func (ars *AuthRolesSelect) Aggregate(fns ...AggregateFunc) *AuthRolesSelect {
// Scan applies the selector query and scans the result into the given value.
func (ars *AuthRolesSelect) Scan(ctx context.Context, v any) error {
ctx = setContextOp(ctx, ars.ctx, "Select")
if err := ars.prepareQuery(ctx); err != nil {
return err
}
return scanWithInterceptors[*AuthRolesQuery, *AuthRolesSelect](ctx, ars.AuthRolesQuery, ars, ars.inters, v)
ars.sql = ars.AuthRolesQuery.sqlQuery(ctx)
return ars.sqlScan(ctx, v)
}
func (ars *AuthRolesSelect) sqlScan(ctx context.Context, root *AuthRolesQuery, v any) error {
selector := root.sqlQuery(ctx)
func (ars *AuthRolesSelect) sqlScan(ctx context.Context, v any) error {
aggregation := make([]string, 0, len(ars.fns))
for _, fn := range ars.fns {
aggregation = append(aggregation, fn(selector))
aggregation = append(aggregation, fn(ars.sql))
}
switch n := len(*ars.selector.flds); {
case n == 0 && len(aggregation) > 0:
selector.Select(aggregation...)
ars.sql.Select(aggregation...)
case n != 0 && len(aggregation) > 0:
selector.AppendSelect(aggregation...)
ars.sql.AppendSelect(aggregation...)
}
rows := &sql.Rows{}
query, args := selector.Query()
query, args := ars.sql.Query()
if err := ars.driver.Query(ctx, query, args, rows); err != nil {
return err
}

View File

@@ -75,7 +75,40 @@ func (aru *AuthRolesUpdate) ClearToken() *AuthRolesUpdate {
// Save executes the query and returns the number of nodes affected by the update operation.
func (aru *AuthRolesUpdate) Save(ctx context.Context) (int, error) {
return withHooks[int, AuthRolesMutation](ctx, aru.sqlSave, aru.mutation, aru.hooks)
var (
err error
affected int
)
if len(aru.hooks) == 0 {
if err = aru.check(); err != nil {
return 0, err
}
affected, err = aru.sqlSave(ctx)
} else {
var mut Mutator = MutateFunc(func(ctx context.Context, m Mutation) (Value, error) {
mutation, ok := m.(*AuthRolesMutation)
if !ok {
return nil, fmt.Errorf("unexpected mutation type %T", m)
}
if err = aru.check(); err != nil {
return 0, err
}
aru.mutation = mutation
affected, err = aru.sqlSave(ctx)
mutation.done = true
return affected, err
})
for i := len(aru.hooks) - 1; i >= 0; i-- {
if aru.hooks[i] == nil {
return 0, fmt.Errorf("ent: uninitialized hook (forgotten import ent/runtime?)")
}
mut = aru.hooks[i](mut)
}
if _, err := mut.Mutate(ctx, aru.mutation); err != nil {
return 0, err
}
}
return affected, err
}
// SaveX is like Save, but panics if an error occurs.
@@ -111,10 +144,16 @@ func (aru *AuthRolesUpdate) check() error {
}
func (aru *AuthRolesUpdate) sqlSave(ctx context.Context) (n int, err error) {
if err := aru.check(); err != nil {
return n, err
_spec := &sqlgraph.UpdateSpec{
Node: &sqlgraph.NodeSpec{
Table: authroles.Table,
Columns: authroles.Columns,
ID: &sqlgraph.FieldSpec{
Type: field.TypeInt,
Column: authroles.FieldID,
},
},
}
_spec := sqlgraph.NewUpdateSpec(authroles.Table, authroles.Columns, sqlgraph.NewFieldSpec(authroles.FieldID, field.TypeInt))
if ps := aru.mutation.predicates; len(ps) > 0 {
_spec.Predicate = func(selector *sql.Selector) {
for i := range ps {
@@ -133,7 +172,10 @@ func (aru *AuthRolesUpdate) sqlSave(ctx context.Context) (n int, err error) {
Columns: []string{authroles.TokenColumn},
Bidi: false,
Target: &sqlgraph.EdgeTarget{
IDSpec: sqlgraph.NewFieldSpec(authtokens.FieldID, field.TypeUUID),
IDSpec: &sqlgraph.FieldSpec{
Type: field.TypeUUID,
Column: authtokens.FieldID,
},
},
}
_spec.Edges.Clear = append(_spec.Edges.Clear, edge)
@@ -146,7 +188,10 @@ func (aru *AuthRolesUpdate) sqlSave(ctx context.Context) (n int, err error) {
Columns: []string{authroles.TokenColumn},
Bidi: false,
Target: &sqlgraph.EdgeTarget{
IDSpec: sqlgraph.NewFieldSpec(authtokens.FieldID, field.TypeUUID),
IDSpec: &sqlgraph.FieldSpec{
Type: field.TypeUUID,
Column: authtokens.FieldID,
},
},
}
for _, k := range nodes {
@@ -162,7 +207,6 @@ func (aru *AuthRolesUpdate) sqlSave(ctx context.Context) (n int, err error) {
}
return 0, err
}
aru.mutation.done = true
return n, nil
}
@@ -218,12 +262,6 @@ func (aruo *AuthRolesUpdateOne) ClearToken() *AuthRolesUpdateOne {
return aruo
}
// Where appends a list predicates to the AuthRolesUpdate builder.
func (aruo *AuthRolesUpdateOne) Where(ps ...predicate.AuthRoles) *AuthRolesUpdateOne {
aruo.mutation.Where(ps...)
return aruo
}
// Select allows selecting one or more fields (columns) of the returned entity.
// The default is selecting all fields defined in the entity schema.
func (aruo *AuthRolesUpdateOne) Select(field string, fields ...string) *AuthRolesUpdateOne {
@@ -233,7 +271,46 @@ func (aruo *AuthRolesUpdateOne) Select(field string, fields ...string) *AuthRole
// Save executes the query and returns the updated AuthRoles entity.
func (aruo *AuthRolesUpdateOne) Save(ctx context.Context) (*AuthRoles, error) {
return withHooks[*AuthRoles, AuthRolesMutation](ctx, aruo.sqlSave, aruo.mutation, aruo.hooks)
var (
err error
node *AuthRoles
)
if len(aruo.hooks) == 0 {
if err = aruo.check(); err != nil {
return nil, err
}
node, err = aruo.sqlSave(ctx)
} else {
var mut Mutator = MutateFunc(func(ctx context.Context, m Mutation) (Value, error) {
mutation, ok := m.(*AuthRolesMutation)
if !ok {
return nil, fmt.Errorf("unexpected mutation type %T", m)
}
if err = aruo.check(); err != nil {
return nil, err
}
aruo.mutation = mutation
node, err = aruo.sqlSave(ctx)
mutation.done = true
return node, err
})
for i := len(aruo.hooks) - 1; i >= 0; i-- {
if aruo.hooks[i] == nil {
return nil, fmt.Errorf("ent: uninitialized hook (forgotten import ent/runtime?)")
}
mut = aruo.hooks[i](mut)
}
v, err := mut.Mutate(ctx, aruo.mutation)
if err != nil {
return nil, err
}
nv, ok := v.(*AuthRoles)
if !ok {
return nil, fmt.Errorf("unexpected node type %T returned from AuthRolesMutation", v)
}
node = nv
}
return node, err
}
// SaveX is like Save, but panics if an error occurs.
@@ -269,10 +346,16 @@ func (aruo *AuthRolesUpdateOne) check() error {
}
func (aruo *AuthRolesUpdateOne) sqlSave(ctx context.Context) (_node *AuthRoles, err error) {
if err := aruo.check(); err != nil {
return _node, err
_spec := &sqlgraph.UpdateSpec{
Node: &sqlgraph.NodeSpec{
Table: authroles.Table,
Columns: authroles.Columns,
ID: &sqlgraph.FieldSpec{
Type: field.TypeInt,
Column: authroles.FieldID,
},
},
}
_spec := sqlgraph.NewUpdateSpec(authroles.Table, authroles.Columns, sqlgraph.NewFieldSpec(authroles.FieldID, field.TypeInt))
id, ok := aruo.mutation.ID()
if !ok {
return nil, &ValidationError{Name: "id", err: errors.New(`ent: missing "AuthRoles.id" for update`)}
@@ -308,7 +391,10 @@ func (aruo *AuthRolesUpdateOne) sqlSave(ctx context.Context) (_node *AuthRoles,
Columns: []string{authroles.TokenColumn},
Bidi: false,
Target: &sqlgraph.EdgeTarget{
IDSpec: sqlgraph.NewFieldSpec(authtokens.FieldID, field.TypeUUID),
IDSpec: &sqlgraph.FieldSpec{
Type: field.TypeUUID,
Column: authtokens.FieldID,
},
},
}
_spec.Edges.Clear = append(_spec.Edges.Clear, edge)
@@ -321,7 +407,10 @@ func (aruo *AuthRolesUpdateOne) sqlSave(ctx context.Context) (_node *AuthRoles,
Columns: []string{authroles.TokenColumn},
Bidi: false,
Target: &sqlgraph.EdgeTarget{
IDSpec: sqlgraph.NewFieldSpec(authtokens.FieldID, field.TypeUUID),
IDSpec: &sqlgraph.FieldSpec{
Type: field.TypeUUID,
Column: authtokens.FieldID,
},
},
}
for _, k := range nodes {
@@ -340,6 +429,5 @@ func (aruo *AuthRolesUpdateOne) sqlSave(ctx context.Context) (_node *AuthRoles,
}
return nil, err
}
aruo.mutation.done = true
return _node, nil
}

View File

@@ -142,19 +142,19 @@ func (at *AuthTokens) assignValues(columns []string, values []any) error {
// QueryUser queries the "user" edge of the AuthTokens entity.
func (at *AuthTokens) QueryUser() *UserQuery {
return NewAuthTokensClient(at.config).QueryUser(at)
return (&AuthTokensClient{config: at.config}).QueryUser(at)
}
// QueryRoles queries the "roles" edge of the AuthTokens entity.
func (at *AuthTokens) QueryRoles() *AuthRolesQuery {
return NewAuthTokensClient(at.config).QueryRoles(at)
return (&AuthTokensClient{config: at.config}).QueryRoles(at)
}
// Update returns a builder for updating this AuthTokens.
// Note that you need to call AuthTokens.Unwrap() before calling this method if this AuthTokens
// was returned from a transaction, and the transaction was committed or rolled back.
func (at *AuthTokens) Update() *AuthTokensUpdateOne {
return NewAuthTokensClient(at.config).UpdateOne(at)
return (&AuthTokensClient{config: at.config}).UpdateOne(at)
}
// Unwrap unwraps the AuthTokens entity that was returned from a transaction after it was closed,
@@ -190,3 +190,9 @@ func (at *AuthTokens) String() string {
// AuthTokensSlice is a parsable slice of AuthTokens.
type AuthTokensSlice []*AuthTokens
func (at AuthTokensSlice) config(cfg config) {
for _i := range at {
at[_i].config = cfg
}
}

View File

@@ -13,227 +13,357 @@ import (
// ID filters vertices based on their ID field.
func ID(id uuid.UUID) predicate.AuthTokens {
return predicate.AuthTokens(sql.FieldEQ(FieldID, id))
return predicate.AuthTokens(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.AuthTokens {
return predicate.AuthTokens(sql.FieldEQ(FieldID, id))
return predicate.AuthTokens(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.AuthTokens {
return predicate.AuthTokens(sql.FieldNEQ(FieldID, id))
return predicate.AuthTokens(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.AuthTokens {
return predicate.AuthTokens(sql.FieldIn(FieldID, ids...))
return predicate.AuthTokens(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.AuthTokens {
return predicate.AuthTokens(sql.FieldNotIn(FieldID, ids...))
return predicate.AuthTokens(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.AuthTokens {
return predicate.AuthTokens(sql.FieldGT(FieldID, id))
return predicate.AuthTokens(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.AuthTokens {
return predicate.AuthTokens(sql.FieldGTE(FieldID, id))
return predicate.AuthTokens(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.AuthTokens {
return predicate.AuthTokens(sql.FieldLT(FieldID, id))
return predicate.AuthTokens(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.AuthTokens {
return predicate.AuthTokens(sql.FieldLTE(FieldID, id))
return predicate.AuthTokens(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.AuthTokens {
return predicate.AuthTokens(sql.FieldEQ(FieldCreatedAt, v))
return predicate.AuthTokens(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.AuthTokens {
return predicate.AuthTokens(sql.FieldEQ(FieldUpdatedAt, v))
return predicate.AuthTokens(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.AuthTokens {
return predicate.AuthTokens(sql.FieldEQ(FieldToken, v))
return predicate.AuthTokens(func(s *sql.Selector) {
s.Where(sql.EQ(s.C(FieldToken), v))
})
}
// ExpiresAt applies equality check predicate on the "expires_at" field. It's identical to ExpiresAtEQ.
func ExpiresAt(v time.Time) predicate.AuthTokens {
return predicate.AuthTokens(sql.FieldEQ(FieldExpiresAt, v))
return predicate.AuthTokens(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.AuthTokens {
return predicate.AuthTokens(sql.FieldEQ(FieldCreatedAt, v))
return predicate.AuthTokens(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.AuthTokens {
return predicate.AuthTokens(sql.FieldNEQ(FieldCreatedAt, v))
return predicate.AuthTokens(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.AuthTokens {
return predicate.AuthTokens(sql.FieldIn(FieldCreatedAt, vs...))
v := make([]any, len(vs))
for i := range v {
v[i] = vs[i]
}
return predicate.AuthTokens(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.AuthTokens {
return predicate.AuthTokens(sql.FieldNotIn(FieldCreatedAt, vs...))
v := make([]any, len(vs))
for i := range v {
v[i] = vs[i]
}
return predicate.AuthTokens(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.AuthTokens {
return predicate.AuthTokens(sql.FieldGT(FieldCreatedAt, v))
return predicate.AuthTokens(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.AuthTokens {
return predicate.AuthTokens(sql.FieldGTE(FieldCreatedAt, v))
return predicate.AuthTokens(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.AuthTokens {
return predicate.AuthTokens(sql.FieldLT(FieldCreatedAt, v))
return predicate.AuthTokens(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.AuthTokens {
return predicate.AuthTokens(sql.FieldLTE(FieldCreatedAt, v))
return predicate.AuthTokens(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.AuthTokens {
return predicate.AuthTokens(sql.FieldEQ(FieldUpdatedAt, v))
return predicate.AuthTokens(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.AuthTokens {
return predicate.AuthTokens(sql.FieldNEQ(FieldUpdatedAt, v))
return predicate.AuthTokens(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.AuthTokens {
return predicate.AuthTokens(sql.FieldIn(FieldUpdatedAt, vs...))
v := make([]any, len(vs))
for i := range v {
v[i] = vs[i]
}
return predicate.AuthTokens(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.AuthTokens {
return predicate.AuthTokens(sql.FieldNotIn(FieldUpdatedAt, vs...))
v := make([]any, len(vs))
for i := range v {
v[i] = vs[i]
}
return predicate.AuthTokens(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.AuthTokens {
return predicate.AuthTokens(sql.FieldGT(FieldUpdatedAt, v))
return predicate.AuthTokens(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.AuthTokens {
return predicate.AuthTokens(sql.FieldGTE(FieldUpdatedAt, v))
return predicate.AuthTokens(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.AuthTokens {
return predicate.AuthTokens(sql.FieldLT(FieldUpdatedAt, v))
return predicate.AuthTokens(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.AuthTokens {
return predicate.AuthTokens(sql.FieldLTE(FieldUpdatedAt, v))
return predicate.AuthTokens(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.AuthTokens {
return predicate.AuthTokens(sql.FieldEQ(FieldToken, v))
return predicate.AuthTokens(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.AuthTokens {
return predicate.AuthTokens(sql.FieldNEQ(FieldToken, v))
return predicate.AuthTokens(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.AuthTokens {
return predicate.AuthTokens(sql.FieldIn(FieldToken, vs...))
v := make([]any, len(vs))
for i := range v {
v[i] = vs[i]
}
return predicate.AuthTokens(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.AuthTokens {
return predicate.AuthTokens(sql.FieldNotIn(FieldToken, vs...))
v := make([]any, len(vs))
for i := range v {
v[i] = vs[i]
}
return predicate.AuthTokens(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.AuthTokens {
return predicate.AuthTokens(sql.FieldGT(FieldToken, v))
return predicate.AuthTokens(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.AuthTokens {
return predicate.AuthTokens(sql.FieldGTE(FieldToken, v))
return predicate.AuthTokens(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.AuthTokens {
return predicate.AuthTokens(sql.FieldLT(FieldToken, v))
return predicate.AuthTokens(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.AuthTokens {
return predicate.AuthTokens(sql.FieldLTE(FieldToken, v))
return predicate.AuthTokens(func(s *sql.Selector) {
s.Where(sql.LTE(s.C(FieldToken), v))
})
}
// ExpiresAtEQ applies the EQ predicate on the "expires_at" field.
func ExpiresAtEQ(v time.Time) predicate.AuthTokens {
return predicate.AuthTokens(sql.FieldEQ(FieldExpiresAt, v))
return predicate.AuthTokens(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.AuthTokens {
return predicate.AuthTokens(sql.FieldNEQ(FieldExpiresAt, v))
return predicate.AuthTokens(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.AuthTokens {
return predicate.AuthTokens(sql.FieldIn(FieldExpiresAt, vs...))
v := make([]any, len(vs))
for i := range v {
v[i] = vs[i]
}
return predicate.AuthTokens(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.AuthTokens {
return predicate.AuthTokens(sql.FieldNotIn(FieldExpiresAt, vs...))
v := make([]any, len(vs))
for i := range v {
v[i] = vs[i]
}
return predicate.AuthTokens(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.AuthTokens {
return predicate.AuthTokens(sql.FieldGT(FieldExpiresAt, v))
return predicate.AuthTokens(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.AuthTokens {
return predicate.AuthTokens(sql.FieldGTE(FieldExpiresAt, v))
return predicate.AuthTokens(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.AuthTokens {
return predicate.AuthTokens(sql.FieldLT(FieldExpiresAt, v))
return predicate.AuthTokens(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.AuthTokens {
return predicate.AuthTokens(sql.FieldLTE(FieldExpiresAt, v))
return predicate.AuthTokens(func(s *sql.Selector) {
s.Where(sql.LTE(s.C(FieldExpiresAt), v))
})
}
// HasUser applies the HasEdge predicate on the "user" edge.
@@ -241,6 +371,7 @@ func HasUser() predicate.AuthTokens {
return predicate.AuthTokens(func(s *sql.Selector) {
step := sqlgraph.NewStep(
sqlgraph.From(Table, FieldID),
sqlgraph.To(UserTable, FieldID),
sqlgraph.Edge(sqlgraph.M2O, true, UserTable, UserColumn),
)
sqlgraph.HasNeighbors(s, step)
@@ -268,6 +399,7 @@ func HasRoles() predicate.AuthTokens {
return predicate.AuthTokens(func(s *sql.Selector) {
step := sqlgraph.NewStep(
sqlgraph.From(Table, FieldID),
sqlgraph.To(RolesTable, FieldID),
sqlgraph.Edge(sqlgraph.O2O, false, RolesTable, RolesColumn),
)
sqlgraph.HasNeighbors(s, step)

View File

@@ -130,8 +130,50 @@ func (atc *AuthTokensCreate) Mutation() *AuthTokensMutation {
// Save creates the AuthTokens in the database.
func (atc *AuthTokensCreate) Save(ctx context.Context) (*AuthTokens, error) {
var (
err error
node *AuthTokens
)
atc.defaults()
return withHooks[*AuthTokens, AuthTokensMutation](ctx, atc.sqlSave, atc.mutation, atc.hooks)
if len(atc.hooks) == 0 {
if err = atc.check(); err != nil {
return nil, err
}
node, err = atc.sqlSave(ctx)
} else {
var mut Mutator = MutateFunc(func(ctx context.Context, m Mutation) (Value, error) {
mutation, ok := m.(*AuthTokensMutation)
if !ok {
return nil, fmt.Errorf("unexpected mutation type %T", m)
}
if err = atc.check(); err != nil {
return nil, err
}
atc.mutation = mutation
if node, err = atc.sqlSave(ctx); err != nil {
return nil, err
}
mutation.id = &node.ID
mutation.done = true
return node, err
})
for i := len(atc.hooks) - 1; i >= 0; i-- {
if atc.hooks[i] == nil {
return nil, fmt.Errorf("ent: uninitialized hook (forgotten import ent/runtime?)")
}
mut = atc.hooks[i](mut)
}
v, err := mut.Mutate(ctx, atc.mutation)
if err != nil {
return nil, err
}
nv, ok := v.(*AuthTokens)
if !ok {
return nil, fmt.Errorf("unexpected node type %T returned from AuthTokensMutation", v)
}
node = nv
}
return node, err
}
// SaveX calls Save and panics if Save returns an error.
@@ -194,9 +236,6 @@ func (atc *AuthTokensCreate) check() error {
}
func (atc *AuthTokensCreate) sqlSave(ctx context.Context) (*AuthTokens, error) {
if err := atc.check(); err != nil {
return nil, err
}
_node, _spec := atc.createSpec()
if err := sqlgraph.CreateNode(ctx, atc.driver, _spec); err != nil {
if sqlgraph.IsConstraintError(err) {
@@ -211,15 +250,19 @@ func (atc *AuthTokensCreate) sqlSave(ctx context.Context) (*AuthTokens, error) {
return nil, err
}
}
atc.mutation.id = &_node.ID
atc.mutation.done = true
return _node, nil
}
func (atc *AuthTokensCreate) createSpec() (*AuthTokens, *sqlgraph.CreateSpec) {
var (
_node = &AuthTokens{config: atc.config}
_spec = sqlgraph.NewCreateSpec(authtokens.Table, sqlgraph.NewFieldSpec(authtokens.FieldID, field.TypeUUID))
_spec = &sqlgraph.CreateSpec{
Table: authtokens.Table,
ID: &sqlgraph.FieldSpec{
Type: field.TypeUUID,
Column: authtokens.FieldID,
},
}
)
if id, ok := atc.mutation.ID(); ok {
_node.ID = id
@@ -249,7 +292,10 @@ func (atc *AuthTokensCreate) createSpec() (*AuthTokens, *sqlgraph.CreateSpec) {
Columns: []string{authtokens.UserColumn},
Bidi: false,
Target: &sqlgraph.EdgeTarget{
IDSpec: sqlgraph.NewFieldSpec(user.FieldID, field.TypeUUID),
IDSpec: &sqlgraph.FieldSpec{
Type: field.TypeUUID,
Column: user.FieldID,
},
},
}
for _, k := range nodes {
@@ -266,7 +312,10 @@ func (atc *AuthTokensCreate) createSpec() (*AuthTokens, *sqlgraph.CreateSpec) {
Columns: []string{authtokens.RolesColumn},
Bidi: false,
Target: &sqlgraph.EdgeTarget{
IDSpec: sqlgraph.NewFieldSpec(authroles.FieldID, field.TypeInt),
IDSpec: &sqlgraph.FieldSpec{
Type: field.TypeInt,
Column: authroles.FieldID,
},
},
}
for _, k := range nodes {

View File

@@ -4,6 +4,7 @@ package ent
import (
"context"
"fmt"
"entgo.io/ent/dialect/sql"
"entgo.io/ent/dialect/sql/sqlgraph"
@@ -27,7 +28,34 @@ func (atd *AuthTokensDelete) Where(ps ...predicate.AuthTokens) *AuthTokensDelete
// Exec executes the deletion query and returns how many vertices were deleted.
func (atd *AuthTokensDelete) Exec(ctx context.Context) (int, error) {
return withHooks[int, AuthTokensMutation](ctx, atd.sqlExec, atd.mutation, atd.hooks)
var (
err error
affected int
)
if len(atd.hooks) == 0 {
affected, err = atd.sqlExec(ctx)
} else {
var mut Mutator = MutateFunc(func(ctx context.Context, m Mutation) (Value, error) {
mutation, ok := m.(*AuthTokensMutation)
if !ok {
return nil, fmt.Errorf("unexpected mutation type %T", m)
}
atd.mutation = mutation
affected, err = atd.sqlExec(ctx)
mutation.done = true
return affected, err
})
for i := len(atd.hooks) - 1; i >= 0; i-- {
if atd.hooks[i] == nil {
return 0, fmt.Errorf("ent: uninitialized hook (forgotten import ent/runtime?)")
}
mut = atd.hooks[i](mut)
}
if _, err := mut.Mutate(ctx, atd.mutation); err != nil {
return 0, err
}
}
return affected, err
}
// ExecX is like Exec, but panics if an error occurs.
@@ -40,7 +68,15 @@ func (atd *AuthTokensDelete) ExecX(ctx context.Context) int {
}
func (atd *AuthTokensDelete) sqlExec(ctx context.Context) (int, error) {
_spec := sqlgraph.NewDeleteSpec(authtokens.Table, sqlgraph.NewFieldSpec(authtokens.FieldID, field.TypeUUID))
_spec := &sqlgraph.DeleteSpec{
Node: &sqlgraph.NodeSpec{
Table: authtokens.Table,
ID: &sqlgraph.FieldSpec{
Type: field.TypeUUID,
Column: authtokens.FieldID,
},
},
}
if ps := atd.mutation.predicates; len(ps) > 0 {
_spec.Predicate = func(selector *sql.Selector) {
for i := range ps {
@@ -52,7 +88,6 @@ func (atd *AuthTokensDelete) sqlExec(ctx context.Context) (int, error) {
if err != nil && sqlgraph.IsConstraintError(err) {
err = &ConstraintError{msg: err.Error(), wrap: err}
}
atd.mutation.done = true
return affected, err
}
@@ -61,12 +96,6 @@ type AuthTokensDeleteOne struct {
atd *AuthTokensDelete
}
// Where appends a list predicates to the AuthTokensDelete builder.
func (atdo *AuthTokensDeleteOne) Where(ps ...predicate.AuthTokens) *AuthTokensDeleteOne {
atdo.atd.mutation.Where(ps...)
return atdo
}
// Exec executes the deletion query.
func (atdo *AuthTokensDeleteOne) Exec(ctx context.Context) error {
n, err := atdo.atd.Exec(ctx)
@@ -82,7 +111,5 @@ func (atdo *AuthTokensDeleteOne) Exec(ctx context.Context) error {
// ExecX is like Exec, but panics if an error occurs.
func (atdo *AuthTokensDeleteOne) ExecX(ctx context.Context) {
if err := atdo.Exec(ctx); err != nil {
panic(err)
}
atdo.atd.ExecX(ctx)
}

View File

@@ -21,9 +21,11 @@ import (
// AuthTokensQuery is the builder for querying AuthTokens entities.
type AuthTokensQuery struct {
config
ctx *QueryContext
limit *int
offset *int
unique *bool
order []OrderFunc
inters []Interceptor
fields []string
predicates []predicate.AuthTokens
withUser *UserQuery
withRoles *AuthRolesQuery
@@ -39,26 +41,26 @@ func (atq *AuthTokensQuery) Where(ps ...predicate.AuthTokens) *AuthTokensQuery {
return atq
}
// Limit the number of records to be returned by this query.
// Limit adds a limit step to the query.
func (atq *AuthTokensQuery) Limit(limit int) *AuthTokensQuery {
atq.ctx.Limit = &limit
atq.limit = &limit
return atq
}
// Offset to start from.
// Offset adds an offset step to the query.
func (atq *AuthTokensQuery) Offset(offset int) *AuthTokensQuery {
atq.ctx.Offset = &offset
atq.offset = &offset
return atq
}
// 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 (atq *AuthTokensQuery) Unique(unique bool) *AuthTokensQuery {
atq.ctx.Unique = &unique
atq.unique = &unique
return atq
}
// Order specifies how the records should be ordered.
// Order adds an order step to the query.
func (atq *AuthTokensQuery) Order(o ...OrderFunc) *AuthTokensQuery {
atq.order = append(atq.order, o...)
return atq
@@ -66,7 +68,7 @@ func (atq *AuthTokensQuery) Order(o ...OrderFunc) *AuthTokensQuery {
// QueryUser chains the current query on the "user" edge.
func (atq *AuthTokensQuery) QueryUser() *UserQuery {
query := (&UserClient{config: atq.config}).Query()
query := &UserQuery{config: atq.config}
query.path = func(ctx context.Context) (fromU *sql.Selector, err error) {
if err := atq.prepareQuery(ctx); err != nil {
return nil, err
@@ -88,7 +90,7 @@ func (atq *AuthTokensQuery) QueryUser() *UserQuery {
// QueryRoles chains the current query on the "roles" edge.
func (atq *AuthTokensQuery) QueryRoles() *AuthRolesQuery {
query := (&AuthRolesClient{config: atq.config}).Query()
query := &AuthRolesQuery{config: atq.config}
query.path = func(ctx context.Context) (fromU *sql.Selector, err error) {
if err := atq.prepareQuery(ctx); err != nil {
return nil, err
@@ -111,7 +113,7 @@ func (atq *AuthTokensQuery) QueryRoles() *AuthRolesQuery {
// First returns the first AuthTokens entity from the query.
// Returns a *NotFoundError when no AuthTokens was found.
func (atq *AuthTokensQuery) First(ctx context.Context) (*AuthTokens, error) {
nodes, err := atq.Limit(1).All(setContextOp(ctx, atq.ctx, "First"))
nodes, err := atq.Limit(1).All(ctx)
if err != nil {
return nil, err
}
@@ -134,7 +136,7 @@ func (atq *AuthTokensQuery) FirstX(ctx context.Context) *AuthTokens {
// Returns a *NotFoundError when no AuthTokens ID was found.
func (atq *AuthTokensQuery) FirstID(ctx context.Context) (id uuid.UUID, err error) {
var ids []uuid.UUID
if ids, err = atq.Limit(1).IDs(setContextOp(ctx, atq.ctx, "FirstID")); err != nil {
if ids, err = atq.Limit(1).IDs(ctx); err != nil {
return
}
if len(ids) == 0 {
@@ -157,7 +159,7 @@ func (atq *AuthTokensQuery) FirstIDX(ctx context.Context) uuid.UUID {
// Returns a *NotSingularError when more than one AuthTokens entity is found.
// Returns a *NotFoundError when no AuthTokens entities are found.
func (atq *AuthTokensQuery) Only(ctx context.Context) (*AuthTokens, error) {
nodes, err := atq.Limit(2).All(setContextOp(ctx, atq.ctx, "Only"))
nodes, err := atq.Limit(2).All(ctx)
if err != nil {
return nil, err
}
@@ -185,7 +187,7 @@ func (atq *AuthTokensQuery) OnlyX(ctx context.Context) *AuthTokens {
// Returns a *NotFoundError when no entities are found.
func (atq *AuthTokensQuery) OnlyID(ctx context.Context) (id uuid.UUID, err error) {
var ids []uuid.UUID
if ids, err = atq.Limit(2).IDs(setContextOp(ctx, atq.ctx, "OnlyID")); err != nil {
if ids, err = atq.Limit(2).IDs(ctx); err != nil {
return
}
switch len(ids) {
@@ -210,12 +212,10 @@ func (atq *AuthTokensQuery) OnlyIDX(ctx context.Context) uuid.UUID {
// All executes the query and returns a list of AuthTokensSlice.
func (atq *AuthTokensQuery) All(ctx context.Context) ([]*AuthTokens, error) {
ctx = setContextOp(ctx, atq.ctx, "All")
if err := atq.prepareQuery(ctx); err != nil {
return nil, err
}
qr := querierAll[[]*AuthTokens, *AuthTokensQuery]()
return withInterceptors[[]*AuthTokens](ctx, atq, qr, atq.inters)
return atq.sqlAll(ctx)
}
// AllX is like All, but panics if an error occurs.
@@ -228,12 +228,9 @@ func (atq *AuthTokensQuery) AllX(ctx context.Context) []*AuthTokens {
}
// IDs executes the query and returns a list of AuthTokens IDs.
func (atq *AuthTokensQuery) IDs(ctx context.Context) (ids []uuid.UUID, err error) {
if atq.ctx.Unique == nil && atq.path != nil {
atq.Unique(true)
}
ctx = setContextOp(ctx, atq.ctx, "IDs")
if err = atq.Select(authtokens.FieldID).Scan(ctx, &ids); err != nil {
func (atq *AuthTokensQuery) IDs(ctx context.Context) ([]uuid.UUID, error) {
var ids []uuid.UUID
if err := atq.Select(authtokens.FieldID).Scan(ctx, &ids); err != nil {
return nil, err
}
return ids, nil
@@ -250,11 +247,10 @@ func (atq *AuthTokensQuery) IDsX(ctx context.Context) []uuid.UUID {
// Count returns the count of the given query.
func (atq *AuthTokensQuery) Count(ctx context.Context) (int, error) {
ctx = setContextOp(ctx, atq.ctx, "Count")
if err := atq.prepareQuery(ctx); err != nil {
return 0, err
}
return withInterceptors[int](ctx, atq, querierCount[*AuthTokensQuery](), atq.inters)
return atq.sqlCount(ctx)
}
// CountX is like Count, but panics if an error occurs.
@@ -268,15 +264,10 @@ func (atq *AuthTokensQuery) CountX(ctx context.Context) int {
// Exist returns true if the query has elements in the graph.
func (atq *AuthTokensQuery) Exist(ctx context.Context) (bool, error) {
ctx = setContextOp(ctx, atq.ctx, "Exist")
switch _, err := atq.FirstID(ctx); {
case IsNotFound(err):
return false, nil
case err != nil:
return false, fmt.Errorf("ent: check existence: %w", err)
default:
return true, nil
if err := atq.prepareQuery(ctx); err != nil {
return false, err
}
return atq.sqlExist(ctx)
}
// ExistX is like Exist, but panics if an error occurs.
@@ -296,22 +287,23 @@ func (atq *AuthTokensQuery) Clone() *AuthTokensQuery {
}
return &AuthTokensQuery{
config: atq.config,
ctx: atq.ctx.Clone(),
limit: atq.limit,
offset: atq.offset,
order: append([]OrderFunc{}, atq.order...),
inters: append([]Interceptor{}, atq.inters...),
predicates: append([]predicate.AuthTokens{}, atq.predicates...),
withUser: atq.withUser.Clone(),
withRoles: atq.withRoles.Clone(),
// clone intermediate query.
sql: atq.sql.Clone(),
path: atq.path,
sql: atq.sql.Clone(),
path: atq.path,
unique: atq.unique,
}
}
// WithUser tells the query-builder to eager-load the nodes that are connected to
// the "user" edge. The optional arguments are used to configure the query builder of the edge.
func (atq *AuthTokensQuery) WithUser(opts ...func(*UserQuery)) *AuthTokensQuery {
query := (&UserClient{config: atq.config}).Query()
query := &UserQuery{config: atq.config}
for _, opt := range opts {
opt(query)
}
@@ -322,7 +314,7 @@ func (atq *AuthTokensQuery) WithUser(opts ...func(*UserQuery)) *AuthTokensQuery
// WithRoles tells the query-builder to eager-load the nodes that are connected to
// the "roles" edge. The optional arguments are used to configure the query builder of the edge.
func (atq *AuthTokensQuery) WithRoles(opts ...func(*AuthRolesQuery)) *AuthTokensQuery {
query := (&AuthRolesClient{config: atq.config}).Query()
query := &AuthRolesQuery{config: atq.config}
for _, opt := range opts {
opt(query)
}
@@ -345,11 +337,16 @@ func (atq *AuthTokensQuery) WithRoles(opts ...func(*AuthRolesQuery)) *AuthTokens
// Aggregate(ent.Count()).
// Scan(ctx, &v)
func (atq *AuthTokensQuery) GroupBy(field string, fields ...string) *AuthTokensGroupBy {
atq.ctx.Fields = append([]string{field}, fields...)
grbuild := &AuthTokensGroupBy{build: atq}
grbuild.flds = &atq.ctx.Fields
grbuild := &AuthTokensGroupBy{config: atq.config}
grbuild.fields = append([]string{field}, fields...)
grbuild.path = func(ctx context.Context) (prev *sql.Selector, err error) {
if err := atq.prepareQuery(ctx); err != nil {
return nil, err
}
return atq.sqlQuery(ctx), nil
}
grbuild.label = authtokens.Label
grbuild.scan = grbuild.Scan
grbuild.flds, grbuild.scan = &grbuild.fields, grbuild.Scan
return grbuild
}
@@ -366,11 +363,11 @@ func (atq *AuthTokensQuery) GroupBy(field string, fields ...string) *AuthTokensG
// Select(authtokens.FieldCreatedAt).
// Scan(ctx, &v)
func (atq *AuthTokensQuery) Select(fields ...string) *AuthTokensSelect {
atq.ctx.Fields = append(atq.ctx.Fields, fields...)
sbuild := &AuthTokensSelect{AuthTokensQuery: atq}
sbuild.label = authtokens.Label
sbuild.flds, sbuild.scan = &atq.ctx.Fields, sbuild.Scan
return sbuild
atq.fields = append(atq.fields, fields...)
selbuild := &AuthTokensSelect{AuthTokensQuery: atq}
selbuild.label = authtokens.Label
selbuild.flds, selbuild.scan = &atq.fields, selbuild.Scan
return selbuild
}
// Aggregate returns a AuthTokensSelect configured with the given aggregations.
@@ -379,17 +376,7 @@ func (atq *AuthTokensQuery) Aggregate(fns ...AggregateFunc) *AuthTokensSelect {
}
func (atq *AuthTokensQuery) prepareQuery(ctx context.Context) error {
for _, inter := range atq.inters {
if inter == nil {
return fmt.Errorf("ent: uninitialized interceptor (forgotten import ent/runtime?)")
}
if trv, ok := inter.(Traverser); ok {
if err := trv.Traverse(ctx, atq); err != nil {
return err
}
}
}
for _, f := range atq.ctx.Fields {
for _, f := range atq.fields {
if !authtokens.ValidColumn(f) {
return &ValidationError{Name: f, err: fmt.Errorf("ent: invalid field %q for query", f)}
}
@@ -466,9 +453,6 @@ func (atq *AuthTokensQuery) loadUser(ctx context.Context, query *UserQuery, node
}
nodeids[fk] = append(nodeids[fk], nodes[i])
}
if len(ids) == 0 {
return nil
}
query.Where(user.IDIn(ids...))
neighbors, err := query.All(ctx)
if err != nil {
@@ -516,22 +500,41 @@ func (atq *AuthTokensQuery) loadRoles(ctx context.Context, query *AuthRolesQuery
func (atq *AuthTokensQuery) sqlCount(ctx context.Context) (int, error) {
_spec := atq.querySpec()
_spec.Node.Columns = atq.ctx.Fields
if len(atq.ctx.Fields) > 0 {
_spec.Unique = atq.ctx.Unique != nil && *atq.ctx.Unique
_spec.Node.Columns = atq.fields
if len(atq.fields) > 0 {
_spec.Unique = atq.unique != nil && *atq.unique
}
return sqlgraph.CountNodes(ctx, atq.driver, _spec)
}
func (atq *AuthTokensQuery) querySpec() *sqlgraph.QuerySpec {
_spec := sqlgraph.NewQuerySpec(authtokens.Table, authtokens.Columns, sqlgraph.NewFieldSpec(authtokens.FieldID, field.TypeUUID))
_spec.From = atq.sql
if unique := atq.ctx.Unique; unique != nil {
_spec.Unique = *unique
} else if atq.path != nil {
_spec.Unique = true
func (atq *AuthTokensQuery) sqlExist(ctx context.Context) (bool, error) {
switch _, err := atq.FirstID(ctx); {
case IsNotFound(err):
return false, nil
case err != nil:
return false, fmt.Errorf("ent: check existence: %w", err)
default:
return true, nil
}
if fields := atq.ctx.Fields; len(fields) > 0 {
}
func (atq *AuthTokensQuery) querySpec() *sqlgraph.QuerySpec {
_spec := &sqlgraph.QuerySpec{
Node: &sqlgraph.NodeSpec{
Table: authtokens.Table,
Columns: authtokens.Columns,
ID: &sqlgraph.FieldSpec{
Type: field.TypeUUID,
Column: authtokens.FieldID,
},
},
From: atq.sql,
Unique: true,
}
if unique := atq.unique; unique != nil {
_spec.Unique = *unique
}
if fields := atq.fields; len(fields) > 0 {
_spec.Node.Columns = make([]string, 0, len(fields))
_spec.Node.Columns = append(_spec.Node.Columns, authtokens.FieldID)
for i := range fields {
@@ -547,10 +550,10 @@ func (atq *AuthTokensQuery) querySpec() *sqlgraph.QuerySpec {
}
}
}
if limit := atq.ctx.Limit; limit != nil {
if limit := atq.limit; limit != nil {
_spec.Limit = *limit
}
if offset := atq.ctx.Offset; offset != nil {
if offset := atq.offset; offset != nil {
_spec.Offset = *offset
}
if ps := atq.order; len(ps) > 0 {
@@ -566,7 +569,7 @@ func (atq *AuthTokensQuery) querySpec() *sqlgraph.QuerySpec {
func (atq *AuthTokensQuery) sqlQuery(ctx context.Context) *sql.Selector {
builder := sql.Dialect(atq.driver.Dialect())
t1 := builder.Table(authtokens.Table)
columns := atq.ctx.Fields
columns := atq.fields
if len(columns) == 0 {
columns = authtokens.Columns
}
@@ -575,7 +578,7 @@ func (atq *AuthTokensQuery) sqlQuery(ctx context.Context) *sql.Selector {
selector = atq.sql
selector.Select(selector.Columns(columns...)...)
}
if atq.ctx.Unique != nil && *atq.ctx.Unique {
if atq.unique != nil && *atq.unique {
selector.Distinct()
}
for _, p := range atq.predicates {
@@ -584,12 +587,12 @@ func (atq *AuthTokensQuery) sqlQuery(ctx context.Context) *sql.Selector {
for _, p := range atq.order {
p(selector)
}
if offset := atq.ctx.Offset; offset != nil {
if offset := atq.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 := atq.ctx.Limit; limit != nil {
if limit := atq.limit; limit != nil {
selector.Limit(*limit)
}
return selector
@@ -597,8 +600,13 @@ func (atq *AuthTokensQuery) sqlQuery(ctx context.Context) *sql.Selector {
// AuthTokensGroupBy is the group-by builder for AuthTokens entities.
type AuthTokensGroupBy struct {
config
selector
build *AuthTokensQuery
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.
@@ -607,46 +615,58 @@ func (atgb *AuthTokensGroupBy) Aggregate(fns ...AggregateFunc) *AuthTokensGroupB
return atgb
}
// Scan applies the selector query and scans the result into the given value.
// Scan applies the group-by query and scans the result into the given value.
func (atgb *AuthTokensGroupBy) Scan(ctx context.Context, v any) error {
ctx = setContextOp(ctx, atgb.build.ctx, "GroupBy")
if err := atgb.build.prepareQuery(ctx); err != nil {
query, err := atgb.path(ctx)
if err != nil {
return err
}
return scanWithInterceptors[*AuthTokensQuery, *AuthTokensGroupBy](ctx, atgb.build, atgb, atgb.build.inters, v)
atgb.sql = query
return atgb.sqlScan(ctx, v)
}
func (atgb *AuthTokensGroupBy) sqlScan(ctx context.Context, root *AuthTokensQuery, v any) error {
selector := root.sqlQuery(ctx).Select()
aggregation := make([]string, 0, len(atgb.fns))
for _, fn := range atgb.fns {
aggregation = append(aggregation, fn(selector))
}
if len(selector.SelectedColumns()) == 0 {
columns := make([]string, 0, len(*atgb.flds)+len(atgb.fns))
for _, f := range *atgb.flds {
columns = append(columns, selector.C(f))
func (atgb *AuthTokensGroupBy) sqlScan(ctx context.Context, v any) error {
for _, f := range atgb.fields {
if !authtokens.ValidColumn(f) {
return &ValidationError{Name: f, err: fmt.Errorf("invalid field %q for group-by", f)}
}
columns = append(columns, aggregation...)
selector.Select(columns...)
}
selector.GroupBy(selector.Columns(*atgb.flds...)...)
selector := atgb.sqlQuery()
if err := selector.Err(); err != nil {
return err
}
rows := &sql.Rows{}
query, args := selector.Query()
if err := atgb.build.driver.Query(ctx, query, args, rows); err != nil {
if err := atgb.driver.Query(ctx, query, args, rows); err != nil {
return err
}
defer rows.Close()
return sql.ScanSlice(rows, v)
}
func (atgb *AuthTokensGroupBy) sqlQuery() *sql.Selector {
selector := atgb.sql.Select()
aggregation := make([]string, 0, len(atgb.fns))
for _, fn := range atgb.fns {
aggregation = append(aggregation, fn(selector))
}
if len(selector.SelectedColumns()) == 0 {
columns := make([]string, 0, len(atgb.fields)+len(atgb.fns))
for _, f := range atgb.fields {
columns = append(columns, selector.C(f))
}
columns = append(columns, aggregation...)
selector.Select(columns...)
}
return selector.GroupBy(selector.Columns(atgb.fields...)...)
}
// AuthTokensSelect is the builder for selecting fields of AuthTokens entities.
type AuthTokensSelect struct {
*AuthTokensQuery
selector
// intermediate query (i.e. traversal path).
sql *sql.Selector
}
// Aggregate adds the given aggregation functions to the selector query.
@@ -657,27 +677,26 @@ func (ats *AuthTokensSelect) Aggregate(fns ...AggregateFunc) *AuthTokensSelect {
// Scan applies the selector query and scans the result into the given value.
func (ats *AuthTokensSelect) Scan(ctx context.Context, v any) error {
ctx = setContextOp(ctx, ats.ctx, "Select")
if err := ats.prepareQuery(ctx); err != nil {
return err
}
return scanWithInterceptors[*AuthTokensQuery, *AuthTokensSelect](ctx, ats.AuthTokensQuery, ats, ats.inters, v)
ats.sql = ats.AuthTokensQuery.sqlQuery(ctx)
return ats.sqlScan(ctx, v)
}
func (ats *AuthTokensSelect) sqlScan(ctx context.Context, root *AuthTokensQuery, v any) error {
selector := root.sqlQuery(ctx)
func (ats *AuthTokensSelect) sqlScan(ctx context.Context, v any) error {
aggregation := make([]string, 0, len(ats.fns))
for _, fn := range ats.fns {
aggregation = append(aggregation, fn(selector))
aggregation = append(aggregation, fn(ats.sql))
}
switch n := len(*ats.selector.flds); {
case n == 0 && len(aggregation) > 0:
selector.Select(aggregation...)
ats.sql.Select(aggregation...)
case n != 0 && len(aggregation) > 0:
selector.AppendSelect(aggregation...)
ats.sql.AppendSelect(aggregation...)
}
rows := &sql.Rows{}
query, args := selector.Query()
query, args := ats.sql.Query()
if err := ats.driver.Query(ctx, query, args, rows); err != nil {
return err
}

View File

@@ -114,8 +114,35 @@ func (atu *AuthTokensUpdate) ClearRoles() *AuthTokensUpdate {
// Save executes the query and returns the number of nodes affected by the update operation.
func (atu *AuthTokensUpdate) Save(ctx context.Context) (int, error) {
var (
err error
affected int
)
atu.defaults()
return withHooks[int, AuthTokensMutation](ctx, atu.sqlSave, atu.mutation, atu.hooks)
if len(atu.hooks) == 0 {
affected, err = atu.sqlSave(ctx)
} else {
var mut Mutator = MutateFunc(func(ctx context.Context, m Mutation) (Value, error) {
mutation, ok := m.(*AuthTokensMutation)
if !ok {
return nil, fmt.Errorf("unexpected mutation type %T", m)
}
atu.mutation = mutation
affected, err = atu.sqlSave(ctx)
mutation.done = true
return affected, err
})
for i := len(atu.hooks) - 1; i >= 0; i-- {
if atu.hooks[i] == nil {
return 0, fmt.Errorf("ent: uninitialized hook (forgotten import ent/runtime?)")
}
mut = atu.hooks[i](mut)
}
if _, err := mut.Mutate(ctx, atu.mutation); err != nil {
return 0, err
}
}
return affected, err
}
// SaveX is like Save, but panics if an error occurs.
@@ -149,7 +176,16 @@ func (atu *AuthTokensUpdate) defaults() {
}
func (atu *AuthTokensUpdate) sqlSave(ctx context.Context) (n int, err error) {
_spec := sqlgraph.NewUpdateSpec(authtokens.Table, authtokens.Columns, sqlgraph.NewFieldSpec(authtokens.FieldID, field.TypeUUID))
_spec := &sqlgraph.UpdateSpec{
Node: &sqlgraph.NodeSpec{
Table: authtokens.Table,
Columns: authtokens.Columns,
ID: &sqlgraph.FieldSpec{
Type: field.TypeUUID,
Column: authtokens.FieldID,
},
},
}
if ps := atu.mutation.predicates; len(ps) > 0 {
_spec.Predicate = func(selector *sql.Selector) {
for i := range ps {
@@ -174,7 +210,10 @@ func (atu *AuthTokensUpdate) sqlSave(ctx context.Context) (n int, err error) {
Columns: []string{authtokens.UserColumn},
Bidi: false,
Target: &sqlgraph.EdgeTarget{
IDSpec: sqlgraph.NewFieldSpec(user.FieldID, field.TypeUUID),
IDSpec: &sqlgraph.FieldSpec{
Type: field.TypeUUID,
Column: user.FieldID,
},
},
}
_spec.Edges.Clear = append(_spec.Edges.Clear, edge)
@@ -187,7 +226,10 @@ func (atu *AuthTokensUpdate) sqlSave(ctx context.Context) (n int, err error) {
Columns: []string{authtokens.UserColumn},
Bidi: false,
Target: &sqlgraph.EdgeTarget{
IDSpec: sqlgraph.NewFieldSpec(user.FieldID, field.TypeUUID),
IDSpec: &sqlgraph.FieldSpec{
Type: field.TypeUUID,
Column: user.FieldID,
},
},
}
for _, k := range nodes {
@@ -203,7 +245,10 @@ func (atu *AuthTokensUpdate) sqlSave(ctx context.Context) (n int, err error) {
Columns: []string{authtokens.RolesColumn},
Bidi: false,
Target: &sqlgraph.EdgeTarget{
IDSpec: sqlgraph.NewFieldSpec(authroles.FieldID, field.TypeInt),
IDSpec: &sqlgraph.FieldSpec{
Type: field.TypeInt,
Column: authroles.FieldID,
},
},
}
_spec.Edges.Clear = append(_spec.Edges.Clear, edge)
@@ -216,7 +261,10 @@ func (atu *AuthTokensUpdate) sqlSave(ctx context.Context) (n int, err error) {
Columns: []string{authtokens.RolesColumn},
Bidi: false,
Target: &sqlgraph.EdgeTarget{
IDSpec: sqlgraph.NewFieldSpec(authroles.FieldID, field.TypeInt),
IDSpec: &sqlgraph.FieldSpec{
Type: field.TypeInt,
Column: authroles.FieldID,
},
},
}
for _, k := range nodes {
@@ -232,7 +280,6 @@ func (atu *AuthTokensUpdate) sqlSave(ctx context.Context) (n int, err error) {
}
return 0, err
}
atu.mutation.done = true
return n, nil
}
@@ -325,12 +372,6 @@ func (atuo *AuthTokensUpdateOne) ClearRoles() *AuthTokensUpdateOne {
return atuo
}
// Where appends a list predicates to the AuthTokensUpdate builder.
func (atuo *AuthTokensUpdateOne) Where(ps ...predicate.AuthTokens) *AuthTokensUpdateOne {
atuo.mutation.Where(ps...)
return atuo
}
// Select allows selecting one or more fields (columns) of the returned entity.
// The default is selecting all fields defined in the entity schema.
func (atuo *AuthTokensUpdateOne) Select(field string, fields ...string) *AuthTokensUpdateOne {
@@ -340,8 +381,41 @@ func (atuo *AuthTokensUpdateOne) Select(field string, fields ...string) *AuthTok
// Save executes the query and returns the updated AuthTokens entity.
func (atuo *AuthTokensUpdateOne) Save(ctx context.Context) (*AuthTokens, error) {
var (
err error
node *AuthTokens
)
atuo.defaults()
return withHooks[*AuthTokens, AuthTokensMutation](ctx, atuo.sqlSave, atuo.mutation, atuo.hooks)
if len(atuo.hooks) == 0 {
node, err = atuo.sqlSave(ctx)
} else {
var mut Mutator = MutateFunc(func(ctx context.Context, m Mutation) (Value, error) {
mutation, ok := m.(*AuthTokensMutation)
if !ok {
return nil, fmt.Errorf("unexpected mutation type %T", m)
}
atuo.mutation = mutation
node, err = atuo.sqlSave(ctx)
mutation.done = true
return node, err
})
for i := len(atuo.hooks) - 1; i >= 0; i-- {
if atuo.hooks[i] == nil {
return nil, fmt.Errorf("ent: uninitialized hook (forgotten import ent/runtime?)")
}
mut = atuo.hooks[i](mut)
}
v, err := mut.Mutate(ctx, atuo.mutation)
if err != nil {
return nil, err
}
nv, ok := v.(*AuthTokens)
if !ok {
return nil, fmt.Errorf("unexpected node type %T returned from AuthTokensMutation", v)
}
node = nv
}
return node, err
}
// SaveX is like Save, but panics if an error occurs.
@@ -375,7 +449,16 @@ func (atuo *AuthTokensUpdateOne) defaults() {
}
func (atuo *AuthTokensUpdateOne) sqlSave(ctx context.Context) (_node *AuthTokens, err error) {
_spec := sqlgraph.NewUpdateSpec(authtokens.Table, authtokens.Columns, sqlgraph.NewFieldSpec(authtokens.FieldID, field.TypeUUID))
_spec := &sqlgraph.UpdateSpec{
Node: &sqlgraph.NodeSpec{
Table: authtokens.Table,
Columns: authtokens.Columns,
ID: &sqlgraph.FieldSpec{
Type: field.TypeUUID,
Column: authtokens.FieldID,
},
},
}
id, ok := atuo.mutation.ID()
if !ok {
return nil, &ValidationError{Name: "id", err: errors.New(`ent: missing "AuthTokens.id" for update`)}
@@ -417,7 +500,10 @@ func (atuo *AuthTokensUpdateOne) sqlSave(ctx context.Context) (_node *AuthTokens
Columns: []string{authtokens.UserColumn},
Bidi: false,
Target: &sqlgraph.EdgeTarget{
IDSpec: sqlgraph.NewFieldSpec(user.FieldID, field.TypeUUID),
IDSpec: &sqlgraph.FieldSpec{
Type: field.TypeUUID,
Column: user.FieldID,
},
},
}
_spec.Edges.Clear = append(_spec.Edges.Clear, edge)
@@ -430,7 +516,10 @@ func (atuo *AuthTokensUpdateOne) sqlSave(ctx context.Context) (_node *AuthTokens
Columns: []string{authtokens.UserColumn},
Bidi: false,
Target: &sqlgraph.EdgeTarget{
IDSpec: sqlgraph.NewFieldSpec(user.FieldID, field.TypeUUID),
IDSpec: &sqlgraph.FieldSpec{
Type: field.TypeUUID,
Column: user.FieldID,
},
},
}
for _, k := range nodes {
@@ -446,7 +535,10 @@ func (atuo *AuthTokensUpdateOne) sqlSave(ctx context.Context) (_node *AuthTokens
Columns: []string{authtokens.RolesColumn},
Bidi: false,
Target: &sqlgraph.EdgeTarget{
IDSpec: sqlgraph.NewFieldSpec(authroles.FieldID, field.TypeInt),
IDSpec: &sqlgraph.FieldSpec{
Type: field.TypeInt,
Column: authroles.FieldID,
},
},
}
_spec.Edges.Clear = append(_spec.Edges.Clear, edge)
@@ -459,7 +551,10 @@ func (atuo *AuthTokensUpdateOne) sqlSave(ctx context.Context) (_node *AuthTokens
Columns: []string{authtokens.RolesColumn},
Bidi: false,
Target: &sqlgraph.EdgeTarget{
IDSpec: sqlgraph.NewFieldSpec(authroles.FieldID, field.TypeInt),
IDSpec: &sqlgraph.FieldSpec{
Type: field.TypeInt,
Column: authroles.FieldID,
},
},
}
for _, k := range nodes {
@@ -478,6 +573,5 @@ func (atuo *AuthTokensUpdateOne) sqlSave(ctx context.Context) (_node *AuthTokens
}
return nil, err
}
atuo.mutation.done = true
return _node, nil
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,70 @@
// Code generated by ent, DO NOT EDIT.
package ent
import (
"entgo.io/ent"
"entgo.io/ent/dialect"
)
// Option function to configure the client.
type Option func(*config)
// Config is the configuration for the client and its builder.
type config struct {
// driver used for executing database requests.
driver dialect.Driver
// debug enable a debug logging.
debug bool
// log used for logging on debug mode.
log func(...any)
// hooks to execute on mutations.
hooks *hooks
}
// hooks per client, for fast access.
type hooks struct {
Attachment []ent.Hook
AuthRoles []ent.Hook
AuthTokens []ent.Hook
Document []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
}
// Options applies the options on the config object.
func (c *config) options(opts ...Option) {
for _, opt := range opts {
opt(c)
}
if c.debug {
c.driver = dialect.Debug(c.driver, c.log)
}
}
// Debug enables debug logging on the ent.Driver.
func Debug() Option {
return func(c *config) {
c.debug = true
}
}
// Log sets the logging function for debug mode.
func Log(fn func(...any)) Option {
return func(c *config) {
c.log = fn
}
}
// Driver configures the client driver.
func Driver(driver dialect.Driver) Option {
return func(c *config) {
c.driver = driver
}
}

View File

@@ -0,0 +1,33 @@
// Code generated by ent, DO NOT EDIT.
package ent
import (
"context"
)
type clientCtxKey struct{}
// FromContext returns a Client stored inside a context, or nil if there isn't one.
func FromContext(ctx context.Context) *Client {
c, _ := ctx.Value(clientCtxKey{}).(*Client)
return c
}
// NewContext returns a new context with the given Client attached.
func NewContext(parent context.Context, c *Client) context.Context {
return context.WithValue(parent, clientCtxKey{}, c)
}
type txCtxKey struct{}
// TxFromContext returns a Tx stored inside a context, or nil if there isn't one.
func TxFromContext(ctx context.Context) *Tx {
tx, _ := ctx.Value(txCtxKey{}).(*Tx)
return tx
}
// NewTxContext returns a new context with the given Tx attached.
func NewTxContext(parent context.Context, tx *Tx) context.Context {
return context.WithValue(parent, txCtxKey{}, tx)
}

View File

@@ -137,19 +137,19 @@ func (d *Document) assignValues(columns []string, values []any) error {
// QueryGroup queries the "group" edge of the Document entity.
func (d *Document) QueryGroup() *GroupQuery {
return NewDocumentClient(d.config).QueryGroup(d)
return (&DocumentClient{config: d.config}).QueryGroup(d)
}
// QueryAttachments queries the "attachments" edge of the Document entity.
func (d *Document) QueryAttachments() *AttachmentQuery {
return NewDocumentClient(d.config).QueryAttachments(d)
return (&DocumentClient{config: d.config}).QueryAttachments(d)
}
// Update returns a builder for updating this Document.
// Note that you need to call Document.Unwrap() before calling this method if this Document
// was returned from a transaction, and the transaction was committed or rolled back.
func (d *Document) Update() *DocumentUpdateOne {
return NewDocumentClient(d.config).UpdateOne(d)
return (&DocumentClient{config: d.config}).UpdateOne(d)
}
// Unwrap unwraps the Document entity that was returned from a transaction after it was closed,
@@ -185,3 +185,9 @@ func (d *Document) String() string {
// Documents is a parsable slice of Document.
type Documents []*Document
func (d Documents) config(cfg config) {
for _i := range d {
d[_i].config = cfg
}
}

View File

@@ -13,277 +13,427 @@ import (
// ID filters vertices based on their ID field.
func ID(id uuid.UUID) predicate.Document {
return predicate.Document(sql.FieldEQ(FieldID, id))
return predicate.Document(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.Document {
return predicate.Document(sql.FieldEQ(FieldID, id))
return predicate.Document(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.Document {
return predicate.Document(sql.FieldNEQ(FieldID, id))
return predicate.Document(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.Document {
return predicate.Document(sql.FieldIn(FieldID, ids...))
return predicate.Document(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.Document {
return predicate.Document(sql.FieldNotIn(FieldID, ids...))
return predicate.Document(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.Document {
return predicate.Document(sql.FieldGT(FieldID, id))
return predicate.Document(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.Document {
return predicate.Document(sql.FieldGTE(FieldID, id))
return predicate.Document(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.Document {
return predicate.Document(sql.FieldLT(FieldID, id))
return predicate.Document(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.Document {
return predicate.Document(sql.FieldLTE(FieldID, id))
return predicate.Document(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.Document {
return predicate.Document(sql.FieldEQ(FieldCreatedAt, v))
return predicate.Document(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.Document {
return predicate.Document(sql.FieldEQ(FieldUpdatedAt, v))
return predicate.Document(func(s *sql.Selector) {
s.Where(sql.EQ(s.C(FieldUpdatedAt), v))
})
}
// Title applies equality check predicate on the "title" field. It's identical to TitleEQ.
func Title(v string) predicate.Document {
return predicate.Document(sql.FieldEQ(FieldTitle, v))
return predicate.Document(func(s *sql.Selector) {
s.Where(sql.EQ(s.C(FieldTitle), v))
})
}
// Path applies equality check predicate on the "path" field. It's identical to PathEQ.
func Path(v string) predicate.Document {
return predicate.Document(sql.FieldEQ(FieldPath, v))
return predicate.Document(func(s *sql.Selector) {
s.Where(sql.EQ(s.C(FieldPath), v))
})
}
// CreatedAtEQ applies the EQ predicate on the "created_at" field.
func CreatedAtEQ(v time.Time) predicate.Document {
return predicate.Document(sql.FieldEQ(FieldCreatedAt, v))
return predicate.Document(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.Document {
return predicate.Document(sql.FieldNEQ(FieldCreatedAt, v))
return predicate.Document(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.Document {
return predicate.Document(sql.FieldIn(FieldCreatedAt, vs...))
v := make([]any, len(vs))
for i := range v {
v[i] = vs[i]
}
return predicate.Document(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.Document {
return predicate.Document(sql.FieldNotIn(FieldCreatedAt, vs...))
v := make([]any, len(vs))
for i := range v {
v[i] = vs[i]
}
return predicate.Document(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.Document {
return predicate.Document(sql.FieldGT(FieldCreatedAt, v))
return predicate.Document(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.Document {
return predicate.Document(sql.FieldGTE(FieldCreatedAt, v))
return predicate.Document(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.Document {
return predicate.Document(sql.FieldLT(FieldCreatedAt, v))
return predicate.Document(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.Document {
return predicate.Document(sql.FieldLTE(FieldCreatedAt, v))
return predicate.Document(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.Document {
return predicate.Document(sql.FieldEQ(FieldUpdatedAt, v))
return predicate.Document(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.Document {
return predicate.Document(sql.FieldNEQ(FieldUpdatedAt, v))
return predicate.Document(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.Document {
return predicate.Document(sql.FieldIn(FieldUpdatedAt, vs...))
v := make([]any, len(vs))
for i := range v {
v[i] = vs[i]
}
return predicate.Document(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.Document {
return predicate.Document(sql.FieldNotIn(FieldUpdatedAt, vs...))
v := make([]any, len(vs))
for i := range v {
v[i] = vs[i]
}
return predicate.Document(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.Document {
return predicate.Document(sql.FieldGT(FieldUpdatedAt, v))
return predicate.Document(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.Document {
return predicate.Document(sql.FieldGTE(FieldUpdatedAt, v))
return predicate.Document(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.Document {
return predicate.Document(sql.FieldLT(FieldUpdatedAt, v))
return predicate.Document(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.Document {
return predicate.Document(sql.FieldLTE(FieldUpdatedAt, v))
return predicate.Document(func(s *sql.Selector) {
s.Where(sql.LTE(s.C(FieldUpdatedAt), v))
})
}
// TitleEQ applies the EQ predicate on the "title" field.
func TitleEQ(v string) predicate.Document {
return predicate.Document(sql.FieldEQ(FieldTitle, v))
return predicate.Document(func(s *sql.Selector) {
s.Where(sql.EQ(s.C(FieldTitle), v))
})
}
// TitleNEQ applies the NEQ predicate on the "title" field.
func TitleNEQ(v string) predicate.Document {
return predicate.Document(sql.FieldNEQ(FieldTitle, v))
return predicate.Document(func(s *sql.Selector) {
s.Where(sql.NEQ(s.C(FieldTitle), v))
})
}
// TitleIn applies the In predicate on the "title" field.
func TitleIn(vs ...string) predicate.Document {
return predicate.Document(sql.FieldIn(FieldTitle, vs...))
v := make([]any, len(vs))
for i := range v {
v[i] = vs[i]
}
return predicate.Document(func(s *sql.Selector) {
s.Where(sql.In(s.C(FieldTitle), v...))
})
}
// TitleNotIn applies the NotIn predicate on the "title" field.
func TitleNotIn(vs ...string) predicate.Document {
return predicate.Document(sql.FieldNotIn(FieldTitle, vs...))
v := make([]any, len(vs))
for i := range v {
v[i] = vs[i]
}
return predicate.Document(func(s *sql.Selector) {
s.Where(sql.NotIn(s.C(FieldTitle), v...))
})
}
// TitleGT applies the GT predicate on the "title" field.
func TitleGT(v string) predicate.Document {
return predicate.Document(sql.FieldGT(FieldTitle, v))
return predicate.Document(func(s *sql.Selector) {
s.Where(sql.GT(s.C(FieldTitle), v))
})
}
// TitleGTE applies the GTE predicate on the "title" field.
func TitleGTE(v string) predicate.Document {
return predicate.Document(sql.FieldGTE(FieldTitle, v))
return predicate.Document(func(s *sql.Selector) {
s.Where(sql.GTE(s.C(FieldTitle), v))
})
}
// TitleLT applies the LT predicate on the "title" field.
func TitleLT(v string) predicate.Document {
return predicate.Document(sql.FieldLT(FieldTitle, v))
return predicate.Document(func(s *sql.Selector) {
s.Where(sql.LT(s.C(FieldTitle), v))
})
}
// TitleLTE applies the LTE predicate on the "title" field.
func TitleLTE(v string) predicate.Document {
return predicate.Document(sql.FieldLTE(FieldTitle, v))
return predicate.Document(func(s *sql.Selector) {
s.Where(sql.LTE(s.C(FieldTitle), v))
})
}
// TitleContains applies the Contains predicate on the "title" field.
func TitleContains(v string) predicate.Document {
return predicate.Document(sql.FieldContains(FieldTitle, v))
return predicate.Document(func(s *sql.Selector) {
s.Where(sql.Contains(s.C(FieldTitle), v))
})
}
// TitleHasPrefix applies the HasPrefix predicate on the "title" field.
func TitleHasPrefix(v string) predicate.Document {
return predicate.Document(sql.FieldHasPrefix(FieldTitle, v))
return predicate.Document(func(s *sql.Selector) {
s.Where(sql.HasPrefix(s.C(FieldTitle), v))
})
}
// TitleHasSuffix applies the HasSuffix predicate on the "title" field.
func TitleHasSuffix(v string) predicate.Document {
return predicate.Document(sql.FieldHasSuffix(FieldTitle, v))
return predicate.Document(func(s *sql.Selector) {
s.Where(sql.HasSuffix(s.C(FieldTitle), v))
})
}
// TitleEqualFold applies the EqualFold predicate on the "title" field.
func TitleEqualFold(v string) predicate.Document {
return predicate.Document(sql.FieldEqualFold(FieldTitle, v))
return predicate.Document(func(s *sql.Selector) {
s.Where(sql.EqualFold(s.C(FieldTitle), v))
})
}
// TitleContainsFold applies the ContainsFold predicate on the "title" field.
func TitleContainsFold(v string) predicate.Document {
return predicate.Document(sql.FieldContainsFold(FieldTitle, v))
return predicate.Document(func(s *sql.Selector) {
s.Where(sql.ContainsFold(s.C(FieldTitle), v))
})
}
// PathEQ applies the EQ predicate on the "path" field.
func PathEQ(v string) predicate.Document {
return predicate.Document(sql.FieldEQ(FieldPath, v))
return predicate.Document(func(s *sql.Selector) {
s.Where(sql.EQ(s.C(FieldPath), v))
})
}
// PathNEQ applies the NEQ predicate on the "path" field.
func PathNEQ(v string) predicate.Document {
return predicate.Document(sql.FieldNEQ(FieldPath, v))
return predicate.Document(func(s *sql.Selector) {
s.Where(sql.NEQ(s.C(FieldPath), v))
})
}
// PathIn applies the In predicate on the "path" field.
func PathIn(vs ...string) predicate.Document {
return predicate.Document(sql.FieldIn(FieldPath, vs...))
v := make([]any, len(vs))
for i := range v {
v[i] = vs[i]
}
return predicate.Document(func(s *sql.Selector) {
s.Where(sql.In(s.C(FieldPath), v...))
})
}
// PathNotIn applies the NotIn predicate on the "path" field.
func PathNotIn(vs ...string) predicate.Document {
return predicate.Document(sql.FieldNotIn(FieldPath, vs...))
v := make([]any, len(vs))
for i := range v {
v[i] = vs[i]
}
return predicate.Document(func(s *sql.Selector) {
s.Where(sql.NotIn(s.C(FieldPath), v...))
})
}
// PathGT applies the GT predicate on the "path" field.
func PathGT(v string) predicate.Document {
return predicate.Document(sql.FieldGT(FieldPath, v))
return predicate.Document(func(s *sql.Selector) {
s.Where(sql.GT(s.C(FieldPath), v))
})
}
// PathGTE applies the GTE predicate on the "path" field.
func PathGTE(v string) predicate.Document {
return predicate.Document(sql.FieldGTE(FieldPath, v))
return predicate.Document(func(s *sql.Selector) {
s.Where(sql.GTE(s.C(FieldPath), v))
})
}
// PathLT applies the LT predicate on the "path" field.
func PathLT(v string) predicate.Document {
return predicate.Document(sql.FieldLT(FieldPath, v))
return predicate.Document(func(s *sql.Selector) {
s.Where(sql.LT(s.C(FieldPath), v))
})
}
// PathLTE applies the LTE predicate on the "path" field.
func PathLTE(v string) predicate.Document {
return predicate.Document(sql.FieldLTE(FieldPath, v))
return predicate.Document(func(s *sql.Selector) {
s.Where(sql.LTE(s.C(FieldPath), v))
})
}
// PathContains applies the Contains predicate on the "path" field.
func PathContains(v string) predicate.Document {
return predicate.Document(sql.FieldContains(FieldPath, v))
return predicate.Document(func(s *sql.Selector) {
s.Where(sql.Contains(s.C(FieldPath), v))
})
}
// PathHasPrefix applies the HasPrefix predicate on the "path" field.
func PathHasPrefix(v string) predicate.Document {
return predicate.Document(sql.FieldHasPrefix(FieldPath, v))
return predicate.Document(func(s *sql.Selector) {
s.Where(sql.HasPrefix(s.C(FieldPath), v))
})
}
// PathHasSuffix applies the HasSuffix predicate on the "path" field.
func PathHasSuffix(v string) predicate.Document {
return predicate.Document(sql.FieldHasSuffix(FieldPath, v))
return predicate.Document(func(s *sql.Selector) {
s.Where(sql.HasSuffix(s.C(FieldPath), v))
})
}
// PathEqualFold applies the EqualFold predicate on the "path" field.
func PathEqualFold(v string) predicate.Document {
return predicate.Document(sql.FieldEqualFold(FieldPath, v))
return predicate.Document(func(s *sql.Selector) {
s.Where(sql.EqualFold(s.C(FieldPath), v))
})
}
// PathContainsFold applies the ContainsFold predicate on the "path" field.
func PathContainsFold(v string) predicate.Document {
return predicate.Document(sql.FieldContainsFold(FieldPath, v))
return predicate.Document(func(s *sql.Selector) {
s.Where(sql.ContainsFold(s.C(FieldPath), v))
})
}
// HasGroup applies the HasEdge predicate on the "group" edge.
@@ -291,6 +441,7 @@ func HasGroup() predicate.Document {
return predicate.Document(func(s *sql.Selector) {
step := sqlgraph.NewStep(
sqlgraph.From(Table, FieldID),
sqlgraph.To(GroupTable, FieldID),
sqlgraph.Edge(sqlgraph.M2O, true, GroupTable, GroupColumn),
)
sqlgraph.HasNeighbors(s, step)
@@ -318,6 +469,7 @@ func HasAttachments() predicate.Document {
return predicate.Document(func(s *sql.Selector) {
step := sqlgraph.NewStep(
sqlgraph.From(Table, FieldID),
sqlgraph.To(AttachmentsTable, FieldID),
sqlgraph.Edge(sqlgraph.O2M, false, AttachmentsTable, AttachmentsColumn),
)
sqlgraph.HasNeighbors(s, step)

View File

@@ -110,8 +110,50 @@ func (dc *DocumentCreate) Mutation() *DocumentMutation {
// Save creates the Document in the database.
func (dc *DocumentCreate) Save(ctx context.Context) (*Document, error) {
var (
err error
node *Document
)
dc.defaults()
return withHooks[*Document, DocumentMutation](ctx, dc.sqlSave, dc.mutation, dc.hooks)
if len(dc.hooks) == 0 {
if err = dc.check(); err != nil {
return nil, err
}
node, err = dc.sqlSave(ctx)
} else {
var mut Mutator = MutateFunc(func(ctx context.Context, m Mutation) (Value, error) {
mutation, ok := m.(*DocumentMutation)
if !ok {
return nil, fmt.Errorf("unexpected mutation type %T", m)
}
if err = dc.check(); err != nil {
return nil, err
}
dc.mutation = mutation
if node, err = dc.sqlSave(ctx); err != nil {
return nil, err
}
mutation.id = &node.ID
mutation.done = true
return node, err
})
for i := len(dc.hooks) - 1; i >= 0; i-- {
if dc.hooks[i] == nil {
return nil, fmt.Errorf("ent: uninitialized hook (forgotten import ent/runtime?)")
}
mut = dc.hooks[i](mut)
}
v, err := mut.Mutate(ctx, dc.mutation)
if err != nil {
return nil, err
}
nv, ok := v.(*Document)
if !ok {
return nil, fmt.Errorf("unexpected node type %T returned from DocumentMutation", v)
}
node = nv
}
return node, err
}
// SaveX calls Save and panics if Save returns an error.
@@ -183,9 +225,6 @@ func (dc *DocumentCreate) check() error {
}
func (dc *DocumentCreate) sqlSave(ctx context.Context) (*Document, error) {
if err := dc.check(); err != nil {
return nil, err
}
_node, _spec := dc.createSpec()
if err := sqlgraph.CreateNode(ctx, dc.driver, _spec); err != nil {
if sqlgraph.IsConstraintError(err) {
@@ -200,15 +239,19 @@ func (dc *DocumentCreate) sqlSave(ctx context.Context) (*Document, error) {
return nil, err
}
}
dc.mutation.id = &_node.ID
dc.mutation.done = true
return _node, nil
}
func (dc *DocumentCreate) createSpec() (*Document, *sqlgraph.CreateSpec) {
var (
_node = &Document{config: dc.config}
_spec = sqlgraph.NewCreateSpec(document.Table, sqlgraph.NewFieldSpec(document.FieldID, field.TypeUUID))
_spec = &sqlgraph.CreateSpec{
Table: document.Table,
ID: &sqlgraph.FieldSpec{
Type: field.TypeUUID,
Column: document.FieldID,
},
}
)
if id, ok := dc.mutation.ID(); ok {
_node.ID = id
@@ -238,7 +281,10 @@ func (dc *DocumentCreate) createSpec() (*Document, *sqlgraph.CreateSpec) {
Columns: []string{document.GroupColumn},
Bidi: false,
Target: &sqlgraph.EdgeTarget{
IDSpec: sqlgraph.NewFieldSpec(group.FieldID, field.TypeUUID),
IDSpec: &sqlgraph.FieldSpec{
Type: field.TypeUUID,
Column: group.FieldID,
},
},
}
for _, k := range nodes {
@@ -255,7 +301,10 @@ func (dc *DocumentCreate) createSpec() (*Document, *sqlgraph.CreateSpec) {
Columns: []string{document.AttachmentsColumn},
Bidi: false,
Target: &sqlgraph.EdgeTarget{
IDSpec: sqlgraph.NewFieldSpec(attachment.FieldID, field.TypeUUID),
IDSpec: &sqlgraph.FieldSpec{
Type: field.TypeUUID,
Column: attachment.FieldID,
},
},
}
for _, k := range nodes {

View File

@@ -4,6 +4,7 @@ package ent
import (
"context"
"fmt"
"entgo.io/ent/dialect/sql"
"entgo.io/ent/dialect/sql/sqlgraph"
@@ -27,7 +28,34 @@ func (dd *DocumentDelete) Where(ps ...predicate.Document) *DocumentDelete {
// Exec executes the deletion query and returns how many vertices were deleted.
func (dd *DocumentDelete) Exec(ctx context.Context) (int, error) {
return withHooks[int, DocumentMutation](ctx, dd.sqlExec, dd.mutation, dd.hooks)
var (
err error
affected int
)
if len(dd.hooks) == 0 {
affected, err = dd.sqlExec(ctx)
} else {
var mut Mutator = MutateFunc(func(ctx context.Context, m Mutation) (Value, error) {
mutation, ok := m.(*DocumentMutation)
if !ok {
return nil, fmt.Errorf("unexpected mutation type %T", m)
}
dd.mutation = mutation
affected, err = dd.sqlExec(ctx)
mutation.done = true
return affected, err
})
for i := len(dd.hooks) - 1; i >= 0; i-- {
if dd.hooks[i] == nil {
return 0, fmt.Errorf("ent: uninitialized hook (forgotten import ent/runtime?)")
}
mut = dd.hooks[i](mut)
}
if _, err := mut.Mutate(ctx, dd.mutation); err != nil {
return 0, err
}
}
return affected, err
}
// ExecX is like Exec, but panics if an error occurs.
@@ -40,7 +68,15 @@ func (dd *DocumentDelete) ExecX(ctx context.Context) int {
}
func (dd *DocumentDelete) sqlExec(ctx context.Context) (int, error) {
_spec := sqlgraph.NewDeleteSpec(document.Table, sqlgraph.NewFieldSpec(document.FieldID, field.TypeUUID))
_spec := &sqlgraph.DeleteSpec{
Node: &sqlgraph.NodeSpec{
Table: document.Table,
ID: &sqlgraph.FieldSpec{
Type: field.TypeUUID,
Column: document.FieldID,
},
},
}
if ps := dd.mutation.predicates; len(ps) > 0 {
_spec.Predicate = func(selector *sql.Selector) {
for i := range ps {
@@ -52,7 +88,6 @@ func (dd *DocumentDelete) sqlExec(ctx context.Context) (int, error) {
if err != nil && sqlgraph.IsConstraintError(err) {
err = &ConstraintError{msg: err.Error(), wrap: err}
}
dd.mutation.done = true
return affected, err
}
@@ -61,12 +96,6 @@ type DocumentDeleteOne struct {
dd *DocumentDelete
}
// Where appends a list predicates to the DocumentDelete builder.
func (ddo *DocumentDeleteOne) Where(ps ...predicate.Document) *DocumentDeleteOne {
ddo.dd.mutation.Where(ps...)
return ddo
}
// Exec executes the deletion query.
func (ddo *DocumentDeleteOne) Exec(ctx context.Context) error {
n, err := ddo.dd.Exec(ctx)
@@ -82,7 +111,5 @@ func (ddo *DocumentDeleteOne) Exec(ctx context.Context) error {
// ExecX is like Exec, but panics if an error occurs.
func (ddo *DocumentDeleteOne) ExecX(ctx context.Context) {
if err := ddo.Exec(ctx); err != nil {
panic(err)
}
ddo.dd.ExecX(ctx)
}

View File

@@ -21,9 +21,11 @@ import (
// DocumentQuery is the builder for querying Document entities.
type DocumentQuery struct {
config
ctx *QueryContext
limit *int
offset *int
unique *bool
order []OrderFunc
inters []Interceptor
fields []string
predicates []predicate.Document
withGroup *GroupQuery
withAttachments *AttachmentQuery
@@ -39,26 +41,26 @@ func (dq *DocumentQuery) Where(ps ...predicate.Document) *DocumentQuery {
return dq
}
// Limit the number of records to be returned by this query.
// Limit adds a limit step to the query.
func (dq *DocumentQuery) Limit(limit int) *DocumentQuery {
dq.ctx.Limit = &limit
dq.limit = &limit
return dq
}
// Offset to start from.
// Offset adds an offset step to the query.
func (dq *DocumentQuery) Offset(offset int) *DocumentQuery {
dq.ctx.Offset = &offset
dq.offset = &offset
return dq
}
// 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 (dq *DocumentQuery) Unique(unique bool) *DocumentQuery {
dq.ctx.Unique = &unique
dq.unique = &unique
return dq
}
// Order specifies how the records should be ordered.
// Order adds an order step to the query.
func (dq *DocumentQuery) Order(o ...OrderFunc) *DocumentQuery {
dq.order = append(dq.order, o...)
return dq
@@ -66,7 +68,7 @@ func (dq *DocumentQuery) Order(o ...OrderFunc) *DocumentQuery {
// QueryGroup chains the current query on the "group" edge.
func (dq *DocumentQuery) QueryGroup() *GroupQuery {
query := (&GroupClient{config: dq.config}).Query()
query := &GroupQuery{config: dq.config}
query.path = func(ctx context.Context) (fromU *sql.Selector, err error) {
if err := dq.prepareQuery(ctx); err != nil {
return nil, err
@@ -88,7 +90,7 @@ func (dq *DocumentQuery) QueryGroup() *GroupQuery {
// QueryAttachments chains the current query on the "attachments" edge.
func (dq *DocumentQuery) QueryAttachments() *AttachmentQuery {
query := (&AttachmentClient{config: dq.config}).Query()
query := &AttachmentQuery{config: dq.config}
query.path = func(ctx context.Context) (fromU *sql.Selector, err error) {
if err := dq.prepareQuery(ctx); err != nil {
return nil, err
@@ -111,7 +113,7 @@ func (dq *DocumentQuery) QueryAttachments() *AttachmentQuery {
// First returns the first Document entity from the query.
// Returns a *NotFoundError when no Document was found.
func (dq *DocumentQuery) First(ctx context.Context) (*Document, error) {
nodes, err := dq.Limit(1).All(setContextOp(ctx, dq.ctx, "First"))
nodes, err := dq.Limit(1).All(ctx)
if err != nil {
return nil, err
}
@@ -134,7 +136,7 @@ func (dq *DocumentQuery) FirstX(ctx context.Context) *Document {
// Returns a *NotFoundError when no Document ID was found.
func (dq *DocumentQuery) FirstID(ctx context.Context) (id uuid.UUID, err error) {
var ids []uuid.UUID
if ids, err = dq.Limit(1).IDs(setContextOp(ctx, dq.ctx, "FirstID")); err != nil {
if ids, err = dq.Limit(1).IDs(ctx); err != nil {
return
}
if len(ids) == 0 {
@@ -157,7 +159,7 @@ func (dq *DocumentQuery) FirstIDX(ctx context.Context) uuid.UUID {
// Returns a *NotSingularError when more than one Document entity is found.
// Returns a *NotFoundError when no Document entities are found.
func (dq *DocumentQuery) Only(ctx context.Context) (*Document, error) {
nodes, err := dq.Limit(2).All(setContextOp(ctx, dq.ctx, "Only"))
nodes, err := dq.Limit(2).All(ctx)
if err != nil {
return nil, err
}
@@ -185,7 +187,7 @@ func (dq *DocumentQuery) OnlyX(ctx context.Context) *Document {
// Returns a *NotFoundError when no entities are found.
func (dq *DocumentQuery) OnlyID(ctx context.Context) (id uuid.UUID, err error) {
var ids []uuid.UUID
if ids, err = dq.Limit(2).IDs(setContextOp(ctx, dq.ctx, "OnlyID")); err != nil {
if ids, err = dq.Limit(2).IDs(ctx); err != nil {
return
}
switch len(ids) {
@@ -210,12 +212,10 @@ func (dq *DocumentQuery) OnlyIDX(ctx context.Context) uuid.UUID {
// All executes the query and returns a list of Documents.
func (dq *DocumentQuery) All(ctx context.Context) ([]*Document, error) {
ctx = setContextOp(ctx, dq.ctx, "All")
if err := dq.prepareQuery(ctx); err != nil {
return nil, err
}
qr := querierAll[[]*Document, *DocumentQuery]()
return withInterceptors[[]*Document](ctx, dq, qr, dq.inters)
return dq.sqlAll(ctx)
}
// AllX is like All, but panics if an error occurs.
@@ -228,12 +228,9 @@ func (dq *DocumentQuery) AllX(ctx context.Context) []*Document {
}
// IDs executes the query and returns a list of Document IDs.
func (dq *DocumentQuery) IDs(ctx context.Context) (ids []uuid.UUID, err error) {
if dq.ctx.Unique == nil && dq.path != nil {
dq.Unique(true)
}
ctx = setContextOp(ctx, dq.ctx, "IDs")
if err = dq.Select(document.FieldID).Scan(ctx, &ids); err != nil {
func (dq *DocumentQuery) IDs(ctx context.Context) ([]uuid.UUID, error) {
var ids []uuid.UUID
if err := dq.Select(document.FieldID).Scan(ctx, &ids); err != nil {
return nil, err
}
return ids, nil
@@ -250,11 +247,10 @@ func (dq *DocumentQuery) IDsX(ctx context.Context) []uuid.UUID {
// Count returns the count of the given query.
func (dq *DocumentQuery) Count(ctx context.Context) (int, error) {
ctx = setContextOp(ctx, dq.ctx, "Count")
if err := dq.prepareQuery(ctx); err != nil {
return 0, err
}
return withInterceptors[int](ctx, dq, querierCount[*DocumentQuery](), dq.inters)
return dq.sqlCount(ctx)
}
// CountX is like Count, but panics if an error occurs.
@@ -268,15 +264,10 @@ func (dq *DocumentQuery) CountX(ctx context.Context) int {
// Exist returns true if the query has elements in the graph.
func (dq *DocumentQuery) Exist(ctx context.Context) (bool, error) {
ctx = setContextOp(ctx, dq.ctx, "Exist")
switch _, err := dq.FirstID(ctx); {
case IsNotFound(err):
return false, nil
case err != nil:
return false, fmt.Errorf("ent: check existence: %w", err)
default:
return true, nil
if err := dq.prepareQuery(ctx); err != nil {
return false, err
}
return dq.sqlExist(ctx)
}
// ExistX is like Exist, but panics if an error occurs.
@@ -296,22 +287,23 @@ func (dq *DocumentQuery) Clone() *DocumentQuery {
}
return &DocumentQuery{
config: dq.config,
ctx: dq.ctx.Clone(),
limit: dq.limit,
offset: dq.offset,
order: append([]OrderFunc{}, dq.order...),
inters: append([]Interceptor{}, dq.inters...),
predicates: append([]predicate.Document{}, dq.predicates...),
withGroup: dq.withGroup.Clone(),
withAttachments: dq.withAttachments.Clone(),
// clone intermediate query.
sql: dq.sql.Clone(),
path: dq.path,
sql: dq.sql.Clone(),
path: dq.path,
unique: dq.unique,
}
}
// WithGroup tells the query-builder to eager-load the nodes that are connected to
// the "group" edge. The optional arguments are used to configure the query builder of the edge.
func (dq *DocumentQuery) WithGroup(opts ...func(*GroupQuery)) *DocumentQuery {
query := (&GroupClient{config: dq.config}).Query()
query := &GroupQuery{config: dq.config}
for _, opt := range opts {
opt(query)
}
@@ -322,7 +314,7 @@ func (dq *DocumentQuery) WithGroup(opts ...func(*GroupQuery)) *DocumentQuery {
// 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 {
query := (&AttachmentClient{config: dq.config}).Query()
query := &AttachmentQuery{config: dq.config}
for _, opt := range opts {
opt(query)
}
@@ -345,11 +337,16 @@ func (dq *DocumentQuery) WithAttachments(opts ...func(*AttachmentQuery)) *Docume
// Aggregate(ent.Count()).
// Scan(ctx, &v)
func (dq *DocumentQuery) GroupBy(field string, fields ...string) *DocumentGroupBy {
dq.ctx.Fields = append([]string{field}, fields...)
grbuild := &DocumentGroupBy{build: dq}
grbuild.flds = &dq.ctx.Fields
grbuild := &DocumentGroupBy{config: dq.config}
grbuild.fields = append([]string{field}, fields...)
grbuild.path = func(ctx context.Context) (prev *sql.Selector, err error) {
if err := dq.prepareQuery(ctx); err != nil {
return nil, err
}
return dq.sqlQuery(ctx), nil
}
grbuild.label = document.Label
grbuild.scan = grbuild.Scan
grbuild.flds, grbuild.scan = &grbuild.fields, grbuild.Scan
return grbuild
}
@@ -366,11 +363,11 @@ func (dq *DocumentQuery) GroupBy(field string, fields ...string) *DocumentGroupB
// Select(document.FieldCreatedAt).
// Scan(ctx, &v)
func (dq *DocumentQuery) Select(fields ...string) *DocumentSelect {
dq.ctx.Fields = append(dq.ctx.Fields, fields...)
sbuild := &DocumentSelect{DocumentQuery: dq}
sbuild.label = document.Label
sbuild.flds, sbuild.scan = &dq.ctx.Fields, sbuild.Scan
return sbuild
dq.fields = append(dq.fields, fields...)
selbuild := &DocumentSelect{DocumentQuery: dq}
selbuild.label = document.Label
selbuild.flds, selbuild.scan = &dq.fields, selbuild.Scan
return selbuild
}
// Aggregate returns a DocumentSelect configured with the given aggregations.
@@ -379,17 +376,7 @@ func (dq *DocumentQuery) Aggregate(fns ...AggregateFunc) *DocumentSelect {
}
func (dq *DocumentQuery) prepareQuery(ctx context.Context) error {
for _, inter := range dq.inters {
if inter == nil {
return fmt.Errorf("ent: uninitialized interceptor (forgotten import ent/runtime?)")
}
if trv, ok := inter.(Traverser); ok {
if err := trv.Traverse(ctx, dq); err != nil {
return err
}
}
}
for _, f := range dq.ctx.Fields {
for _, f := range dq.fields {
if !document.ValidColumn(f) {
return &ValidationError{Name: f, err: fmt.Errorf("ent: invalid field %q for query", f)}
}
@@ -467,9 +454,6 @@ func (dq *DocumentQuery) loadGroup(ctx context.Context, query *GroupQuery, nodes
}
nodeids[fk] = append(nodeids[fk], nodes[i])
}
if len(ids) == 0 {
return nil
}
query.Where(group.IDIn(ids...))
neighbors, err := query.All(ctx)
if err != nil {
@@ -520,22 +504,41 @@ func (dq *DocumentQuery) loadAttachments(ctx context.Context, query *AttachmentQ
func (dq *DocumentQuery) sqlCount(ctx context.Context) (int, error) {
_spec := dq.querySpec()
_spec.Node.Columns = dq.ctx.Fields
if len(dq.ctx.Fields) > 0 {
_spec.Unique = dq.ctx.Unique != nil && *dq.ctx.Unique
_spec.Node.Columns = dq.fields
if len(dq.fields) > 0 {
_spec.Unique = dq.unique != nil && *dq.unique
}
return sqlgraph.CountNodes(ctx, dq.driver, _spec)
}
func (dq *DocumentQuery) querySpec() *sqlgraph.QuerySpec {
_spec := sqlgraph.NewQuerySpec(document.Table, document.Columns, sqlgraph.NewFieldSpec(document.FieldID, field.TypeUUID))
_spec.From = dq.sql
if unique := dq.ctx.Unique; unique != nil {
_spec.Unique = *unique
} else if dq.path != nil {
_spec.Unique = true
func (dq *DocumentQuery) sqlExist(ctx context.Context) (bool, error) {
switch _, err := dq.FirstID(ctx); {
case IsNotFound(err):
return false, nil
case err != nil:
return false, fmt.Errorf("ent: check existence: %w", err)
default:
return true, nil
}
if fields := dq.ctx.Fields; len(fields) > 0 {
}
func (dq *DocumentQuery) querySpec() *sqlgraph.QuerySpec {
_spec := &sqlgraph.QuerySpec{
Node: &sqlgraph.NodeSpec{
Table: document.Table,
Columns: document.Columns,
ID: &sqlgraph.FieldSpec{
Type: field.TypeUUID,
Column: document.FieldID,
},
},
From: dq.sql,
Unique: true,
}
if unique := dq.unique; unique != nil {
_spec.Unique = *unique
}
if fields := dq.fields; len(fields) > 0 {
_spec.Node.Columns = make([]string, 0, len(fields))
_spec.Node.Columns = append(_spec.Node.Columns, document.FieldID)
for i := range fields {
@@ -551,10 +554,10 @@ func (dq *DocumentQuery) querySpec() *sqlgraph.QuerySpec {
}
}
}
if limit := dq.ctx.Limit; limit != nil {
if limit := dq.limit; limit != nil {
_spec.Limit = *limit
}
if offset := dq.ctx.Offset; offset != nil {
if offset := dq.offset; offset != nil {
_spec.Offset = *offset
}
if ps := dq.order; len(ps) > 0 {
@@ -570,7 +573,7 @@ func (dq *DocumentQuery) querySpec() *sqlgraph.QuerySpec {
func (dq *DocumentQuery) sqlQuery(ctx context.Context) *sql.Selector {
builder := sql.Dialect(dq.driver.Dialect())
t1 := builder.Table(document.Table)
columns := dq.ctx.Fields
columns := dq.fields
if len(columns) == 0 {
columns = document.Columns
}
@@ -579,7 +582,7 @@ func (dq *DocumentQuery) sqlQuery(ctx context.Context) *sql.Selector {
selector = dq.sql
selector.Select(selector.Columns(columns...)...)
}
if dq.ctx.Unique != nil && *dq.ctx.Unique {
if dq.unique != nil && *dq.unique {
selector.Distinct()
}
for _, p := range dq.predicates {
@@ -588,12 +591,12 @@ func (dq *DocumentQuery) sqlQuery(ctx context.Context) *sql.Selector {
for _, p := range dq.order {
p(selector)
}
if offset := dq.ctx.Offset; offset != nil {
if offset := dq.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 := dq.ctx.Limit; limit != nil {
if limit := dq.limit; limit != nil {
selector.Limit(*limit)
}
return selector
@@ -601,8 +604,13 @@ func (dq *DocumentQuery) sqlQuery(ctx context.Context) *sql.Selector {
// DocumentGroupBy is the group-by builder for Document entities.
type DocumentGroupBy struct {
config
selector
build *DocumentQuery
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.
@@ -611,46 +619,58 @@ func (dgb *DocumentGroupBy) Aggregate(fns ...AggregateFunc) *DocumentGroupBy {
return dgb
}
// Scan applies the selector query and scans the result into the given value.
// Scan applies the group-by query and scans the result into the given value.
func (dgb *DocumentGroupBy) Scan(ctx context.Context, v any) error {
ctx = setContextOp(ctx, dgb.build.ctx, "GroupBy")
if err := dgb.build.prepareQuery(ctx); err != nil {
query, err := dgb.path(ctx)
if err != nil {
return err
}
return scanWithInterceptors[*DocumentQuery, *DocumentGroupBy](ctx, dgb.build, dgb, dgb.build.inters, v)
dgb.sql = query
return dgb.sqlScan(ctx, v)
}
func (dgb *DocumentGroupBy) sqlScan(ctx context.Context, root *DocumentQuery, v any) error {
selector := root.sqlQuery(ctx).Select()
aggregation := make([]string, 0, len(dgb.fns))
for _, fn := range dgb.fns {
aggregation = append(aggregation, fn(selector))
}
if len(selector.SelectedColumns()) == 0 {
columns := make([]string, 0, len(*dgb.flds)+len(dgb.fns))
for _, f := range *dgb.flds {
columns = append(columns, selector.C(f))
func (dgb *DocumentGroupBy) sqlScan(ctx context.Context, v any) error {
for _, f := range dgb.fields {
if !document.ValidColumn(f) {
return &ValidationError{Name: f, err: fmt.Errorf("invalid field %q for group-by", f)}
}
columns = append(columns, aggregation...)
selector.Select(columns...)
}
selector.GroupBy(selector.Columns(*dgb.flds...)...)
selector := dgb.sqlQuery()
if err := selector.Err(); err != nil {
return err
}
rows := &sql.Rows{}
query, args := selector.Query()
if err := dgb.build.driver.Query(ctx, query, args, rows); err != nil {
if err := dgb.driver.Query(ctx, query, args, rows); err != nil {
return err
}
defer rows.Close()
return sql.ScanSlice(rows, v)
}
func (dgb *DocumentGroupBy) sqlQuery() *sql.Selector {
selector := dgb.sql.Select()
aggregation := make([]string, 0, len(dgb.fns))
for _, fn := range dgb.fns {
aggregation = append(aggregation, fn(selector))
}
if len(selector.SelectedColumns()) == 0 {
columns := make([]string, 0, len(dgb.fields)+len(dgb.fns))
for _, f := range dgb.fields {
columns = append(columns, selector.C(f))
}
columns = append(columns, aggregation...)
selector.Select(columns...)
}
return selector.GroupBy(selector.Columns(dgb.fields...)...)
}
// DocumentSelect is the builder for selecting fields of Document entities.
type DocumentSelect struct {
*DocumentQuery
selector
// intermediate query (i.e. traversal path).
sql *sql.Selector
}
// Aggregate adds the given aggregation functions to the selector query.
@@ -661,27 +681,26 @@ func (ds *DocumentSelect) Aggregate(fns ...AggregateFunc) *DocumentSelect {
// Scan applies the selector query and scans the result into the given value.
func (ds *DocumentSelect) Scan(ctx context.Context, v any) error {
ctx = setContextOp(ctx, ds.ctx, "Select")
if err := ds.prepareQuery(ctx); err != nil {
return err
}
return scanWithInterceptors[*DocumentQuery, *DocumentSelect](ctx, ds.DocumentQuery, ds, ds.inters, v)
ds.sql = ds.DocumentQuery.sqlQuery(ctx)
return ds.sqlScan(ctx, v)
}
func (ds *DocumentSelect) sqlScan(ctx context.Context, root *DocumentQuery, v any) error {
selector := root.sqlQuery(ctx)
func (ds *DocumentSelect) sqlScan(ctx context.Context, v any) error {
aggregation := make([]string, 0, len(ds.fns))
for _, fn := range ds.fns {
aggregation = append(aggregation, fn(selector))
aggregation = append(aggregation, fn(ds.sql))
}
switch n := len(*ds.selector.flds); {
case n == 0 && len(aggregation) > 0:
selector.Select(aggregation...)
ds.sql.Select(aggregation...)
case n != 0 && len(aggregation) > 0:
selector.AppendSelect(aggregation...)
ds.sql.AppendSelect(aggregation...)
}
rows := &sql.Rows{}
query, args := selector.Query()
query, args := ds.sql.Query()
if err := ds.driver.Query(ctx, query, args, rows); err != nil {
return err
}

View File

@@ -109,8 +109,41 @@ func (du *DocumentUpdate) RemoveAttachments(a ...*Attachment) *DocumentUpdate {
// Save executes the query and returns the number of nodes affected by the update operation.
func (du *DocumentUpdate) Save(ctx context.Context) (int, error) {
var (
err error
affected int
)
du.defaults()
return withHooks[int, DocumentMutation](ctx, du.sqlSave, du.mutation, du.hooks)
if len(du.hooks) == 0 {
if err = du.check(); err != nil {
return 0, err
}
affected, err = du.sqlSave(ctx)
} else {
var mut Mutator = MutateFunc(func(ctx context.Context, m Mutation) (Value, error) {
mutation, ok := m.(*DocumentMutation)
if !ok {
return nil, fmt.Errorf("unexpected mutation type %T", m)
}
if err = du.check(); err != nil {
return 0, err
}
du.mutation = mutation
affected, err = du.sqlSave(ctx)
mutation.done = true
return affected, err
})
for i := len(du.hooks) - 1; i >= 0; i-- {
if du.hooks[i] == nil {
return 0, fmt.Errorf("ent: uninitialized hook (forgotten import ent/runtime?)")
}
mut = du.hooks[i](mut)
}
if _, err := mut.Mutate(ctx, du.mutation); err != nil {
return 0, err
}
}
return affected, err
}
// SaveX is like Save, but panics if an error occurs.
@@ -162,10 +195,16 @@ func (du *DocumentUpdate) check() error {
}
func (du *DocumentUpdate) sqlSave(ctx context.Context) (n int, err error) {
if err := du.check(); err != nil {
return n, err
_spec := &sqlgraph.UpdateSpec{
Node: &sqlgraph.NodeSpec{
Table: document.Table,
Columns: document.Columns,
ID: &sqlgraph.FieldSpec{
Type: field.TypeUUID,
Column: document.FieldID,
},
},
}
_spec := sqlgraph.NewUpdateSpec(document.Table, document.Columns, sqlgraph.NewFieldSpec(document.FieldID, field.TypeUUID))
if ps := du.mutation.predicates; len(ps) > 0 {
_spec.Predicate = func(selector *sql.Selector) {
for i := range ps {
@@ -190,7 +229,10 @@ func (du *DocumentUpdate) sqlSave(ctx context.Context) (n int, err error) {
Columns: []string{document.GroupColumn},
Bidi: false,
Target: &sqlgraph.EdgeTarget{
IDSpec: sqlgraph.NewFieldSpec(group.FieldID, field.TypeUUID),
IDSpec: &sqlgraph.FieldSpec{
Type: field.TypeUUID,
Column: group.FieldID,
},
},
}
_spec.Edges.Clear = append(_spec.Edges.Clear, edge)
@@ -203,7 +245,10 @@ func (du *DocumentUpdate) sqlSave(ctx context.Context) (n int, err error) {
Columns: []string{document.GroupColumn},
Bidi: false,
Target: &sqlgraph.EdgeTarget{
IDSpec: sqlgraph.NewFieldSpec(group.FieldID, field.TypeUUID),
IDSpec: &sqlgraph.FieldSpec{
Type: field.TypeUUID,
Column: group.FieldID,
},
},
}
for _, k := range nodes {
@@ -219,7 +264,10 @@ func (du *DocumentUpdate) sqlSave(ctx context.Context) (n int, err error) {
Columns: []string{document.AttachmentsColumn},
Bidi: false,
Target: &sqlgraph.EdgeTarget{
IDSpec: sqlgraph.NewFieldSpec(attachment.FieldID, field.TypeUUID),
IDSpec: &sqlgraph.FieldSpec{
Type: field.TypeUUID,
Column: attachment.FieldID,
},
},
}
_spec.Edges.Clear = append(_spec.Edges.Clear, edge)
@@ -232,7 +280,10 @@ func (du *DocumentUpdate) sqlSave(ctx context.Context) (n int, err error) {
Columns: []string{document.AttachmentsColumn},
Bidi: false,
Target: &sqlgraph.EdgeTarget{
IDSpec: sqlgraph.NewFieldSpec(attachment.FieldID, field.TypeUUID),
IDSpec: &sqlgraph.FieldSpec{
Type: field.TypeUUID,
Column: attachment.FieldID,
},
},
}
for _, k := range nodes {
@@ -248,7 +299,10 @@ func (du *DocumentUpdate) sqlSave(ctx context.Context) (n int, err error) {
Columns: []string{document.AttachmentsColumn},
Bidi: false,
Target: &sqlgraph.EdgeTarget{
IDSpec: sqlgraph.NewFieldSpec(attachment.FieldID, field.TypeUUID),
IDSpec: &sqlgraph.FieldSpec{
Type: field.TypeUUID,
Column: attachment.FieldID,
},
},
}
for _, k := range nodes {
@@ -264,7 +318,6 @@ func (du *DocumentUpdate) sqlSave(ctx context.Context) (n int, err error) {
}
return 0, err
}
du.mutation.done = true
return n, nil
}
@@ -352,12 +405,6 @@ func (duo *DocumentUpdateOne) RemoveAttachments(a ...*Attachment) *DocumentUpdat
return duo.RemoveAttachmentIDs(ids...)
}
// Where appends a list predicates to the DocumentUpdate builder.
func (duo *DocumentUpdateOne) Where(ps ...predicate.Document) *DocumentUpdateOne {
duo.mutation.Where(ps...)
return duo
}
// Select allows selecting one or more fields (columns) of the returned entity.
// The default is selecting all fields defined in the entity schema.
func (duo *DocumentUpdateOne) Select(field string, fields ...string) *DocumentUpdateOne {
@@ -367,8 +414,47 @@ func (duo *DocumentUpdateOne) Select(field string, fields ...string) *DocumentUp
// Save executes the query and returns the updated Document entity.
func (duo *DocumentUpdateOne) Save(ctx context.Context) (*Document, error) {
var (
err error
node *Document
)
duo.defaults()
return withHooks[*Document, DocumentMutation](ctx, duo.sqlSave, duo.mutation, duo.hooks)
if len(duo.hooks) == 0 {
if err = duo.check(); err != nil {
return nil, err
}
node, err = duo.sqlSave(ctx)
} else {
var mut Mutator = MutateFunc(func(ctx context.Context, m Mutation) (Value, error) {
mutation, ok := m.(*DocumentMutation)
if !ok {
return nil, fmt.Errorf("unexpected mutation type %T", m)
}
if err = duo.check(); err != nil {
return nil, err
}
duo.mutation = mutation
node, err = duo.sqlSave(ctx)
mutation.done = true
return node, err
})
for i := len(duo.hooks) - 1; i >= 0; i-- {
if duo.hooks[i] == nil {
return nil, fmt.Errorf("ent: uninitialized hook (forgotten import ent/runtime?)")
}
mut = duo.hooks[i](mut)
}
v, err := mut.Mutate(ctx, duo.mutation)
if err != nil {
return nil, err
}
nv, ok := v.(*Document)
if !ok {
return nil, fmt.Errorf("unexpected node type %T returned from DocumentMutation", v)
}
node = nv
}
return node, err
}
// SaveX is like Save, but panics if an error occurs.
@@ -420,10 +506,16 @@ func (duo *DocumentUpdateOne) check() error {
}
func (duo *DocumentUpdateOne) sqlSave(ctx context.Context) (_node *Document, err error) {
if err := duo.check(); err != nil {
return _node, err
_spec := &sqlgraph.UpdateSpec{
Node: &sqlgraph.NodeSpec{
Table: document.Table,
Columns: document.Columns,
ID: &sqlgraph.FieldSpec{
Type: field.TypeUUID,
Column: document.FieldID,
},
},
}
_spec := sqlgraph.NewUpdateSpec(document.Table, document.Columns, sqlgraph.NewFieldSpec(document.FieldID, field.TypeUUID))
id, ok := duo.mutation.ID()
if !ok {
return nil, &ValidationError{Name: "id", err: errors.New(`ent: missing "Document.id" for update`)}
@@ -465,7 +557,10 @@ func (duo *DocumentUpdateOne) sqlSave(ctx context.Context) (_node *Document, err
Columns: []string{document.GroupColumn},
Bidi: false,
Target: &sqlgraph.EdgeTarget{
IDSpec: sqlgraph.NewFieldSpec(group.FieldID, field.TypeUUID),
IDSpec: &sqlgraph.FieldSpec{
Type: field.TypeUUID,
Column: group.FieldID,
},
},
}
_spec.Edges.Clear = append(_spec.Edges.Clear, edge)
@@ -478,7 +573,10 @@ func (duo *DocumentUpdateOne) sqlSave(ctx context.Context) (_node *Document, err
Columns: []string{document.GroupColumn},
Bidi: false,
Target: &sqlgraph.EdgeTarget{
IDSpec: sqlgraph.NewFieldSpec(group.FieldID, field.TypeUUID),
IDSpec: &sqlgraph.FieldSpec{
Type: field.TypeUUID,
Column: group.FieldID,
},
},
}
for _, k := range nodes {
@@ -494,7 +592,10 @@ func (duo *DocumentUpdateOne) sqlSave(ctx context.Context) (_node *Document, err
Columns: []string{document.AttachmentsColumn},
Bidi: false,
Target: &sqlgraph.EdgeTarget{
IDSpec: sqlgraph.NewFieldSpec(attachment.FieldID, field.TypeUUID),
IDSpec: &sqlgraph.FieldSpec{
Type: field.TypeUUID,
Column: attachment.FieldID,
},
},
}
_spec.Edges.Clear = append(_spec.Edges.Clear, edge)
@@ -507,7 +608,10 @@ func (duo *DocumentUpdateOne) sqlSave(ctx context.Context) (_node *Document, err
Columns: []string{document.AttachmentsColumn},
Bidi: false,
Target: &sqlgraph.EdgeTarget{
IDSpec: sqlgraph.NewFieldSpec(attachment.FieldID, field.TypeUUID),
IDSpec: &sqlgraph.FieldSpec{
Type: field.TypeUUID,
Column: attachment.FieldID,
},
},
}
for _, k := range nodes {
@@ -523,7 +627,10 @@ func (duo *DocumentUpdateOne) sqlSave(ctx context.Context) (_node *Document, err
Columns: []string{document.AttachmentsColumn},
Bidi: false,
Target: &sqlgraph.EdgeTarget{
IDSpec: sqlgraph.NewFieldSpec(attachment.FieldID, field.TypeUUID),
IDSpec: &sqlgraph.FieldSpec{
Type: field.TypeUUID,
Column: attachment.FieldID,
},
},
}
for _, k := range nodes {
@@ -542,6 +649,5 @@ func (duo *DocumentUpdateOne) sqlSave(ctx context.Context) (_node *Document, err
}
return nil, err
}
duo.mutation.done = true
return _node, nil
}

View File

@@ -6,7 +6,6 @@ import (
"context"
"errors"
"fmt"
"reflect"
"entgo.io/ent"
"entgo.io/ent/dialect/sql"
@@ -22,55 +21,21 @@ import (
"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/notifier"
"github.com/hay-kot/homebox/backend/internal/data/ent/user"
)
// ent aliases to avoid import conflicts in user's code.
type (
Op = ent.Op
Hook = ent.Hook
Value = ent.Value
Query = ent.Query
QueryContext = ent.QueryContext
Querier = ent.Querier
QuerierFunc = ent.QuerierFunc
Interceptor = ent.Interceptor
InterceptFunc = ent.InterceptFunc
Traverser = ent.Traverser
TraverseFunc = ent.TraverseFunc
Policy = ent.Policy
Mutator = ent.Mutator
Mutation = ent.Mutation
MutateFunc = ent.MutateFunc
Op = ent.Op
Hook = ent.Hook
Value = ent.Value
Query = ent.Query
Policy = ent.Policy
Mutator = ent.Mutator
Mutation = ent.Mutation
MutateFunc = ent.MutateFunc
)
type clientCtxKey struct{}
// FromContext returns a Client stored inside a context, or nil if there isn't one.
func FromContext(ctx context.Context) *Client {
c, _ := ctx.Value(clientCtxKey{}).(*Client)
return c
}
// NewContext returns a new context with the given Client attached.
func NewContext(parent context.Context, c *Client) context.Context {
return context.WithValue(parent, clientCtxKey{}, c)
}
type txCtxKey struct{}
// TxFromContext returns a Tx stored inside a context, or nil if there isn't one.
func TxFromContext(ctx context.Context) *Tx {
tx, _ := ctx.Value(txCtxKey{}).(*Tx)
return tx
}
// NewTxContext returns a new context with the given Tx attached.
func NewTxContext(parent context.Context, tx *Tx) context.Context {
return context.WithValue(parent, txCtxKey{}, tx)
}
// OrderFunc applies an ordering on the sql selector.
type OrderFunc func(*sql.Selector)
@@ -88,7 +53,6 @@ func columnChecker(table string) func(string) error {
label.Table: label.ValidColumn,
location.Table: location.ValidColumn,
maintenanceentry.Table: maintenanceentry.ValidColumn,
notifier.Table: notifier.ValidColumn,
user.Table: user.ValidColumn,
}
check, ok := checks[table]
@@ -520,121 +484,5 @@ func (s *selector) BoolX(ctx context.Context) bool {
return v
}
// withHooks invokes the builder operation with the given hooks, if any.
func withHooks[V Value, M any, PM interface {
*M
Mutation
}](ctx context.Context, exec func(context.Context) (V, error), mutation PM, hooks []Hook) (value V, err error) {
if len(hooks) == 0 {
return exec(ctx)
}
var mut Mutator = MutateFunc(func(ctx context.Context, m Mutation) (Value, error) {
mutationT, ok := any(m).(PM)
if !ok {
return nil, fmt.Errorf("unexpected mutation type %T", m)
}
// Set the mutation to the builder.
*mutation = *mutationT
return exec(ctx)
})
for i := len(hooks) - 1; i >= 0; i-- {
if hooks[i] == nil {
return value, fmt.Errorf("ent: uninitialized hook (forgotten import ent/runtime?)")
}
mut = hooks[i](mut)
}
v, err := mut.Mutate(ctx, mutation)
if err != nil {
return value, err
}
nv, ok := v.(V)
if !ok {
return value, fmt.Errorf("unexpected node type %T returned from %T", v, mutation)
}
return nv, nil
}
// setContextOp returns a new context with the given QueryContext attached (including its op) in case it does not exist.
func setContextOp(ctx context.Context, qc *QueryContext, op string) context.Context {
if ent.QueryFromContext(ctx) == nil {
qc.Op = op
ctx = ent.NewQueryContext(ctx, qc)
}
return ctx
}
func querierAll[V Value, Q interface {
sqlAll(context.Context, ...queryHook) (V, error)
}]() Querier {
return QuerierFunc(func(ctx context.Context, q Query) (Value, error) {
query, ok := q.(Q)
if !ok {
return nil, fmt.Errorf("unexpected query type %T", q)
}
return query.sqlAll(ctx)
})
}
func querierCount[Q interface {
sqlCount(context.Context) (int, error)
}]() Querier {
return QuerierFunc(func(ctx context.Context, q Query) (Value, error) {
query, ok := q.(Q)
if !ok {
return nil, fmt.Errorf("unexpected query type %T", q)
}
return query.sqlCount(ctx)
})
}
func withInterceptors[V Value](ctx context.Context, q Query, qr Querier, inters []Interceptor) (v V, err error) {
for i := len(inters) - 1; i >= 0; i-- {
qr = inters[i].Intercept(qr)
}
rv, err := qr.Query(ctx, q)
if err != nil {
return v, err
}
vt, ok := rv.(V)
if !ok {
return v, fmt.Errorf("unexpected type %T returned from %T. expected type: %T", vt, q, v)
}
return vt, nil
}
func scanWithInterceptors[Q1 ent.Query, Q2 interface {
sqlScan(context.Context, Q1, any) error
}](ctx context.Context, rootQuery Q1, selectOrGroup Q2, inters []Interceptor, v any) error {
rv := reflect.ValueOf(v)
var qr Querier = QuerierFunc(func(ctx context.Context, q Query) (Value, error) {
query, ok := q.(Q1)
if !ok {
return nil, fmt.Errorf("unexpected query type %T", q)
}
if err := selectOrGroup.sqlScan(ctx, query, v); err != nil {
return nil, err
}
if k := rv.Kind(); k == reflect.Pointer && rv.Elem().CanInterface() {
return rv.Elem().Interface(), nil
}
return v, nil
})
for i := len(inters) - 1; i >= 0; i-- {
qr = inters[i].Intercept(qr)
}
vv, err := qr.Query(ctx, rootQuery)
if err != nil {
return err
}
switch rv2 := reflect.ValueOf(vv); {
case rv.IsNil(), rv2.IsNil(), rv.Kind() != reflect.Pointer:
case rv.Type() == rv2.Type():
rv.Elem().Set(rv2.Elem())
case rv.Elem().Type() == rv2.Type():
rv.Elem().Set(rv2)
}
return nil
}
// queryHook describes an internal hook for the different sqlAll methods.
type queryHook func(context.Context, *sqlgraph.QuerySpec)

View File

@@ -44,11 +44,9 @@ type GroupEdges struct {
Documents []*Document `json:"documents,omitempty"`
// InvitationTokens holds the value of the invitation_tokens edge.
InvitationTokens []*GroupInvitationToken `json:"invitation_tokens,omitempty"`
// Notifiers holds the value of the notifiers edge.
Notifiers []*Notifier `json:"notifiers,omitempty"`
// loadedTypes holds the information for reporting if a
// type was loaded (or requested) in eager-loading or not.
loadedTypes [7]bool
loadedTypes [6]bool
}
// UsersOrErr returns the Users value or an error if the edge
@@ -105,15 +103,6 @@ func (e GroupEdges) InvitationTokensOrErr() ([]*GroupInvitationToken, error) {
return nil, &NotLoadedError{edge: "invitation_tokens"}
}
// NotifiersOrErr returns the Notifiers value or an error if the edge
// was not loaded in eager-loading.
func (e GroupEdges) NotifiersOrErr() ([]*Notifier, error) {
if e.loadedTypes[6] {
return e.Notifiers, nil
}
return nil, &NotLoadedError{edge: "notifiers"}
}
// scanValues returns the types for scanning values from sql.Rows.
func (*Group) scanValues(columns []string) ([]any, error) {
values := make([]any, len(columns))
@@ -177,44 +166,39 @@ func (gr *Group) assignValues(columns []string, values []any) error {
// QueryUsers queries the "users" edge of the Group entity.
func (gr *Group) QueryUsers() *UserQuery {
return NewGroupClient(gr.config).QueryUsers(gr)
return (&GroupClient{config: gr.config}).QueryUsers(gr)
}
// QueryLocations queries the "locations" edge of the Group entity.
func (gr *Group) QueryLocations() *LocationQuery {
return NewGroupClient(gr.config).QueryLocations(gr)
return (&GroupClient{config: gr.config}).QueryLocations(gr)
}
// QueryItems queries the "items" edge of the Group entity.
func (gr *Group) QueryItems() *ItemQuery {
return NewGroupClient(gr.config).QueryItems(gr)
return (&GroupClient{config: gr.config}).QueryItems(gr)
}
// QueryLabels queries the "labels" edge of the Group entity.
func (gr *Group) QueryLabels() *LabelQuery {
return NewGroupClient(gr.config).QueryLabels(gr)
return (&GroupClient{config: gr.config}).QueryLabels(gr)
}
// QueryDocuments queries the "documents" edge of the Group entity.
func (gr *Group) QueryDocuments() *DocumentQuery {
return NewGroupClient(gr.config).QueryDocuments(gr)
return (&GroupClient{config: gr.config}).QueryDocuments(gr)
}
// QueryInvitationTokens queries the "invitation_tokens" edge of the Group entity.
func (gr *Group) QueryInvitationTokens() *GroupInvitationTokenQuery {
return NewGroupClient(gr.config).QueryInvitationTokens(gr)
}
// QueryNotifiers queries the "notifiers" edge of the Group entity.
func (gr *Group) QueryNotifiers() *NotifierQuery {
return NewGroupClient(gr.config).QueryNotifiers(gr)
return (&GroupClient{config: gr.config}).QueryInvitationTokens(gr)
}
// Update returns a builder for updating this Group.
// Note that you need to call Group.Unwrap() before calling this method if this Group
// was returned from a transaction, and the transaction was committed or rolled back.
func (gr *Group) Update() *GroupUpdateOne {
return NewGroupClient(gr.config).UpdateOne(gr)
return (&GroupClient{config: gr.config}).UpdateOne(gr)
}
// Unwrap unwraps the Group entity that was returned from a transaction after it was closed,
@@ -250,3 +234,9 @@ func (gr *Group) String() string {
// Groups is a parsable slice of Group.
type Groups []*Group
func (gr Groups) config(cfg config) {
for _i := range gr {
gr[_i].config = cfg
}
}

View File

@@ -34,8 +34,6 @@ const (
EdgeDocuments = "documents"
// EdgeInvitationTokens holds the string denoting the invitation_tokens edge name in mutations.
EdgeInvitationTokens = "invitation_tokens"
// EdgeNotifiers holds the string denoting the notifiers edge name in mutations.
EdgeNotifiers = "notifiers"
// Table holds the table name of the group in the database.
Table = "groups"
// UsersTable is the table that holds the users relation/edge.
@@ -80,13 +78,6 @@ const (
InvitationTokensInverseTable = "group_invitation_tokens"
// InvitationTokensColumn is the table column denoting the invitation_tokens relation/edge.
InvitationTokensColumn = "group_invitation_tokens"
// NotifiersTable is the table that holds the notifiers relation/edge.
NotifiersTable = "notifiers"
// NotifiersInverseTable is the table name for the Notifier entity.
// It exists in this package in order to avoid circular dependency with the "notifier" package.
NotifiersInverseTable = "notifiers"
// NotifiersColumn is the table column denoting the notifiers relation/edge.
NotifiersColumn = "group_id"
)
// Columns holds all SQL columns for group fields.
@@ -136,17 +127,10 @@ const (
CurrencyZar Currency = "zar"
CurrencyAud Currency = "aud"
CurrencyNok Currency = "nok"
CurrencyNzd Currency = "nzd"
CurrencySek Currency = "sek"
CurrencyDkk Currency = "dkk"
CurrencyInr Currency = "inr"
CurrencyRmb Currency = "rmb"
CurrencyBgn Currency = "bgn"
CurrencyChf Currency = "chf"
CurrencyPln Currency = "pln"
CurrencyTry Currency = "try"
CurrencyRon Currency = "ron"
CurrencyCzk Currency = "czk"
)
func (c Currency) String() string {
@@ -156,7 +140,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, CurrencyNzd, CurrencySek, CurrencyDkk, CurrencyInr, CurrencyRmb, CurrencyBgn, CurrencyChf, CurrencyPln, CurrencyTry, CurrencyRon, CurrencyCzk:
case CurrencyUsd, CurrencyEur, CurrencyGbp, CurrencyJpy, CurrencyZar, CurrencyAud, CurrencyNok, CurrencySek, CurrencyDkk, CurrencyInr, CurrencyRmb:
return nil
default:
return fmt.Errorf("group: invalid enum value for currency field: %q", c)

View File

@@ -13,227 +13,357 @@ import (
// ID filters vertices based on their ID field.
func ID(id uuid.UUID) predicate.Group {
return predicate.Group(sql.FieldEQ(FieldID, id))
return predicate.Group(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.Group {
return predicate.Group(sql.FieldEQ(FieldID, id))
return predicate.Group(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.Group {
return predicate.Group(sql.FieldNEQ(FieldID, id))
return predicate.Group(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.Group {
return predicate.Group(sql.FieldIn(FieldID, ids...))
return predicate.Group(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.Group {
return predicate.Group(sql.FieldNotIn(FieldID, ids...))
return predicate.Group(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.Group {
return predicate.Group(sql.FieldGT(FieldID, id))
return predicate.Group(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.Group {
return predicate.Group(sql.FieldGTE(FieldID, id))
return predicate.Group(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.Group {
return predicate.Group(sql.FieldLT(FieldID, id))
return predicate.Group(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.Group {
return predicate.Group(sql.FieldLTE(FieldID, id))
return predicate.Group(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.Group {
return predicate.Group(sql.FieldEQ(FieldCreatedAt, v))
return predicate.Group(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.Group {
return predicate.Group(sql.FieldEQ(FieldUpdatedAt, v))
return predicate.Group(func(s *sql.Selector) {
s.Where(sql.EQ(s.C(FieldUpdatedAt), v))
})
}
// Name applies equality check predicate on the "name" field. It's identical to NameEQ.
func Name(v string) predicate.Group {
return predicate.Group(sql.FieldEQ(FieldName, v))
return predicate.Group(func(s *sql.Selector) {
s.Where(sql.EQ(s.C(FieldName), v))
})
}
// CreatedAtEQ applies the EQ predicate on the "created_at" field.
func CreatedAtEQ(v time.Time) predicate.Group {
return predicate.Group(sql.FieldEQ(FieldCreatedAt, v))
return predicate.Group(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.Group {
return predicate.Group(sql.FieldNEQ(FieldCreatedAt, v))
return predicate.Group(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.Group {
return predicate.Group(sql.FieldIn(FieldCreatedAt, vs...))
v := make([]any, len(vs))
for i := range v {
v[i] = vs[i]
}
return predicate.Group(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.Group {
return predicate.Group(sql.FieldNotIn(FieldCreatedAt, vs...))
v := make([]any, len(vs))
for i := range v {
v[i] = vs[i]
}
return predicate.Group(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.Group {
return predicate.Group(sql.FieldGT(FieldCreatedAt, v))
return predicate.Group(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.Group {
return predicate.Group(sql.FieldGTE(FieldCreatedAt, v))
return predicate.Group(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.Group {
return predicate.Group(sql.FieldLT(FieldCreatedAt, v))
return predicate.Group(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.Group {
return predicate.Group(sql.FieldLTE(FieldCreatedAt, v))
return predicate.Group(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.Group {
return predicate.Group(sql.FieldEQ(FieldUpdatedAt, v))
return predicate.Group(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.Group {
return predicate.Group(sql.FieldNEQ(FieldUpdatedAt, v))
return predicate.Group(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.Group {
return predicate.Group(sql.FieldIn(FieldUpdatedAt, vs...))
v := make([]any, len(vs))
for i := range v {
v[i] = vs[i]
}
return predicate.Group(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.Group {
return predicate.Group(sql.FieldNotIn(FieldUpdatedAt, vs...))
v := make([]any, len(vs))
for i := range v {
v[i] = vs[i]
}
return predicate.Group(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.Group {
return predicate.Group(sql.FieldGT(FieldUpdatedAt, v))
return predicate.Group(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.Group {
return predicate.Group(sql.FieldGTE(FieldUpdatedAt, v))
return predicate.Group(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.Group {
return predicate.Group(sql.FieldLT(FieldUpdatedAt, v))
return predicate.Group(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.Group {
return predicate.Group(sql.FieldLTE(FieldUpdatedAt, v))
return predicate.Group(func(s *sql.Selector) {
s.Where(sql.LTE(s.C(FieldUpdatedAt), v))
})
}
// NameEQ applies the EQ predicate on the "name" field.
func NameEQ(v string) predicate.Group {
return predicate.Group(sql.FieldEQ(FieldName, v))
return predicate.Group(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.Group {
return predicate.Group(sql.FieldNEQ(FieldName, v))
return predicate.Group(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.Group {
return predicate.Group(sql.FieldIn(FieldName, vs...))
v := make([]any, len(vs))
for i := range v {
v[i] = vs[i]
}
return predicate.Group(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.Group {
return predicate.Group(sql.FieldNotIn(FieldName, vs...))
v := make([]any, len(vs))
for i := range v {
v[i] = vs[i]
}
return predicate.Group(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.Group {
return predicate.Group(sql.FieldGT(FieldName, v))
return predicate.Group(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.Group {
return predicate.Group(sql.FieldGTE(FieldName, v))
return predicate.Group(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.Group {
return predicate.Group(sql.FieldLT(FieldName, v))
return predicate.Group(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.Group {
return predicate.Group(sql.FieldLTE(FieldName, v))
return predicate.Group(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.Group {
return predicate.Group(sql.FieldContains(FieldName, v))
return predicate.Group(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.Group {
return predicate.Group(sql.FieldHasPrefix(FieldName, v))
return predicate.Group(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.Group {
return predicate.Group(sql.FieldHasSuffix(FieldName, v))
return predicate.Group(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.Group {
return predicate.Group(sql.FieldEqualFold(FieldName, v))
return predicate.Group(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.Group {
return predicate.Group(sql.FieldContainsFold(FieldName, v))
return predicate.Group(func(s *sql.Selector) {
s.Where(sql.ContainsFold(s.C(FieldName), v))
})
}
// CurrencyEQ applies the EQ predicate on the "currency" field.
func CurrencyEQ(v Currency) predicate.Group {
return predicate.Group(sql.FieldEQ(FieldCurrency, v))
return predicate.Group(func(s *sql.Selector) {
s.Where(sql.EQ(s.C(FieldCurrency), v))
})
}
// CurrencyNEQ applies the NEQ predicate on the "currency" field.
func CurrencyNEQ(v Currency) predicate.Group {
return predicate.Group(sql.FieldNEQ(FieldCurrency, v))
return predicate.Group(func(s *sql.Selector) {
s.Where(sql.NEQ(s.C(FieldCurrency), v))
})
}
// CurrencyIn applies the In predicate on the "currency" field.
func CurrencyIn(vs ...Currency) predicate.Group {
return predicate.Group(sql.FieldIn(FieldCurrency, vs...))
v := make([]any, len(vs))
for i := range v {
v[i] = vs[i]
}
return predicate.Group(func(s *sql.Selector) {
s.Where(sql.In(s.C(FieldCurrency), v...))
})
}
// CurrencyNotIn applies the NotIn predicate on the "currency" field.
func CurrencyNotIn(vs ...Currency) predicate.Group {
return predicate.Group(sql.FieldNotIn(FieldCurrency, vs...))
v := make([]any, len(vs))
for i := range v {
v[i] = vs[i]
}
return predicate.Group(func(s *sql.Selector) {
s.Where(sql.NotIn(s.C(FieldCurrency), v...))
})
}
// HasUsers applies the HasEdge predicate on the "users" edge.
@@ -241,6 +371,7 @@ func HasUsers() predicate.Group {
return predicate.Group(func(s *sql.Selector) {
step := sqlgraph.NewStep(
sqlgraph.From(Table, FieldID),
sqlgraph.To(UsersTable, FieldID),
sqlgraph.Edge(sqlgraph.O2M, false, UsersTable, UsersColumn),
)
sqlgraph.HasNeighbors(s, step)
@@ -268,6 +399,7 @@ func HasLocations() predicate.Group {
return predicate.Group(func(s *sql.Selector) {
step := sqlgraph.NewStep(
sqlgraph.From(Table, FieldID),
sqlgraph.To(LocationsTable, FieldID),
sqlgraph.Edge(sqlgraph.O2M, false, LocationsTable, LocationsColumn),
)
sqlgraph.HasNeighbors(s, step)
@@ -295,6 +427,7 @@ func HasItems() predicate.Group {
return predicate.Group(func(s *sql.Selector) {
step := sqlgraph.NewStep(
sqlgraph.From(Table, FieldID),
sqlgraph.To(ItemsTable, FieldID),
sqlgraph.Edge(sqlgraph.O2M, false, ItemsTable, ItemsColumn),
)
sqlgraph.HasNeighbors(s, step)
@@ -322,6 +455,7 @@ func HasLabels() predicate.Group {
return predicate.Group(func(s *sql.Selector) {
step := sqlgraph.NewStep(
sqlgraph.From(Table, FieldID),
sqlgraph.To(LabelsTable, FieldID),
sqlgraph.Edge(sqlgraph.O2M, false, LabelsTable, LabelsColumn),
)
sqlgraph.HasNeighbors(s, step)
@@ -349,6 +483,7 @@ func HasDocuments() predicate.Group {
return predicate.Group(func(s *sql.Selector) {
step := sqlgraph.NewStep(
sqlgraph.From(Table, FieldID),
sqlgraph.To(DocumentsTable, FieldID),
sqlgraph.Edge(sqlgraph.O2M, false, DocumentsTable, DocumentsColumn),
)
sqlgraph.HasNeighbors(s, step)
@@ -376,6 +511,7 @@ func HasInvitationTokens() predicate.Group {
return predicate.Group(func(s *sql.Selector) {
step := sqlgraph.NewStep(
sqlgraph.From(Table, FieldID),
sqlgraph.To(InvitationTokensTable, FieldID),
sqlgraph.Edge(sqlgraph.O2M, false, InvitationTokensTable, InvitationTokensColumn),
)
sqlgraph.HasNeighbors(s, step)
@@ -398,33 +534,6 @@ func HasInvitationTokensWith(preds ...predicate.GroupInvitationToken) predicate.
})
}
// HasNotifiers applies the HasEdge predicate on the "notifiers" edge.
func HasNotifiers() predicate.Group {
return predicate.Group(func(s *sql.Selector) {
step := sqlgraph.NewStep(
sqlgraph.From(Table, FieldID),
sqlgraph.Edge(sqlgraph.O2M, false, NotifiersTable, NotifiersColumn),
)
sqlgraph.HasNeighbors(s, step)
})
}
// HasNotifiersWith applies the HasEdge predicate on the "notifiers" edge with a given conditions (other predicates).
func HasNotifiersWith(preds ...predicate.Notifier) predicate.Group {
return predicate.Group(func(s *sql.Selector) {
step := sqlgraph.NewStep(
sqlgraph.From(Table, FieldID),
sqlgraph.To(NotifiersInverseTable, FieldID),
sqlgraph.Edge(sqlgraph.O2M, false, NotifiersTable, NotifiersColumn),
)
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.Group) predicate.Group {
return predicate.Group(func(s *sql.Selector) {

View File

@@ -17,7 +17,6 @@ import (
"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"
"github.com/hay-kot/homebox/backend/internal/data/ent/notifier"
"github.com/hay-kot/homebox/backend/internal/data/ent/user"
)
@@ -180,21 +179,6 @@ func (gc *GroupCreate) AddInvitationTokens(g ...*GroupInvitationToken) *GroupCre
return gc.AddInvitationTokenIDs(ids...)
}
// AddNotifierIDs adds the "notifiers" edge to the Notifier entity by IDs.
func (gc *GroupCreate) AddNotifierIDs(ids ...uuid.UUID) *GroupCreate {
gc.mutation.AddNotifierIDs(ids...)
return gc
}
// AddNotifiers adds the "notifiers" edges to the Notifier entity.
func (gc *GroupCreate) AddNotifiers(n ...*Notifier) *GroupCreate {
ids := make([]uuid.UUID, len(n))
for i := range n {
ids[i] = n[i].ID
}
return gc.AddNotifierIDs(ids...)
}
// Mutation returns the GroupMutation object of the builder.
func (gc *GroupCreate) Mutation() *GroupMutation {
return gc.mutation
@@ -202,8 +186,50 @@ func (gc *GroupCreate) Mutation() *GroupMutation {
// Save creates the Group in the database.
func (gc *GroupCreate) Save(ctx context.Context) (*Group, error) {
var (
err error
node *Group
)
gc.defaults()
return withHooks[*Group, GroupMutation](ctx, gc.sqlSave, gc.mutation, gc.hooks)
if len(gc.hooks) == 0 {
if err = gc.check(); err != nil {
return nil, err
}
node, err = gc.sqlSave(ctx)
} else {
var mut Mutator = MutateFunc(func(ctx context.Context, m Mutation) (Value, error) {
mutation, ok := m.(*GroupMutation)
if !ok {
return nil, fmt.Errorf("unexpected mutation type %T", m)
}
if err = gc.check(); err != nil {
return nil, err
}
gc.mutation = mutation
if node, err = gc.sqlSave(ctx); err != nil {
return nil, err
}
mutation.id = &node.ID
mutation.done = true
return node, err
})
for i := len(gc.hooks) - 1; i >= 0; i-- {
if gc.hooks[i] == nil {
return nil, fmt.Errorf("ent: uninitialized hook (forgotten import ent/runtime?)")
}
mut = gc.hooks[i](mut)
}
v, err := mut.Mutate(ctx, gc.mutation)
if err != nil {
return nil, err
}
nv, ok := v.(*Group)
if !ok {
return nil, fmt.Errorf("unexpected node type %T returned from GroupMutation", v)
}
node = nv
}
return node, err
}
// SaveX calls Save and panics if Save returns an error.
@@ -276,9 +302,6 @@ func (gc *GroupCreate) check() error {
}
func (gc *GroupCreate) sqlSave(ctx context.Context) (*Group, error) {
if err := gc.check(); err != nil {
return nil, err
}
_node, _spec := gc.createSpec()
if err := sqlgraph.CreateNode(ctx, gc.driver, _spec); err != nil {
if sqlgraph.IsConstraintError(err) {
@@ -293,15 +316,19 @@ func (gc *GroupCreate) sqlSave(ctx context.Context) (*Group, error) {
return nil, err
}
}
gc.mutation.id = &_node.ID
gc.mutation.done = true
return _node, nil
}
func (gc *GroupCreate) createSpec() (*Group, *sqlgraph.CreateSpec) {
var (
_node = &Group{config: gc.config}
_spec = sqlgraph.NewCreateSpec(group.Table, sqlgraph.NewFieldSpec(group.FieldID, field.TypeUUID))
_spec = &sqlgraph.CreateSpec{
Table: group.Table,
ID: &sqlgraph.FieldSpec{
Type: field.TypeUUID,
Column: group.FieldID,
},
}
)
if id, ok := gc.mutation.ID(); ok {
_node.ID = id
@@ -331,7 +358,10 @@ func (gc *GroupCreate) createSpec() (*Group, *sqlgraph.CreateSpec) {
Columns: []string{group.UsersColumn},
Bidi: false,
Target: &sqlgraph.EdgeTarget{
IDSpec: sqlgraph.NewFieldSpec(user.FieldID, field.TypeUUID),
IDSpec: &sqlgraph.FieldSpec{
Type: field.TypeUUID,
Column: user.FieldID,
},
},
}
for _, k := range nodes {
@@ -347,7 +377,10 @@ func (gc *GroupCreate) createSpec() (*Group, *sqlgraph.CreateSpec) {
Columns: []string{group.LocationsColumn},
Bidi: false,
Target: &sqlgraph.EdgeTarget{
IDSpec: sqlgraph.NewFieldSpec(location.FieldID, field.TypeUUID),
IDSpec: &sqlgraph.FieldSpec{
Type: field.TypeUUID,
Column: location.FieldID,
},
},
}
for _, k := range nodes {
@@ -363,7 +396,10 @@ func (gc *GroupCreate) createSpec() (*Group, *sqlgraph.CreateSpec) {
Columns: []string{group.ItemsColumn},
Bidi: false,
Target: &sqlgraph.EdgeTarget{
IDSpec: sqlgraph.NewFieldSpec(item.FieldID, field.TypeUUID),
IDSpec: &sqlgraph.FieldSpec{
Type: field.TypeUUID,
Column: item.FieldID,
},
},
}
for _, k := range nodes {
@@ -379,7 +415,10 @@ func (gc *GroupCreate) createSpec() (*Group, *sqlgraph.CreateSpec) {
Columns: []string{group.LabelsColumn},
Bidi: false,
Target: &sqlgraph.EdgeTarget{
IDSpec: sqlgraph.NewFieldSpec(label.FieldID, field.TypeUUID),
IDSpec: &sqlgraph.FieldSpec{
Type: field.TypeUUID,
Column: label.FieldID,
},
},
}
for _, k := range nodes {
@@ -395,7 +434,10 @@ func (gc *GroupCreate) createSpec() (*Group, *sqlgraph.CreateSpec) {
Columns: []string{group.DocumentsColumn},
Bidi: false,
Target: &sqlgraph.EdgeTarget{
IDSpec: sqlgraph.NewFieldSpec(document.FieldID, field.TypeUUID),
IDSpec: &sqlgraph.FieldSpec{
Type: field.TypeUUID,
Column: document.FieldID,
},
},
}
for _, k := range nodes {
@@ -411,23 +453,10 @@ func (gc *GroupCreate) createSpec() (*Group, *sqlgraph.CreateSpec) {
Columns: []string{group.InvitationTokensColumn},
Bidi: false,
Target: &sqlgraph.EdgeTarget{
IDSpec: sqlgraph.NewFieldSpec(groupinvitationtoken.FieldID, field.TypeUUID),
},
}
for _, k := range nodes {
edge.Target.Nodes = append(edge.Target.Nodes, k)
}
_spec.Edges = append(_spec.Edges, edge)
}
if nodes := gc.mutation.NotifiersIDs(); len(nodes) > 0 {
edge := &sqlgraph.EdgeSpec{
Rel: sqlgraph.O2M,
Inverse: false,
Table: group.NotifiersTable,
Columns: []string{group.NotifiersColumn},
Bidi: false,
Target: &sqlgraph.EdgeTarget{
IDSpec: sqlgraph.NewFieldSpec(notifier.FieldID, field.TypeUUID),
IDSpec: &sqlgraph.FieldSpec{
Type: field.TypeUUID,
Column: groupinvitationtoken.FieldID,
},
},
}
for _, k := range nodes {

View File

@@ -4,6 +4,7 @@ package ent
import (
"context"
"fmt"
"entgo.io/ent/dialect/sql"
"entgo.io/ent/dialect/sql/sqlgraph"
@@ -27,7 +28,34 @@ func (gd *GroupDelete) Where(ps ...predicate.Group) *GroupDelete {
// Exec executes the deletion query and returns how many vertices were deleted.
func (gd *GroupDelete) Exec(ctx context.Context) (int, error) {
return withHooks[int, GroupMutation](ctx, gd.sqlExec, gd.mutation, gd.hooks)
var (
err error
affected int
)
if len(gd.hooks) == 0 {
affected, err = gd.sqlExec(ctx)
} else {
var mut Mutator = MutateFunc(func(ctx context.Context, m Mutation) (Value, error) {
mutation, ok := m.(*GroupMutation)
if !ok {
return nil, fmt.Errorf("unexpected mutation type %T", m)
}
gd.mutation = mutation
affected, err = gd.sqlExec(ctx)
mutation.done = true
return affected, err
})
for i := len(gd.hooks) - 1; i >= 0; i-- {
if gd.hooks[i] == nil {
return 0, fmt.Errorf("ent: uninitialized hook (forgotten import ent/runtime?)")
}
mut = gd.hooks[i](mut)
}
if _, err := mut.Mutate(ctx, gd.mutation); err != nil {
return 0, err
}
}
return affected, err
}
// ExecX is like Exec, but panics if an error occurs.
@@ -40,7 +68,15 @@ func (gd *GroupDelete) ExecX(ctx context.Context) int {
}
func (gd *GroupDelete) sqlExec(ctx context.Context) (int, error) {
_spec := sqlgraph.NewDeleteSpec(group.Table, sqlgraph.NewFieldSpec(group.FieldID, field.TypeUUID))
_spec := &sqlgraph.DeleteSpec{
Node: &sqlgraph.NodeSpec{
Table: group.Table,
ID: &sqlgraph.FieldSpec{
Type: field.TypeUUID,
Column: group.FieldID,
},
},
}
if ps := gd.mutation.predicates; len(ps) > 0 {
_spec.Predicate = func(selector *sql.Selector) {
for i := range ps {
@@ -52,7 +88,6 @@ func (gd *GroupDelete) sqlExec(ctx context.Context) (int, error) {
if err != nil && sqlgraph.IsConstraintError(err) {
err = &ConstraintError{msg: err.Error(), wrap: err}
}
gd.mutation.done = true
return affected, err
}
@@ -61,12 +96,6 @@ type GroupDeleteOne struct {
gd *GroupDelete
}
// Where appends a list predicates to the GroupDelete builder.
func (gdo *GroupDeleteOne) Where(ps ...predicate.Group) *GroupDeleteOne {
gdo.gd.mutation.Where(ps...)
return gdo
}
// Exec executes the deletion query.
func (gdo *GroupDeleteOne) Exec(ctx context.Context) error {
n, err := gdo.gd.Exec(ctx)
@@ -82,7 +111,5 @@ func (gdo *GroupDeleteOne) Exec(ctx context.Context) error {
// ExecX is like Exec, but panics if an error occurs.
func (gdo *GroupDeleteOne) ExecX(ctx context.Context) {
if err := gdo.Exec(ctx); err != nil {
panic(err)
}
gdo.gd.ExecX(ctx)
}

Some files were not shown because too many files have changed in this diff Show More