Skip to content

Commit 68b16dd

Browse files
added product queries
1 parent 496e56c commit 68b16dd

File tree

12 files changed

+542
-30
lines changed

12 files changed

+542
-30
lines changed

cmd/cli.go

Lines changed: 102 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -29,25 +29,40 @@ func main() {
2929
fmt.Println(align("coupons") + "Get coupons")
3030
fmt.Println(align("recalls") + "Get recalls")
3131
fmt.Println(align("discounts -id <market-id> [-raw] [-catGroup]") + "Get discounts")
32+
fmt.Println(align("categories -id <market-id> [-printAll]") + "Get product categories")
33+
fmt.Println(align("products -id <market-id> [-category <category> -search <query>] [-page <page>] [-perPage <productsPerPage>]") + "\n" + align("") + "Get products from a category or by query")
3234
fmt.Println("\nSubcommand-Flags:")
3335
fmt.Println(align("-query <query>") + "Search query. Can be a city, postal code, street, etc.")
3436
fmt.Println(align("-id <market-id>") + "ID of the market or discount. Get it from marketsearch.")
3537
fmt.Println(align("-raw") + "Whether you want raw output format (directly from the API) or parsed.")
36-
fmt.Println(align("-catGroup") + "Group by product category instead of rewe-app-category")
38+
fmt.Println(align("-catGroup") + "Group by product category instead of rewe-app category")
39+
fmt.Println(align("-printAll") + "Print all available product categories (very many)")
40+
fmt.Println(align("-category <category>") + "The slug of the category to fetch the products from")
41+
fmt.Println(align("-search <query>") + "Search query for products. Can be any term or EANs for example")
42+
fmt.Println(align("-page <page>") + "Page number for pagination. Starts at 1, default 1. The amount of available pages is included in the output")
43+
fmt.Println(align("-perPage <productsPerPage>") + "Number of products per page. Default 30")
3744
fmt.Println("\nExamples:")
45+
fmt.Println(" rewerse.exe -cert cert.pem -key p.key marketsearch -query Köln")
3846
fmt.Println(" rewerse.exe marketsearch -query Köln")
3947
fmt.Println(" rewerse.exe marketdetails -id 1763153")
4048
fmt.Println(" rewerse.exe discounts -id 1763153")
4149
fmt.Println(" rewerse.exe -json discounts -id 1763153 -raw")
4250
fmt.Println(" rewerse.exe discounts -id 1763153 -catGroup")
43-
fmt.Println(" rewerse.exe -cert cert.pem -key p.key marketsearch -query Köln")
51+
fmt.Println(" rewerse.exe categories -id 831002 -printAll")
52+
fmt.Println(" rewerse.exe products -id 831002 -category kueche-haushalt -page 2 -perPage 10")
53+
fmt.Println(" rewerse.exe products -id 831002 -search Karotten")
4454
}
4555

4656
certFile := flag.String("cert", "", "Path to the certificate file")
4757
keyFile := flag.String("key", "", "Path to the key file")
4858
jsonOutput := flag.Bool("json", false, "Output in JSON format (default false)")
4959
flag.Parse()
5060

