ducky/subscriptions

Paddy 2015-06-22 Child:c4cfceb2f2fb

4:36e90e828dd0 Go to Latest

ducky/subscriptions/api/subscription_handlers.go

Add an API and subscriptionsd . Create a barebones implementation of the API, including only methods to create a Subscription and retrieve the Subscription associated with a user. Also create a subscriptiond service that will bootstrap the service and stores, and get everything stood up.

History
1 package api
3 import (
4 "net/http"
6 "code.secondbit.org/api.hg"
7 "code.secondbit.org/ducky/subscriptions.hg"
8 "code.secondbit.org/trout.hg"
9 "code.secondbit.org/uuid.hg"
11 "golang.org/x/net/context"
12 )
14 const (
15 SubscriptionScope = "subscriptions"
16 SubscriptionAdminScope = "subscriptions_admin"
17 )
19 func HandleSubscriptions(router *trout.Router, c context.Context) {
20 router.Endpoint("/subscriptions").Methods("POST", "OPTIONS").Handler(
21 api.CORSMiddleware(api.NegotiateMiddleware(api.ContextWrapper(c, CreateSubscriptionHandler))))
22 router.Endpoint("/subscriptions/{id}").Methods("GET", "OPTIONS").Handler(
23 api.CORSMiddleware(api.NegotiateMiddleware(api.ContextWrapper(c, GetSubscriptionHandler))))
24 }
26 func CreateSubscriptionHandler(w http.ResponseWriter, r *http.Request, c context.Context) {
27 store, err := getSubscriptionStore(c)
28 if err != nil {
29 api.Encode(w, r, http.StatusInternalServerError, Response{Errors: api.ActOfGodError})
30 return
31 }
32 stripe, err := getStripeClient(c)
33 if err != nil {
34 api.Encode(w, r, http.StatusInternalServerError, Response{Errors: api.ActOfGodError})
35 return
36 }
37 if !api.CheckScopes(r, SubscriptionScope) {
38 api.Encode(w, r, http.StatusUnauthorized, Response{Errors: []api.RequestError{{Slug: api.RequestErrAccessDenied}}})
39 return
40 }
41 userID, err := api.AuthUser(r)
42 if err != nil {
43 api.Encode(w, r, http.StatusUnauthorized, Response{Errors: []api.RequestError{{Slug: api.RequestErrAccessDenied}}})
44 return
45 }
46 var req subscriptions.SubscriptionRequest
47 err = api.Decode(r, &req)
48 if err != nil {
49 api.Encode(w, r, http.StatusBadRequest, Response{Errors: api.InvalidFormatError})
50 return
51 }
52 errs := req.Validate(userID, api.CheckScopes(r, SubscriptionAdminScope))
53 if len(errs) != 0 {
54 api.Encode(w, r, http.StatusBadRequest, Response{Errors: errs})
55 return
56 }
57 // TODO: need some way of validating the email they sent actually belongs to them
58 sub, err := subscriptions.New(req, stripe, store)
59 if err != nil {
60 var rErr api.RequestError
61 var code int
62 switch err {
63 case subscriptions.ErrSubscriptionAlreadyExists:
64 rErr = api.RequestError{Slug: api.RequestErrConflict, Field: "/user_id"}
65 code = http.StatusBadRequest
66 case subscriptions.ErrStripeSubscriptionAlreadyExists:
67 rErr = api.RequestError{Slug: api.RequestErrConflict, Field: "/stripe_token"}
68 code = http.StatusBadRequest
69 default:
70 rErr = api.RequestError{Slug: api.RequestErrActOfGod}
71 code = http.StatusInternalServerError
72 }
73 api.Encode(w, r, code, Response{Errors: []api.RequestError{rErr}})
74 return
75 }
76 resp := Response{Subscriptions: []subscriptions.Subscription{sub}}
77 api.Encode(w, r, http.StatusCreated, resp)
78 }
80 func GetSubscriptionHandler(w http.ResponseWriter, r *http.Request, c context.Context) {
81 store, err := getSubscriptionStore(c)
82 if err != nil {
83 api.Encode(w, r, http.StatusInternalServerError, Response{Errors: api.ActOfGodError})
84 return
85 }
86 vars := trout.RequestVars(r)
87 rawID := vars.Get("id")
88 if rawID == "" {
89 api.Encode(w, r, http.StatusBadRequest, Response{Errors: []api.RequestError{{Slug: api.RequestErrMissing, Param: "id"}}})
90 return
91 }
92 id, err := uuid.Parse(rawID)
93 if err != nil {
94 api.Encode(w, r, http.StatusBadRequest, Response{Errors: []api.RequestError{{Slug: api.RequestErrInvalidFormat, Param: "id"}}})
95 return
96 }
97 if !api.CheckScopes(r, SubscriptionScope) {
98 api.Encode(w, r, http.StatusUnauthorized, Response{Errors: []api.RequestError{{Slug: api.RequestErrAccessDenied}}})
99 return
100 }
101 userID, err := api.AuthUser(r)
102 if err != nil {
103 api.Encode(w, r, http.StatusUnauthorized, Response{Errors: []api.RequestError{{Slug: api.RequestErrAccessDenied}}})
104 return
105 }
106 if !id.Equal(userID) && !api.CheckScopes(r, SubscriptionAdminScope) {
107 api.Encode(w, r, http.StatusUnauthorized, Response{Errors: []api.RequestError{{Slug: api.RequestErrAccessDenied}}})
108 return
109 }
110 subs, err := store.GetSubscriptions([]uuid.ID{id})
111 if err != nil {
112 api.Encode(w, r, http.StatusInternalServerError, Response{Errors: api.ActOfGodError})
113 return
114 }
115 sub, ok := subs[id.String()]
116 if !ok {
117 api.Encode(w, r, http.StatusNotFound, Response{Errors: []api.RequestError{{Slug: api.RequestErrNotFound, Param: "id"}}})
118 return
119 }
120 resp := Response{Subscriptions: []subscriptions.Subscription{sub}}
121 api.Encode(w, r, http.StatusOK, resp)
122 }