genofire/hs_monolith
genofire
/
hs_monolith
Archived
1
0
Fork 0

[Task]: add comments + comment quality saving

This commit is contained in:
mlabusch 2017-06-21 15:25:18 +02:00
parent d38839c4f9
commit 99d62fcb47
47 changed files with 2435 additions and 2476 deletions

View File

@ -1,5 +1,5 @@
# Stock-Microservice # Stock-Microservice
This is a microservice cutted out of a [Monolith](https://gitlab.com/matthiasstock/monolith). This microservice is cut out of a [Monolith](https://gitlab.com/matthiasstock/monolith).
[![Build Status](https://travis-ci.org/genofire/hs_master-kss-monolith.svg?branch=master)](https://travis-ci.org/genofire/hs_master-kss-monolith) [![CircleCI](https://circleci.com/gh/genofire/hs_master-kss-monolith/tree/master.svg?style=svg)](https://circleci.com/gh/genofire/hs_master-kss-monolith/tree/master) [![Coverage Status](https://coveralls.io/repos/github/genofire/hs_master-kss-monolith/badge.svg?branch=master)](https://coveralls.io/github/genofire/hs_master-kss-monolith?branch=master) [![GoDoc](https://godoc.org/github.com/genofire/hs_master-kss-monolith?status.svg)](https://godoc.org/github.com/genofire/hs_master-kss-monolith) [![Build Status](https://travis-ci.org/genofire/hs_master-kss-monolith.svg?branch=master)](https://travis-ci.org/genofire/hs_master-kss-monolith) [![CircleCI](https://circleci.com/gh/genofire/hs_master-kss-monolith/tree/master.svg?style=svg)](https://circleci.com/gh/genofire/hs_master-kss-monolith/tree/master) [![Coverage Status](https://coveralls.io/repos/github/genofire/hs_master-kss-monolith/badge.svg?branch=master)](https://coveralls.io/github/genofire/hs_master-kss-monolith?branch=master) [![GoDoc](https://godoc.org/github.com/genofire/hs_master-kss-monolith?status.svg)](https://godoc.org/github.com/genofire/hs_master-kss-monolith)
@ -9,14 +9,14 @@ This is a microservice cutted out of a [Monolith](https://gitlab.com/matthiassto
* [Easy dummy Shop-Cart in browser-cache](https://stock.pub.warehost.de/dummy_cart/) * [Easy dummy Shop-Cart in browser-cache](https://stock.pub.warehost.de/dummy_cart/)
## Features of this stock mircoservice ## Features of this stock mircoservice
* The main functionality of the microservice is to store the goods with their storage location and the date, when they are too old to be sell. * The main functionality of the microservice is to store goods with their storage location and the date, when they are too old to sell.
* Functionality of the admin frontend * Functionality of the admin frontend
* Add new goods to the stock * Add new goods to the stock
* Manually remove a single goods from the stock, for example when they are rancid * Manually remove a single good from the stock, for example when it is fouled
* Remove single goods from the stock, when they are send to a costumer * Remove a single good from the stock, when it is send to a costumer
* Block goods from the stock, when a costumer adds them to his cart * Block goods from the stock, when a costumer adds them to his shop-cart
* Functionality of the costumer frontend * Functionality of the costumer frontend
* Show the store with a traffic light food labelling * Show the stock of a product with a traffic light food labelling system
* Optional Features * Optional Features
* Admin frontend: display of a statistic on the amount and average of goods in the stock * Admin frontend: display of a statistic of the current and average amount of goods in the stock
* Admin frontend: display a traffic light food labelling for each good, which indicates whether the good is too old * Admin frontend: display a traffic light food labelling system for each good, which indicates whether the good is too old

View File

@ -1,4 +1,4 @@
// Package that containts the cmd binary of the microservice to run it // Package that contains the cmd binary of the microservice to run it
package main package main
import ( import (
@ -50,13 +50,15 @@ func main() {
} }
grw := runtime.NewGoodReleaseWorker(config.GoodRelease) grw := runtime.NewGoodReleaseWorker(config.GoodRelease)
cw := runtime.NewCacheWorker() cw := runtime.NewCacheWorker()
fw := worker.NewWorker(config.FouledDeleter.Duration, func() { runtime.GoodFouled() }) fw := worker.NewWorker(config.FouledDeleter.Duration, func() {
runtime.GoodFouled()
})
go grw.Start() go grw.Start()
go cw.Start() go cw.Start()
if config.FouledDeleter.Duration != time.Duration(0) { if config.FouledDeleter.Duration != time.Duration(0) {
go fw.Start() go fw.Start()
} }
// Startwebsrver // Start webserver
router := goji.NewMux() router := goji.NewMux()
web.BindAPI(router) web.BindAPI(router)

View File

@ -1,24 +0,0 @@
# bind the webserver to a dynamic ports
webserver_bind = ":65000"
webroot = "webroot"
good_availablity_template = "contrib/good_availablity.svg"
[database]
type = "sqlite3"
# logging = true
connection = "file::memory:?mode=memory&cache=shared"
# For Master-Slave cluster
# read_connection = ""
[good_release]
every = "5m"
after = "30m"
[cache_clean]
every = "5m"
after = "30m"
[microservice_dependencies]
product = "http://localhost:65000/api-test/product/%d/"
permission = "http://localhost:65000/api-test/session/%s/%d/"

View File

@ -1,8 +1,7 @@
<!-- SVG to show the current stock with a traffic light food labeling system --> <!-- SVG to show the current stock with a traffic light food labeling system -->
<!-- Used functions --> <!-- Used functions -->
<!-- process_radius ANZAHL MAXANZAHL RADIUS --> <!-- process_radius ANZAHL MAXANZAHL RADIUS -->
<!-- procent ANZAHL MAXANZAHL --> <!-- procent ANZAHL MAXANZAHL ex. {procent .Count 10}%-->
<!-- ex. {procent .Count 10}%-->
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100"> <svg version="1.1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100">
<g class="arcs"> <g class="arcs">
{{if eq .Count 0}} {{if eq .Count 0}}

Before

Width:  |  Height:  |  Size: 854 B

After

Width:  |  Height:  |  Size: 840 B

View File

@ -58,7 +58,8 @@ Die Packages und Go-Files des Application Layers umfassen die Logik des Microser
\paragraph{http:} Go-Files, die die Anwendungslogik (Funktionen) und die API-Routen beinhalten \paragraph{http:} Go-Files, die die Anwendungslogik (Funktionen) und die API-Routen beinhalten
\begin{itemize} \begin{itemize}
\item \texttt{bindapi.go}: Funktionen, die für das Binden der URL-Pfade notwendig sind \item \texttt{bindapi.go}: Funktionen, die für das Binden der URL-Pfade notwendig sind
\item \texttt{good.go}: Funktionen für die Verwaltung der Waren im Warenbestand \item \texttt{good.go}: Funktionen für das Hinzufügen von Waren zum Warenbestand und die Anzeige, ob eine Ware abgelaufen ist
\item \texttt{good\_lock.go}: Funktionen für das Blockieren von Waren, die sich im Warenkorb befinden
\item \texttt{good\_show.go}: Funktionen für die Auflistung und Zählung der vorhandenen Waren sowie die Feststellung ihrer Verfügbarkeit \item \texttt{good\_show.go}: Funktionen für die Auflistung und Zählung der vorhandenen Waren sowie die Feststellung ihrer Verfügbarkeit
\item \texttt{good\_temp.go}: Hilfsfunktionen, die für die Darstellung des Warenbestandes als Ampel im Kunden-Frontend benötigt werden \item \texttt{good\_temp.go}: Hilfsfunktionen, die für die Darstellung des Warenbestandes als Ampel im Kunden-Frontend benötigt werden
\item \texttt{status.go}: Funktion, die den Status des Microservice abfragt \item \texttt{status.go}: Funktion, die den Status des Microservice abfragt
@ -77,9 +78,9 @@ Die Packages und Go-Files des Application Layers umfassen die Logik des Microser
\begin{itemize} \begin{itemize}
\item \texttt{auth.go}: Hilfsfunktionen zur Prüfung, ob eine Berechtigung für den Zugriff vorliegt \item \texttt{auth.go}: Hilfsfunktionen zur Prüfung, ob eine Berechtigung für den Zugriff vorliegt
\item \texttt{cache\_worker.go}: Hilfsfunktionen für das Löschen und Anlegen von Cache-Workers \item \texttt{cache\_worker.go}: Hilfsfunktionen für das Löschen und Anlegen von Cache-Workers
\item \texttt{good\_fouled.go}: Hilfsfunktion, um abgelaufene Waren automatisch aus dem Warenbestand zu entfernen
\item \texttt{good\_release.go}: Hilfsfunktionen zum Blockieren und Entsperren von Waren \item \texttt{good\_release.go}: Hilfsfunktionen zum Blockieren und Entsperren von Waren
\item \texttt{productcache.go}: Hilfsfunktionen zum Anlegen eines Caches für Produkte \item \texttt{productcache.go}: Hilfsfunktionen zum Anlegen eines Caches für Produkte
\item \texttt{runtime.go}: Übergreifende Hintergrundfunktionalitäten
\end{itemize} \end{itemize}
@ -122,7 +123,7 @@ connection = "file::memory:?mode=memory&cache=shared"
\newpage \newpage
\subsection{Integrierte Tests} \subsection{Integrierte Tests}
\label{subsec: Integrierte Test} \label{subsec: Integrierte Test}
Neben den bisherigen Packages, die bereits Whitebox-Tests umfassen, ist in dem Package \textbf{\texttt{test}} ein weiteres Go-File (\texttt{testRest.go}) enthalten. Dieses setzt einen Test des Webservers um, bei dem auf Testdaten eines Produktkataloges zurückgegriffen wird. Mit Hilfe der integrierten Tests wird in der hier beschriebenen Version eine Code-Coverage von 100\% erreicht, das heißt jedes Stück Code wird mindestens einmal zur Ausführung gebracht. Neben den bisherigen Packages, die bereits Whitebox-Tests umfassen, ist in dem Package \textbf{\texttt{test}} ein weiteres Go-File (\texttt{testrest.go}) enthalten. Dieses setzt einen Test des Webservers um, bei dem auf Testdaten eines Produktkataloges zurückgegriffen wird. Mit Hilfe der integrierten Tests wird in der hier beschriebenen Version eine Code-Coverage von 100\% erreicht, das heißt jedes Stück Code wird mindestens einmal zur Ausführung gebracht.
\subsection{Anpassung des Monolithen} \subsection{Anpassung des Monolithen}
\label{subsec: Anpassung des Monolithen} \label{subsec: Anpassung des Monolithen}

View File

@ -28,8 +28,8 @@ func addGood(w http.ResponseWriter, r *http.Request) {
id, err := strconv.ParseInt(pat.Param(r, "productid"), 10, 64) id, err := strconv.ParseInt(pat.Param(r, "productid"), 10, 64)
if err != nil { if err != nil {
log.Warn("false productid format") log.Warn("false product id format")
http.Error(w, "the product id is false", http.StatusNotAcceptable) http.Error(w, "the product id has a false format", http.StatusNotAcceptable)
return return
} }
log = log.WithField("productid", id) log = log.WithField("productid", id)
@ -40,7 +40,7 @@ func addGood(w http.ResponseWriter, r *http.Request) {
return return
} }
if !ok { if !ok {
log.Warn("false product, product not found") log.Warn("product not found")
http.Error(w, "the product was not found", http.StatusNotFound) http.Error(w, "the product was not found", http.StatusNotFound)
return return
} }
@ -66,7 +66,7 @@ func addGood(w http.ResponseWriter, r *http.Request) {
} }
if db.Error != nil { if db.Error != nil {
log.Error("database not able to write", db.Error) log.Error("database unable to write", db.Error)
http.Error(w, "the product could not be written into the database", http.StatusInternalServerError) http.Error(w, "the product could not be written into the database", http.StatusInternalServerError)
} }
lib.Write(w, &obj) lib.Write(w, &obj)
@ -79,7 +79,7 @@ func delGood(w http.ResponseWriter, r *http.Request) {
log := logger.HTTP(r) log := logger.HTTP(r)
id, err := strconv.ParseInt(pat.Param(r, "goodid"), 10, 64) id, err := strconv.ParseInt(pat.Param(r, "goodid"), 10, 64)
if err != nil { if err != nil {
log.Warn("wrong goodid format") log.Warn("wrong good id format")
http.Error(w, "the good id has a false format", http.StatusNotAcceptable) http.Error(w, "the good id has a false format", http.StatusNotAcceptable)
return return
} }
@ -90,8 +90,8 @@ func delGood(w http.ResponseWriter, r *http.Request) {
good.ID = id good.ID = id
db := good.FilterAvailable(database.Read).First(&good) db := good.FilterAvailable(database.Read).First(&good)
if db.RecordNotFound() { if db.RecordNotFound() {
log.Warnf("good could not found: %s", db.Error) log.Warnf("could not find good: %s", db.Error)
http.Error(w, "the good could not found", http.StatusNotFound) http.Error(w, "the good was not found", http.StatusNotFound)
return return
} }
good.ManuelleDelete = true good.ManuelleDelete = true
@ -99,8 +99,8 @@ func delGood(w http.ResponseWriter, r *http.Request) {
db = database.Write.Save(&good) db = database.Write.Save(&good)
if db.Error != nil { if db.Error != nil {
log.Warnf("good could not delete: %s", db.Error) log.Warnf("could not delete good: %s", db.Error)
http.Error(w, "the good could not delete", http.StatusInternalServerError) http.Error(w, "the good could not be deleted", http.StatusInternalServerError)
return return
} }
log.Info("done") log.Info("done")

View File

@ -1,3 +1,4 @@
// Package that contains all api routes of this microservice
package http package http
import ( import (
@ -14,6 +15,7 @@ type LockGood struct {
Count int `json:"count"` Count int `json:"count"`
} }
// Function to lock goods
func lockGoods(w http.ResponseWriter, r *http.Request) { func lockGoods(w http.ResponseWriter, r *http.Request) {
log := logger.HTTP(r) log := logger.HTTP(r)
secret := r.Header.Get("secret") secret := r.Header.Get("secret")
@ -37,8 +39,8 @@ func lockGoods(w http.ResponseWriter, r *http.Request) {
} }
if len(goods) <= 0 { if len(goods) <= 0 {
log.Warn("try to log nothing") log.Warn("tried to log nothing")
http.Error(w, "try to log nothing", http.StatusBadRequest) http.Error(w, "tried to log nothing", http.StatusBadRequest)
return return
} }
@ -47,9 +49,9 @@ func lockGoods(w http.ResponseWriter, r *http.Request) {
for _, good := range goods { for _, good := range goods {
if good.ProductID <= 0 { if good.ProductID <= 0 {
log.Warn("try to log nothing") log.Warn("tried to log nothing")
tx.Rollback() tx.Rollback()
http.Error(w, "try to log nothing", http.StatusBadRequest) http.Error(w, "tried to log nothing", http.StatusBadRequest)
return return
} }
for i := 0; i < good.Count; i++ { for i := 0; i < good.Count; i++ {
@ -68,7 +70,7 @@ func lockGoods(w http.ResponseWriter, r *http.Request) {
if db.Error != nil || db.RowsAffected != 1 { if db.Error != nil || db.RowsAffected != 1 {
http.Error(w, "the good was not found in database", http.StatusInternalServerError) http.Error(w, "the good was not found in database", http.StatusInternalServerError)
tx.Rollback() tx.Rollback()
log.Panic("more then one good locked: ", db.Error) log.Panic("there is more than one good locked: ", db.Error)
return return
} }
count += 1 count += 1
@ -81,6 +83,7 @@ func lockGoods(w http.ResponseWriter, r *http.Request) {
} }
// Function to release locked goods
func releaseGoods(w http.ResponseWriter, r *http.Request) { func releaseGoods(w http.ResponseWriter, r *http.Request) {
log := logger.HTTP(r) log := logger.HTTP(r)
secret := r.Header.Get("secret") secret := r.Header.Get("secret")
@ -91,14 +94,14 @@ func releaseGoods(w http.ResponseWriter, r *http.Request) {
result := db.RowsAffected result := db.RowsAffected
if err != nil { if err != nil {
log.Warn("database error during release goods: ", err) log.Warn("database error during the release of goods: ", err)
http.Error(w, "secret could not validate", http.StatusInternalServerError) http.Error(w, "the secret could not be validated", http.StatusInternalServerError)
return return
} }
if result <= 0 { if result <= 0 {
log.Warn("no goods found") log.Warn("no goods found")
http.Error(w, "no goods found to release", http.StatusNotFound) http.Error(w, "there are no goods to release", http.StatusNotFound)
return return
} }

View File

@ -11,6 +11,7 @@ import (
"github.com/genofire/hs_master-kss-monolith/test" "github.com/genofire/hs_master-kss-monolith/test"
) )
// Function to test lockGoods()
func TestLockGoods(t *testing.T) { func TestLockGoods(t *testing.T) {
assertion, router := test.Init(t) assertion, router := test.Init(t)
good := &models.Good{ good := &models.Good{
@ -58,6 +59,7 @@ func TestLockGoods(t *testing.T) {
test.Close() test.Close()
} }
// Function to test releaseGoods()
func TestReleaseGoods(t *testing.T) { func TestReleaseGoods(t *testing.T) {
now := time.Now() now := time.Now()
assertion, router := test.Init(t) assertion, router := test.Init(t)

View File

@ -38,7 +38,7 @@ func TestListGood(t *testing.T) {
test.Close() test.Close()
} }
// Function to getGoodAvailability() and getGoodAvailabilityCount() // Function to test getGoodAvailability() and getGoodAvailabilityCount()
func TestGetGoodAvailable(t *testing.T) { func TestGetGoodAvailable(t *testing.T) {
now := time.Now() now := time.Now()
assertion, router := test.Init(t) assertion, router := test.Init(t)
@ -99,7 +99,7 @@ func TestGetGoodAvailable(t *testing.T) {
} }
// Function to getGoodFreshness() // Function to test getGoodFreshness()
func TestGetGoodFreshness(t *testing.T) { func TestGetGoodFreshness(t *testing.T) {
now := time.Now().Add(36 * time.Hour) now := time.Now().Add(36 * time.Hour)
assertion, router := test.Init(t) assertion, router := test.Init(t)

View File

@ -9,19 +9,19 @@ import (
"text/template" "text/template"
) )
// Path to the svg image template, that shows the availablity or freshness of a given good // Path to the svg image template, that shows the availability or freshness of a given good
// with a traffic light food labeling system // with a traffic light food labeling system
var GoodAvailabilityTemplate string var GoodAvailabilityTemplate string
var GoodFreshnessTemplate string var GoodFreshnessTemplate string
// Function to calculate a percent value from a given value and an maximum value // Function to calculate a percent value from a given value and a maximum value
func tempPercent(value, max int) int { func tempPercent(value, max int) int {
return value * 100 / max return value * 100 / max
} }
// Function to calculate a partial radius, depending on a percentage value // Function to calculate a partial radius, depending on a percentage value
func tempProcessRadius(value, max, radius int) float64 { func tempProcessRadius(value, max, radius int) float64 {
return (1 - float64(value)/float64(max)) * float64(radius) * 2 * 3.14 return (1 - float64(value) / float64(max)) * float64(radius) * 2 * 3.14
} }
// Function to get the SVG, that shows the availability with a traffic light food labeling system for a given good // Function to get the SVG, that shows the availability with a traffic light food labeling system for a given good

View File

@ -73,14 +73,14 @@ func TestAddGood(t *testing.T) {
runtime.HasPermission("testsessionkey", runtime.PermissionCreateGood) runtime.HasPermission("testsessionkey", runtime.PermissionCreateGood)
runtime.CleanCache() runtime.CleanCache()
// Test gatewaytimeout on product exists // Test the gatewaytimeout on product exists
_, w = session.JSONRequest("POST", "/api/good/1", good) _, w = session.JSONRequest("POST", "/api/good/1", good)
assertion.Equal(http.StatusGatewayTimeout, w.StatusCode) assertion.Equal(http.StatusGatewayTimeout, w.StatusCode)
time.Sleep(time.Duration(10) * time.Millisecond) time.Sleep(time.Duration(10) * time.Millisecond)
runtime.CleanCache() runtime.CleanCache()
// Test gatewaytimeout on permission exists // Test the gatewaytimeout on permission exists
_, w = session.JSONRequest("POST", "/api/good/1", good) _, w = session.JSONRequest("POST", "/api/good/1", good)
assertion.Equal(http.StatusGatewayTimeout, w.StatusCode) assertion.Equal(http.StatusGatewayTimeout, w.StatusCode)

View File

@ -23,13 +23,13 @@ var (
// Configuration of the database connection // Configuration of the database connection
type Config struct { type Config struct {
// type of the database, currently supports sqlite and postgres // Type of the database (currently supports sqlite and postgres)
Type string Type string
// connection configuration // Connection configuration
Connection string Connection string
// create another connection for reading only // Create another connection for reading only
ReadConnection string ReadConnection string
// enable logging of the generated sql string // Enable logging of the generated sql string
Logging bool Logging bool
} }

View File

@ -22,7 +22,7 @@ func Read(r *http.Request, to interface{}) (err error) {
func Write(w http.ResponseWriter, data interface{}) { func Write(w http.ResponseWriter, data interface{}) {
js, err := json.Marshal(data) js, err := json.Marshal(data)
if err != nil { if err != nil {
http.Error(w, "failed to encode response: "+err.Error(), http.StatusInternalServerError) http.Error(w, "failed to encode response: " + err.Error(), http.StatusInternalServerError)
return return
} }
w.Header().Set("Content-Type", "application/json") w.Header().Set("Content-Type", "application/json")

View File

@ -1,4 +1,4 @@
// Package with a lib for cronjobs to run in background // Package with a lib for cronjobs to run in the background
package worker package worker
import "time" import "time"

View File

@ -1,4 +1,4 @@
// Package with a lib for cronjobs to run in background // Package with a lib for cronjobs to run in the background
package worker package worker
import ( import (
@ -14,7 +14,7 @@ func TestWorker(t *testing.T) {
runtime := 0 runtime := 0
w := NewWorker(time.Duration(5)*time.Millisecond, func() { w := NewWorker(time.Duration(5) * time.Millisecond, func() {
runtime = runtime + 1 runtime = runtime + 1
}) })
go w.Start() go w.Start()

View File

@ -22,14 +22,14 @@ type Config struct {
GoodRelease GoodReleaseConfig `toml:"good_release"` GoodRelease GoodReleaseConfig `toml:"good_release"`
CacheClean CacheWorkerConfig `toml:"cache_clean"` CacheClean CacheWorkerConfig `toml:"cache_clean"`
// path to the svg image templates to show the availablity and freshness // path to the SVG image templates to show the availability and freshness
// of a given good with a traffic light food labeling system // of a given good with a traffic light food labeling system
GoodAvailabilityTemplate string `toml:"good_availablity_template"` GoodAvailabilityTemplate string `toml:"good_availablity_template"`
GoodFreshnessTemplate string `toml:"good_freshness_template"` GoodFreshnessTemplate string `toml:"good_freshness_template"`
FouledDeleter Duration `toml:"fouled_deleted"` FouledDeleter Duration `toml:"fouled_deleted"`
// URLs to other microservices that this services uses // URLs to other microservices, which this service uses
MicroserviceDependencies struct { MicroserviceDependencies struct {
Product string `toml:"product"` Product string `toml:"product"`
Permission string `toml:"permission"` Permission string `toml:"permission"`
@ -48,7 +48,7 @@ type CacheWorkerConfig struct {
type GoodReleaseConfig struct { type GoodReleaseConfig struct {
// Run worker every Duration // Run worker every Duration
Every Duration `toml:"every"` Every Duration `toml:"every"`
// Unlock those which are not used since Duration // Unlock those, which are not used since Duration
After Duration `toml:"after"` After Duration `toml:"after"`
} }

View File

@ -28,8 +28,8 @@ func (d *Duration) UnmarshalTOML(dataInterface interface{}) error {
return fmt.Errorf("invalid duration: \"%s\"", data) return fmt.Errorf("invalid duration: \"%s\"", data)
} }
unit := data[len(data)-1] unit := data[len(data) - 1]
value, err := strconv.Atoi(string(data[:len(data)-1])) value, err := strconv.Atoi(string(data[:len(data) - 1]))
if err != nil { if err != nil {
return fmt.Errorf("unable to parse duration %s: %s", data, err) return fmt.Errorf("unable to parse duration %s: %s", data, err)
} }

View File

@ -10,7 +10,7 @@ import (
"github.com/genofire/hs_master-kss-monolith/lib/database" "github.com/genofire/hs_master-kss-monolith/lib/database"
) )
// Goods managed in this stock microservice // Type of goods managed in this stock microservice
type Good struct { type Good struct {
ID int64 `json:"id"` ID int64 `json:"id"`
ProductID int64 `json:"product_id"` ProductID int64 `json:"product_id"`

View File

@ -1,4 +0,0 @@
// Package with the mostly static content (models) of this microservice
package models
// Store all the structs

View File

@ -86,6 +86,7 @@ public class DataTransferObjectFactory {
ProductI18n i18n = product.getI18n().get(locale.getLanguage()); ProductI18n i18n = product.getI18n().get(locale.getLanguage());
String price = numberFormat.format(i18n.getPrice()); String price = numberFormat.format(i18n.getPrice());
ProductDTO productDTO = new ProductDTO(); ProductDTO productDTO = new ProductDTO();
// Addition: productDTO.setID()
productDTO.setId(product.getId()); productDTO.setId(product.getId());
productDTO.setItemNumber(product.getItemNumber()); productDTO.setItemNumber(product.getItemNumber());
productDTO.setUnit(product.getUnit()); productDTO.setUnit(product.getUnit());

View File

@ -17,16 +17,18 @@ public class HomepageController {
@Autowired @Autowired
private ShopService shopService; private ShopService shopService;
// Addition: contant with the address of the stock microservice adminfrontend
private final String STOCKADMINFRONTENDTEMPLATE = "https://stock.pub.warehost.de/index.html"; private final String STOCKADMINFRONTENDTEMPLATE = "https://stock.pub.warehost.de/index.html";
/** /**
* Redirect * Redirect to stock admin frontend
* *
* @param model Template model * @param model Template model
* @return The constant template name for the stock admin frontend. * @return The constant template name for the stock admin frontend.
*/ */
@RequestMapping(value = "/stockadmin", method = RequestMethod.GET) @RequestMapping(value = "/stockadmin", method = RequestMethod.GET)
public String redirect(Model model) {return "redirect:"+ this.STOCKADMINFRONTENDTEMPLATE; public String redirect(Model model) {
return "redirect:" + this.STOCKADMINFRONTENDTEMPLATE;
} }
/** /**

View File

@ -15,6 +15,7 @@ public class ProductDTO {
private String description; private String description;
private List<ReviewDTO> reviews; private List<ReviewDTO> reviews;
// Addition: int id, getId() and setID()
public int getId() { public int getId() {
return id; return id;
} }

View File

@ -31,7 +31,10 @@
</div> </div>
<div class="col-md-8"> <div class="col-md-8">
<h2 th:text="${product.name}">Product Name</h2> <h2 th:text="${product.name}">Product Name</h2>
<!-- Addition: traffic light food labeling system of the stock microservice -->
<img width="10%" class="icon" th:src="${'https://stock.pub.warehost.de/api/good/availablity/'+product.id}"/> <img width="10%" class="icon" th:src="${'https://stock.pub.warehost.de/api/good/availablity/'+product.id}"/>
<p class="text-info text-uppercase" th:text="${product.price}">0,00 Euro</p> <p class="text-info text-uppercase" th:text="${product.price}">0,00 Euro</p>
<p class="lead" th:text="${product.description}">Description.</p> <p class="lead" th:text="${product.description}">Description.</p>
<div th:replace="fragments/reviews :: reviews"></div> <div th:replace="fragments/reviews :: reviews"></div>

View File

@ -1,32 +0,0 @@
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head th:replace="fragments/skeleton :: head">
<meta charset="utf-8" />
<meta http-equiv="refresh" content="5; URL=http://localhost:65000/"/>
<meta name="viewport" content="width=device-width, initial-scale=1" />
<link href="../static/css/bootstrap.min.css" rel="stylesheet" />
<link href="../static/css/mosh.css" rel="stylesheet" />
</head>
<body>
<div th:replace="fragments/skeleton :: navigation">
<div class="container">
<nav>Navigation</nav>
</div>
</div>
<div class="container product">
<div class="row info">
<p align="center"><a href="http://localhost:65000/">If the automatic redirection to the stock management admin
front end does not work, click here.</a></p>
</div>
<footer th:replace="fragments/skeleton :: footer">
<p>&copy; 2017</p>
</footer>
</div>
<script src="../static/js/jquery-3.1.1.min.js"
th:src="@{/js/jquery-3.1.1.min.js}"></script>
<!-- Include all compiled plugins (below), or include individual files as needed -->
<script src="../static/js/bootstrap.min.js"
th:src="@{/js/bootstrap.min.js}"></script>
</body>
</html>

View File

@ -10,20 +10,20 @@ import (
"sync" "sync"
) )
// URL to the microservice which manages permissions // URL to the microservice, which manages permissions
var PermissionURL string var PermissionURL string
// Type of permission // Type of permission
type Permission int type Permission int
// Some permissions (the real permissions need to come from the permission microservice) // Some permissions (the real permissions need to come from a permission microservice)
const ( const (
// permission to add goods to the stock // permission to add goods to the stock
// e.g. if a good is received and now available to sell // e.g. if a good is received and now available for selling
PermissionCreateGood = 1 PermissionCreateGood = 1
// permission to delete goods from the stock // permission to delete goods from the stock
// e.g. if a good becomes fouled and has to be removed // e.g. if a good becomes fouled and has to be removed manually
PermissionDeleteGood = 2 PermissionDeleteGood = 2
) )

View File

@ -8,7 +8,7 @@ import (
"github.com/genofire/hs_master-kss-monolith/models" "github.com/genofire/hs_master-kss-monolith/models"
) )
// Function to test the cache Worker // Function to test the cacheWorker
func TestCacheWorker(t *testing.T) { func TestCacheWorker(t *testing.T) {
productExistCache[2] = boolMicroServiceCache{LastCheck: time.Now(), Value: true} productExistCache[2] = boolMicroServiceCache{LastCheck: time.Now(), Value: true}

View File

@ -8,7 +8,7 @@ import (
"github.com/genofire/hs_master-kss-monolith/models" "github.com/genofire/hs_master-kss-monolith/models"
) )
// Function to remove automaticle goods after the are fouled // Function to automatically remove goods, if they are fouled
func GoodFouled() int { func GoodFouled() int {
var goods []*models.Good var goods []*models.Good
var g models.Good var g models.Good

View File

@ -11,7 +11,7 @@ import (
"github.com/genofire/hs_master-kss-monolith/models" "github.com/genofire/hs_master-kss-monolith/models"
) )
// Function to test the unlocking of goods // Function to test fouledDelete()
func TestFouledDelete(t *testing.T) { func TestFouledDelete(t *testing.T) {
assert := assert.New(t) assert := assert.New(t)
database.Open(database.Config{ database.Open(database.Config{

View File

@ -11,7 +11,7 @@ import (
"github.com/genofire/hs_master-kss-monolith/models" "github.com/genofire/hs_master-kss-monolith/models"
) )
// Function to test the unlocking of goods // Function to test goodRelease()
func TestGoodRelease(t *testing.T) { func TestGoodRelease(t *testing.T) {
assert := assert.New(t) assert := assert.New(t)
database.Open(database.Config{ database.Open(database.Config{

View File

@ -9,7 +9,7 @@ import (
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
) )
// Function to test, if and which products exist (get information from the products catalogue) // Function to test, if and which products exist (get information from the product catalogue)
func TestProductExists(t *testing.T) { func TestProductExists(t *testing.T) {
assert := assert.New(t) assert := assert.New(t)

View File

@ -1,5 +0,0 @@
// Package with supporting functionality to run the microservice
package runtime
// some mingled functionality to handle in background

View File

@ -25,6 +25,7 @@ type MockTransport struct {
running bool running bool
} }
// Function to use the http handler
func (t *MockTransport) RoundTrip(req *http.Request) (*http.Response, error) { func (t *MockTransport) RoundTrip(req *http.Request) (*http.Response, error) {
if !t.running { if !t.running {
return nil, errors.New("mock a error") return nil, errors.New("mock a error")
@ -33,9 +34,13 @@ func (t *MockTransport) RoundTrip(req *http.Request) (*http.Response, error) {
t.Handler.ServeHTTP(w, req) t.Handler.ServeHTTP(w, req)
return w.Result(), nil return w.Result(), nil
} }
// Function to start the http handler
func (t *MockTransport) Start() { func (t *MockTransport) Start() {
t.running = true t.running = true
} }
// Function to close/stop the http handler
func (t *MockTransport) Close() { func (t *MockTransport) Close() {
t.running = false t.running = false
} }
@ -73,7 +78,7 @@ func Close() {
mock.Close() mock.Close()
} }
// Handle a test session with cookies // Struct to dandle a test session with cookies
type Request struct { type Request struct {
req *http.Request req *http.Request
cookies []*http.Cookie cookies []*http.Cookie

View File

@ -1,4 +1,4 @@
{ {
"id": 1, "id": 1,
"title": "Kiwi" "title": "Kiwi"
} }

View File

@ -1,4 +1,4 @@
{ {
"id": 2, "id": 2,
"title": "Blueberries" "title": "Blueberries"
} }

View File

@ -1,4 +1,4 @@
{ {
"id": 3, "id": 3,
"title": "Cherries" "title": "Cherries"
} }

View File

@ -1,4 +1,4 @@
{ {
"id": 4, "id": 4,
"title": "Potatoes" "title": "Potatoes"
} }

View File

@ -1,4 +1,4 @@
{ {
"id": 5, "id": 5,
"title": "Tomatoes" "title": "Tomatoes"
} }

View File

@ -1,4 +1,4 @@
{ {
"id": 6, "id": 6,
"title": "Rhubarb" "title": "Rhubarb"
} }

View File

@ -1,8 +1,8 @@
[ [
{"id":1, "title": "Kiwi"}, {"id":1, "title": "Kiwi"},
{"id":2, "title": "Blueberries"}, {"id":2, "title": "Blueberries"},
{"id":3, "title": "Cherries"}, {"id":3, "title": "Cherries"},
{"id":4, "title": "Potatoes"}, {"id":4, "title": "Potatoes"},
{"id":5, "title": "Tomatoes"}, {"id":5, "title": "Tomatoes"},
{"id":6, "title": "Rhubarb"} {"id":6, "title": "Rhubarb"}
] ]

View File

@ -1,15 +1,15 @@
<!DOCTYPE html> <!DOCTYPE html>
<html lang="en"> <html lang="en">
<head> <head>
<meta charset="utf-8" /> <meta charset="utf-8"/>
<meta http-equiv="X-UA-Compatible" content="IE=edge" /> <meta http-equiv="X-UA-Compatible" content="IE=edge"/>
<meta name="viewport" content="width=device-width, initial-scale=1" /> <meta name="viewport" content="width=device-width, initial-scale=1"/>
<link href="/node_modules/semantic-ui-css/semantic.min.css" rel="stylesheet" /> <link href="/node_modules/semantic-ui-css/semantic.min.css" rel="stylesheet"/>
<link href="/static/css/main.css" rel="stylesheet" /> <link href="/static/css/main.css" rel="stylesheet"/>
<title>microStock Dummy Cart</title> <title>microStock Dummy Cart</title>
</head> </head>
<body ng-app="microStockDummieCare" ng-controller="MainCtrl"> <body ng-app="microStockDummieCare" ng-controller="MainCtrl">
<nav class="ui stackable inverted menu"> <nav class="ui stackable inverted menu">
<div class="ui container"> <div class="ui container">
<div class="header item">Dummy Cart</div> <div class="header item">Dummy Cart</div>
<div class="right menu"> <div class="right menu">
@ -19,9 +19,9 @@
</a> </a>
</div> </div>
</div> </div>
</nav> </nav>
<div class="ui container"> <div class="ui container">
<form class="ui form" ng-submit="add()"> <form class="ui form" ng-submit="add()">
<div class="three fields"> <div class="three fields">
<div class="field"> <div class="field">
@ -30,7 +30,9 @@
<i class="dropdown icon"></i> <i class="dropdown icon"></i>
<div class="default text">Select Product</div> <div class="default text">Select Product</div>
<div class="menu"> <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}}</div> <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}}
</div>
</div> </div>
</div> </div>
</div> </div>
@ -60,20 +62,20 @@
</tr> </tr>
</tbody> </tbody>
</table> </table>
</div> </div>
<footer class="ui vertical footer segment"> <footer class="ui vertical footer segment">
<div class="ui center aligned container"> <div class="ui center aligned container">
<p>&copy; 2017 MM / Go - Team</p> <p>&copy; 2017 MM / Go - Team</p>
</div> </div>
</footer> </footer>
<script src="/node_modules/jquery/dist/jquery.min.js"></script> <script src="/node_modules/jquery/dist/jquery.min.js"></script>
<script src="/node_modules/semantic-ui-css/semantic.min.js"></script> <script src="/node_modules/semantic-ui-css/semantic.min.js"></script>
<script src="/node_modules/angular/angular.min.js"></script> <script src="/node_modules/angular/angular.min.js"></script>
<script src="/node_modules/angular-animate/angular-animate.min.js"></script> <script src="/node_modules/angular-animate/angular-animate.min.js"></script>
<script src="/node_modules/angular-ui-router/release/angular-ui-router.min.js"></script> <script src="/node_modules/angular-ui-router/release/angular-ui-router.min.js"></script>
<script src="/node_modules/angular-loading-bar/build/loading-bar.min.js"></script> <script src="/node_modules/angular-loading-bar/build/loading-bar.min.js"></script>
<script> <script>
var config = { var config = {
'microservice_dependencies': { 'microservice_dependencies': {
'products': '/api-test/product/', 'products': '/api-test/product/',
@ -192,6 +194,7 @@
}; };
}]); }]);
</script>
</script>
</body> </body>
</html> </html>

View File

@ -1,15 +1,15 @@
<!DOCTYPE html> <!DOCTYPE html>
<html lang="en"> <html lang="en">
<head> <head>
<meta charset="utf-8" /> <meta charset="utf-8"/>
<meta http-equiv="X-UA-Compatible" content="IE=edge" /> <meta http-equiv="X-UA-Compatible" content="IE=edge"/>
<meta name="viewport" content="width=device-width, initial-scale=1" /> <meta name="viewport" content="width=device-width, initial-scale=1"/>
<link href="/node_modules/semantic-ui-css/semantic.min.css" rel="stylesheet" /> <link href="/node_modules/semantic-ui-css/semantic.min.css" rel="stylesheet"/>
<link href="/static/css/main.css" rel="stylesheet" /> <link href="/static/css/main.css" rel="stylesheet"/>
<title>Stock Admin</title> <title>Stock Admin</title>
</head> </head>
<body ng-app="microStock"> <body ng-app="microStock">
<nav class="ui stackable inverted menu" ng-controller="GlobalCtrl"> <nav class="ui stackable inverted menu" ng-controller="GlobalCtrl">
<div class="ui container"> <div class="ui container">
<div class="header item">Stock Admin</div> <div class="header item">Stock Admin</div>
<a class="item" ui-sref="list" ui-active="active">List</a> <a class="item" ui-sref="list" ui-active="active">List</a>
@ -20,29 +20,30 @@
</a> </a>
</div> </div>
</div> </div>
</nav> </nav>
<div class="ui container" ui-view=""></div> <div class="ui container" ui-view=""></div>
<footer class="ui vertical footer segment"> <footer class="ui vertical footer segment">
<div class="ui center aligned container"> <div class="ui center aligned container">
<p>&copy; 2017 MM / Go - Team</p> <p>&copy; 2017 MM / Go - Team</p>
</div> </div>
</footer> </footer>
<script src="//cdn.jsdelivr.net/webshim/1.14.5/polyfiller.js"></script> <script src="//cdn.jsdelivr.net/webshim/1.14.5/polyfiller.js"></script>
<script> <script>
webshims.setOptions('forms-ext', {types: 'date'}); webshims.setOptions('forms-ext', {types: 'date'});
webshims.polyfill('forms forms-ext'); webshims.polyfill('forms forms-ext');
</script>
<script src="/node_modules/angular/angular.min.js"></script> </script>
<script src="/node_modules/angular-animate/angular-animate.min.js"></script> <script src="/node_modules/angular/angular.min.js"></script>
<script src="/node_modules/angular-ui-router/release/angular-ui-router.min.js"></script> <script src="/node_modules/angular-animate/angular-animate.min.js"></script>
<script src="/node_modules/angular-loading-bar/build/loading-bar.min.js"></script> <script src="/node_modules/angular-ui-router/release/angular-ui-router.min.js"></script>
<script src="/static/js/main.js"></script> <script src="/node_modules/angular-loading-bar/build/loading-bar.min.js"></script>
<script src="/static/js/item.controller.js"></script> <script src="/static/js/main.js"></script>
<script src="/static/js/item-add.controller.js"></script> <script src="/static/js/item.controller.js"></script>
<script src="/static/js/global.js"></script> <script src="/static/js/item-add.controller.js"></script>
<script src="/static/js/list.controller.js"></script> <script src="/static/js/global.js"></script>
<script src="/static/js/statistics.controller.js"></script> <script src="/static/js/list.controller.js"></script>
<script src="/static/js/statistics.controller.js"></script>
</body> </body>
</html> </html>

File diff suppressed because it is too large Load Diff

Before

Width:  |  Height:  |  Size: 434 KiB

After

Width:  |  Height:  |  Size: 451 KiB

View File

@ -14,11 +14,11 @@
</div> </div>
<div class="field"> <div class="field">
<label>Position</label> <label>Position</label>
<input type="text" name="position" placeholder="Location in Store", ng-model="obj.position"> <input type="text" name="position" placeholder="Location in Store" , ng-model="obj.position">
</div> </div>
<div class="field"> <div class="field">
<label>Comment</label> <label>Comment</label>
<input type="text" name="comment" placeholder="Comment for this good", ng-model="obj.comment"> <input type="text" name="comment" placeholder="Comment for this good" , ng-model="obj.comment">
</div> </div>
<div class="field"> <div class="field">
<label>Count</label> <label>Count</label>