checkout backoffice
This commit is contained in:
@@ -18,19 +18,24 @@ import (
|
||||
|
||||
"git.k6n.net/go-cart-actor/pkg/actor"
|
||||
"git.k6n.net/go-cart-actor/pkg/cart"
|
||||
"git.k6n.net/go-cart-actor/pkg/checkout"
|
||||
"google.golang.org/protobuf/proto"
|
||||
)
|
||||
|
||||
type FileServer struct {
|
||||
// Define fields here
|
||||
dataDir string
|
||||
storage actor.LogStorage[cart.CartGrain]
|
||||
dataDir string
|
||||
checkoutDataDir string
|
||||
storage actor.LogStorage[cart.CartGrain]
|
||||
checkoutStorage actor.LogStorage[checkout.CheckoutGrain]
|
||||
}
|
||||
|
||||
func NewFileServer(dataDir string, storage actor.LogStorage[cart.CartGrain]) *FileServer {
|
||||
func NewFileServer(dataDir string, checkoutDataDir string, storage actor.LogStorage[cart.CartGrain], checkoutStorage actor.LogStorage[checkout.CheckoutGrain]) *FileServer {
|
||||
return &FileServer{
|
||||
dataDir: dataDir,
|
||||
storage: storage,
|
||||
dataDir: dataDir,
|
||||
checkoutDataDir: checkoutDataDir,
|
||||
storage: storage,
|
||||
checkoutStorage: checkoutStorage,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -79,6 +84,12 @@ func appendFileInfo(info fs.FileInfo, out *CartFileInfo) *CartFileInfo {
|
||||
return out
|
||||
}
|
||||
|
||||
func appendCheckoutFileInfo(info fs.FileInfo, out *CheckoutFileInfo) *CheckoutFileInfo {
|
||||
out.Size = info.Size()
|
||||
out.Modified = info.ModTime()
|
||||
return out
|
||||
}
|
||||
|
||||
// var cartFileRe = regexp.MustCompile(`^(\d+)\.events\.log$`)
|
||||
|
||||
func listCartFiles(dir string) ([]*CartFileInfo, error) {
|
||||
@@ -112,6 +123,36 @@ func listCartFiles(dir string) ([]*CartFileInfo, error) {
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func listCheckoutFiles(dir string) ([]*CheckoutFileInfo, error) {
|
||||
entries, err := os.ReadDir(dir)
|
||||
if err != nil {
|
||||
if errors.Is(err, os.ErrNotExist) {
|
||||
return []*CheckoutFileInfo{}, nil
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
out := make([]*CheckoutFileInfo, 0)
|
||||
for _, e := range entries {
|
||||
if e.IsDir() {
|
||||
continue
|
||||
}
|
||||
id, valid := isValidFileId(e.Name())
|
||||
if !valid {
|
||||
continue
|
||||
}
|
||||
|
||||
info, err := e.Info()
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
out = append(out, appendCheckoutFileInfo(info, &CheckoutFileInfo{
|
||||
ID: fmt.Sprintf("%d", id),
|
||||
CheckoutId: checkout.CheckoutId(id),
|
||||
}))
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func readRawLogLines(path string) ([]json.RawMessage, error) {
|
||||
fh, err := os.Open(path)
|
||||
if err != nil {
|
||||
@@ -158,6 +199,21 @@ func (fs *FileServer) CartsHandler(w http.ResponseWriter, r *http.Request) {
|
||||
})
|
||||
}
|
||||
|
||||
func (fs *FileServer) CheckoutsHandler(w http.ResponseWriter, r *http.Request) {
|
||||
list, err := listCheckoutFiles(fs.checkoutDataDir)
|
||||
if err != nil {
|
||||
writeJSON(w, http.StatusInternalServerError, map[string]string{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
// sort by modified desc
|
||||
sort.Slice(list, func(i, j int) bool { return list[i].Modified.After(list[j].Modified) })
|
||||
|
||||
writeJSON(w, http.StatusOK, map[string]any{
|
||||
"count": len(list),
|
||||
"checkouts": list,
|
||||
})
|
||||
}
|
||||
|
||||
func (fs *FileServer) PromotionsHandler(w http.ResponseWriter, r *http.Request) {
|
||||
fileName := filepath.Join(fs.dataDir, "promotions.json")
|
||||
if r.Method == http.MethodGet {
|
||||
@@ -315,3 +371,69 @@ func (fs *FileServer) CartHandler(w http.ResponseWriter, r *http.Request) {
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func (fs *FileServer) CheckoutHandler(w http.ResponseWriter, r *http.Request) {
|
||||
idStr := r.PathValue("id")
|
||||
if idStr == "" {
|
||||
writeJSON(w, http.StatusBadRequest, JsonError{Error: "missing id"})
|
||||
return
|
||||
}
|
||||
id, ok := isValidId(idStr)
|
||||
if !ok {
|
||||
writeJSON(w, http.StatusBadRequest, JsonError{Error: "invalid id"})
|
||||
return
|
||||
}
|
||||
// parse query parameters for filtering
|
||||
query := r.URL.Query()
|
||||
filterFunction := acceptAll
|
||||
if maxIndexStr := query.Get("maxIndex"); maxIndexStr != "" {
|
||||
log.Printf("filter maxIndex: %s", maxIndexStr)
|
||||
maxIndex, err := strconv.Atoi(maxIndexStr)
|
||||
if err != nil {
|
||||
writeJSON(w, http.StatusBadRequest, JsonError{Error: "invalid maxIndex"})
|
||||
return
|
||||
}
|
||||
filterFunction = acceptUntilIndex(maxIndex)
|
||||
} else if untilStr := query.Get("until"); untilStr != "" {
|
||||
log.Printf("filter until: %s", untilStr)
|
||||
until, err := time.Parse(time.RFC3339, untilStr)
|
||||
if err != nil {
|
||||
writeJSON(w, http.StatusBadRequest, JsonError{Error: "invalid until timestamp"})
|
||||
return
|
||||
}
|
||||
filterFunction = acceptUntilTimestamp(until)
|
||||
}
|
||||
// reconstruct state from event log if present
|
||||
grain := checkout.NewCheckoutGrain(id, cart.CartId(id), 0, time.Now(), nil)
|
||||
err := fs.checkoutStorage.LoadEventsFunc(r.Context(), id, grain, filterFunction)
|
||||
if err != nil {
|
||||
writeJSON(w, http.StatusInternalServerError, JsonError{Error: err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
path := filepath.Join(fs.checkoutDataDir, fmt.Sprintf("%d.events.log", id))
|
||||
info, err := os.Stat(path)
|
||||
if err != nil && errors.Is(err, os.ErrNotExist) {
|
||||
writeJSON(w, http.StatusNotFound, JsonError{Error: "checkout not found"})
|
||||
return
|
||||
} else if err != nil {
|
||||
writeJSON(w, http.StatusInternalServerError, JsonError{Error: err.Error()})
|
||||
return
|
||||
}
|
||||
lines, err := readRawLogLines(path)
|
||||
if err != nil {
|
||||
writeJSON(w, http.StatusInternalServerError, JsonError{Error: err.Error()})
|
||||
return
|
||||
}
|
||||
writeJSON(w, http.StatusOK, map[string]any{
|
||||
"id": id,
|
||||
"checkoutId": checkout.CheckoutId(id).String(),
|
||||
"state": grain,
|
||||
"mutations": lines,
|
||||
"meta": map[string]any{
|
||||
"size": info.Size(),
|
||||
"modified": info.ModTime(),
|
||||
"path": path,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user