split cart and checkout and checkout vs payments
This commit is contained in:
@@ -8,7 +8,7 @@ import (
|
||||
"net"
|
||||
"time"
|
||||
|
||||
messages "git.k6n.net/go-cart-actor/pkg/messages"
|
||||
messages "git.k6n.net/go-cart-actor/proto/control"
|
||||
"go.opentelemetry.io/contrib/bridges/otelslog"
|
||||
"go.opentelemetry.io/otel"
|
||||
"go.opentelemetry.io/otel/attribute"
|
||||
@@ -145,7 +145,7 @@ func (s *ControlServer[V]) AnnounceExpiry(ctx context.Context, req *messages.Exp
|
||||
}
|
||||
|
||||
// ControlPlane: Ping
|
||||
func (s *ControlServer[V]) Ping(ctx context.Context, _ *messages.Empty) (*messages.PingReply, error) {
|
||||
func (s *ControlServer[V]) Ping(ctx context.Context, req *messages.Empty) (*messages.PingReply, error) {
|
||||
|
||||
host := s.pool.Hostname()
|
||||
|
||||
@@ -191,7 +191,7 @@ func (s *ControlServer[V]) Negotiate(ctx context.Context, req *messages.Negotiat
|
||||
}
|
||||
|
||||
// ControlPlane: GetCartIds (locally owned carts only)
|
||||
func (s *ControlServer[V]) GetLocalActorIds(ctx context.Context, _ *messages.Empty) (*messages.ActorIdsReply, error) {
|
||||
func (s *ControlServer[V]) GetLocalActorIds(ctx context.Context, req *messages.Empty) (*messages.ActorIdsReply, error) {
|
||||
ctx, span := tracer.Start(ctx, "grpc_get_local_actor_ids")
|
||||
defer span.End()
|
||||
ids := s.pool.GetLocalIds()
|
||||
|
||||
@@ -4,7 +4,8 @@ import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"git.k6n.net/go-cart-actor/pkg/messages"
|
||||
cart_messages "git.k6n.net/go-cart-actor/proto/cart"
|
||||
control_plane_messages "git.k6n.net/go-cart-actor/proto/control"
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/credentials/insecure"
|
||||
"google.golang.org/protobuf/proto"
|
||||
@@ -70,12 +71,12 @@ func TestApplyRequestWithMutations(t *testing.T) {
|
||||
}
|
||||
defer conn.Close()
|
||||
|
||||
client := messages.NewControlPlaneClient(conn)
|
||||
client := control_plane_messages.NewControlPlaneClient(conn)
|
||||
|
||||
// Prepare ApplyRequest with multiple Any messages
|
||||
addItemAny, _ := anypb.New(&messages.AddItem{ItemId: 1, Quantity: 2})
|
||||
removeItemAny, _ := anypb.New(&messages.RemoveItem{Id: 1})
|
||||
req := &messages.ApplyRequest{
|
||||
addItemAny, _ := anypb.New(&cart_messages.AddItem{ItemId: 1, Quantity: 2})
|
||||
removeItemAny, _ := anypb.New(&cart_messages.RemoveItem{Id: 1})
|
||||
req := &control_plane_messages.ApplyRequest{
|
||||
Id: 123,
|
||||
Messages: []*anypb.Any{addItemAny, removeItemAny},
|
||||
}
|
||||
@@ -95,10 +96,10 @@ func TestApplyRequestWithMutations(t *testing.T) {
|
||||
if len(pool.applied) != 2 {
|
||||
t.Errorf("expected 2 mutations applied, got %d", len(pool.applied))
|
||||
}
|
||||
if addItem, ok := pool.applied[0].(*messages.AddItem); !ok || addItem.ItemId != 1 {
|
||||
if addItem, ok := pool.applied[0].(*cart_messages.AddItem); !ok || addItem.ItemId != 1 {
|
||||
t.Errorf("expected AddItem with ItemId=1, got %v", pool.applied[0])
|
||||
}
|
||||
if removeItem, ok := pool.applied[1].(*messages.RemoveItem); !ok || removeItem.Id != 1 {
|
||||
if removeItem, ok := pool.applied[1].(*cart_messages.RemoveItem); !ok || removeItem.Id != 1 {
|
||||
t.Errorf("expected RemoveItem with Id=1, got %v", pool.applied[1])
|
||||
}
|
||||
}
|
||||
|
||||
@@ -57,7 +57,7 @@ func TestRegisteredMutationBasics(t *testing.T) {
|
||||
}
|
||||
|
||||
// GetTypeName should fail for unregistered type
|
||||
if name, ok := reg.GetTypeName(&messages.Noop{}); ok || name != "" {
|
||||
if name, ok := reg.GetTypeName(&messages.RemoveItem{}); ok || name != "" {
|
||||
t.Fatalf("expected GetTypeName to fail for unregistered message, got (%q,%v)", name, ok)
|
||||
}
|
||||
|
||||
@@ -99,7 +99,7 @@ func TestRegisteredMutationBasics(t *testing.T) {
|
||||
}
|
||||
|
||||
// Apply unregistered message
|
||||
_, err := reg.Apply(context.Background(), state, &messages.Noop{})
|
||||
_, err := reg.Apply(context.Background(), state, &messages.RemoveItem{})
|
||||
if err != ErrMutationNotRegistered {
|
||||
t.Fatalf("expected ErrMutationNotRegistered, got %v", err)
|
||||
}
|
||||
|
||||
@@ -2,11 +2,9 @@ package cart
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"slices"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
messages "git.k6n.net/go-cart-actor/pkg/messages"
|
||||
"git.k6n.net/go-cart-actor/pkg/voucher"
|
||||
"github.com/matst80/go-redis-inventory/pkg/inventory"
|
||||
)
|
||||
@@ -55,14 +53,6 @@ type CartItem struct {
|
||||
ReservationEndTime *time.Time `json:"reservationEndTime,omitempty"`
|
||||
}
|
||||
|
||||
type CartDelivery struct {
|
||||
Id uint32 `json:"id"`
|
||||
Provider string `json:"provider"`
|
||||
Price Price `json:"price"`
|
||||
Items []uint32 `json:"items"`
|
||||
PickupPoint *messages.PickupPoint `json:"pickupPoint,omitempty"`
|
||||
}
|
||||
|
||||
type CartNotification struct {
|
||||
LinkedId int `json:"id"`
|
||||
Provider string `json:"provider"`
|
||||
@@ -84,103 +74,46 @@ type Notice struct {
|
||||
Code *string `json:"code,omitempty"`
|
||||
}
|
||||
|
||||
type PaymentStatus string
|
||||
type CartPaymentStatus PaymentStatus
|
||||
type CartPaymentStatus string
|
||||
|
||||
const (
|
||||
PaymentStatusPending PaymentStatus = "pending"
|
||||
PaymentStatusFailed PaymentStatus = "failed"
|
||||
PaymentStatusSuccess PaymentStatus = "success"
|
||||
CartPaymentStatusPending CartPaymentStatus = "pending"
|
||||
CartPaymentStatusFailed CartPaymentStatus = "failed"
|
||||
CartPaymentStatusSuccess CartPaymentStatus = "success"
|
||||
CartPaymentStatusCancelled CartPaymentStatus = "partial"
|
||||
)
|
||||
|
||||
type CartPayment struct {
|
||||
PaymentId string `json:"paymentId"`
|
||||
Status PaymentStatus `json:"status"`
|
||||
Amount int64 `json:"amount"`
|
||||
Currency string `json:"currency"`
|
||||
Provider string `json:"provider,omitempty"`
|
||||
Method *string `json:"method,omitempty"`
|
||||
Events []*PaymentEvent `json:"events,omitempty"`
|
||||
ProcessorReference *string `json:"processorReference,omitempty"`
|
||||
StartedAt *time.Time `json:"startedAt,omitempty"`
|
||||
CompletedAt *time.Time `json:"completedAt,omitempty"`
|
||||
}
|
||||
|
||||
type PaymentEvent struct {
|
||||
Name string `json:"name"`
|
||||
Success bool `json:"success"`
|
||||
Data json.RawMessage `json:"data"`
|
||||
}
|
||||
|
||||
func (p *CartPayment) IsSettled() bool {
|
||||
if p == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
switch p.Status {
|
||||
case PaymentStatusSuccess:
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
type Marking struct {
|
||||
Type uint32 `json:"type"`
|
||||
Text string `json:"text"`
|
||||
}
|
||||
|
||||
type GiftcardItem struct {
|
||||
Id uint32 `json:"id"`
|
||||
Value Price `json:"value"`
|
||||
DeliveryDate string `json:"deliveryDate"`
|
||||
Recipient string `json:"recipient"`
|
||||
RecipientType string `json:"recipientType"`
|
||||
Message string `json:"message"`
|
||||
DesignConfig json.RawMessage `json:"designConfig,omitempty"`
|
||||
}
|
||||
|
||||
type CartGrain struct {
|
||||
mu sync.RWMutex
|
||||
lastItemId uint32
|
||||
lastDeliveryId uint32
|
||||
lastVoucherId uint32
|
||||
lastGiftcardId uint32
|
||||
lastAccess time.Time
|
||||
lastChange time.Time // unix seconds of last successful mutation (replay sets from event ts)
|
||||
userId string
|
||||
Version uint `json:"version"`
|
||||
InventoryReserved bool `json:"inventoryReserved"`
|
||||
Id CartId `json:"id"`
|
||||
Items []*CartItem `json:"items"`
|
||||
Giftcards []*GiftcardItem `json:"giftcards,omitempty"`
|
||||
TotalPrice *Price `json:"totalPrice"`
|
||||
TotalDiscount *Price `json:"totalDiscount"`
|
||||
Deliveries []*CartDelivery `json:"deliveries,omitempty"`
|
||||
Processing bool `json:"processing"`
|
||||
PaymentInProgress uint16 `json:"paymentInProgress"`
|
||||
OrderReference string `json:"orderReference,omitempty"`
|
||||
PaymentStatus PaymentStatus `json:"paymentStatus,omitempty"`
|
||||
PaidInFull bool `json:"paidInFull"`
|
||||
Vouchers []*Voucher `json:"vouchers,omitempty"`
|
||||
Notifications []CartNotification `json:"cartNotification,omitempty"`
|
||||
SubscriptionDetails map[string]*SubscriptionDetails `json:"subscriptionDetails,omitempty"`
|
||||
PaymentDeclinedNotices []Notice `json:"paymentDeclinedNotices,omitempty"`
|
||||
Payments []*CartPayment `json:"payments,omitempty"`
|
||||
Confirmation *ConfirmationStatus `json:"confirmation,omitempty"`
|
||||
//CheckoutOrderId string `json:"checkoutOrderId,omitempty"`
|
||||
CheckoutStatus CartPaymentStatus `json:"checkoutStatus,omitempty"`
|
||||
//CheckoutCountry string `json:"checkoutCountry,omitempty"`
|
||||
}
|
||||
mu sync.RWMutex
|
||||
lastItemId uint32
|
||||
lastVoucherId uint32
|
||||
lastAccess time.Time
|
||||
lastChange time.Time // unix seconds of last successful mutation (replay sets from event ts)
|
||||
userId string
|
||||
Currency string `json:"currency"`
|
||||
Language string `json:"language"`
|
||||
Version uint `json:"version"`
|
||||
InventoryReserved bool `json:"inventoryReserved"`
|
||||
Id CartId `json:"id"`
|
||||
Items []*CartItem `json:"items"`
|
||||
TotalPrice *Price `json:"totalPrice"`
|
||||
TotalDiscount *Price `json:"totalDiscount"`
|
||||
Processing bool `json:"processing"`
|
||||
//PaymentInProgress uint16 `json:"paymentInProgress"`
|
||||
OrderReference string `json:"orderReference,omitempty"`
|
||||
|
||||
type ConfirmationStatus struct {
|
||||
Code *string `json:"code,omitempty"`
|
||||
ViewCount int `json:"viewCount"`
|
||||
LastViewedAt time.Time `json:"lastViewedAt"`
|
||||
Vouchers []*Voucher `json:"vouchers,omitempty"`
|
||||
Notifications []CartNotification `json:"cartNotification,omitempty"`
|
||||
SubscriptionDetails map[string]*SubscriptionDetails `json:"subscriptionDetails,omitempty"`
|
||||
|
||||
//CheckoutOrderId string `json:"checkoutOrderId,omitempty"`
|
||||
CheckoutStatus *CartPaymentStatus `json:"checkoutStatus,omitempty"`
|
||||
//CheckoutCountry string `json:"checkoutCountry,omitempty"`
|
||||
}
|
||||
|
||||
type Voucher struct {
|
||||
@@ -243,19 +176,14 @@ func (v *Voucher) AppliesTo(cart *CartGrain) ([]*CartItem, bool) {
|
||||
func NewCartGrain(id uint64, ts time.Time) *CartGrain {
|
||||
return &CartGrain{
|
||||
lastItemId: 0,
|
||||
lastDeliveryId: 0,
|
||||
lastVoucherId: 0,
|
||||
lastGiftcardId: 0,
|
||||
lastAccess: ts,
|
||||
lastChange: ts,
|
||||
TotalDiscount: NewPrice(),
|
||||
Vouchers: []*Voucher{},
|
||||
Deliveries: []*CartDelivery{},
|
||||
Giftcards: []*GiftcardItem{},
|
||||
Id: CartId(id),
|
||||
Items: []*CartItem{},
|
||||
TotalPrice: NewPrice(),
|
||||
Payments: []*CartPayment{},
|
||||
SubscriptionDetails: make(map[string]*SubscriptionDetails),
|
||||
}
|
||||
}
|
||||
@@ -294,33 +222,6 @@ func (c *CartGrain) GetState() ([]byte, error) {
|
||||
return json.Marshal(c)
|
||||
}
|
||||
|
||||
func (c *CartGrain) ItemsWithDelivery() []uint32 {
|
||||
ret := make([]uint32, 0, len(c.Items))
|
||||
for _, item := range c.Items {
|
||||
for _, delivery := range c.Deliveries {
|
||||
for _, id := range delivery.Items {
|
||||
if item.Id == id {
|
||||
ret = append(ret, id)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
func (c *CartGrain) ItemsWithoutDelivery() []uint32 {
|
||||
ret := make([]uint32, 0, len(c.Items))
|
||||
hasDelivery := c.ItemsWithDelivery()
|
||||
for _, item := range c.Items {
|
||||
found := slices.Contains(hasDelivery, item.Id)
|
||||
|
||||
if !found {
|
||||
ret = append(ret, item.Id)
|
||||
}
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
func (c *CartGrain) FindItemWithSku(sku string) (*CartItem, bool) {
|
||||
c.mu.RLock()
|
||||
defer c.mu.RUnlock()
|
||||
@@ -332,73 +233,6 @@ func (c *CartGrain) FindItemWithSku(sku string) (*CartItem, bool) {
|
||||
return nil, false
|
||||
}
|
||||
|
||||
func (c *CartGrain) FindPayment(paymentId string) (*CartPayment, bool) {
|
||||
if paymentId == "" {
|
||||
return nil, false
|
||||
}
|
||||
for _, payment := range c.Payments {
|
||||
if payment != nil && payment.PaymentId == paymentId {
|
||||
return payment, true
|
||||
}
|
||||
}
|
||||
return nil, false
|
||||
}
|
||||
|
||||
func (c *CartGrain) SettledPayments() []*CartPayment {
|
||||
if len(c.Payments) == 0 {
|
||||
return nil
|
||||
}
|
||||
settled := make([]*CartPayment, 0, len(c.Payments))
|
||||
for _, payment := range c.Payments {
|
||||
if payment != nil && payment.IsSettled() {
|
||||
settled = append(settled, payment)
|
||||
}
|
||||
}
|
||||
if len(settled) == 0 {
|
||||
return nil
|
||||
}
|
||||
return settled
|
||||
}
|
||||
|
||||
func (c *CartGrain) OpenPayments() []*CartPayment {
|
||||
if len(c.Payments) == 0 {
|
||||
return nil
|
||||
}
|
||||
pending := make([]*CartPayment, 0, len(c.Payments))
|
||||
for _, payment := range c.Payments {
|
||||
if payment == nil {
|
||||
continue
|
||||
}
|
||||
if !payment.IsSettled() {
|
||||
pending = append(pending, payment)
|
||||
}
|
||||
}
|
||||
if len(pending) == 0 {
|
||||
return nil
|
||||
}
|
||||
return pending
|
||||
}
|
||||
|
||||
// func (c *CartGrain) Apply(content proto.Message, isReplay bool) (*CartGrain, error) {
|
||||
|
||||
// updated, err := ApplyRegistered(c, content)
|
||||
// if err != nil {
|
||||
// if err == ErrMutationNotRegistered {
|
||||
// return nil, fmt.Errorf("unsupported mutation type %T (not registered)", content)
|
||||
// }
|
||||
// return nil, err
|
||||
// }
|
||||
|
||||
// // Sliding TTL: update lastChange only for non-replay successful mutations.
|
||||
// if updated != nil && !isReplay {
|
||||
// c.lastChange = time.Now()
|
||||
// c.lastAccess = time.Now()
|
||||
// go AppendCartEvent(c.Id, content)
|
||||
// }
|
||||
|
||||
// return updated, nil
|
||||
// }
|
||||
|
||||
func (c *CartGrain) UpdateTotals() {
|
||||
c.TotalPrice = NewPrice()
|
||||
c.TotalDiscount = NewPrice()
|
||||
@@ -423,12 +257,7 @@ func (c *CartGrain) UpdateTotals() {
|
||||
c.TotalPrice.Add(*rowTotal)
|
||||
|
||||
}
|
||||
for _, delivery := range c.Deliveries {
|
||||
c.TotalPrice.Add(delivery.Price)
|
||||
}
|
||||
for _, giftcard := range c.Giftcards {
|
||||
c.TotalPrice.Add(giftcard.Value)
|
||||
}
|
||||
|
||||
for _, voucher := range c.Vouchers {
|
||||
_, ok := voucher.AppliesTo(c)
|
||||
voucher.Applied = false
|
||||
|
||||
@@ -5,7 +5,7 @@ import (
|
||||
"time"
|
||||
|
||||
"git.k6n.net/go-cart-actor/pkg/actor"
|
||||
messages "git.k6n.net/go-cart-actor/pkg/messages"
|
||||
messages "git.k6n.net/go-cart-actor/proto/cart"
|
||||
"github.com/matst80/go-redis-inventory/pkg/inventory"
|
||||
)
|
||||
|
||||
@@ -70,21 +70,6 @@ func NewCartMultationRegistry(context *CartMutationContext) actor.MutationRegist
|
||||
actor.NewMutation(context.RemoveItem, func() *messages.RemoveItem {
|
||||
return &messages.RemoveItem{}
|
||||
}),
|
||||
actor.NewMutation(context.InitializeCheckout, func() *messages.InitializeCheckout {
|
||||
return &messages.InitializeCheckout{}
|
||||
}),
|
||||
actor.NewMutation(OrderCreated, func() *messages.OrderCreated {
|
||||
return &messages.OrderCreated{}
|
||||
}),
|
||||
actor.NewMutation(RemoveDelivery, func() *messages.RemoveDelivery {
|
||||
return &messages.RemoveDelivery{}
|
||||
}),
|
||||
actor.NewMutation(SetDelivery, func() *messages.SetDelivery {
|
||||
return &messages.SetDelivery{}
|
||||
}),
|
||||
actor.NewMutation(SetPickupPoint, func() *messages.SetPickupPoint {
|
||||
return &messages.SetPickupPoint{}
|
||||
}),
|
||||
actor.NewMutation(ClearCart, func() *messages.ClearCartRequest {
|
||||
return &messages.ClearCartRequest{}
|
||||
}),
|
||||
@@ -97,12 +82,6 @@ func NewCartMultationRegistry(context *CartMutationContext) actor.MutationRegist
|
||||
actor.NewMutation(UpsertSubscriptionDetails, func() *messages.UpsertSubscriptionDetails {
|
||||
return &messages.UpsertSubscriptionDetails{}
|
||||
}),
|
||||
actor.NewMutation(context.InventoryReserved, func() *messages.InventoryReserved {
|
||||
return &messages.InventoryReserved{}
|
||||
}),
|
||||
actor.NewMutation(PreConditionFailed, func() *messages.PreConditionFailed {
|
||||
return &messages.PreConditionFailed{}
|
||||
}),
|
||||
actor.NewMutation(SetUserId, func() *messages.SetUserId {
|
||||
return &messages.SetUserId{}
|
||||
}),
|
||||
@@ -115,30 +94,6 @@ func NewCartMultationRegistry(context *CartMutationContext) actor.MutationRegist
|
||||
actor.NewMutation(SubscriptionAdded, func() *messages.SubscriptionAdded {
|
||||
return &messages.SubscriptionAdded{}
|
||||
}),
|
||||
actor.NewMutation(PaymentStarted, func() *messages.PaymentStarted {
|
||||
return &messages.PaymentStarted{}
|
||||
}),
|
||||
actor.NewMutation(PaymentCompleted, func() *messages.PaymentCompleted {
|
||||
return &messages.PaymentCompleted{}
|
||||
}),
|
||||
actor.NewMutation(PaymentDeclined, func() *messages.PaymentDeclined {
|
||||
return &messages.PaymentDeclined{}
|
||||
}),
|
||||
actor.NewMutation(PaymentEventHandler, func() *messages.PaymentEvent {
|
||||
return &messages.PaymentEvent{}
|
||||
}),
|
||||
actor.NewMutation(ConfirmationViewed, func() *messages.ConfirmationViewed {
|
||||
return &messages.ConfirmationViewed{}
|
||||
}),
|
||||
actor.NewMutation(CreateCheckoutOrder, func() *messages.CreateCheckoutOrder {
|
||||
return &messages.CreateCheckoutOrder{}
|
||||
}),
|
||||
actor.NewMutation(AddGiftcard, func() *messages.AddGiftcard {
|
||||
return &messages.AddGiftcard{}
|
||||
}),
|
||||
actor.NewMutation(RemoveGiftcard, func() *messages.RemoveGiftcard {
|
||||
return &messages.RemoveGiftcard{}
|
||||
}),
|
||||
)
|
||||
return reg
|
||||
|
||||
|
||||
@@ -1,48 +0,0 @@
|
||||
package cart
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
// helper to create a cart grain with items and deliveries
|
||||
func newTestCart() *CartGrain {
|
||||
return &CartGrain{Items: []*CartItem{}, Deliveries: []*CartDelivery{}, Vouchers: []*Voucher{}, Notifications: []CartNotification{}}
|
||||
}
|
||||
|
||||
func TestCartGrainUpdateTotalsBasic(t *testing.T) {
|
||||
c := newTestCart()
|
||||
// Item1 price 1250 (ex 1000 vat 250) org price higher -> discount 200 per unit
|
||||
item1Price := Price{IncVat: 1250, VatRates: map[float32]int64{25: 250}}
|
||||
item1Org := &Price{IncVat: 1500, VatRates: map[float32]int64{25: 300}}
|
||||
item2Price := Price{IncVat: 2000, VatRates: map[float32]int64{25: 400}}
|
||||
c.Items = []*CartItem{
|
||||
{Id: 1, Price: item1Price, OrgPrice: item1Org, Quantity: 2},
|
||||
{Id: 2, Price: item2Price, OrgPrice: &item2Price, Quantity: 1},
|
||||
}
|
||||
deliveryPrice := Price{IncVat: 4900, VatRates: map[float32]int64{25: 980}}
|
||||
c.Deliveries = []*CartDelivery{{Id: 1, Price: deliveryPrice, Items: []uint32{1, 2}}}
|
||||
|
||||
c.UpdateTotals()
|
||||
|
||||
// Expected totals: sum inc vat of items * qty plus delivery
|
||||
// item1 total inc = 1250*2 = 2500
|
||||
// item2 total inc = 2000*1 = 2000
|
||||
// delivery inc = 4900
|
||||
expectedInc := int64(2500 + 2000 + 4900)
|
||||
if c.TotalPrice.IncVat != expectedInc {
|
||||
t.Fatalf("TotalPrice IncVat expected %d got %d", expectedInc, c.TotalPrice.IncVat)
|
||||
}
|
||||
|
||||
// Discount: current implementation computes (OrgPrice - Price) ignoring quantity -> 1500-1250=250
|
||||
if c.TotalDiscount.IncVat != 500 {
|
||||
t.Fatalf("TotalDiscount expected 500 got %d", c.TotalDiscount.IncVat)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCartGrainUpdateTotalsNoItems(t *testing.T) {
|
||||
c := newTestCart()
|
||||
c.UpdateTotals()
|
||||
if c.TotalPrice.IncVat != 0 || c.TotalDiscount.IncVat != 0 {
|
||||
t.Fatalf("expected zero totals got %+v", c)
|
||||
}
|
||||
}
|
||||
@@ -1,44 +0,0 @@
|
||||
package cart
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
|
||||
messages "git.k6n.net/go-cart-actor/pkg/messages"
|
||||
"google.golang.org/protobuf/proto"
|
||||
)
|
||||
|
||||
func AddGiftcard(grain *CartGrain, req *messages.AddGiftcard) error {
|
||||
if req.Giftcard == nil {
|
||||
return fmt.Errorf("giftcard cannot be nil")
|
||||
}
|
||||
if req.Giftcard.Value <= 0 {
|
||||
return fmt.Errorf("giftcard value must be positive")
|
||||
}
|
||||
if grain.PaymentInProgress > 0 {
|
||||
return ErrPaymentInProgress
|
||||
}
|
||||
grain.lastGiftcardId++
|
||||
designConfig := json.RawMessage{}
|
||||
if req.Giftcard.DesignConfig != nil {
|
||||
// Convert Any to RawMessage
|
||||
data, err := proto.Marshal(req.Giftcard.DesignConfig)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to marshal designConfig: %w", err)
|
||||
}
|
||||
designConfig = data
|
||||
}
|
||||
value := NewPriceFromIncVat(req.Giftcard.Value, 25) // Assuming 25% tax; adjust as needed
|
||||
item := &GiftcardItem{
|
||||
Id: grain.lastGiftcardId,
|
||||
Value: *value,
|
||||
DeliveryDate: req.Giftcard.DeliveryDate,
|
||||
Recipient: req.Giftcard.Recipient,
|
||||
RecipientType: req.Giftcard.RecipientType,
|
||||
Message: req.Giftcard.Message,
|
||||
DesignConfig: designConfig,
|
||||
}
|
||||
grain.Giftcards = append(grain.Giftcards, item)
|
||||
grain.UpdateTotals()
|
||||
return nil
|
||||
}
|
||||
@@ -7,7 +7,7 @@ import (
|
||||
"log"
|
||||
"time"
|
||||
|
||||
messages "git.k6n.net/go-cart-actor/pkg/messages"
|
||||
cart_messages "git.k6n.net/go-cart-actor/proto/cart"
|
||||
"google.golang.org/protobuf/types/known/timestamppb"
|
||||
)
|
||||
|
||||
@@ -26,7 +26,7 @@ import (
|
||||
// must keep this handler in sync.
|
||||
var ErrPaymentInProgress = errors.New("payment in progress")
|
||||
|
||||
func (c *CartMutationContext) AddItem(g *CartGrain, m *messages.AddItem) error {
|
||||
func (c *CartMutationContext) AddItem(g *CartGrain, m *cart_messages.AddItem) error {
|
||||
ctx := context.Background()
|
||||
if m == nil {
|
||||
return fmt.Errorf("AddItem: nil payload")
|
||||
@@ -34,9 +34,6 @@ func (c *CartMutationContext) AddItem(g *CartGrain, m *messages.AddItem) error {
|
||||
if m.Quantity < 1 {
|
||||
return fmt.Errorf("AddItem: invalid quantity %d", m.Quantity)
|
||||
}
|
||||
if g.PaymentInProgress > 0 {
|
||||
return ErrPaymentInProgress
|
||||
}
|
||||
|
||||
// Merge with any existing item having same SKU and matching StoreId (including both nil).
|
||||
for _, existing := range g.Items {
|
||||
|
||||
@@ -4,7 +4,7 @@ import (
|
||||
"slices"
|
||||
|
||||
"git.k6n.net/go-cart-actor/pkg/actor"
|
||||
"git.k6n.net/go-cart-actor/pkg/messages"
|
||||
messages "git.k6n.net/go-cart-actor/proto/cart"
|
||||
)
|
||||
|
||||
func RemoveVoucher(g *CartGrain, m *messages.RemoveVoucher) error {
|
||||
@@ -15,7 +15,7 @@ func RemoveVoucher(g *CartGrain, m *messages.RemoveVoucher) error {
|
||||
StatusCode: 400,
|
||||
}
|
||||
}
|
||||
if g.PaymentInProgress > 0 {
|
||||
if g.CheckoutStatus != nil {
|
||||
return ErrPaymentInProgress
|
||||
}
|
||||
|
||||
@@ -45,10 +45,6 @@ func AddVoucher(g *CartGrain, m *messages.AddVoucher) error {
|
||||
}
|
||||
}
|
||||
|
||||
if g.PaymentInProgress > 0 {
|
||||
return ErrPaymentInProgress
|
||||
}
|
||||
|
||||
if slices.ContainsFunc(g.Vouchers, func(v *Voucher) bool {
|
||||
return v.Code == m.Code
|
||||
}) {
|
||||
|
||||
@@ -5,7 +5,7 @@ import (
|
||||
"fmt"
|
||||
"log"
|
||||
|
||||
messages "git.k6n.net/go-cart-actor/pkg/messages"
|
||||
messages "git.k6n.net/go-cart-actor/proto/cart"
|
||||
)
|
||||
|
||||
// mutation_change_quantity.go
|
||||
@@ -32,9 +32,7 @@ func (c *CartMutationContext) ChangeQuantity(g *CartGrain, m *messages.ChangeQua
|
||||
if m == nil {
|
||||
return fmt.Errorf("ChangeQuantity: nil payload")
|
||||
}
|
||||
if g.PaymentInProgress > 0 {
|
||||
return ErrPaymentInProgress
|
||||
}
|
||||
|
||||
ctx := context.Background()
|
||||
|
||||
foundIndex := -1
|
||||
|
||||
26
pkg/cart/mutation_clear_cart.go
Normal file
26
pkg/cart/mutation_clear_cart.go
Normal file
@@ -0,0 +1,26 @@
|
||||
package cart
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
messages "git.k6n.net/go-cart-actor/proto/cart"
|
||||
)
|
||||
|
||||
func ClearCart(g *CartGrain, m *messages.ClearCartRequest) error {
|
||||
if m == nil {
|
||||
return fmt.Errorf("ClearCart: nil payload")
|
||||
}
|
||||
if g.CheckoutStatus != nil {
|
||||
return fmt.Errorf("ClearCart: cart is in checkout")
|
||||
}
|
||||
// Clear items, vouchers, etc., but keep userId, etc.
|
||||
g.Items = g.Items[:0]
|
||||
g.Vouchers = g.Vouchers[:0]
|
||||
g.Notifications = g.Notifications[:0]
|
||||
g.OrderReference = ""
|
||||
g.Processing = false
|
||||
|
||||
// g.InventoryReserved = false maybe should release inventory
|
||||
g.UpdateTotals()
|
||||
return nil
|
||||
}
|
||||
@@ -1,21 +0,0 @@
|
||||
package cart
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
messages "git.k6n.net/go-cart-actor/pkg/messages"
|
||||
)
|
||||
|
||||
func ConfirmationViewed(grain *CartGrain, req *messages.ConfirmationViewed) error {
|
||||
if grain.Confirmation == nil {
|
||||
grain.Confirmation = &ConfirmationStatus{
|
||||
ViewCount: 1,
|
||||
LastViewedAt: time.Now(),
|
||||
}
|
||||
} else {
|
||||
grain.Confirmation.ViewCount++
|
||||
grain.Confirmation.LastViewedAt = time.Now()
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -1,21 +0,0 @@
|
||||
package cart
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
messages "git.k6n.net/go-cart-actor/pkg/messages"
|
||||
)
|
||||
|
||||
func CreateCheckoutOrder(grain *CartGrain, req *messages.CreateCheckoutOrder) error {
|
||||
if len(grain.Items) == 0 {
|
||||
return errors.New("cannot checkout empty cart")
|
||||
}
|
||||
if req.Terms != "accepted" {
|
||||
return errors.New("terms must be accepted")
|
||||
}
|
||||
// Validate other fields as needed
|
||||
//grain.CheckoutOrderId = uuid.New().String()
|
||||
grain.CheckoutStatus = "pending"
|
||||
//grain.CheckoutCountry = req.Country
|
||||
return nil
|
||||
}
|
||||
@@ -1,59 +0,0 @@
|
||||
package cart
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
messages "git.k6n.net/go-cart-actor/pkg/messages"
|
||||
)
|
||||
|
||||
// mutation_initialize_checkout.go
|
||||
//
|
||||
// Registers the InitializeCheckout mutation.
|
||||
// This mutation is invoked AFTER an external Klarna checkout session
|
||||
// has been successfully created or updated. It persists the Klarna
|
||||
// order reference / status and marks the cart as having a payment in progress.
|
||||
//
|
||||
// Behavior:
|
||||
// - Sets OrderReference to the Klarna order ID (overwriting if already set).
|
||||
// - Sets PaymentStatus to the current Klarna status.
|
||||
// - Sets / updates PaymentInProgress flag.
|
||||
// - Does NOT alter pricing or line items (so no totals recalculation).
|
||||
//
|
||||
// Validation:
|
||||
// - Returns an error if payload is nil.
|
||||
// - Returns an error if orderId is empty (integrity guard).
|
||||
//
|
||||
// Concurrency:
|
||||
// - Relies on upstream mutation serialization for a single grain. If
|
||||
// parallel checkout attempts are possible, add higher-level guards
|
||||
// (e.g. reject if PaymentInProgress already true unless reusing
|
||||
// the same OrderReference).
|
||||
|
||||
func (c *CartMutationContext) InitializeCheckout(g *CartGrain, m *messages.InitializeCheckout) error {
|
||||
if m == nil {
|
||||
return fmt.Errorf("InitializeCheckout: nil payload")
|
||||
}
|
||||
if m.OrderId == "" {
|
||||
return fmt.Errorf("InitializeCheckout: missing orderId")
|
||||
}
|
||||
ctx := context.Background()
|
||||
now := time.Now()
|
||||
for _, item := range g.Items {
|
||||
if item.ReservationEndTime != nil {
|
||||
if now.After(*item.ReservationEndTime) {
|
||||
endTime, err := c.ReserveItem(ctx, g.Id, item.Sku, item.StoreId, item.Quantity)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
item.ReservationEndTime = endTime
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
g.OrderReference = m.OrderId
|
||||
//g.PaymentStatus = m.Status
|
||||
//g.PaymentInProgress = m.PaymentInProgress
|
||||
return nil
|
||||
}
|
||||
@@ -1,22 +0,0 @@
|
||||
package cart
|
||||
|
||||
import (
|
||||
"context"
|
||||
"log"
|
||||
"time"
|
||||
|
||||
"git.k6n.net/go-cart-actor/pkg/messages"
|
||||
)
|
||||
|
||||
func (c *CartMutationContext) InventoryReserved(g *CartGrain, m *messages.InventoryReserved) error {
|
||||
for _, item := range g.Items {
|
||||
if item.ReservationEndTime != nil && item.ReservationEndTime.After(time.Now()) {
|
||||
err := c.ReleaseItem(context.Background(), g.Id, item.Sku, item.StoreId)
|
||||
if err != nil {
|
||||
log.Printf("unable to release item reservation")
|
||||
}
|
||||
}
|
||||
}
|
||||
g.InventoryReserved = true
|
||||
return nil
|
||||
}
|
||||
@@ -3,7 +3,7 @@ package cart
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
messages "git.k6n.net/go-cart-actor/pkg/messages"
|
||||
messages "git.k6n.net/go-cart-actor/proto/cart"
|
||||
)
|
||||
|
||||
func LineItemMarking(grain *CartGrain, req *messages.LineItemMarking) error {
|
||||
|
||||
@@ -1,33 +0,0 @@
|
||||
package cart
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
messages "git.k6n.net/go-cart-actor/pkg/messages"
|
||||
)
|
||||
|
||||
// PaymentStarted registers the beginning of a payment attempt for a cart.
|
||||
// It either upserts the payment entry (based on paymentId) or creates a new one,
|
||||
// marks the cart as having an in-progress payment, and recalculates the PaidInFull flag.
|
||||
func PaymentCompleted(grain *CartGrain, msg *messages.PaymentCompleted) error {
|
||||
if msg == nil {
|
||||
return fmt.Errorf("PaymentStarted: nil payload")
|
||||
}
|
||||
paymentId := msg.PaymentId
|
||||
payment, found := grain.FindPayment(paymentId)
|
||||
if !found {
|
||||
return fmt.Errorf("PaymentStarted: payment not found")
|
||||
}
|
||||
|
||||
payment.ProcessorReference = msg.ProcessorReference
|
||||
payment.Status = PaymentStatusSuccess
|
||||
payment.Amount = msg.Amount
|
||||
payment.Currency = msg.Currency
|
||||
payment.CompletedAt = asPointer(time.Now())
|
||||
|
||||
// maybe update cart status
|
||||
grain.PaymentInProgress--
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -1,27 +0,0 @@
|
||||
package cart
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"time"
|
||||
|
||||
messages "git.k6n.net/go-cart-actor/pkg/messages"
|
||||
)
|
||||
|
||||
func asPointer[T any](value T) *T {
|
||||
return &value
|
||||
}
|
||||
|
||||
var ErrPaymentNotFound = errors.New("payment not found")
|
||||
|
||||
func PaymentDeclined(grain *CartGrain, req *messages.PaymentDeclined) error {
|
||||
|
||||
payment, found := grain.FindPayment(req.PaymentId)
|
||||
if !found {
|
||||
return ErrPaymentNotFound
|
||||
}
|
||||
|
||||
payment.CompletedAt = asPointer(time.Now())
|
||||
payment.Status = PaymentStatusFailed
|
||||
grain.PaymentInProgress--
|
||||
return nil
|
||||
}
|
||||
@@ -1,22 +0,0 @@
|
||||
package cart
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
|
||||
messages "git.k6n.net/go-cart-actor/pkg/messages"
|
||||
)
|
||||
|
||||
func PaymentEventHandler(grain *CartGrain, req *messages.PaymentEvent) error {
|
||||
|
||||
payment, found := grain.FindPayment(req.PaymentId)
|
||||
if !found {
|
||||
return ErrPaymentNotFound
|
||||
}
|
||||
metaBytes := req.Data.GetValue()
|
||||
payment.Events = append(payment.Events, &PaymentEvent{
|
||||
Name: req.Name,
|
||||
Success: req.Success,
|
||||
Data: json.RawMessage(metaBytes),
|
||||
})
|
||||
return nil
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
package cart
|
||||
|
||||
import messages "git.k6n.net/go-cart-actor/pkg/messages"
|
||||
|
||||
func PreConditionFailed(g *CartGrain, m *messages.PreConditionFailed) error {
|
||||
return nil
|
||||
}
|
||||
@@ -1,21 +0,0 @@
|
||||
package cart
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
messages "git.k6n.net/go-cart-actor/pkg/messages"
|
||||
)
|
||||
|
||||
func RemoveGiftcard(grain *CartGrain, req *messages.RemoveGiftcard) error {
|
||||
if grain.PaymentInProgress > 0 {
|
||||
return ErrPaymentInProgress
|
||||
}
|
||||
for i, item := range grain.Giftcards {
|
||||
if item.Id == req.Id {
|
||||
grain.Giftcards = append(grain.Giftcards[:i], grain.Giftcards[i+1:]...)
|
||||
grain.UpdateTotals()
|
||||
return nil
|
||||
}
|
||||
}
|
||||
return fmt.Errorf("giftcard with ID %d not found", req.Id)
|
||||
}
|
||||
@@ -6,7 +6,7 @@ import (
|
||||
"log"
|
||||
"time"
|
||||
|
||||
messages "git.k6n.net/go-cart-actor/pkg/messages"
|
||||
messages "git.k6n.net/go-cart-actor/proto/cart"
|
||||
)
|
||||
|
||||
// mutation_remove_item.go
|
||||
@@ -29,9 +29,7 @@ func (c *CartMutationContext) RemoveItem(g *CartGrain, m *messages.RemoveItem) e
|
||||
if m == nil {
|
||||
return fmt.Errorf("RemoveItem: nil payload")
|
||||
}
|
||||
if g.PaymentInProgress > 0 {
|
||||
return ErrPaymentInProgress
|
||||
}
|
||||
|
||||
targetID := uint32(m.Id)
|
||||
|
||||
index := -1
|
||||
|
||||
@@ -3,7 +3,7 @@ package cart
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
messages "git.k6n.net/go-cart-actor/pkg/messages"
|
||||
messages "git.k6n.net/go-cart-actor/proto/cart"
|
||||
)
|
||||
|
||||
func RemoveLineItemMarking(grain *CartGrain, req *messages.RemoveLineItemMarking) error {
|
||||
|
||||
@@ -1,99 +0,0 @@
|
||||
package cart
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"slices"
|
||||
|
||||
messages "git.k6n.net/go-cart-actor/pkg/messages"
|
||||
)
|
||||
|
||||
// mutation_set_delivery.go
|
||||
//
|
||||
// Registers the SetDelivery mutation.
|
||||
//
|
||||
// Semantics (mirrors legacy switch logic):
|
||||
// - If the payload specifies an explicit list of item IDs (payload.Items):
|
||||
// - Each referenced cart line must exist.
|
||||
// - None of the referenced items may already belong to a delivery.
|
||||
// - Only those items are associated with the new delivery.
|
||||
// - If payload.Items is empty:
|
||||
// - All items currently without any delivery are associated with the new delivery.
|
||||
// - A new delivery line is created with:
|
||||
// - Auto-incremented delivery ID (cart-local)
|
||||
// - Provider from payload
|
||||
// - Fixed price (currently hard-coded: 4900 minor units) – adjust as needed
|
||||
// - Optional PickupPoint copied from payload
|
||||
// - Cart totals are recalculated (WithTotals)
|
||||
//
|
||||
// Error cases:
|
||||
// - Referenced item does not exist
|
||||
// - Referenced item already has a delivery
|
||||
// - No items qualify (resulting association set empty) -> returns error (prevents creating empty delivery)
|
||||
//
|
||||
// Concurrency:
|
||||
// - Uses g.mu to protect lastDeliveryId increment and append to Deliveries slice.
|
||||
// Item scans are read-only and performed outside the lock for simplicity;
|
||||
// if stricter guarantees are needed, widen the lock section.
|
||||
//
|
||||
// Future extension points:
|
||||
// - Variable delivery pricing (based on weight, distance, provider, etc.)
|
||||
// - Validation of provider codes
|
||||
// - Multi-currency delivery pricing
|
||||
|
||||
func SetDelivery(g *CartGrain, m *messages.SetDelivery) error {
|
||||
if m == nil {
|
||||
return fmt.Errorf("SetDelivery: nil payload")
|
||||
}
|
||||
if m.Provider == "" {
|
||||
return fmt.Errorf("SetDelivery: provider is empty")
|
||||
}
|
||||
if g.PaymentInProgress > 0 {
|
||||
return ErrPaymentInProgress
|
||||
}
|
||||
|
||||
withDelivery := g.ItemsWithDelivery()
|
||||
targetItems := make([]uint32, 0)
|
||||
|
||||
if len(m.Items) == 0 {
|
||||
// Use every item currently without a delivery
|
||||
targetItems = append(targetItems, g.ItemsWithoutDelivery()...)
|
||||
} else {
|
||||
// Validate explicit list
|
||||
for _, id64 := range m.Items {
|
||||
id := uint32(id64)
|
||||
found := false
|
||||
for _, it := range g.Items {
|
||||
if it.Id == id {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
return fmt.Errorf("SetDelivery: item id %d not found", id)
|
||||
}
|
||||
if slices.Contains(withDelivery, id) {
|
||||
return fmt.Errorf("SetDelivery: item id %d already has a delivery", id)
|
||||
}
|
||||
targetItems = append(targetItems, id)
|
||||
}
|
||||
}
|
||||
|
||||
if len(targetItems) == 0 {
|
||||
return fmt.Errorf("SetDelivery: no eligible items to attach")
|
||||
}
|
||||
|
||||
// Append new delivery
|
||||
g.mu.Lock()
|
||||
g.lastDeliveryId++
|
||||
newId := g.lastDeliveryId
|
||||
g.Deliveries = append(g.Deliveries, &CartDelivery{
|
||||
Id: newId,
|
||||
Provider: m.Provider,
|
||||
PickupPoint: m.PickupPoint,
|
||||
Price: *NewPriceFromIncVat(4900, 25.0),
|
||||
Items: targetItems,
|
||||
})
|
||||
g.mu.Unlock()
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -3,7 +3,7 @@ package cart
|
||||
import (
|
||||
"errors"
|
||||
|
||||
messages "git.k6n.net/go-cart-actor/pkg/messages"
|
||||
messages "git.k6n.net/go-cart-actor/proto/cart"
|
||||
)
|
||||
|
||||
func SetUserId(grain *CartGrain, req *messages.SetUserId) error {
|
||||
|
||||
@@ -3,13 +3,11 @@ package cart
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
messages "git.k6n.net/go-cart-actor/pkg/messages"
|
||||
messages "git.k6n.net/go-cart-actor/proto/cart"
|
||||
)
|
||||
|
||||
func SubscriptionAdded(grain *CartGrain, req *messages.SubscriptionAdded) error {
|
||||
if grain.PaymentInProgress > 0 {
|
||||
return ErrPaymentInProgress
|
||||
}
|
||||
|
||||
for i, item := range grain.Items {
|
||||
if item.Id == req.ItemId {
|
||||
grain.Items[i].SubscriptionDetailsId = req.DetailsId
|
||||
|
||||
@@ -1,809 +0,0 @@
|
||||
package cart
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"slices"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/matst80/go-redis-inventory/pkg/inventory"
|
||||
"google.golang.org/protobuf/proto"
|
||||
"google.golang.org/protobuf/types/known/anypb"
|
||||
|
||||
"git.k6n.net/go-cart-actor/pkg/actor"
|
||||
messages "git.k6n.net/go-cart-actor/pkg/messages"
|
||||
)
|
||||
|
||||
// ----------------------
|
||||
// Helper constructors
|
||||
// ----------------------
|
||||
|
||||
func newTestGrain() *CartGrain {
|
||||
return NewCartGrain(123, time.Now())
|
||||
}
|
||||
|
||||
type MockReservationService struct {
|
||||
}
|
||||
|
||||
func (m *MockReservationService) ReserveForCart(ctx context.Context, req inventory.CartReserveRequest) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *MockReservationService) ReleaseForCart(ctx context.Context, sku inventory.SKU, locationID inventory.LocationID, cartID inventory.CartID) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *MockReservationService) GetAvailableInventory(ctx context.Context, sku inventory.SKU, locationID inventory.LocationID) (int64, error) {
|
||||
return 1000, nil
|
||||
}
|
||||
|
||||
func (m *MockReservationService) GetReservationExpiry(ctx context.Context, sku inventory.SKU, locationID inventory.LocationID, cartID inventory.CartID) (time.Time, error) {
|
||||
return time.Time{}, nil
|
||||
}
|
||||
|
||||
func (m *MockReservationService) GetReservationStatus(ctx context.Context, sku inventory.SKU, locationID inventory.LocationID, cartID inventory.CartID) (*inventory.ReservationStatus, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (m *MockReservationService) GetReservationSummary(ctx context.Context, sku inventory.SKU, locationID inventory.LocationID) (*inventory.ReservationSummary, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func newRegistry() actor.MutationRegistry {
|
||||
cartCtx := &CartMutationContext{
|
||||
reservationService: &MockReservationService{},
|
||||
}
|
||||
return NewCartMultationRegistry(cartCtx)
|
||||
}
|
||||
|
||||
func msgAddItem(sku string, price int64, qty int32, storePtr *string) *messages.AddItem {
|
||||
return &messages.AddItem{
|
||||
Sku: sku,
|
||||
Price: price,
|
||||
Quantity: qty,
|
||||
// Tax left 0 -> handler uses default 25%
|
||||
StoreId: storePtr,
|
||||
}
|
||||
}
|
||||
|
||||
func msgChangeQty(id uint32, qty int32) *messages.ChangeQuantity {
|
||||
return &messages.ChangeQuantity{Id: id, Quantity: qty}
|
||||
}
|
||||
|
||||
func msgRemoveItem(id uint32) *messages.RemoveItem {
|
||||
return &messages.RemoveItem{Id: id}
|
||||
}
|
||||
|
||||
func msgSetDelivery(provider string, items ...uint32) *messages.SetDelivery {
|
||||
uitems := make([]uint32, len(items))
|
||||
copy(uitems, items)
|
||||
return &messages.SetDelivery{Provider: provider, Items: uitems}
|
||||
}
|
||||
|
||||
func msgSetPickupPoint(deliveryId uint32, id string) *messages.SetPickupPoint {
|
||||
return &messages.SetPickupPoint{
|
||||
DeliveryId: deliveryId,
|
||||
Id: id,
|
||||
Name: ptr("Pickup"),
|
||||
Address: ptr("Street 1"),
|
||||
City: ptr("Town"),
|
||||
Zip: ptr("12345"),
|
||||
Country: ptr("SE"),
|
||||
}
|
||||
}
|
||||
|
||||
func msgClearCart() *messages.ClearCartRequest {
|
||||
return &messages.ClearCartRequest{}
|
||||
}
|
||||
|
||||
func msgAddVoucher(code string, value int64, rules ...string) *messages.AddVoucher {
|
||||
return &messages.AddVoucher{Code: code, Value: value, VoucherRules: rules}
|
||||
}
|
||||
|
||||
func msgRemoveVoucher(id uint32) *messages.RemoveVoucher {
|
||||
return &messages.RemoveVoucher{Id: id}
|
||||
}
|
||||
|
||||
func msgInitializeCheckout(orderId, status string, inProgress bool) *messages.InitializeCheckout {
|
||||
return &messages.InitializeCheckout{OrderId: orderId, Status: status, PaymentInProgress: inProgress}
|
||||
}
|
||||
|
||||
func msgOrderCreated(orderId, status string) *messages.OrderCreated {
|
||||
return &messages.OrderCreated{OrderId: orderId, Status: status}
|
||||
}
|
||||
|
||||
func msgSetUserId(userId string) *messages.SetUserId {
|
||||
return &messages.SetUserId{UserId: userId}
|
||||
}
|
||||
|
||||
func msgLineItemMarking(id uint32, typ uint32, marking string) *messages.LineItemMarking {
|
||||
return &messages.LineItemMarking{Id: id, Type: typ, Marking: marking}
|
||||
}
|
||||
|
||||
func msgRemoveLineItemMarking(id uint32) *messages.RemoveLineItemMarking {
|
||||
return &messages.RemoveLineItemMarking{Id: id}
|
||||
}
|
||||
|
||||
func msgSubscriptionAdded(itemId uint32, detailsId, orderRef string) *messages.SubscriptionAdded {
|
||||
return &messages.SubscriptionAdded{ItemId: itemId, DetailsId: detailsId, OrderReference: orderRef}
|
||||
}
|
||||
|
||||
// func msgPaymentDeclined(message, code string) *messages.PaymentDeclined {
|
||||
// return &messages.PaymentDeclined{Message: message, Code: &code}
|
||||
// }
|
||||
|
||||
func msgConfirmationViewed() *messages.ConfirmationViewed {
|
||||
return &messages.ConfirmationViewed{}
|
||||
}
|
||||
|
||||
func msgCreateCheckoutOrder(terms, country string) *messages.CreateCheckoutOrder {
|
||||
return &messages.CreateCheckoutOrder{Terms: terms, Country: country}
|
||||
}
|
||||
|
||||
func msgAddGiftcard(value int64, deliveryDate, recipient, recipientType, message string, designConfig *anypb.Any) *messages.AddGiftcard {
|
||||
return &messages.AddGiftcard{
|
||||
Giftcard: &messages.GiftcardItem{
|
||||
Value: value,
|
||||
DeliveryDate: deliveryDate,
|
||||
Recipient: recipient,
|
||||
RecipientType: recipientType,
|
||||
Message: message,
|
||||
DesignConfig: designConfig,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func msgRemoveGiftcard(id uint32) *messages.RemoveGiftcard {
|
||||
return &messages.RemoveGiftcard{Id: id}
|
||||
}
|
||||
|
||||
func ptr[T any](v T) *T { return &v }
|
||||
|
||||
// ----------------------
|
||||
// Apply helpers
|
||||
// ----------------------
|
||||
|
||||
func applyOne(t *testing.T, reg actor.MutationRegistry, g *CartGrain, msg proto.Message) actor.ApplyResult {
|
||||
t.Helper()
|
||||
results, err := reg.Apply(context.Background(), g, msg)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected registry-level error applying %T: %v", msg, err)
|
||||
}
|
||||
if len(results) != 1 {
|
||||
t.Fatalf("expected exactly one ApplyResult, got %d", len(results))
|
||||
}
|
||||
return results[0]
|
||||
}
|
||||
|
||||
// Expect success (nil error inside ApplyResult).
|
||||
func applyOK(t *testing.T, reg actor.MutationRegistry, g *CartGrain, msg proto.Message) {
|
||||
t.Helper()
|
||||
res := applyOne(t, reg, g, msg)
|
||||
if res.Error != nil {
|
||||
t.Fatalf("expected mutation %s (%T) to succeed, got error: %v", res.Type, msg, res.Error)
|
||||
}
|
||||
}
|
||||
|
||||
// Expect an error matching substring.
|
||||
func applyErrorContains(t *testing.T, reg actor.MutationRegistry, g *CartGrain, msg proto.Message, substr string) {
|
||||
t.Helper()
|
||||
res := applyOne(t, reg, g, msg)
|
||||
if res.Error == nil {
|
||||
t.Fatalf("expected error applying %T, got nil", msg)
|
||||
}
|
||||
if substr != "" && !strings.Contains(res.Error.Error(), substr) {
|
||||
t.Fatalf("error mismatch, want substring %q got %q", substr, res.Error.Error())
|
||||
}
|
||||
}
|
||||
|
||||
// ----------------------
|
||||
// Tests
|
||||
// ----------------------
|
||||
|
||||
func TestMutationRegistryCoverage(t *testing.T) {
|
||||
reg := newRegistry()
|
||||
|
||||
expected := []string{
|
||||
"AddItem",
|
||||
"ChangeQuantity",
|
||||
"RemoveItem",
|
||||
"InitializeCheckout",
|
||||
"OrderCreated",
|
||||
"RemoveDelivery",
|
||||
"SetDelivery",
|
||||
"SetPickupPoint",
|
||||
"ClearCartRequest",
|
||||
"AddVoucher",
|
||||
"RemoveVoucher",
|
||||
"UpsertSubscriptionDetails",
|
||||
"InventoryReserved",
|
||||
"PreConditionFailed",
|
||||
"SetUserId",
|
||||
"LineItemMarking",
|
||||
"RemoveLineItemMarking",
|
||||
"SubscriptionAdded",
|
||||
"PaymentDeclined",
|
||||
"ConfirmationViewed",
|
||||
"CreateCheckoutOrder",
|
||||
"AddGiftcard",
|
||||
"RemoveGiftcard",
|
||||
}
|
||||
|
||||
names := reg.(*actor.ProtoMutationRegistry).RegisteredMutations()
|
||||
for _, want := range expected {
|
||||
if !slices.Contains(names, want) {
|
||||
t.Fatalf("registry missing mutation %s; got %v", want, names)
|
||||
}
|
||||
}
|
||||
|
||||
// Create() by name returns correct concrete type.
|
||||
for _, name := range expected {
|
||||
msg, ok := reg.Create(name)
|
||||
if !ok {
|
||||
t.Fatalf("Create failed for %s", name)
|
||||
}
|
||||
rt := reflect.TypeOf(msg)
|
||||
if rt.Kind() == reflect.Ptr {
|
||||
rt = rt.Elem()
|
||||
}
|
||||
if rt.Name() != name {
|
||||
t.Fatalf("Create(%s) returned wrong type %s", name, rt.Name())
|
||||
}
|
||||
}
|
||||
|
||||
// Unregistered create
|
||||
if m, ok := reg.Create("DoesNotExist"); ok || m != nil {
|
||||
t.Fatalf("Create should fail for unknown; got (%T,%v)", m, ok)
|
||||
}
|
||||
|
||||
// GetTypeName sanity
|
||||
add := &messages.AddItem{}
|
||||
nm, ok := reg.GetTypeName(add)
|
||||
if !ok || nm != "AddItem" {
|
||||
t.Fatalf("GetTypeName failed for AddItem, got (%q,%v)", nm, ok)
|
||||
}
|
||||
|
||||
// Apply unregistered message -> should return error
|
||||
results, err := reg.Apply(context.Background(), newTestGrain(), &messages.Noop{})
|
||||
if err == nil {
|
||||
t.Fatalf("expected error for unregistered mutation")
|
||||
}
|
||||
if len(results) != 1 || results[0].Error == nil || results[0].Error != actor.ErrMutationNotRegistered {
|
||||
t.Fatalf("expected ApplyResult with ErrMutationNotRegistered, got %#v", results)
|
||||
}
|
||||
}
|
||||
|
||||
func TestAddItemAndMerging(t *testing.T) {
|
||||
reg := newRegistry()
|
||||
g := newTestGrain()
|
||||
|
||||
// Merge scenario (same SKU + same store pointer)
|
||||
add1 := msgAddItem("SKU-1", 1000, 2, nil)
|
||||
applyOK(t, reg, g, add1)
|
||||
|
||||
if len(g.Items) != 1 || g.Items[0].Quantity != 2 {
|
||||
t.Fatalf("expected first item added; items=%d qty=%d", len(g.Items), g.Items[0].Quantity)
|
||||
}
|
||||
|
||||
applyOK(t, reg, g, msgAddItem("SKU-1", 1000, 3, nil)) // should merge
|
||||
if len(g.Items) != 1 || g.Items[0].Quantity != 5 {
|
||||
t.Fatalf("expected merge quantity=5 items=%d qty=%d", len(g.Items), g.Items[0].Quantity)
|
||||
}
|
||||
|
||||
// Different store pointer -> new line
|
||||
store := "S1"
|
||||
applyOK(t, reg, g, msgAddItem("SKU-1", 1000, 1, &store))
|
||||
if len(g.Items) != 2 {
|
||||
t.Fatalf("expected second line for different store pointer; items=%d", len(g.Items))
|
||||
}
|
||||
|
||||
// Same store pointer & SKU -> merge with second line
|
||||
applyOK(t, reg, g, msgAddItem("SKU-1", 1000, 4, &store))
|
||||
if len(g.Items) != 2 || g.Items[1].Quantity != 5 {
|
||||
t.Fatalf("expected merge on second line; items=%d second.qty=%d", len(g.Items), g.Items[1].Quantity)
|
||||
}
|
||||
|
||||
// Invalid quantity
|
||||
applyErrorContains(t, reg, g, msgAddItem("BAD", 1000, 0, nil), "invalid quantity")
|
||||
}
|
||||
|
||||
func TestChangeQuantityBehavior(t *testing.T) {
|
||||
reg := newRegistry()
|
||||
g := newTestGrain()
|
||||
|
||||
applyOK(t, reg, g, msgAddItem("A", 1500, 2, nil))
|
||||
id := g.Items[0].Id
|
||||
|
||||
// Increase quantity
|
||||
applyOK(t, reg, g, msgChangeQty(id, 5))
|
||||
if g.Items[0].Quantity != 5 {
|
||||
t.Fatalf("quantity not updated expected=5 got=%d", g.Items[0].Quantity)
|
||||
}
|
||||
|
||||
// Remove item by setting <=0
|
||||
applyOK(t, reg, g, msgChangeQty(id, 0))
|
||||
if len(g.Items) != 0 {
|
||||
t.Fatalf("expected item removed; items=%d", len(g.Items))
|
||||
}
|
||||
|
||||
// Not found
|
||||
applyErrorContains(t, reg, g, msgChangeQty(9999, 1), "not found")
|
||||
}
|
||||
|
||||
func TestRemoveItemBehavior(t *testing.T) {
|
||||
reg := newRegistry()
|
||||
g := newTestGrain()
|
||||
|
||||
applyOK(t, reg, g, msgAddItem("X", 1200, 1, nil))
|
||||
id := g.Items[0].Id
|
||||
|
||||
applyOK(t, reg, g, msgRemoveItem(id))
|
||||
if len(g.Items) != 0 {
|
||||
t.Fatalf("expected item removed; items=%d", len(g.Items))
|
||||
}
|
||||
|
||||
applyErrorContains(t, reg, g, msgRemoveItem(id), "not found")
|
||||
}
|
||||
|
||||
func TestDeliveryMutations(t *testing.T) {
|
||||
reg := newRegistry()
|
||||
g := newTestGrain()
|
||||
|
||||
applyOK(t, reg, g, msgAddItem("D1", 1000, 1, nil))
|
||||
applyOK(t, reg, g, msgAddItem("D2", 2000, 1, nil))
|
||||
i1 := g.Items[0].Id
|
||||
|
||||
// Explicit items
|
||||
applyOK(t, reg, g, msgSetDelivery("POSTNORD", i1))
|
||||
if len(g.Deliveries) != 1 || len(g.Deliveries[0].Items) != 1 || g.Deliveries[0].Items[0] != i1 {
|
||||
t.Fatalf("delivery not created as expected: %+v", g.Deliveries)
|
||||
}
|
||||
|
||||
// Attempt to attach an already-delivered item
|
||||
applyErrorContains(t, reg, g, msgSetDelivery("POSTNORD", i1), "already has a delivery")
|
||||
|
||||
// Attach remaining item via empty list (auto include items without delivery)
|
||||
applyOK(t, reg, g, msgSetDelivery("DHL"))
|
||||
if len(g.Deliveries) != 2 {
|
||||
t.Fatalf("expected second delivery; deliveries=%d", len(g.Deliveries))
|
||||
}
|
||||
|
||||
// Non-existent item
|
||||
applyErrorContains(t, reg, g, msgSetDelivery("UPS", 99999), "not found")
|
||||
|
||||
// No eligible items left
|
||||
applyErrorContains(t, reg, g, msgSetDelivery("UPS"), "no eligible items")
|
||||
|
||||
// Set pickup point on first delivery
|
||||
did := g.Deliveries[0].Id
|
||||
applyOK(t, reg, g, msgSetPickupPoint(did, "PP1"))
|
||||
if g.Deliveries[0].PickupPoint == nil || g.Deliveries[0].PickupPoint.Id != "PP1" {
|
||||
t.Fatalf("pickup point not set correctly: %+v", g.Deliveries[0].PickupPoint)
|
||||
}
|
||||
|
||||
// Bad delivery id
|
||||
applyErrorContains(t, reg, g, msgSetPickupPoint(9999, "PPX"), "delivery id")
|
||||
|
||||
// Remove delivery
|
||||
applyOK(t, reg, g, &messages.RemoveDelivery{Id: did})
|
||||
if len(g.Deliveries) != 1 || g.Deliveries[0].Id == did {
|
||||
t.Fatalf("expected first delivery removed, remaining: %+v", g.Deliveries)
|
||||
}
|
||||
|
||||
// Remove delivery not found
|
||||
applyErrorContains(t, reg, g, &messages.RemoveDelivery{Id: did}, "not found")
|
||||
}
|
||||
|
||||
func TestClearCart(t *testing.T) {
|
||||
reg := newRegistry()
|
||||
g := newTestGrain()
|
||||
|
||||
applyOK(t, reg, g, msgAddItem("X", 1000, 2, nil))
|
||||
applyOK(t, reg, g, msgSetDelivery("P", g.Items[0].Id))
|
||||
|
||||
applyOK(t, reg, g, msgClearCart())
|
||||
|
||||
if len(g.Items) != 0 || len(g.Deliveries) != 0 {
|
||||
t.Fatalf("expected cart cleared; items=%d deliveries=%d", len(g.Items), len(g.Deliveries))
|
||||
}
|
||||
}
|
||||
|
||||
func TestVoucherMutations(t *testing.T) {
|
||||
reg := newRegistry()
|
||||
g := newTestGrain()
|
||||
|
||||
applyOK(t, reg, g, msgAddItem("VOUCH", 10000, 1, nil))
|
||||
applyOK(t, reg, g, msgAddVoucher("PROMO", 5000))
|
||||
|
||||
if len(g.Vouchers) != 1 {
|
||||
t.Fatalf("voucher not stored")
|
||||
}
|
||||
if g.TotalDiscount.IncVat != 5000 {
|
||||
t.Fatalf("expected discount 5000 got %d", g.TotalDiscount.IncVat)
|
||||
}
|
||||
if g.TotalPrice.IncVat != 5000 {
|
||||
t.Fatalf("expected total price 5000 got %d", g.TotalPrice.IncVat)
|
||||
}
|
||||
|
||||
// Duplicate voucher code
|
||||
applyErrorContains(t, reg, g, msgAddVoucher("PROMO", 1000), "already applied")
|
||||
|
||||
// Add a large voucher (should not apply because value > total price)
|
||||
applyOK(t, reg, g, msgAddVoucher("BIG", 100000))
|
||||
if len(g.Vouchers) != 2 {
|
||||
t.Fatalf("expected second voucher stored")
|
||||
}
|
||||
if g.TotalDiscount.IncVat != 5000 || g.TotalPrice.IncVat != 5000 {
|
||||
t.Fatalf("large voucher incorrectly applied discount=%d total=%d",
|
||||
g.TotalDiscount.IncVat, g.TotalPrice.IncVat)
|
||||
}
|
||||
|
||||
// Remove existing voucher
|
||||
firstId := g.Vouchers[0].Id
|
||||
applyOK(t, reg, g, msgRemoveVoucher(firstId))
|
||||
|
||||
if slices.ContainsFunc(g.Vouchers, func(v *Voucher) bool { return v.Id == firstId }) {
|
||||
t.Fatalf("voucher id %d not removed", firstId)
|
||||
}
|
||||
// After removing PROMO, BIG remains but is not applied (exceeds price)
|
||||
if g.TotalDiscount.IncVat != 0 || g.TotalPrice.IncVat != 10000 {
|
||||
t.Fatalf("totals incorrect after removal discount=%d total=%d",
|
||||
g.TotalDiscount.IncVat, g.TotalPrice.IncVat)
|
||||
}
|
||||
|
||||
// Remove not applied
|
||||
applyErrorContains(t, reg, g, msgRemoveVoucher(firstId), "not applied")
|
||||
}
|
||||
|
||||
// func TestCheckoutMutations(t *testing.T) {
|
||||
// reg := newRegistry()
|
||||
// g := newTestGrain()
|
||||
|
||||
// applyOK(t, reg, g, msgInitializeCheckout("ORD-1", "PENDING", true))
|
||||
// if g.OrderReference != "ORD-1" || g.PaymentStatus != "PENDING" || !g.PaymentInProgress {
|
||||
// t.Fatalf("initialize checkout failed: ref=%s status=%s inProgress=%v",
|
||||
// g.OrderReference, g.PaymentStatus, g.PaymentInProgress)
|
||||
// }
|
||||
|
||||
// applyOK(t, reg, g, msgOrderCreated("ORD-1", "COMPLETED"))
|
||||
// if g.OrderReference != "ORD-1" || g.PaymentStatus != "COMPLETED" || g.PaymentInProgress {
|
||||
// t.Fatalf("order created mutation failed: ref=%s status=%s inProgress=%v",
|
||||
// g.OrderReference, g.PaymentStatus, g.PaymentInProgress)
|
||||
// }
|
||||
|
||||
// applyErrorContains(t, reg, g, msgInitializeCheckout("", "X", true), "missing orderId")
|
||||
// applyErrorContains(t, reg, g, msgOrderCreated("", "X"), "missing orderId")
|
||||
// }
|
||||
|
||||
func TestSubscriptionDetailsMutation(t *testing.T) {
|
||||
reg := newRegistry()
|
||||
g := newTestGrain()
|
||||
|
||||
// Upsert new (Id == nil)
|
||||
msgNew := &messages.UpsertSubscriptionDetails{
|
||||
OfferingCode: "OFF1",
|
||||
SigningType: "TYPE1",
|
||||
}
|
||||
applyOK(t, reg, g, msgNew)
|
||||
if len(g.SubscriptionDetails) != 1 {
|
||||
t.Fatalf("expected one subscription detail; got=%d", len(g.SubscriptionDetails))
|
||||
}
|
||||
|
||||
// Capture created id
|
||||
var createdId string
|
||||
for k := range g.SubscriptionDetails {
|
||||
createdId = k
|
||||
}
|
||||
|
||||
// Update existing
|
||||
msgUpdate := &messages.UpsertSubscriptionDetails{
|
||||
Id: &createdId,
|
||||
OfferingCode: "OFF2",
|
||||
SigningType: "TYPE2",
|
||||
}
|
||||
applyOK(t, reg, g, msgUpdate)
|
||||
if g.SubscriptionDetails[createdId].OfferingCode != "OFF2" ||
|
||||
g.SubscriptionDetails[createdId].SigningType != "TYPE2" {
|
||||
t.Fatalf("subscription details not updated: %+v", g.SubscriptionDetails[createdId])
|
||||
}
|
||||
|
||||
// Update non-existent
|
||||
badId := "NON_EXISTENT"
|
||||
applyErrorContains(t, reg, g, &messages.UpsertSubscriptionDetails{Id: &badId}, "not found")
|
||||
|
||||
// Nil mutation should be ignored and produce zero results.
|
||||
resultsNil, errNil := reg.Apply(context.Background(), g, (*messages.UpsertSubscriptionDetails)(nil))
|
||||
if errNil != nil {
|
||||
t.Fatalf("unexpected error for nil mutation element: %v", errNil)
|
||||
}
|
||||
if len(resultsNil) != 0 {
|
||||
t.Fatalf("expected zero results for nil mutation, got %d", len(resultsNil))
|
||||
}
|
||||
}
|
||||
|
||||
// Ensure registry Apply handles nil grain and nil message defensive errors consistently.
|
||||
func TestRegistryDefensiveErrors(t *testing.T) {
|
||||
reg := newRegistry()
|
||||
g := newTestGrain()
|
||||
|
||||
// Nil grain
|
||||
results, err := reg.Apply(context.Background(), nil, &messages.AddItem{})
|
||||
if err == nil {
|
||||
t.Fatalf("expected error for nil grain")
|
||||
}
|
||||
if len(results) != 0 {
|
||||
t.Fatalf("expected no results for nil grain")
|
||||
}
|
||||
|
||||
// Nil message slice
|
||||
results, _ = reg.Apply(context.Background(), g, nil)
|
||||
|
||||
if len(results) != 0 {
|
||||
t.Fatalf("expected no results when message slice nil")
|
||||
}
|
||||
}
|
||||
|
||||
type SubscriptionDetailsRequest struct {
|
||||
Id *string `json:"id,omitempty"`
|
||||
OfferingCode string `json:"offeringCode,omitempty"`
|
||||
SigningType string `json:"signingType,omitempty"`
|
||||
Data json.RawMessage `json:"data,omitempty"`
|
||||
}
|
||||
|
||||
func (sd *SubscriptionDetailsRequest) ToMessage() *messages.UpsertSubscriptionDetails {
|
||||
return &messages.UpsertSubscriptionDetails{
|
||||
Id: sd.Id,
|
||||
OfferingCode: sd.OfferingCode,
|
||||
SigningType: sd.SigningType,
|
||||
Data: &anypb.Any{Value: sd.Data},
|
||||
}
|
||||
}
|
||||
|
||||
func TestSubscriptionDetailsJSONValidation(t *testing.T) {
|
||||
reg := newRegistry()
|
||||
g := newTestGrain()
|
||||
|
||||
// Valid JSON on create
|
||||
jsonStr := `{"offeringCode": "OFFJSON", "signingType": "TYPEJSON", "data": {"value":"test","a":1}}`
|
||||
var validCreate SubscriptionDetailsRequest
|
||||
if err := json.Unmarshal([]byte(jsonStr), &validCreate); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
applyOK(t, reg, g, validCreate.ToMessage())
|
||||
if len(g.SubscriptionDetails) != 1 {
|
||||
t.Fatalf("expected one subscription detail after valid create, got %d", len(g.SubscriptionDetails))
|
||||
}
|
||||
var id string
|
||||
for k := range g.SubscriptionDetails {
|
||||
id = k
|
||||
}
|
||||
if string(g.SubscriptionDetails[id].Meta) != `{"value":"test","a":1}` {
|
||||
t.Fatalf("expected meta stored as valid json, got %s", string(g.SubscriptionDetails[id].Meta))
|
||||
}
|
||||
|
||||
// Update with valid JSON replaces meta
|
||||
jsonStr2 := fmt.Sprintf(`{"id": "%s", "data": {"value": "eyJjaGFuZ2VkIjoxMjN9"}}`, id)
|
||||
var updateValid messages.UpsertSubscriptionDetails
|
||||
if err := json.Unmarshal([]byte(jsonStr2), &updateValid); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
applyOK(t, reg, g, &updateValid)
|
||||
if string(g.SubscriptionDetails[id].Meta) != `{"changed":123}` {
|
||||
t.Fatalf("expected meta updated to new json, got %s", string(g.SubscriptionDetails[id].Meta))
|
||||
}
|
||||
|
||||
// Invalid JSON on create
|
||||
jsonStr3 := `{"offeringCode": "BAD", "signingType": "TYPE", "data": {"value": "eyJicm9rZW4iO30="}}`
|
||||
var invalidCreate messages.UpsertSubscriptionDetails
|
||||
if err := json.Unmarshal([]byte(jsonStr3), &invalidCreate); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
res := applyOne(t, reg, g, &invalidCreate)
|
||||
if res.Error == nil || !strings.Contains(res.Error.Error(), "invalid json") {
|
||||
t.Fatalf("expected invalid json error on create, got %v", res.Error)
|
||||
}
|
||||
|
||||
// Invalid JSON on update
|
||||
jsonStr4 := fmt.Sprintf(`{"id": "%s", "data": {"value": "e29vcHM="}}`, id)
|
||||
var badUpdate messages.UpsertSubscriptionDetails
|
||||
if err := json.Unmarshal([]byte(jsonStr4), &badUpdate); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
res2 := applyOne(t, reg, g, &badUpdate)
|
||||
if res2.Error == nil || !strings.Contains(res2.Error.Error(), "invalid json") {
|
||||
t.Fatalf("expected invalid json error on update, got %v", res2.Error)
|
||||
}
|
||||
|
||||
// Empty Data should not overwrite existing meta
|
||||
jsonStr5 := fmt.Sprintf(`{"id": "%s"}`, id)
|
||||
var emptyUpdate messages.UpsertSubscriptionDetails
|
||||
if err := json.Unmarshal([]byte(jsonStr5), &emptyUpdate); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
applyOK(t, reg, g, &emptyUpdate)
|
||||
if string(g.SubscriptionDetails[id].Meta) != `{"changed":123}` {
|
||||
t.Fatalf("empty update should not change meta, got %s", string(g.SubscriptionDetails[id].Meta))
|
||||
}
|
||||
}
|
||||
|
||||
func TestSetUserId(t *testing.T) {
|
||||
reg := newRegistry()
|
||||
g := newTestGrain()
|
||||
|
||||
applyOK(t, reg, g, msgSetUserId("user123"))
|
||||
if g.userId != "user123" {
|
||||
t.Fatalf("expected userId=user123, got %s", g.userId)
|
||||
}
|
||||
|
||||
applyErrorContains(t, reg, g, msgSetUserId(""), "cannot be empty")
|
||||
}
|
||||
|
||||
func TestLineItemMarking(t *testing.T) {
|
||||
reg := newRegistry()
|
||||
g := newTestGrain()
|
||||
|
||||
applyOK(t, reg, g, msgAddItem("MARK", 1000, 1, nil))
|
||||
id := g.Items[0].Id
|
||||
|
||||
applyOK(t, reg, g, msgLineItemMarking(id, 1, "Gift message"))
|
||||
if g.Items[0].Marking == nil || g.Items[0].Marking.Type != 1 || g.Items[0].Marking.Text != "Gift message" {
|
||||
t.Fatalf("marking not set correctly: %+v", g.Items[0].Marking)
|
||||
}
|
||||
|
||||
applyErrorContains(t, reg, g, msgLineItemMarking(9999, 2, "Test"), "not found")
|
||||
}
|
||||
|
||||
func TestRemoveLineItemMarking(t *testing.T) {
|
||||
reg := newRegistry()
|
||||
g := newTestGrain()
|
||||
|
||||
applyOK(t, reg, g, msgAddItem("REMOVE", 1000, 1, nil))
|
||||
id := g.Items[0].Id
|
||||
|
||||
// First set a marking
|
||||
applyOK(t, reg, g, msgLineItemMarking(id, 1, "Test marking"))
|
||||
if g.Items[0].Marking == nil || g.Items[0].Marking.Text != "Test marking" {
|
||||
t.Fatalf("marking not set")
|
||||
}
|
||||
|
||||
// Now remove it
|
||||
applyOK(t, reg, g, msgRemoveLineItemMarking(id))
|
||||
if g.Items[0].Marking != nil {
|
||||
t.Fatalf("marking not removed")
|
||||
}
|
||||
|
||||
applyErrorContains(t, reg, g, msgRemoveLineItemMarking(9999), "not found")
|
||||
}
|
||||
|
||||
func TestSubscriptionAdded(t *testing.T) {
|
||||
reg := newRegistry()
|
||||
g := newTestGrain()
|
||||
|
||||
applyOK(t, reg, g, msgAddItem("SUB", 1000, 1, nil))
|
||||
id := g.Items[0].Id
|
||||
|
||||
applyOK(t, reg, g, msgSubscriptionAdded(id, "det123", "ord456"))
|
||||
if g.Items[0].SubscriptionDetailsId != "det123" || g.Items[0].OrderReference != "ord456" || !g.Items[0].IsSubscribed {
|
||||
t.Fatalf("subscription not added: detailsId=%s orderRef=%s isSubscribed=%v",
|
||||
g.Items[0].SubscriptionDetailsId, g.Items[0].OrderReference, g.Items[0].IsSubscribed)
|
||||
}
|
||||
|
||||
applyErrorContains(t, reg, g, msgSubscriptionAdded(9999, "", ""), "not found")
|
||||
}
|
||||
|
||||
// func TestPaymentDeclined(t *testing.T) {
|
||||
// reg := newRegistry()
|
||||
// g := newTestGrain()
|
||||
|
||||
// applyOK(t, reg, g, msgPaymentDeclined("Payment failed due to insufficient funds", "INSUFFICIENT_FUNDS"))
|
||||
|
||||
// if g.PaymentStatus != "declined" || g.CheckoutOrderId != "" {
|
||||
// t.Fatalf("payment declined not handled: status=%s checkoutId=%s", g.PaymentStatus, g.CheckoutOrderId)
|
||||
// }
|
||||
// if len(g.PaymentDeclinedNotices) != 1 {
|
||||
// t.Fatalf("expected 1 notice, got %d", len(g.PaymentDeclinedNotices))
|
||||
// }
|
||||
// notice := g.PaymentDeclinedNotices[0]
|
||||
// if notice.Message != "Payment failed due to insufficient funds" {
|
||||
// t.Fatalf("notice message not set correctly: %s", notice.Message)
|
||||
// }
|
||||
// if notice.Code == nil || *notice.Code != "INSUFFICIENT_FUNDS" {
|
||||
// t.Fatalf("notice code not set correctly: %v", notice.Code)
|
||||
// }
|
||||
// if notice.Timestamp.IsZero() {
|
||||
// t.Fatalf("notice timestamp not set")
|
||||
// }
|
||||
// }
|
||||
|
||||
func TestConfirmationViewed(t *testing.T) {
|
||||
reg := newRegistry()
|
||||
g := newTestGrain()
|
||||
|
||||
// Initial state
|
||||
if g.Confirmation != nil {
|
||||
t.Fatalf("confirmation should be nil, got %v", g.Confirmation)
|
||||
}
|
||||
|
||||
// First view
|
||||
applyOK(t, reg, g, msgConfirmationViewed())
|
||||
if g.Confirmation.ViewCount != 1 {
|
||||
t.Fatalf("view count should be 1, got %d", g.Confirmation.ViewCount)
|
||||
}
|
||||
if g.Confirmation.LastViewedAt.IsZero() {
|
||||
t.Fatalf("ConfirmationLastViewedAt not set")
|
||||
}
|
||||
firstTime := g.Confirmation.LastViewedAt
|
||||
|
||||
// Second view
|
||||
applyOK(t, reg, g, msgConfirmationViewed())
|
||||
if g.Confirmation.ViewCount != 2 {
|
||||
t.Fatalf("view count should be 2, got %d", g.Confirmation.ViewCount)
|
||||
}
|
||||
if g.Confirmation.LastViewedAt == firstTime {
|
||||
t.Fatalf("ConfirmationLastViewedAt should have updated")
|
||||
}
|
||||
}
|
||||
|
||||
func TestCreateCheckoutOrder(t *testing.T) {
|
||||
reg := newRegistry()
|
||||
g := newTestGrain()
|
||||
|
||||
applyOK(t, reg, g, msgAddItem("CHECKOUT", 1000, 1, nil))
|
||||
|
||||
applyOK(t, reg, g, msgCreateCheckoutOrder("accepted", "SE"))
|
||||
// if g.CheckoutOrderId == "" || g.CheckoutStatus != "pending" || g.CheckoutCountry != "SE" {
|
||||
// t.Fatalf("checkout order not created: id=%s status=%s country=%s",
|
||||
// g.CheckoutOrderId, g.CheckoutStatus, g.CheckoutCountry)
|
||||
// }
|
||||
|
||||
// Empty cart
|
||||
g2 := newTestGrain()
|
||||
applyErrorContains(t, reg, g2, msgCreateCheckoutOrder("accepted", ""), "empty cart")
|
||||
|
||||
// Terms not accepted
|
||||
applyErrorContains(t, reg, g, msgCreateCheckoutOrder("no", ""), "terms must be accepted")
|
||||
}
|
||||
|
||||
func TestAddGiftcard(t *testing.T) {
|
||||
reg := newRegistry()
|
||||
g := newTestGrain()
|
||||
|
||||
designConfig, _ := anypb.New(&messages.AddItem{}) // example
|
||||
applyOK(t, reg, g, msgAddGiftcard(5000, "2023-12-25", "John", "email", "Happy Birthday!", designConfig))
|
||||
|
||||
if len(g.Giftcards) != 1 {
|
||||
t.Fatalf("expected 1 giftcard, got %d", len(g.Giftcards))
|
||||
}
|
||||
gc := g.Giftcards[0]
|
||||
if gc.Value.IncVat != 5000 || gc.DeliveryDate != "2023-12-25" || gc.Recipient != "John" || gc.RecipientType != "email" || gc.Message != "Happy Birthday!" {
|
||||
t.Fatalf("giftcard not set correctly: %+v", gc)
|
||||
}
|
||||
if g.TotalPrice.IncVat != 5000 {
|
||||
t.Fatalf("total price not updated, got %d", g.TotalPrice.IncVat)
|
||||
}
|
||||
|
||||
// Test invalid value
|
||||
applyErrorContains(t, reg, g, msgAddGiftcard(0, "", "", "", "", nil), "must be positive")
|
||||
}
|
||||
|
||||
func TestRemoveGiftcard(t *testing.T) {
|
||||
reg := newRegistry()
|
||||
g := newTestGrain()
|
||||
|
||||
applyOK(t, reg, g, msgAddGiftcard(1000, "2023-01-01", "Jane", "sms", "Cheers!", nil))
|
||||
id := g.Giftcards[0].Id
|
||||
|
||||
applyOK(t, reg, g, msgRemoveGiftcard(id))
|
||||
if len(g.Giftcards) != 0 {
|
||||
t.Fatalf("giftcard not removed")
|
||||
}
|
||||
if g.TotalPrice.IncVat != 0 {
|
||||
t.Fatalf("total price not updated after removal, got %d", g.TotalPrice.IncVat)
|
||||
}
|
||||
|
||||
applyErrorContains(t, reg, g, msgRemoveGiftcard(id), "not found")
|
||||
}
|
||||
@@ -4,16 +4,14 @@ import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
|
||||
messages "git.k6n.net/go-cart-actor/pkg/messages"
|
||||
messages "git.k6n.net/go-cart-actor/proto/cart"
|
||||
)
|
||||
|
||||
func UpsertSubscriptionDetails(g *CartGrain, m *messages.UpsertSubscriptionDetails) error {
|
||||
if m == nil {
|
||||
return nil
|
||||
}
|
||||
if g.PaymentInProgress > 0 {
|
||||
return ErrPaymentInProgress
|
||||
}
|
||||
|
||||
metaBytes := m.Data.GetValue()
|
||||
|
||||
// Create new subscription details when Id is nil.
|
||||
|
||||
184
pkg/checkout/checkout-grain.go
Normal file
184
pkg/checkout/checkout-grain.go
Normal file
@@ -0,0 +1,184 @@
|
||||
package checkout
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"git.k6n.net/go-cart-actor/pkg/cart"
|
||||
)
|
||||
|
||||
// CheckoutId is the same as CartId for simplicity
|
||||
type CheckoutId = cart.CartId
|
||||
|
||||
type PickupPoint struct {
|
||||
DeliveryId uint32 `json:"deliveryId,omitempty"`
|
||||
Id string `json:"id"`
|
||||
Name *string `json:"name,omitempty"`
|
||||
Address *string `json:"address,omitempty"`
|
||||
City *string `json:"city,omitempty"`
|
||||
Zip *string `json:"zip,omitempty"`
|
||||
Country *string `json:"country,omitempty"`
|
||||
}
|
||||
|
||||
type CheckoutDelivery struct {
|
||||
Id uint32 `json:"id"`
|
||||
Provider string `json:"provider"`
|
||||
Price cart.Price `json:"price"`
|
||||
Items []uint32 `json:"items"`
|
||||
PickupPoint *PickupPoint `json:"pickupPoint,omitempty"`
|
||||
}
|
||||
type PaymentStatus string
|
||||
type CheckoutPaymentStatus PaymentStatus
|
||||
|
||||
const (
|
||||
PaymentStatusPending PaymentStatus = "pending"
|
||||
PaymentStatusFailed PaymentStatus = "failed"
|
||||
PaymentStatusSuccess PaymentStatus = "success"
|
||||
CheckoutPaymentStatusPending CheckoutPaymentStatus = "pending"
|
||||
CheckoutPaymentStatusFailed CheckoutPaymentStatus = "failed"
|
||||
CheckoutPaymentStatusSuccess CheckoutPaymentStatus = "success"
|
||||
CheckoutPaymentStatusCancelled CheckoutPaymentStatus = "partial"
|
||||
)
|
||||
|
||||
type Payment struct {
|
||||
PaymentId string `json:"paymentId"`
|
||||
Status PaymentStatus `json:"status"`
|
||||
Amount int64 `json:"amount"`
|
||||
Currency string `json:"currency"`
|
||||
Provider string `json:"provider,omitempty"`
|
||||
Method *string `json:"method,omitempty"`
|
||||
Events []*PaymentEvent `json:"events,omitempty"`
|
||||
ProcessorReference *string `json:"processorReference,omitempty"`
|
||||
StartedAt *time.Time `json:"startedAt,omitempty"`
|
||||
CompletedAt *time.Time `json:"completedAt,omitempty"`
|
||||
}
|
||||
|
||||
type PaymentEvent struct {
|
||||
Name string `json:"name"`
|
||||
Success bool `json:"success"`
|
||||
Data json.RawMessage `json:"data"`
|
||||
}
|
||||
|
||||
func (p *Payment) IsSettled() bool {
|
||||
if p == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
switch p.Status {
|
||||
case PaymentStatusSuccess:
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
type ConfirmationStatus struct {
|
||||
Code *string `json:"code,omitempty"`
|
||||
ViewCount int `json:"viewCount"`
|
||||
LastViewedAt time.Time `json:"lastViewedAt"`
|
||||
}
|
||||
|
||||
type CheckoutGrain struct {
|
||||
mu sync.RWMutex
|
||||
lastDeliveryId uint32
|
||||
lastGiftcardId uint32
|
||||
lastAccess time.Time
|
||||
lastChange time.Time
|
||||
Version uint32
|
||||
Id CheckoutId `json:"id"`
|
||||
CartId cart.CartId `json:"cartId"`
|
||||
CartVersion uint64 `json:"cartVersion"`
|
||||
CartState *cart.CartGrain `json:"cartState"` // snapshot of items
|
||||
CartTotalPrice *cart.Price `json:"cartTotalPrice"`
|
||||
OrderId *string `json:"orderId"`
|
||||
Deliveries []*CheckoutDelivery `json:"deliveries,omitempty"`
|
||||
PaymentInProgress uint16 `json:"paymentInProgress"`
|
||||
InventoryReserved bool `json:"inventoryReserved"`
|
||||
Confirmation *ConfirmationStatus `json:"confirmationViewed,omitempty"`
|
||||
Payments []*Payment `json:"payments,omitempty"`
|
||||
}
|
||||
|
||||
func NewCheckoutGrain(id uint64, cartId cart.CartId, cartVersion uint64, ts time.Time, cartState *cart.CartGrain) *CheckoutGrain {
|
||||
return &CheckoutGrain{
|
||||
lastDeliveryId: 0,
|
||||
lastGiftcardId: 0,
|
||||
lastAccess: ts,
|
||||
lastChange: ts,
|
||||
Id: CheckoutId(id),
|
||||
CartId: cartId,
|
||||
CartVersion: cartVersion,
|
||||
Deliveries: []*CheckoutDelivery{},
|
||||
Payments: []*Payment{},
|
||||
CartState: cartState,
|
||||
CartTotalPrice: cartState.TotalPrice,
|
||||
}
|
||||
}
|
||||
|
||||
func (c *CheckoutGrain) GetId() uint64 {
|
||||
return uint64(c.Id)
|
||||
}
|
||||
|
||||
func (c *CheckoutGrain) GetLastChange() time.Time {
|
||||
return c.lastChange
|
||||
}
|
||||
|
||||
func (c *CheckoutGrain) GetLastAccess() time.Time {
|
||||
return c.lastAccess
|
||||
}
|
||||
|
||||
func (c *CheckoutGrain) GetCurrentState() (*CheckoutGrain, error) {
|
||||
c.lastAccess = time.Now()
|
||||
return c, nil
|
||||
}
|
||||
|
||||
func (c *CheckoutGrain) GetState() ([]byte, error) {
|
||||
return json.Marshal(c)
|
||||
}
|
||||
|
||||
func (c *CheckoutGrain) FindPayment(paymentId string) (*Payment, bool) {
|
||||
if paymentId == "" {
|
||||
return nil, false
|
||||
}
|
||||
for _, payment := range c.Payments {
|
||||
if payment != nil && payment.PaymentId == paymentId {
|
||||
return payment, true
|
||||
}
|
||||
}
|
||||
return nil, false
|
||||
}
|
||||
|
||||
func (c *CheckoutGrain) SettledPayments() []*Payment {
|
||||
if len(c.Payments) == 0 {
|
||||
return nil
|
||||
}
|
||||
settled := make([]*Payment, 0, len(c.Payments))
|
||||
for _, payment := range c.Payments {
|
||||
if payment != nil && payment.IsSettled() {
|
||||
settled = append(settled, payment)
|
||||
}
|
||||
}
|
||||
if len(settled) == 0 {
|
||||
return nil
|
||||
}
|
||||
return settled
|
||||
}
|
||||
|
||||
func (c *CheckoutGrain) OpenPayments() []*Payment {
|
||||
if len(c.Payments) == 0 {
|
||||
return nil
|
||||
}
|
||||
pending := make([]*Payment, 0, len(c.Payments))
|
||||
for _, payment := range c.Payments {
|
||||
if payment == nil {
|
||||
continue
|
||||
}
|
||||
if !payment.IsSettled() {
|
||||
pending = append(pending, payment)
|
||||
}
|
||||
}
|
||||
if len(pending) == 0 {
|
||||
return nil
|
||||
}
|
||||
return pending
|
||||
}
|
||||
35
pkg/checkout/mutation-context.go
Normal file
35
pkg/checkout/mutation-context.go
Normal file
@@ -0,0 +1,35 @@
|
||||
package checkout
|
||||
|
||||
import (
|
||||
"git.k6n.net/go-cart-actor/pkg/actor"
|
||||
messages "git.k6n.net/go-cart-actor/proto/checkout"
|
||||
)
|
||||
|
||||
type CheckoutMutationContext struct {
|
||||
// Add any services needed, e.g., for delivery calculations, but since inventory is pre-handled, maybe none
|
||||
}
|
||||
|
||||
func NewCheckoutMutationContext() *CheckoutMutationContext {
|
||||
return &CheckoutMutationContext{}
|
||||
}
|
||||
|
||||
func NewCheckoutMutationRegistry(ctx *CheckoutMutationContext) actor.MutationRegistry {
|
||||
reg := actor.NewMutationRegistry()
|
||||
reg.RegisterMutations(
|
||||
actor.NewMutation(HandleInitializeCheckout, func() *messages.InitializeCheckout { return &messages.InitializeCheckout{} }),
|
||||
actor.NewMutation(HandlePaymentStarted, func() *messages.PaymentStarted { return &messages.PaymentStarted{} }),
|
||||
actor.NewMutation(HandlePaymentCompleted, func() *messages.PaymentCompleted { return &messages.PaymentCompleted{} }),
|
||||
actor.NewMutation(HandlePaymentDeclined, func() *messages.PaymentDeclined { return &messages.PaymentDeclined{} }),
|
||||
actor.NewMutation(HandlePaymentEvent, func() *messages.PaymentEvent { return &messages.PaymentEvent{} }),
|
||||
actor.NewMutation(HandleConfirmationViewed, func() *messages.ConfirmationViewed { return &messages.ConfirmationViewed{} }),
|
||||
//actor.NewMutation(HandleCreateCheckoutOrder, func() *messages.CreateCheckoutOrder { return &messages.CreateCheckoutOrder{} }),
|
||||
actor.NewMutation(HandleOrderCreated, func() *messages.OrderCreated { return &messages.OrderCreated{} }),
|
||||
actor.NewMutation(HandleInventoryReserved, func() *messages.InventoryReserved { return &messages.InventoryReserved{} }),
|
||||
actor.NewMutation(HandleSetDelivery, func() *messages.SetDelivery { return &messages.SetDelivery{} }),
|
||||
actor.NewMutation(HandleSetPickupPoint, func() *messages.SetPickupPoint { return &messages.SetPickupPoint{} }),
|
||||
actor.NewMutation(HandleRemoveDelivery, func() *messages.RemoveDelivery { return &messages.RemoveDelivery{} }),
|
||||
// actor.NewMutation(HandleAddGiftcard, func() *messages.AddGiftcard { return &messages.AddGiftcard{} }),
|
||||
// actor.NewMutation(HandleRemoveGiftcard, func() *messages.RemoveGiftcard { return &messages.RemoveGiftcard{} }),
|
||||
)
|
||||
return reg
|
||||
}
|
||||
26
pkg/checkout/mutation_confirmation_viewed.go
Normal file
26
pkg/checkout/mutation_confirmation_viewed.go
Normal file
@@ -0,0 +1,26 @@
|
||||
package checkout
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
messages "git.k6n.net/go-cart-actor/proto/checkout"
|
||||
)
|
||||
|
||||
func HandleConfirmationViewed(g *CheckoutGrain, m *messages.ConfirmationViewed) error {
|
||||
if m == nil {
|
||||
return fmt.Errorf("ConfirmationViewed: nil payload")
|
||||
}
|
||||
|
||||
if g.Confirmation != nil {
|
||||
g.Confirmation = &ConfirmationStatus{
|
||||
ViewCount: 1,
|
||||
LastViewedAt: time.Now(),
|
||||
}
|
||||
} else {
|
||||
g.Confirmation.ViewCount++
|
||||
g.Confirmation.LastViewedAt = time.Now()
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
55
pkg/checkout/mutation_delivery.go
Normal file
55
pkg/checkout/mutation_delivery.go
Normal file
@@ -0,0 +1,55 @@
|
||||
package checkout
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"git.k6n.net/go-cart-actor/pkg/cart"
|
||||
messages "git.k6n.net/go-cart-actor/proto/checkout"
|
||||
)
|
||||
|
||||
func asPickupPoint(p *messages.PickupPoint, deliveryId uint32) *PickupPoint {
|
||||
if p == nil {
|
||||
return nil
|
||||
}
|
||||
return &PickupPoint{
|
||||
Id: p.Id,
|
||||
Name: p.Name,
|
||||
Address: p.Address,
|
||||
City: p.City,
|
||||
Country: p.Country,
|
||||
Zip: p.Zip,
|
||||
}
|
||||
}
|
||||
|
||||
// HandleSetDelivery mutation
|
||||
// HandleSetDelivery mutation
|
||||
func HandleSetDelivery(g *CheckoutGrain, m *messages.SetDelivery) error {
|
||||
if m == nil {
|
||||
return fmt.Errorf("HandleSetDelivery: nil payload")
|
||||
}
|
||||
if m.Provider == "" {
|
||||
return fmt.Errorf("HandleSetDelivery: missing provider")
|
||||
}
|
||||
|
||||
// Check if delivery already exists, update or add
|
||||
for _, d := range g.Deliveries {
|
||||
if d.Provider == m.Provider {
|
||||
// Update existing
|
||||
d.Items = m.Items
|
||||
d.PickupPoint = asPickupPoint(m.PickupPoint, d.Id)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// Add new delivery
|
||||
g.lastDeliveryId++
|
||||
delivery := &CheckoutDelivery{
|
||||
Id: g.lastDeliveryId,
|
||||
Provider: m.Provider,
|
||||
Items: m.Items,
|
||||
PickupPoint: asPickupPoint(m.PickupPoint, g.lastDeliveryId),
|
||||
Price: *cart.NewPrice(), // Price might need calculation, but for now zero
|
||||
}
|
||||
g.Deliveries = append(g.Deliveries, delivery)
|
||||
return nil
|
||||
}
|
||||
44
pkg/checkout/mutation_initialize_checkout.go
Normal file
44
pkg/checkout/mutation_initialize_checkout.go
Normal file
@@ -0,0 +1,44 @@
|
||||
package checkout
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
|
||||
messages "git.k6n.net/go-cart-actor/proto/checkout"
|
||||
)
|
||||
|
||||
// mutation_initialize_checkout.go
|
||||
//
|
||||
// Registers the InitializeCheckout mutation.
|
||||
// This mutation is invoked AFTER an external checkout session
|
||||
// has been successfully created or updated. It persists the
|
||||
// order reference / status and marks the checkout as having a payment in progress.
|
||||
//
|
||||
// Behavior:
|
||||
// - Sets OrderId to the order ID.
|
||||
// - Sets Status to the current status.
|
||||
// - Sets PaymentInProgress flag.
|
||||
// - Assumes inventory is already reserved.
|
||||
//
|
||||
// Validation:
|
||||
// - Returns an error if payload is nil.
|
||||
// - Returns an error if orderId is empty.
|
||||
|
||||
func HandleInitializeCheckout(g *CheckoutGrain, m *messages.InitializeCheckout) error {
|
||||
if m == nil {
|
||||
return fmt.Errorf("InitializeCheckout: nil payload")
|
||||
}
|
||||
if m.OrderId == "" {
|
||||
return fmt.Errorf("InitializeCheckout: missing orderId")
|
||||
}
|
||||
if m.CartState != nil {
|
||||
return fmt.Errorf("InitializeCheckout: checkout already initialized")
|
||||
}
|
||||
|
||||
err := json.Unmarshal(m.CartState.Value, &g.CartState)
|
||||
if err != nil {
|
||||
return fmt.Errorf("InitializeCheckout: failed to unmarshal cart state: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
16
pkg/checkout/mutation_inventory_reserved.go
Normal file
16
pkg/checkout/mutation_inventory_reserved.go
Normal file
@@ -0,0 +1,16 @@
|
||||
package checkout
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
messages "git.k6n.net/go-cart-actor/proto/checkout"
|
||||
)
|
||||
|
||||
func HandleInventoryReserved(g *CheckoutGrain, m *messages.InventoryReserved) error {
|
||||
if m == nil {
|
||||
return fmt.Errorf("HandleInventoryReserved: nil payload")
|
||||
}
|
||||
|
||||
g.InventoryReserved = m.Status == "success"
|
||||
return nil
|
||||
}
|
||||
@@ -1,9 +1,9 @@
|
||||
package cart
|
||||
package checkout
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
messages "git.k6n.net/go-cart-actor/pkg/messages"
|
||||
messages "git.k6n.net/go-cart-actor/proto/checkout"
|
||||
)
|
||||
|
||||
// mutation_order_created.go
|
||||
@@ -11,21 +11,20 @@ import (
|
||||
// Registers the OrderCreated mutation.
|
||||
//
|
||||
// This mutation represents the completion (or state transition) of an order
|
||||
// initiated earlier via InitializeCheckout / external Klarna processing.
|
||||
// It finalizes (or updates) the cart's order metadata.
|
||||
// initiated earlier via InitializeCheckout / external processing.
|
||||
// It finalizes (or updates) the checkout's order metadata.
|
||||
//
|
||||
// Behavior:
|
||||
// - Validates payload non-nil and OrderId not empty.
|
||||
// - Sets (or overwrites) OrderReference with the provided OrderId.
|
||||
// - Sets PaymentStatus from payload.Status.
|
||||
// - Marks PaymentInProgress = false (checkout flow finished / acknowledged).
|
||||
// - Does NOT adjust monetary totals (no WithTotals()).
|
||||
// - Sets Status from payload.Status.
|
||||
// - Sets OrderId if not already set.
|
||||
// - Does NOT adjust monetary totals.
|
||||
//
|
||||
// Notes / Future Extensions:
|
||||
// - If multiple order completion events can arrive (e.g., retries / webhook
|
||||
// replays), this handler is idempotent: it simply overwrites fields.
|
||||
// - If you need to guard against conflicting order IDs, add a check:
|
||||
// if g.OrderReference != "" && g.OrderReference != m.OrderId { ... }
|
||||
// if g.OrderId != "" && g.OrderId != m.OrderId { ... }
|
||||
// - Add audit logging or metrics here if required.
|
||||
//
|
||||
// Concurrency:
|
||||
@@ -33,16 +32,18 @@ import (
|
||||
// per grain. If out-of-order events are possible, embed versioning or
|
||||
// timestamps in the mutation and compare before applying changes.
|
||||
|
||||
func OrderCreated(g *CartGrain, m *messages.OrderCreated) error {
|
||||
func HandleOrderCreated(g *CheckoutGrain, m *messages.OrderCreated) error {
|
||||
if m == nil {
|
||||
return fmt.Errorf("OrderCreated: nil payload")
|
||||
return fmt.Errorf("HandleOrderCreated: nil payload")
|
||||
}
|
||||
if m.OrderId == "" {
|
||||
return fmt.Errorf("OrderCreated: missing orderId")
|
||||
}
|
||||
if g.OrderId == nil {
|
||||
g.OrderId = &m.OrderId
|
||||
} else if *g.OrderId != m.OrderId {
|
||||
return fmt.Errorf("OrderCreated: conflicting order ID")
|
||||
}
|
||||
|
||||
g.OrderReference = m.OrderId
|
||||
//g.PaymentStatus = m.Status
|
||||
//g.PaymentInProgress = false
|
||||
return nil
|
||||
}
|
||||
37
pkg/checkout/mutation_payment_completed.go
Normal file
37
pkg/checkout/mutation_payment_completed.go
Normal file
@@ -0,0 +1,37 @@
|
||||
package checkout
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
messages "git.k6n.net/go-cart-actor/proto/checkout"
|
||||
)
|
||||
|
||||
// PaymentCompleted registers the completion of a payment for a checkout.
|
||||
func HandlePaymentCompleted(g *CheckoutGrain, m *messages.PaymentCompleted) error {
|
||||
if m == nil {
|
||||
return fmt.Errorf("PaymentCompleted: nil payload")
|
||||
}
|
||||
paymentId := m.PaymentId
|
||||
payment, found := g.FindPayment(paymentId)
|
||||
|
||||
if !found {
|
||||
return fmt.Errorf("PaymentCompleted: payment not found")
|
||||
}
|
||||
|
||||
payment.ProcessorReference = m.ProcessorReference
|
||||
payment.Status = PaymentStatusSuccess
|
||||
payment.Amount = m.Amount
|
||||
payment.Currency = m.Currency
|
||||
payment.CompletedAt = &time.Time{}
|
||||
if m.CompletedAt != nil {
|
||||
*payment.CompletedAt = m.CompletedAt.AsTime()
|
||||
} else {
|
||||
*payment.CompletedAt = time.Now()
|
||||
}
|
||||
|
||||
// Update checkout status
|
||||
g.PaymentInProgress--
|
||||
|
||||
return nil
|
||||
}
|
||||
28
pkg/checkout/mutation_payment_declined.go
Normal file
28
pkg/checkout/mutation_payment_declined.go
Normal file
@@ -0,0 +1,28 @@
|
||||
package checkout
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"time"
|
||||
|
||||
messages "git.k6n.net/go-cart-actor/proto/checkout"
|
||||
)
|
||||
|
||||
func asPointer[T any](value T) *T {
|
||||
return &value
|
||||
}
|
||||
|
||||
var ErrPaymentNotFound = errors.New("payment not found")
|
||||
|
||||
func HandlePaymentDeclined(g *CheckoutGrain, m *messages.PaymentDeclined) error {
|
||||
|
||||
payment, found := g.FindPayment(m.PaymentId)
|
||||
if !found {
|
||||
return ErrPaymentNotFound
|
||||
}
|
||||
|
||||
payment.CompletedAt = asPointer(time.Now())
|
||||
payment.Status = "failed"
|
||||
g.PaymentInProgress--
|
||||
|
||||
return nil
|
||||
}
|
||||
20
pkg/checkout/mutation_payment_event.go
Normal file
20
pkg/checkout/mutation_payment_event.go
Normal file
@@ -0,0 +1,20 @@
|
||||
package checkout
|
||||
|
||||
import (
|
||||
messages "git.k6n.net/go-cart-actor/proto/checkout"
|
||||
)
|
||||
|
||||
func HandlePaymentEvent(g *CheckoutGrain, m *messages.PaymentEvent) error {
|
||||
|
||||
payment, found := g.FindPayment(m.PaymentId)
|
||||
if !found {
|
||||
return ErrPaymentNotFound
|
||||
}
|
||||
metaBytes := m.Data.Value
|
||||
payment.Events = append(payment.Events, &PaymentEvent{
|
||||
Name: m.Name,
|
||||
Success: m.Success,
|
||||
Data: metaBytes,
|
||||
})
|
||||
return nil
|
||||
}
|
||||
@@ -1,48 +1,48 @@
|
||||
package cart
|
||||
package checkout
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
messages "git.k6n.net/go-cart-actor/pkg/messages"
|
||||
messages "git.k6n.net/go-cart-actor/proto/checkout"
|
||||
)
|
||||
|
||||
// PaymentStarted registers the beginning of a payment attempt for a cart.
|
||||
// PaymentStarted registers the beginning of a payment attempt for a checkout.
|
||||
// It either upserts the payment entry (based on paymentId) or creates a new one,
|
||||
// marks the cart as having an in-progress payment, and recalculates the PaidInFull flag.
|
||||
func PaymentStarted(grain *CartGrain, msg *messages.PaymentStarted) error {
|
||||
if msg == nil {
|
||||
// marks the checkout as having a payment in progress.
|
||||
func HandlePaymentStarted(g *CheckoutGrain, m *messages.PaymentStarted) error {
|
||||
if m == nil {
|
||||
return fmt.Errorf("PaymentStarted: nil payload")
|
||||
}
|
||||
paymentID := strings.TrimSpace(msg.PaymentId)
|
||||
paymentID := strings.TrimSpace(m.PaymentId)
|
||||
if paymentID == "" {
|
||||
return fmt.Errorf("PaymentStarted: missing paymentId")
|
||||
}
|
||||
if msg.Amount < 0 {
|
||||
if m.Amount < 0 {
|
||||
return fmt.Errorf("PaymentStarted: amount cannot be negative")
|
||||
}
|
||||
|
||||
currency := strings.TrimSpace(msg.Currency)
|
||||
provider := strings.TrimSpace(msg.Provider)
|
||||
method := copyOptionalString(msg.Method)
|
||||
currency := strings.TrimSpace(m.Currency)
|
||||
provider := strings.TrimSpace(m.Provider)
|
||||
method := copyOptionalString(m.Method)
|
||||
|
||||
startedAt := time.Now().UTC()
|
||||
if msg.StartedAt != nil {
|
||||
startedAt = msg.StartedAt.AsTime()
|
||||
if m.StartedAt != nil {
|
||||
startedAt = m.StartedAt.AsTime()
|
||||
}
|
||||
|
||||
payment, found := grain.FindPayment(paymentID)
|
||||
payment, found := g.FindPayment(paymentID)
|
||||
|
||||
if found {
|
||||
if payment.Status != PaymentStatusPending {
|
||||
if payment.Status != "pending" {
|
||||
return fmt.Errorf("PaymentStarted: payment already started")
|
||||
}
|
||||
if payment.PaymentId != paymentID {
|
||||
payment.PaymentId = paymentID
|
||||
}
|
||||
payment.Status = PaymentStatusPending
|
||||
payment.Amount = msg.Amount
|
||||
payment.Status = "pending"
|
||||
payment.Amount = m.Amount
|
||||
if currency != "" {
|
||||
payment.Currency = currency
|
||||
}
|
||||
@@ -56,10 +56,11 @@ func PaymentStarted(grain *CartGrain, msg *messages.PaymentStarted) error {
|
||||
payment.CompletedAt = nil
|
||||
payment.ProcessorReference = nil
|
||||
} else {
|
||||
grain.Payments = append(grain.Payments, &CartPayment{
|
||||
g.PaymentInProgress++
|
||||
g.Payments = append(g.Payments, &Payment{
|
||||
PaymentId: paymentID,
|
||||
Status: PaymentStatusPending,
|
||||
Amount: msg.Amount,
|
||||
Status: "pending",
|
||||
Amount: m.Amount,
|
||||
Currency: currency,
|
||||
Provider: provider,
|
||||
Method: method,
|
||||
@@ -67,9 +68,6 @@ func PaymentStarted(grain *CartGrain, msg *messages.PaymentStarted) error {
|
||||
})
|
||||
}
|
||||
|
||||
grain.PaymentInProgress++
|
||||
grain.PaymentStatus = PaymentStatusPending
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
3
pkg/checkout/mutation_payments.go
Normal file
3
pkg/checkout/mutation_payments.go
Normal file
@@ -0,0 +1,3 @@
|
||||
package checkout
|
||||
|
||||
// This file is now empty as mutations have been moved to separate files.
|
||||
@@ -1,9 +1,9 @@
|
||||
package cart
|
||||
package checkout
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
messages "git.k6n.net/go-cart-actor/pkg/messages"
|
||||
messages "git.k6n.net/go-cart-actor/proto/checkout"
|
||||
)
|
||||
|
||||
// mutation_remove_delivery.go
|
||||
@@ -13,26 +13,13 @@ import (
|
||||
// Behavior:
|
||||
// - Removes the delivery entry whose Id == payload.Id.
|
||||
// - If not found, returns an error.
|
||||
// - Cart totals are recalculated (WithTotals) after removal.
|
||||
// - Items previously associated with that delivery simply become "without delivery";
|
||||
// subsequent delivery mutations can reassign them.
|
||||
//
|
||||
// Differences vs legacy:
|
||||
// - Legacy logic decremented TotalPrice explicitly before recalculating.
|
||||
// Here we rely solely on UpdateTotals() to recompute from remaining
|
||||
// deliveries and items (simpler / single source of truth).
|
||||
//
|
||||
// Future considerations:
|
||||
// - If delivery pricing logic changes (e.g., dynamic taxes per delivery),
|
||||
// UpdateTotals() may need enhancement to incorporate delivery tax properly.
|
||||
|
||||
func RemoveDelivery(g *CartGrain, m *messages.RemoveDelivery) error {
|
||||
func HandleRemoveDelivery(g *CheckoutGrain, m *messages.RemoveDelivery) error {
|
||||
if m == nil {
|
||||
return fmt.Errorf("RemoveDelivery: nil payload")
|
||||
}
|
||||
if g.PaymentInProgress > 0 {
|
||||
return ErrPaymentInProgress
|
||||
}
|
||||
targetID := uint32(m.Id)
|
||||
index := -1
|
||||
for i, d := range g.Deliveries {
|
||||
@@ -47,6 +34,5 @@ func RemoveDelivery(g *CartGrain, m *messages.RemoveDelivery) error {
|
||||
|
||||
// Remove delivery (order not preserved beyond necessity)
|
||||
g.Deliveries = append(g.Deliveries[:index], g.Deliveries[index+1:]...)
|
||||
g.UpdateTotals()
|
||||
return nil
|
||||
}
|
||||
@@ -1,9 +1,9 @@
|
||||
package cart
|
||||
package checkout
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
messages "git.k6n.net/go-cart-actor/pkg/messages"
|
||||
messages "git.k6n.net/go-cart-actor/proto/checkout"
|
||||
)
|
||||
|
||||
// mutation_set_pickup_point.go
|
||||
@@ -29,37 +29,23 @@ import (
|
||||
// - Track history / audit of pickup point changes
|
||||
// - Trigger delivery price adjustments (which would then require WithTotals()).
|
||||
|
||||
func SetPickupPoint(g *CartGrain, m *messages.SetPickupPoint) error {
|
||||
func HandleSetPickupPoint(g *CheckoutGrain, m *messages.SetPickupPoint) error {
|
||||
if m == nil {
|
||||
return fmt.Errorf("SetPickupPoint: nil payload")
|
||||
}
|
||||
if g.PaymentInProgress > 0 {
|
||||
return ErrPaymentInProgress
|
||||
}
|
||||
|
||||
for _, d := range g.Deliveries {
|
||||
if d.Id == uint32(m.DeliveryId) {
|
||||
d.PickupPoint = &messages.PickupPoint{
|
||||
Id: m.Id,
|
||||
Name: m.Name,
|
||||
Address: m.Address,
|
||||
City: m.City,
|
||||
Zip: m.Zip,
|
||||
Country: m.Country,
|
||||
d.PickupPoint = &PickupPoint{
|
||||
Id: m.PickupPoint.Id,
|
||||
Name: m.PickupPoint.Name,
|
||||
Address: m.PickupPoint.Address,
|
||||
City: m.PickupPoint.City,
|
||||
Zip: m.PickupPoint.Zip,
|
||||
Country: m.PickupPoint.Country,
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
return fmt.Errorf("SetPickupPoint: delivery id %d not found", m.DeliveryId)
|
||||
}
|
||||
|
||||
func ClearCart(g *CartGrain, m *messages.ClearCartRequest) error {
|
||||
if m == nil {
|
||||
return fmt.Errorf("ClearCart: nil payload")
|
||||
}
|
||||
// maybe check if payment is done?
|
||||
g.Deliveries = g.Deliveries[:0]
|
||||
g.Items = g.Items[:0]
|
||||
g.UpdateTotals()
|
||||
return nil
|
||||
}
|
||||
@@ -14,17 +14,16 @@ import (
|
||||
)
|
||||
|
||||
type K8sDiscovery struct {
|
||||
ctx context.Context
|
||||
client *kubernetes.Clientset
|
||||
ctx context.Context
|
||||
client *kubernetes.Clientset
|
||||
listOptions metav1.ListOptions
|
||||
}
|
||||
|
||||
func (k *K8sDiscovery) Discover() ([]string, error) {
|
||||
return k.DiscoverInNamespace("")
|
||||
}
|
||||
func (k *K8sDiscovery) DiscoverInNamespace(namespace string) ([]string, error) {
|
||||
pods, err := k.client.CoreV1().Pods(namespace).List(k.ctx, metav1.ListOptions{
|
||||
LabelSelector: "actor-pool=cart",
|
||||
})
|
||||
pods, err := k.client.CoreV1().Pods(namespace).List(k.ctx, k.listOptions)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -44,14 +43,10 @@ func hasReadyCondition(pod *v1.Pod) bool {
|
||||
}
|
||||
|
||||
func (k *K8sDiscovery) Watch() (<-chan HostChange, error) {
|
||||
timeout := int64(30)
|
||||
ipsThatAreReady := make(map[string]bool)
|
||||
m := sync.Mutex{}
|
||||
watcherFn := func(options metav1.ListOptions) (watch.Interface, error) {
|
||||
return k.client.CoreV1().Pods("").Watch(k.ctx, metav1.ListOptions{
|
||||
LabelSelector: "actor-pool=cart",
|
||||
TimeoutSeconds: &timeout,
|
||||
})
|
||||
return k.client.CoreV1().Pods("").Watch(k.ctx, k.listOptions)
|
||||
}
|
||||
watcher, err := toolsWatch.NewRetryWatcherWithContext(k.ctx, "1", &cache.ListWatch{WatchFunc: watcherFn})
|
||||
if err != nil {
|
||||
@@ -82,9 +77,10 @@ func (k *K8sDiscovery) Watch() (<-chan HostChange, error) {
|
||||
return ch, nil
|
||||
}
|
||||
|
||||
func NewK8sDiscovery(client *kubernetes.Clientset) *K8sDiscovery {
|
||||
func NewK8sDiscovery(client *kubernetes.Clientset, listOptions metav1.ListOptions) *K8sDiscovery {
|
||||
return &K8sDiscovery{
|
||||
ctx: context.Background(),
|
||||
client: client,
|
||||
ctx: context.Background(),
|
||||
client: client,
|
||||
listOptions: listOptions,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/client-go/kubernetes"
|
||||
"k8s.io/client-go/tools/clientcmd"
|
||||
)
|
||||
@@ -17,7 +18,9 @@ func TestDiscovery(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Errorf("Error creating client: %v", err)
|
||||
}
|
||||
d := NewK8sDiscovery(client)
|
||||
d := NewK8sDiscovery(client, metav1.ListOptions{
|
||||
LabelSelector: "app",
|
||||
})
|
||||
res, err := d.DiscoverInNamespace("")
|
||||
if err != nil {
|
||||
t.Errorf("Error discovering: %v", err)
|
||||
@@ -36,7 +39,9 @@ func TestWatch(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Errorf("Error creating client: %v", err)
|
||||
}
|
||||
d := NewK8sDiscovery(client)
|
||||
d := NewK8sDiscovery(client, metav1.ListOptions{
|
||||
LabelSelector: "app",
|
||||
})
|
||||
ch, err := d.Watch()
|
||||
if err != nil {
|
||||
t.Errorf("Error watching: %v", err)
|
||||
|
||||
@@ -1,800 +0,0 @@
|
||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||
// versions:
|
||||
// protoc-gen-go v1.36.5
|
||||
// protoc v6.33.1
|
||||
// source: control_plane.proto
|
||||
|
||||
package messages
|
||||
|
||||
import (
|
||||
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
|
||||
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
|
||||
anypb "google.golang.org/protobuf/types/known/anypb"
|
||||
reflect "reflect"
|
||||
sync "sync"
|
||||
unsafe "unsafe"
|
||||
)
|
||||
|
||||
const (
|
||||
// Verify that this generated code is sufficiently up-to-date.
|
||||
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
|
||||
// Verify that runtime/protoimpl is sufficiently up-to-date.
|
||||
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
|
||||
)
|
||||
|
||||
// Empty request placeholder (common pattern).
|
||||
type Empty struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
|
||||
func (x *Empty) Reset() {
|
||||
*x = Empty{}
|
||||
mi := &file_control_plane_proto_msgTypes[0]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
|
||||
func (x *Empty) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*Empty) ProtoMessage() {}
|
||||
|
||||
func (x *Empty) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_control_plane_proto_msgTypes[0]
|
||||
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 Empty.ProtoReflect.Descriptor instead.
|
||||
func (*Empty) Descriptor() ([]byte, []int) {
|
||||
return file_control_plane_proto_rawDescGZIP(), []int{0}
|
||||
}
|
||||
|
||||
// Ping reply includes responding host and its current unix time (seconds).
|
||||
type PingReply struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
Host string `protobuf:"bytes,1,opt,name=host,proto3" json:"host,omitempty"`
|
||||
UnixTime int64 `protobuf:"varint,2,opt,name=unix_time,json=unixTime,proto3" json:"unix_time,omitempty"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
|
||||
func (x *PingReply) Reset() {
|
||||
*x = PingReply{}
|
||||
mi := &file_control_plane_proto_msgTypes[1]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
|
||||
func (x *PingReply) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*PingReply) ProtoMessage() {}
|
||||
|
||||
func (x *PingReply) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_control_plane_proto_msgTypes[1]
|
||||
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 PingReply.ProtoReflect.Descriptor instead.
|
||||
func (*PingReply) Descriptor() ([]byte, []int) {
|
||||
return file_control_plane_proto_rawDescGZIP(), []int{1}
|
||||
}
|
||||
|
||||
func (x *PingReply) GetHost() string {
|
||||
if x != nil {
|
||||
return x.Host
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *PingReply) GetUnixTime() int64 {
|
||||
if x != nil {
|
||||
return x.UnixTime
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
// NegotiateRequest carries the caller's full view of known hosts (including self).
|
||||
type NegotiateRequest struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
KnownHosts []string `protobuf:"bytes,1,rep,name=known_hosts,json=knownHosts,proto3" json:"known_hosts,omitempty"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
|
||||
func (x *NegotiateRequest) Reset() {
|
||||
*x = NegotiateRequest{}
|
||||
mi := &file_control_plane_proto_msgTypes[2]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
|
||||
func (x *NegotiateRequest) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*NegotiateRequest) ProtoMessage() {}
|
||||
|
||||
func (x *NegotiateRequest) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_control_plane_proto_msgTypes[2]
|
||||
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 NegotiateRequest.ProtoReflect.Descriptor instead.
|
||||
func (*NegotiateRequest) Descriptor() ([]byte, []int) {
|
||||
return file_control_plane_proto_rawDescGZIP(), []int{2}
|
||||
}
|
||||
|
||||
func (x *NegotiateRequest) GetKnownHosts() []string {
|
||||
if x != nil {
|
||||
return x.KnownHosts
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// NegotiateReply returns the callee's healthy hosts (including itself).
|
||||
type NegotiateReply struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
Hosts []string `protobuf:"bytes,1,rep,name=hosts,proto3" json:"hosts,omitempty"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
|
||||
func (x *NegotiateReply) Reset() {
|
||||
*x = NegotiateReply{}
|
||||
mi := &file_control_plane_proto_msgTypes[3]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
|
||||
func (x *NegotiateReply) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*NegotiateReply) ProtoMessage() {}
|
||||
|
||||
func (x *NegotiateReply) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_control_plane_proto_msgTypes[3]
|
||||
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 NegotiateReply.ProtoReflect.Descriptor instead.
|
||||
func (*NegotiateReply) Descriptor() ([]byte, []int) {
|
||||
return file_control_plane_proto_rawDescGZIP(), []int{3}
|
||||
}
|
||||
|
||||
func (x *NegotiateReply) GetHosts() []string {
|
||||
if x != nil {
|
||||
return x.Hosts
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// CartIdsReply returns the list of cart IDs (string form) currently owned locally.
|
||||
type ActorIdsReply struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
Ids []uint64 `protobuf:"varint,1,rep,packed,name=ids,proto3" json:"ids,omitempty"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
|
||||
func (x *ActorIdsReply) Reset() {
|
||||
*x = ActorIdsReply{}
|
||||
mi := &file_control_plane_proto_msgTypes[4]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
|
||||
func (x *ActorIdsReply) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*ActorIdsReply) ProtoMessage() {}
|
||||
|
||||
func (x *ActorIdsReply) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_control_plane_proto_msgTypes[4]
|
||||
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 ActorIdsReply.ProtoReflect.Descriptor instead.
|
||||
func (*ActorIdsReply) Descriptor() ([]byte, []int) {
|
||||
return file_control_plane_proto_rawDescGZIP(), []int{4}
|
||||
}
|
||||
|
||||
func (x *ActorIdsReply) GetIds() []uint64 {
|
||||
if x != nil {
|
||||
return x.Ids
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// OwnerChangeAck retained as response type for Closing RPC (ConfirmOwner removed).
|
||||
type OwnerChangeAck struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
Accepted bool `protobuf:"varint,1,opt,name=accepted,proto3" json:"accepted,omitempty"`
|
||||
Message string `protobuf:"bytes,2,opt,name=message,proto3" json:"message,omitempty"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
|
||||
func (x *OwnerChangeAck) Reset() {
|
||||
*x = OwnerChangeAck{}
|
||||
mi := &file_control_plane_proto_msgTypes[5]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
|
||||
func (x *OwnerChangeAck) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*OwnerChangeAck) ProtoMessage() {}
|
||||
|
||||
func (x *OwnerChangeAck) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_control_plane_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 OwnerChangeAck.ProtoReflect.Descriptor instead.
|
||||
func (*OwnerChangeAck) Descriptor() ([]byte, []int) {
|
||||
return file_control_plane_proto_rawDescGZIP(), []int{5}
|
||||
}
|
||||
|
||||
func (x *OwnerChangeAck) GetAccepted() bool {
|
||||
if x != nil {
|
||||
return x.Accepted
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (x *OwnerChangeAck) GetMessage() string {
|
||||
if x != nil {
|
||||
return x.Message
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// ClosingNotice notifies peers this host is terminating (so they can drop / re-resolve).
|
||||
type ClosingNotice struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
Host string `protobuf:"bytes,1,opt,name=host,proto3" json:"host,omitempty"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
|
||||
func (x *ClosingNotice) Reset() {
|
||||
*x = ClosingNotice{}
|
||||
mi := &file_control_plane_proto_msgTypes[6]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
|
||||
func (x *ClosingNotice) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*ClosingNotice) ProtoMessage() {}
|
||||
|
||||
func (x *ClosingNotice) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_control_plane_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 ClosingNotice.ProtoReflect.Descriptor instead.
|
||||
func (*ClosingNotice) Descriptor() ([]byte, []int) {
|
||||
return file_control_plane_proto_rawDescGZIP(), []int{6}
|
||||
}
|
||||
|
||||
func (x *ClosingNotice) GetHost() string {
|
||||
if x != nil {
|
||||
return x.Host
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// OwnershipAnnounce broadcasts first-touch ownership claims for cart IDs.
|
||||
// First claim wins; receivers SHOULD NOT overwrite an existing different owner.
|
||||
type OwnershipAnnounce struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
Host string `protobuf:"bytes,1,opt,name=host,proto3" json:"host,omitempty"` // announcing host
|
||||
Ids []uint64 `protobuf:"varint,2,rep,packed,name=ids,proto3" json:"ids,omitempty"` // newly claimed cart ids
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
|
||||
func (x *OwnershipAnnounce) Reset() {
|
||||
*x = OwnershipAnnounce{}
|
||||
mi := &file_control_plane_proto_msgTypes[7]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
|
||||
func (x *OwnershipAnnounce) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*OwnershipAnnounce) ProtoMessage() {}
|
||||
|
||||
func (x *OwnershipAnnounce) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_control_plane_proto_msgTypes[7]
|
||||
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 OwnershipAnnounce.ProtoReflect.Descriptor instead.
|
||||
func (*OwnershipAnnounce) Descriptor() ([]byte, []int) {
|
||||
return file_control_plane_proto_rawDescGZIP(), []int{7}
|
||||
}
|
||||
|
||||
func (x *OwnershipAnnounce) GetHost() string {
|
||||
if x != nil {
|
||||
return x.Host
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *OwnershipAnnounce) GetIds() []uint64 {
|
||||
if x != nil {
|
||||
return x.Ids
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// ExpiryAnnounce broadcasts that a host evicted the provided cart IDs.
|
||||
type ExpiryAnnounce struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
Host string `protobuf:"bytes,1,opt,name=host,proto3" json:"host,omitempty"`
|
||||
Ids []uint64 `protobuf:"varint,2,rep,packed,name=ids,proto3" json:"ids,omitempty"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
|
||||
func (x *ExpiryAnnounce) Reset() {
|
||||
*x = ExpiryAnnounce{}
|
||||
mi := &file_control_plane_proto_msgTypes[8]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
|
||||
func (x *ExpiryAnnounce) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*ExpiryAnnounce) ProtoMessage() {}
|
||||
|
||||
func (x *ExpiryAnnounce) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_control_plane_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 ExpiryAnnounce.ProtoReflect.Descriptor instead.
|
||||
func (*ExpiryAnnounce) Descriptor() ([]byte, []int) {
|
||||
return file_control_plane_proto_rawDescGZIP(), []int{8}
|
||||
}
|
||||
|
||||
func (x *ExpiryAnnounce) GetHost() string {
|
||||
if x != nil {
|
||||
return x.Host
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *ExpiryAnnounce) GetIds() []uint64 {
|
||||
if x != nil {
|
||||
return x.Ids
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type ApplyRequest struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
Id uint64 `protobuf:"varint,1,opt,name=id,proto3" json:"id,omitempty"`
|
||||
Messages []*anypb.Any `protobuf:"bytes,2,rep,name=messages,proto3" json:"messages,omitempty"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
|
||||
func (x *ApplyRequest) Reset() {
|
||||
*x = ApplyRequest{}
|
||||
mi := &file_control_plane_proto_msgTypes[9]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
|
||||
func (x *ApplyRequest) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*ApplyRequest) ProtoMessage() {}
|
||||
|
||||
func (x *ApplyRequest) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_control_plane_proto_msgTypes[9]
|
||||
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 ApplyRequest.ProtoReflect.Descriptor instead.
|
||||
func (*ApplyRequest) Descriptor() ([]byte, []int) {
|
||||
return file_control_plane_proto_rawDescGZIP(), []int{9}
|
||||
}
|
||||
|
||||
func (x *ApplyRequest) GetId() uint64 {
|
||||
if x != nil {
|
||||
return x.Id
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *ApplyRequest) GetMessages() []*anypb.Any {
|
||||
if x != nil {
|
||||
return x.Messages
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type GetRequest struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
Id uint64 `protobuf:"varint,1,opt,name=id,proto3" json:"id,omitempty"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
|
||||
func (x *GetRequest) Reset() {
|
||||
*x = GetRequest{}
|
||||
mi := &file_control_plane_proto_msgTypes[10]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
|
||||
func (x *GetRequest) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*GetRequest) ProtoMessage() {}
|
||||
|
||||
func (x *GetRequest) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_control_plane_proto_msgTypes[10]
|
||||
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 GetRequest.ProtoReflect.Descriptor instead.
|
||||
func (*GetRequest) Descriptor() ([]byte, []int) {
|
||||
return file_control_plane_proto_rawDescGZIP(), []int{10}
|
||||
}
|
||||
|
||||
func (x *GetRequest) GetId() uint64 {
|
||||
if x != nil {
|
||||
return x.Id
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
type GetReply struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
Grain *anypb.Any `protobuf:"bytes,1,opt,name=grain,proto3" json:"grain,omitempty"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
|
||||
func (x *GetReply) Reset() {
|
||||
*x = GetReply{}
|
||||
mi := &file_control_plane_proto_msgTypes[11]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
|
||||
func (x *GetReply) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*GetReply) ProtoMessage() {}
|
||||
|
||||
func (x *GetReply) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_control_plane_proto_msgTypes[11]
|
||||
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 GetReply.ProtoReflect.Descriptor instead.
|
||||
func (*GetReply) Descriptor() ([]byte, []int) {
|
||||
return file_control_plane_proto_rawDescGZIP(), []int{11}
|
||||
}
|
||||
|
||||
func (x *GetReply) GetGrain() *anypb.Any {
|
||||
if x != nil {
|
||||
return x.Grain
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type ApplyResult struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
Accepted bool `protobuf:"varint,1,opt,name=accepted,proto3" json:"accepted,omitempty"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
|
||||
func (x *ApplyResult) Reset() {
|
||||
*x = ApplyResult{}
|
||||
mi := &file_control_plane_proto_msgTypes[12]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
|
||||
func (x *ApplyResult) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*ApplyResult) ProtoMessage() {}
|
||||
|
||||
func (x *ApplyResult) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_control_plane_proto_msgTypes[12]
|
||||
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 ApplyResult.ProtoReflect.Descriptor instead.
|
||||
func (*ApplyResult) Descriptor() ([]byte, []int) {
|
||||
return file_control_plane_proto_rawDescGZIP(), []int{12}
|
||||
}
|
||||
|
||||
func (x *ApplyResult) GetAccepted() bool {
|
||||
if x != nil {
|
||||
return x.Accepted
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
var File_control_plane_proto protoreflect.FileDescriptor
|
||||
|
||||
var file_control_plane_proto_rawDesc = string([]byte{
|
||||
0x0a, 0x13, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x5f, 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x2e,
|
||||
0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x08, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x73, 0x1a,
|
||||
0x19, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66,
|
||||
0x2f, 0x61, 0x6e, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x07, 0x0a, 0x05, 0x45, 0x6d,
|
||||
0x70, 0x74, 0x79, 0x22, 0x3c, 0x0a, 0x09, 0x50, 0x69, 0x6e, 0x67, 0x52, 0x65, 0x70, 0x6c, 0x79,
|
||||
0x12, 0x12, 0x0a, 0x04, 0x68, 0x6f, 0x73, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04,
|
||||
0x68, 0x6f, 0x73, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x75, 0x6e, 0x69, 0x78, 0x5f, 0x74, 0x69, 0x6d,
|
||||
0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x08, 0x75, 0x6e, 0x69, 0x78, 0x54, 0x69, 0x6d,
|
||||
0x65, 0x22, 0x33, 0x0a, 0x10, 0x4e, 0x65, 0x67, 0x6f, 0x74, 0x69, 0x61, 0x74, 0x65, 0x52, 0x65,
|
||||
0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1f, 0x0a, 0x0b, 0x6b, 0x6e, 0x6f, 0x77, 0x6e, 0x5f, 0x68,
|
||||
0x6f, 0x73, 0x74, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0a, 0x6b, 0x6e, 0x6f, 0x77,
|
||||
0x6e, 0x48, 0x6f, 0x73, 0x74, 0x73, 0x22, 0x26, 0x0a, 0x0e, 0x4e, 0x65, 0x67, 0x6f, 0x74, 0x69,
|
||||
0x61, 0x74, 0x65, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x68, 0x6f, 0x73, 0x74,
|
||||
0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x05, 0x68, 0x6f, 0x73, 0x74, 0x73, 0x22, 0x21,
|
||||
0x0a, 0x0d, 0x41, 0x63, 0x74, 0x6f, 0x72, 0x49, 0x64, 0x73, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12,
|
||||
0x10, 0x0a, 0x03, 0x69, 0x64, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x04, 0x52, 0x03, 0x69, 0x64,
|
||||
0x73, 0x22, 0x46, 0x0a, 0x0e, 0x4f, 0x77, 0x6e, 0x65, 0x72, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65,
|
||||
0x41, 0x63, 0x6b, 0x12, 0x1a, 0x0a, 0x08, 0x61, 0x63, 0x63, 0x65, 0x70, 0x74, 0x65, 0x64, 0x18,
|
||||
0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x61, 0x63, 0x63, 0x65, 0x70, 0x74, 0x65, 0x64, 0x12,
|
||||
0x18, 0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09,
|
||||
0x52, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x22, 0x23, 0x0a, 0x0d, 0x43, 0x6c, 0x6f,
|
||||
0x73, 0x69, 0x6e, 0x67, 0x4e, 0x6f, 0x74, 0x69, 0x63, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x68, 0x6f,
|
||||
0x73, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x68, 0x6f, 0x73, 0x74, 0x22, 0x39,
|
||||
0x0a, 0x11, 0x4f, 0x77, 0x6e, 0x65, 0x72, 0x73, 0x68, 0x69, 0x70, 0x41, 0x6e, 0x6e, 0x6f, 0x75,
|
||||
0x6e, 0x63, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x68, 0x6f, 0x73, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28,
|
||||
0x09, 0x52, 0x04, 0x68, 0x6f, 0x73, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x69, 0x64, 0x73, 0x18, 0x02,
|
||||
0x20, 0x03, 0x28, 0x04, 0x52, 0x03, 0x69, 0x64, 0x73, 0x22, 0x36, 0x0a, 0x0e, 0x45, 0x78, 0x70,
|
||||
0x69, 0x72, 0x79, 0x41, 0x6e, 0x6e, 0x6f, 0x75, 0x6e, 0x63, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x68,
|
||||
0x6f, 0x73, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x68, 0x6f, 0x73, 0x74, 0x12,
|
||||
0x10, 0x0a, 0x03, 0x69, 0x64, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x04, 0x52, 0x03, 0x69, 0x64,
|
||||
0x73, 0x22, 0x50, 0x0a, 0x0c, 0x41, 0x70, 0x70, 0x6c, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73,
|
||||
0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x02, 0x69,
|
||||
0x64, 0x12, 0x30, 0x0a, 0x08, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x73, 0x18, 0x02, 0x20,
|
||||
0x03, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f,
|
||||
0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x41, 0x6e, 0x79, 0x52, 0x08, 0x6d, 0x65, 0x73, 0x73, 0x61,
|
||||
0x67, 0x65, 0x73, 0x22, 0x1c, 0x0a, 0x0a, 0x47, 0x65, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73,
|
||||
0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x02, 0x69,
|
||||
0x64, 0x22, 0x36, 0x0a, 0x08, 0x47, 0x65, 0x74, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x2a, 0x0a,
|
||||
0x05, 0x67, 0x72, 0x61, 0x69, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x67,
|
||||
0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x41,
|
||||
0x6e, 0x79, 0x52, 0x05, 0x67, 0x72, 0x61, 0x69, 0x6e, 0x22, 0x29, 0x0a, 0x0b, 0x41, 0x70, 0x70,
|
||||
0x6c, 0x79, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x61, 0x63, 0x63, 0x65,
|
||||
0x70, 0x74, 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x61, 0x63, 0x63, 0x65,
|
||||
0x70, 0x74, 0x65, 0x64, 0x32, 0xf6, 0x03, 0x0a, 0x0c, 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c,
|
||||
0x50, 0x6c, 0x61, 0x6e, 0x65, 0x12, 0x2c, 0x0a, 0x04, 0x50, 0x69, 0x6e, 0x67, 0x12, 0x0f, 0x2e,
|
||||
0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x73, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x13,
|
||||
0x2e, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x73, 0x2e, 0x50, 0x69, 0x6e, 0x67, 0x52, 0x65,
|
||||
0x70, 0x6c, 0x79, 0x12, 0x41, 0x0a, 0x09, 0x4e, 0x65, 0x67, 0x6f, 0x74, 0x69, 0x61, 0x74, 0x65,
|
||||
0x12, 0x1a, 0x2e, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x73, 0x2e, 0x4e, 0x65, 0x67, 0x6f,
|
||||
0x74, 0x69, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x18, 0x2e, 0x6d,
|
||||
0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x73, 0x2e, 0x4e, 0x65, 0x67, 0x6f, 0x74, 0x69, 0x61, 0x74,
|
||||
0x65, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x3c, 0x0a, 0x10, 0x47, 0x65, 0x74, 0x4c, 0x6f, 0x63,
|
||||
0x61, 0x6c, 0x41, 0x63, 0x74, 0x6f, 0x72, 0x49, 0x64, 0x73, 0x12, 0x0f, 0x2e, 0x6d, 0x65, 0x73,
|
||||
0x73, 0x61, 0x67, 0x65, 0x73, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x17, 0x2e, 0x6d, 0x65,
|
||||
0x73, 0x73, 0x61, 0x67, 0x65, 0x73, 0x2e, 0x41, 0x63, 0x74, 0x6f, 0x72, 0x49, 0x64, 0x73, 0x52,
|
||||
0x65, 0x70, 0x6c, 0x79, 0x12, 0x4a, 0x0a, 0x11, 0x41, 0x6e, 0x6e, 0x6f, 0x75, 0x6e, 0x63, 0x65,
|
||||
0x4f, 0x77, 0x6e, 0x65, 0x72, 0x73, 0x68, 0x69, 0x70, 0x12, 0x1b, 0x2e, 0x6d, 0x65, 0x73, 0x73,
|
||||
0x61, 0x67, 0x65, 0x73, 0x2e, 0x4f, 0x77, 0x6e, 0x65, 0x72, 0x73, 0x68, 0x69, 0x70, 0x41, 0x6e,
|
||||
0x6e, 0x6f, 0x75, 0x6e, 0x63, 0x65, 0x1a, 0x18, 0x2e, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65,
|
||||
0x73, 0x2e, 0x4f, 0x77, 0x6e, 0x65, 0x72, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x41, 0x63, 0x6b,
|
||||
0x12, 0x36, 0x0a, 0x05, 0x41, 0x70, 0x70, 0x6c, 0x79, 0x12, 0x16, 0x2e, 0x6d, 0x65, 0x73, 0x73,
|
||||
0x61, 0x67, 0x65, 0x73, 0x2e, 0x41, 0x70, 0x70, 0x6c, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73,
|
||||
0x74, 0x1a, 0x15, 0x2e, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x73, 0x2e, 0x41, 0x70, 0x70,
|
||||
0x6c, 0x79, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x12, 0x44, 0x0a, 0x0e, 0x41, 0x6e, 0x6e, 0x6f,
|
||||
0x75, 0x6e, 0x63, 0x65, 0x45, 0x78, 0x70, 0x69, 0x72, 0x79, 0x12, 0x18, 0x2e, 0x6d, 0x65, 0x73,
|
||||
0x73, 0x61, 0x67, 0x65, 0x73, 0x2e, 0x45, 0x78, 0x70, 0x69, 0x72, 0x79, 0x41, 0x6e, 0x6e, 0x6f,
|
||||
0x75, 0x6e, 0x63, 0x65, 0x1a, 0x18, 0x2e, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x73, 0x2e,
|
||||
0x4f, 0x77, 0x6e, 0x65, 0x72, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x41, 0x63, 0x6b, 0x12, 0x3c,
|
||||
0x0a, 0x07, 0x43, 0x6c, 0x6f, 0x73, 0x69, 0x6e, 0x67, 0x12, 0x17, 0x2e, 0x6d, 0x65, 0x73, 0x73,
|
||||
0x61, 0x67, 0x65, 0x73, 0x2e, 0x43, 0x6c, 0x6f, 0x73, 0x69, 0x6e, 0x67, 0x4e, 0x6f, 0x74, 0x69,
|
||||
0x63, 0x65, 0x1a, 0x18, 0x2e, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x73, 0x2e, 0x4f, 0x77,
|
||||
0x6e, 0x65, 0x72, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x41, 0x63, 0x6b, 0x12, 0x2f, 0x0a, 0x03,
|
||||
0x47, 0x65, 0x74, 0x12, 0x14, 0x2e, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x73, 0x2e, 0x47,
|
||||
0x65, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x12, 0x2e, 0x6d, 0x65, 0x73, 0x73,
|
||||
0x61, 0x67, 0x65, 0x73, 0x2e, 0x47, 0x65, 0x74, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x42, 0x2a, 0x5a,
|
||||
0x28, 0x67, 0x69, 0x74, 0x2e, 0x6b, 0x36, 0x6e, 0x2e, 0x6e, 0x65, 0x74, 0x2f, 0x67, 0x6f, 0x2d,
|
||||
0x63, 0x61, 0x72, 0x74, 0x2d, 0x61, 0x63, 0x74, 0x6f, 0x72, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f,
|
||||
0x3b, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f,
|
||||
0x33,
|
||||
})
|
||||
|
||||
var (
|
||||
file_control_plane_proto_rawDescOnce sync.Once
|
||||
file_control_plane_proto_rawDescData []byte
|
||||
)
|
||||
|
||||
func file_control_plane_proto_rawDescGZIP() []byte {
|
||||
file_control_plane_proto_rawDescOnce.Do(func() {
|
||||
file_control_plane_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_control_plane_proto_rawDesc), len(file_control_plane_proto_rawDesc)))
|
||||
})
|
||||
return file_control_plane_proto_rawDescData
|
||||
}
|
||||
|
||||
var file_control_plane_proto_msgTypes = make([]protoimpl.MessageInfo, 13)
|
||||
var file_control_plane_proto_goTypes = []any{
|
||||
(*Empty)(nil), // 0: messages.Empty
|
||||
(*PingReply)(nil), // 1: messages.PingReply
|
||||
(*NegotiateRequest)(nil), // 2: messages.NegotiateRequest
|
||||
(*NegotiateReply)(nil), // 3: messages.NegotiateReply
|
||||
(*ActorIdsReply)(nil), // 4: messages.ActorIdsReply
|
||||
(*OwnerChangeAck)(nil), // 5: messages.OwnerChangeAck
|
||||
(*ClosingNotice)(nil), // 6: messages.ClosingNotice
|
||||
(*OwnershipAnnounce)(nil), // 7: messages.OwnershipAnnounce
|
||||
(*ExpiryAnnounce)(nil), // 8: messages.ExpiryAnnounce
|
||||
(*ApplyRequest)(nil), // 9: messages.ApplyRequest
|
||||
(*GetRequest)(nil), // 10: messages.GetRequest
|
||||
(*GetReply)(nil), // 11: messages.GetReply
|
||||
(*ApplyResult)(nil), // 12: messages.ApplyResult
|
||||
(*anypb.Any)(nil), // 13: google.protobuf.Any
|
||||
}
|
||||
var file_control_plane_proto_depIdxs = []int32{
|
||||
13, // 0: messages.ApplyRequest.messages:type_name -> google.protobuf.Any
|
||||
13, // 1: messages.GetReply.grain:type_name -> google.protobuf.Any
|
||||
0, // 2: messages.ControlPlane.Ping:input_type -> messages.Empty
|
||||
2, // 3: messages.ControlPlane.Negotiate:input_type -> messages.NegotiateRequest
|
||||
0, // 4: messages.ControlPlane.GetLocalActorIds:input_type -> messages.Empty
|
||||
7, // 5: messages.ControlPlane.AnnounceOwnership:input_type -> messages.OwnershipAnnounce
|
||||
9, // 6: messages.ControlPlane.Apply:input_type -> messages.ApplyRequest
|
||||
8, // 7: messages.ControlPlane.AnnounceExpiry:input_type -> messages.ExpiryAnnounce
|
||||
6, // 8: messages.ControlPlane.Closing:input_type -> messages.ClosingNotice
|
||||
10, // 9: messages.ControlPlane.Get:input_type -> messages.GetRequest
|
||||
1, // 10: messages.ControlPlane.Ping:output_type -> messages.PingReply
|
||||
3, // 11: messages.ControlPlane.Negotiate:output_type -> messages.NegotiateReply
|
||||
4, // 12: messages.ControlPlane.GetLocalActorIds:output_type -> messages.ActorIdsReply
|
||||
5, // 13: messages.ControlPlane.AnnounceOwnership:output_type -> messages.OwnerChangeAck
|
||||
12, // 14: messages.ControlPlane.Apply:output_type -> messages.ApplyResult
|
||||
5, // 15: messages.ControlPlane.AnnounceExpiry:output_type -> messages.OwnerChangeAck
|
||||
5, // 16: messages.ControlPlane.Closing:output_type -> messages.OwnerChangeAck
|
||||
11, // 17: messages.ControlPlane.Get:output_type -> messages.GetReply
|
||||
10, // [10:18] is the sub-list for method output_type
|
||||
2, // [2:10] is the sub-list for method input_type
|
||||
2, // [2:2] is the sub-list for extension type_name
|
||||
2, // [2:2] is the sub-list for extension extendee
|
||||
0, // [0:2] is the sub-list for field type_name
|
||||
}
|
||||
|
||||
func init() { file_control_plane_proto_init() }
|
||||
func file_control_plane_proto_init() {
|
||||
if File_control_plane_proto != nil {
|
||||
return
|
||||
}
|
||||
type x struct{}
|
||||
out := protoimpl.TypeBuilder{
|
||||
File: protoimpl.DescBuilder{
|
||||
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
||||
RawDescriptor: unsafe.Slice(unsafe.StringData(file_control_plane_proto_rawDesc), len(file_control_plane_proto_rawDesc)),
|
||||
NumEnums: 0,
|
||||
NumMessages: 13,
|
||||
NumExtensions: 0,
|
||||
NumServices: 1,
|
||||
},
|
||||
GoTypes: file_control_plane_proto_goTypes,
|
||||
DependencyIndexes: file_control_plane_proto_depIdxs,
|
||||
MessageInfos: file_control_plane_proto_msgTypes,
|
||||
}.Build()
|
||||
File_control_plane_proto = out.File
|
||||
file_control_plane_proto_goTypes = nil
|
||||
file_control_plane_proto_depIdxs = nil
|
||||
}
|
||||
@@ -1,403 +0,0 @@
|
||||
// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
|
||||
// versions:
|
||||
// - protoc-gen-go-grpc v1.5.1
|
||||
// - protoc v6.33.1
|
||||
// source: control_plane.proto
|
||||
|
||||
package messages
|
||||
|
||||
import (
|
||||
context "context"
|
||||
grpc "google.golang.org/grpc"
|
||||
codes "google.golang.org/grpc/codes"
|
||||
status "google.golang.org/grpc/status"
|
||||
)
|
||||
|
||||
// This is a compile-time assertion to ensure that this generated file
|
||||
// is compatible with the grpc package it is being compiled against.
|
||||
// Requires gRPC-Go v1.64.0 or later.
|
||||
const _ = grpc.SupportPackageIsVersion9
|
||||
|
||||
const (
|
||||
ControlPlane_Ping_FullMethodName = "/messages.ControlPlane/Ping"
|
||||
ControlPlane_Negotiate_FullMethodName = "/messages.ControlPlane/Negotiate"
|
||||
ControlPlane_GetLocalActorIds_FullMethodName = "/messages.ControlPlane/GetLocalActorIds"
|
||||
ControlPlane_AnnounceOwnership_FullMethodName = "/messages.ControlPlane/AnnounceOwnership"
|
||||
ControlPlane_Apply_FullMethodName = "/messages.ControlPlane/Apply"
|
||||
ControlPlane_AnnounceExpiry_FullMethodName = "/messages.ControlPlane/AnnounceExpiry"
|
||||
ControlPlane_Closing_FullMethodName = "/messages.ControlPlane/Closing"
|
||||
ControlPlane_Get_FullMethodName = "/messages.ControlPlane/Get"
|
||||
)
|
||||
|
||||
// ControlPlaneClient is the client API for ControlPlane service.
|
||||
//
|
||||
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream.
|
||||
//
|
||||
// ControlPlane defines cluster coordination and ownership operations.
|
||||
type ControlPlaneClient interface {
|
||||
// Ping for liveness; lightweight health signal.
|
||||
Ping(ctx context.Context, in *Empty, opts ...grpc.CallOption) (*PingReply, error)
|
||||
// Negotiate merges host views; used during discovery & convergence.
|
||||
Negotiate(ctx context.Context, in *NegotiateRequest, opts ...grpc.CallOption) (*NegotiateReply, error)
|
||||
// GetCartIds lists currently owned cart IDs on this node.
|
||||
GetLocalActorIds(ctx context.Context, in *Empty, opts ...grpc.CallOption) (*ActorIdsReply, error)
|
||||
// Ownership announcement: first-touch claim broadcast (idempotent; best-effort).
|
||||
AnnounceOwnership(ctx context.Context, in *OwnershipAnnounce, opts ...grpc.CallOption) (*OwnerChangeAck, error)
|
||||
Apply(ctx context.Context, in *ApplyRequest, opts ...grpc.CallOption) (*ApplyResult, error)
|
||||
// Expiry announcement: drop remote ownership hints when local TTL expires.
|
||||
AnnounceExpiry(ctx context.Context, in *ExpiryAnnounce, opts ...grpc.CallOption) (*OwnerChangeAck, error)
|
||||
// Closing announces graceful shutdown so peers can proactively adjust.
|
||||
Closing(ctx context.Context, in *ClosingNotice, opts ...grpc.CallOption) (*OwnerChangeAck, error)
|
||||
Get(ctx context.Context, in *GetRequest, opts ...grpc.CallOption) (*GetReply, error)
|
||||
}
|
||||
|
||||
type controlPlaneClient struct {
|
||||
cc grpc.ClientConnInterface
|
||||
}
|
||||
|
||||
func NewControlPlaneClient(cc grpc.ClientConnInterface) ControlPlaneClient {
|
||||
return &controlPlaneClient{cc}
|
||||
}
|
||||
|
||||
func (c *controlPlaneClient) Ping(ctx context.Context, in *Empty, opts ...grpc.CallOption) (*PingReply, error) {
|
||||
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||
out := new(PingReply)
|
||||
err := c.cc.Invoke(ctx, ControlPlane_Ping_FullMethodName, in, out, cOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *controlPlaneClient) Negotiate(ctx context.Context, in *NegotiateRequest, opts ...grpc.CallOption) (*NegotiateReply, error) {
|
||||
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||
out := new(NegotiateReply)
|
||||
err := c.cc.Invoke(ctx, ControlPlane_Negotiate_FullMethodName, in, out, cOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *controlPlaneClient) GetLocalActorIds(ctx context.Context, in *Empty, opts ...grpc.CallOption) (*ActorIdsReply, error) {
|
||||
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||
out := new(ActorIdsReply)
|
||||
err := c.cc.Invoke(ctx, ControlPlane_GetLocalActorIds_FullMethodName, in, out, cOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *controlPlaneClient) AnnounceOwnership(ctx context.Context, in *OwnershipAnnounce, opts ...grpc.CallOption) (*OwnerChangeAck, error) {
|
||||
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||
out := new(OwnerChangeAck)
|
||||
err := c.cc.Invoke(ctx, ControlPlane_AnnounceOwnership_FullMethodName, in, out, cOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *controlPlaneClient) Apply(ctx context.Context, in *ApplyRequest, opts ...grpc.CallOption) (*ApplyResult, error) {
|
||||
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||
out := new(ApplyResult)
|
||||
err := c.cc.Invoke(ctx, ControlPlane_Apply_FullMethodName, in, out, cOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *controlPlaneClient) AnnounceExpiry(ctx context.Context, in *ExpiryAnnounce, opts ...grpc.CallOption) (*OwnerChangeAck, error) {
|
||||
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||
out := new(OwnerChangeAck)
|
||||
err := c.cc.Invoke(ctx, ControlPlane_AnnounceExpiry_FullMethodName, in, out, cOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *controlPlaneClient) Closing(ctx context.Context, in *ClosingNotice, opts ...grpc.CallOption) (*OwnerChangeAck, error) {
|
||||
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||
out := new(OwnerChangeAck)
|
||||
err := c.cc.Invoke(ctx, ControlPlane_Closing_FullMethodName, in, out, cOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *controlPlaneClient) Get(ctx context.Context, in *GetRequest, opts ...grpc.CallOption) (*GetReply, error) {
|
||||
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||
out := new(GetReply)
|
||||
err := c.cc.Invoke(ctx, ControlPlane_Get_FullMethodName, in, out, cOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
// ControlPlaneServer is the server API for ControlPlane service.
|
||||
// All implementations must embed UnimplementedControlPlaneServer
|
||||
// for forward compatibility.
|
||||
//
|
||||
// ControlPlane defines cluster coordination and ownership operations.
|
||||
type ControlPlaneServer interface {
|
||||
// Ping for liveness; lightweight health signal.
|
||||
Ping(context.Context, *Empty) (*PingReply, error)
|
||||
// Negotiate merges host views; used during discovery & convergence.
|
||||
Negotiate(context.Context, *NegotiateRequest) (*NegotiateReply, error)
|
||||
// GetCartIds lists currently owned cart IDs on this node.
|
||||
GetLocalActorIds(context.Context, *Empty) (*ActorIdsReply, error)
|
||||
// Ownership announcement: first-touch claim broadcast (idempotent; best-effort).
|
||||
AnnounceOwnership(context.Context, *OwnershipAnnounce) (*OwnerChangeAck, error)
|
||||
Apply(context.Context, *ApplyRequest) (*ApplyResult, error)
|
||||
// Expiry announcement: drop remote ownership hints when local TTL expires.
|
||||
AnnounceExpiry(context.Context, *ExpiryAnnounce) (*OwnerChangeAck, error)
|
||||
// Closing announces graceful shutdown so peers can proactively adjust.
|
||||
Closing(context.Context, *ClosingNotice) (*OwnerChangeAck, error)
|
||||
Get(context.Context, *GetRequest) (*GetReply, error)
|
||||
mustEmbedUnimplementedControlPlaneServer()
|
||||
}
|
||||
|
||||
// UnimplementedControlPlaneServer must be embedded to have
|
||||
// forward compatible implementations.
|
||||
//
|
||||
// NOTE: this should be embedded by value instead of pointer to avoid a nil
|
||||
// pointer dereference when methods are called.
|
||||
type UnimplementedControlPlaneServer struct{}
|
||||
|
||||
func (UnimplementedControlPlaneServer) Ping(context.Context, *Empty) (*PingReply, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method Ping not implemented")
|
||||
}
|
||||
func (UnimplementedControlPlaneServer) Negotiate(context.Context, *NegotiateRequest) (*NegotiateReply, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method Negotiate not implemented")
|
||||
}
|
||||
func (UnimplementedControlPlaneServer) GetLocalActorIds(context.Context, *Empty) (*ActorIdsReply, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method GetLocalActorIds not implemented")
|
||||
}
|
||||
func (UnimplementedControlPlaneServer) AnnounceOwnership(context.Context, *OwnershipAnnounce) (*OwnerChangeAck, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method AnnounceOwnership not implemented")
|
||||
}
|
||||
func (UnimplementedControlPlaneServer) Apply(context.Context, *ApplyRequest) (*ApplyResult, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method Apply not implemented")
|
||||
}
|
||||
func (UnimplementedControlPlaneServer) AnnounceExpiry(context.Context, *ExpiryAnnounce) (*OwnerChangeAck, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method AnnounceExpiry not implemented")
|
||||
}
|
||||
func (UnimplementedControlPlaneServer) Closing(context.Context, *ClosingNotice) (*OwnerChangeAck, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method Closing not implemented")
|
||||
}
|
||||
func (UnimplementedControlPlaneServer) Get(context.Context, *GetRequest) (*GetReply, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method Get not implemented")
|
||||
}
|
||||
func (UnimplementedControlPlaneServer) mustEmbedUnimplementedControlPlaneServer() {}
|
||||
func (UnimplementedControlPlaneServer) testEmbeddedByValue() {}
|
||||
|
||||
// UnsafeControlPlaneServer may be embedded to opt out of forward compatibility for this service.
|
||||
// Use of this interface is not recommended, as added methods to ControlPlaneServer will
|
||||
// result in compilation errors.
|
||||
type UnsafeControlPlaneServer interface {
|
||||
mustEmbedUnimplementedControlPlaneServer()
|
||||
}
|
||||
|
||||
func RegisterControlPlaneServer(s grpc.ServiceRegistrar, srv ControlPlaneServer) {
|
||||
// If the following call pancis, it indicates UnimplementedControlPlaneServer was
|
||||
// embedded by pointer and is nil. This will cause panics if an
|
||||
// unimplemented method is ever invoked, so we test this at initialization
|
||||
// time to prevent it from happening at runtime later due to I/O.
|
||||
if t, ok := srv.(interface{ testEmbeddedByValue() }); ok {
|
||||
t.testEmbeddedByValue()
|
||||
}
|
||||
s.RegisterService(&ControlPlane_ServiceDesc, srv)
|
||||
}
|
||||
|
||||
func _ControlPlane_Ping_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(Empty)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(ControlPlaneServer).Ping(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: ControlPlane_Ping_FullMethodName,
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(ControlPlaneServer).Ping(ctx, req.(*Empty))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _ControlPlane_Negotiate_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(NegotiateRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(ControlPlaneServer).Negotiate(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: ControlPlane_Negotiate_FullMethodName,
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(ControlPlaneServer).Negotiate(ctx, req.(*NegotiateRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _ControlPlane_GetLocalActorIds_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(Empty)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(ControlPlaneServer).GetLocalActorIds(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: ControlPlane_GetLocalActorIds_FullMethodName,
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(ControlPlaneServer).GetLocalActorIds(ctx, req.(*Empty))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _ControlPlane_AnnounceOwnership_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(OwnershipAnnounce)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(ControlPlaneServer).AnnounceOwnership(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: ControlPlane_AnnounceOwnership_FullMethodName,
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(ControlPlaneServer).AnnounceOwnership(ctx, req.(*OwnershipAnnounce))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _ControlPlane_Apply_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(ApplyRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(ControlPlaneServer).Apply(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: ControlPlane_Apply_FullMethodName,
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(ControlPlaneServer).Apply(ctx, req.(*ApplyRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _ControlPlane_AnnounceExpiry_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(ExpiryAnnounce)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(ControlPlaneServer).AnnounceExpiry(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: ControlPlane_AnnounceExpiry_FullMethodName,
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(ControlPlaneServer).AnnounceExpiry(ctx, req.(*ExpiryAnnounce))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _ControlPlane_Closing_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(ClosingNotice)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(ControlPlaneServer).Closing(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: ControlPlane_Closing_FullMethodName,
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(ControlPlaneServer).Closing(ctx, req.(*ClosingNotice))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _ControlPlane_Get_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(GetRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(ControlPlaneServer).Get(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: ControlPlane_Get_FullMethodName,
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(ControlPlaneServer).Get(ctx, req.(*GetRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
// ControlPlane_ServiceDesc is the grpc.ServiceDesc for ControlPlane service.
|
||||
// It's only intended for direct use with grpc.RegisterService,
|
||||
// and not to be introspected or modified (even as a copy)
|
||||
var ControlPlane_ServiceDesc = grpc.ServiceDesc{
|
||||
ServiceName: "messages.ControlPlane",
|
||||
HandlerType: (*ControlPlaneServer)(nil),
|
||||
Methods: []grpc.MethodDesc{
|
||||
{
|
||||
MethodName: "Ping",
|
||||
Handler: _ControlPlane_Ping_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "Negotiate",
|
||||
Handler: _ControlPlane_Negotiate_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "GetLocalActorIds",
|
||||
Handler: _ControlPlane_GetLocalActorIds_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "AnnounceOwnership",
|
||||
Handler: _ControlPlane_AnnounceOwnership_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "Apply",
|
||||
Handler: _ControlPlane_Apply_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "AnnounceExpiry",
|
||||
Handler: _ControlPlane_AnnounceExpiry_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "Closing",
|
||||
Handler: _ControlPlane_Closing_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "Get",
|
||||
Handler: _ControlPlane_Get_Handler,
|
||||
},
|
||||
},
|
||||
Streams: []grpc.StreamDesc{},
|
||||
Metadata: "control_plane.proto",
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -11,7 +11,7 @@ import (
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"git.k6n.net/go-cart-actor/pkg/messages"
|
||||
messages "git.k6n.net/go-cart-actor/proto/control"
|
||||
"go.opentelemetry.io/contrib/bridges/otelslog"
|
||||
"go.opentelemetry.io/otel"
|
||||
"go.opentelemetry.io/otel/attribute"
|
||||
|
||||
@@ -6,7 +6,7 @@ import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"git.k6n.net/go-cart-actor/pkg/messages"
|
||||
messages "git.k6n.net/go-cart-actor/proto/cart"
|
||||
)
|
||||
|
||||
type Rule struct {
|
||||
|
||||
Reference in New Issue
Block a user