@@ -8,116 +8,123 @@ import (
88 "net/http"
99 "os"
1010 "time"
11-
12- "github.com/joho/godotenv"
1311)
1412
1513type ProductClient struct {
1614 base string
17- c * http.Client
1815 adminKey string
16+ c * http.Client
1917}
2018
21- func NewProductClientFromEnv () * ProductClient {
22- godotenv .Load ()
19+ func NewProductClient () * ProductClient {
2320 base := os .Getenv ("PRODUCT_SVC_BASE" )
2421 if base == "" {
25- base = "http://localhost:8080"
22+ // Docker DNS default
23+ base = "http://product:8080"
24+ }
25+
26+ adminKey := os .Getenv ("ADMIN_KEY" )
27+ if adminKey == "" {
28+ adminKey = os .Getenv ("PRODUCT_ADMIN_KEY" )
2629 }
2730
28- adminKey := os .Getenv ("PRODUCT_ADMIN_KEY" )
2931 if adminKey == "" {
30- fmt .Println ("WARNING: PRODUCT_ADMIN_KEY is not set" )
32+ fmt .Println ("WARNING: ADMIN_KEY not set for ProductClient " )
3133 }
3234
3335 return & ProductClient {
3436 base : base ,
35- c : & http.Client {Timeout : 5 * time .Second },
3637 adminKey : adminKey ,
38+ c : & http.Client {
39+ Timeout : 5 * time .Second ,
40+ },
3741 }
3842}
3943
40- func (p * ProductClient ) GetProductDetail (ctx context.Context , productID string ) (map [string ]any , error ) {
44+ /* -------------------- READ APIs -------------------- */
45+
46+ func (p * ProductClient ) GetProductDetail (
47+ ctx context.Context ,
48+ productID string ,
49+ ) (map [string ]any , error ) {
50+
4151 url := fmt .Sprintf ("%s/v1/products/%s" , p .base , productID )
42- req , _ := http .NewRequestWithContext (ctx , "GET" , url , nil )
52+
53+ req , _ := http .NewRequestWithContext (ctx , http .MethodGet , url , nil )
54+
4355 resp , err := p .c .Do (req )
4456 if err != nil {
4557 return nil , err
4658 }
4759 defer resp .Body .Close ()
48- if resp .StatusCode != 200 {
60+
61+ if resp .StatusCode != http .StatusOK {
4962 return nil , fmt .Errorf ("product service returned %d" , resp .StatusCode )
5063 }
64+
5165 var out map [string ]any
5266 if err := json .NewDecoder (resp .Body ).Decode (& out ); err != nil {
5367 return nil , err
5468 }
69+
5570 return out , nil
5671}
5772
58- func (p * ProductClient ) DeductStock (ctx context.Context , variantID string , quantity int ) error {
59- url := fmt .Sprintf ("%s/v1/admin/variants/%s/deduct" , p .base , variantID )
60- body := map [string ]any {"quantity" : quantity }
61- b , _ := json .Marshal (body )
62-
63- req , _ := http .NewRequestWithContext (ctx , "POST" , url , bytes .NewReader (b ))
64- req .Header .Set ("Content-Type" , "application/json" )
65-
66- // 💥 THE FIX: FORWARD ADMIN KEY
67- req .Header .Set ("X-ADMIN-KEY" , p .adminKey )
68-
69- resp , err := p .c .Do (req )
70- if err != nil {
71- return err
72- }
73- defer resp .Body .Close ()
73+ /* -------------------- STOCK APIs -------------------- */
7474
75- if resp .StatusCode != 200 {
76- return fmt .Errorf ("deduct returned %d" , resp .StatusCode )
77- }
78- return nil
75+ func (p * ProductClient ) ReserveStock (
76+ ctx context.Context ,
77+ variantID string ,
78+ quantity int ,
79+ ) error {
80+ return p .postStockAction (ctx , "reserve" , variantID , quantity )
7981}
8082
81- func (p * ProductClient ) ReserveStock (ctx context.Context , variantID string , quantity int ) error {
82- url := fmt .Sprintf ("%s/v1/admin/variants/%s/reserve" , p .base , variantID )
83- body := map [string ]any {"quantity" : quantity }
84- b , _ := json .Marshal (body )
85-
86- req , _ := http .NewRequestWithContext (ctx , "POST" , url , bytes .NewReader (b ))
87- req .Header .Set ("Content-Type" , "application/json" )
88-
89- // 💥 THE FIX AGAIN
90- req .Header .Set ("X-ADMIN-KEY" , p .adminKey )
91-
92- resp , err := p .c .Do (req )
93- if err != nil {
94- return err
95- }
96- defer resp .Body .Close ()
83+ func (p * ProductClient ) DeductStock (
84+ ctx context.Context ,
85+ variantID string ,
86+ quantity int ,
87+ ) error {
88+ return p .postStockAction (ctx , "deduct" , variantID , quantity )
89+ }
9790
98- if resp .StatusCode != 200 {
99- return fmt .Errorf ("reserve returned %d" , resp .StatusCode )
100- }
101- return nil
91+ func (p * ProductClient ) ReleaseStock (
92+ ctx context.Context ,
93+ variantID string ,
94+ quantity int ,
95+ ) error {
96+ return p .postStockAction (ctx , "release" , variantID , quantity )
10297}
10398
104- func (p * ProductClient ) DeductReservedStock (
99+ /* -------------------- INTERNAL HELPER -------------------- */
100+
101+ func (p * ProductClient ) postStockAction (
105102 ctx context.Context ,
103+ action string ,
106104 variantID string ,
107105 quantity int ,
108106) error {
109107
110- url := fmt .Sprintf ("%s/v1/admin/variants/%s/deduct" , p .base , variantID )
108+ url := fmt .Sprintf (
109+ "%s/v1/admin/variants/%s/%s" ,
110+ p .base ,
111+ variantID ,
112+ action ,
113+ )
111114
112- body := map [string ]any {
115+ body := map [string ]int {
113116 "quantity" : quantity ,
114117 }
115118 b , _ := json .Marshal (body )
116119
117- req , _ := http .NewRequestWithContext (ctx , "POST" , url , bytes .NewReader (b ))
118- req .Header .Set ("Content-Type" , "application/json" )
120+ req , _ := http .NewRequestWithContext (
121+ ctx ,
122+ http .MethodPost ,
123+ url ,
124+ bytes .NewReader (b ),
125+ )
119126
120- // INTERNAL AUTH
127+ req . Header . Set ( "Content-Type" , "application/json" )
121128 req .Header .Set ("X-ADMIN-KEY" , p .adminKey )
122129
123130 resp , err := p .c .Do (req )
@@ -127,7 +134,11 @@ func (p *ProductClient) DeductReservedStock(
127134 defer resp .Body .Close ()
128135
129136 if resp .StatusCode != http .StatusOK {
130- return fmt .Errorf ("deduct reserved stock failed: %d" , resp .StatusCode )
137+ return fmt .Errorf (
138+ "product %s failed: %d" ,
139+ action ,
140+ resp .StatusCode ,
141+ )
131142 }
132143
133144 return nil
0 commit comments