ducky/subscriptions

Paddy 2015-09-30 Parent:b240b6123548

14:fb2c0e498e37 Go to Latest

ducky/subscriptions/subscription_memstore.go

Update with comments for all exported functions. We now have golint-approved comments for all the exported functions in the subscriptions package. Next challenge: all the sub-packages!

History
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 }