server/api/v0/product.go

255 lines
6.0 KiB
Go

/**
* file: api/v0/product.go
* author: Theo Technicguy
* license: Apache 2.0
*
* Product tags API endpoint
*/
package apiv0
// TODO: refactor status code setting and json encoding into a function
import (
"encoding/json"
"errors"
"net/http"
"strconv"
"strings"
"git.licolas.net/hoffman/server/types"
"github.com/gorilla/mux"
)
const (
eanQry = "ean"
namesQry = "names"
tagsQry = "tags"
masterOperatorQry = "operator"
nameOperatorQry = "names_operator"
tagsOperatorQry = "tags_operator"
)
var (
ErrMismatchingIds = errors.New("mismatching request and product ids")
)
func getAllProducts(w http.ResponseWriter, r *http.Request) {
logger.Trace().Msg("getting all products")
products, err := lgc.GetAllProducts()
if err != nil {
respondWithError(w, http.StatusInternalServerError, err)
return
}
respond(w, http.StatusOK, products)
}
func getProductById(w http.ResponseWriter, r *http.Request) {
id, err := strconv.ParseUint(mux.Vars(r)[idQry], 10, 64)
if err != nil {
respondWithError(w, http.StatusBadRequest, err)
return
}
logger.Trace().Uint("product", uint(id)).Msg("getting product by id")
product, err := lgc.GetProductByID(uint(id))
if err != nil {
var status int
if err == types.ErrIDNotFound {
status = http.StatusNotFound
} else {
status = http.StatusInternalServerError
}
respondWithError(w, status, err)
return
}
respond(w, http.StatusOK, product)
}
func getProductsLike(w http.ResponseWriter, r *http.Request) {
logger.Trace().Msg("getting products like")
vars := r.URL.Query()
logger.Trace().Msg("parsing search tags")
tags := []uint{}
for _, tag := range strings.Split(vars.Get(tagsQry), ",") {
if tag == "" {
continue
}
uint64Tag, err := strconv.ParseUint(tag, 10, 64)
if err != nil {
respondWithError(w, http.StatusBadRequest, err)
return
}
tags = append(tags, uint(uint64Tag))
}
logger.Trace().Msg("parsing search names")
names := strings.Split(vars.Get(namesQry), ",")
if names[0] == "" {
names = []string{}
}
ps := &types.ProductSearch{
Ean: vars.Get(eanQry),
Names: names,
TagIds: tags,
Oprators: types.ProductSearchOperators{
Master: strings.ToUpper(vars.Get(masterOperatorQry)),
Names: strings.ToUpper(vars.Get(nameOperatorQry)),
TagIds: strings.ToUpper(vars.Get(tagsOperatorQry)),
},
}
ps.Oprators.FillDefaults()
logger.Debug().Any("product search", ps).Msg("created search")
products, err := lgc.GetProductsLike(ps)
if err != nil {
respondWithError(w, http.StatusInternalServerError, err)
return
}
respond(w, http.StatusOK, products)
}
func postProduct(w http.ResponseWriter, r *http.Request) {
logger.Trace().Msg("posting product")
var product types.Product
if err := json.NewDecoder(r.Body).Decode(&product); err != nil {
respondWithError(w, http.StatusBadRequest, err)
return
}
if err := lgc.CreateProduct(&product); err != nil {
respondWithError(w, http.StatusInternalServerError, err)
return
}
respond(w, http.StatusCreated, product)
}
func putProductById(w http.ResponseWriter, r *http.Request) {
id, err := strconv.ParseUint(mux.Vars(r)[idQry], 10, 64)
if err != nil {
respondWithError(w, http.StatusBadRequest, err)
return
}
logger := logger.With().Uint("product", uint(id)).Logger()
logger.Trace().Msg("putting product")
var newProduct types.Product
if err = json.NewDecoder(r.Body).Decode(&newProduct); err != nil {
respondWithError(w, http.StatusBadRequest, err)
return
}
logger.Trace().Msg("checking ids")
if id != uint64(newProduct.Id) {
respondWithError(w, http.StatusBadRequest, ErrMismatchingIds)
return
}
logger.Trace().Msg("updating product")
product, err := lgc.UpdateProduct(&newProduct)
if err != nil {
var status int
if err == types.ErrIDNotFound {
status = http.StatusNotFound
} else {
status = http.StatusInternalServerError
}
respondWithError(w, status, err)
return
}
respond(w, http.StatusCreated, product)
}
func deleteProductById(w http.ResponseWriter, r *http.Request) {
id, err := strconv.ParseUint(mux.Vars(r)[idQry], 10, 64)
if err != nil {
respondWithError(w, http.StatusBadRequest, err)
return
}
logger.With().Uint("product", uint(id)).Logger()
logger.Trace().Msg("deleting product")
product, err := lgc.DeleteProduct(uint(id))
if err != nil {
var status int
if err == types.ErrIDNotFound {
status = http.StatusNotFound
} else {
status = http.StatusInternalServerError
}
respondWithError(w, status, err)
return
}
respond(w, http.StatusAccepted, product)
}
func SetProductRoutes(router *mux.Router) {
productRouter := router.PathPrefix("/product").Subrouter()
productRouter.HandleFunc("", getAllProducts).Methods("GET")
productRouter.HandleFunc("/", getAllProducts).Methods("GET")
// Searches
searchPath := "/search"
eanVar := "{" + eanQry + ":[0-9]+}"
namesVar := "{" + namesQry + ":[^\x00-+.:-@[\\]^{|}~]+}"
tagVar := "{" + tagsQry + ":[0-9]+(?:,[0-9]+)*,?}"
productRouter.
HandleFunc(searchPath, getProductsLike).
Methods("GET").
Queries(eanQry, eanVar, namesQry, namesVar, tagsQry, tagVar)
productRouter.
HandleFunc(searchPath, getProductsLike).
Methods("GET").
Queries(eanQry, eanVar, namesQry, namesVar)
productRouter.
HandleFunc(searchPath, getProductsLike).
Methods("GET").
Queries(eanQry, eanVar, tagsQry, tagVar)
productRouter.
HandleFunc(searchPath, getProductsLike).
Methods("GET").
Queries(namesQry, namesVar, tagsQry, tagVar)
productRouter.
HandleFunc(searchPath, getProductsLike).
Methods("GET").
Queries(eanQry, eanVar)
productRouter.
HandleFunc(searchPath, getProductsLike).
Methods("GET").
Queries(namesQry, namesVar)
productRouter.
HandleFunc(searchPath, getProductsLike).
Methods("GET").
Queries(tagsQry, tagVar)
idPath := "/" + idVar
productRouter.HandleFunc(idPath, getProductById).Methods("GET")
productRouter.HandleFunc("", postProduct).Methods("POST")
productRouter.HandleFunc("/new", postProduct).Methods("POST")
productRouter.HandleFunc(idPath, putProductById).Methods("PUT")
productRouter.HandleFunc(idPath, deleteProductById).Methods("DELETE")
}