more changes

This commit is contained in:
matst80
2025-10-10 09:34:40 +00:00
parent b97eb8f285
commit e7c67fbb9b
25 changed files with 1888 additions and 3689 deletions

View File

@@ -1,7 +1,6 @@
package main
import (
"bytes"
"context"
"fmt"
"log"
@@ -68,69 +67,209 @@ func (g *RemoteGrainGRPC) GetId() CartId {
return g.Id
}
// HandleMessage serializes the underlying mutation proto (without legacy message header)
// and invokes the CartActor.Mutate RPC. It wraps the reply into a FrameWithPayload
// for compatibility with existing higher-level code paths.
func (g *RemoteGrainGRPC) HandleMessage(message *Message, isReplay bool) (*FrameWithPayload, error) {
if message == nil {
return nil, fmt.Errorf("nil message")
}
// Apply executes a cart mutation remotely using the typed oneof MutationEnvelope
// and returns a *CartGrain reconstructed from the typed MutationReply (state oneof).
func (g *RemoteGrainGRPC) Apply(content interface{}, isReplay bool) (*CartGrain, error) {
if isReplay {
// Remote replay not expected; ignore to keep parity with old implementation.
return nil, fmt.Errorf("replay not supported for remote grains")
}
handler, err := GetMessageHandler(message.Type)
if err != nil {
return nil, err
if content == nil {
return nil, fmt.Errorf("nil mutation content")
}
// Ensure timestamp set (legacy behavior)
if message.TimeStamp == nil {
ts := time.Now().Unix()
message.TimeStamp = &ts
}
// Marshal underlying proto payload only (no StorableMessageHeader)
var buf bytes.Buffer
err = handler.Write(message, &buf)
if err != nil {
return nil, fmt.Errorf("encode mutation payload: %w", err)
}
req := &proto.MutationRequest{
ts := time.Now().Unix()
env := &proto.MutationEnvelope{
CartId: g.Id.String(),
Type: proto.MutationType(message.Type), // numeric mapping preserved
Payload: buf.Bytes(),
ClientTimestamp: *message.TimeStamp,
ClientTimestamp: ts,
}
switch m := content.(type) {
case *proto.AddRequest:
env.Mutation = &proto.MutationEnvelope_AddRequest{AddRequest: m}
case *proto.AddItem:
env.Mutation = &proto.MutationEnvelope_AddItem{AddItem: m}
case *proto.RemoveItem:
env.Mutation = &proto.MutationEnvelope_RemoveItem{RemoveItem: m}
case *proto.RemoveDelivery:
env.Mutation = &proto.MutationEnvelope_RemoveDelivery{RemoveDelivery: m}
case *proto.ChangeQuantity:
env.Mutation = &proto.MutationEnvelope_ChangeQuantity{ChangeQuantity: m}
case *proto.SetDelivery:
env.Mutation = &proto.MutationEnvelope_SetDelivery{SetDelivery: m}
case *proto.SetPickupPoint:
env.Mutation = &proto.MutationEnvelope_SetPickupPoint{SetPickupPoint: m}
case *proto.CreateCheckoutOrder:
env.Mutation = &proto.MutationEnvelope_CreateCheckoutOrder{CreateCheckoutOrder: m}
case *proto.SetCartRequest:
env.Mutation = &proto.MutationEnvelope_SetCartItems{SetCartItems: m}
case *proto.OrderCreated:
env.Mutation = &proto.MutationEnvelope_OrderCompleted{OrderCompleted: m}
default:
return nil, fmt.Errorf("unsupported mutation type %T", content)
}
ctx, cancel := context.WithTimeout(context.Background(), g.mutateTimeout)
defer cancel()
resp, err := g.client.Mutate(ctx, req)
resp, err := g.client.Mutate(ctx, env)
if err != nil {
return nil, err
}
frame := MakeFrameWithPayload(RemoteHandleMutationReply, StatusCode(resp.StatusCode), resp.Payload)
return &frame, nil
// Map typed reply
if resp.StatusCode < 200 || resp.StatusCode >= 300 {
if e := resp.GetError(); e != "" {
return nil, fmt.Errorf("remote mutation failed %d: %s", resp.StatusCode, e)
}
return nil, fmt.Errorf("remote mutation failed %d", resp.StatusCode)
}
state := resp.GetState()
if state == nil {
return nil, fmt.Errorf("mutation reply missing state on success")
}
// Reconstruct a lightweight CartGrain (only fields we expose internally)
grain := &CartGrain{
Id: ToCartId(state.CartId),
TotalPrice: state.TotalPrice,
TotalTax: state.TotalTax,
TotalDiscount: state.TotalDiscount,
PaymentInProgress: state.PaymentInProgress,
OrderReference: state.OrderReference,
PaymentStatus: state.PaymentStatus,
}
// Items
for _, it := range state.Items {
if it == nil {
continue
}
outlet := toPtr(it.Outlet)
storeId := toPtr(it.StoreId)
grain.Items = append(grain.Items, &CartItem{
Id: int(it.Id),
ItemId: int(it.SourceItemId),
Sku: it.Sku,
Name: it.Name,
Price: it.UnitPrice,
Quantity: int(it.Quantity),
TotalPrice: it.TotalPrice,
TotalTax: it.TotalTax,
OrgPrice: it.OrgPrice,
TaxRate: int(it.TaxRate),
Brand: it.Brand,
Category: it.Category,
Category2: it.Category2,
Category3: it.Category3,
Category4: it.Category4,
Category5: it.Category5,
Image: it.Image,
ArticleType: it.ArticleType,
SellerId: it.SellerId,
SellerName: it.SellerName,
Disclaimer: it.Disclaimer,
Outlet: outlet,
StoreId: storeId,
Stock: StockStatus(it.Stock),
})
}
// Deliveries
for _, d := range state.Deliveries {
if d == nil {
continue
}
intIds := make([]int, 0, len(d.ItemIds))
for _, id := range d.ItemIds {
intIds = append(intIds, int(id))
}
grain.Deliveries = append(grain.Deliveries, &CartDelivery{
Id: int(d.Id),
Provider: d.Provider,
Price: d.Price,
Items: intIds,
PickupPoint: d.PickupPoint,
})
}
return grain, nil
}
// GetCurrentState calls CartActor.GetState and returns a FrameWithPayload
// shaped like the legacy RemoteGetStateReply.
func (g *RemoteGrainGRPC) GetCurrentState() (*FrameWithPayload, error) {
// GetCurrentState retrieves the current cart state using the typed StateReply oneof.
func (g *RemoteGrainGRPC) GetCurrentState() (*CartGrain, error) {
ctx, cancel := context.WithTimeout(context.Background(), g.stateTimeout)
defer cancel()
resp, err := g.client.GetState(ctx, &proto.StateRequest{
CartId: g.Id.String(),
})
resp, err := g.client.GetState(ctx, &proto.StateRequest{CartId: g.Id.String()})
if err != nil {
return nil, err
}
frame := MakeFrameWithPayload(RemoteGetStateReply, StatusCode(resp.StatusCode), resp.Payload)
return &frame, nil
if resp.StatusCode < 200 || resp.StatusCode >= 300 {
if e := resp.GetError(); e != "" {
return nil, fmt.Errorf("remote get state failed %d: %s", resp.StatusCode, e)
}
return nil, fmt.Errorf("remote get state failed %d", resp.StatusCode)
}
state := resp.GetState()
if state == nil {
return nil, fmt.Errorf("state reply missing state on success")
}
grain := &CartGrain{
Id: ToCartId(state.CartId),
TotalPrice: state.TotalPrice,
TotalTax: state.TotalTax,
TotalDiscount: state.TotalDiscount,
PaymentInProgress: state.PaymentInProgress,
OrderReference: state.OrderReference,
PaymentStatus: state.PaymentStatus,
}
for _, it := range state.Items {
if it == nil {
continue
}
outlet := toPtr(it.Outlet)
storeId := toPtr(it.StoreId)
grain.Items = append(grain.Items, &CartItem{
Id: int(it.Id),
ItemId: int(it.SourceItemId),
Sku: it.Sku,
Name: it.Name,
Price: it.UnitPrice,
Quantity: int(it.Quantity),
TotalPrice: it.TotalPrice,
TotalTax: it.TotalTax,
OrgPrice: it.OrgPrice,
TaxRate: int(it.TaxRate),
Brand: it.Brand,
Category: it.Category,
Category2: it.Category2,
Category3: it.Category3,
Category4: it.Category4,
Category5: it.Category5,
Image: it.Image,
ArticleType: it.ArticleType,
SellerId: it.SellerId,
SellerName: it.SellerName,
Disclaimer: it.Disclaimer,
Outlet: outlet,
StoreId: storeId,
Stock: StockStatus(it.Stock),
})
}
for _, d := range state.Deliveries {
if d == nil {
continue
}
intIds := make([]int, 0, len(d.ItemIds))
for _, id := range d.ItemIds {
intIds = append(intIds, int(id))
}
grain.Deliveries = append(grain.Deliveries, &CartDelivery{
Id: int(d.Id),
Provider: d.Provider,
Price: d.Price,
Items: intIds,
PickupPoint: d.PickupPoint,
})
}
return grain, nil
}
// Close closes the underlying gRPC connection if this adapter created it.