[TASK] add buy option (delete without tag - by secret)
This commit is contained in:
		
							parent
							
								
									ca0d349c04
								
							
						
					
					
						commit
						9c57445eeb
					
				|  | @ -8,7 +8,7 @@ fouled_deleted = "0m" | |||
| 
 | ||||
| [database] | ||||
| type = "sqlite3" | ||||
| #logging = true | ||||
| logging = true | ||||
| connection = "file::memory:?mode=memory&cache=shared" | ||||
| # For Master-Slave cluster | ||||
| # read_connection = "" | ||||
|  |  | |||
|  | @ -20,4 +20,5 @@ func BindAPI(router *goji.Mux) { | |||
| 
 | ||||
| 	router.HandleFunc(pat.Post("/api/goods/locking"), lockGoods) | ||||
| 	router.HandleFunc(pat.Delete("/api/goods/locking"), releaseGoods) | ||||
| 	router.HandleFunc(pat.Put("/api/goods/locking"), delLockedGoods) | ||||
| } | ||||
|  |  | |||
|  | @ -3,6 +3,7 @@ package http | |||
| 
 | ||||
| import ( | ||||
| 	"net/http" | ||||
| 	"time" | ||||
| 
 | ||||
| 	"github.com/genofire/hs_master-kss-monolith/lib/database" | ||||
| 	lib "github.com/genofire/hs_master-kss-monolith/lib/http" | ||||
|  | @ -45,12 +46,12 @@ func lockGoods(w http.ResponseWriter, r *http.Request) { | |||
| 	} | ||||
| 
 | ||||
| 	tx := database.Write.Begin() | ||||
| 	defer tx.Rollback() | ||||
| 	count := int64(0) | ||||
| 
 | ||||
| 	for _, good := range goods { | ||||
| 		if good.ProductID <= 0 { | ||||
| 			log.Warn("tried to log nothing") | ||||
| 			tx.Rollback() | ||||
| 			http.Error(w, "tried to log nothing", http.StatusBadRequest) | ||||
| 			return | ||||
| 		} | ||||
|  | @ -59,7 +60,6 @@ func lockGoods(w http.ResponseWriter, r *http.Request) { | |||
| 			db := g.FilterAvailable(tx).First(g) | ||||
| 			if db.RecordNotFound() { | ||||
| 				log.Warn("good not found") | ||||
| 				tx.Rollback() | ||||
| 				http.Error(w, "the good was not found in database", http.StatusNotFound) | ||||
| 				return | ||||
| 			} | ||||
|  | @ -69,7 +69,6 @@ func lockGoods(w http.ResponseWriter, r *http.Request) { | |||
| 
 | ||||
| 			if db.Error != nil || db.RowsAffected != 1 { | ||||
| 				http.Error(w, "the good was not found in database", http.StatusInternalServerError) | ||||
| 				tx.Rollback() | ||||
| 				log.Panic("there is more than one good locked: ", db.Error) | ||||
| 				return | ||||
| 			} | ||||
|  | @ -85,11 +84,71 @@ func lockGoods(w http.ResponseWriter, r *http.Request) { | |||
| 
 | ||||
| // Function to release locked goods
 | ||||
| func releaseGoods(w http.ResponseWriter, r *http.Request) { | ||||
| 	log := logger.HTTP(r) | ||||
| 	secret := r.Header.Get("secret") | ||||
| 	if secret == "" { | ||||
| 		log.Warn("no secred for locking given") | ||||
| 		http.Error(w, "no secred for locking given", http.StatusBadRequest) | ||||
| 		return | ||||
| 	} | ||||
| 
 | ||||
| 	log = log.WithField("lSecret", secret) | ||||
| 	var goods []*LockGood | ||||
| 
 | ||||
| 	err := lib.Read(r, &goods) | ||||
| 
 | ||||
| 	if err != nil { | ||||
| 		log.Warn(err.Error()) | ||||
| 		http.Error(w, err.Error(), http.StatusBadRequest) | ||||
| 		return | ||||
| 	} | ||||
| 
 | ||||
| 	if len(goods) <= 0 { | ||||
| 		log.Warn("tried to log nothing") | ||||
| 		http.Error(w, "tried to log nothing", http.StatusBadRequest) | ||||
| 		return | ||||
| 	} | ||||
| 
 | ||||
| 	tx := database.Write.Begin() | ||||
| 	defer tx.Rollback() | ||||
| 	for _, good := range goods { | ||||
| 		if good.ProductID <= 0 { | ||||
| 			log.Warn("tried to log nothing") | ||||
| 			http.Error(w, "tried to release nothing", http.StatusBadRequest) | ||||
| 			return | ||||
| 		} | ||||
| 		for i := 0; i < good.Count; i++ { | ||||
| 			g := &models.Good{} | ||||
| 			db := tx.Where(map[string]interface{}{"product_id": good.ProductID, "locked_secret": secret}).First(g) | ||||
| 			if db.RecordNotFound() { | ||||
| 				log.Warn("good not found") | ||||
| 				http.Error(w, "the good was not found in database", http.StatusNotFound) | ||||
| 				return | ||||
| 			} | ||||
| 
 | ||||
| 			g.Unlock(secret) | ||||
| 			db = tx.Save(g) | ||||
| 
 | ||||
| 			if db.Error != nil || db.RowsAffected != 1 { | ||||
| 				http.Error(w, "the good was not found in database", http.StatusInternalServerError) | ||||
| 				log.Panic("there is more than one good released: ", db.Error) | ||||
| 				return | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	lib.Write(w, true) | ||||
| 	tx.Commit() | ||||
| 	log.Info("done") | ||||
| } | ||||
| 
 | ||||
| // Function to del locked goods
 | ||||
| func delLockedGoods(w http.ResponseWriter, r *http.Request) { | ||||
| 	log := logger.HTTP(r) | ||||
| 	secret := r.Header.Get("secret") | ||||
| 	log = log.WithField("lSecret", secret) | ||||
| 
 | ||||
| 	db := database.Write.Model(&models.Good{}).Where(&models.Good{LockedSecret: secret}).Updates(map[string]interface{}{"locked_secret": nil, "locked_at": nil}) | ||||
| 	db := database.Write.Model(&models.Good{}).Where(&models.Good{LockedSecret: secret}).Updates(map[string]interface{}{"deleted_at": time.Now(), "locked_secret": ""}) | ||||
| 	err := db.Error | ||||
| 	result := db.RowsAffected | ||||
| 
 | ||||
|  | @ -101,7 +160,7 @@ func releaseGoods(w http.ResponseWriter, r *http.Request) { | |||
| 
 | ||||
| 	if result <= 0 { | ||||
| 		log.Warn("no goods found") | ||||
| 		http.Error(w, "there are no goods to release", http.StatusNotFound) | ||||
| 		http.Error(w, "there are no goods to buy", http.StatusNotFound) | ||||
| 		return | ||||
| 	} | ||||
| 
 | ||||
|  |  | |||
|  | @ -60,7 +60,54 @@ func TestLockGoods(t *testing.T) { | |||
| } | ||||
| 
 | ||||
| // Function to test releaseGoods()
 | ||||
| func TestReleaseGoods(t *testing.T) { | ||||
| func TestReleaseLockGoods(t *testing.T) { | ||||
| 	assertion, router := test.Init(t) | ||||
| 	good := &models.Good{ | ||||
| 		ProductID:    3, | ||||
| 		Comment:      "blabla", | ||||
| 		LockedSecret: "hiddenLockTest", | ||||
| 	} | ||||
| 	database.Write.Create(good) | ||||
| 
 | ||||
| 	BindAPI(router) | ||||
| 	session := test.NewSession(router) | ||||
| 
 | ||||
| 	_, w := session.JSONRequest("DELETE", "/api/goods/locking", []interface{}{LockGood{ProductID: 3, Count: 1}}) | ||||
| 	assertion.Equal(http.StatusBadRequest, w.StatusCode) | ||||
| 
 | ||||
| 	session.Header["secret"] = "hiddenLockTest" | ||||
| 
 | ||||
| 	_, w = session.JSONRequest("DELETE", "/api/goods/locking", 13) | ||||
| 	assertion.Equal(http.StatusBadRequest, w.StatusCode) | ||||
| 
 | ||||
| 	_, w = session.JSONRequest("DELETE", "/api/goods/locking", nil) | ||||
| 	assertion.Equal(http.StatusBadRequest, w.StatusCode) | ||||
| 
 | ||||
| 	_, w = session.JSONRequest("DELETE", "/api/goods/locking", []interface{}{LockGood{ProductID: 0, Count: 2}}) | ||||
| 	assertion.Equal(http.StatusBadRequest, w.StatusCode) | ||||
| 
 | ||||
| 	_, w = session.JSONRequest("DELETE", "/api/goods/locking", []interface{}{LockGood{ProductID: 3, Count: 2}}) | ||||
| 	assertion.Equal(http.StatusNotFound, w.StatusCode) | ||||
| 
 | ||||
| 	result, w := session.JSONRequest("DELETE", "/api/goods/locking", []interface{}{LockGood{ProductID: 3, Count: 1}}) | ||||
| 	assertion.Equal(http.StatusOK, w.StatusCode) | ||||
| 	resultBool := result.(bool) | ||||
| 	assertion.True(resultBool) | ||||
| 
 | ||||
| 	_, w = session.JSONRequest("DELETE", "/api/goods/locking", []interface{}{LockGood{ProductID: 3, Count: 1}}) | ||||
| 	assertion.Equal(http.StatusNotFound, w.StatusCode) | ||||
| 
 | ||||
| 	database.Close() | ||||
| 
 | ||||
| 	assertion.Panics(func() { | ||||
| 		_, w = session.JSONRequest("DELETE", "/api/goods/locking", []interface{}{LockGood{ProductID: 3, Count: 1}}) | ||||
| 		assertion.Equal(http.StatusInternalServerError, w.StatusCode) | ||||
| 	}) | ||||
| 
 | ||||
| 	test.Close() | ||||
| } | ||||
| 
 | ||||
| func TestDelLockGoods(t *testing.T) { | ||||
| 	now := time.Now() | ||||
| 	assertion, router := test.Init(t) | ||||
| 
 | ||||
|  | @ -75,18 +122,18 @@ func TestReleaseGoods(t *testing.T) { | |||
| 	session := test.NewSession(router) | ||||
| 
 | ||||
| 	session.Header["secret"] = "a" | ||||
| 	result, w := session.JSONRequest("DELETE", "/api/goods/locking", nil) | ||||
| 	result, w := session.JSONRequest("PUT", "/api/goods/locking", nil) | ||||
| 	assertion.Equal(http.StatusNotFound, w.StatusCode) | ||||
| 
 | ||||
| 	session.Header["secret"] = "hidden" | ||||
| 	result, w = session.JSONRequest("DELETE", "/api/goods/locking", nil) | ||||
| 	result, w = session.JSONRequest("PUT", "/api/goods/locking", nil) | ||||
| 	assertion.Equal(http.StatusOK, w.StatusCode) | ||||
| 	resultMap := result.(map[string]interface{}) | ||||
| 	count := resultMap["count"] | ||||
| 	assertion.Equal(float64(1), count) | ||||
| 
 | ||||
| 	database.Close() | ||||
| 	result, w = session.JSONRequest("DELETE", "/api/goods/locking", nil) | ||||
| 	result, w = session.JSONRequest("PUT", "/api/goods/locking", nil) | ||||
| 	assertion.Equal(http.StatusInternalServerError, w.StatusCode) | ||||
| 
 | ||||
| 	test.Close() | ||||
|  |  | |||
|  | @ -16,6 +16,9 @@ var GoodFreshnessTemplate string | |||
| 
 | ||||
| // Function to calculate a percent value from a given value and a maximum value
 | ||||
| func tempPercent(value, max int) int { | ||||
| 	if value >= max { | ||||
| 		return 100 | ||||
| 	} | ||||
| 	return value * 100 / max | ||||
| } | ||||
| 
 | ||||
|  |  | |||
|  | @ -13,6 +13,9 @@ func TestTempFuncs(t *testing.T) { | |||
| 	resultInt := tempPercent(3, 9) | ||||
| 	assert.Equal(33, resultInt) | ||||
| 
 | ||||
| 	resultInt = tempPercent(13, 9) | ||||
| 	assert.Equal(100, resultInt) | ||||
| 
 | ||||
| 	// TODO is there a other way to calc this?
 | ||||
| 	resultFloat := tempProcessRadius(3, 9, 0) | ||||
| 	assert.Equal(float64(0), resultFloat) | ||||
|  |  | |||
|  | @ -31,7 +31,7 @@ | |||
|                     <div class="default text">Select Product</div> | ||||
|                     <div class="menu"> | ||||
|                         <div class="item" ng-repeat="item in products" data-value="{{item.id}}"><img class="icon" | ||||
|                                                                                                      ng-src="{{'/api/good/availablity/'+item.id| reloadSrc}}"/>{{item.title}} | ||||
|                                                                                                      ng-src="{{'/api/good/availablity/'+item.id| reloadSrc}}"/>{{item.name}} | ||||
|                         </div> | ||||
|                     </div> | ||||
|                 </div> | ||||
|  | @ -55,13 +55,14 @@ | |||
|         <tbody> | ||||
|         <tr ng-repeat="item in cart"> | ||||
|             <td>{{item.count}}</td> | ||||
|             <td>{{getProduct(item.product_id).title}}</td> | ||||
|             <td>{{getProduct(item.product_id).name}}</td> | ||||
|             <td> | ||||
|                 <div class="ui button icon" ng-click="del(item)"><i class="icon trash"></i></div> | ||||
|             </td> | ||||
|         </tr> | ||||
|         </tbody> | ||||
|     </table> | ||||
|     <button class="ui icon button" tabindex="0" ng-click="buy()"><i class="icon shop"></i>Buy</button> | ||||
| </div> | ||||
| 
 | ||||
| <footer class="ui vertical footer segment"> | ||||
|  | @ -81,7 +82,8 @@ | |||
| 			'products': '/api-test/product/', | ||||
| 			'productById': '/api-test/product/%d/', | ||||
| 			'lockGoods': '/api/goods/locking', | ||||
| 			'unlockGoods': '/api/goods/locking' | ||||
| 			'unlockGoods': '/api/goods/locking', | ||||
| 			'buyGoods': '/api/goods/locking', | ||||
| 	  } | ||||
| 	}; | ||||
| 
 | ||||
|  | @ -127,8 +129,9 @@ | |||
| 	  }) | ||||
| 		.controller('MainCtrl',['$scope', '$http',function($scope, $http) { | ||||
| 			$scope.products = []; | ||||
| 			$scope.goods = {count:1}; | ||||
| 			$scope.goods = {count: 1}; | ||||
| 			$scope.cart = []; | ||||
| 			let secret = createUUID(); | ||||
| 
 | ||||
| 			$http.get(config.microservice_dependencies.products).then(function(res) { | ||||
| 				$scope.products = res.data; | ||||
|  | @ -136,19 +139,24 @@ | |||
| 
 | ||||
| 			function load() { | ||||
| 				const tmp = JSON.parse(localStorage.getItem("cart")); | ||||
| 				const secretTmp = JSON.parse(localStorage.getItem("cart")); | ||||
| 				if(tmp) { | ||||
| 					$scope.cart = tmp.map((item) => { | ||||
| 						delete item.$$hashKey | ||||
| 						return item; | ||||
| 					}); | ||||
| 				} | ||||
| 				if(secretTmp && secretTmp === "") { | ||||
| 					secret = secretTmp; | ||||
| 				} | ||||
| 			function save(){ | ||||
| 				localStorage.setItem("cart",JSON.stringify($scope.cart)); | ||||
| 			} | ||||
| 			function save() { | ||||
| 				localStorage.setItem("cart", JSON.stringify($scope.cart)); | ||||
| 				localStorage.setItem("secret", secret); | ||||
| 			} | ||||
| 			load(); | ||||
| 
 | ||||
| 			$scope.getProduct = function getProduct(id){ | ||||
| 			$scope.getProduct = function getProduct(id) { | ||||
| 				if(id) { | ||||
| 					return $scope.products.filter((item) => item.id === id)[0]; | ||||
| 				} | ||||
|  | @ -156,7 +164,6 @@ | |||
| 
 | ||||
| 			$scope.add = function add() { | ||||
| 				$scope.goods.product_id = selectedProduct; | ||||
| 				const secret = createUUID(); | ||||
| 				$http({ | ||||
| 					method: 'POST', | ||||
| 					headers: { | ||||
|  | @ -166,10 +173,9 @@ | |||
| 					data: [$scope.goods] | ||||
| 				}).then(function(res) { | ||||
| 					console.log('add', $scope.goods); | ||||
| 					$scope.goods.secret = secret; | ||||
| 					$scope.cart.push($scope.goods); | ||||
| 					save(); | ||||
| 					$scope.goods = {count:1}; | ||||
| 					$scope.goods = {count: 1}; | ||||
| 				}); | ||||
| 			}; | ||||
| 
 | ||||
|  | @ -177,9 +183,11 @@ | |||
| 				$http({ | ||||
| 					method: 'DELETE', | ||||
| 					headers: { | ||||
| 						'secret': entry.secret | ||||
| 						'secret': secret, | ||||
| 						'Content-Type': 'application/json', | ||||
| 					}, | ||||
| 					url: config.microservice_dependencies.unlockGoods | ||||
| 					url: config.microservice_dependencies.unlockGoods, | ||||
| 					data: [entry] | ||||
| 				}).then(function(res) { | ||||
| 					console.log('del', entry); | ||||
| 					$scope.cart = $scope.cart.filter((item) => item !== entry); | ||||
|  | @ -187,9 +195,25 @@ | |||
| 				}); | ||||
| 			}; | ||||
| 
 | ||||
| 			$scope.buy = function buy() { | ||||
| 				$http({ | ||||
| 					method: 'PUT', | ||||
| 					headers: { | ||||
| 						'secret': secret | ||||
| 					}, | ||||
| 					url: config.microservice_dependencies.buyGoods, | ||||
| 				}).then(function(res) { | ||||
| 					console.log('buy'); | ||||
| 					$scope.reset(); | ||||
| 				},function(err) { | ||||
| 					console.warn('buy', err); | ||||
| 				}); | ||||
| 			}; | ||||
| 
 | ||||
| 			$scope.reset = function reset() { | ||||
| 				console.log("reset"); | ||||
| 				localStorage.setItem("cart","[]"); | ||||
| 				localStorage.setItem("cart", "[]"); | ||||
| 				localStorage.setItem("secret", createUUID()); | ||||
| 				load(); | ||||
| 			}; | ||||
| 
 | ||||
|  |  | |||
		Reference in New Issue