From b6ade49a4152fafdcac2e880d32e4b0794f88c03 Mon Sep 17 00:00:00 2001 From: Martin Geno Date: Thu, 18 May 2017 23:42:00 +0200 Subject: [PATCH] [TASK] delete good and fouled timer --- cmd/stock/main.go | 7 +++++ config_example.conf | 2 ++ http/bindapi.go | 1 + http/good.go | 30 +++++++++++++++++++++ http/good_test.go | 42 +++++++++++++++++++++++++++++ models/config.go | 12 +++++---- models/good.go | 5 ++-- runtime/good_fouled.go | 24 +++++++++++++++++ runtime/good_fouled_test.go | 52 ++++++++++++++++++++++++++++++++++++ runtime/good_release.go | 4 +-- runtime/good_release_test.go | 6 +++-- 11 files changed, 174 insertions(+), 11 deletions(-) create mode 100644 runtime/good_fouled.go create mode 100644 runtime/good_fouled_test.go diff --git a/cmd/stock/main.go b/cmd/stock/main.go index 97b8e22..ecf5c76 100644 --- a/cmd/stock/main.go +++ b/cmd/stock/main.go @@ -7,11 +7,13 @@ import ( "os" "os/signal" "syscall" + "time" "github.com/NYTimes/gziphandler" goji "goji.io" "goji.io/pat" + "github.com/genofire/golang-lib/worker" web "github.com/genofire/hs_master-kss-monolith/http" "github.com/genofire/hs_master-kss-monolith/lib/database" "github.com/genofire/hs_master-kss-monolith/lib/log" @@ -48,8 +50,12 @@ func main() { } grw := runtime.NewGoodReleaseWorker(config.GoodRelease) cw := runtime.NewCacheWorker() + fw := worker.NewWorker(config.FouledDeleter.Duration, func() { runtime.GoodFouled() }) go grw.Start() go cw.Start() + if config.FouledDeleter.Duration != time.Duration(0) { + go fw.Start() + } // Startwebsrver router := goji.NewMux() web.BindAPI(router) @@ -77,6 +83,7 @@ func main() { srv.Close() grw.Close() cw.Close() + fw.Close() database.Close() log.Log.Info("received", sig) diff --git a/config_example.conf b/config_example.conf index 5a8d718..f4976e8 100644 --- a/config_example.conf +++ b/config_example.conf @@ -4,6 +4,8 @@ webroot = "webroot" good_availablity_template = "contrib/good_availablity.svg" good_freshness_template = "contrib/good_freshness.svg" +fouled_deleted = "0m" + [database] type = "sqlite3" # logging = true diff --git a/http/bindapi.go b/http/bindapi.go index 6139bc9..eca7ae2 100644 --- a/http/bindapi.go +++ b/http/bindapi.go @@ -16,4 +16,5 @@ func BindAPI(router *goji.Mux) { router.HandleFunc(pat.Get("/api/good/availablity/:productid"), getGoodAvailability) router.HandleFunc(pat.Get("/api/good/freshness/:goodid"), getGoodFreshness) router.HandleFunc(pat.Post("/api/good/:productid"), http.PermissionHandler(addGood, runtime.HasPermission, runtime.PermissionCreateGood)) + router.HandleFunc(pat.Delete("/api/good/:goodid"), http.PermissionHandler(delGood, runtime.HasPermission, runtime.PermissionDeleteGood)) } diff --git a/http/good.go b/http/good.go index 2683eaf..ed71931 100644 --- a/http/good.go +++ b/http/good.go @@ -4,6 +4,7 @@ package http import ( "net/http" "strconv" + "time" "goji.io/pat" @@ -55,3 +56,32 @@ func addGood(w http.ResponseWriter, r *http.Request) { log.Info("done") } + +// Function that returns the freshness of a good +func delGood(w http.ResponseWriter, r *http.Request) { + log := logger.HTTP(r) + id, err := strconv.ParseInt(pat.Param(r, "goodid"), 10, 64) + if err != nil { + log.Warn("wrong goodid format") + http.Error(w, "the good id has a false format", http.StatusNotAcceptable) + return + } + log = log.WithField("goodid", id) + + now := time.Now() + var good models.Good + good.ID = id + good.ManuelleDelete = true + good.DeletedAt = &now + db := database.Write.Save(&good) + if db.Error != nil { + log.Warnf("good could not delete: %s", db.Error) + http.Error(w, "the good could not delete", http.StatusInternalServerError) + return + } + if db.RowsAffected != 1 { + log.Warnf("good could not found: %s", db.Error) + http.Error(w, "the good could not found", http.StatusNotFound) + return + } +} diff --git a/http/good_test.go b/http/good_test.go index 838dd62..b44e914 100644 --- a/http/good_test.go +++ b/http/good_test.go @@ -72,3 +72,45 @@ func TestAddGood(t *testing.T) { test.Close() } + +// Function to test delGood() +func TestDelGood(t *testing.T) { + assertion, router := test.Init(t) + + BindAPI(router) + runtime.PermissionURL = "http://localhost:8080/api-test/session/%s/%d/" + session := test.NewSession(router) + + good := models.Good{ + ID: 3, + Comment: "blub", + } + + database.Write.Create(&good) + + _, w := session.JSONRequest("DELETE", "/api/good/1", nil) + assertion.Equal(http.StatusNonAuthoritativeInfo, w.StatusCode) + + session.Login() + + _, w = session.JSONRequest("DELETE", "/api/good/a", nil) + assertion.Equal(http.StatusNotAcceptable, w.StatusCode) + + _, w = session.JSONRequest("DELETE", "/api/good/3", nil) + assertion.Equal(http.StatusOK, w.StatusCode) + + _, w = session.JSONRequest("DELETE", "/api/good/3", nil) + assertion.Equal(http.StatusNotFound, w.StatusCode) + + database.Close() + + _, w = session.JSONRequest("DELETE", "/api/good/1", nil) + assertion.Equal(http.StatusInternalServerError, w.StatusCode) + + session.Logout() + + _, w = session.JSONRequest("DELETE", "/api/good/1", nil) + assertion.Equal(http.StatusForbidden, w.StatusCode) + + test.Close() +} diff --git a/models/config.go b/models/config.go index c7689d6..7f3d926 100644 --- a/models/config.go +++ b/models/config.go @@ -13,20 +13,22 @@ import ( // Config file for this daemon (more information at the config_example.conf in this git repository) type Config struct { // address under which the api and static content of the webserver runs - WebserverBind string `toml:"webserver_bind"` + WebserverBind string `toml:"webserver_bind"` // path to deliver static content - Webroot string `toml:"webroot"` + Webroot string `toml:"webroot"` - Database database.Config `toml:"database"` - GoodRelease GoodReleaseConfig `toml:"good_release"` - CacheClean CacheWorkerConfig `toml:"cache_clean"` + Database database.Config `toml:"database"` + GoodRelease GoodReleaseConfig `toml:"good_release"` + CacheClean CacheWorkerConfig `toml:"cache_clean"` // path to the svg image templates to show the availablity and freshness // of a given good with a traffic light food labeling system GoodAvailabilityTemplate string `toml:"good_availablity_template"` GoodFreshnessTemplate string `toml:"good_freshness_template"` + FouledDeleter Duration `toml:"fouled_deleted"` + // URLs to other microservices that this services uses MicroserviceDependencies struct { Product string `toml:"product"` diff --git a/models/good.go b/models/good.go index 3192c01..866d2c5 100644 --- a/models/good.go +++ b/models/good.go @@ -23,8 +23,9 @@ type Good struct { LockedAt *time.Time `json:"-"` LockedSecret string `json:"-"` // Make it unusable - DeletedAt *time.Time `json:"-"` - Sended bool `json:"-"` + DeletedAt *time.Time `json:"-"` + ManuelleDelete bool `json:"-"` + FouledDelete bool `json:"-"` } // Function to generate a database and select locked goods with a filter diff --git a/runtime/good_fouled.go b/runtime/good_fouled.go new file mode 100644 index 0000000..a62d43c --- /dev/null +++ b/runtime/good_fouled.go @@ -0,0 +1,24 @@ +// Package with supporting functionality to run the microservice +package runtime + +import ( + "time" + + "github.com/genofire/hs_master-kss-monolith/lib/database" + "github.com/genofire/hs_master-kss-monolith/models" +) + +// Function to remove automaticle goods after the are fouled +func GoodFouled() int { + var goods []*models.Good + var g models.Good + g.FilterAvailable(database.Read).Where("fouled_at <= ?", time.Now()).Find(&goods) + now := time.Now() + + for _, good := range goods { + good.FouledDelete = true + good.DeletedAt = &now + database.Write.Save(&good) + } + return len(goods) +} diff --git a/runtime/good_fouled_test.go b/runtime/good_fouled_test.go new file mode 100644 index 0000000..f8f67c4 --- /dev/null +++ b/runtime/good_fouled_test.go @@ -0,0 +1,52 @@ +// Package with supporting functionality to run the microservice +package runtime + +import ( + "testing" + "time" + + "github.com/stretchr/testify/assert" + + "github.com/genofire/hs_master-kss-monolith/lib/database" + "github.com/genofire/hs_master-kss-monolith/models" +) + +// Function to test the unlocking of goods +func TestFouledDelete(t *testing.T) { + assert := assert.New(t) + database.Open(database.Config{ + Type: "sqlite3", + Logging: true, + Connection: ":memory:", + }) + + now := time.Now().Add(-time.Hour * 48) + good := models.Good{ + FouledAt: &now, + } + database.Write.Create(&good) + + good = models.Good{ + FouledAt: &now, + } + database.Write.Create(&good) + + count := GoodFouled() + assert.Equal(2, count, "not fouled") + + good = models.Good{ + FouledAt: &now, + } + database.Write.Create(&good) + + now = time.Now().Add(time.Hour * 48) + good = models.Good{ + FouledAt: &now, + } + database.Write.Create(&good) + + count = GoodFouled() + assert.Equal(1, count, "fouled") + + database.Close() +} diff --git a/runtime/good_release.go b/runtime/good_release.go index 5a6a5ce..be62f31 100644 --- a/runtime/good_release.go +++ b/runtime/good_release.go @@ -12,12 +12,12 @@ import ( // Function to create a Worker and to unlock goods func NewGoodReleaseWorker(grc models.GoodReleaseConfig) *worker.Worker { return worker.NewWorker(grc.Every.Duration, func() { - goodRelease(grc.After.Duration) + GoodRelease(grc.After.Duration) }) } // Function to unlock goods after a specified time -func goodRelease(unlockAfter time.Duration) int64 { +func GoodRelease(unlockAfter time.Duration) int64 { res := database.Write.Model(&models.Good{}).Where("locked_secret is not NULL and locked_at < ?", time.Now().Add(-unlockAfter)).Updates(map[string]interface{}{"locked_secret": "", "locked_at": nil}) return res.RowsAffected } diff --git a/runtime/good_release_test.go b/runtime/good_release_test.go index 048cfb3..1cbf305 100644 --- a/runtime/good_release_test.go +++ b/runtime/good_release_test.go @@ -25,13 +25,13 @@ func TestGoodRelease(t *testing.T) { LockedSecret: "never used", } database.Write.Create(&good) - count := goodRelease(time.Duration(3) * time.Second) + count := GoodRelease(time.Duration(3) * time.Second) assert.Equal(int64(0), count, "no locked in timeout") older := now.Add(-time.Duration(10) * time.Minute) good.LockedAt = &older database.Write.Save(&good) - count = goodRelease(time.Duration(3) * time.Second) + count = GoodRelease(time.Duration(3) * time.Second) assert.Equal(int64(1), count, "unlock after timeout") grw := NewGoodReleaseWorker(models.GoodReleaseConfig{ @@ -41,4 +41,6 @@ func TestGoodRelease(t *testing.T) { go grw.Start() time.Sleep(time.Duration(15) * time.Millisecond) grw.Close() + + database.Close() }