Merge branch 'main' of git-ssh.tornberg.me:mats/go-cart-actor
Some checks failed
Build and Publish / BuildAndDeployAmd64 (push) Successful in 28s
Build and Publish / BuildAndDeploy (push) Has been cancelled

This commit is contained in:
matst80
2024-11-15 09:29:00 +01:00
17 changed files with 1129 additions and 157 deletions

42
api-tests/cart.http Normal file
View File

@@ -0,0 +1,42 @@
### Add item to cart
POST https://cart.tornberg.me/api/12345
Content-Type: application/json
{
"sku": "763281",
"quantity": 1
}
### Update quanity of item in cart
PUT https://cart.tornberg.me/api/12345
Content-Type: application/json
{
"id": 1,
"quantity": 1
}
### Delete item from cart
DELETE https://cart.tornberg.me/api/12345/1
### Set delivery
POST https://cart.tornberg.me/api/1002/delivery
Content-Type: application/json
{
"provider": "postnord",
"items": []
}
### Get cart
GET https://cart.tornberg.me/api/12345
### Remove delivery method
DELETE https://cart.tornberg.me/api/12345/delivery/2

View File

@@ -8,6 +8,7 @@ import (
"time" "time"
messages "git.tornberg.me/go-cart-actor/proto" messages "git.tornberg.me/go-cart-actor/proto"
klarna "github.com/Flaconi/go-klarna"
) )
type CartId [16]byte type CartId [16]byte
@@ -26,19 +27,36 @@ func (id *CartId) UnmarshalJSON(data []byte) error {
return nil return nil
} }
type StockStatus int
const (
OutOfStock StockStatus = 0
LowStock StockStatus = 1
InStock StockStatus = 2
)
type CartItem struct { type CartItem struct {
Id int `json:"id"` Id int `json:"id"`
ParentId int `json:"parentId,omitempty"`
Sku string `json:"sku"` Sku string `json:"sku"`
Name string `json:"name"` Name string `json:"name"`
Price int64 `json:"price"` Price int64 `json:"price"`
OrgPrice int64 `json:"orgPrice"`
Stock StockStatus `json:"stock"`
Quantity int `json:"qty"` Quantity int `json:"qty"`
Image string `json:"image"` Tax int `json:"tax"`
Disclaimer string `json:"disclaimer,omitempty"`
ArticleType string `json:"type,omitempty"`
Image string `json:"image,omitempty"`
Outlet *string `json:"outlet,omitempty"`
} }
type CartDelivery struct { type CartDelivery struct {
Id int `json:"id"`
Provider string `json:"provider"` Provider string `json:"provider"`
Price int64 `json:"price"` Price int64 `json:"price"`
Items []int `json:"items"` Items []int `json:"items"`
PickupPoint *messages.PickupPoint `json:"pickupPoint,omitempty"`
} }
type CartGrain struct { type CartGrain struct {
@@ -49,7 +67,8 @@ type CartGrain struct {
Id CartId `json:"id"` Id CartId `json:"id"`
Items []*CartItem `json:"items"` Items []*CartItem `json:"items"`
TotalPrice int64 `json:"totalPrice"` TotalPrice int64 `json:"totalPrice"`
Deliveries []CartDelivery `json:"deliveries,omitempty"` Deliveries []*CartDelivery `json:"deliveries,omitempty"`
Processing bool `json:"processing"`
} }
type Grain interface { type Grain interface {
@@ -79,34 +98,55 @@ func (c *CartGrain) GetCurrentState() (*FrameWithPayload, error) {
return &ret, nil return &ret, nil
} }
func getInt(data interface{}) (int, error) {
switch v := data.(type) {
case float64:
return int(v), nil
case int:
return v, nil
default:
return 0, fmt.Errorf("invalid type")
}
}
func getItemData(sku string, qty int) (*messages.AddItem, error) { func getItemData(sku string, qty int) (*messages.AddItem, error) {
item, err := FetchItem(sku) item, err := FetchItem(sku)
if err != nil { if err != nil {
return nil, err return nil, err
} }
price := 0 orgPrice, _ := getInt(item.Fields[5])
priceField, ok := item.Fields[4]
if ok { price, priceErr := getInt(item.Fields[4])
priceFloat, ok := priceField.(float64)
if !ok { if priceErr != nil {
price, ok = priceField.(int)
if !ok {
return nil, fmt.Errorf("invalid price type")
}
} else {
price = int(priceFloat)
}
}
if price == 0 {
return nil, fmt.Errorf("invalid price") return nil, fmt.Errorf("invalid price")
} }
stock := InStock
if item.StockLevel == "0" || item.StockLevel == "" {
stock = OutOfStock
} else if item.StockLevel == "5+" {
stock = LowStock
}
articleType, _ := item.Fields[1].(string)
outletGrade, ok := item.Fields[20].(string)
var outlet *string
if ok {
outlet = &outletGrade
}
return &messages.AddItem{ return &messages.AddItem{
Quantity: int32(qty), Quantity: int32(qty),
Price: int64(price), Price: int64(price),
OrgPrice: int64(orgPrice),
Sku: sku, Sku: sku,
Name: item.Title, Name: item.Title,
Image: item.Img, Image: item.Img,
Stock: int32(stock),
Tax: 2500,
ArticleType: articleType,
Disclaimer: item.Disclaimer,
Outlet: outlet,
}, nil }, nil
} }
@@ -182,6 +222,11 @@ func (c *CartGrain) FindItemWithSku(sku string) (*CartItem, bool) {
return nil, false return nil, false
} }
func GetTaxAmount(total int, tax int) int {
taxD := 10000 / float64(tax)
return int(float64(total) / float64((1 + taxD)))
}
func (c *CartGrain) HandleMessage(message *Message, isReplay bool) (*FrameWithPayload, error) { func (c *CartGrain) HandleMessage(message *Message, isReplay bool) (*FrameWithPayload, error) {
if message.TimeStamp == nil { if message.TimeStamp == nil {
now := time.Now().Unix() now := time.Now().Unix()
@@ -219,6 +264,10 @@ func (c *CartGrain) HandleMessage(message *Message, isReplay bool) (*FrameWithPa
} else { } else {
c.mu.Lock() c.mu.Lock()
c.lastItemId++ c.lastItemId++
tax := 2500
if msg.Tax > 0 {
tax = int(msg.Tax)
}
c.Items = append(c.Items, &CartItem{ c.Items = append(c.Items, &CartItem{
Id: c.lastItemId, Id: c.lastItemId,
Quantity: int(msg.Quantity), Quantity: int(msg.Quantity),
@@ -226,6 +275,12 @@ func (c *CartGrain) HandleMessage(message *Message, isReplay bool) (*FrameWithPa
Name: msg.Name, Name: msg.Name,
Price: msg.Price, Price: msg.Price,
Image: msg.Image, Image: msg.Image,
Stock: StockStatus(msg.Stock),
Disclaimer: msg.Disclaimer,
OrgPrice: msg.OrgPrice,
ArticleType: msg.ArticleType,
Outlet: msg.Outlet,
Tax: tax,
}) })
c.TotalPrice += msg.Price * int64(msg.Quantity) c.TotalPrice += msg.Price * int64(msg.Quantity)
c.mu.Unlock() c.mu.Unlock()
@@ -287,13 +342,101 @@ func (c *CartGrain) HandleMessage(message *Message, isReplay bool) (*FrameWithPa
} }
} }
} }
c.Deliveries = append(c.Deliveries, CartDelivery{ if len(items) > 0 {
c.Deliveries = append(c.Deliveries, &CartDelivery{
Id: c.lastDeliveryId,
Provider: msg.Provider, Provider: msg.Provider,
Price: 49, Price: 49,
Items: items, Items: items,
}) })
c.Processing = true
go func() {
time.Sleep(5 * time.Second)
c.Processing = false
}()
}
} }
case RemoveDeliveryType: case RemoveDeliveryType:
msg, ok := message.Content.(*messages.RemoveDelivery)
if !ok {
err = fmt.Errorf("expected RemoveDelivery")
} else {
deliveries := make([]*CartDelivery, 0, len(c.Deliveries))
for _, delivery := range c.Deliveries {
if delivery.Id == int(msg.Id) {
c.TotalPrice -= delivery.Price
} else {
deliveries = append(deliveries, delivery)
}
}
c.Deliveries = deliveries
}
case SetPickupPointType:
msg, ok := message.Content.(*messages.SetPickupPoint)
if !ok {
err = fmt.Errorf("expected SetPickupPoint")
} else {
for _, delivery := range c.Deliveries {
if delivery.Id == int(msg.DeliveryId) {
delivery.PickupPoint = &messages.PickupPoint{
Id: msg.Id,
Address: msg.Address,
City: msg.City,
Zip: msg.Zip,
Country: msg.Country,
Name: msg.Name,
}
break
}
}
}
case CreateCheckoutOrderType:
msg, ok := message.Content.(*messages.CreateCheckoutOrder)
if !ok {
err = fmt.Errorf("expected CreateCheckoutOrder")
} else {
orderLines := make([]*klarna.Line, 0, len(c.Items))
totalTax := 0
for _, item := range c.Items {
total := int(item.Price) * item.Quantity
taxAmount := GetTaxAmount(total, item.Tax)
totalTax += taxAmount
orderLines = append(orderLines, &klarna.Line{
Type: "physical",
Reference: item.Sku,
Name: item.Name,
Quantity: item.Quantity,
UnitPrice: int(item.Price),
TaxRate: int(item.Tax),
QuantityUnit: "st",
TotalAmount: total,
TotalTaxAmount: taxAmount,
ImageURL: item.Image,
})
}
order := klarna.CheckoutOrder{
PurchaseCountry: "SE",
PurchaseCurrency: "SEK",
Locale: "sv-se",
OrderAmount: int(c.TotalPrice),
OrderTaxAmount: totalTax,
OrderLines: orderLines,
MerchantReference1: c.Id.String(),
MerchantURLS: &klarna.CheckoutMerchantURLS{
Terms: msg.Terms,
Checkout: msg.Checkout,
Confirmation: msg.Confirmation,
Push: msg.Push,
},
}
orderPayload, err := json.Marshal(order)
if err != nil {
return nil, err
}
result := MakeFrameWithPayload(RemoteCreateOrderReply, 200, orderPayload)
return &result, nil
}
default: default:
err = fmt.Errorf("unknown message type %d", message.Type) err = fmt.Errorf("unknown message type %d", message.Type)
} }

