feat: better API returns, asset update
This commit is contained in:
@@ -67,6 +67,7 @@ func (s *APIServer) handleIndex(w http.ResponseWriter, r *http.Request) error {
|
|||||||
func makeHandler(f APIFunc) http.HandlerFunc {
|
func makeHandler(f APIFunc) http.HandlerFunc {
|
||||||
return func(w http.ResponseWriter, r *http.Request) {
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
if err := f(w, r); err != nil {
|
if err := f(w, r); err != nil {
|
||||||
|
log.Printf("Err: %v", err)
|
||||||
render.Render(w, r, errBadRequest(err))
|
render.Render(w, r, errBadRequest(err))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ func (s *APIServer) setupAssetRoutes() func(r chi.Router) {
|
|||||||
r.Use(s.AssetCtx)
|
r.Use(s.AssetCtx)
|
||||||
r.Get("/", makeHandler(s.getAsset))
|
r.Get("/", makeHandler(s.getAsset))
|
||||||
r.Delete("/", makeHandler(s.deleteAsset))
|
r.Delete("/", makeHandler(s.deleteAsset))
|
||||||
|
r.Put("/", makeHandler(s.updateAsset))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -42,7 +43,7 @@ func (s *APIServer) AssetCtx(next http.Handler) http.Handler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *APIServer) getAssets(w http.ResponseWriter, r *http.Request) error {
|
func (s *APIServer) getAssets(w http.ResponseWriter, r *http.Request) error {
|
||||||
assets, err := s.db.GetAssets(0, 50) // TODO: Proper Pagination
|
assets, err := s.db.GetAssets(0, 1000) // TODO: Proper Pagination
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -120,3 +121,41 @@ func (s *APIServer) createAsset(w http.ResponseWriter, r *http.Request) error {
|
|||||||
Asset: asset,
|
Asset: asset,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *APIServer) updateAsset(w http.ResponseWriter, r *http.Request) error {
|
||||||
|
ctx := r.Context()
|
||||||
|
eAsset, ok := ctx.Value("asset").(*types.Asset)
|
||||||
|
if !ok {
|
||||||
|
return render.Render(w, r, errUnprocessable)
|
||||||
|
}
|
||||||
|
|
||||||
|
data := &types.CreateAssetRequest{}
|
||||||
|
if err := render.Bind(r, data); err != nil {
|
||||||
|
return render.Render(w, r, errBadRequest(err))
|
||||||
|
}
|
||||||
|
|
||||||
|
asset := &types.Asset{
|
||||||
|
ID: eAsset.ID,
|
||||||
|
Name: data.Name,
|
||||||
|
Quantity: data.Quantity,
|
||||||
|
Length: data.Length,
|
||||||
|
Manufacturer: data.Manufacturer,
|
||||||
|
ModelName: data.ModelName,
|
||||||
|
Price: data.Price,
|
||||||
|
Comments: data.Comments,
|
||||||
|
ShelfLocationID: data.ShelfLocationID,
|
||||||
|
CategoryID: data.CategoryID,
|
||||||
|
}
|
||||||
|
|
||||||
|
err := s.db.UpdateAsset(eAsset.ID, asset)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return render.Render(w, r, &types.AssetResponse{
|
||||||
|
Response: &types.Response{
|
||||||
|
HTTPStatusCode: http.StatusOK,
|
||||||
|
},
|
||||||
|
Asset: asset,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package api
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
|
||||||
@@ -15,24 +16,27 @@ func (s *APIServer) setupShelfRoutes() func(chi.Router) {
|
|||||||
r.Get("/", makeHandler(s.getShelves))
|
r.Get("/", makeHandler(s.getShelves))
|
||||||
r.Post("/", makeHandler(s.createShelf))
|
r.Post("/", makeHandler(s.createShelf))
|
||||||
|
|
||||||
r.Route("/{shelfID}", func(r chi.Router) {
|
r.Route("/{shelfId}", func(r chi.Router) {
|
||||||
r.Use(s.ShelfCtx)
|
r.Use(s.ShelfCtx)
|
||||||
r.Get("/", makeHandler(s.getShelf))
|
r.Get("/", makeHandler(s.getShelf))
|
||||||
r.Delete("/", makeHandler(s.deleteShelf))
|
r.Delete("/", makeHandler(s.deleteShelf))
|
||||||
|
r.Get("/count", makeHandler(s.getShelfAssetCount))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *APIServer) ShelfCtx(next http.Handler) http.Handler {
|
func (s *APIServer) ShelfCtx(next http.Handler) http.Handler {
|
||||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
shelfIdStr := chi.URLParam(r, "shelfID")
|
shelfIdStr := chi.URLParam(r, "shelfId")
|
||||||
shelfId, err := strconv.ParseUint(shelfIdStr, 10, 64)
|
shelfId, err := strconv.ParseUint(shelfIdStr, 10, 64)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
log.Println("Test")
|
||||||
render.Render(w, r, errNotFound)
|
render.Render(w, r, errNotFound)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
shelf, err := s.db.GetShelfByID(shelfId)
|
shelf, err := s.db.GetShelfByID(shelfId)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
log.Printf("Test 2: %v\n", err)
|
||||||
render.Render(w, r, errNotFound)
|
render.Render(w, r, errNotFound)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -120,3 +124,23 @@ func (s *APIServer) createShelf(w http.ResponseWriter, r *http.Request) error {
|
|||||||
ShelfLocation: shelf,
|
ShelfLocation: shelf,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *APIServer) getShelfAssetCount(w http.ResponseWriter, r *http.Request) error {
|
||||||
|
ctx := r.Context()
|
||||||
|
shelf, ok := ctx.Value("shelf").(*types.ShelfLocation)
|
||||||
|
if !ok {
|
||||||
|
return render.Render(w, r, errUnprocessable)
|
||||||
|
}
|
||||||
|
|
||||||
|
count, err := s.db.AssetCountInLocation(shelf.ID)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return render.Render(w, r, &types.CountResponse{
|
||||||
|
Response: &types.Response{
|
||||||
|
HTTPStatusCode: http.StatusOK,
|
||||||
|
},
|
||||||
|
Count: count,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import (
|
|||||||
"git.brettb.xyz/goinv/server/internal/types"
|
"git.brettb.xyz/goinv/server/internal/types"
|
||||||
"gorm.io/driver/postgres"
|
"gorm.io/driver/postgres"
|
||||||
"gorm.io/gorm"
|
"gorm.io/gorm"
|
||||||
|
"gorm.io/gorm/clause"
|
||||||
"gorm.io/gorm/logger"
|
"gorm.io/gorm/logger"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -47,6 +48,11 @@ func (s *DataStore) CreateAsset(asset *types.Asset) error {
|
|||||||
return result.Error
|
return result.Error
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *DataStore) UpdateAsset(id uint64, update *types.Asset) error {
|
||||||
|
result := s.db.Model(&types.Asset{}).Where("id = ?", id).Select("*").Updates(update)
|
||||||
|
return result.Error
|
||||||
|
}
|
||||||
|
|
||||||
func (s *DataStore) CreateBuilding(building *types.Building) error {
|
func (s *DataStore) CreateBuilding(building *types.Building) error {
|
||||||
result := s.db.Create(building)
|
result := s.db.Create(building)
|
||||||
return result.Error
|
return result.Error
|
||||||
@@ -64,7 +70,7 @@ func (s *DataStore) CreateShelfLocation(shelf *types.ShelfLocation) error {
|
|||||||
|
|
||||||
func (s *DataStore) GetAssetByID(id uint64) (*types.Asset, error) {
|
func (s *DataStore) GetAssetByID(id uint64) (*types.Asset, error) {
|
||||||
var result types.Asset
|
var result types.Asset
|
||||||
tx := s.db.Model(&types.Asset{}).Where("id = ?", id).First(&result)
|
tx := s.db.Model(&types.Asset{}).Where("id = ?", id).Preload(clause.Associations).First(&result)
|
||||||
if tx.Error != nil {
|
if tx.Error != nil {
|
||||||
return nil, fmt.Errorf("asset %d not found", id)
|
return nil, fmt.Errorf("asset %d not found", id)
|
||||||
}
|
}
|
||||||
@@ -88,6 +94,14 @@ func (s *DataStore) TotalAssets() (int64, error) {
|
|||||||
return count, nil
|
return count, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *DataStore) AssetCountInLocation(shelfId uint64) (int64, error) {
|
||||||
|
var count int64
|
||||||
|
if tx := s.db.Model(&types.Asset{}).Where("shelf_location_id = ?", shelfId).Find(&types.Asset{}).Count(&count); tx.Error != nil {
|
||||||
|
return 0, tx.Error
|
||||||
|
}
|
||||||
|
return count, nil
|
||||||
|
}
|
||||||
|
|
||||||
func (s *DataStore) DeleteAssetByID(id uint64) (bool, error) {
|
func (s *DataStore) DeleteAssetByID(id uint64) (bool, error) {
|
||||||
tx := s.db.Delete(&types.Asset{}, id)
|
tx := s.db.Delete(&types.Asset{}, id)
|
||||||
if tx.Error != nil {
|
if tx.Error != nil {
|
||||||
@@ -98,16 +112,16 @@ func (s *DataStore) DeleteAssetByID(id uint64) (bool, error) {
|
|||||||
|
|
||||||
func (s *DataStore) GetShelfByID(id uint64) (*types.ShelfLocation, error) {
|
func (s *DataStore) GetShelfByID(id uint64) (*types.ShelfLocation, error) {
|
||||||
var result types.ShelfLocation
|
var result types.ShelfLocation
|
||||||
tx := s.db.Model(&types.ShelfLocation{}).Where("id = ?", id).First(&result)
|
tx := s.db.Joins("Building").Where(&types.ShelfLocation{ID: id}).First(&result)
|
||||||
if tx.Error != nil {
|
if tx.Error != nil {
|
||||||
return nil, fmt.Errorf("shelf %d not found", id)
|
return nil, fmt.Errorf("shelf %d not found: %v", id, tx.Error)
|
||||||
}
|
}
|
||||||
return &result, nil
|
return &result, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *DataStore) GetShelves(offset, limit int) ([]*types.ShelfLocation, error) {
|
func (s *DataStore) GetShelves(offset, limit uint64) ([]*types.ShelfLocation, error) {
|
||||||
var shelves []*types.ShelfLocation
|
var shelves []*types.ShelfLocation
|
||||||
s.db.Offset(offset).Limit(limit).Find(&shelves)
|
s.db.Joins("Building").Order("id asc").Offset(int(offset)).Limit(int(limit)).Find(&shelves)
|
||||||
if len(shelves) == 0 {
|
if len(shelves) == 0 {
|
||||||
return nil, fmt.Errorf("no shelves found")
|
return nil, fmt.Errorf("no shelves found")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,6 +26,11 @@ type IndexResponse struct {
|
|||||||
Version APIVersion `json:"version"`
|
Version APIVersion `json:"version"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type CountResponse struct {
|
||||||
|
*Response
|
||||||
|
Count int64 `json:"count"`
|
||||||
|
}
|
||||||
|
|
||||||
type APIError struct {
|
type APIError struct {
|
||||||
*Response
|
*Response
|
||||||
Err error `json:"-"`
|
Err error `json:"-"`
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ import (
|
|||||||
type Building struct {
|
type Building struct {
|
||||||
ID uint64 `gorm:"primarykey" json:"id"`
|
ID uint64 `gorm:"primarykey" json:"id"`
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
ShelfLocations []ShelfLocation `json:"shelves"`
|
ShelfLocations []ShelfLocation `json:"shelves,omitempty"`
|
||||||
CreatedAt time.Time `json:"created_at"`
|
CreatedAt time.Time `json:"created_at"`
|
||||||
UpdatedAt time.Time `json:"updated_at"`
|
UpdatedAt time.Time `json:"updated_at"`
|
||||||
DeletedAt gorm.DeletedAt `gorm:"index" json:"deleted_at,omitempty"`
|
DeletedAt gorm.DeletedAt `gorm:"index" json:"deleted_at,omitempty"`
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ type ShelfLocation struct {
|
|||||||
RoomNumber string `json:"room_number,omitempty"`
|
RoomNumber string `json:"room_number,omitempty"`
|
||||||
Description string `json:"description,omitempty"`
|
Description string `json:"description,omitempty"`
|
||||||
BuildingID uint64 `json:"-"`
|
BuildingID uint64 `json:"-"`
|
||||||
Building Building `json:"building"`
|
Building Building `json:"building",omitempty`
|
||||||
Assets []Asset `json:"assets,omitempty"`
|
Assets []Asset `json:"assets,omitempty"`
|
||||||
CreatedAt time.Time `json:"created_at"`
|
CreatedAt time.Time `json:"created_at"`
|
||||||
UpdatedAt time.Time `json:"updated_at"`
|
UpdatedAt time.Time `json:"updated_at"`
|
||||||
|
|||||||
Reference in New Issue
Block a user