Skip to content

Commit

Permalink
Implements endpoint to get subscriptions in `/organizations/{address}…
Browse files Browse the repository at this point in the history
…/subscription`
  • Loading branch information
emmdim committed Oct 9, 2024
1 parent 06803dc commit 6f5c526
Show file tree
Hide file tree
Showing 6 changed files with 123 additions and 19 deletions.
3 changes: 3 additions & 0 deletions api/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,9 @@ func (a *API) initRouter() http.Handler {
// update the organization
log.Infow("new route", "method", "PUT", "path", organizationEndpoint)
r.Put(organizationEndpoint, a.updateOrganizationHandler)
// get organization subscription
log.Infow("new route", "method", "GET", "path", organizationSubscriptionEndpoint)
r.Get(organizationSubscriptionEndpoint, a.getOrganizationSubscriptionHandler)
})

// Public routes
Expand Down
40 changes: 21 additions & 19 deletions api/errors_definition.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,25 +25,27 @@ import (
// Do note that HTTPstatus 204 No Content implies the response body will be empty,
// so the Code and Message will actually be discarded, never sent to the client
var (
ErrUnauthorized = Error{Code: 40001, HTTPstatus: http.StatusUnauthorized, Err: fmt.Errorf("user not authorized")}
ErrEmailMalformed = Error{Code: 40002, HTTPstatus: http.StatusBadRequest, Err: fmt.Errorf("email malformed")}
ErrPasswordTooShort = Error{Code: 40003, HTTPstatus: http.StatusBadRequest, Err: fmt.Errorf("password too short")}
ErrMalformedBody = Error{Code: 40004, HTTPstatus: http.StatusBadRequest, Err: fmt.Errorf("malformed JSON body")}
ErrDuplicateConflict = Error{Code: 40901, HTTPstatus: http.StatusConflict, Err: fmt.Errorf("duplicate conflict")}
ErrInvalidUserData = Error{Code: 40005, HTTPstatus: http.StatusBadRequest, Err: fmt.Errorf("invalid user data")}
ErrCouldNotSignTransaction = Error{Code: 40006, HTTPstatus: http.StatusBadRequest, Err: fmt.Errorf("could not sign transaction")}
ErrInvalidTxFormat = Error{Code: 40007, HTTPstatus: http.StatusBadRequest, Err: fmt.Errorf("invalid transaction format")}
ErrTxTypeNotAllowed = Error{Code: 40008, HTTPstatus: http.StatusBadRequest, Err: fmt.Errorf("transaction type not allowed")}
ErrOrganizationNotFound = Error{Code: 40009, HTTPstatus: http.StatusNotFound, Err: fmt.Errorf("organization not found")}
ErrMalformedURLParam = Error{Code: 40010, HTTPstatus: http.StatusBadRequest, Err: fmt.Errorf("malformed URL parameter")}
ErrNoOrganizationProvided = Error{Code: 40011, HTTPstatus: http.StatusBadRequest, Err: fmt.Errorf("no organization provided")}
ErrNoOrganizations = Error{Code: 40012, HTTPstatus: http.StatusNotFound, Err: fmt.Errorf("this user has not been assigned to any organization")}
ErrInvalidOrganizationData = Error{Code: 40013, HTTPstatus: http.StatusBadRequest, Err: fmt.Errorf("invalid organization data")}
ErrUserNoVerified = Error{Code: 40014, HTTPstatus: http.StatusUnauthorized, Err: fmt.Errorf("user account not verified")}
ErrUserAlreadyVerified = Error{Code: 40015, HTTPstatus: http.StatusBadRequest, Err: fmt.Errorf("user account already verified")}
ErrVerificationCodeExpired = Error{Code: 40016, HTTPstatus: http.StatusUnauthorized, Err: fmt.Errorf("verification code expired")}
ErrVerificationCodeValid = Error{Code: 40017, HTTPstatus: http.StatusBadRequest, Err: fmt.Errorf("last verification code still valid")}
ErrUserNotFound = Error{Code: 40018, HTTPstatus: http.StatusNotFound, Err: fmt.Errorf("user not found")}
ErrUnauthorized = Error{Code: 40001, HTTPstatus: http.StatusUnauthorized, Err: fmt.Errorf("user not authorized")}
ErrEmailMalformed = Error{Code: 40002, HTTPstatus: http.StatusBadRequest, Err: fmt.Errorf("email malformed")}
ErrPasswordTooShort = Error{Code: 40003, HTTPstatus: http.StatusBadRequest, Err: fmt.Errorf("password too short")}
ErrMalformedBody = Error{Code: 40004, HTTPstatus: http.StatusBadRequest, Err: fmt.Errorf("malformed JSON body")}
ErrDuplicateConflict = Error{Code: 40901, HTTPstatus: http.StatusConflict, Err: fmt.Errorf("duplicate conflict")}
ErrInvalidUserData = Error{Code: 40005, HTTPstatus: http.StatusBadRequest, Err: fmt.Errorf("invalid user data")}
ErrCouldNotSignTransaction = Error{Code: 40006, HTTPstatus: http.StatusBadRequest, Err: fmt.Errorf("could not sign transaction")}
ErrInvalidTxFormat = Error{Code: 40007, HTTPstatus: http.StatusBadRequest, Err: fmt.Errorf("invalid transaction format")}
ErrTxTypeNotAllowed = Error{Code: 40008, HTTPstatus: http.StatusBadRequest, Err: fmt.Errorf("transaction type not allowed")}
ErrOrganizationNotFound = Error{Code: 40009, HTTPstatus: http.StatusNotFound, Err: fmt.Errorf("organization not found")}
ErrMalformedURLParam = Error{Code: 40010, HTTPstatus: http.StatusBadRequest, Err: fmt.Errorf("malformed URL parameter")}
ErrNoOrganizationProvided = Error{Code: 40011, HTTPstatus: http.StatusBadRequest, Err: fmt.Errorf("no organization provided")}
ErrNoOrganizations = Error{Code: 40012, HTTPstatus: http.StatusNotFound, Err: fmt.Errorf("this user has not been assigned to any organization")}
ErrInvalidOrganizationData = Error{Code: 40013, HTTPstatus: http.StatusBadRequest, Err: fmt.Errorf("invalid organization data")}
ErrUserNoVerified = Error{Code: 40014, HTTPstatus: http.StatusUnauthorized, Err: fmt.Errorf("user account not verified")}
ErrUserAlreadyVerified = Error{Code: 40015, HTTPstatus: http.StatusBadRequest, Err: fmt.Errorf("user account already verified")}
ErrVerificationCodeExpired = Error{Code: 40016, HTTPstatus: http.StatusUnauthorized, Err: fmt.Errorf("verification code expired")}
ErrVerificationCodeValid = Error{Code: 40017, HTTPstatus: http.StatusBadRequest, Err: fmt.Errorf("last verification code still valid")}
ErrUserNotFound = Error{Code: 40018, HTTPstatus: http.StatusNotFound, Err: fmt.Errorf("user not found")}
ErrNoOrganizationSubscription = Error{Code: 40019, HTTPstatus: http.StatusNotFound, Err: fmt.Errorf("organiation subscription not found")}
ErrrOganizationSubscriptionIncative = Error{Code: 40020, HTTPstatus: http.StatusBadRequest, Err: fmt.Errorf("organiation subscription not active")}

ErrMarshalingServerJSONFailed = Error{Code: 50001, HTTPstatus: http.StatusInternalServerError, Err: fmt.Errorf("marshaling (server-side) JSON failed")}
ErrGenericInternalServerError = Error{Code: 50002, HTTPstatus: http.StatusInternalServerError, Err: fmt.Errorf("internal server error")}
Expand Down
32 changes: 32 additions & 0 deletions api/organizations.go
Original file line number Diff line number Diff line change
Expand Up @@ -234,3 +234,35 @@ func (a *API) updateOrganizationHandler(w http.ResponseWriter, r *http.Request)
}
httpWriteOK(w)
}

