-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathmain.go
183 lines (158 loc) · 3.9 KB
/
main.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
package main
import (
"bytes"
"context"
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
"sync"
"time"
"github.com/sirupsen/logrus"
)
// Cart represents a shopping cart.
type Cart struct {
Cases []*Case
}
// Case represents a case of beer.
type Case struct {
Count int
Beer *Beer
Price float64
}
// Beer represents a type of beer.
type Beer struct {
Brand string
Name string
Ounces float64
}
// NewCart initializes a new shopping cart.
func NewCart() *Cart {
return &Cart{}
}
// AddCase adds a case of beer to the shopping cart.
func (c *Cart) AddCase(beerCase *Case) {
c.Cases = append(c.Cases, beerCase)
}
// Subtotal calculates the subtotal of the shopping cart.
func (c *Cart) Subtotal() float64 {
var subtotal float64
for _, beerCase := range c.Cases {
subtotal += beerCase.Price
}
return subtotal
}
// ProcessPayment sends the total to an external payment api.
func ProcessPayment(paymentServer string, total float64) ([]byte, error) {
b, _ := json.Marshal(total)
resp, err := http.Post(paymentServer, "application/json", bytes.NewBuffer(b))
if err != nil {
return nil, err
}
defer resp.Body.Close()
if resp.StatusCode >= 400 {
return nil, fmt.Errorf("payment server error: %d", resp.StatusCode)
}
return ioutil.ReadAll(resp.Body)
}
// PlaceOrder places the order in the warehouse.
func (o *OrderHandler) PlaceOrder(ctx context.Context, cart *Cart) error {
o.ProcessedOrders = append(o.ProcessedOrders, cart)
return nil
}
// OrderHandler represents a concurrent order handler.
type OrderHandler struct {
ProcessedOrders []*Cart
messageChan chan interface{}
}
var logger = logrus.WithFields(logrus.Fields{
"component": "beer",
})
// startOrderHandler listens to the message channel and handles incoming orders.
func (o *OrderHandler) startOrderHandler(ctx context.Context) {
for {
msg, ok := <-o.messageChan
if !ok {
logger.Debug("message channel closed")
return
}
cart, ok := msg.(*Cart)
if ok {
if err := o.PlaceOrder(ctx, cart); err != nil {
logger.WithError(err).Error("error placing order")
continue
}
logger.Info("successfully placed order")
continue
}
logger.WithField("msg", msg).Errorf("received invalid message on message channel")
}
}
// Subscription represents a shopping cart.
type Subscription struct {
cart *Cart
interval time.Duration
messageChan chan interface{}
mu sync.Mutex
}
// GetCart safely retrieves the subscriptions shopping cart.
func (s *Subscription) GetCart() *Cart {
s.mu.Lock()
defer s.mu.Unlock()
return s.cart
}
// SetCart safely sets the subscriptions shopping cart.
func (s *Subscription) SetCart(c *Cart) {
s.mu.Lock()
defer s.mu.Unlock()
s.cart = c
}
// GetInterval safely retrieves the subscriptions interval.
func (s *Subscription) GetInterval() time.Duration {
s.mu.Lock()
defer s.mu.Unlock()
return s.interval
}
// SetInterval safely sets the subscriptions interval.
func (s *Subscription) SetInterval(t time.Duration) {
s.mu.Lock()
defer s.mu.Unlock()
s.interval = t
}
// startSubscriptionTimer starts a timer and fires the cart to the
// order handler when the order is ready.
func (s *Subscription) startSubscriptionTimer(ctx context.Context) {
ticker := time.NewTicker(s.GetInterval())
defer ticker.Stop()
for {
select {
case <-ctx.Done():
return
case <-ticker.C:
s.messageChan <- s.GetCart()
}
}
}
// FixtureBeer creates a Beer fixture for use in test.
func FixtureBeer(brand string, name string, ounces float64) *Beer {
return &Beer{
Brand: brand,
Name: name,
Ounces: ounces,
}
}
// FixtureCase creates a Case fixture for use in test.
func FixtureCase(count int, beer *Beer, price float64) *Case {
return &Case{
Count: count,
Beer: beer,
Price: price,
}
}
// FixtureCart creates a Cart fixture for use in test.
func FixtureCart() *Cart {
return &Cart{
Cases: []*Case{FixtureCase(4, FixtureBeer("Duvel", "Tripel Hop", 11.0), 14)},
}
}
func main() {}