Skip to content

Commit

Permalink
CORE-1983: support the periods and end_date settings in the bulk subs…
Browse files Browse the repository at this point in the history
…cription request endpoint
  • Loading branch information
slr71 committed Feb 22, 2024
1 parent 6815274 commit 36306a6
Show file tree
Hide file tree
Showing 4 changed files with 91 additions and 13 deletions.
7 changes: 1 addition & 6 deletions internal/controllers/subscriptions.go
Original file line number Diff line number Diff line change
Expand Up @@ -113,13 +113,8 @@ func (sa *SubscriptionAdder) AddSubscription(tx *gorm.DB, req model.Subscription
return sa.subscriptionError(*username, err.Error())
}

// Define the subscription options.
opts := &model.SubscriptionOptions{
Paid: paid,
}

// Add the subscription.
sub, err := db.SubscribeUserToPlan(sa.cfg.Ctx, tx, user, plan, opts)
sub, err := db.SubscribeUserToPlan(sa.cfg.Ctx, tx, user, plan, &req.SubscriptionOptions)
if err != nil {
log.Error(err)
return sa.subscriptionError(*username, err.Error())
Expand Down
4 changes: 3 additions & 1 deletion internal/controllers/users.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import (
"github.com/cyverse/QMS/internal/db"
"github.com/cyverse/QMS/internal/httpmodel"
"github.com/cyverse/QMS/internal/model"
"github.com/cyverse/QMS/internal/model/timestamp"
"github.com/cyverse/QMS/internal/query"
"github.com/labstack/echo/v4"
"github.com/sirupsen/logrus"
Expand Down Expand Up @@ -403,10 +404,11 @@ func (s Server) UpdateSubscription(ctx echo.Context) error {
log.Debug("deactivated all active plans for the user")

// Define the subscription options.
endTimestamp := timestamp.Timestamp(endDate)
opts := &model.SubscriptionOptions{
Paid: &paid,
Periods: &periods,
EndDate: &endDate,
EndDate: &endTimestamp,
}

// Subscribe the user to the plan.
Expand Down
15 changes: 9 additions & 6 deletions internal/model/subscriptions.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
package model

import "time"
import (
"time"

"github.com/cyverse/QMS/internal/model/timestamp"
)

// SubscriptionOptions represents options that can be applied to a new subscription.
//
Expand All @@ -13,7 +17,7 @@ type SubscriptionOptions struct {
Periods *int32 `json:"periods"`

// The effective end date of the subscription.
EndDate *time.Time `json:"end_date"`
EndDate *timestamp.Timestamp `json:"end_date"`
}

// Return the appropriate paid flag for the subscription options.
Expand All @@ -39,14 +43,16 @@ func (o *SubscriptionOptions) GetEndDate(startDate time.Time) time.Time {
if o.EndDate == nil {
return startDate.AddDate(int(o.GetPeriods()), 0, 0)
} else {
return *o.EndDate
return time.Time(*o.EndDate)
}
}

// SubscriptionRequest represents a request for a single subscription.
//
// swagger: model
type SubscriptionRequest struct {
SubscriptionOptions

// The username to associate with the subscription
//
// required: true
Expand All @@ -56,9 +62,6 @@ type SubscriptionRequest struct {
//
// required: true
PlanName *string `json:"plan_name"`

// True if the user paid for the subscription.
Paid *bool `json:"paid"`
}

// SubscriptionRequests represents a list of subscription requests.
Expand Down
78 changes: 78 additions & 0 deletions internal/model/timestamp/timestamp.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
package timestamp

import (
"fmt"
"regexp"
"strconv"
"time"
)

const (
DateOnly = time.DateOnly
DateTimeLocal = "2006-01-02T15:04:05"
RFC3339 = time.RFC3339
)

var (
DateOnlyRegexp = regexp.MustCompile(`^\d{4}-\d{2}-\d{2}$`)
DateTimeLocalRegexp = regexp.MustCompile(`^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}$`)
RFC3339Regexp = regexp.MustCompile(`^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(Z|[+-]\d{2}:\d{2})$`)
)

// Timestamp is an alias for time.Time with lenient parsing in the local time zone by default.
type Timestamp time.Time

// layoutForValue returns the layout to use for a given timestamp value.
func layoutForValue(value string) (string, error) {
switch {
case DateOnlyRegexp.MatchString(value):
return DateOnly, nil
case DateTimeLocalRegexp.MatchString(value):
return DateTimeLocal, nil
case RFC3339Regexp.MatchString(value):
return RFC3339, nil
default:
return "", fmt.Errorf("unrecognized timestamp layout: %s", value)
}
}

// Parse attempts to parse the given value as a timestamp. The timestamp will be parsed in the time zone of the
// current location unless the time zone is included in the timestamp itself. The accepted formats are:
//
// 2024-02-21 - Midnight on the specified date in the local time zone.
// 2024-02-21T01:02:03 - The specified date and time in the local time zone.
// 2024-02-21T01:02:03Z - The specified date and time in UTC.
// 2024-02-01T01:02:03-07:00 - The specified date and time in the specified time zone.
func Parse(value string) (Timestamp, error) {
var t time.Time

// Determine the timestamp layout.
layout, err := layoutForValue(value)
if err != nil {
return Timestamp(t), err
}

// Parse the timestamp.
t, err = time.ParseInLocation(layout, value, time.Now().Location())
return Timestamp(t), err
}

// UnmarshalJSON converts a JSON to a timestamp.
func (t *Timestamp) UnmarshalJSON(data []byte) error {
value := string(data)

// Ignore empty values.
if value == "null" || value == `""` {
return nil
}

// Unquote the string.
value, err := strconv.Unquote(value)
if err != nil {
return err
}

// Parse the timestamp.
*t, err = Parse(value)
return err
}

0 comments on commit 36306a6

Please sign in to comment.