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.
| paddy@0 | 1 package subscriptions |
| paddy@0 | 2 |
| paddy@0 | 3 import ( |
| paddy@0 | 4 "code.secondbit.org/uuid.hg" |
| paddy@0 | 5 ) |
| paddy@0 | 6 |
| paddy@2 | 7 func stripeSubscriptionInMemstore(stripeSubscription string, m *Memstore) bool { |
| paddy@0 | 8 for _, sub := range m.subscriptions { |
| paddy@2 | 9 if sub.StripeSubscription == stripeSubscription { |
| paddy@0 | 10 return true |
| paddy@0 | 11 } |
| paddy@0 | 12 } |
| paddy@0 | 13 return false |
| paddy@0 | 14 } |
| paddy@0 | 15 |
| paddy@3 | 16 func (m *Memstore) CreateSubscription(sub Subscription) error { |
| paddy@0 | 17 m.subscriptionLock.Lock() |
| paddy@0 | 18 defer m.subscriptionLock.Unlock() |
| paddy@0 | 19 |
| paddy@1 | 20 if _, ok := m.subscriptions[sub.UserID.String()]; ok { |
| paddy@0 | 21 return ErrSubscriptionAlreadyExists |
| paddy@0 | 22 } |
| paddy@2 | 23 if stripeSubscriptionInMemstore(sub.StripeSubscription, m) { |
| paddy@2 | 24 return ErrStripeSubscriptionAlreadyExists |
| paddy@0 | 25 } |
| paddy@1 | 26 m.subscriptions[sub.UserID.String()] = sub |
| paddy@0 | 27 return nil |
| paddy@0 | 28 } |
| paddy@0 | 29 |
| paddy@3 | 30 func (m *Memstore) UpdateSubscription(id uuid.ID, change SubscriptionChange) error { |
| paddy@0 | 31 if change.IsEmpty() { |
| paddy@0 | 32 return ErrSubscriptionChangeEmpty |
| paddy@0 | 33 } |
| paddy@0 | 34 |
| paddy@0 | 35 m.subscriptionLock.Lock() |
| paddy@0 | 36 defer m.subscriptionLock.Unlock() |
| paddy@0 | 37 |
| paddy@0 | 38 s, ok := m.subscriptions[id.String()] |
| paddy@0 | 39 if !ok { |
| paddy@0 | 40 return ErrSubscriptionNotFound |
| paddy@0 | 41 } |
| paddy@2 | 42 if change.StripeSubscription != nil { |
| paddy@2 | 43 if stripeSubscriptionInMemstore(*change.StripeSubscription, m) { |
| paddy@2 | 44 return ErrStripeSubscriptionAlreadyExists |
| paddy@0 | 45 } |
| paddy@0 | 46 } |
| paddy@0 | 47 s.ApplyChange(change) |
| paddy@0 | 48 m.subscriptions[id.String()] = s |
| paddy@0 | 49 return nil |
| paddy@0 | 50 } |
| paddy@0 | 51 |
| paddy@3 | 52 func (m *Memstore) DeleteSubscription(id uuid.ID) error { |
| paddy@0 | 53 m.subscriptionLock.Lock() |
| paddy@0 | 54 defer m.subscriptionLock.Unlock() |
| paddy@0 | 55 |
| paddy@0 | 56 _, ok := m.subscriptions[id.String()] |
| paddy@0 | 57 if !ok { |
| paddy@0 | 58 return ErrSubscriptionNotFound |
| paddy@0 | 59 } |
| paddy@0 | 60 delete(m.subscriptions, id.String()) |
| paddy@0 | 61 return nil |
| paddy@0 | 62 } |
| paddy@0 | 63 |
| paddy@3 | 64 func (m *Memstore) GetSubscriptions(ids []uuid.ID) (map[string]Subscription, error) { |
| paddy@0 | 65 if len(ids) < 1 { |
| paddy@0 | 66 return map[string]Subscription{}, ErrNoSubscriptionID |
| paddy@0 | 67 } |
| paddy@0 | 68 m.subscriptionLock.RLock() |
| paddy@0 | 69 defer m.subscriptionLock.RUnlock() |
| paddy@0 | 70 |
| paddy@0 | 71 result := map[string]Subscription{} |
| paddy@0 | 72 |
| paddy@0 | 73 for _, id := range ids { |
| paddy@0 | 74 s, ok := m.subscriptions[id.String()] |
| paddy@0 | 75 if !ok { |
| paddy@0 | 76 continue |
| paddy@0 | 77 } |
| paddy@1 | 78 result[s.UserID.String()] = s |
| paddy@0 | 79 } |
| paddy@0 | 80 return result, nil |
| paddy@0 | 81 } |
| paddy@0 | 82 |
| paddy@3 | 83 func (m *Memstore) GetSubscriptionStats() (SubscriptionStats, error) { |
| paddy@0 | 84 m.subscriptionLock.RLock() |
| paddy@0 | 85 defer m.subscriptionLock.RUnlock() |
| paddy@0 | 86 |
| paddy@2 | 87 stats := SubscriptionStats{ |
| paddy@2 | 88 Plans: map[string]int64{}, |
| paddy@2 | 89 } |
| paddy@1 | 90 |
| paddy@0 | 91 for _, subscription := range m.subscriptions { |
| paddy@1 | 92 stats.Number++ |
| paddy@2 | 93 stats.Plans[subscription.Plan]++ |
| paddy@0 | 94 |
| paddy@2 | 95 if subscription.Canceling { |
| paddy@2 | 96 stats.Canceling++ |
| paddy@2 | 97 } |
| paddy@2 | 98 if subscription.Status == "past_due" || subscription.Status == "unpaid" { |
| paddy@2 | 99 stats.Failing++ |
| paddy@2 | 100 } |
| paddy@1 | 101 } |
| paddy@1 | 102 return stats, nil |
| paddy@0 | 103 } |