Sobes.tech
Middle+

Implement the grpc server method AddItemAndOrder: 1. Add items to the cart 2. Be sure to notify potential consumers about the cart checkout via a message broker. 3. When the cart composition changes, recalculate basket.Total=sum(count*price) 4. All cart items must be unique by ProductID key 5. Change the cart status to ordered for checkout 6. No changes are allowed in finalized carts

sobes.tech AI

Answer from AI

Below is an example implementation of the AddItemAndOrder method for a gRPC server in Go, considering the requirements:

  • The Basket contains unique items by ProductID.
  • When adding items, the Total sum is recalculated.
  • When the basket is checked out, its status changes to ordered.
  • After checkout, modifications are prohibited.
  • After checkout, a message is sent via a broker (e.g., through the MessageBroker interface).
package service

import (
    "context"
    "errors"
    "sync"
)

// Example structures

type BasketStatus string

const (
    StatusOpen    BasketStatus = "open"
    StatusOrdered BasketStatus = "ordered"
)

type BasketItem struct {
    ProductID string
    Count     int
    Price     float64
}

type Basket struct {
    ID     string
    Items  map[string]*BasketItem // key - ProductID
    Total  float64
    Status BasketStatus
    mu     sync.Mutex
}

// Message broker interface

type MessageBroker interface {
    Publish(topic string, message interface{}) error
}

// Basket service with baskets and broker

type BasketService struct {
    baskets map[string]*Basket
    broker  MessageBroker
}

func NewBasketService(broker MessageBroker) *BasketService {
    return &BasketService{
        baskets: make(map[string]*Basket),
        broker:  broker,
    }
}

// AddItemAndOrderRequest and Response are protobuf-generated structures

func (s *BasketService) AddItemAndOrder(ctx context.Context, req *AddItemAndOrderRequest) (*AddItemAndOrderResponse, error) {
    basket, ok := s.baskets[req.BasketId]
    if !ok {
        return nil, errors.New("basket not found")
    }

    basket.mu.Lock()
    defer basket.mu.Unlock()

    if basket.Status == StatusOrdered {
        return nil, errors.New("cannot modify ordered basket")
    }

    // Add or update items
    for _, item := range req.Items {
        if existing, exists := basket.Items[item.ProductId]; exists {
            existing.Count += int(item.Count)
            existing.Price = item.Price // update price if needed
        } else {
            basket.Items[item.ProductId] = &BasketItem{
                ProductID: item.ProductId,
                Count:     int(item.Count),
                Price:     item.Price,
            }
        }
    }

    // Recalculate total
    var total float64
    for _, item := range basket.Items {
        total += float64(item.Count) * item.Price
    }
    basket.Total = total

    // If the request has a checkout flag
    if req.Order { // assume there's a boolean field Order
        basket.Status = StatusOrdered

        // Send message to broker
        err := s.broker.Publish("basket_ordered", map[string]interface{}{
            "basket_id": basket.ID,
            "total":     basket.Total,
            "items":     basket.Items,
        })
        if err != nil {
            return nil, err
        }
    }

    return &AddItemAndOrderResponse{
        Total:  basket.Total,
        Status: string(basket.Status),
    }, nil
}

In this example:

  • A mutex protects basket data from race conditions.
  • Items are stored in a map for uniqueness by ProductID.
  • On checkout, the status changes and an event is sent via the broker.
  • After checkout, modifications are prohibited.

For a real application, validation, error handling, and integration with a real message broker should be added.

Implement the grpc server method AddItemAndOrder: 1… - sobes.tech