diff --git a/models/book.go b/models/book.go index 6167908..6dd1c0e 100644 --- a/models/book.go +++ b/models/book.go @@ -27,51 +27,6 @@ func (Book) TableName() string { return "books" } -// Quantity is the quantity for a book -type Quantity struct { - ID int64 `xorm:"int(11) autoincr not null unique pk"` - BookID int64 `xorm:"int(11) not null"` - Quantity int64 `xorm:"int(11) not null"` - Created int64 `xorm:"created"` -} - -// TableName returns the name for the quantites table -func (Quantity) TableName() string { - return "books_quantities" -} - -// GetQuantityByBook returns the current quantity for a book -func GetQuantityByBook(book Book) (quantity int64, err error) { - bq := Quantity{BookID: book.ID} - has, err := x.Desc("id").Get(&bq) - if err != nil { - return 0, err - } - - if has { - quantity = bq.Quantity - } - - return quantity, nil -} - -// SetBookQuantity sets a new quantity for a book -func (book Book) setBookQuantity(quantity int64) (err error) { - // Check if the quantity already exists and only insert it if not - qty, err := GetQuantityByBook(book) - if err != nil { - return err - } - - if qty != quantity { - q := Quantity{BookID: book.ID, Quantity: quantity} - _, err = x.Insert(q) - return err - } - - return nil -} - // GetBookByID gets a Book by its ID func GetBookByID(ID int64) (book Book, exists bool, err error) { // Get the Book @@ -79,7 +34,7 @@ func GetBookByID(ID int64) (book Book, exists bool, err error) { if has { // Get the books quantity. We can't join it because xorm ignores the Quantity option in struct - book.Quantity, err = GetQuantityByBook(book) + book.Quantity, err = book.getQuantity() if err != nil { fmt.Println("Error getting quantity:", err) } diff --git a/models/books_add_update.go b/models/books_add_update.go index 279c5a5..d298810 100644 --- a/models/books_add_update.go +++ b/models/books_add_update.go @@ -75,7 +75,7 @@ func AddOrUpdateBook(book Book) (newBook Book, err error) { } // Set the Quantity - err = book.setBookQuantity(qty) + err = book.setQuantity(qty) if err != nil { return Book{}, err } diff --git a/models/books_delete.go b/models/books_delete.go index 600e025..5bb0add 100644 --- a/models/books_delete.go +++ b/models/books_delete.go @@ -18,6 +18,12 @@ func DeleteBookByID(id int64) error { // Delete all authors associated with that book _, err = x.Delete(&AuthorBook{BookID: id}) + if err != nil { + return err + } + + // Delete all quantites for this book + _, err = x.Delete(&Quantity{ItemID: id}) return err } diff --git a/models/books_list.go b/models/books_list.go index 9c4ab4b..e983efd 100644 --- a/models/books_list.go +++ b/models/books_list.go @@ -29,7 +29,7 @@ func ListBooks(searchterm string) (books []*Book, err error) { for i, book := range books { // Get quantities - books[i].Quantity, err = GetQuantityByBook(*book) + books[i].Quantity, err = book.getQuantity() if err != nil { fmt.Println("Error getting quantity:", err) } diff --git a/models/item.go b/models/item.go new file mode 100644 index 0000000..a0595f1 --- /dev/null +++ b/models/item.go @@ -0,0 +1,31 @@ +package models + +// Item holds an item +type Item struct { + ID int64 `xorm:"int(11) autoincr not null unique pk" json:"id"` + Title string `xorm:"varchar(250)" json:"title"` + Price float64 `xorm:"double" json:"price"` + Description string `xorm:"varchar(750)" json:"description"` + Other string `xorm:"varchar(750)" json:"other"` + Created int64 `xorm:"created" json:"created"` + Updated int64 `xorm:"updated" json:"updated"` + + Quantity int64 `xorm:"-" json:"quantity"` +} + +// TableName returns the table name for items +func (Item) TableName() string { + return "items" +} + +// GetItemByID returns an item by its ID +func GetItemByID(id int64) (item Item, exists bool, err error) { + exists, err = x.Id(id).Get(&item) + if err != nil { + return + } + + item.Quantity, err = item.getQuantity() + + return +} diff --git a/models/items_add_update.go b/models/items_add_update.go new file mode 100644 index 0000000..43cfd66 --- /dev/null +++ b/models/items_add_update.go @@ -0,0 +1,33 @@ +package models + +// AddOrUpdateItem adds or updates a item from a item struct +func AddOrUpdateItem(item Item) (newItem Item, err error) { + // save the quantity for later use + qty := item.Quantity + + if item.ID == 0 { + if item.Title != "" { // Only insert it if the title is not empty + _, err = x.Insert(&item) + + if err != nil { + return Item{}, err + } + } + } else { + _, err = x.ID(item.ID).Update(&item) + + if err != nil { + return Item{}, err + } + } + + // Set the Quantity + err = item.setQuantity(qty) + if err != nil { + return Item{}, err + } + + newItem, _, err = GetItemByID(item.ID) + + return newItem, err +} diff --git a/models/items_delete.go b/models/items_delete.go new file mode 100644 index 0000000..28c80d1 --- /dev/null +++ b/models/items_delete.go @@ -0,0 +1,23 @@ +package models + +import "fmt" + +// DeleteItemByID deletes a item by its ID +func DeleteItemByID(id int64) error { + // Check if the id is 0 + if id == 0 { + return fmt.Errorf("ID cannot be 0") + } + + // Delete the item + _, err := x.Id(id).Delete(&Item{}) + + if err != nil { + return err + } + + // Delete all quantites for this item + _, err = x.Delete(&Quantity{ItemID: id}) + + return err +} diff --git a/models/items_list.go b/models/items_list.go new file mode 100644 index 0000000..99a823b --- /dev/null +++ b/models/items_list.go @@ -0,0 +1,28 @@ +package models + +// ListItems returns a list with all items, filtered by an optional searchstring +func ListItems(searchterm string) (items []Item, err error) { + + if searchterm == "" { + err = x.Find(&items) + } else { + err = x. + Where("title LIKE ?", "%"+searchterm+"%"). + Find(&items) + } + + if err != nil { + return []Item{}, err + } + + // Range over all items and get their quantity. + // Yes I know how inperformant this is. But we can't use a join here because of the way xorm handels the quantity property. + for i, item := range items { + items[i].Quantity, err = item.getQuantity() + if err != nil { + return []Item{}, err + } + } + + return items, nil +} diff --git a/models/models.go b/models/models.go index 302c169..51ea1d7 100644 --- a/models/models.go +++ b/models/models.go @@ -32,6 +32,8 @@ func SetEngine() (err error) { x.Sync(&AuthorBook{}) x.Sync(&Status{}) x.Sync(&Quantity{}) + x.Sync(&quantityRelation{}) + x.Sync(&Item{}) x.ShowSQL(Config.Database.ShowQueries) diff --git a/models/publishers_add.go b/models/publishers_add_update.go similarity index 89% rename from models/publishers_add.go rename to models/publishers_add_update.go index 21bdc41..7bd4f18 100644 --- a/models/publishers_add.go +++ b/models/publishers_add_update.go @@ -11,7 +11,7 @@ func AddOrUpdatePublisher(publisher Publisher) (newPublisher Publisher, err erro } } } else { - _, err = x.Where("id = ?", publisher.ID).Update(&publisher) + _, err = x.ID(publisher.ID).Update(&publisher) if err != nil { return Publisher{}, err diff --git a/models/quantity.go b/models/quantity.go new file mode 100644 index 0000000..c1dd32d --- /dev/null +++ b/models/quantity.go @@ -0,0 +1,160 @@ +package models + +// Quantity is the quantity for a book +type Quantity struct { + ID int64 `xorm:"int(11) autoincr not null unique pk"` + ItemID int64 `xorm:"int(11) not null"` + Quantity int64 `xorm:"int(11) not null"` + Created int64 `xorm:"created"` +} + +// TableName returns the name for the quantites table +func (Quantity) TableName() string { + return "quantities" +} + +// Quantityrelations holds information about wherether the quantity we are currently dealing with is one of a book or an item +// We cannot directly link quantites to items and books because they can have the same id... which is why we need this. +type quantityRelation struct { + ID int64 `xorm:"int(11) autoincr not null unique pk"` + ItemID int64 `xorm:"int(11) not null"` + BookID int64 `xorm:"int(11) not null"` + Created int64 `xorm:"created"` +} + +// TableName returns the name for the quantites table +func (quantityRelation) TableName() string { + return "quantity_relations" +} + +// GetQuantity gets the latest quantity +func GetQuantity(itemID int64) (quantity int64, err error) { + + // Return nothing if we dont have an item + if itemID == 0 { + return 0, nil + } + + // Get the quanitty + bq := Quantity{ItemID: itemID} + has, err := x.Desc("id").Get(&bq) + if err != nil { + return 0, err + } + + if has { + quantity = bq.Quantity + } + + return quantity, nil +} + +// SetQuantity sets the quantity for an item +func SetQuantity(itemID, quantity int64) (err error) { + // Check if the quantity already exists and only insert it if not + qty, err := GetQuantity(itemID) + if err != nil { + return err + } + + if qty != quantity { + q := Quantity{ItemID: itemID, Quantity: quantity} + _, err = x.Insert(q) + return err + } + + return nil +} + +// ===== ITEMS ===== + +// Get item quantity with relation +func (item Item) getQuantity() (quantity int64, err error) { + // get the quantity relation for the item + qtyID, _, err := item.getQuantityRelation() + if err != nil { + return 0, err + } + + return GetQuantity(qtyID) +} + +func (item Item) getQuantityRelation() (qtyID int64, exists bool, err error) { + // get the quantity relation for the item + qty := quantityRelation{ItemID: item.ID} + has, err := x.Get(&qty) + if err != nil { + return 0, false, err + } + + return qty.ID, has, nil +} + +// Set item quantity with relation +func (item Item) setQuantity(quantity int64) (err error) { + // Check if the relation already exists, if not, create a new one + qtyItemID, exists, err := item.getQuantityRelation() + if err != nil { + return + } + + if !exists { + rel := quantityRelation{ItemID: item.ID} + _, err := x.Insert(&rel) + if err != nil { + return err + } + + qtyItemID = rel.ID + } + + // Insert the new quantity + return SetQuantity(qtyItemID, quantity) +} + +// ===== BOOKS ===== + +// Get book quantity with relation +func (book Book) getQuantity() (quantity int64, err error) { + // get the quantity relation for the item + qtyID, _, err := book.getQuantityRelation() + if err != nil { + return 0, err + } + + return GetQuantity(qtyID) +} + +// Get the quantity relation +func (book Book) getQuantityRelation() (qtyID int64, exists bool, err error) { + // get the quantity relation for the item + qty := quantityRelation{BookID: book.ID} + has, err := x.Get(&qty) + if err != nil { + return 0, false, err + } + + return qty.ID, has, nil +} + +// Set book quantity with relation +func (book Book) setQuantity(quantity int64) (err error) { + // Check if the relation already exists, if not, create a new one + qtyItemID, exists, err := book.getQuantityRelation() + if err != nil { + return + } + + if !exists { + rel := quantityRelation{BookID: book.ID} + _, err := x.Insert(&rel) + if err != nil { + return err + } + + qtyItemID = rel.ID + } + + // Insert the new quantity + return SetQuantity(qtyItemID, quantity) +} diff --git a/routes/api/v1/items_add_update.go b/routes/api/v1/items_add_update.go new file mode 100644 index 0000000..1b8062e --- /dev/null +++ b/routes/api/v1/items_add_update.go @@ -0,0 +1,58 @@ +package v1 + +import ( + "encoding/json" + "git.mowie.cc/konrad/Library/models" + "github.com/labstack/echo" + "net/http" + "strconv" + "strings" +) + +type itemPayload struct { + Item models.Item `json:"item" form:"item"` +} + +// ItemAddOrUpdate is the handler to add a item +func ItemAddOrUpdate(c echo.Context) error { + // Check for Request Content + itemFromString := c.FormValue("item") + var datItem models.Item + + if itemFromString == "" { + b := new(itemPayload) + if err := c.Bind(b); err != nil { + return c.JSON(http.StatusBadRequest, models.Message{"No item model provided"}) + } + datItem = b.Item + } else { + // Decode the JSON + dec := json.NewDecoder(strings.NewReader(itemFromString)) + err := dec.Decode(&datItem) + + if err != nil { + return c.JSON(http.StatusInternalServerError, models.Message{"Error decoding item: " + err.Error()}) + } + } + + // Check if we have an ID other than the one in the struct + id := c.Param("id") + if id != "" { + // Make int + itemID, err := strconv.ParseInt(id, 10, 64) + + if err != nil { + return c.JSON(http.StatusInternalServerError, models.Message{"Could not get item id"}) + } + datItem.ID = itemID + } + + // Insert or update the item + newItem, err := models.AddOrUpdateItem(datItem) + + if err != nil { + return c.JSON(http.StatusInternalServerError, models.Message{"Error"}) + } + + return c.JSON(http.StatusOK, newItem) +} diff --git a/routes/api/v1/items_delete.go b/routes/api/v1/items_delete.go new file mode 100644 index 0000000..ac63a8b --- /dev/null +++ b/routes/api/v1/items_delete.go @@ -0,0 +1,41 @@ +package v1 + +import ( + "git.mowie.cc/konrad/Library/models" + "github.com/labstack/echo" + "net/http" + "strconv" +) + +// ItemDelete is the handler to delete a item +func ItemDelete(c echo.Context) error { + + id := c.Param("id") + + // Make int + itemID, err := strconv.ParseInt(id, 10, 64) + + if err != nil { + return c.JSON(http.StatusInternalServerError, models.Message{"Could not get item infos"}) + } + + // Check if the item exists + _, exists, err := models.GetItemByID(itemID) + + if err != nil { + return c.JSON(http.StatusInternalServerError, models.Message{"Could get item"}) + } + + if !exists { + return c.JSON(http.StatusBadRequest, models.Message{"The item does not exist."}) + } + + // Delete it + err = models.DeleteItemByID(itemID) + + if err != nil { + return c.JSON(http.StatusInternalServerError, models.Message{"Could not delete item"}) + } + + return c.JSON(http.StatusOK, models.Message{"success"}) +} diff --git a/routes/api/v1/items_list.go b/routes/api/v1/items_list.go new file mode 100644 index 0000000..fd4b9c5 --- /dev/null +++ b/routes/api/v1/items_list.go @@ -0,0 +1,22 @@ +package v1 + +import ( + "git.mowie.cc/konrad/Library/models" + "github.com/labstack/echo" + "net/http" +) + +// ItemsList is the handler to list Items, optionally filtered +func ItemsList(c echo.Context) error { + + // Prepare the searchterm + search := c.QueryParam("s") + + list, err := models.ListItems(search) + + if err != nil { + return c.JSON(http.StatusInternalServerError, models.Message{"Error getting items"}) + } + + return c.JSON(http.StatusOK, list) +} diff --git a/routes/api/v1/items_show.go b/routes/api/v1/items_show.go new file mode 100644 index 0000000..24d3e74 --- /dev/null +++ b/routes/api/v1/items_show.go @@ -0,0 +1,38 @@ +package v1 + +import ( + "git.mowie.cc/konrad/Library/models" + "github.com/labstack/echo" + "net/http" + "strconv" +) + +// ItemShow is the handler to show informations about a item +func ItemShow(c echo.Context) error { + item := c.Param("id") + + if item == "" { + return c.JSON(http.StatusBadRequest, models.Message{"Item ID cannot be empty."}) + } + + // Make int + itemID, err := strconv.ParseInt(item, 10, 64) + if err != nil { + return c.JSON(http.StatusInternalServerError, models.Message{"Error getting item infos."}) + } + + // Get item Infos + itemInfos, exists, err := models.GetItemByID(itemID) + + if err != nil { + return c.JSON(http.StatusInternalServerError, models.Message{"Error getting item infos."}) + } + + // Check if it exists + if !exists { + return c.JSON(http.StatusNotFound, models.Message{"Item not found."}) + } + + return c.JSON(http.StatusOK, itemInfos) + +} diff --git a/routes/api/v1/publishers_add_update.go b/routes/api/v1/publishers_add_update.go index 5ebab9f..7d74876 100644 --- a/routes/api/v1/publishers_add_update.go +++ b/routes/api/v1/publishers_add_update.go @@ -2,7 +2,6 @@ package v1 import ( "encoding/json" - "fmt" "git.mowie.cc/konrad/Library/models" "github.com/labstack/echo" "net/http" @@ -23,7 +22,6 @@ func PublisherAddOrUpdate(c echo.Context) error { if publisherFromString == "" { b := new(publisherPayload) if err := c.Bind(b); err != nil { - fmt.Println(err) return c.JSON(http.StatusBadRequest, models.Message{"No publisher model provided"}) } datPublisher = b.Publisher @@ -44,7 +42,7 @@ func PublisherAddOrUpdate(c echo.Context) error { publisherID, err := strconv.ParseInt(id, 10, 64) if err != nil { - return c.JSON(http.StatusInternalServerError, models.Message{"Could not get book id"}) + return c.JSON(http.StatusInternalServerError, models.Message{"Could not get item id"}) } datPublisher.ID = publisherID } diff --git a/routes/api/v1/publisher_show.go b/routes/api/v1/publishers_show.go similarity index 100% rename from routes/api/v1/publisher_show.go rename to routes/api/v1/publishers_show.go diff --git a/routes/routes.go b/routes/routes.go index aff5759..480d65c 100644 --- a/routes/routes.go +++ b/routes/routes.go @@ -74,6 +74,10 @@ func RegisterRoutes(e *echo.Echo) { a.GET("/publishers/:id", apiv1.PublisherShow) a.GET("/publishers/search", apiv1.PublisherSearch) + // Lookup Items + a.GET("/items", apiv1.ItemsList) + a.GET("/items/:id", apiv1.ItemShow) + // Lookup Status a.GET("/status", apiv1.StatusListShow) a.GET("/status/:id", apiv1.StatusByIDShow) @@ -98,6 +102,11 @@ func RegisterRoutes(e *echo.Echo) { a.DELETE("/publishers/:id", apiv1.PublisherDelete) a.POST("/publishers/:id", apiv1.PublisherAddOrUpdate) + // Manage Items + a.PUT("/items", apiv1.ItemAddOrUpdate) + a.DELETE("/items/:id", apiv1.ItemDelete) + a.POST("/items/:id", apiv1.ItemAddOrUpdate) + // Manage Users /* @@ -111,7 +120,7 @@ func RegisterRoutes(e *echo.Echo) { POST /logout - ausloggen GET /books/:id - ✔ Buch anzeigen - POST /books/:id - |Buch bearbeiten (inkl mengen) + POST /books/:id - ✔ |Buch bearbeiten (inkl mengen) DELETE /books/:id - ✔ |Buch löschen (+alle einträge in authors_books löschen dessen Bush dazu gehört) GET /books/search?s=se - ✔ Suchen GET /books/list - ✔ Auflisten