View File

@@ -16,6 +16,13 @@ func GetMessage(t uint16, data interface{}) *Message {
} }
} }
func TestTaxAmount(t *testing.T) {
taxAmount := GetTaxAmount(12500, 2500)
if taxAmount != 2500 {
t.Errorf("Expected 2500, got %d\n", taxAmount)
}
}
func TestAddToCartShortCut(t *testing.T) { func TestAddToCartShortCut(t *testing.T) {
grain, err := spawn(ToCartId("kalle")) grain, err := spawn(ToCartId("kalle"))
if err != nil { if err != nil {
@@ -67,6 +74,25 @@ func TestAddToCartShortCut(t *testing.T) {
} }
} }
func TestAddRequestToGrain(t *testing.T) {
grain, err := spawn(ToCartId("kalle"))
if err != nil {
t.Errorf("Error spawning: %v\n", err)
}
msg := GetMessage(AddRequestType, &messages.AddRequest{
Quantity: 2,
Sku: "763281",
})
result, err := grain.HandleMessage(msg, false)
if err != nil {
t.Errorf("Error handling message: %v\n", err)
}
if result.StatusCode != 200 {
t.Errorf("Call failed\n")
}
t.Log(result)
}
func TestAddToCart(t *testing.T) { func TestAddToCart(t *testing.T) {
grain, err := spawn(ToCartId("kalle")) grain, err := spawn(ToCartId("kalle"))
if err != nil { if err != nil {

View File

@@ -1,3 +1,12 @@
apiVersion: v1
kind: Secret
metadata:
name: klarna-api-credentials
data:
username: ZjQzZDY3YjEtNzA2Yy00NTk2LTliNTgtYjg1YjU2NDEwZTUw
password: a2xhcm5hX3Rlc3RfYXBpX0trUWhWVE5yYVZsV2FsTnhTRVp3Y1ZSSFF5UkVNRmxyY25Kd1AxSndQMGdzWmpRelpEWTNZakV0TnpBMll5MDBOVGsyTFRsaU5UZ3RZamcxWWpVMk5ERXdaVFV3TERFc2JUUkNjRFpWU1RsTllsSk1aMlEyVEc4MmRVODNZMkozUlRaaFdEZDViV3AwYkhGV1JqTjVNQzlaYXow
type: Opaque
---
apiVersion: apps/v1 apiVersion: apps/v1
kind: Deployment kind: Deployment
metadata: metadata:
@@ -75,6 +84,16 @@ spec:
env: env:
- name: TZ - name: TZ
value: "Europe/Stockholm" value: "Europe/Stockholm"
- name: KLARNA_API_USERNAME
valueFrom:
secretKeyRef:
name: klarna-api-credentials
key: username
- name: KLARNA_API_PASSWORD
valueFrom:
secretKeyRef:
name: klarna-api-credentials
key: password
- name: POD_IP - name: POD_IP
valueFrom: valueFrom:
fieldRef: fieldRef:
@@ -165,6 +184,16 @@ spec:
env: env:
- name: TZ - name: TZ
value: "Europe/Stockholm" value: "Europe/Stockholm"
- name: KLARNA_API_USERNAME
valueFrom:
secretKeyRef:
name: klarna-api-credentials
key: username
- name: KLARNA_API_PASSWORD
valueFrom:
secretKeyRef:
name: klarna-api-credentials
key: password
- name: POD_IP - name: POD_IP
valueFrom: valueFrom:
fieldRef: fieldRef:

83
discarded-host.go Normal file
View File

@@ -0,0 +1,83 @@
package main
import (
"fmt"
"log"
"net"
"sync"
"time"
)
type DiscardedHost struct {
*Connection
Host string
Tries int
}
type DiscardedHostHandler struct {
mu sync.RWMutex
port int
hosts []*DiscardedHost
onConnection *func(string)
}
func (d *DiscardedHostHandler) run() {
for range time.Tick(time.Second) {
d.mu.RLock()
lst := make([]*DiscardedHost, 0, len(d.hosts))
for _, host := range d.hosts {
if host.Tries >= 0 || host.Tries < 5 {
go d.testConnection(host)
lst = append(lst, host)
} else {
if host.Tries > 0 {
log.Printf("Host %s discarded after %d tries", host.Host, host.Tries)
}
}
}
d.mu.RUnlock()
d.mu.Lock()
d.hosts = lst
d.mu.Unlock()
}
}
func (d *DiscardedHostHandler) testConnection(host *DiscardedHost) {
addr := fmt.Sprintf("%s:%d", host.Host, d.port)
conn, err := net.Dial("tcp", addr)
if err != nil {
host.Tries++
host.Tries = -1
} else {
conn.Close()
if d.onConnection != nil {
fn := *d.onConnection
fn(host.Host)
}
}
}
func NewDiscardedHostHandler(port int) *DiscardedHostHandler {
ret := &DiscardedHostHandler{
hosts: make([]*DiscardedHost, 0),
port: port,
}
go ret.run()
return ret
}
func (d *DiscardedHostHandler) SetReconnectHandler(fn func(string)) {
d.onConnection = &fn
}
func (d *DiscardedHostHandler) AppendHost(host string) {
d.mu.Lock()
defer d.mu.Unlock()
log.Printf("Adding host %s to retry list", host)
d.hosts = append(d.hosts, &DiscardedHost{
Host: host,
Tries: 0,
})
}

18
discarded-host_test.go Normal file
View File

@@ -0,0 +1,18 @@
package main
import (
"testing"
"time"
)
func TestDiscardedHost(t *testing.T) {
dh := NewDiscardedHostHandler(8080)
dh.SetReconnectHandler(func(host string) {
t.Log(host)
})
dh.AppendHost("localhost")
time.Sleep(2 * time.Second)
if dh.hosts[0].Tries == 0 {
t.Error("Host not tested")
}
}

1
go.mod
View File

@@ -11,6 +11,7 @@ require (
) )
require ( require (
github.com/Flaconi/go-klarna v0.0.0-20230216165926-e2f708c721d9 // indirect
github.com/beorn7/perks v1.0.1 // indirect github.com/beorn7/perks v1.0.1 // indirect
github.com/cespare/xxhash/v2 v2.3.0 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect

2
go.sum
View File

@@ -1,3 +1,5 @@
github.com/Flaconi/go-klarna v0.0.0-20230216165926-e2f708c721d9 h1:U5gu3M9/khqtvgg6iRKo0+nxGEfPHWFHRlKrbZvFxIY=
github.com/Flaconi/go-klarna v0.0.0-20230216165926-e2f708c721d9/go.mod h1:+LVFV9FXH5cwN1VcU30WcNYRs5FhkEtL7/IqqTD42cU=
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=

View File

@@ -37,7 +37,7 @@ func spawn(id CartId) (*CartGrain, error) {
ret := &CartGrain{ ret := &CartGrain{
lastItemId: 0, lastItemId: 0,
lastDeliveryId: 0, lastDeliveryId: 0,
Deliveries: []CartDelivery{}, Deliveries: []*CartDelivery{},
Id: id, Id: id,
Items: []*CartItem{}, Items: []*CartItem{},
storageMessages: []Message{}, storageMessages: []Message{},
@@ -178,6 +178,13 @@ func main() {
w.WriteHeader(http.StatusOK) w.WriteHeader(http.StatusOK)
w.Write([]byte("ok")) w.Write([]byte("ok"))
}) })
mux.HandleFunc("/push", func(w http.ResponseWriter, r *http.Request) {
log.Print(r.Body)
})
mux.HandleFunc("/version", func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK)
w.Write([]byte("1.0.0"))
})
sigs := make(chan os.Signal, 1) sigs := make(chan os.Signal, 1)
done := make(chan bool, 1) done := make(chan bool, 1)

View File

@@ -219,3 +219,34 @@ func (h *RemoveDeliveryHandler) Is(m *Message) bool {
_, ok := m.Content.(*messages.RemoveDelivery) _, ok := m.Content.(*messages.RemoveDelivery)
return ok return ok
} }
type CheckoutHandler struct {
TypedMessageHandler
}
func (h *CheckoutHandler) Write(m *Message, w io.Writer) error {
messageBytes, err := proto.Marshal(m.Content.(*messages.CreateCheckoutOrder))
if err != nil {
return err
}
w.Write(messageBytes)
return nil
}
func (h *CheckoutHandler) Read(data []byte) (interface{}, error) {
msg := &messages.CreateCheckoutOrder{}
err := proto.Unmarshal(data, msg)
if err != nil {
return nil, err
}
return msg, nil
}
func (h *CheckoutHandler) Is(m *Message) bool {
if m.Type != CreateCheckoutOrderType {
return false
}
_, ok := m.Content.(*messages.CreateCheckoutOrder)
return ok
}

View File

@@ -3,9 +3,11 @@ package main
const ( const (
AddRequestType = 1 AddRequestType = 1
AddItemType = 2 AddItemType = 2
//AddDeliveryType = 3
RemoveItemType = 4 RemoveItemType = 4
RemoveDeliveryType = 5 RemoveDeliveryType = 5
ChangeQuantityType = 6 ChangeQuantityType = 6
SetDeliveryType = 7 SetDeliveryType = 7
SetPickupPointType = 8
CreateCheckoutOrderType = 9
) )

View File

@@ -6,6 +6,7 @@ const (
ResponseBody = FrameType(0x03) ResponseBody = FrameType(0x03)
RemoteGetStateReply = FrameType(0x04) RemoteGetStateReply = FrameType(0x04)
RemoteHandleMutationReply = FrameType(0x05) RemoteHandleMutationReply = FrameType(0x05)
RemoteCreateOrderReply = FrameType(0x06)
) )
// type CartPacket struct { // type CartPacket struct {

View File

@@ -1,9 +1,11 @@
package main package main
import ( import (
"bytes"
"encoding/json" "encoding/json"
"log" "log"
"net/http" "net/http"
"os"
"strconv" "strconv"
messages "git.tornberg.me/go-cart-actor/proto" messages "git.tornberg.me/go-cart-actor/proto"
@@ -59,6 +61,7 @@ func (s *PoolServer) WriteResult(w http.ResponseWriter, result *FrameWithPayload
w.Header().Set("Content-Type", "application/json") w.Header().Set("Content-Type", "application/json")
w.Header().Set("Access-Control-Allow-Origin", "*") w.Header().Set("Access-Control-Allow-Origin", "*")
w.Header().Set("X-Pod-Name", s.pod_name) w.Header().Set("X-Pod-Name", s.pod_name)
w.Header().Set("Access-Control-Allow-Origin", "https://tornberg.me")
if result.StatusCode != 200 { if result.StatusCode != 200 {
log.Printf("Call error: %d\n", result.StatusCode) log.Printf("Call error: %d\n", result.StatusCode)
if result.StatusCode >= 200 && result.StatusCode < 600 { if result.StatusCode >= 200 && result.StatusCode < 600 {
@@ -116,6 +119,137 @@ func (s *PoolServer) HandleSetDelivery(w http.ResponseWriter, r *http.Request) e
return s.WriteResult(w, data) return s.WriteResult(w, data)
} }
func (s *PoolServer) HandleSetPickupPoint(w http.ResponseWriter, r *http.Request) error {
id := r.PathValue("id")
deliveryIdString := r.PathValue("deliveryId")
deliveryId, err := strconv.Atoi(deliveryIdString)
if err != nil {
return err
}
pickupPoint := messages.PickupPoint{}
err = json.NewDecoder(r.Body).Decode(&pickupPoint)
if err != nil {
return err
}
reply, err := s.pool.Process(ToCartId(id), Message{
Type: SetPickupPointType,
Content: &messages.SetPickupPoint{
DeliveryId: int64(deliveryId),
Id: pickupPoint.Id,
Name: pickupPoint.Name,
Address: pickupPoint.Address,
City: pickupPoint.City,
Zip: pickupPoint.Zip,
Country: pickupPoint.Country,
},
})
if err != nil {
return err
}
return s.WriteResult(w, reply)
}
func (s *PoolServer) HandleRemoveDelivery(w http.ResponseWriter, r *http.Request) error {
id := r.PathValue("id")
deliveryIdString := r.PathValue("deliveryId")
deliveryId, err := strconv.Atoi(deliveryIdString)
if err != nil {
return err
}
reply, err := s.pool.Process(ToCartId(id), Message{
Type: RemoveDeliveryType,
Content: &messages.RemoveDelivery{Id: int64(deliveryId)},
})
if err != nil {
return err
}
return s.WriteResult(w, reply)
}
func (s *PoolServer) HandleQuantityChange(w http.ResponseWriter, r *http.Request) error {
id := r.PathValue("id")
changeQuantity := messages.ChangeQuantity{}
err := json.NewDecoder(r.Body).Decode(&changeQuantity)
if err != nil {
return err
}
reply, err := s.pool.Process(ToCartId(id), Message{
Type: ChangeQuantityType,
Content: &changeQuantity,
})
if err != nil {
return err
}
return s.WriteResult(w, reply)
}
func (s *PoolServer) HandleAddRequest(w http.ResponseWriter, r *http.Request) error {
id := r.PathValue("id")
addRequest := messages.AddRequest{}
err := json.NewDecoder(r.Body).Decode(&addRequest)
if err != nil {
return err
}
reply, err := s.pool.Process(ToCartId(id), Message{
Type: AddRequestType,
Content: &addRequest,
})
if err != nil {
return err
}
return s.WriteResult(w, reply)
}
var (
APIUsername = os.Getenv("KLARNA_API_USERNAME")
APIPassword = os.Getenv("KLARNA_API_PASSWORD")
)
func (s *PoolServer) HandleCheckout(w http.ResponseWriter, r *http.Request) error {
id := r.PathValue("id")
reply, err := s.pool.Process(ToCartId(id), Message{
Type: CreateCheckoutOrderType,
Content: &messages.CreateCheckoutOrder{
Terms: "https://tornberg.me/terms",
Checkout: "https://tornberg.me/checkout",
Confirmation: "https://tornberg.me/confirmation",
Push: "https://cart.tornberg.me/push",
},
})
if err != nil {
return err
}
if reply.StatusCode != 200 {
return s.WriteResult(w, reply)
}
req, err := http.NewRequest("POST", "https://api.playground.klarna.com/checkout/v3/orders", bytes.NewReader(reply.Payload))
if err != nil {
return err
}
req.Header.Add("Content-Type", "application/json")
req.SetBasicAuth(APIUsername, APIPassword)
res, err := http.DefaultClient.Do(req)
if nil != err {
return err
}
buf := new(bytes.Buffer)
buf.ReadFrom(res.Body)
w.Header().Set("Content-Type", "application/json")
w.Header().Set("X-Pod-Name", s.pod_name)
w.Header().Set("Access-Control-Allow-Origin", "https://tornberg.me")
w.WriteHeader(res.StatusCode)
w.Write(buf.Bytes())
return nil
}
func (s *PoolServer) Serve() *http.ServeMux { func (s *PoolServer) Serve() *http.ServeMux {
mux := http.NewServeMux() mux := http.NewServeMux()
mux.HandleFunc("OPTIONS /{id}", func(w http.ResponseWriter, r *http.Request) { mux.HandleFunc("OPTIONS /{id}", func(w http.ResponseWriter, r *http.Request) {
@@ -126,7 +260,13 @@ func (s *PoolServer) Serve() *http.ServeMux {
}) })
mux.HandleFunc("GET /{id}", ErrorHandler(s.HandleGet)) mux.HandleFunc("GET /{id}", ErrorHandler(s.HandleGet))
mux.HandleFunc("GET /{id}/add/{sku}", ErrorHandler(s.HandleAddSku)) mux.HandleFunc("GET /{id}/add/{sku}", ErrorHandler(s.HandleAddSku))
mux.HandleFunc("POST /{id}", ErrorHandler(s.HandleAddRequest))
mux.HandleFunc("DELETE /{id}/{itemId}", ErrorHandler(s.HandleDeleteItem)) mux.HandleFunc("DELETE /{id}/{itemId}", ErrorHandler(s.HandleDeleteItem))
mux.HandleFunc("PUT /{id}", ErrorHandler(s.HandleQuantityChange))
mux.HandleFunc("POST /{id}/delivery", ErrorHandler(s.HandleSetDelivery)) mux.HandleFunc("POST /{id}/delivery", ErrorHandler(s.HandleSetDelivery))
mux.HandleFunc("DELETE /{id}/delivery/{deliveryId}", ErrorHandler(s.HandleRemoveDelivery))
mux.HandleFunc("PUT /{id}/delivery/{deliveryId}/pickupPoint", ErrorHandler(s.HandleSetPickupPoint))
mux.HandleFunc("GET /{id}/checkout", ErrorHandler(s.HandleCheckout))
return mux return mux
} }

View File

@@ -25,8 +25,8 @@ type AddRequest struct {
sizeCache protoimpl.SizeCache sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields unknownFields protoimpl.UnknownFields
Quantity int32 `protobuf:"varint,1,opt,name=Quantity,proto3" json:"Quantity,omitempty"` Quantity int32 `protobuf:"varint,1,opt,name=Quantity,proto3" json:"quantity,omitempty"`
Sku string `protobuf:"bytes,2,opt,name=Sku,proto3" json:"Sku,omitempty"` Sku string `protobuf:"bytes,2,opt,name=Sku,proto3" json:"sku,omitempty"`
} }
func (x *AddRequest) Reset() { func (x *AddRequest) Reset() {
@@ -78,11 +78,17 @@ type AddItem struct {
sizeCache protoimpl.SizeCache sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields unknownFields protoimpl.UnknownFields
Quantity int32 `protobuf:"varint,2,opt,name=Quantity,proto3" json:"Quantity,omitempty"` Quantity int32 `protobuf:"varint,2,opt,name=Quantity,proto3" json:"quantity,omitempty"`
Price int64 `protobuf:"varint,3,opt,name=Price,proto3" json:"Price,omitempty"` Price int64 `protobuf:"varint,3,opt,name=Price,proto3" json:"price,omitempty"`
Sku string `protobuf:"bytes,4,opt,name=Sku,proto3" json:"Sku,omitempty"` OrgPrice int64 `protobuf:"varint,9,opt,name=OrgPrice,proto3" json:"orgPrice,omitempty"`
Name string `protobuf:"bytes,5,opt,name=Name,proto3" json:"Name,omitempty"` Sku string `protobuf:"bytes,4,opt,name=Sku,proto3" json:"sku,omitempty"`
Image string `protobuf:"bytes,6,opt,name=Image,proto3" json:"Image,omitempty"` Name string `protobuf:"bytes,5,opt,name=Name,proto3" json:"name,omitempty"`
Image string `protobuf:"bytes,6,opt,name=Image,proto3" json:"image,omitempty"`
Stock int32 `protobuf:"varint,7,opt,name=Stock,proto3" json:"stock,omitempty"`
Tax int32 `protobuf:"varint,8,opt,name=Tax,proto3" json:"tax,omitempty"`
Disclaimer string `protobuf:"bytes,10,opt,name=Disclaimer,proto3" json:"disclaimer,omitempty"`
ArticleType string `protobuf:"bytes,11,opt,name=ArticleType,proto3" json:"articleType,omitempty"`
Outlet *string `protobuf:"bytes,12,opt,name=Outlet,proto3,oneof" json:"outlet,omitempty"`
} }
func (x *AddItem) Reset() { func (x *AddItem) Reset() {
@@ -129,6 +135,13 @@ func (x *AddItem) GetPrice() int64 {
return 0 return 0
} }
func (x *AddItem) GetOrgPrice() int64 {
if x != nil {
return x.OrgPrice
}
return 0
}
func (x *AddItem) GetSku() string { func (x *AddItem) GetSku() string {
if x != nil { if x != nil {
return x.Sku return x.Sku
@@ -150,12 +163,47 @@ func (x *AddItem) GetImage() string {
return "" return ""
} }
func (x *AddItem) GetStock() int32 {
if x != nil {
return x.Stock
}
return 0
}
func (x *AddItem) GetTax() int32 {
if x != nil {
return x.Tax
}
return 0
}
func (x *AddItem) GetDisclaimer() string {
if x != nil {
return x.Disclaimer
}
return ""
}
func (x *AddItem) GetArticleType() string {
if x != nil {
return x.ArticleType
}
return ""
}
func (x *AddItem) GetOutlet() string {
if x != nil && x.Outlet != nil {
return *x.Outlet
}
return ""
}
type RemoveItem struct { type RemoveItem struct {
state protoimpl.MessageState state protoimpl.MessageState
sizeCache protoimpl.SizeCache sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields unknownFields protoimpl.UnknownFields
Id int64 `protobuf:"varint,1,opt,name=Id,proto3" json:"Id,omitempty"` Id int64 `protobuf:"varint,1,opt,name=Id,proto3" json:"id,omitempty"`
} }
func (x *RemoveItem) Reset() { func (x *RemoveItem) Reset() {
@@ -200,8 +248,8 @@ type ChangeQuantity struct {
sizeCache protoimpl.SizeCache sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields unknownFields protoimpl.UnknownFields
Id int64 `protobuf:"varint,1,opt,name=Id,proto3" json:"Id,omitempty"` Id int64 `protobuf:"varint,1,opt,name=Id,proto3" json:"id,omitempty"`
Quantity int32 `protobuf:"varint,2,opt,name=Quantity,proto3" json:"Quantity,omitempty"` Quantity int32 `protobuf:"varint,2,opt,name=Quantity,proto3" json:"quantity,omitempty"`
} }
func (x *ChangeQuantity) Reset() { func (x *ChangeQuantity) Reset() {
@@ -253,8 +301,13 @@ type SetDelivery struct {
sizeCache protoimpl.SizeCache sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields unknownFields protoimpl.UnknownFields
Provider string `protobuf:"bytes,1,opt,name=Provider,proto3" json:"Provider,omitempty"` Provider string `protobuf:"bytes,1,opt,name=Provider,proto3" json:"provider,omitempty"`
Items []int64 `protobuf:"varint,2,rep,packed,name=Items,proto3" json:"Items,omitempty"` Items []int64 `protobuf:"varint,2,rep,packed,name=Items,proto3" json:"items,omitempty"`
PickupPoint *PickupPoint `protobuf:"bytes,3,opt,name=PickupPoint,proto3,oneof" json:"pickupPoint,omitempty"`
Country string `protobuf:"bytes,4,opt,name=Country,proto3" json:"country,omitempty"`
Zip string `protobuf:"bytes,5,opt,name=Zip,proto3" json:"zip,omitempty"`
Address *string `protobuf:"bytes,6,opt,name=Address,proto3,oneof" json:"address,omitempty"`
City *string `protobuf:"bytes,7,opt,name=City,proto3,oneof" json:"city,omitempty"`
} }
func (x *SetDelivery) Reset() { func (x *SetDelivery) Reset() {
@@ -301,17 +354,230 @@ func (x *SetDelivery) GetItems() []int64 {
return nil return nil
} }
func (x *SetDelivery) GetPickupPoint() *PickupPoint {
if x != nil {
return x.PickupPoint
}
return nil
}
func (x *SetDelivery) GetCountry() string {
if x != nil {
return x.Country
}
return ""
}
func (x *SetDelivery) GetZip() string {
if x != nil {
return x.Zip
}
return ""
}
func (x *SetDelivery) GetAddress() string {
if x != nil && x.Address != nil {
return *x.Address
}
return ""
}
func (x *SetDelivery) GetCity() string {
if x != nil && x.City != nil {
return *x.City
}
return ""
}
type SetPickupPoint struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
DeliveryId int64 `protobuf:"varint,1,opt,name=DeliveryId,proto3" json:"deliveryId,omitempty"`
Id string `protobuf:"bytes,2,opt,name=Id,proto3" json:"id,omitempty"`
Name *string `protobuf:"bytes,3,opt,name=Name,proto3,oneof" json:"name,omitempty"`
Address *string `protobuf:"bytes,4,opt,name=Address,proto3,oneof" json:"address,omitempty"`
City *string `protobuf:"bytes,5,opt,name=City,proto3,oneof" json:"city,omitempty"`
Zip *string `protobuf:"bytes,6,opt,name=Zip,proto3,oneof" json:"zip,omitempty"`
Country *string `protobuf:"bytes,7,opt,name=Country,proto3,oneof" json:"country,omitempty"`
}
func (x *SetPickupPoint) Reset() {
*x = SetPickupPoint{}
mi := &file_messages_proto_msgTypes[5]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *SetPickupPoint) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*SetPickupPoint) ProtoMessage() {}
func (x *SetPickupPoint) ProtoReflect() protoreflect.Message {
mi := &file_messages_proto_msgTypes[5]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use SetPickupPoint.ProtoReflect.Descriptor instead.
func (*SetPickupPoint) Descriptor() ([]byte, []int) {
return file_messages_proto_rawDescGZIP(), []int{5}
}
func (x *SetPickupPoint) GetDeliveryId() int64 {
if x != nil {
return x.DeliveryId
}
return 0
}
func (x *SetPickupPoint) GetId() string {
if x != nil {
return x.Id
}
return ""
}
func (x *SetPickupPoint) GetName() string {
if x != nil && x.Name != nil {
return *x.Name
}
return ""
}
func (x *SetPickupPoint) GetAddress() string {
if x != nil && x.Address != nil {
return *x.Address
}
return ""
}
func (x *SetPickupPoint) GetCity() string {
if x != nil && x.City != nil {
return *x.City
}
return ""
}
func (x *SetPickupPoint) GetZip() string {
if x != nil && x.Zip != nil {
return *x.Zip
}
return ""
}
func (x *SetPickupPoint) GetCountry() string {
if x != nil && x.Country != nil {
return *x.Country
}
return ""
}
type PickupPoint struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Id string `protobuf:"bytes,1,opt,name=Id,proto3" json:"id,omitempty"`
Name *string `protobuf:"bytes,2,opt,name=Name,proto3,oneof" json:"name,omitempty"`
Address *string `protobuf:"bytes,3,opt,name=Address,proto3,oneof" json:"address,omitempty"`
City *string `protobuf:"bytes,4,opt,name=City,proto3,oneof" json:"city,omitempty"`
Zip *string `protobuf:"bytes,5,opt,name=Zip,proto3,oneof" json:"zip,omitempty"`
Country *string `protobuf:"bytes,6,opt,name=Country,proto3,oneof" json:"country,omitempty"`
}
func (x *PickupPoint) Reset() {
*x = PickupPoint{}
mi := &file_messages_proto_msgTypes[6]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *PickupPoint) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*PickupPoint) ProtoMessage() {}
func (x *PickupPoint) ProtoReflect() protoreflect.Message {
mi := &file_messages_proto_msgTypes[6]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use PickupPoint.ProtoReflect.Descriptor instead.
func (*PickupPoint) Descriptor() ([]byte, []int) {
return file_messages_proto_rawDescGZIP(), []int{6}
}
func (x *PickupPoint) GetId() string {
if x != nil {
return x.Id
}
return ""
}
func (x *PickupPoint) GetName() string {
if x != nil && x.Name != nil {
return *x.Name
}
return ""
}
func (x *PickupPoint) GetAddress() string {
if x != nil && x.Address != nil {
return *x.Address
}
return ""
}
func (x *PickupPoint) GetCity() string {
if x != nil && x.City != nil {
return *x.City
}
return ""
}
func (x *PickupPoint) GetZip() string {
if x != nil && x.Zip != nil {
return *x.Zip
}
return ""
}
func (x *PickupPoint) GetCountry() string {
if x != nil && x.Country != nil {
return *x.Country
}
return ""
}
type RemoveDelivery struct { type RemoveDelivery struct {
state protoimpl.MessageState state protoimpl.MessageState
sizeCache protoimpl.SizeCache sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields unknownFields protoimpl.UnknownFields
Id int64 `protobuf:"varint,1,opt,name=Id,proto3" json:"Id,omitempty"` Id int64 `protobuf:"varint,1,opt,name=Id,proto3" json:"id,omitempty"`
} }
func (x *RemoveDelivery) Reset() { func (x *RemoveDelivery) Reset() {
*x = RemoveDelivery{} *x = RemoveDelivery{}
mi := &file_messages_proto_msgTypes[5] mi := &file_messages_proto_msgTypes[7]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi) ms.StoreMessageInfo(mi)
} }
@@ -323,7 +589,7 @@ func (x *RemoveDelivery) String() string {
func (*RemoveDelivery) ProtoMessage() {} func (*RemoveDelivery) ProtoMessage() {}
func (x *RemoveDelivery) ProtoReflect() protoreflect.Message { func (x *RemoveDelivery) ProtoReflect() protoreflect.Message {
mi := &file_messages_proto_msgTypes[5] mi := &file_messages_proto_msgTypes[7]
if x != nil { if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil { if ms.LoadMessageInfo() == nil {
@@ -336,7 +602,7 @@ func (x *RemoveDelivery) ProtoReflect() protoreflect.Message {
// Deprecated: Use RemoveDelivery.ProtoReflect.Descriptor instead. // Deprecated: Use RemoveDelivery.ProtoReflect.Descriptor instead.
func (*RemoveDelivery) Descriptor() ([]byte, []int) { func (*RemoveDelivery) Descriptor() ([]byte, []int) {
return file_messages_proto_rawDescGZIP(), []int{5} return file_messages_proto_rawDescGZIP(), []int{7}
} }
func (x *RemoveDelivery) GetId() int64 { func (x *RemoveDelivery) GetId() int64 {
@@ -346,6 +612,75 @@ func (x *RemoveDelivery) GetId() int64 {
return 0 return 0
} }
type CreateCheckoutOrder struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Terms string `protobuf:"bytes,1,opt,name=Terms,proto3" json:"terms,omitempty"`
Checkout string `protobuf:"bytes,2,opt,name=Checkout,proto3" json:"checkout,omitempty"`
Confirmation string `protobuf:"bytes,3,opt,name=Confirmation,proto3" json:"confirmation,omitempty"`
Push string `protobuf:"bytes,4,opt,name=Push,proto3" json:"push,omitempty"`
}
func (x *CreateCheckoutOrder) Reset() {
*x = CreateCheckoutOrder{}
mi := &file_messages_proto_msgTypes[8]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *CreateCheckoutOrder) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*CreateCheckoutOrder) ProtoMessage() {}
func (x *CreateCheckoutOrder) ProtoReflect() protoreflect.Message {
mi := &file_messages_proto_msgTypes[8]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use CreateCheckoutOrder.ProtoReflect.Descriptor instead.
func (*CreateCheckoutOrder) Descriptor() ([]byte, []int) {
return file_messages_proto_rawDescGZIP(), []int{8}
}
func (x *CreateCheckoutOrder) GetTerms() string {
if x != nil {
return x.Terms
}
return ""
}
func (x *CreateCheckoutOrder) GetCheckout() string {
if x != nil {
return x.Checkout
}
return ""
}
func (x *CreateCheckoutOrder) GetConfirmation() string {
if x != nil {
return x.Confirmation
}
return ""
}
func (x *CreateCheckoutOrder) GetPush() string {
if x != nil {
return x.Push
}
return ""
}
var File_messages_proto protoreflect.FileDescriptor var File_messages_proto protoreflect.FileDescriptor
var file_messages_proto_rawDesc = []byte{ var file_messages_proto_rawDesc = []byte{
@@ -354,28 +689,89 @@ var file_messages_proto_rawDesc = []byte{
0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x51, 0x75, 0x61, 0x6e, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x51, 0x75, 0x61, 0x6e,
0x74, 0x69, 0x74, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x08, 0x51, 0x75, 0x61, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x08, 0x51, 0x75, 0x61, 0x6e,
0x74, 0x69, 0x74, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x53, 0x6b, 0x75, 0x18, 0x02, 0x20, 0x01, 0x28, 0x74, 0x69, 0x74, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x53, 0x6b, 0x75, 0x18, 0x02, 0x20, 0x01, 0x28,
0x09, 0x52, 0x03, 0x53, 0x6b, 0x75, 0x22, 0x77, 0x0a, 0x07, 0x41, 0x64, 0x64, 0x49, 0x74, 0x65, 0x09, 0x52, 0x03, 0x53, 0x6b, 0x75, 0x22, 0xa5, 0x02, 0x0a, 0x07, 0x41, 0x64, 0x64, 0x49, 0x74,
0x6d, 0x12, 0x1a, 0x0a, 0x08, 0x51, 0x75, 0x61, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x18, 0x02, 0x20, 0x65, 0x6d, 0x12, 0x1a, 0x0a, 0x08, 0x51, 0x75, 0x61, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x18, 0x02,
0x01, 0x28, 0x05, 0x52, 0x08, 0x51, 0x75, 0x61, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x12, 0x14, 0x0a, 0x20, 0x01, 0x28, 0x05, 0x52, 0x08, 0x51, 0x75, 0x61, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x12, 0x14,
0x05, 0x50, 0x72, 0x69, 0x63, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x05, 0x50, 0x72, 0x0a, 0x05, 0x50, 0x72, 0x69, 0x63, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x05, 0x50,
0x69, 0x63, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x53, 0x6b, 0x75, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x72, 0x69, 0x63, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x4f, 0x72, 0x67, 0x50, 0x72, 0x69, 0x63, 0x65,
0x52, 0x03, 0x53, 0x6b, 0x75, 0x12, 0x12, 0x0a, 0x04, 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x05, 0x20, 0x18, 0x09, 0x20, 0x01, 0x28, 0x03, 0x52, 0x08, 0x4f, 0x72, 0x67, 0x50, 0x72, 0x69, 0x63, 0x65,
0x01, 0x28, 0x09, 0x52, 0x04, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x49, 0x6d, 0x61, 0x12, 0x10, 0x0a, 0x03, 0x53, 0x6b, 0x75, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x53,
0x67, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x49, 0x6d, 0x61, 0x67, 0x65, 0x22, 0x6b, 0x75, 0x12, 0x12, 0x0a, 0x04, 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09,
0x1c, 0x0a, 0x0a, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x49, 0x74, 0x65, 0x6d, 0x12, 0x0e, 0x0a, 0x52, 0x04, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x49, 0x6d, 0x61, 0x67, 0x65, 0x18,
0x02, 0x49, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x02, 0x49, 0x64, 0x22, 0x3c, 0x0a, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x49, 0x6d, 0x61, 0x67, 0x65, 0x12, 0x14, 0x0a, 0x05,
0x0e, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x51, 0x75, 0x61, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x12, 0x53, 0x74, 0x6f, 0x63, 0x6b, 0x18, 0x07, 0x20, 0x01, 0x28, 0x05, 0x52, 0x05, 0x53, 0x74, 0x6f,
0x0e, 0x0a, 0x02, 0x49, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x02, 0x49, 0x64, 0x12, 0x63, 0x6b, 0x12, 0x10, 0x0a, 0x03, 0x54, 0x61, 0x78, 0x18, 0x08, 0x20, 0x01, 0x28, 0x05, 0x52,
0x1a, 0x0a, 0x08, 0x51, 0x75, 0x61, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x54, 0x61, 0x78, 0x12, 0x1e, 0x0a, 0x0a, 0x44, 0x69, 0x73, 0x63, 0x6c, 0x61, 0x69, 0x6d,
0x05, 0x52, 0x08, 0x51, 0x75, 0x61, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x22, 0x3f, 0x0a, 0x0b, 0x53, 0x65, 0x72, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x44, 0x69, 0x73, 0x63, 0x6c, 0x61,
0x69, 0x6d, 0x65, 0x72, 0x12, 0x20, 0x0a, 0x0b, 0x41, 0x72, 0x74, 0x69, 0x63, 0x6c, 0x65, 0x54,
0x79, 0x70, 0x65, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x41, 0x72, 0x74, 0x69, 0x63,
0x6c, 0x65, 0x54, 0x79, 0x70, 0x65, 0x12, 0x1b, 0x0a, 0x06, 0x4f, 0x75, 0x74, 0x6c, 0x65, 0x74,
0x18, 0x0c, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x06, 0x4f, 0x75, 0x74, 0x6c, 0x65, 0x74,
0x88, 0x01, 0x01, 0x42, 0x09, 0x0a, 0x07, 0x5f, 0x4f, 0x75, 0x74, 0x6c, 0x65, 0x74, 0x22, 0x1c,
0x0a, 0x0a, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x49, 0x74, 0x65, 0x6d, 0x12, 0x0e, 0x0a, 0x02,
0x49, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x02, 0x49, 0x64, 0x22, 0x3c, 0x0a, 0x0e,
0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x51, 0x75, 0x61, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x12, 0x0e,
0x0a, 0x02, 0x49, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x02, 0x49, 0x64, 0x12, 0x1a,
0x0a, 0x08, 0x51, 0x75, 0x61, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05,
0x52, 0x08, 0x51, 0x75, 0x61, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x22, 0x86, 0x02, 0x0a, 0x0b, 0x53,
0x65, 0x74, 0x44, 0x65, 0x6c, 0x69, 0x76, 0x65, 0x72, 0x79, 0x12, 0x1a, 0x0a, 0x08, 0x50, 0x72, 0x65, 0x74, 0x44, 0x65, 0x6c, 0x69, 0x76, 0x65, 0x72, 0x79, 0x12, 0x1a, 0x0a, 0x08, 0x50, 0x72,
0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x50, 0x72,
0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x12, 0x14, 0x0a, 0x05, 0x49, 0x74, 0x65, 0x6d, 0x73, 0x18, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x12, 0x14, 0x0a, 0x05, 0x49, 0x74, 0x65, 0x6d, 0x73, 0x18,
0x02, 0x20, 0x03, 0x28, 0x03, 0x52, 0x05, 0x49, 0x74, 0x65, 0x6d, 0x73, 0x22, 0x20, 0x0a, 0x0e, 0x02, 0x20, 0x03, 0x28, 0x03, 0x52, 0x05, 0x49, 0x74, 0x65, 0x6d, 0x73, 0x12, 0x3c, 0x0a, 0x0b,
0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x44, 0x65, 0x6c, 0x69, 0x76, 0x65, 0x72, 0x79, 0x12, 0x0e, 0x50, 0x69, 0x63, 0x6b, 0x75, 0x70, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28,
0x0a, 0x02, 0x49, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x02, 0x49, 0x64, 0x42, 0x0c, 0x0b, 0x32, 0x15, 0x2e, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x73, 0x2e, 0x50, 0x69, 0x63,
0x5a, 0x0a, 0x2e, 0x3b, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x73, 0x62, 0x06, 0x70, 0x72, 0x6b, 0x75, 0x70, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x48, 0x00, 0x52, 0x0b, 0x50, 0x69, 0x63, 0x6b,
0x6f, 0x74, 0x6f, 0x33, 0x75, 0x70, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x88, 0x01, 0x01, 0x12, 0x18, 0x0a, 0x07, 0x43, 0x6f,
0x75, 0x6e, 0x74, 0x72, 0x79, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x43, 0x6f, 0x75,
0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x5a, 0x69, 0x70, 0x18, 0x05, 0x20, 0x01, 0x28,
0x09, 0x52, 0x03, 0x5a, 0x69, 0x70, 0x12, 0x1d, 0x0a, 0x07, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73,
0x73, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x48, 0x01, 0x52, 0x07, 0x41, 0x64, 0x64, 0x72, 0x65,
0x73, 0x73, 0x88, 0x01, 0x01, 0x12, 0x17, 0x0a, 0x04, 0x43, 0x69, 0x74, 0x79, 0x18, 0x07, 0x20,
0x01, 0x28, 0x09, 0x48, 0x02, 0x52, 0x04, 0x43, 0x69, 0x74, 0x79, 0x88, 0x01, 0x01, 0x42, 0x0e,
0x0a, 0x0c, 0x5f, 0x50, 0x69, 0x63, 0x6b, 0x75, 0x70, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x42, 0x0a,
0x0a, 0x08, 0x5f, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x42, 0x07, 0x0a, 0x05, 0x5f, 0x43,
0x69, 0x74, 0x79, 0x22, 0xf9, 0x01, 0x0a, 0x0e, 0x53, 0x65, 0x74, 0x50, 0x69, 0x63, 0x6b, 0x75,
0x70, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x12, 0x1e, 0x0a, 0x0a, 0x44, 0x65, 0x6c, 0x69, 0x76, 0x65,
0x72, 0x79, 0x49, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0a, 0x44, 0x65, 0x6c, 0x69,
0x76, 0x65, 0x72, 0x79, 0x49, 0x64, 0x12, 0x0e, 0x0a, 0x02, 0x49, 0x64, 0x18, 0x02, 0x20, 0x01,
0x28, 0x09, 0x52, 0x02, 0x49, 0x64, 0x12, 0x17, 0x0a, 0x04, 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x03,
0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x04, 0x4e, 0x61, 0x6d, 0x65, 0x88, 0x01, 0x01, 0x12,
0x1d, 0x0a, 0x07, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09,
0x48, 0x01, 0x52, 0x07, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x88, 0x01, 0x01, 0x12, 0x17,
0x0a, 0x04, 0x43, 0x69, 0x74, 0x79, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x48, 0x02, 0x52, 0x04,
0x43, 0x69, 0x74, 0x79, 0x88, 0x01, 0x01, 0x12, 0x15, 0x0a, 0x03, 0x5a, 0x69, 0x70, 0x18, 0x06,
0x20, 0x01, 0x28, 0x09, 0x48, 0x03, 0x52, 0x03, 0x5a, 0x69, 0x70, 0x88, 0x01, 0x01, 0x12, 0x1d,
0x0a, 0x07, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x72, 0x79, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x48,
0x04, 0x52, 0x07, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x72, 0x79, 0x88, 0x01, 0x01, 0x42, 0x07, 0x0a,
0x05, 0x5f, 0x4e, 0x61, 0x6d, 0x65, 0x42, 0x0a, 0x0a, 0x08, 0x5f, 0x41, 0x64, 0x64, 0x72, 0x65,
0x73, 0x73, 0x42, 0x07, 0x0a, 0x05, 0x5f, 0x43, 0x69, 0x74, 0x79, 0x42, 0x06, 0x0a, 0x04, 0x5f,
0x5a, 0x69, 0x70, 0x42, 0x0a, 0x0a, 0x08, 0x5f, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x72, 0x79, 0x22,
0xd6, 0x01, 0x0a, 0x0b, 0x50, 0x69, 0x63, 0x6b, 0x75, 0x70, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x12,
0x0e, 0x0a, 0x02, 0x49, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x49, 0x64, 0x12,
0x17, 0x0a, 0x04, 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52,
0x04, 0x4e, 0x61, 0x6d, 0x65, 0x88, 0x01, 0x01, 0x12, 0x1d, 0x0a, 0x07, 0x41, 0x64, 0x64, 0x72,
0x65, 0x73, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x48, 0x01, 0x52, 0x07, 0x41, 0x64, 0x64,
0x72, 0x65, 0x73, 0x73, 0x88, 0x01, 0x01, 0x12, 0x17, 0x0a, 0x04, 0x43, 0x69, 0x74, 0x79, 0x18,
0x04, 0x20, 0x01, 0x28, 0x09, 0x48, 0x02, 0x52, 0x04, 0x43, 0x69, 0x74, 0x79, 0x88, 0x01, 0x01,
0x12, 0x15, 0x0a, 0x03, 0x5a, 0x69, 0x70, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x48, 0x03, 0x52,
0x03, 0x5a, 0x69, 0x70, 0x88, 0x01, 0x01, 0x12, 0x1d, 0x0a, 0x07, 0x43, 0x6f, 0x75, 0x6e, 0x74,
0x72, 0x79, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x48, 0x04, 0x52, 0x07, 0x43, 0x6f, 0x75, 0x6e,
0x74, 0x72, 0x79, 0x88, 0x01, 0x01, 0x42, 0x07, 0x0a, 0x05, 0x5f, 0x4e, 0x61, 0x6d, 0x65, 0x42,
0x0a, 0x0a, 0x08, 0x5f, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x42, 0x07, 0x0a, 0x05, 0x5f,
0x43, 0x69, 0x74, 0x79, 0x42, 0x06, 0x0a, 0x04, 0x5f, 0x5a, 0x69, 0x70, 0x42, 0x0a, 0x0a, 0x08,
0x5f, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x72, 0x79, 0x22, 0x20, 0x0a, 0x0e, 0x52, 0x65, 0x6d, 0x6f,
0x76, 0x65, 0x44, 0x65, 0x6c, 0x69, 0x76, 0x65, 0x72, 0x79, 0x12, 0x0e, 0x0a, 0x02, 0x49, 0x64,
0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x02, 0x49, 0x64, 0x22, 0x7f, 0x0a, 0x13, 0x43, 0x72,
0x65, 0x61, 0x74, 0x65, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x6f, 0x75, 0x74, 0x4f, 0x72, 0x64, 0x65,
0x72, 0x12, 0x14, 0x0a, 0x05, 0x54, 0x65, 0x72, 0x6d, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09,
0x52, 0x05, 0x54, 0x65, 0x72, 0x6d, 0x73, 0x12, 0x1a, 0x0a, 0x08, 0x43, 0x68, 0x65, 0x63, 0x6b,
0x6f, 0x75, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x43, 0x68, 0x65, 0x63, 0x6b,
0x6f, 0x75, 0x74, 0x12, 0x22, 0x0a, 0x0c, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x72, 0x6d, 0x61, 0x74,
0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x43, 0x6f, 0x6e, 0x66, 0x69,
0x72, 0x6d, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x12, 0x0a, 0x04, 0x50, 0x75, 0x73, 0x68, 0x18,
0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x50, 0x75, 0x73, 0x68, 0x42, 0x0c, 0x5a, 0x0a, 0x2e,
0x3b, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f,
0x33,
} }
var ( var (
@@ -390,21 +786,25 @@ func file_messages_proto_rawDescGZIP() []byte {
return file_messages_proto_rawDescData return file_messages_proto_rawDescData
} }
var file_messages_proto_msgTypes = make([]protoimpl.MessageInfo, 6) var file_messages_proto_msgTypes = make([]protoimpl.MessageInfo, 9)
var file_messages_proto_goTypes = []any{ var file_messages_proto_goTypes = []any{
(*AddRequest)(nil), // 0: messages.AddRequest (*AddRequest)(nil), // 0: messages.AddRequest
(*AddItem)(nil), // 1: messages.AddItem (*AddItem)(nil), // 1: messages.AddItem
(*RemoveItem)(nil), // 2: messages.RemoveItem (*RemoveItem)(nil), // 2: messages.RemoveItem
(*ChangeQuantity)(nil), // 3: messages.ChangeQuantity (*ChangeQuantity)(nil), // 3: messages.ChangeQuantity
(*SetDelivery)(nil), // 4: messages.SetDelivery (*SetDelivery)(nil), // 4: messages.SetDelivery
(*RemoveDelivery)(nil), // 5: messages.RemoveDelivery (*SetPickupPoint)(nil), // 5: messages.SetPickupPoint
(*PickupPoint)(nil), // 6: messages.PickupPoint
(*RemoveDelivery)(nil), // 7: messages.RemoveDelivery
(*CreateCheckoutOrder)(nil), // 8: messages.CreateCheckoutOrder
} }
var file_messages_proto_depIdxs = []int32{ var file_messages_proto_depIdxs = []int32{
0, // [0:0] is the sub-list for method output_type 6, // 0: messages.SetDelivery.PickupPoint:type_name -> messages.PickupPoint
0, // [0:0] is the sub-list for method input_type 1, // [1:1] is the sub-list for method output_type
0, // [0:0] is the sub-list for extension type_name 1, // [1:1] is the sub-list for method input_type
0, // [0:0] is the sub-list for extension extendee 1, // [1:1] is the sub-list for extension type_name
0, // [0:0] is the sub-list for field type_name 1, // [1:1] is the sub-list for extension extendee
0, // [0:1] is the sub-list for field type_name
} }
func init() { file_messages_proto_init() } func init() { file_messages_proto_init() }
@@ -412,13 +812,17 @@ func file_messages_proto_init() {
if File_messages_proto != nil { if File_messages_proto != nil {
return return
} }
file_messages_proto_msgTypes[1].OneofWrappers = []any{}
file_messages_proto_msgTypes[4].OneofWrappers = []any{}
file_messages_proto_msgTypes[5].OneofWrappers = []any{}
file_messages_proto_msgTypes[6].OneofWrappers = []any{}
type x struct{} type x struct{}
out := protoimpl.TypeBuilder{ out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{ File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(), GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: file_messages_proto_rawDesc, RawDescriptor: file_messages_proto_rawDesc,
NumEnums: 0, NumEnums: 0,
NumMessages: 6, NumMessages: 9,
NumExtensions: 0, NumExtensions: 0,
NumServices: 0, NumServices: 0,
}, },

View File

@@ -10,9 +10,15 @@ message AddRequest {
message AddItem { message AddItem {
int32 Quantity = 2; int32 Quantity = 2;
int64 Price = 3; int64 Price = 3;
int64 OrgPrice = 9;
string Sku = 4; string Sku = 4;
string Name = 5; string Name = 5;
string Image = 6; string Image = 6;
int32 Stock = 7;
int32 Tax = 8;
string Disclaimer = 10;
string ArticleType = 11;
optional string Outlet = 12;
} }
message RemoveItem { message RemoveItem {
@@ -27,10 +33,39 @@ message ChangeQuantity {
message SetDelivery { message SetDelivery {
string Provider = 1; string Provider = 1;
repeated int64 Items = 2; repeated int64 Items = 2;
optional PickupPoint PickupPoint = 3;
string Country = 4;
string Zip = 5;
optional string Address = 6;
optional string City = 7;
}
message SetPickupPoint {
int64 DeliveryId = 1;
string Id = 2;
optional string Name = 3;
optional string Address = 4;
optional string City = 5;
optional string Zip = 6;
optional string Country = 7;
}
message PickupPoint {
string Id = 1;
optional string Name = 2;
optional string Address = 3;
optional string City = 4;
optional string Zip = 5;
optional string Country = 6;
} }
message RemoveDelivery { message RemoveDelivery {
int64 Id = 1; int64 Id = 1;
} }
message CreateCheckoutOrder {
string Terms = 1;
string Checkout = 2;
string Confirmation = 3;
string Push = 4;
}

View File

@@ -24,6 +24,7 @@ type HealthHandler interface {
type SyncedPool struct { type SyncedPool struct {
Server *GenericListener Server *GenericListener
mu sync.RWMutex mu sync.RWMutex
discardedHostHandler *DiscardedHostHandler
Hostname string Hostname string
local *GrainLocalPool local *GrainLocalPool
remotes map[string]*RemoteHost remotes map[string]*RemoteHost
@@ -93,7 +94,15 @@ func (p *SyncedPool) NegotiateHandler(data *FrameWithPayload, resultChan chan<-
go p.AddRemote(host) go p.AddRemote(host)
} }
resultChan <- MakeFrameWithPayload(RemoteNegotiateResponse, 200, []byte("ok")) p.mu.RLock()
defer p.mu.RUnlock()
hosts := make([]string, 0, len(p.remotes))
for _, r := range p.remotes {
if r.IsHealthy() {
hosts = append(hosts, r.Host)
}
}
resultChan <- MakeFrameWithPayload(RemoteNegotiateResponse, 200, []byte(strings.Join(hosts, ";")))
return nil return nil
} }
@@ -102,13 +111,13 @@ func (p *SyncedPool) GrainOwnerChangeHandler(data *FrameWithPayload, resultChan
idAndHostParts := strings.Split(string(data.Payload), ";") idAndHostParts := strings.Split(string(data.Payload), ";")
if len(idAndHostParts) != 2 { if len(idAndHostParts) != 2 {
log.Printf("Invalid remote grain change message\n") log.Printf("Invalid remote grain change message")
resultChan <- MakeFrameWithPayload(AckError, 500, []byte("invalid")) resultChan <- MakeFrameWithPayload(AckError, 500, []byte("invalid"))
return nil return nil
} }
id := ToCartId(idAndHostParts[0]) id := ToCartId(idAndHostParts[0])
host := idAndHostParts[1] host := idAndHostParts[1]
log.Printf("Handling remote grain owner change to %s for id %s\n", host, id) log.Printf("Handling remote grain owner change to %s for id %s", host, id)
for _, r := range p.remotes { for _, r := range p.remotes {
if r.Host == host && r.IsHealthy() { if r.Host == host && r.IsHealthy() {
go p.SpawnRemoteGrain(id, host) go p.SpawnRemoteGrain(id, host)
@@ -128,11 +137,11 @@ func (p *SyncedPool) RemoveRemoteGrain(id CartId) {
func (p *SyncedPool) SpawnRemoteGrain(id CartId, host string) { func (p *SyncedPool) SpawnRemoteGrain(id CartId, host string) {
if id.String() == "" { if id.String() == "" {
log.Printf("Invalid grain id, %s\n", id) log.Printf("Invalid grain id, %s", id)
return return
} }
if p.local.grains[id] != nil { if p.local.grains[id] != nil {
log.Printf("Grain %s already exists locally, owner is (%s)\n", id, host) log.Printf("Grain %s already exists locally, owner is (%s)", id, host)
p.mu.Lock() p.mu.Lock()
delete(p.local.grains, id) delete(p.local.grains, id)
p.mu.Unlock() p.mu.Unlock()
@@ -140,15 +149,9 @@ func (p *SyncedPool) SpawnRemoteGrain(id CartId, host string) {
remote, err := NewRemoteGrain(id, host) remote, err := NewRemoteGrain(id, host)
if err != nil { if err != nil {
log.Printf("Error creating remote grain %v\n", err) log.Printf("Error creating remote grain %v", err)
return return
} }
// go func() {
// <-remote.Died
// p.RemoveRemoteGrain(id)
// p.HandleHostError(host)
// log.Printf("Remote grain %s died, host: %s\n", id.String(), host)
// }()
p.mu.Lock() p.mu.Lock()
p.remoteIndex[id] = remote p.remoteIndex[id] = remote
@@ -175,16 +178,16 @@ func NewSyncedPool(local *GrainLocalPool, hostname string, discovery Discovery)
} }
log.Printf("Listening on %s", listen) log.Printf("Listening on %s", listen)
dh := NewDiscardedHostHandler(1338)
pool := &SyncedPool{ pool := &SyncedPool{
Server: server, Server: server,
Hostname: hostname, Hostname: hostname,
local: local, local: local,
discardedHostHandler: dh,
remotes: make(map[string]*RemoteHost), remotes: make(map[string]*RemoteHost),
remoteIndex: make(map[CartId]*RemoteGrain), remoteIndex: make(map[CartId]*RemoteGrain),
} }
dh.SetReconnectHandler(pool.AddRemote)
server.AddHandler(Ping, pool.PongHandler) server.AddHandler(Ping, pool.PongHandler)
server.AddHandler(GetCartIds, pool.GetCartIdHandler) server.AddHandler(GetCartIds, pool.GetCartIdHandler)
server.AddHandler(RemoteNegotiate, pool.NegotiateHandler) server.AddHandler(RemoteNegotiate, pool.NegotiateHandler)
@@ -258,11 +261,11 @@ func (p *SyncedPool) ExcludeKnown(hosts []string) []string {
} }
func (p *SyncedPool) RemoveHost(host *RemoteHost) { func (p *SyncedPool) RemoveHost(host *RemoteHost) {
p.mu.Lock() p.mu.Lock()
delete(p.remotes, host.Host) delete(p.remotes, host.Host)
p.mu.Unlock() p.mu.Unlock()
p.RemoveHostMappedCarts(host) p.RemoveHostMappedCarts(host)
p.discardedHostHandler.AppendHost(host.Host)
connectedRemotes.Set(float64(len(p.remotes))) connectedRemotes.Set(float64(len(p.remotes)))
} }
@@ -361,32 +364,44 @@ func (p *SyncedPool) removeLocalGrain(id CartId) {
} }
func (p *SyncedPool) AddRemote(host string) { func (p *SyncedPool) AddRemote(host string) {
p.mu.Lock()
defer p.mu.Unlock()
_, hasHost := p.remotes[host] _, hasHost := p.remotes[host]
if host == "" || hasHost { if host == "" || hasHost || host == p.Hostname {
return return
} }
// if host == "" || hasHost || host == p.Hostname {
// return
// }
client := NewConnection(fmt.Sprintf("%s:1338", host)) client := NewConnection(fmt.Sprintf("%s:1338", host))
r, err := client.Call(Ping, nil)
var err error
pings := 3
for pings >= 0 {
_, err = client.Call(Ping, nil)
if err != nil { if err != nil {
log.Printf("Error connecting to remote %s: %s\n", host, err) log.Printf("Ping failed when adding %s, trying %d more times\n", host, pings)
return pings--
time.Sleep(time.Millisecond * 300)
continue
} }
log.Printf("Connected to remote %s: %v\n", host, r) break
}
log.Printf("Connected to remote %s", host)
remote := RemoteHost{ remote := RemoteHost{
Connection: client, Connection: client,
MissedPings: 0, MissedPings: 0,
Host: host, Host: host,
} }
p.mu.Lock()
p.remotes[host] = &remote
p.mu.Unlock()
go func() { p.remotes[host] = &remote
connectedRemotes.Set(float64(len(p.remotes)))
go p.HandlePing(&remote)
go remote.Initialize(p)
}
func (p *SyncedPool) HandlePing(remote *RemoteHost) {
for range time.Tick(time.Second * 3) { for range time.Tick(time.Second * 3) {
err := remote.Ping() err := remote.Ping()
@@ -394,20 +409,13 @@ func (p *SyncedPool) AddRemote(host string) {
for err != nil { for err != nil {
time.Sleep(time.Millisecond * 200) time.Sleep(time.Millisecond * 200)
if !remote.IsHealthy() { if !remote.IsHealthy() {
log.Printf("Removing host, unable to communicate with %s", host) log.Printf("Removing host, unable to communicate with %s", remote.Host)
p.RemoveHost(&remote) p.RemoveHost(remote)
return return
} }
err = remote.Ping() err = remote.Ping()
} }
} }
}()
connectedRemotes.Set(float64(len(p.remotes)))
log.Printf("Added remote %s\n", remote.Host)
go remote.Initialize(p)
return
} }
func (p *SyncedPool) getGrain(id CartId) (Grain, error) { func (p *SyncedPool) getGrain(id CartId) (Grain, error) {

View File

@@ -16,7 +16,7 @@ func TestConnection(t *testing.T) {
Id: id, Id: id,
storageMessages: []Message{}, storageMessages: []Message{},
Items: []*CartItem{}, Items: []*CartItem{},
Deliveries: make([]CartDelivery, 0), Deliveries: make([]*CartDelivery, 0),
TotalPrice: 0, TotalPrice: 0,
}, nil }, nil
}) })