diff --git a/api/api.go b/api/api.go index 51916791c938a72a7816f4da3663ecdbe984b255..d846436d6292c032acb5d17fd1399e06c8db53b2 100644 --- a/api/api.go +++ b/api/api.go @@ -18,6 +18,7 @@ func initDB(dbPath string) (*gorm.DB, error) { } db.AutoMigrate(&Member{}) + db.AutoMigrate(&Product{}) return db, err } @@ -29,10 +30,17 @@ func Init(dbPath string, signKey string, r *mux.Router) error { a := api{db, []byte(signKey)} r.HandleFunc("/signin", a.SignIn).Methods("POST") + r.HandleFunc("/member", a.auth(a.ListMembers)).Methods("GET") r.HandleFunc("/member", a.auth(a.AddMember)).Methods("POST") r.HandleFunc("/member/{num:[0-9]+}", a.auth(a.GetMember)).Methods("GET") r.HandleFunc("/member/{num:[0-9]+}", a.auth(a.UpdateMember)).Methods("PUT") r.HandleFunc("/member/{num:[0-9]+}", a.auth(a.DeleteMember)).Methods("DELETE") + + r.HandleFunc("/product", a.auth(a.ListProducts)).Methods("GET") + r.HandleFunc("/product", a.auth(a.AddProduct)).Methods("POST") + r.HandleFunc("/product/{code:[0-9]+}", a.auth(a.GetProduct)).Methods("GET") + r.HandleFunc("/product/{code:[0-9]+}", a.auth(a.UpdateProduct)).Methods("PUT") + r.HandleFunc("/product/{code:[0-9]+}", a.auth(a.DeleteProduct)).Methods("DELETE") return nil } diff --git a/api/member.go b/api/member.go index 2408b33d02946d97ca1ddc771b591f3948bbcc25..60fa3e587e8966a5491dc7e73883c5c723d39cea 100644 --- a/api/member.go +++ b/api/member.go @@ -11,8 +11,8 @@ import ( type Member struct { gorm.Model `json:"-"` - Num int `json:"num",gorm:"unique;index"` - Name string `json:"name",gorm:"unique;index"` + Num int `json:"num" gorm:"unique;index"` + Name string `json:"name" gorm:"unique;index"` Email string `json:"email"` Balance int `json:"balance"` Role string `json:"role"` diff --git a/api/member_test.go b/api/member_test.go index a0108f6708d1026ed59a090f0dece52005641b17..330d3734b5ab85b41a21a0d001ed7d124bfe2cb0 100644 --- a/api/member_test.go +++ b/api/member_test.go @@ -5,7 +5,7 @@ import ( "testing" ) -func TestAddList(t *testing.T) { +func TestMemberAddList(t *testing.T) { tapi := newTestAPI(t) defer tapi.close() @@ -36,7 +36,7 @@ func TestAddList(t *testing.T) { } } -func TestGetDelete(t *testing.T) { +func TestMemberGetDelete(t *testing.T) { tapi := newTestAPI(t) defer tapi.close() @@ -67,9 +67,14 @@ func TestGetDelete(t *testing.T) { if resp.StatusCode != http.StatusOK { t.Error("Can't find the member:", resp.Status) } + + resp = tapi.do("GET", "/member/10", nil, nil) + if resp.StatusCode != http.StatusNotFound { + t.Error("Expected not found after delete:", resp.Status, resp.Body) + } } -func TestPut(t *testing.T) { +func TestMemberUpdate(t *testing.T) { tapi := newTestAPI(t) defer tapi.close() diff --git a/api/product.go b/api/product.go new file mode 100644 index 0000000000000000000000000000000000000000..07a8ca3c62e4a70cf27ef5f78fa409dc3e737924 --- /dev/null +++ b/api/product.go @@ -0,0 +1,146 @@ +package api + +import ( + "encoding/json" + "log" + "net/http" + + "github.com/gorilla/mux" + "gorm.io/gorm" +) + +type Product struct { + gorm.Model `json:"-"` + Code int `json:"code" gorm:"unique;index"` + Name string `json:"name" gorm:"unique;index"` + Price int `json:"price"` + Stock int `json:"stock"` +} + +func (a *api) AddProduct(w http.ResponseWriter, req *http.Request) { + var product Product + err := json.NewDecoder(req.Body).Decode(&product) + if err != nil { + log.Printf("Can't create product: %v", err) + w.WriteHeader(http.StatusInternalServerError) + return + } + err = a.db.Create(&product).Error + if err != nil { + log.Printf("Can't create product: %v\n%v", err, product) + w.WriteHeader(http.StatusInternalServerError) + return + } + + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(http.StatusCreated) + err = json.NewEncoder(w).Encode(product) + if err != nil { + log.Printf("Can't encode added product: %v", err) + w.WriteHeader(http.StatusInternalServerError) + return + } +} + +func (a *api) ListProducts(w http.ResponseWriter, req *http.Request) { + var products []Product + err := a.db.Find(&products).Error + if err != nil { + log.Printf("Can't list products: %v", err) + w.WriteHeader(http.StatusInternalServerError) + return + } + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + err = json.NewEncoder(w).Encode(products) + if err != nil { + log.Printf("Can't encode products: %v", err) + w.WriteHeader(http.StatusInternalServerError) + return + } +} + +func (a *api) GetProduct(w http.ResponseWriter, req *http.Request) { + vars := mux.Vars(req) + var product Product + err := a.db.Where("code = ?", vars["code"]).First(&product).Error + if err != nil { + if err.Error() == "record not found" { + w.WriteHeader(http.StatusNotFound) + return + } + log.Printf("Can't get product %s: %v", vars["code"], err) + w.WriteHeader(http.StatusInternalServerError) + return + } + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + err = json.NewEncoder(w).Encode(product) + if err != nil { + log.Printf("Can't encode product: %v", err) + w.WriteHeader(http.StatusInternalServerError) + return + } +} + +func (a *api) DeleteProduct(w http.ResponseWriter, req *http.Request) { + vars := mux.Vars(req) + err := a.db.Where("code = ?", vars["code"]).Delete(&Product{}).Error + if err != nil { + log.Printf("Can't delete product %s: %v", vars["code"], err) + w.WriteHeader(http.StatusInternalServerError) + return + } + w.WriteHeader(http.StatusOK) +} + +func (a *api) UpdateProduct(w http.ResponseWriter, req *http.Request) { + var product Product + err := json.NewDecoder(req.Body).Decode(&product) + if err != nil { + log.Printf("Can't decode memeber: %v", err) + w.WriteHeader(http.StatusInternalServerError) + return + } + + vars := mux.Vars(req) + var dbProduct Product + err = a.db.Where("code = ?", vars["code"]).First(&dbProduct).Error + if err != nil { + if err.Error() == "record not found" { + w.WriteHeader(http.StatusNotFound) + return + } + log.Printf("Can't get product %s: %v", vars["code"], err) + w.WriteHeader(http.StatusInternalServerError) + return + } + + if product.Code != 0 { + dbProduct.Code = product.Code + } + if product.Name != "" { + dbProduct.Name = product.Name + } + if product.Price >= 0 { + dbProduct.Price = product.Price + } + if product.Stock >= 0 { + dbProduct.Stock = product.Stock + } + err = a.db.Save(&dbProduct).Error + if err != nil { + log.Printf("Can't update product %s: %v %v", vars["code"], err, product) + w.WriteHeader(http.StatusInternalServerError) + return + } + + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(http.StatusAccepted) + err = json.NewEncoder(w).Encode(dbProduct) + if err != nil { + log.Printf("Can't encode updated product: %v", err) + w.WriteHeader(http.StatusInternalServerError) + return + } +} diff --git a/api/product_test.go b/api/product_test.go new file mode 100644 index 0000000000000000000000000000000000000000..66b7ddcc106fe1b2410547fecac9a576114633cb --- /dev/null +++ b/api/product_test.go @@ -0,0 +1,106 @@ +package api + +import ( + "net/http" + "testing" +) + +func TestProductAddList(t *testing.T) { + tapi := newTestAPI(t) + defer tapi.close() + + product := Product{ + Code: 234, + Name: "Aceite", + Price: 1700, + Stock: 10, + } + resp := tapi.do("POST", "/product", product, nil) + if resp.StatusCode != http.StatusCreated { + t.Fatal("Can't create product:", resp.Status) + } + var products []Product + resp = tapi.do("GET", "/product", nil, &products) + if resp.StatusCode != http.StatusOK { + t.Fatal("Can't get products:", resp.Status) + } + + if len(products) != 1 { + t.Fatal("Wrong number of products", len(products), products) + } + if products[0].Name != "Aceite" { + t.Error("Wrong name:", products[0].Name) + } + if products[0].Price != 1700 { + t.Error("Wrong price:", products[0].Price) + } +} + +func TestProductGetDelete(t *testing.T) { + tapi := newTestAPI(t) + defer tapi.close() + + resp := tapi.do("GET", "/product/234", nil, nil) + if resp.StatusCode != http.StatusNotFound { + t.Error("Expected not found:", resp.Status, resp.Body) + } + product := Product{ + Code: 234, + Name: "Aceite", + Price: 1700, + Stock: 10, + } + resp = tapi.do("POST", "/product", product, nil) + if resp.StatusCode != http.StatusCreated { + t.Fatal("Can't create product:", resp.Status) + } + + var gotProduct Product + resp = tapi.do("GET", "/product/234", nil, &gotProduct) + if resp.StatusCode != http.StatusOK { + t.Error("Can't find the product:", resp.Status) + } + if gotProduct.Code != 234 { + t.Error("Wrong product:", gotProduct.Code) + } + resp = tapi.do("DELETE", "/product/234", nil, nil) + if resp.StatusCode != http.StatusOK { + t.Error("Can't find the product:", resp.Status) + } + + resp = tapi.do("GET", "/product/234", nil, nil) + if resp.StatusCode != http.StatusNotFound { + t.Error("Expected not found after delete:", resp.Status, resp.Body) + } +} + +func TestProductUpdate(t *testing.T) { + tapi := newTestAPI(t) + defer tapi.close() + + product := Product{ + Code: 234, + Name: "Aceite", + Price: 1700, + Stock: 10, + } + resp := tapi.do("POST", "/product", product, nil) + if resp.StatusCode != http.StatusCreated { + t.Fatal("Can't create product:", resp.Status) + } + + product.Stock = product.Stock - 5 + resp = tapi.do("PUT", "/product/234", product, nil) + if resp.StatusCode != http.StatusAccepted { + t.Fatal("Can't update product:", resp.Status) + } + + var gotProduct Product + resp = tapi.do("GET", "/product/234", nil, &gotProduct) + if resp.StatusCode != http.StatusOK { + t.Error("Can't find the product:", resp.Status) + } + if gotProduct.Stock != 5 { + t.Error("Wrong sotck:", gotProduct) + } +}