ducky/subscriptions
2015-10-04
Parent:fb2c0e498e37
ducky/subscriptions/subscription_memstore.go
Make api subpackage golint-passing. Add comments to all the exported functions, methods, and variables in the api subpackage, to make golint happy. Also, make the individual endpoints in the api subpackage unexported, as there's no real use case for exporting them. The handlers depend on the placeholders in the endpoint, so we need them to be controlled in unison, which means it's probably a bad idea to declare the route outside of the API package. And the only reason to expose the Handler is so people can declare custom endpoints.
| 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@14 | 16 // CreateSubscription stores the passed Subscription in the Memstore. If |
| paddy@14 | 17 // a Subscription sharing the same UserID already exists, an |
| paddy@14 | 18 // ErrSubscriptionAlreadyExists error will be returned. If a Subscription |
| paddy@14 | 19 // sharing the same StripeSubscription already exists, an |
| paddy@14 | 20 // ErrStripeSubscriptionAlreadyExists error will be returned. |
| paddy@3 | 21 func (m *Memstore) CreateSubscription(sub Subscription) error { |
| paddy@0 | 22 m.subscriptionLock.Lock() |
| paddy@0 | 23 defer m.subscriptionLock.Unlock() |
| paddy@0 | 24 |
| paddy@1 | 25 if _, ok := m.subscriptions[sub.UserID.String()]; ok { |
| paddy@0 | 26 return ErrSubscriptionAlreadyExists |
| paddy@0 | 27 } |
| paddy@2 | 28 if stripeSubscriptionInMemstore(sub.StripeSubscription, m) { |
| paddy@2 | 29 return ErrStripeSubscriptionAlreadyExists |
| paddy@0 | 30 } |
| paddy@1 | 31 m.subscriptions[sub.UserID.String()] = sub |
| paddy@0 | 32 return nil |
| paddy@0 | 33 } |
| paddy@0 | 34 |
| paddy@14 | 35 // UpdateSubscription applies the SubscriptionChange passed to the Subscription |
| paddy@14 | 36 // stored in the Memstore associated with the passed ID. If change is empty, |
| paddy@14 | 37 // an ErrSubscriptionChangeEmpty error is returned. If no Subscription is found |
| paddy@14 | 38 // in the Memstore with the passed ID, an ErrSubscriptionNotFound error is returned. |
| paddy@14 | 39 // If change is updating the StripeSubscription, and a Subscription in the Memstore |
| paddy@14 | 40 // already has that value set for StripeSubscription, an |
| paddy@14 | 41 // ErrStripeSubscriptionAlreadyExists error is returned. |
| paddy@3 | 42 func (m *Memstore) UpdateSubscription(id uuid.ID, change SubscriptionChange) error { |
| paddy@0 | 43 if change.IsEmpty() { |
| paddy@0 | 44 return ErrSubscriptionChangeEmpty |
| paddy@0 | 45 } |
| paddy@0 | 46 |
| paddy@0 | 47 m.subscriptionLock.Lock() |
| paddy@0 | 48 defer m.subscriptionLock.Unlock() |
| paddy@0 | 49 |
| paddy@0 | 50 s, ok := m.subscriptions[id.String()] |
| paddy@0 | 51 if !ok { |
| paddy@0 | 52 return ErrSubscriptionNotFound |
| paddy@0 | 53 } |
| paddy@2 | 54 if change.StripeSubscription != nil { |
| paddy@2 | 55 if stripeSubscriptionInMemstore(*change.StripeSubscription, m) { |
| paddy@2 | 56 return ErrStripeSubscriptionAlreadyExists |
| paddy@0 | 57 } |
| paddy@0 | 58 } |
| paddy@0 | 59 s.ApplyChange(change) |
| paddy@0 | 60 m.subscriptions[id.String()] = s |
| paddy@0 | 61 return nil |
| paddy@0 | 62 } |
| paddy@0 | 63 |
| paddy@14 | 64 // DeleteSubscription removes the Subscription stored in the Memstore associated |
| paddy@14 | 65 // with the passed ID from the Memstore. If no Subscription is found |
| paddy@14 | 66 // in the Memstore with the passed ID, an ErrSubscriptionNotFound error is returned. |
| paddy@3 | 67 func (m *Memstore) DeleteSubscription(id uuid.ID) error { |
| paddy@0 | 68 m.subscriptionLock.Lock() |
| paddy@0 | 69 defer m.subscriptionLock.Unlock() |
| paddy@0 | 70 |
| paddy@0 | 71 _, ok := m.subscriptions[id.String()] |
| paddy@0 | 72 if !ok { |
| paddy@0 | 73 return ErrSubscriptionNotFound |
| paddy@0 | 74 } |
| paddy@0 | 75 delete(m.subscriptions, id.String()) |
| paddy@0 | 76 return nil |
| paddy@0 | 77 } |
| paddy@0 | 78 |
| paddy@14 | 79 // GetSubscriptions retrieves the Subscriptions stored in the Memstore associated |
| paddy@14 | 80 // with the passed IDs. If no IDs are passed, an ErrNoSubscriptionID error is |
| paddy@14 | 81 // returned. No matter how many of the IDs are found (including none), a map is |
| paddy@14 | 82 // returned, with the key being a String()ed version of the ID for the Subscription in |
| paddy@14 | 83 // the value. If no error is returned, the map will represent all of the Subscriptions// matching the passed IDs that exist in the Memstore, even if it's empty. |
| paddy@3 | 84 func (m *Memstore) GetSubscriptions(ids []uuid.ID) (map[string]Subscription, error) { |
| paddy@0 | 85 if len(ids) < 1 { |
| paddy@0 | 86 return map[string]Subscription{}, ErrNoSubscriptionID |
| paddy@0 | 87 } |
| paddy@0 | 88 m.subscriptionLock.RLock() |
| paddy@0 | 89 defer m.subscriptionLock.RUnlock() |
| paddy@0 | 90 |
| paddy@0 | 91 result := map[string]Subscription{} |
| paddy@0 | 92 |
| paddy@0 | 93 for _, id := range ids { |
| paddy@0 | 94 s, ok := m.subscriptions[id.String()] |
| paddy@0 | 95 if !ok { |
| paddy@0 | 96 continue |
| paddy@0 | 97 } |
| paddy@1 | 98 result[s.UserID.String()] = s |
| paddy@0 | 99 } |
| paddy@0 | 100 return result, nil |
| paddy@0 | 101 } |
| paddy@0 | 102 |
| paddy@14 | 103 // GetSubscriptionStats returns statistics about the subscription data stored in the |
| paddy@14 | 104 // Memstore as a SubscriptionStats variable. The number of Subscriptions, the |
| paddy@14 | 105 // breakdown of how many Subscriptions belong to each plan, the number of |
| paddy@14 | 106 // Subscriptions that are canceling, and the number of Subscriptions whose payment |
| paddy@14 | 107 // information is failing are all tracked. |
| paddy@3 | 108 func (m *Memstore) GetSubscriptionStats() (SubscriptionStats, error) { |
| paddy@0 | 109 m.subscriptionLock.RLock() |
| paddy@0 | 110 defer m.subscriptionLock.RUnlock() |
| paddy@0 | 111 |
| paddy@2 | 112 stats := SubscriptionStats{ |
| paddy@2 | 113 Plans: map[string]int64{}, |
| paddy@2 | 114 } |
| paddy@1 | 115 |
| paddy@0 | 116 for _, subscription := range m.subscriptions { |
| paddy@1 | 117 stats.Number++ |
| paddy@2 | 118 stats.Plans[subscription.Plan]++ |
| paddy@0 | 119 |
| paddy@2 | 120 if subscription.Canceling { |
| paddy@2 | 121 stats.Canceling++ |
| paddy@2 | 122 } |
| paddy@2 | 123 if subscription.Status == "past_due" || subscription.Status == "unpaid" { |
| paddy@2 | 124 stats.Failing++ |
| paddy@2 | 125 } |
| paddy@1 | 126 } |
| paddy@1 | 127 return stats, nil |
| paddy@0 | 128 } |