// getOrganizationSubscriptionHandler handles the request to get the subscription of an organization.
// It returns the subscription with its information.
func (a *API) getOrganizationSubscriptionHandler(w http.ResponseWriter, r *http.Request) {
// get the organization info from the request context
org, _, ok := a.organizationFromRequest(r)
if !ok {
ErrNoOrganizationProvided.Write(w)
return
}
if org.Subscription == (db.OrganizationSubscription{}) {
ErrNoOrganizationSubscription.Write(w)
return
}
if !org.Subscription.Active ||
(org.Subscription.EndDate.After(time.Now()) && org.Subscription.StartDate.Before(time.Now())) {
ErrrOganizationSubscriptionIncative.Write(w)
return
}
// get the subscription from the database
plan, err := a.db.Subscription(org.Subscription.SubscriptionID)
if err != nil {
ErrGenericInternalServerError.Withf("could not get subscription: %v", err).Write(w)
return
}
info := &OrganizationSubscriptionInfo{
SubcriptionDetails: &org.Subscription,
Usage: &org.Counters,
Plan: plan,
}
httpWriteJSON(w, info)
}
2 changes: 2 additions & 0 deletions api/routes.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,8 @@ const (
organizationEndpoint = "/organizations/{address}"
// GET /organizations/{address}/members to get the organization members
organizationMembersEndpoint = "/organizations/{address}/members"
// GET /organizations/{address}/subscription to get the organization subscription
organizationSubscriptionEndpoint = "/organizations/{address}/subscription"

// subscription routes
// GET /subscriptions to get the subscriptions of an organization
Expand Down
6 changes: 6 additions & 0 deletions api/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -135,3 +135,9 @@ func organizationFromDB(dbOrg, parent *db.Organization) *OrganizationInfo {
Parent: parentOrg,
}
}

type OrganizationSubscriptionInfo struct {
SubcriptionDetails *db.OrganizationSubscription `json:"subscriptionDetails"`
Usage *db.OrganizationCounters `json:"usage"`
Plan *db.Subscription `json:"plan"`
}
59 changes: 59 additions & 0 deletions db/subscriptions.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
[
{
"ID": 1,
"Name": "Basic",
"StripeID": "stripe_123",
"Organization": {
"Memberships": 1,
"SubOrgs": 1
},
"VotingTypes": {
"Approval": true,
"Ranked": true,
"Weighted": true
},
"Features": {
"Personalization": false,
"EmailReminder": false,
"SmsNotification": false
}
},
{
"ID": 2,
"Name": "Pro",
"StripeID": "stripe_456",
"Organization": {
"Memberships": 10,
"SubOrgs": 5
},
"VotingTypes": {
"Approval": true,
"Ranked": false,
"Weighted": true
},
"Features": {
"Personalization": false,
"EmailReminder": false,
"SmsNotification": false
}
},
{
"ID": 3,
"Name": "Ulimited",
"StripeID": "stripe_789",
"Organization": {
"Memberships": 10,
"SubOrgs": 5
},
"VotingTypes": {
"Approval": true,
"Ranked": true,
"Weighted": true
},
"Features": {
"Personalization": true,
"EmailReminder": true,
"SmsNotification": true
}
}
]

0 comments on commit 6f5c526

Please sign in to comment.