forked from mirrors/homebox
fix: refactoring errors (#359)
* #352 - require one date to be set to save * fix many type regressions
This commit is contained in:
parent
4a8ba6231d
commit
97fb94d231
22 changed files with 164 additions and 289 deletions
|
@ -9,6 +9,14 @@ import (
|
|||
"github.com/hay-kot/safeserve/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"`
|
||||
}
|
||||
|
|
|
@ -15,7 +15,7 @@ import (
|
|||
// @Summary Get All Labels
|
||||
// @Tags Labels
|
||||
// @Produce json
|
||||
// @Success 200 {object} Wrapped{items=[]repo.LabelOut}
|
||||
// @Success 200 {object} []repo.LabelOut
|
||||
// @Router /v1/labels [GET]
|
||||
// @Security Bearer
|
||||
func (ctrl *V1Controller) HandleLabelsGetAll() errchain.HandlerFunc {
|
||||
|
|
|
@ -16,7 +16,7 @@ import (
|
|||
// @Tags Locations
|
||||
// @Produce json
|
||||
// @Param withItems query bool false "include items in response tree"
|
||||
// @Success 200 {object} Wrapped{items=[]repo.TreeItem}
|
||||
// @Success 200 {object} []repo.TreeItem
|
||||
// @Router /v1/locations/tree [GET]
|
||||
// @Security Bearer
|
||||
func (ctrl *V1Controller) HandleLocationTreeQuery() errchain.HandlerFunc {
|
||||
|
@ -34,7 +34,7 @@ func (ctrl *V1Controller) HandleLocationTreeQuery() errchain.HandlerFunc {
|
|||
// @Tags Locations
|
||||
// @Produce json
|
||||
// @Param filterChildren query bool false "Filter locations with parents"
|
||||
// @Success 200 {object} Wrapped{items=[]repo.LocationOutCount}
|
||||
// @Success 200 {object} []repo.LocationOutCount
|
||||
// @Router /v1/locations [GET]
|
||||
// @Security Bearer
|
||||
func (ctrl *V1Controller) HandleLocationGetAll() errchain.HandlerFunc {
|
||||
|
|
|
@ -16,7 +16,7 @@ import (
|
|||
// @Summary Get Notifiers
|
||||
// @Tags Notifiers
|
||||
// @Produce json
|
||||
// @Success 200 {object} Wrapped{items=[]repo.NotifierOut}
|
||||
// @Success 200 {object} []repo.NotifierOut
|
||||
// @Router /v1/notifiers [GET]
|
||||
// @Security Bearer
|
||||
func (ctrl *V1Controller) HandleGetUserNotifiers() errchain.HandlerFunc {
|
||||
|
|
|
@ -945,22 +945,10 @@ const docTemplate = `{
|
|||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/v1.Wrapped"
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"items": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/repo.LabelOut"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/repo.LabelOut"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1117,22 +1105,10 @@ const docTemplate = `{
|
|||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/v1.Wrapped"
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"items": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/repo.LocationOutCount"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/repo.LocationOutCount"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1197,22 +1173,10 @@ const docTemplate = `{
|
|||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/v1.Wrapped"
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"items": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/repo.TreeItem"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/repo.TreeItem"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1337,22 +1301,10 @@ const docTemplate = `{
|
|||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/v1.Wrapped"
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"items": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/repo.NotifierOut"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/repo.NotifierOut"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1917,14 +1869,12 @@ const docTemplate = `{
|
|||
"repo.ItemCreate": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"description",
|
||||
"name"
|
||||
],
|
||||
"properties": {
|
||||
"description": {
|
||||
"type": "string",
|
||||
"maxLength": 1000,
|
||||
"minLength": 1
|
||||
"maxLength": 1000
|
||||
},
|
||||
"labelIds": {
|
||||
"type": "array",
|
||||
|
@ -2433,6 +2383,9 @@ const docTemplate = `{
|
|||
},
|
||||
"repo.MaintenanceEntryCreate": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"name"
|
||||
],
|
||||
"properties": {
|
||||
"completedDate": {
|
||||
"description": "Sold",
|
||||
|
|
|
@ -937,22 +937,10 @@
|
|||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/v1.Wrapped"
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"items": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/repo.LabelOut"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/repo.LabelOut"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1109,22 +1097,10 @@
|
|||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/v1.Wrapped"
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"items": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/repo.LocationOutCount"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/repo.LocationOutCount"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1189,22 +1165,10 @@
|
|||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/v1.Wrapped"
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"items": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/repo.TreeItem"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/repo.TreeItem"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1329,22 +1293,10 @@
|
|||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/v1.Wrapped"
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"items": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/repo.NotifierOut"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/repo.NotifierOut"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1909,14 +1861,12 @@
|
|||
"repo.ItemCreate": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"description",
|
||||
"name"
|
||||
],
|
||||
"properties": {
|
||||
"description": {
|
||||
"type": "string",
|
||||
"maxLength": 1000,
|
||||
"minLength": 1
|
||||
"maxLength": 1000
|
||||
},
|
||||
"labelIds": {
|
||||
"type": "array",
|
||||
|
@ -2425,6 +2375,9 @@
|
|||
},
|
||||
"repo.MaintenanceEntryCreate": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"name"
|
||||
],
|
||||
"properties": {
|
||||
"completedDate": {
|
||||
"description": "Sold",
|
||||
|
|
|
@ -77,7 +77,6 @@ definitions:
|
|||
properties:
|
||||
description:
|
||||
maxLength: 1000
|
||||
minLength: 1
|
||||
type: string
|
||||
labelIds:
|
||||
items:
|
||||
|
@ -94,7 +93,6 @@ definitions:
|
|||
type: string
|
||||
x-nullable: true
|
||||
required:
|
||||
- description
|
||||
- name
|
||||
type: object
|
||||
repo.ItemField:
|
||||
|
@ -442,6 +440,8 @@ definitions:
|
|||
scheduledDate:
|
||||
description: Sold
|
||||
type: string
|
||||
required:
|
||||
- name
|
||||
type: object
|
||||
repo.MaintenanceEntryUpdate:
|
||||
properties:
|
||||
|
@ -1258,14 +1258,9 @@ paths:
|
|||
"200":
|
||||
description: OK
|
||||
schema:
|
||||
allOf:
|
||||
- $ref: '#/definitions/v1.Wrapped'
|
||||
- properties:
|
||||
items:
|
||||
items:
|
||||
$ref: '#/definitions/repo.LabelOut'
|
||||
type: array
|
||||
type: object
|
||||
items:
|
||||
$ref: '#/definitions/repo.LabelOut'
|
||||
type: array
|
||||
security:
|
||||
- Bearer: []
|
||||
summary: Get All Labels
|
||||
|
@ -1360,14 +1355,9 @@ paths:
|
|||
"200":
|
||||
description: OK
|
||||
schema:
|
||||
allOf:
|
||||
- $ref: '#/definitions/v1.Wrapped'
|
||||
- properties:
|
||||
items:
|
||||
items:
|
||||
$ref: '#/definitions/repo.LocationOutCount'
|
||||
type: array
|
||||
type: object
|
||||
items:
|
||||
$ref: '#/definitions/repo.LocationOutCount'
|
||||
type: array
|
||||
security:
|
||||
- Bearer: []
|
||||
summary: Get All Locations
|
||||
|
@ -1468,14 +1458,9 @@ paths:
|
|||
"200":
|
||||
description: OK
|
||||
schema:
|
||||
allOf:
|
||||
- $ref: '#/definitions/v1.Wrapped'
|
||||
- properties:
|
||||
items:
|
||||
items:
|
||||
$ref: '#/definitions/repo.TreeItem'
|
||||
type: array
|
||||
type: object
|
||||
items:
|
||||
$ref: '#/definitions/repo.TreeItem'
|
||||
type: array
|
||||
security:
|
||||
- Bearer: []
|
||||
summary: Get Locations Tree
|
||||
|
@ -1489,14 +1474,9 @@ paths:
|
|||
"200":
|
||||
description: OK
|
||||
schema:
|
||||
allOf:
|
||||
- $ref: '#/definitions/v1.Wrapped'
|
||||
- properties:
|
||||
items:
|
||||
items:
|
||||
$ref: '#/definitions/repo.NotifierOut'
|
||||
type: array
|
||||
type: object
|
||||
items:
|
||||
$ref: '#/definitions/repo.NotifierOut'
|
||||
type: array
|
||||
security:
|
||||
- Bearer: []
|
||||
summary: Get Notifiers
|
||||
|
|
|
@ -61,6 +61,7 @@ func (svc *UserService) RegisterUser(ctx context.Context, data UserRegistration)
|
|||
|
||||
switch data.GroupToken {
|
||||
case "":
|
||||
log.Debug().Msg("creating new group")
|
||||
creatingGroup = true
|
||||
group, err = svc.repos.Groups.GroupCreate(ctx, "Home")
|
||||
if err != nil {
|
||||
|
@ -68,6 +69,7 @@ func (svc *UserService) RegisterUser(ctx context.Context, data UserRegistration)
|
|||
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")
|
||||
|
@ -94,14 +96,14 @@ func (svc *UserService) RegisterUser(ctx context.Context, data UserRegistration)
|
|||
// Create the default labels and locations for the group.
|
||||
if creatingGroup {
|
||||
for _, label := range defaultLabels() {
|
||||
_, err := svc.repos.Labels.Create(ctx, group.ID, label)
|
||||
_, 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, group.ID, location)
|
||||
_, err := svc.repos.Locations.Create(ctx, usr.GroupID, location)
|
||||
if err != nil {
|
||||
return repo.UserOut{}, err
|
||||
}
|
||||
|
|
|
@ -234,11 +234,17 @@ func (r *GroupRepository) StatsGroup(ctx context.Context, GID uuid.UUID) (GroupS
|
|||
var stats GroupStatistics
|
||||
row := r.db.Sql().QueryRowContext(ctx, q, GID, GID, GID, GID, GID, GID)
|
||||
|
||||
err := row.Scan(&stats.TotalUsers, &stats.TotalItems, &stats.TotalLocations, &stats.TotalLabels, &stats.TotalItemPrice, &stats.TotalWithWarranty)
|
||||
var maybeTotalItemPrice *float64
|
||||
var maybeTotalWithWarranty *int
|
||||
|
||||
err := row.Scan(&stats.TotalUsers, &stats.TotalItems, &stats.TotalLocations, &stats.TotalLabels, &maybeTotalItemPrice, &maybeTotalWithWarranty)
|
||||
if err != nil {
|
||||
return GroupStatistics{}, err
|
||||
}
|
||||
|
||||
stats.TotalItemPrice = orDefault(maybeTotalItemPrice, 0)
|
||||
stats.TotalWithWarranty = orDefault(maybeTotalWithWarranty, 0)
|
||||
|
||||
return stats, nil
|
||||
}
|
||||
|
||||
|
|
|
@ -52,7 +52,7 @@ type (
|
|||
ImportRef string `json:"-"`
|
||||
ParentID uuid.UUID `json:"parentId" extensions:"x-nullable"`
|
||||
Name string `json:"name" validate:"required,min=1,max=255"`
|
||||
Description string `json:"description" validate:"required,min=1,max=1000"`
|
||||
Description string `json:"description" validate:"max=1000"`
|
||||
AssetID AssetID `json:"-"`
|
||||
|
||||
// Edges
|
||||
|
|
|
@ -2,6 +2,7 @@ package repo
|
|||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"time"
|
||||
|
||||
"github.com/google/uuid"
|
||||
|
@ -18,15 +19,38 @@ import (
|
|||
type MaintenanceEntryRepository struct {
|
||||
db *ent.Client
|
||||
}
|
||||
type (
|
||||
MaintenanceEntryCreate struct {
|
||||
CompletedDate types.Date `json:"completedDate"`
|
||||
ScheduledDate types.Date `json:"scheduledDate"`
|
||||
Name string `json:"name"`
|
||||
Description string `json:"description"`
|
||||
Cost float64 `json:"cost,string"`
|
||||
}
|
||||
|
||||
type MaintenanceEntryCreate struct {
|
||||
CompletedDate types.Date `json:"completedDate"`
|
||||
ScheduledDate types.Date `json:"scheduledDate"`
|
||||
Name string `json:"name" validate:"required"`
|
||||
Description string `json:"description"`
|
||||
Cost float64 `json:"cost,string"`
|
||||
}
|
||||
|
||||
func (mc MaintenanceEntryCreate) Validate() error {
|
||||
if mc.CompletedDate.Time().IsZero() && mc.ScheduledDate.Time().IsZero() {
|
||||
return errors.New("either completedDate or scheduledDate must be set")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type MaintenanceEntryUpdate struct {
|
||||
CompletedDate types.Date `json:"completedDate"`
|
||||
ScheduledDate types.Date `json:"scheduledDate"`
|
||||
Name string `json:"name"`
|
||||
Description string `json:"description"`
|
||||
Cost float64 `json:"cost,string"`
|
||||
}
|
||||
|
||||
func (mu MaintenanceEntryUpdate) Validate() error {
|
||||
if mu.CompletedDate.Time().IsZero() && mu.ScheduledDate.Time().IsZero() {
|
||||
return errors.New("either completedDate or scheduledDate must be set")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type (
|
||||
MaintenanceEntry struct {
|
||||
ID uuid.UUID `json:"id"`
|
||||
CompletedDate types.Date `json:"completedDate"`
|
||||
|
@ -36,14 +60,6 @@ type (
|
|||
Cost float64 `json:"cost,string"`
|
||||
}
|
||||
|
||||
MaintenanceEntryUpdate struct {
|
||||
CompletedDate types.Date `json:"completedDate"`
|
||||
ScheduledDate types.Date `json:"scheduledDate"`
|
||||
Name string `json:"name"`
|
||||
Description string `json:"description"`
|
||||
Cost float64 `json:"cost,string"`
|
||||
}
|
||||
|
||||
MaintenanceLog struct {
|
||||
ItemID uuid.UUID `json:"itemId"`
|
||||
CostAverage float64 `json:"costAverage"`
|
||||
|
|
|
@ -29,20 +29,31 @@ func DecodeQuery[T any](r *http.Request) (T, error) {
|
|||
return v, nil
|
||||
}
|
||||
|
||||
type Validator interface {
|
||||
Validate() error
|
||||
}
|
||||
|
||||
func DecodeBody[T any](r *http.Request) (T, error) {
|
||||
var v T
|
||||
var val T
|
||||
|
||||
err := server.Decode(r, &v)
|
||||
err := server.Decode(r, &val)
|
||||
if err != nil {
|
||||
return v, errors.Wrap(err, "body decoding error")
|
||||
return val, errors.Wrap(err, "body decoding error")
|
||||
}
|
||||
|
||||
err = validate.Check(v)
|
||||
err = validate.Check(val)
|
||||
if err != nil {
|
||||
return v, errors.Wrap(err, "validation error")
|
||||
return val, err
|
||||
}
|
||||
|
||||
return v, nil
|
||||
if v, ok := any(val).(Validator); ok {
|
||||
err = v.Validate()
|
||||
if err != nil {
|
||||
return val, errors.Wrap(err, "validation error")
|
||||
}
|
||||
}
|
||||
|
||||
return val, nil
|
||||
}
|
||||
|
||||
func RouteUUID(r *http.Request, key string) (uuid.UUID, error) {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue