Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 32 additions & 2 deletions internal/cli/cart_helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -231,11 +231,36 @@ func buildItemPayloadFromAssortment(assortment map[string]any, itemID string) ma
optionGroupIndex[strings.ToLower(groupID)] = group
}
}
// The basket API (and the Wolt app) key options by the item-level option id
// ("id"), while the shared option library is keyed by "option_id". Map one to the other.
itemOptionIDs := map[string]string{}
for _, rawOption := range asSlice(item["options"]) {
option := asMap(rawOption)
if option == nil {
continue
}
libraryID := strings.TrimSpace(asString(option["option_id"]))
itemLevelID := strings.TrimSpace(asString(option["id"]))
if libraryID != "" && itemLevelID != "" {
itemOptionIDs[strings.ToLower(libraryID)] = itemLevelID
}
}
optionGroups := make([]any, 0, len(optionGroupIDs))
for _, groupID := range optionGroupIDs {
if group, ok := optionGroupIndex[strings.ToLower(groupID)]; ok {
optionGroups = append(optionGroups, group)
group, ok := optionGroupIndex[strings.ToLower(groupID)]
if !ok {
continue
}
if itemLevelID, mapped := itemOptionIDs[strings.ToLower(groupID)]; mapped && !strings.EqualFold(itemLevelID, groupID) {
rewritten := make(map[string]any, len(group)+2)
for key, value := range group {
rewritten[key] = value
}
rewritten["id"] = itemLevelID
rewritten["option_id"] = groupID
group = rewritten
}
optionGroups = append(optionGroups, group)
}
if len(optionGroups) == 0 {
optionGroups = asSlice(coalesceAny(item["option_groups"], item["options"]))
Expand Down Expand Up @@ -328,6 +353,11 @@ func resolveOptionGroupToken(
if _, ok := specs[token]; ok {
return token, nil
}
for groupID, spec := range specs {
if spec.OptionID != "" && strings.EqualFold(spec.OptionID, token) {
return groupID, nil
}
}
groupIDs := make([]string, 0, len(specs))
for groupID := range specs {
groupIDs = append(groupIDs, groupID)
Expand Down
54 changes: 54 additions & 0 deletions internal/cli/cart_helpers_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -332,6 +332,60 @@ func TestBuildItemPayloadFromAssortment(t *testing.T) {
}
}

// Wolt keys a dish's options by an item-level "id" and points at the shared
// library group through "option_id". The basket API (and the Wolt app) only
// recognise the item-level id, so the group sent in the basket must carry it.
func TestBuildBasketOptionsUsesItemLevelOptionGroupID(t *testing.T) {
assortment := map[string]any{
"items": []any{
map[string]any{
"id": "item-1",
"name": "Make Your Own Pasta",
"price": 1200,
"options": []any{
map[string]any{"id": "item-grp-pasta", "option_id": "lib-grp-pasta", "name": "Choose your pasta"},
},
},
},
"options": []any{
map[string]any{
"id": "lib-grp-pasta",
"name": "Choose your pasta",
"values": []any{
map[string]any{"id": "val-penne", "name": "penne", "price": 0},
map[string]any{"id": "val-linguine", "name": "linguine", "price": 0},
},
},
},
}
itemPayload := buildItemPayloadFromAssortment(assortment, "item-1")
if itemPayload == nil {
t.Fatalf("expected assortment payload for item")
}

for _, group := range []string{"item-grp-pasta", "lib-grp-pasta", "Choose your pasta"} {
t.Run(group, func(t *testing.T) {
options, err := buildBasketOptions(itemPayload, map[string][]optionSelection{
group: {{ValueID: "val-linguine", Count: 1}},
})
if err != nil {
t.Fatalf("buildBasketOptions() error = %v", err)
}
if len(options) != 1 {
t.Fatalf("expected one option group, got %#v", options)
}
got := asMap(options[0])
if asString(got["id"]) != "item-grp-pasta" {
t.Fatalf("basket option group id = %v, want item-level id item-grp-pasta", got["id"])
}
values := asSlice(got["values"])
if len(values) != 1 || asString(asMap(values[0])["id"]) != "val-linguine" {
t.Fatalf("unexpected values: %#v", values)
}
})
}
}

func TestBuildItemPayloadFromAssortmentMatchesUppercaseID(t *testing.T) {
const itemID = "abcdefabcdefabcdefabcdef"
payload := buildItemPayloadFromAssortment(
Expand Down
4 changes: 4 additions & 0 deletions internal/service/payloadutil/payloadutil.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ type OptionValueSpec struct {

type OptionGroupSpec struct {
ID string
OptionID string // shared option-library id, when it differs from ID
Name string
Required bool
MinSelect int
Expand Down Expand Up @@ -595,6 +596,9 @@ func ExtractOptionSpecs(payload map[string]any) map[string]OptionGroupSpec {
spec.ID = canonicalGroupID
spec.Values = map[string]OptionValueSpec{}
}
if libraryID := strings.TrimSpace(String(group["option_id"])); spec.OptionID == "" && libraryID != "" && !strings.EqualFold(libraryID, canonicalGroupID) {
spec.OptionID = libraryID
}
if spec.Name == "" {
spec.Name = String(CoalesceAny(group["name"], group["title"]))
}
Expand Down
Loading