ducky/subscriptions
2015-06-22
Child:c4cfceb2f2fb
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.
1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/api/subscription_handlers.go Mon Jun 22 18:50:02 2015 -0400 1.3 @@ -0,0 +1,122 @@ 1.4 +package api 1.5 + 1.6 +import ( 1.7 + "net/http" 1.8 + 1.9 + "code.secondbit.org/api.hg" 1.10 + "code.secondbit.org/ducky/subscriptions.hg" 1.11 + "code.secondbit.org/trout.hg" 1.12 + "code.secondbit.org/uuid.hg" 1.13 + 1.14 + "golang.org/x/net/context" 1.15 +) 1.16 + 1.17 +const ( 1.18 + SubscriptionScope = "subscriptions" 1.19 + SubscriptionAdminScope = "subscriptions_admin" 1.20 +) 1.21 + 1.22 +func HandleSubscriptions(router *trout.Router, c context.Context) { 1.23 + router.Endpoint("/subscriptions").Methods("POST", "OPTIONS").Handler( 1.24 + api.CORSMiddleware(api.NegotiateMiddleware(api.ContextWrapper(c, CreateSubscriptionHandler)))) 1.25 + router.Endpoint("/subscriptions/{id}").Methods("GET", "OPTIONS").Handler( 1.26 + api.CORSMiddleware(api.NegotiateMiddleware(api.ContextWrapper(c, GetSubscriptionHandler)))) 1.27 +} 1.28 + 1.29 +func CreateSubscriptionHandler(w http.ResponseWriter, r *http.Request, c context.Context) { 1.30 + store, err := getSubscriptionStore(c) 1.31 + if err != nil { 1.32 + api.Encode(w, r, http.StatusInternalServerError, Response{Errors: api.ActOfGodError}) 1.33 + return 1.34 + } 1.35 + stripe, err := getStripeClient(c) 1.36 + if err != nil { 1.37 + api.Encode(w, r, http.StatusInternalServerError, Response{Errors: api.ActOfGodError}) 1.38 + return 1.39 + } 1.40 + if !api.CheckScopes(r, SubscriptionScope) { 1.41 + api.Encode(w, r, http.StatusUnauthorized, Response{Errors: []api.RequestError{{Slug: api.RequestErrAccessDenied}}}) 1.42 + return 1.43 + } 1.44 + userID, err := api.AuthUser(r) 1.45 + if err != nil { 1.46 + api.Encode(w, r, http.StatusUnauthorized, Response{Errors: []api.RequestError{{Slug: api.RequestErrAccessDenied}}}) 1.47 + return 1.48 + } 1.49 + var req subscriptions.SubscriptionRequest 1.50 + err = api.Decode(r, &req) 1.51 + if err != nil { 1.52 + api.Encode(w, r, http.StatusBadRequest, Response{Errors: api.InvalidFormatError}) 1.53 + return 1.54 + } 1.55 + errs := req.Validate(userID, api.CheckScopes(r, SubscriptionAdminScope)) 1.56 + if len(errs) != 0 { 1.57 + api.Encode(w, r, http.StatusBadRequest, Response{Errors: errs}) 1.58 + return 1.59 + } 1.60 + // TODO: need some way of validating the email they sent actually belongs to them 1.61 + sub, err := subscriptions.New(req, stripe, store) 1.62 + if err != nil { 1.63 + var rErr api.RequestError 1.64 + var code int 1.65 + switch err { 1.66 + case subscriptions.ErrSubscriptionAlreadyExists: 1.67 + rErr = api.RequestError{Slug: api.RequestErrConflict, Field: "/user_id"} 1.68 + code = http.StatusBadRequest 1.69 + case subscriptions.ErrStripeSubscriptionAlreadyExists: 1.70 + rErr = api.RequestError{Slug: api.RequestErrConflict, Field: "/stripe_token"} 1.71 + code = http.StatusBadRequest 1.72 + default: 1.73 + rErr = api.RequestError{Slug: api.RequestErrActOfGod} 1.74 + code = http.StatusInternalServerError 1.75 + } 1.76 + api.Encode(w, r, code, Response{Errors: []api.RequestError{rErr}}) 1.77 + return 1.78 + } 1.79 + resp := Response{Subscriptions: []subscriptions.Subscription{sub}} 1.80 + api.Encode(w, r, http.StatusCreated, resp) 1.81 +} 1.82 + 1.83 +func GetSubscriptionHandler(w http.ResponseWriter, r *http.Request, c context.Context) { 1.84 + store, err := getSubscriptionStore(c) 1.85 + if err != nil { 1.86 + api.Encode(w, r, http.StatusInternalServerError, Response{Errors: api.ActOfGodError}) 1.87 + return 1.88 + } 1.89 + vars := trout.RequestVars(r) 1.90 + rawID := vars.Get("id") 1.91 + if rawID == "" { 1.92 + api.Encode(w, r, http.StatusBadRequest, Response{Errors: []api.RequestError{{Slug: api.RequestErrMissing, Param: "id"}}}) 1.93 + return 1.94 + } 1.95 + id, err := uuid.Parse(rawID) 1.96 + if err != nil { 1.97 + api.Encode(w, r, http.StatusBadRequest, Response{Errors: []api.RequestError{{Slug: api.RequestErrInvalidFormat, Param: "id"}}}) 1.98 + return 1.99 + } 1.100 + if !api.CheckScopes(r, SubscriptionScope) { 1.101 + api.Encode(w, r, http.StatusUnauthorized, Response{Errors: []api.RequestError{{Slug: api.RequestErrAccessDenied}}}) 1.102 + return 1.103 + } 1.104 + userID, err := api.AuthUser(r) 1.105 + if err != nil { 1.106 + api.Encode(w, r, http.StatusUnauthorized, Response{Errors: []api.RequestError{{Slug: api.RequestErrAccessDenied}}}) 1.107 + return 1.108 + } 1.109 + if !id.Equal(userID) && !api.CheckScopes(r, SubscriptionAdminScope) { 1.110 + api.Encode(w, r, http.StatusUnauthorized, Response{Errors: []api.RequestError{{Slug: api.RequestErrAccessDenied}}}) 1.111 + return 1.112 + } 1.113 + subs, err := store.GetSubscriptions([]uuid.ID{id}) 1.114 + if err != nil { 1.115 + api.Encode(w, r, http.StatusInternalServerError, Response{Errors: api.ActOfGodError}) 1.116 + return 1.117 + } 1.118 + sub, ok := subs[id.String()] 1.119 + if !ok { 1.120 + api.Encode(w, r, http.StatusNotFound, Response{Errors: []api.RequestError{{Slug: api.RequestErrNotFound, Param: "id"}}}) 1.121 + return 1.122 + } 1.123 + resp := Response{Subscriptions: []subscriptions.Subscription{sub}} 1.124 + api.Encode(w, r, http.StatusOK, resp) 1.125 +}