2019-06-04 20:17:01 +01:00
|
|
|
// Copyright (C) 2019 Storj Labs, Inc.
|
|
|
|
// See LICENSE for copying information
|
|
|
|
|
2019-06-24 21:51:54 +01:00
|
|
|
package rewards
|
2019-06-04 20:17:01 +01:00
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
|
|
|
"time"
|
2019-06-28 15:34:10 +01:00
|
|
|
|
2019-07-30 14:21:00 +01:00
|
|
|
"github.com/zeebo/errs"
|
|
|
|
|
2019-07-01 20:16:49 +01:00
|
|
|
"storj.io/storj/internal/currency"
|
|
|
|
)
|
2019-06-28 15:34:10 +01:00
|
|
|
|
2019-07-30 14:21:00 +01:00
|
|
|
var (
|
|
|
|
// MaxRedemptionErr is the error class used when an offer has reached its redemption capacity
|
|
|
|
MaxRedemptionErr = errs.Class("offer redemption has reached its capacity")
|
|
|
|
// NoCurrentOfferErr is the error class used when no current offer is set
|
|
|
|
NoCurrentOfferErr = errs.Class("no current offer")
|
|
|
|
)
|
2019-07-16 00:30:00 +01:00
|
|
|
|
2019-06-24 21:51:54 +01:00
|
|
|
// DB holds information about offer
|
|
|
|
type DB interface {
|
2019-07-10 18:12:40 +01:00
|
|
|
ListAll(ctx context.Context) (Offers, error)
|
2019-08-01 18:46:33 +01:00
|
|
|
GetActiveOffersByType(ctx context.Context, offerType OfferType) (Offers, error)
|
2019-06-04 20:17:01 +01:00
|
|
|
Create(ctx context.Context, offer *NewOffer) (*Offer, error)
|
2019-06-12 16:53:19 +01:00
|
|
|
Finish(ctx context.Context, offerID int) error
|
2019-06-04 20:17:01 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// NewOffer holds information that's needed for creating a new offer
|
|
|
|
type NewOffer struct {
|
|
|
|
Name string
|
|
|
|
Description string
|
|
|
|
|
2019-07-01 20:16:49 +01:00
|
|
|
AwardCredit currency.USD
|
|
|
|
InviteeCredit currency.USD
|
2019-06-04 20:17:01 +01:00
|
|
|
|
|
|
|
RedeemableCap int
|
|
|
|
|
|
|
|
AwardCreditDurationDays int
|
|
|
|
InviteeCreditDurationDays int
|
|
|
|
|
|
|
|
ExpiresAt time.Time
|
|
|
|
|
|
|
|
Status OfferStatus
|
|
|
|
Type OfferType
|
|
|
|
}
|
|
|
|
|
|
|
|
// UpdateOffer holds fields needed for update an offer
|
|
|
|
type UpdateOffer struct {
|
2019-06-12 16:53:19 +01:00
|
|
|
ID int
|
|
|
|
Status OfferStatus
|
|
|
|
ExpiresAt time.Time
|
2019-06-04 20:17:01 +01:00
|
|
|
}
|
|
|
|
|
2019-08-14 20:53:48 +01:00
|
|
|
// RedeemOffer holds field needed for redeem an offer
|
|
|
|
type RedeemOffer struct {
|
|
|
|
RedeemableCap int
|
|
|
|
Status OfferStatus
|
|
|
|
Type OfferType
|
|
|
|
}
|
|
|
|
|
|
|
|
// Offers contains a slice of offers.
|
|
|
|
type Offers []Offer
|
|
|
|
|
2019-06-04 20:17:01 +01:00
|
|
|
// OfferType indicates the type of an offer
|
|
|
|
type OfferType int
|
|
|
|
|
|
|
|
const (
|
2019-07-08 19:39:56 +01:00
|
|
|
// Invalid is a default value for offers that don't have correct type associated with it
|
|
|
|
Invalid = OfferType(0)
|
2019-06-04 20:17:01 +01:00
|
|
|
// FreeCredit is a type of offers used for Free Credit Program
|
2019-07-08 19:39:56 +01:00
|
|
|
FreeCredit = OfferType(1)
|
2019-06-04 20:17:01 +01:00
|
|
|
// Referral is a type of offers used for Referral Program
|
2019-07-08 19:39:56 +01:00
|
|
|
Referral = OfferType(2)
|
2019-07-31 17:01:18 +01:00
|
|
|
// Partner is an OfferType used be the Open Source Partner Program
|
2019-07-19 19:22:10 +01:00
|
|
|
Partner = OfferType(3)
|
2019-06-04 20:17:01 +01:00
|
|
|
)
|
|
|
|
|
2019-07-10 18:12:40 +01:00
|
|
|
// OfferStatus represents the different stage an offer can have in its life-cycle.
|
2019-06-04 20:17:01 +01:00
|
|
|
type OfferStatus int
|
|
|
|
|
|
|
|
const (
|
2019-07-10 18:12:40 +01:00
|
|
|
|
|
|
|
// Done is the status of an offer that is no longer in use.
|
2019-06-04 20:17:01 +01:00
|
|
|
Done = OfferStatus(iota)
|
2019-07-10 18:12:40 +01:00
|
|
|
|
|
|
|
// Default is the status of an offer when there is no active offer.
|
|
|
|
Default
|
2019-08-14 20:53:48 +01:00
|
|
|
|
|
|
|
// Active is the status of an offer that is currently in use.
|
|
|
|
Active
|
2019-06-04 20:17:01 +01:00
|
|
|
)
|
|
|
|
|
|
|
|
// Offer contains info needed for giving users free credits through different offer programs
|
|
|
|
type Offer struct {
|
|
|
|
ID int
|
|
|
|
Name string
|
|
|
|
Description string
|
|
|
|
|
2019-07-01 20:16:49 +01:00
|
|
|
AwardCredit currency.USD
|
|
|
|
InviteeCredit currency.USD
|
2019-06-04 20:17:01 +01:00
|
|
|
|
|
|
|
AwardCreditDurationDays int
|
|
|
|
InviteeCreditDurationDays int
|
|
|
|
|
|
|
|
RedeemableCap int
|
|
|
|
|
|
|
|
ExpiresAt time.Time
|
|
|
|
CreatedAt time.Time
|
|
|
|
|
|
|
|
Status OfferStatus
|
|
|
|
Type OfferType
|
|
|
|
}
|
2019-06-28 15:34:10 +01:00
|
|
|
|
2019-07-10 18:12:40 +01:00
|
|
|
// IsEmpty evaluates whether or not an on offer is empty
|
|
|
|
func (o Offer) IsEmpty() bool {
|
|
|
|
return o.Name == ""
|
2019-06-28 15:34:10 +01:00
|
|
|
}
|
|
|
|
|
2019-08-01 18:46:33 +01:00
|
|
|
// GetActiveOffer returns an offer that is active based on its type
|
|
|
|
func (offers Offers) GetActiveOffer(offerType OfferType, partnerID string) (offer *Offer, err error) {
|
|
|
|
if len(offers) < 1 {
|
|
|
|
return nil, NoCurrentOfferErr.New("no active offers")
|
|
|
|
}
|
|
|
|
switch offerType {
|
|
|
|
case Partner:
|
|
|
|
if partnerID == "" {
|
|
|
|
return nil, errs.New("partner ID is empty")
|
|
|
|
}
|
|
|
|
partnerInfo, ok := LoadPartnerInfos()[partnerID]
|
|
|
|
if !ok {
|
|
|
|
return nil, NoMatchPartnerIDErr.New("no partnerInfo found")
|
|
|
|
}
|
|
|
|
for i := range offers {
|
|
|
|
if offers[i].Name == partnerInfo.Name {
|
|
|
|
offer = &offers[i]
|
|
|
|
}
|
|
|
|
}
|
|
|
|
default:
|
|
|
|
if len(offers) > 1 {
|
|
|
|
return nil, errs.New("multiple active offers found")
|
|
|
|
}
|
|
|
|
offer = &offers[0]
|
|
|
|
}
|
|
|
|
|
|
|
|
return offer, nil
|
|
|
|
}
|
|
|
|
|
2019-08-14 20:53:48 +01:00
|
|
|
// IsDefault checks if a offer's status is default
|
|
|
|
func (status OfferStatus) IsDefault() bool {
|
|
|
|
return status == Default
|
|
|
|
}
|