ducky/subscriptions
ducky/subscriptions/subscription_memstore.go
Return errors from responses in client. When the client makes a request, non-200 responses _are not_ considered errors. So we need to check the response.Errors property, and if it has errors, _then_ we consider the request to have an error. To make this happen, we created an httpErrors type that fulfills the error interface and just wraps the response Errors property. Then callers can type-cast it and interrogate it.
1 package subscriptions
3 import (
4 "code.secondbit.org/uuid.hg"
5 )
7 func stripeSubscriptionInMemstore(stripeSubscription string, m *Memstore) bool {
8 for _, sub := range m.subscriptions {
9 if sub.StripeSubscription == stripeSubscription {
10 return true
11 }
12 }
13 return false
14 }
16 func (m *Memstore) CreateSubscription(sub Subscription) error {
17 m.subscriptionLock.Lock()
18 defer m.subscriptionLock.Unlock()
20 if _, ok := m.subscriptions[sub.UserID.String()]; ok {
21 return ErrSubscriptionAlreadyExists
22 }
23 if stripeSubscriptionInMemstore(sub.StripeSubscription, m) {
24 return ErrStripeSubscriptionAlreadyExists
25 }
26 m.subscriptions[sub.UserID.String()] = sub
27 return nil
28 }
30 func (m *Memstore) UpdateSubscription(id uuid.ID, change SubscriptionChange) error {
31 if change.IsEmpty() {
32 return ErrSubscriptionChangeEmpty
33 }
35 m.subscriptionLock.Lock()
36 defer m.subscriptionLock.Unlock()
38 s, ok := m.subscriptions[id.String()]
39 if !ok {
40 return ErrSubscriptionNotFound
41 }
42 if change.StripeSubscription != nil {
43 if stripeSubscriptionInMemstore(*change.StripeSubscription, m) {
44 return ErrStripeSubscriptionAlreadyExists
45 }
46 }
47 s.ApplyChange(change)
48 m.subscriptions[id.String()] = s
49 return nil
50 }
52 func (m *Memstore) DeleteSubscription(id uuid.ID) error {
53 m.subscriptionLock.Lock()
54 defer m.subscriptionLock.Unlock()
56 _, ok := m.subscriptions[id.String()]
57 if !ok {
58 return ErrSubscriptionNotFound
59 }
60 delete(m.subscriptions, id.String())
61 return nil
62 }
64 func (m *Memstore) GetSubscriptions(ids []uuid.ID) (map[string]Subscription, error) {
65 if len(ids) < 1 {
66 return map[string]Subscription{}, ErrNoSubscriptionID
67 }
68 m.subscriptionLock.RLock()
69 defer m.subscriptionLock.RUnlock()
71 result := map[string]Subscription{}
73 for _, id := range ids {
74 s, ok := m.subscriptions[id.String()]
75 if !ok {
76 continue
77 }
78 result[s.UserID.String()] = s
79 }
80 return result, nil
81 }
83 func (m *Memstore) GetSubscriptionStats() (SubscriptionStats, error) {
84 m.subscriptionLock.RLock()
85 defer m.subscriptionLock.RUnlock()
87 stats := SubscriptionStats{
88 Plans: map[string]int64{},
89 }
91 for _, subscription := range m.subscriptions {
92 stats.Number++
93 stats.Plans[subscription.Plan]++
95 if subscription.Canceling {
96 stats.Canceling++
97 }
98 if subscription.Status == "past_due" || subscription.Status == "unpaid" {
99 stats.Failing++
100 }
101 }
102 return stats, nil
103 }