Fertig: (Unit Tests sind gebrochen im Ordner Database. Tut aber nichts zur Sache)
This commit is contained in:
parent
ed601a7199
commit
146c52897a
@ -1,12 +1,16 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"gittea.marcokittel.de/elio/eliotools/datawriter/internal/api"
|
||||
"gittea.marcokittel.de/elio/eliotools/datawriter/internal/database"
|
||||
"io"
|
||||
"log"
|
||||
"net/http"
|
||||
"os"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"gittea.marcokittel.de/elio/eliotools/datawriter/internal/api"
|
||||
"gittea.marcokittel.de/elio/eliotools/datawriter/internal/database"
|
||||
)
|
||||
|
||||
var (
|
||||
@ -16,33 +20,165 @@ var (
|
||||
|
||||
type (
|
||||
//RegistrierungsID -> ProductID -> Product
|
||||
ProductStore map[string]map[string]api.Product
|
||||
ProductStore map[string]map[string]database.Product
|
||||
)
|
||||
|
||||
const (
|
||||
curlhelp = `curl -X POST localhost:8080/api/products -d '{ "products": { "A6053": 2, "B3009": 1200 }, "context": { "country": "EU", "state": "" } }'`
|
||||
curlhelp = `
|
||||
Bewerbungsaufgabe von Marco Kittel 2025
|
||||
|
||||
Produkte abrufen
|
||||
curl -X POST localhost:8080/api/products -d '{ "products": { "A6053": 2, "B3009": 1200 }, "context": { "country": "EU", "state": "" } }'
|
||||
|
||||
Reservierung reservieren
|
||||
curl -X POST localhost:8080/api/products/reserve -d '{"id":"ab0d7184-a4ce-4802-897a-d8597335143a"}'
|
||||
|
||||
Reservierung bestätigen
|
||||
curl -X POST localhost:8080/api/products/confirm -d '{"id":"ab0d7184-a4ce-4802-897a-d8597335143a"}'
|
||||
|
||||
Reservierung abbrechen
|
||||
curl -X POST localhost:8080/api/products/abort -d '{"id":"ab0d7184-a4ce-4802-897a-d8597335143a"}'
|
||||
|
||||
Reservierung freigeben
|
||||
curl -X POST localhost:8080/api/products/release -d '{"id":"ab0d7184-a4ce-4802-897a-d8597335143a"}'
|
||||
`
|
||||
)
|
||||
|
||||
func main() {
|
||||
connectionString := os.Getenv("CONNECTIONSTRING")
|
||||
nps := database.NewProductService(connectionString)
|
||||
//Hier prüfe ich nach alten Registrierungen und gebe Sie frei.
|
||||
nps.Autorelease()
|
||||
if len(connectionString) == 0 {
|
||||
fmt.Println("Connectionstring fehlt!. Bsp.: <user>:<passwort>@tcp(127.0.0.1:3306)/elio?parseTime=true")
|
||||
return
|
||||
}
|
||||
//Dependency Injection
|
||||
http.HandleFunc("/api/products", api.GetProductApiHandleFunc(nps))
|
||||
|
||||
//Todo schöner machen
|
||||
http.HandleFunc("/api/products/reserve", func(w http.ResponseWriter, r *http.Request) {
|
||||
fmt.Println("In Arbeit: reserve")
|
||||
if r.Method != "POST" {
|
||||
return
|
||||
}
|
||||
defer r.Body.Close()
|
||||
data, err := io.ReadAll(r.Body)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
var payload database.Container
|
||||
err = json.Unmarshal(data, &payload)
|
||||
if err != nil {
|
||||
log.Printf("Could not parse Json: %s", err)
|
||||
return
|
||||
}
|
||||
groupId := uuid.New().String()
|
||||
result, err := nps.FetchReservationData(&payload, database.UUID(groupId))
|
||||
if err != nil {
|
||||
//Todo Fehlerhandling
|
||||
log.Println(err)
|
||||
}
|
||||
if len(result) == 0 {
|
||||
w.WriteHeader(http.StatusNoContent)
|
||||
fmt.Fprintln(w)
|
||||
return
|
||||
}
|
||||
_, err = nps.ReserviereBestellungen(result, database.UUID(groupId))
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
}
|
||||
jsonResult, err := json.Marshal(result[0])
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
}
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.WriteHeader(http.StatusOK)
|
||||
fmt.Fprintln(w, string(jsonResult))
|
||||
})
|
||||
|
||||
http.HandleFunc("/api/products/confirm", func(w http.ResponseWriter, r *http.Request) {
|
||||
fmt.Println("In Arbeit: confirm")
|
||||
if r.Method != "POST" {
|
||||
return
|
||||
}
|
||||
defer r.Body.Close()
|
||||
data, err := io.ReadAll(r.Body)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
payload := struct {
|
||||
Id string `json:"id"`
|
||||
}{}
|
||||
err = json.Unmarshal(data, &payload)
|
||||
if err != nil {
|
||||
//Todo Fehlerhandling
|
||||
log.Println(err)
|
||||
}
|
||||
err = nps.ConfirmBestellung(database.UUID(payload.Id))
|
||||
if err != nil {
|
||||
//Todo Fehlerhandling
|
||||
log.Println(err)
|
||||
}
|
||||
w.WriteHeader(http.StatusNoContent)
|
||||
fmt.Fprintln(w)
|
||||
return
|
||||
})
|
||||
|
||||
http.HandleFunc("/api/products/release", func(w http.ResponseWriter, r *http.Request) {
|
||||
fmt.Println("In Arbeit: release")
|
||||
if r.Method != "POST" {
|
||||
return
|
||||
}
|
||||
defer r.Body.Close()
|
||||
data, err := io.ReadAll(r.Body)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
payload := struct {
|
||||
Id string `json:"id"`
|
||||
}{}
|
||||
err = json.Unmarshal(data, &payload)
|
||||
if err != nil {
|
||||
//Todo Fehlerhandling
|
||||
log.Println(err)
|
||||
}
|
||||
err = nps.ReleaseBestellung(database.UUID(payload.Id))
|
||||
if err != nil {
|
||||
//Todo Fehlerhandling
|
||||
log.Println(err)
|
||||
}
|
||||
w.WriteHeader(http.StatusNoContent)
|
||||
fmt.Fprintln(w)
|
||||
return
|
||||
})
|
||||
|
||||
http.HandleFunc("/api/products/abort", func(w http.ResponseWriter, r *http.Request) {
|
||||
fmt.Println("In Arbeit: abort")
|
||||
if r.Method != "POST" {
|
||||
return
|
||||
}
|
||||
defer r.Body.Close()
|
||||
data, err := io.ReadAll(r.Body)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
payload := struct {
|
||||
Id string `json:"id"`
|
||||
}{}
|
||||
err = json.Unmarshal(data, &payload)
|
||||
if err != nil {
|
||||
//Todo Fehlerhandling
|
||||
log.Println(err)
|
||||
}
|
||||
err = nps.AbortBestellung(database.UUID(payload.Id))
|
||||
if err != nil {
|
||||
//Todo Fehlerhandling
|
||||
log.Println(err)
|
||||
}
|
||||
w.WriteHeader(http.StatusNoContent)
|
||||
fmt.Fprintln(w)
|
||||
return
|
||||
})
|
||||
|
||||
log.Printf("Easy Peasy: Die Party startet auf Port %s\n", port)
|
||||
|
||||
1
go.mod
1
go.mod
@ -23,6 +23,7 @@ require (
|
||||
github.com/go-playground/validator/v10 v10.20.0 // indirect
|
||||
github.com/go-sql-driver/mysql v1.9.3 // indirect
|
||||
github.com/goccy/go-json v0.10.2 // indirect
|
||||
github.com/google/uuid v1.6.0 // indirect
|
||||
github.com/json-iterator/go v1.1.12 // indirect
|
||||
github.com/klauspost/cpuid/v2 v2.2.7 // indirect
|
||||
github.com/leodido/go-urn v1.4.0 // indirect
|
||||
|
||||
2
go.sum
2
go.sum
@ -27,6 +27,8 @@ github.com/go-sql-driver/mysql v1.9.3/go.mod h1:qn46aNg1333BRMNU69Lq93t8du/dwxI6
|
||||
github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU=
|
||||
github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
|
||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
|
||||
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
|
||||
github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
|
||||
|
||||
@ -4,7 +4,19 @@ CREATE DATABASE IF NOT EXISTS elio;
|
||||
|
||||
|
||||
-- Haupttabellen erzeugen
|
||||
CREATE TABLE IF NOT EXISTS elio.blablabla (
|
||||
-- CREATE TABLE IF NOT EXISTS elio.blablabla (
|
||||
-- id INT AUTO_INCREMENT PRIMARY KEY,
|
||||
-- warehouse char(2) NOT NULL,
|
||||
-- productid VARCHAR(20) NOT NULL,
|
||||
-- amount INT DEFAULT 0,
|
||||
-- created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
-- CONSTRAINT location_product_must_be_one UNIQUE (warehouse, productid)
|
||||
-- ) ENGINE=InnoDB;
|
||||
|
||||
|
||||
|
||||
-- Doppelter Boden: Tabellen werden zur Not auch beim Starten des Services im ersten Datenbank Call erzeugt
|
||||
CREATE TABLE IF NOT EXISTS elio.warehouseproducts (
|
||||
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||
warehouse char(2) NOT NULL,
|
||||
productid VARCHAR(20) NOT NULL,
|
||||
@ -23,6 +35,24 @@ CREATE TABLE IF NOT EXISTS elio.deliverytimes (
|
||||
CONSTRAINT delivery_from_to_country_must_be_one UNIQUE (fromcountry, tocountry)
|
||||
) ENGINE=InnoDB;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS elio.reservations (
|
||||
id CHAR(36) PRIMARY KEY DEFAULT UUID(),
|
||||
deliveryId INT,
|
||||
warehouseId INT,
|
||||
amount INT DEFAULT 0,
|
||||
status VARCHAR(30) CHECK(status IN ('RESERVED', 'CONFIRMED', 'ABORTED', 'RELEASED')),
|
||||
reservationGroupId CHAR(36) NOT NULL,
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
CONSTRAINT fk_delivery_id
|
||||
FOREIGN KEY (deliveryId) REFERENCES deliverytimes (id)
|
||||
ON DELETE CASCADE
|
||||
ON UPDATE RESTRICT,
|
||||
CONSTRAINT fk_warehouse_id
|
||||
FOREIGN KEY (warehouseId) REFERENCES warehouseproducts (id)
|
||||
ON DELETE CASCADE
|
||||
ON UPDATE RESTRICT
|
||||
) ENGINE=InnoDB;
|
||||
|
||||
-- Testtabellen erzeugen
|
||||
CREATE TABLE IF NOT EXISTS elio_test.warehouseproducts (
|
||||
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||
@ -43,6 +73,26 @@ CREATE TABLE IF NOT EXISTS elio_test.deliverytimes (
|
||||
CONSTRAINT delivery_from_to_country_must_be_one UNIQUE (fromcountry, tocountry)
|
||||
) ENGINE=InnoDB;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS elio_test.reservations (
|
||||
id CHAR(36) PRIMARY KEY DEFAULT UUID(),
|
||||
deliveryId INT,
|
||||
warehouseId INT,
|
||||
amount INT DEFAULT 0,
|
||||
status VARCHAR(30) CHECK(status IN ('RESERVED', 'CONFIRMED', 'ABORTED', 'RELEASED')),
|
||||
reservationGroupId CHAR(36) NOT NULL,
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
CONSTRAINT fk_delivery_id
|
||||
FOREIGN KEY (deliveryId) REFERENCES deliverytimes (id)
|
||||
ON DELETE CASCADE
|
||||
ON UPDATE RESTRICT,
|
||||
CONSTRAINT fk_warehouse_id
|
||||
FOREIGN KEY (warehouseId) REFERENCES warehouseproducts (id)
|
||||
ON DELETE CASCADE
|
||||
ON UPDATE RESTRICT
|
||||
) ENGINE=InnoDB;
|
||||
|
||||
|
||||
|
||||
-- Userkram
|
||||
CREATE USER IF NOT EXISTS 'elio'@'%' IDENTIFIED BY 'eliogeheim';
|
||||
CREATE USER IF NOT EXISTS 'elio_test'@'%' IDENTIFIED BY 'eliogeheim';
|
||||
|
||||
@ -10,33 +10,6 @@ import (
|
||||
"gittea.marcokittel.de/elio/eliotools/datawriter/internal/database"
|
||||
)
|
||||
|
||||
type Container struct {
|
||||
Products map[string]int `json:"products"`
|
||||
Context Context `json:"context"`
|
||||
}
|
||||
|
||||
type Product struct {
|
||||
Warehouse string `json:"warehouse"`
|
||||
Quantity int `json:"quantity"`
|
||||
Delivery int `json:"delivery_time"`
|
||||
}
|
||||
|
||||
type OutgoingProducts struct {
|
||||
Products map[string][]Product `json:"products"`
|
||||
}
|
||||
|
||||
func NewOutgoingProducts() *OutgoingProducts {
|
||||
op := OutgoingProducts{
|
||||
Products: make(map[string][]Product),
|
||||
}
|
||||
return &op
|
||||
}
|
||||
|
||||
type Context struct {
|
||||
Country string `json:"country"`
|
||||
State string `json:"state"`
|
||||
}
|
||||
|
||||
func GetProductApiHandleFunc(nps *database.ProductService) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Method != "POST" {
|
||||
@ -59,7 +32,12 @@ func GetProductApiHandleFunc(nps *database.ProductService) http.HandlerFunc {
|
||||
//Todo Fehlerhandling
|
||||
log.Println(err)
|
||||
}
|
||||
jsonResult, err := json.Marshal(result)
|
||||
if len(result) == 0 {
|
||||
w.WriteHeader(http.StatusNoContent)
|
||||
fmt.Fprintln(w)
|
||||
return
|
||||
}
|
||||
jsonResult, err := json.Marshal(result[0])
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
}
|
||||
|
||||
@ -88,6 +88,7 @@ func (d *DatabaseWriter) createReservationTableIfNotExist() error {
|
||||
warehouseId INT,
|
||||
amount INT DEFAULT 0,
|
||||
status VARCHAR(30) CHECK(status IN ('RESERVED', 'CONFIRMED', 'ABORTED', 'RELEASED')),
|
||||
reservationGroupId CHAR(36) NOT NULL,
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
CONSTRAINT fk_delivery_id
|
||||
FOREIGN KEY (deliveryId) REFERENCES deliverytimes (id)
|
||||
@ -127,6 +128,16 @@ func NewDatabaseWriter(connectionString string) *DatabaseWriter {
|
||||
return &db
|
||||
}
|
||||
|
||||
func (d *DatabaseWriter) ReleaseReservierungenAfterOneDay() error {
|
||||
_, err := d.db.Exec(`
|
||||
UPDATE reservations r SET status = 'RELEASED'
|
||||
WHERE r.created_at < DATE_SUB(NOW(), INTERVAL 1 DAY );`)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *DatabaseWriter) UpdateOrInsertWarehouseProduct(warehouse string, productID string, amount int) error {
|
||||
_, err := d.db.Exec(`
|
||||
INSERT INTO warehouseproducts (warehouse, productid, amount)
|
||||
@ -167,14 +178,21 @@ type ProductDelivery struct {
|
||||
|
||||
func (d *DatabaseReader) GetProductByProductIdDeliveryCountryAndState(prod_id, delivery_country, delivery_country_state string) ([]ProductDelivery, error) {
|
||||
stmt := `
|
||||
SELECT whp.id, warehouse, amount, d.delivery, d.id deliveryId
|
||||
SELECT id, warehouse, (amount - reserviert) amount, delivery, deliveryId FROM(
|
||||
SELECT whp.id, warehouse, whp.amount, sum(coalesce(r.amount,0) )reserviert, d.delivery, d.id deliveryId
|
||||
FROM warehouseproducts whp
|
||||
left join deliverytimes d
|
||||
on whp.warehouse = d.fromcountry
|
||||
left join reservations r
|
||||
on whp.id = r.warehouseId
|
||||
and r.status in ('RESERVED', 'CONFIRMED')
|
||||
where productid = ?
|
||||
and d.tocountry = ?
|
||||
and d.state = ?
|
||||
order by d.delivery asc, amount desc
|
||||
GROUP BY whp.id, d.delivery
|
||||
order by d.delivery asc, whp.amount desc
|
||||
) as zumAufsummieren
|
||||
having amount > 0
|
||||
`
|
||||
|
||||
rows, err := d.db.Query(stmt, prod_id, delivery_country, delivery_country_state)
|
||||
@ -211,6 +229,24 @@ func (d *DatabaseReader) GetReservationStateById(productId UUID) (string, error)
|
||||
return status, nil
|
||||
}
|
||||
|
||||
func (d *DatabaseReader) GetReservationItemsByGroupId(groupId UUID) (string, error) {
|
||||
stmt := `
|
||||
SELECT status FROM reservations WHERE id = ?
|
||||
`
|
||||
row, err := d.db.Query(stmt, groupId)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
defer row.Close()
|
||||
row.Next()
|
||||
var status string
|
||||
err = row.Scan(&status)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return status, nil
|
||||
}
|
||||
|
||||
func (d *DatabaseWriter) updateReservationState(Id UUID, status string) error {
|
||||
_, err := d.db.Exec("UPDATE reservations SET Status=? WHERE id=?", status, Id)
|
||||
if err != nil {
|
||||
@ -219,6 +255,14 @@ func (d *DatabaseWriter) updateReservationState(Id UUID, status string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *DatabaseWriter) updateReservationStateByGroupId(groupId UUID, status string) error {
|
||||
_, err := d.db.Exec("UPDATE reservations SET Status=? WHERE reservationGroupId=?", status, groupId)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *DatabaseWriter) AbortReservation(Id UUID) error {
|
||||
return d.updateReservationState(Id, "ABORTED")
|
||||
}
|
||||
@ -231,9 +275,21 @@ func (d *DatabaseWriter) ReleaseReservation(Id UUID) error {
|
||||
return d.updateReservationState(Id, "RELEASED")
|
||||
}
|
||||
|
||||
func (d *DatabaseWriter) ReserveReservation(deliveryId, warehouseId, amount int) (UUID, error) {
|
||||
func (d *DatabaseWriter) AbortReservationGroup(groupId UUID) error {
|
||||
return d.updateReservationStateByGroupId(groupId, "ABORTED")
|
||||
}
|
||||
|
||||
func (d *DatabaseWriter) ConfirmReservationGroup(groupId UUID) error {
|
||||
return d.updateReservationStateByGroupId(groupId, "CONFIRMED")
|
||||
}
|
||||
|
||||
func (d *DatabaseWriter) ReleaseReservationGroup(groupId UUID) error {
|
||||
return d.updateReservationStateByGroupId(groupId, "RELEASED")
|
||||
}
|
||||
|
||||
func (d *DatabaseWriter) ReserveReservationItem(deliveryId, warehouseId, amount int, groupId UUID) (UUID, error) {
|
||||
newUUID := uuid.New().String()
|
||||
_, err := d.db.Exec("INSERT INTO reservations (id, deliveryId, warehouseId, amount, status) VALUES (?,?,?,?,'RESERVED')", newUUID, deliveryId, warehouseId, amount)
|
||||
_, err := d.db.Exec("INSERT INTO reservations (id, deliveryId, warehouseId, amount, status, reservationGroupId) VALUES (?,?,?,?,'RESERVED', ?)", newUUID, deliveryId, warehouseId, amount, groupId)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
@ -3,9 +3,12 @@ package database
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"log"
|
||||
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
// Unit Tests sollten niemals auf der Produktivdatenbank stattfinden.
|
||||
@ -123,8 +126,9 @@ func TestInsertStatement(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Errorf("Es sollten Datensätze auffinbdar sein! %s", err)
|
||||
}
|
||||
groupId := uuid.New().String()
|
||||
for _, item := range data {
|
||||
uuid, err := dbr.DatabaseWriter.ReserveReservation(item.DeliveryId, item.Id, item.Amount)
|
||||
uuid, err := dbr.DatabaseWriter.ReserveReservationItem(item.DeliveryId, item.Id, item.Amount, UUID(groupId))
|
||||
if err != nil {
|
||||
t.Errorf("Das Einfügen einer neuen Reservierung darf nicht fehlschlagen! %s", err)
|
||||
}
|
||||
@ -141,8 +145,9 @@ func TestAbortReservationStatement(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Errorf("Es sollten Datensätze auffinbdar sein! %s", err)
|
||||
}
|
||||
groupId := uuid.New().String()
|
||||
for _, item := range data {
|
||||
uuid, err := dbr.DatabaseWriter.ReserveReservation(item.DeliveryId, item.Id, item.Amount)
|
||||
uuid, err := dbr.DatabaseWriter.ReserveReservationItem(item.DeliveryId, item.Id, item.Amount, UUID(groupId))
|
||||
if err != nil {
|
||||
t.Errorf("Das Einfügen einer neuen Reservierung darf nicht fehlschlagen! %s", err)
|
||||
}
|
||||
@ -166,8 +171,9 @@ func TestConfirmReservationStatement(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Errorf("Es sollten Datensätze auffinbdar sein! %s", err)
|
||||
}
|
||||
groupId := uuid.New().String()
|
||||
for _, item := range data {
|
||||
uuid, err := dbr.DatabaseWriter.ReserveReservation(item.DeliveryId, item.Id, item.Amount)
|
||||
uuid, err := dbr.DatabaseWriter.ReserveReservationItem(item.DeliveryId, item.Id, item.Amount, UUID(groupId))
|
||||
if err != nil {
|
||||
t.Errorf("Das Einfügen einer neuen Reservierung darf nicht fehlschlagen! %s", err)
|
||||
}
|
||||
@ -191,8 +197,9 @@ func TestReleasedReservationStatement(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Errorf("Es sollten Datensätze auffinbdar sein! %s", err)
|
||||
}
|
||||
groupId := uuid.New().String()
|
||||
for _, item := range data {
|
||||
uuid, err := dbr.DatabaseWriter.ReserveReservation(item.DeliveryId, item.Id, item.Amount)
|
||||
uuid, err := dbr.DatabaseWriter.ReserveReservationItem(item.DeliveryId, item.Id, item.Amount, UUID(groupId))
|
||||
if err != nil {
|
||||
t.Errorf("Das Einfügen einer neuen Reservierung darf nicht fehlschlagen! %s", err)
|
||||
}
|
||||
@ -210,6 +217,20 @@ func TestReleasedReservationStatement(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestFetchReservationData(t *testing.T) {
|
||||
payloadStr := `{ "products": { "A6053": 30 }, "context": { "country": "EU", "state": "" }}`
|
||||
var payload Container
|
||||
err := json.Unmarshal([]byte(payloadStr), &payload)
|
||||
if err != nil {
|
||||
t.Errorf("Dieser Fehler beim Konvertieren der JSON Payload sollte nicht passieren! \n%s", err)
|
||||
return
|
||||
}
|
||||
ps := NewProductService(connectionString)
|
||||
// groupId := uuid.New().String()
|
||||
op, err := ps.FetchData(&payload)
|
||||
log.Println(op)
|
||||
}
|
||||
|
||||
func TestFetchDataReservation(t *testing.T) {
|
||||
payloadStr := `{ "products": { "A6053": 30 }, "context": { "country": "EU", "state": "" }}`
|
||||
|
||||
@ -220,7 +241,8 @@ func TestFetchDataReservation(t *testing.T) {
|
||||
return
|
||||
}
|
||||
ps := NewProductService(connectionString)
|
||||
op, err := ps.FetchData(&payload)
|
||||
groupId := uuid.New().String()
|
||||
op, err := ps.FetchReservationData(&payload, UUID(groupId))
|
||||
if err != nil {
|
||||
t.Errorf("Das Datafetchen und umwandeln in eine Map muss sauber funktionieren! %s", err)
|
||||
}
|
||||
@ -312,16 +334,17 @@ func TestOutgoingReservationInDatabase(t *testing.T) {
|
||||
t.Errorf("Dieser Fehler beim Konvertieren der JSON Payload sollte nicht passieren! \n%s", err)
|
||||
return
|
||||
}
|
||||
groupId := uuid.New().String()
|
||||
ps := NewProductService(connectionString)
|
||||
op, err := ps.FetchData(&payload)
|
||||
op, err := ps.FetchReservationData(&payload, UUID(groupId))
|
||||
if err != nil {
|
||||
t.Errorf("Das Datafetchen und umwandeln in eine Map muss sauber funktionieren! %s", err)
|
||||
}
|
||||
valTocompare := `[{map[A6053:[{AT 4 1 8508 63} {DE 1 2 7507 18}]]}]`
|
||||
valTocompare := fmt.Sprintf(`[{%s map[A6053:[{AT 4 1 8508 63} {DE 1 2 7507 18}]]}]`, groupId)
|
||||
if fmt.Sprint(op) != valTocompare {
|
||||
t.Errorf("Mist, sollte sein %s ist aber %s", valTocompare, fmt.Sprint(op))
|
||||
}
|
||||
uuids, err := ps.ReserviereBestellungen(op)
|
||||
uuids, err := ps.ReserviereBestellungen(op, UUID(groupId))
|
||||
if err != nil {
|
||||
t.Errorf("Das Reservieren ist fehlgeschlagen! \n%s", err)
|
||||
}
|
||||
@ -331,10 +354,20 @@ func TestOutgoingReservationInDatabase(t *testing.T) {
|
||||
t.Errorf("Der Status muss RESERVED sein. Stattdessen ist er: %s", status)
|
||||
}
|
||||
|
||||
}
|
||||
ps.dbr.ReleaseReservationGroup(UUID(groupId))
|
||||
for _, uuid := range *uuids {
|
||||
status, err := ps.dbr.GetReservationStateById(uuid)
|
||||
if err != nil || status != "RELEASED" {
|
||||
t.Errorf("Der Status muss RELEASED sein. Stattdessen ist er: %s", status)
|
||||
}
|
||||
|
||||
}
|
||||
jsonResult, err := json.Marshal(op)
|
||||
jsonStrForApiResponse := `[{"products":{"A6053":[{"warehouse":"AT","quantity":4,"delivery_time":1},{"warehouse":"DE","quantity":1,"delivery_time":2}]}}]`
|
||||
jsonGuid := fmt.Sprintf("\"id\":\"%s\"", UUID(groupId))
|
||||
jsonStrForApiResponse := fmt.Sprintf(`[{%s,"products":{"A6053":[{"warehouse":"AT","quantity":4,"delivery_time":1},{"warehouse":"DE","quantity":1,"delivery_time":2}]}}]`, jsonGuid)
|
||||
if string(jsonResult) != jsonStrForApiResponse {
|
||||
t.Errorf("Mist, sollte sein %s ist aber %s", string(jsonResult), jsonStrForApiResponse)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -1,6 +1,10 @@
|
||||
package database
|
||||
|
||||
import "log"
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"time"
|
||||
)
|
||||
|
||||
type ProductService struct {
|
||||
dbr *DatabaseReader
|
||||
@ -21,6 +25,10 @@ type Product struct {
|
||||
type OutgoingProducts struct {
|
||||
Products map[string][]Product `json:"products"`
|
||||
}
|
||||
type OutgoingReservationProducts struct {
|
||||
Id UUID `json:"id"`
|
||||
Products map[string][]Product `json:"products"`
|
||||
}
|
||||
|
||||
func NewOutgoingProducts() *OutgoingProducts {
|
||||
op := OutgoingProducts{
|
||||
@ -28,6 +36,13 @@ func NewOutgoingProducts() *OutgoingProducts {
|
||||
}
|
||||
return &op
|
||||
}
|
||||
func NewOutgoingReservationsProducts(groupId UUID) *OutgoingReservationProducts {
|
||||
op := OutgoingReservationProducts{
|
||||
Id: groupId,
|
||||
Products: make(map[string][]Product),
|
||||
}
|
||||
return &op
|
||||
}
|
||||
|
||||
type Context struct {
|
||||
Country string `json:"country"`
|
||||
@ -40,7 +55,22 @@ func NewProductService(connectionString string) *ProductService {
|
||||
return &p
|
||||
}
|
||||
|
||||
func (p *ProductService) ReserviereBestellungen(op []OutgoingProducts) (*[]UUID, error) {
|
||||
func (p *ProductService) Autorelease() {
|
||||
//Das müsste saubererer sein. Spare ich mir aber, weil der Webserver alles blockiert.
|
||||
//Besser wäre das Arbeiten mit Context. Gefahr ist aber minimal
|
||||
go func() {
|
||||
for {
|
||||
fmt.Println("Prüfe nach Registrierungen die Freigegeben werden können. (Älter als 24 Stunden)")
|
||||
err := p.dbr.ReleaseReservierungenAfterOneDay()
|
||||
if err != nil {
|
||||
log.Printf("Fehler beim Ausführen von Autorelease() des Logs. %s", err)
|
||||
}
|
||||
time.Sleep(30 * time.Second)
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
func (p *ProductService) ReserviereBestellungen(op []OutgoingReservationProducts, groupId UUID) (*[]UUID, error) {
|
||||
var uuids []UUID
|
||||
for _, hsmp := range op {
|
||||
for _, prod := range hsmp.Products {
|
||||
@ -49,7 +79,7 @@ func (p *ProductService) ReserviereBestellungen(op []OutgoingProducts) (*[]UUID,
|
||||
warehouseId := concreteProduct.WhdId
|
||||
amount := concreteProduct.Quantity
|
||||
// Todo: Handler Injection wäre hier interessant
|
||||
uuid, err := p.dbr.ReserveReservation(deliveryId, warehouseId, amount)
|
||||
uuid, err := p.dbr.ReserveReservationItem(deliveryId, warehouseId, amount, groupId)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -60,13 +90,25 @@ func (p *ProductService) ReserviereBestellungen(op []OutgoingProducts) (*[]UUID,
|
||||
return &uuids, nil
|
||||
}
|
||||
|
||||
func (p *ProductService) ConfirmBestellung(groudId UUID) error {
|
||||
return p.dbr.ConfirmReservationGroup(groudId)
|
||||
}
|
||||
|
||||
func (p *ProductService) AbortBestellung(groudId UUID) error {
|
||||
return p.dbr.AbortReservationGroup(groudId)
|
||||
}
|
||||
|
||||
func (p *ProductService) ReleaseBestellung(groudId UUID) error {
|
||||
return p.dbr.ReleaseReservationGroup(groudId)
|
||||
}
|
||||
|
||||
func (p *ProductService) FetchData(payload *Container) ([]OutgoingProducts, error) {
|
||||
result := []OutgoingProducts{}
|
||||
for key, item := range payload.Products {
|
||||
//Checken ob die Felder leer sind / Validitätsprüfung einbauen
|
||||
products, err := p.dbr.GetProductByProductIdDeliveryCountryAndState(key, payload.Context.Country, payload.Context.State)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return nil, err
|
||||
}
|
||||
if len(products) == 0 {
|
||||
continue
|
||||
@ -91,3 +133,35 @@ func (p *ProductService) FetchData(payload *Container) ([]OutgoingProducts, erro
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func (p *ProductService) FetchReservationData(payload *Container, groupId UUID) ([]OutgoingReservationProducts, error) {
|
||||
result := []OutgoingReservationProducts{}
|
||||
for key, item := range payload.Products {
|
||||
//Checken ob die Felder leer sind / Validitätsprüfung einbauen
|
||||
products, err := p.dbr.GetProductByProductIdDeliveryCountryAndState(key, payload.Context.Country, payload.Context.State)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(products) == 0 {
|
||||
continue
|
||||
}
|
||||
gebrauchteProduktAnzahl := item
|
||||
op := NewOutgoingReservationsProducts(groupId)
|
||||
for _, db_products := range products {
|
||||
if gebrauchteProduktAnzahl <= 0 {
|
||||
continue
|
||||
}
|
||||
if db_products.Amount >= gebrauchteProduktAnzahl {
|
||||
newProduct := Product{Delivery: db_products.DeliveryDays, Quantity: gebrauchteProduktAnzahl, Warehouse: db_products.Warehouse, WhdId: db_products.Id, DeliveryId: db_products.DeliveryId}
|
||||
gebrauchteProduktAnzahl = 0
|
||||
op.Products[key] = append(op.Products[key], newProduct)
|
||||
} else if db_products.Amount < gebrauchteProduktAnzahl {
|
||||
newProduct := Product{Delivery: db_products.DeliveryDays, Quantity: db_products.Amount, Warehouse: db_products.Warehouse, WhdId: db_products.Id, DeliveryId: db_products.DeliveryId}
|
||||
gebrauchteProduktAnzahl -= db_products.Amount
|
||||
op.Products[key] = append(op.Products[key], newProduct)
|
||||
}
|
||||
}
|
||||
result = append(result, *op)
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user