ducky/subscriptions

Paddy 2015-07-18 Parent:8eb19bcbf17d Child:7eef47ecc01c

10:2c8250237566 Go to Latest

ducky/subscriptions/client/client.go

Update subscription_creator to use the new strategy. When creating subscriptions through the client, detect when the returned error is saying the account already has a subscription, or the subscription already exists in stripe. Add an UpdateSubscription function that will update a subscription through the API. Update our subscription_creator listener to listen for profile creation and login verification messages. This mainly involved fixing the constants (the system, model, and topic) that the listener for profile creation was listening for. It also meant adding a new updateMessageHandler that listens for login verification, tries to create a subscription that has the user ID and email of the login that was verified, and if a subscription already exists, updates it instead to use the email address that was just verified. This will ensure that users get their receipts automatically emailed to them by Stripe.

History
paddy@7 1 package client
paddy@7 2
paddy@7 3 import (
paddy@7 4 "bytes"
paddy@7 5 "encoding/json"
paddy@7 6 "errors"
paddy@9 7 "fmt"
paddy@7 8 "io"
paddy@7 9 "net/http"
paddy@7 10 "strings"
paddy@7 11 "time"
paddy@7 12
paddy@9 13 commonAPI "code.secondbit.org/api.hg"
paddy@7 14 "code.secondbit.org/uuid.hg"
paddy@7 15
paddy@7 16 "code.secondbit.org/ducky/subscriptions.hg/api"
paddy@7 17 )
paddy@7 18
paddy@7 19 var (
paddy@7 20 ErrNilClient = errors.New("nil client wrapper")
paddy@7 21 ErrNilHTTPClient = errors.New("nil client")
paddy@7 22 )
paddy@7 23
paddy@7 24 type Client struct {
paddy@7 25 client *http.Client
paddy@7 26 address string
paddy@7 27 ID uuid.ID
paddy@7 28 }
paddy@7 29
paddy@7 30 func New(address string, id uuid.ID) *Client {
paddy@7 31 address = strings.TrimRight(address, "/")
paddy@7 32 return &Client{
paddy@7 33 address: address,
paddy@7 34 client: &http.Client{},
paddy@7 35 ID: id,
paddy@7 36 }
paddy@7 37 }
paddy@7 38
paddy@7 39 func (c *Client) do(method, url string, request interface{}, scopes []string, subject uuid.ID) (api.Response, error) {
paddy@7 40 if c == nil {
paddy@7 41 return api.Response{}, ErrNilClient
paddy@7 42 }
paddy@7 43 if c.client == nil {
paddy@7 44 return api.Response{}, ErrNilHTTPClient
paddy@7 45 }
paddy@7 46 var response api.Response
paddy@7 47 if !strings.HasPrefix(url, "http") {
paddy@7 48 url = strings.TrimLeft(url, "/")
paddy@7 49 url = "/" + url
paddy@7 50 url = c.address + url
paddy@7 51 }
paddy@7 52 var body io.Reader
paddy@7 53 if request != nil {
paddy@7 54 data, err := json.Marshal(request)
paddy@7 55 if err != nil {
paddy@7 56 return response, err
paddy@7 57 }
paddy@7 58 body = bytes.NewBuffer(data)
paddy@7 59 }
paddy@7 60 req, err := http.NewRequest(method, url, body)
paddy@7 61 if err != nil {
paddy@7 62 return response, err
paddy@7 63 }
paddy@7 64 req.Header.Set("Ducky-Scope", strings.Join(scopes, " "))
paddy@7 65 req.Header.Set("Ducky-Issuer", c.ID.String())
paddy@7 66 if subject != nil {
paddy@7 67 req.Header.Set("Ducky-Subject", subject.String())
paddy@7 68 }
paddy@7 69 req.Header.Set("Ducky-Expires", time.Now().Add(time.Hour).String())
paddy@7 70 req.Header.Set("Ducky-Issued-At", time.Now().String())
paddy@7 71 req.Header.Set("Ducky-Not-Before", time.Now().Add(-5*time.Minute).String())
paddy@7 72 resp, err := c.client.Do(req)
paddy@7 73 if err != nil {
paddy@7 74 return response, err
paddy@7 75 }
paddy@7 76 defer resp.Body.Close()
paddy@7 77 switch resp.Header.Get("Content-Type") {
paddy@7 78 case "application/json":
paddy@7 79 dec := json.NewDecoder(resp.Body)
paddy@7 80 err = dec.Decode(&response)
paddy@7 81 if err != nil {
paddy@7 82 return response, err
paddy@7 83 }
paddy@7 84 default:
paddy@7 85 dec := json.NewDecoder(resp.Body)
paddy@7 86 err = dec.Decode(&response)
paddy@7 87 if err != nil {
paddy@7 88 return response, err
paddy@7 89 }
paddy@7 90 }
paddy@9 91 if len(response.Errors) > 0 {
paddy@9 92 return response, httpErrors(response.Errors)
paddy@9 93 }
paddy@7 94 return response, nil
paddy@7 95 }
paddy@7 96
paddy@9 97 type httpErrors []commonAPI.RequestError
paddy@9 98
paddy@9 99 func (h httpErrors) Error() string {
paddy@9 100 return fmt.Sprintf("%+#v", h)
paddy@9 101 }
paddy@9 102
paddy@7 103 func (c *Client) Get(url string, scopes []string, subject uuid.ID) (api.Response, error) {
paddy@7 104 return c.do("GET", url, nil, scopes, subject)
paddy@7 105 }
paddy@7 106
paddy@7 107 func (c *Client) Post(url string, request interface{}, scopes []string, subject uuid.ID) (api.Response, error) {
paddy@7 108 return c.do("POST", url, request, scopes, subject)
paddy@7 109 }
paddy@7 110
paddy@7 111 func (c *Client) Patch(url string, request interface{}, scopes []string, subject uuid.ID) (api.Response, error) {
paddy@7 112 return c.do("PATCH", url, request, scopes, subject)
paddy@7 113 }