61+
if flag.NArg() == 0 {
62+
flag.Usage()
63+
os.Exit(1)
64+
}
65+
5166
var crt, key string
5267
if *certFile == "" {
5368
crt = "certificate.pem"
@@ -63,16 +78,11 @@ func main() {
6378
var err error
6479
err = rewerse.SetCertificate(crt, key)
6580
if err != nil && *certFile == "" && *keyFile == "" {
66-
flag.Usage()
81+
fmt.Println("Please provide the paths to the certificate and key files.\n\nrewerse.exe -cert cert.pem -key p.key [...]")
6782
os.Exit(1)
6883
}
6984
hdl(err)
7085

71-
if flag.NArg() == 0 {
72-
flag.Usage()
73-
os.Exit(1)
74-
}
75-
7686
marketSearchCmd := flag.NewFlagSet("marketsearch", flag.ExitOnError)
7787
marketSearchQuery := marketSearchCmd.String("query", "", "Search query")
7888

@@ -84,6 +94,17 @@ func main() {
8494
rawOutput := discountsCmd.Bool("raw", false, "Whether to return raw API Data")
8595
groupProduct := discountsCmd.Bool("catGroup", false, "Group by product category instead of app-category")
8696

97+
pcategories := flag.NewFlagSet("categories", flag.ExitOnError)
98+
pcategoriesMarketID := pcategories.String("id", "", "Market-ID")
99+
all := pcategories.Bool("printAll", false, "Print all available product categories")
100+
101+
products := flag.NewFlagSet("products", flag.ExitOnError)
102+
productsMarketID := products.String("id", "", "Market-ID")
103+
productsCategory := products.String("category", "", "Category slug")
104+
productsSearch := products.String("search", "", "Search query")
105+
productsPage := products.Int("page", 0, "Page number")
106+
productsPerPage := products.Int("perPage", 0, "Products per page")
107+
87108
var data any
88109
switch flag.Arg(0) {
89110
case "marketsearch":
@@ -118,9 +139,73 @@ func main() {
118139
if *groupProduct {
119140
data = data.(rewerse.Discounts).GroupByProductCategory()
120141
}
142+
case "categories":
143+
hdl(pcategories.Parse(flag.Args()[1:]))
144+
if *pcategoriesMarketID == "" {
145+
fmt.Println("Expected market ID")
146+
os.Exit(1)
147+
}
148+
149+
var so rewerse.ShopOverview
150+
so, err = rewerse.GetShopOverview(*pcategoriesMarketID)
151+
hdl(err)
152+
153+
if *all && *jsonOutput {
154+
fmt.Println("Cannot print all and output in JSON format (json is all by default)")
155+
return
156+
}
157+
158+
if *all {
159+
fmt.Println(so.StringAll())
160+
return
161+
} else if !*jsonOutput {
162+
fmt.Println(so.String())
163+
return
164+
} else {
165+
data = so.ProductCategories
166+
}
167+
case "products":
168+
hdl(products.Parse(flag.Args()[1:]))
169+
if *productsMarketID == "" {
170+
fmt.Println("Expected market ID")
171+
os.Exit(1)
172+
}
173+
174+
if *productsCategory == "" && *productsSearch == "" {
175+
fmt.Println("Expected category or search query")
176+
os.Exit(1)
177+
}
178+
179+
if *productsCategory != "" && *productsSearch != "" {
180+
fmt.Println("Expected either category or search query, not both")
181+
os.Exit(1)
182+
}
183+
184+
var opts *rewerse.ProductOpts
185+
if *productsPage != 0 {
186+
opts = &rewerse.ProductOpts{
187+
Page: *productsPage,
188+
}
189+
}
190+
191+
if *productsPerPage != 0 {
192+
if opts == nil {
193+
opts = &rewerse.ProductOpts{
194+
ObjectsPerPage: *productsPerPage,
195+
}
196+
} else {
197+
opts.ObjectsPerPage = *productsPerPage
198+
}
199+
}
200+
201+
if *productsCategory != "" {
202+
data, err = rewerse.GetCategoryProducts(*productsMarketID, *productsCategory, opts)
203+
} else {
204+
data, err = rewerse.GetProducts(*productsMarketID, *productsSearch, opts)
205+
}
121206

122207
default:
123-
fmt.Println("Expected 'marketsearch', 'marketdetails', 'coupons', 'recalls' or 'discounts' subcommands")
208+
fmt.Println("Expected one of the following subcommands:\n- marketsearch\n- marketdetails\n- coupons\n- recalls\n- discounts\n- categories\n- products")
124209
os.Exit(1)
125210
}
126211

@@ -135,5 +220,12 @@ func main() {
135220
}
136221

137222
func align(s string) string {
138-
return " " + s + strings.Repeat(" ", 50-len(s))
223+
al := 50
224+
if len(s) > al {
225+
al = 0
226+
} else {
227+
al -= len(s)
228+
}
229+
230+
return " " + s + strings.Repeat(" ", al)
139231
}

pkg/category_structs.go

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
package rewerse
2+
3+
import (
4+
"fmt"
5+
"strings"
6+
)
7+
8+
type ProductSearchResults struct {
9+
Data struct {
10+
Products struct {
11+
Pagination struct {
12+
ObjectsPerPage int `json:"objectsPerPage"`
13+
CurrentPage int `json:"currentPage"`
14+
PageCount int `json:"pageCount"`
15+
ObjectCount int `json:"objectCount"`
16+
} `json:"pagination"`
17+
Search struct {
18+
Term struct {
19+
Original string `json:"original"`
20+
Corrected any `json:"corrected"`
21+
} `json:"term"`
22+
} `json:"search"`
23+
Products []Product `json:"products"`
24+
} `json:"products"`
25+
} `json:"data"`
26+
Extensions struct {
27+
HTTP []struct {
28+
Path []string `json:"path"`
29+
Message string `json:"message"`
30+
StatusCode int `json:"statusCode"`
31+
ResponseBody any `json:"responseBody"`
32+
} `json:"http"`
33+
} `json:"extensions"`
34+
}
35+
36+
func (psr ProductSearchResults) String() string {
37+
var s strings.Builder
38+
s.WriteString("Products for query " + psr.Data.Products.Search.Term.Original + "\n")
39+
s.WriteString(fmt.Sprintf("Page %d of %d\n", psr.Data.Products.Pagination.CurrentPage, psr.Data.Products.Pagination.PageCount))
40+
s.WriteString(sep("") + "\n")
41+
for _, product := range psr.Data.Products.Products {
42+
s.WriteString(product.Title + "\n")
43+
}
44+
return s.String()
45+
}
46+
47+
type Product struct {
48+
ProductID string `json:"productId"`
49+
Title string `json:"title"`
50+
DepositLabel any `json:"depositLabel"`
51+
ImageURL string `json:"imageURL"`
52+
Attributes struct {
53+
IsBulkyGood bool `json:"isBulkyGood"`
54+
IsOrganic bool `json:"isOrganic"`
55+
IsVegan bool `json:"isVegan"`
56+
IsVegetarian bool `json:"isVegetarian"`
57+
IsDairyFree bool `json:"isDairyFree"`
58+
IsGlutenFree bool `json:"isGlutenFree"`
59+
IsBiocide bool `json:"isBiocide"`
60+
IsAgeRestricted any `json:"isAgeRestricted"`
61+
IsRegional bool `json:"isRegional"`
62+
IsNew bool `json:"isNew"`
63+
} `json:"attributes"`
64+
OrderLimit int `json:"orderLimit"`
65+
Categories []string `json:"categories"`
66+
DetailsViewRequired bool `json:"detailsViewRequired"`
67+
ArticleID string `json:"articleId"`
68+
Listing struct {
69+
ListingID string `json:"listingId"`
70+
ListingVersion int `json:"listingVersion"`
71+
CurrentRetailPrice int `json:"currentRetailPrice"`
72+
TotalRefundPrice any `json:"totalRefundPrice"`
73+
Grammage string `json:"grammage"`
74+
Discount any `json:"discount"`
75+
LoyaltyBonus any `json:"loyaltyBonus"`
76+
} `json:"listing"`
77+
Advertisement any `json:"advertisement"`
78+
}

pkg/coupons.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ type OneScanCoupons struct {
2626
}
2727

2828
func (o OneScanCoupons) String() string {
29-
coupons := fmt.Sprintf("%d OneScan-Coupons\n\n", len(o.Coupons))
29+
coupons := fmt.Sprintf("Got %d OneScan-Coupons\n\n", len(o.Coupons))
3030
for _, c := range o.Coupons {
3131
coupons += c.Title + "\n"
3232
coupons += strings.ReplaceAll(c.Description, "\n", " ")
@@ -83,7 +83,7 @@ type Coupons struct {
8383
}
8484

8585
func (cs Coupons) String() string {
86-
coupons := fmt.Sprintf("%d Coupons\n\n", len(cs.Data.GetCoupons.Coupons))
86+
coupons := fmt.Sprintf("Got %d Coupons\n\n", len(cs.Data.GetCoupons.Coupons))
8787
for _, c := range cs.Data.GetCoupons.Coupons {
8888
coupons += c.Title + " // " + c.OfferTitle + "\n"
8989
coupons += c.Subtitle + "\n"

pkg/discounts.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -41,13 +41,13 @@ const (
4141
// It contains the discount categories, which in turn contain the actual discounts.
4242
// I removed various parameters I deemed unnecessary and parsed into different datatypes where it made sense to me.
4343
// The struct Discounts also provides some helper methods.
44-
func GetDiscounts(marketID string) (d Discounts, err error) {
44+
func GetDiscounts(marketID string) (ds Discounts, err error) {
4545
rd, err := GetDiscountsRaw(marketID)
4646
if err != nil {
4747
return
4848
}
4949

50-
d.ValidUntil = time.Unix(int64(rd.Data.Offers.UntilDate)/1000, 0)
50+
ds.ValidUntil = time.Unix(int64(rd.Data.Offers.UntilDate)/1000, 0)
5151
var foundAnyManuf bool // detect if the manufacturer format changed
5252
for _, rawCat := range rd.Data.Offers.Categories {
5353
cat := DiscountCategory{
@@ -101,7 +101,7 @@ func GetDiscounts(marketID string) (d Discounts, err error) {
101101
cat.Offers = append(cat.Offers, discount)
102102
}
103103

104-
d.Categories = append(d.Categories, cat)
104+
ds.Categories = append(ds.Categories, cat)
105105
}
106106

107107
if !foundAnyManuf {

pkg/market_structs.go

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import (
1111
type Markets []Market
1212

1313
func (ms Markets) String() string {
14-
s := "ID Standort\n"
14+
s := "ID Location\n"
1515
for _, m := range ms {
1616
s += m.String() + "\n"
1717
}
@@ -140,5 +140,16 @@ func sep(base string) string {
140140
}
141141

142142
func align(s string) string {
143-
return " " + s + ":" + strings.Repeat(" ", 22-len(s))
143+
al := 22
144+
if len(s) > al {
145+
al = 0
146+
} else {
147+
al -= len(s)
148+
}
149+
150+
return " " + s + ":" + strings.Repeat(" ", al)
151+
}
152+
153+
func alignL(s string, n int) string {
154+
return strings.Repeat(" ", n*3) + s
144155
}

pkg/misc.go

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,10 @@ type Recalls []Recall
77

88
func (rs Recalls) String() string {
99
if len(rs) == 0 {
10-
return "Aktuell keine Produktrückrufe"
10+
return "Currently no recalls"
1111
}
1212

13-
recalls := "Produktrückrufe:\n"
13+
recalls := "Recalls:\n"
1414
for _, r := range rs {
1515
recalls += r.String() + "\n"
1616
}
@@ -57,14 +57,14 @@ type RecipeHub struct {
5757

5858
func (rh RecipeHub) String() string {
5959
recipeHub := "Recipe Hub\n\n"
60-
recipeHub += "Rezept des Tages\n--------------------\n" + rh.RecipeOfTheDay.String() + "\n"
60+
recipeHub += "Recipe of the Day\n--------------------\n" + rh.RecipeOfTheDay.String() + "\n"
6161

62-
recipeHub += "Beliebte Rezepte\n--------------------\n"
62+
recipeHub += "Popular Recipes\n--------------------\n"
6363
for _, r := range rh.PopularRecipes {
6464
recipeHub += r.String() + "\n"
6565
}
6666

67-
recipeHub += "Verfügbare Rezept-Kategorien\n--------------------\n"
67+
recipeHub += "Available Categories\n--------------------\n"
6868
for _, c := range rh.Categories {
6969
recipeHub += c.Title + "\n"
7070
}

0 commit comments

Comments
 (0)