ducky/subscriptions

Paddy 2015-06-11 Child:f1a22fc2321d

0:56a2bef197cd Go to Latest

ducky/subscriptions/subscription_memstore.go

First implementation of datastore interface. Define Subscriptions, and the SubscriptionChange type that can modify Subscriptions. Define the subscriptionStore, which describes how Subscriptions are to be created, retrieved, updated, and deleted in/from the storage backend they're stored in. Create a Memstore implementation of the subscriptionStore, which stores all our data in-memory, for use in testing. Write tests to cover the subscriptionStore interface, testing the entire Memstore. We'll plug future storage backends into these tests, to make sure they all behave the same, and to exercise each storage backend without requiring a suite of tests for each. At this point, we have 100% test coverage and no complaints from golint or go vet. I expect it's all downhill from here.

History
paddy@0 1 package subscriptions
paddy@0 2
paddy@0 3 import (
paddy@0 4 "sort"
paddy@0 5 "time"
paddy@0 6
paddy@0 7 "code.secondbit.org/uuid.hg"
paddy@0 8 )
paddy@0 9
paddy@0 10 func stripeCustomerInMemstore(stripeCustomer string, m *Memstore) bool {
paddy@0 11 for _, sub := range m.subscriptions {
paddy@0 12 if sub.StripeCustomer == stripeCustomer {
paddy@0 13 return true
paddy@0 14 }
paddy@0 15 }
paddy@0 16 return false
paddy@0 17 }
paddy@0 18
paddy@0 19 func (m *Memstore) createSubscription(sub Subscription) error {
paddy@0 20 m.subscriptionLock.Lock()
paddy@0 21 defer m.subscriptionLock.Unlock()
paddy@0 22
paddy@0 23 if _, ok := m.subscriptions[sub.ID.String()]; ok {
paddy@0 24 return ErrSubscriptionAlreadyExists
paddy@0 25 }
paddy@0 26 if stripeCustomerInMemstore(sub.StripeCustomer, m) {
paddy@0 27 return ErrStripeCustomerAlreadyExists
paddy@0 28 }
paddy@0 29 m.subscriptions[sub.ID.String()] = sub
paddy@0 30 return nil
paddy@0 31 }
paddy@0 32
paddy@0 33 func (m *Memstore) updateSubscription(id uuid.ID, change SubscriptionChange) error {
paddy@0 34 if change.IsEmpty() {
paddy@0 35 return ErrSubscriptionChangeEmpty
paddy@0 36 }
paddy@0 37
paddy@0 38 m.subscriptionLock.Lock()
paddy@0 39 defer m.subscriptionLock.Unlock()
paddy@0 40
paddy@0 41 s, ok := m.subscriptions[id.String()]
paddy@0 42 if !ok {
paddy@0 43 return ErrSubscriptionNotFound
paddy@0 44 }
paddy@0 45 if change.StripeCustomer != nil {
paddy@0 46 if stripeCustomerInMemstore(*change.StripeCustomer, m) {
paddy@0 47 return ErrStripeCustomerAlreadyExists
paddy@0 48 }
paddy@0 49 }
paddy@0 50 s.ApplyChange(change)
paddy@0 51 m.subscriptions[id.String()] = s
paddy@0 52 return nil
paddy@0 53 }
paddy@0 54
paddy@0 55 func (m *Memstore) deleteSubscription(id uuid.ID) error {
paddy@0 56 m.subscriptionLock.Lock()
paddy@0 57 defer m.subscriptionLock.Unlock()
paddy@0 58
paddy@0 59 _, ok := m.subscriptions[id.String()]
paddy@0 60 if !ok {
paddy@0 61 return ErrSubscriptionNotFound
paddy@0 62 }
paddy@0 63 delete(m.subscriptions, id.String())
paddy@0 64 return nil
paddy@0 65 }
paddy@0 66
paddy@0 67 func (m *Memstore) listSubscriptionsLastChargedBefore(cutoff time.Time) ([]Subscription, error) {
paddy@0 68 m.subscriptionLock.RLock()
paddy@0 69 defer m.subscriptionLock.RUnlock()
paddy@0 70
paddy@0 71 var result []Subscription
paddy@0 72 for _, s := range m.subscriptions {
paddy@0 73 if cutoff.Before(s.LastCharged) {
paddy@0 74 continue
paddy@0 75 }
paddy@0 76 result = append(result, s)
paddy@0 77 }
paddy@0 78
paddy@0 79 sorted := ByLastChargeDate(result)
paddy@0 80 sort.Sort(sorted)
paddy@0 81 result = []Subscription(sorted)
paddy@0 82
paddy@0 83 return result, nil
paddy@0 84 }
paddy@0 85
paddy@0 86 func (m *Memstore) getSubscriptions(ids []uuid.ID) (map[string]Subscription, error) {
paddy@0 87 if len(ids) < 1 {
paddy@0 88 return map[string]Subscription{}, ErrNoSubscriptionID
paddy@0 89 }
paddy@0 90 m.subscriptionLock.RLock()
paddy@0 91 defer m.subscriptionLock.RUnlock()
paddy@0 92
paddy@0 93 result := map[string]Subscription{}
paddy@0 94
paddy@0 95 for _, id := range ids {
paddy@0 96 s, ok := m.subscriptions[id.String()]
paddy@0 97 if !ok {
paddy@0 98 continue
paddy@0 99 }
paddy@0 100 result[s.ID.String()] = s
paddy@0 101 }
paddy@0 102 return result, nil
paddy@0 103 }
paddy@0 104
paddy@0 105 func (m *Memstore) getSubscriptionByUser(id uuid.ID) (Subscription, error) {
paddy@0 106 m.subscriptionLock.RLock()
paddy@0 107 defer m.subscriptionLock.RUnlock()
paddy@0 108
paddy@0 109 for _, subscription := range m.subscriptions {
paddy@0 110 if subscription.UserID.Equal(id) {
paddy@0 111 return subscription, nil
paddy@0 112 }
paddy@0 113 }
paddy@0 114
paddy@0 115 return Subscription{}, ErrSubscriptionNotFound
paddy@0 116 }