From 72cd1c1b074c296cf9a918a1da27a46c4ab6e3c3 Mon Sep 17 00:00:00 2001 From: emmdim Date: Thu, 7 Nov 2024 14:07:01 +0100 Subject: [PATCH] Improves naming differentiating subscriptions and plans --- api/organizations.go | 12 ++-- api/stripe.go | 14 ++--- api/subscriptions.go | 2 +- api/types.go | 2 +- db/helpers.go | 16 +++--- db/mongo.go | 4 +- db/mongo_types.go | 4 +- db/organizations.go | 2 +- db/organizations_test.go | 10 ++-- db/subscriptions.go | 118 +++++++++++++++++++-------------------- db/subscriptions_test.go | 48 ++++++++-------- db/types.go | 30 +++++----- 12 files changed, 131 insertions(+), 131 deletions(-) diff --git a/api/organizations.go b/api/organizations.go index 4ed164e..c24f5eb 100644 --- a/api/organizations.go +++ b/api/organizations.go @@ -69,16 +69,16 @@ func (a *API) createOrganizationHandler(w http.ResponseWriter, r *http.Request) parentOrg = orgInfo.Parent.Address } // find default plan - defaultPlan, err := a.db.DefaultSubscription() + defaultPlan, err := a.db.DefaultPlan() if err != nil || defaultPlan == nil { ErrNoDefaultPLan.WithErr((err)).Write(w) return } subscription := &db.OrganizationSubscription{ - SubscriptionID: defaultPlan.ID, - StartDate: time.Now(), - Active: true, - MaxCensusSize: defaultPlan.Organization.CensusSize, + PlanID: defaultPlan.ID, + StartDate: time.Now(), + Active: true, + MaxCensusSize: defaultPlan.Organization.CensusSize, } // create the organization if err := a.db.SetOrganization(&db.Organization{ @@ -485,7 +485,7 @@ func (a *API) getOrganizationSubscriptionHandler(w http.ResponseWriter, r *http. return } // get the subscription from the database - plan, err := a.db.Subscription(org.Subscription.SubscriptionID) + plan, err := a.db.Plan(org.Subscription.PlanID) if err != nil { ErrGenericInternalServerError.Withf("could not get subscription: %v", err).Write(w) return diff --git a/api/stripe.go b/api/stripe.go index a459e50..5689fb5 100644 --- a/api/stripe.go +++ b/api/stripe.go @@ -49,7 +49,7 @@ func (a *API) handleWebhook(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusBadRequest) return } - dbSubscription, err := a.db.SubscriptionByStripeId(subscription.Items.Data[0].Plan.Product.ID) + dbSubscription, err := a.db.PlanByStripeId(subscription.Items.Data[0].Plan.Product.ID) if err != nil || dbSubscription == nil { log.Warnf("Could not update subscription %s, a corresponding subscription was not found.", subscription.ID) @@ -62,12 +62,12 @@ func (a *API) handleWebhook(w http.ResponseWriter, r *http.Request) { renewalDate := time.Unix(subscription.BillingCycleAnchor, 0) organizationSubscription := &db.OrganizationSubscription{ - SubscriptionID: dbSubscription.ID, - StartDate: startDate, - EndDate: endDate, - RenewalDate: renewalDate, - Active: subscription.Status == "active", - MaxCensusSize: int(subscription.Items.Data[0].Quantity), + PlanID: dbSubscription.ID, + StartDate: startDate, + EndDate: endDate, + RenewalDate: renewalDate, + Active: subscription.Status == "active", + MaxCensusSize: int(subscription.Items.Data[0].Quantity), } // TODO will only worked for new subscriptions diff --git a/api/subscriptions.go b/api/subscriptions.go index 4d4bade..d3cc0bb 100644 --- a/api/subscriptions.go +++ b/api/subscriptions.go @@ -8,7 +8,7 @@ import ( // It returns the list of subscriptions with their information. func (a *API) getSubscriptionsHandler(w http.ResponseWriter, r *http.Request) { // get the subscritions from the database - subscriptions, err := a.db.Subscriptions() + subscriptions, err := a.db.Plans() if err != nil { ErrGenericInternalServerError.Withf("could not get subscriptions: %v", err).Write(w) return diff --git a/api/types.go b/api/types.go index 0ddabd9..d04b7f6 100644 --- a/api/types.go +++ b/api/types.go @@ -180,5 +180,5 @@ func organizationFromDB(dbOrg, parent *db.Organization) *OrganizationInfo { type OrganizationSubscriptionInfo struct { SubcriptionDetails *db.OrganizationSubscription `json:"subscriptionDetails"` Usage *db.OrganizationCounters `json:"usage"` - Plan *db.Subscription `json:"plan"` + Plan *db.Plan `json:"plan"` } diff --git a/db/helpers.go b/db/helpers.go index d831616..f40899b 100644 --- a/db/helpers.go +++ b/db/helpers.go @@ -27,7 +27,7 @@ func (ms *MongoStorage) initCollections(database string) error { } log.Infow("current collections", "collections", currentCollections) log.Infow("reading subscriptions from file %s", ms.subscriptionsFile) - loadedSubscriptions, err := readSubscriptionJSON(ms.subscriptionsFile) + loadedPlans, err := readPlanJSON(ms.subscriptionsFile) if err != nil { return err } @@ -70,11 +70,11 @@ func (ms *MongoStorage) initCollections(database string) error { } if name == "subscriptions" { var subscriptions []interface{} - for _, sub := range loadedSubscriptions { + for _, sub := range loadedPlans { subscriptions = append(subscriptions, sub) } count, err := ms.client.Database(database).Collection(name).InsertMany(ctx, subscriptions) - if err != nil || len(count.InsertedIDs) != len(loadedSubscriptions) { + if err != nil || len(count.InsertedIDs) != len(loadedPlans) { return nil, fmt.Errorf("failed to insert subscriptions: %w", err) } } @@ -98,7 +98,7 @@ func (ms *MongoStorage) initCollections(database string) error { return err } // subscriptions collection - if ms.subscriptions, err = getCollection("subscriptions"); err != nil { + if ms.plans, err = getCollection("plans"); err != nil { return err } return nil @@ -222,9 +222,9 @@ func dynamicUpdateDocument(item interface{}, alwaysUpdateTags []string) (bson.M, return bson.M{"$set": update}, nil } -// readSubscriptionJSON reads a JSON file with an array of subscritpions -// and return it as a Subscription array -func readSubscriptionJSON(subscriptionsFile string) ([]*Subscription, error) { +// readPlanJSON reads a JSON file with an array of subscritpions +// and return it as a Plan array +func readPlanJSON(subscriptionsFile string) ([]*Plan, error) { log.Warnf("Reading subscriptions from %s", subscriptionsFile) file, err := root.Assets.Open(fmt.Sprintf("assets/%s", subscriptionsFile)) if err != nil { @@ -243,7 +243,7 @@ func readSubscriptionJSON(subscriptionsFile string) ([]*Subscription, error) { // Create a JSON decoder decoder := json.NewDecoder(file) - var subscriptions []*Subscription + var subscriptions []*Plan err = decoder.Decode(&subscriptions) if err != nil { return nil, err diff --git a/db/mongo.go b/db/mongo.go index df0fa04..9757102 100644 --- a/db/mongo.go +++ b/db/mongo.go @@ -26,7 +26,7 @@ type MongoStorage struct { verifications *mongo.Collection organizations *mongo.Collection organizationInvites *mongo.Collection - subscriptions *mongo.Collection + plans *mongo.Collection } type Options struct { @@ -116,7 +116,7 @@ func (ms *MongoStorage) Reset() error { return err } // drop subscriptions collection - if err := ms.subscriptions.Drop(ctx); err != nil { + if err := ms.plans.Drop(ctx); err != nil { return err } // init the collections diff --git a/db/mongo_types.go b/db/mongo_types.go index 5a2bde7..fd8be57 100644 --- a/db/mongo_types.go +++ b/db/mongo_types.go @@ -12,8 +12,8 @@ type OrganizationCollection struct { Organizations []Organization `json:"organizations" bson:"organizations"` } -type SubscriptionCollection struct { - Subscriptions []Subscription `json:"subscriptions" bson:"subscriptions"` +type PlanCollection struct { + Plans []Plan `json:"plans" bson:"plans"` } type OrganizationInvitesCollection struct { diff --git a/db/organizations.go b/db/organizations.go index dcaac9f..9ea3ae4 100644 --- a/db/organizations.go +++ b/db/organizations.go @@ -191,7 +191,7 @@ func (ms *MongoStorage) OrganizationsMembers(address string) ([]User, error) { // the given address. If an error occurs, it returns the error. This method must // be called with the keysLock held. func (ms *MongoStorage) AddSubscriptionToOrganization(address string, orgSubscription *OrganizationSubscription) error { - if _, err := ms.Subscription(orgSubscription.SubscriptionID); err != nil { + if _, err := ms.Plan(orgSubscription.PlanID); err != nil { return ErrInvalidData } ms.keysLock.Lock() diff --git a/db/organizations_test.go b/db/organizations_test.go index 3a4d407..b73c5e2 100644 --- a/db/organizations_test.go +++ b/db/organizations_test.go @@ -181,7 +181,7 @@ func TestOrganizationsMembers(t *testing.T) { c.Assert(singleMember.Email, qt.Equals, testUserEmail) } -func TestAddOrganizationSubscription(t *testing.T) { +func TestAddOrganizationPlan(t *testing.T) { defer func() { if err := db.Reset(); err != nil { t.Error(err) @@ -189,12 +189,12 @@ func TestAddOrganizationSubscription(t *testing.T) { }() c := qt.New(t) // create a new organization - address := "orgToAddSubscription" + address := "orgToAddPlan" c.Assert(db.SetOrganization(&Organization{ Address: address, }), qt.IsNil) // add a subscription to the organization - subscriptionName := "testSubscription" + subscriptionName := "testPlan" startDate := time.Now() endDate := startDate.AddDate(1, 0, 0) active := true @@ -206,14 +206,14 @@ func TestAddOrganizationSubscription(t *testing.T) { } // using a non existing subscription should fail c.Assert(db.AddSubscriptionToOrganization(address, orgSubscription), qt.IsNotNil) - subscriptionID, err := db.SetSubscription(&Subscription{ + subscriptionID, err := db.SetPlan(&Plan{ Name: subscriptionName, StripeID: stripeID, }) if err != nil { t.Error(err) } - orgSubscription.SubscriptionID = subscriptionID + orgSubscription.PlanID = subscriptionID c.Assert(db.AddSubscriptionToOrganization(address, orgSubscription), qt.IsNil) // retrieve the organization and check the subscription details org, _, err := db.Organization(address, false) diff --git a/db/subscriptions.go b/db/subscriptions.go index 5d49474..79e400d 100644 --- a/db/subscriptions.go +++ b/db/subscriptions.go @@ -11,160 +11,160 @@ import ( "go.vocdoni.io/dvote/log" ) -// nextSubscriptionID internal method returns the next available subsbscription ID. If an error +// nextPlanID internal method returns the next available subsbscription ID. If an error // occurs, it returns the error. This method must be called with the keysLock // held. -func (ms *MongoStorage) nextSubscriptionID(ctx context.Context) (uint64, error) { - var subscription Subscription +func (ms *MongoStorage) nextPlanID(ctx context.Context) (uint64, error) { + var plan Plan opts := options.FindOne().SetSort(bson.D{{Key: "_id", Value: -1}}) - if err := ms.subscriptions.FindOne(ctx, bson.M{}, opts).Decode(&subscription); err != nil { + if err := ms.plans.FindOne(ctx, bson.M{}, opts).Decode(&plan); err != nil { if err == mongo.ErrNoDocuments { return 1, nil } else { return 0, err } } - return subscription.ID + 1, nil + return plan.ID + 1, nil } -// SetSubscription method creates or updates the subscription in the database. -// If the subscription already exists, it updates the fields that have changed. -func (ms *MongoStorage) SetSubscription(subscription *Subscription) (uint64, error) { +// SetPlan method creates or updates the plan in the database. +// If the plan already exists, it updates the fields that have changed. +func (ms *MongoStorage) SetPlan(plan *Plan) (uint64, error) { ms.keysLock.Lock() defer ms.keysLock.Unlock() // create a context with a timeout ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) defer cancel() - nextID, err := ms.nextSubscriptionID(ctx) + nextID, err := ms.nextPlanID(ctx) if err != nil { return 0, err } - if subscription.ID > 0 { - if subscription.ID >= nextID { + if plan.ID > 0 { + if plan.ID >= nextID { return 0, ErrInvalidData } - updateDoc, err := dynamicUpdateDocument(subscription, nil) + updateDoc, err := dynamicUpdateDocument(plan, nil) if err != nil { return 0, err } // set upsert to true to create the document if it doesn't exist - if _, err := ms.subscriptions.UpdateOne(ctx, bson.M{"_id": subscription.ID}, updateDoc); err != nil { + if _, err := ms.plans.UpdateOne(ctx, bson.M{"_id": plan.ID}, updateDoc); err != nil { return 0, err } } else { - subscription.ID = nextID - if _, err := ms.subscriptions.InsertOne(ctx, subscription); err != nil { + plan.ID = nextID + if _, err := ms.plans.InsertOne(ctx, plan); err != nil { return 0, err } } - return subscription.ID, nil + return plan.ID, nil } -// Subscription method returns the subscription with the given ID. If the -// subscription doesn't exist, it returns the specific error. -func (ms *MongoStorage) Subscription(subscriptionID uint64) (*Subscription, error) { +// Plan method returns the plan with the given ID. If the +// plan doesn't exist, it returns the specific error. +func (ms *MongoStorage) Plan(planID uint64) (*Plan, error) { ms.keysLock.RLock() defer ms.keysLock.RUnlock() // create a context with a timeout ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) defer cancel() - // find the subscription in the database - filter := bson.M{"_id": subscriptionID} - subscription := &Subscription{} - err := ms.subscriptions.FindOne(ctx, filter).Decode(subscription) + // find the plan in the database + filter := bson.M{"_id": planID} + plan := &Plan{} + err := ms.plans.FindOne(ctx, filter).Decode(plan) if err != nil { if err == mongo.ErrNoDocuments { - return nil, ErrNotFound // Subscription not found + return nil, ErrNotFound // Plan not found } - return nil, errors.New("failed to get subscription") + return nil, errors.New("failed to get plan") } - return subscription, nil + return plan, nil } -// SubscriptionByStripeId method returns the subscription with the given stripe ID. If the -// subscription doesn't exist, it returns the specific error. -func (ms *MongoStorage) SubscriptionByStripeId(stripeID string) (*Subscription, error) { +// PlanByStripeId method returns the plan with the given stripe ID. If the +// plan doesn't exist, it returns the specific error. +func (ms *MongoStorage) PlanByStripeId(stripeID string) (*Plan, error) { ms.keysLock.RLock() defer ms.keysLock.RUnlock() // create a context with a timeout ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) defer cancel() - // find the subscription in the database + // find the plan in the database filter := bson.M{"stripeID": stripeID} - subscription := &Subscription{} - err := ms.subscriptions.FindOne(ctx, filter).Decode(subscription) + plan := &Plan{} + err := ms.plans.FindOne(ctx, filter).Decode(plan) if err != nil { if err == mongo.ErrNoDocuments { - return nil, ErrNotFound // Subscription not found + return nil, ErrNotFound // Plan not found } - return nil, errors.New("failed to get subscription") + return nil, errors.New("failed to get plan") } - return subscription, nil + return plan, nil } -// DefaultSubscription method returns the default subscription plan. If the -// subscription doesn't exist, it returns the specific error. -func (ms *MongoStorage) DefaultSubscription() (*Subscription, error) { +// DefaultPlan method returns the default plan plan. If the +// plan doesn't exist, it returns the specific error. +func (ms *MongoStorage) DefaultPlan() (*Plan, error) { ms.keysLock.RLock() defer ms.keysLock.RUnlock() // create a context with a timeout ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) defer cancel() - // find the subscription in the database + // find the plan in the database filter := bson.M{"default": true} - subscription := &Subscription{} - err := ms.subscriptions.FindOne(ctx, filter).Decode(subscription) + plan := &Plan{} + err := ms.plans.FindOne(ctx, filter).Decode(plan) if err != nil { if err == mongo.ErrNoDocuments { - return nil, ErrNotFound // Subscription not found + return nil, ErrNotFound // Plan not found } - return nil, errors.New("failed to get subscription") + return nil, errors.New("failed to get plan") } - return subscription, nil + return plan, nil } -// Subscriptions method returns all subscriptions from the database. -func (ms *MongoStorage) Subscriptions() ([]*Subscription, error) { +// Plans method returns all plans from the database. +func (ms *MongoStorage) Plans() ([]*Plan, error) { ms.keysLock.RLock() defer ms.keysLock.RUnlock() // create a context with a timeout ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) defer cancel() - // find all subscriptions in the database - cursor, err := ms.subscriptions.Find(ctx, bson.M{}) + // find all plans in the database + cursor, err := ms.plans.Find(ctx, bson.M{}) if err != nil { return nil, err } defer func() { if err := cursor.Close(ctx); err != nil { - log.Warnw("failed to close subscriptions file", "error", err) + log.Warnw("failed to close plans file", "error", err) } }() - // iterate over the cursor and decode each subscription - var subscriptions []*Subscription + // iterate over the cursor and decode each plan + var plans []*Plan for cursor.Next(ctx) { - subscription := &Subscription{} - if err := cursor.Decode(subscription); err != nil { + plan := &Plan{} + if err := cursor.Decode(plan); err != nil { return nil, err } - subscriptions = append(subscriptions, subscription) + plans = append(plans, plan) } if err := cursor.Err(); err != nil { return nil, err } - return subscriptions, nil + return plans, nil } -// DelSubscription method deletes the subscription with the given ID. If the -// subscription doesn't exist, it returns the specific error. -func (ms *MongoStorage) DelSubscription(subscription *Subscription) error { +// DelPlan method deletes the plan with the given ID. If the +// plan doesn't exist, it returns the specific error. +func (ms *MongoStorage) DelPlan(plan *Plan) error { ms.keysLock.Lock() defer ms.keysLock.Unlock() // create a context with a timeout ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) defer cancel() // delete the organization from the database - _, err := ms.subscriptions.DeleteOne(ctx, bson.M{"_id": subscription.ID}) + _, err := ms.plans.DeleteOne(ctx, bson.M{"_id": plan.ID}) return err } diff --git a/db/subscriptions_test.go b/db/subscriptions_test.go index 650621b..b1a05ec 100644 --- a/db/subscriptions_test.go +++ b/db/subscriptions_test.go @@ -6,7 +6,7 @@ import ( qt "github.com/frankban/quicktest" ) -func TestSetSubscription(t *testing.T) { +func TestSetPlan(t *testing.T) { defer func() { if err := db.Reset(); err != nil { t.Error(err) @@ -14,59 +14,59 @@ func TestSetSubscription(t *testing.T) { }() c := qt.New(t) - subscription := &Subscription{ - Name: "Test Subscription", + plan := &Plan{ + Name: "Test Plan", StripeID: "stripeID", } - _, err := db.SetSubscription(subscription) + _, err := db.SetPlan(plan) c.Assert(err, qt.IsNil) } -func TestSubscription(t *testing.T) { +func TestPlan(t *testing.T) { defer func() { if err := db.Reset(); err != nil { t.Error(err) } }() c := qt.New(t) // Create a new quicktest instance - subscriptionID := uint64(123) - // Test not found subscription - subscription, err := db.Subscription(subscriptionID) + planID := uint64(123) + // Test not found plan + plan, err := db.Plan(planID) c.Assert(err, qt.Equals, ErrNotFound) - c.Assert(subscription, qt.IsNil) - subscription = &Subscription{ - Name: "Test Subscription", + c.Assert(plan, qt.IsNil) + plan = &Plan{ + Name: "Test Plan", StripeID: "stripeID", } - subscriptionID, err = db.SetSubscription(subscription) + planID, err = db.SetPlan(plan) if err != nil { t.Error(err) } - // Test found subscription - subscriptionDB, err := db.Subscription(subscriptionID) + // Test found plan + planDB, err := db.Plan(planID) c.Assert(err, qt.IsNil) - c.Assert(subscriptionDB, qt.Not(qt.IsNil)) - c.Assert(subscriptionDB.ID, qt.Equals, subscription.ID) + c.Assert(planDB, qt.Not(qt.IsNil)) + c.Assert(planDB.ID, qt.Equals, plan.ID) } -func TestDelSubscription(t *testing.T) { +func TestDelPlan(t *testing.T) { defer func() { if err := db.Reset(); err != nil { t.Error(err) } }() c := qt.New(t) - // Create a new subscription and delete it - subscription := &Subscription{ - Name: "Test Subscription", + // Create a new plan and delete it + plan := &Plan{ + Name: "Test Plan", StripeID: "stripeID", } - id, err := db.SetSubscription(subscription) + id, err := db.SetPlan(plan) c.Assert(err, qt.IsNil) - err = db.DelSubscription(subscription) + err = db.DelPlan(plan) c.Assert(err, qt.IsNil) - // Test not found subscription - _, err = db.Subscription(id) + // Test not found plan + _, err = db.Plan(id) c.Assert(err, qt.Equals, ErrNotFound) } diff --git a/db/types.go b/db/types.go index 2a65684..ee9a5da 100644 --- a/db/types.go +++ b/db/types.go @@ -60,7 +60,7 @@ type Organization struct { Subscription OrganizationSubscription `json:"subscription" bson:"subscription"` Counters OrganizationCounters `json:"counters" bson:"counters"` } -type SubscriptionLimits struct { +type PlanLimits struct { Memberships int `json:"memberships" bson:"memberships"` SubOrgs int `json:"subOrgs" bson:"subOrgs"` CensusSize int `json:"censusSize" bson:"censusSize"` @@ -78,23 +78,23 @@ type Features struct { SmsNotification bool `json:"smsNotification" bson:"smsNotification"` } -type Subscription struct { - ID uint64 `json:"id" bson:"_id"` - Name string `json:"name" bson:"name"` - StripeID string `json:"stripeID" bson:"stripeID"` - Default bool `json:"default" bson:"default"` - Organization SubscriptionLimits `json:"organization" bson:"organization"` - VotingTypes VotingTypes `json:"votingTypes" bson:"votingTypes"` - Features Features `json:"features" bson:"features"` +type Plan struct { + ID uint64 `json:"id" bson:"_id"` + Name string `json:"name" bson:"name"` + StripeID string `json:"stripeID" bson:"stripeID"` + Default bool `json:"default" bson:"default"` + Organization PlanLimits `json:"organization" bson:"organization"` + VotingTypes VotingTypes `json:"votingTypes" bson:"votingTypes"` + Features Features `json:"features" bson:"features"` } type OrganizationSubscription struct { - SubscriptionID uint64 `bson:"subscriptionID"` - StartDate time.Time `bson:"startDate"` - EndDate time.Time `bson:"endDate"` - RenewalDate time.Time `bson:"renewalDate"` - Active bool `bson:"active"` - MaxCensusSize int `bson:"maxCensusSize"` + PlanID uint64 `bson:"planID"` + StartDate time.Time `bson:"startDate"` + EndDate time.Time `bson:"endDate"` + RenewalDate time.Time `bson:"renewalDate"` + Active bool `bson:"active"` + MaxCensusSize int `bson:"maxCensusSize"` } type OrganizationCounters struct {