Feature 17: Shopping List and Supply Archive (#140)
Co-authored-by: orchestrator-agent <20+orchestrator-agent@code.cubecraftcreations.com>
This commit was merged in pull request #140.
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
package server
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"log/slog"
|
||||
"net/http"
|
||||
@@ -9,6 +10,7 @@ import (
|
||||
|
||||
"code.cubecraftlabs.com/Maniacs_Mansion/Meal-Quest/internal/gcal"
|
||||
"code.cubecraftlabs.com/Maniacs_Mansion/Meal-Quest/internal/plan"
|
||||
"code.cubecraftlabs.com/Maniacs_Mansion/Meal-Quest/internal/shopping"
|
||||
"code.cubecraftlabs.com/Maniacs_Mansion/Meal-Quest/internal/store"
|
||||
)
|
||||
|
||||
@@ -94,20 +96,72 @@ func putPlanDay(logger *slog.Logger, svc *plan.Service) http.HandlerFunc {
|
||||
}
|
||||
}
|
||||
|
||||
// pushedPlan is the POST /plans/{weekStart}/push-calendar answer: the
|
||||
// plan as synced, and what became of the week's Supply run.
|
||||
type pushedPlan struct {
|
||||
plan.Plan
|
||||
SupplyRun pushedSupplyRun `json:"supply_run"`
|
||||
}
|
||||
|
||||
// pushedSupplyRun says whether a Push to Calendar made the week's Supply
|
||||
// run. status is generated when it did; archived when the week's Supply
|
||||
// run is in the Supply archive and was left as it was; or failed when it
|
||||
// was not made. A failed Supply run's error is plan_changed when the plan
|
||||
// was edited after it was pushed, so the Supply run was not made from a
|
||||
// plan Calendar does not show and pushing again makes it; or
|
||||
// supply_run_failed when it could not be made, and POST
|
||||
// /shopping/{weekStart}/generate makes it again. error is null when status
|
||||
// is generated.
|
||||
type pushedSupplyRun struct {
|
||||
Status string `json:"status"`
|
||||
Error *apiError `json:"error"`
|
||||
}
|
||||
|
||||
// postPlanPush pushes the week's plan to the chosen Google calendar
|
||||
// (Push to Calendar) and answers with the plan as synced.
|
||||
func postPlanPush(logger *slog.Logger, svc *plan.Service) http.HandlerFunc {
|
||||
// (Push to Calendar) and, once the push is recorded, makes the week's
|
||||
// Supply run from the plan pushed. It answers with the plan as synced and
|
||||
// the Supply run's outcome.
|
||||
//
|
||||
// The Supply run is made after the push, never as part of it: Calendar
|
||||
// already shows the plan, so a Supply run that cannot be made does not
|
||||
// undo or fail the push. The answer is still 200 with supply_run failed,
|
||||
// and the failure is logged, so nothing claims a Supply run that was not
|
||||
// made. It is made only from the plan as pushed: one edited between the
|
||||
// push and the Supply run is not made into a Supply run.
|
||||
func postPlanPush(logger *slog.Logger, svc *plan.Service, shop *shopping.Service) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
week, ok := weekStartOrFail(w, r, logger)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
p, err := svc.Push(r.Context(), week)
|
||||
p, revision, err := svc.Push(r.Context(), week)
|
||||
if err != nil {
|
||||
writePlanPushError(w, r, logger, err)
|
||||
return
|
||||
}
|
||||
writeJSON(w, r, logger, http.StatusOK, p)
|
||||
// The push is done: make the Supply run even if the client has
|
||||
// gone.
|
||||
ctx := context.WithoutCancel(r.Context())
|
||||
run := pushedSupplyRun{Status: "generated"}
|
||||
if _, err := shop.GeneratePushed(ctx, week, revision); errors.Is(err, shopping.ErrFrozen) {
|
||||
logger.InfoContext(ctx, "Supply run in the Supply archive left as it was after Push to Calendar", "week", week.String())
|
||||
run = pushedSupplyRun{Status: "archived", Error: &apiError{Code: "archived", Message: err.Error()}}
|
||||
} else if errors.Is(err, shopping.ErrPlanChanged) {
|
||||
logger.WarnContext(ctx, "plan edited after Push to Calendar; Supply run not made", "week", week.String())
|
||||
run = pushedSupplyRun{Status: "failed", Error: &apiError{
|
||||
Code: "plan_changed",
|
||||
Message: "the plan is on Calendar as pushed, but it was edited before its Supply run was made, so no Supply run was made from it; push again",
|
||||
Retryable: true,
|
||||
}}
|
||||
} else if err != nil {
|
||||
logger.ErrorContext(ctx, "generate Supply run after Push to Calendar", "week", week.String(), "error", err)
|
||||
run = pushedSupplyRun{Status: "failed", Error: &apiError{
|
||||
Code: "supply_run_failed",
|
||||
Message: "the plan is on Calendar, but its Supply run could not be made; make it again from the Supply run",
|
||||
Retryable: true,
|
||||
}}
|
||||
}
|
||||
writeJSON(w, r, logger, http.StatusOK, pushedPlan{Plan: p, SupplyRun: run})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user