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