Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Now we can use flag genesis=true to specify that only one referral mu… #21

Merged
merged 3 commits into from
Jun 3, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,8 @@ referrals (if count == 0, do not create)
b) User exists, `N` > `count` -> add `N - count` active referrals
c) User exists, `N` < `count` -> consume `count - N` active referrals (not delete!)
d) User exists, `N` = `count` -> do nothing
f) Flag `genesis = true` that mean that will be created only one referral with
`usage_count = count`. Referrals which have `usage_count <= 0` is inactive

Where `N` is the current number of active referrals for the user, `count` is
query parameter value.
Expand Down
33 changes: 33 additions & 0 deletions internal/assets/migrations/002_genesis_referrals.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
-- +migrate Up
ALTER TABLE referrals
RENAME COLUMN is_consumed TO usage_left;

ALTER TABLE referrals ALTER COLUMN usage_left DROP DEFAULT;

ALTER TABLE referrals
ALTER usage_left TYPE INTEGER
USING
CASE
WHEN usage_left=TRUE THEN 0
ELSE 1
END;

ALTER TABLE referrals ALTER COLUMN usage_left SET DEFAULT 1;

ALTER TABLE balances DROP CONSTRAINT balances_referred_by_key;

-- +migrate Down
ALTER TABLE referrals ALTER COLUMN usage_left DROP DEFAULT;

ALTER TABLE referrals
ALTER usage_left TYPE BOOLEAN
USING
CASE
WHEN usage_left > 0 THEN FALSE
ELSE TRUE
END;

ALTER TABLE referrals
RENAME COLUMN usage_left TO is_consumed;

ALTER TABLE referrals ALTER COLUMN is_consumed SET DEFAULT FALSE;
10 changes: 5 additions & 5 deletions internal/data/pg/referrals.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ func NewReferrals(db *pgdb.DB) data.ReferralsQ {
return &referrals{
db: db,
selector: squirrel.Select("*").From(referralsTable),
updater: squirrel.Update(referralsTable).Set("is_consumed", true),
updater: squirrel.Update(referralsTable).Set("usage_left", squirrel.Expr("usage_left - 1")),
counter: squirrel.Select("COUNT(*) as count").From(referralsTable),
}
}
Expand All @@ -37,9 +37,9 @@ func (q *referrals) Insert(referrals ...data.Referral) error {
return nil
}

stmt := squirrel.Insert(referralsTable).Columns("id", "nullifier")
stmt := squirrel.Insert(referralsTable).Columns("id", "nullifier", "usage_left")
for _, ref := range referrals {
stmt = stmt.Values(ref.ID, ref.Nullifier)
stmt = stmt.Values(ref.ID, ref.Nullifier, ref.UsageLeft)
}

if err := q.db.Exec(stmt); err != nil {
Expand Down Expand Up @@ -122,8 +122,8 @@ func (q *referrals) FilterByNullifier(nullifier string) data.ReferralsQ {
return q.applyCondition(squirrel.Eq{"nullifier": nullifier})
}

func (q *referrals) FilterByIsConsumed(isConsumed bool) data.ReferralsQ {
return q.applyCondition(squirrel.Eq{"is_consumed": isConsumed})
func (q *referrals) FilterConsumed() data.ReferralsQ {
return q.applyCondition(squirrel.Gt{"usage_left": 0})
}

func (q *referrals) applyCondition(cond squirrel.Sqlizer) data.ReferralsQ {
Expand Down
9 changes: 4 additions & 5 deletions internal/data/referrals.go
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
package data

type Referral struct {
ID string `db:"id"`
Nullifier string `db:"nullifier"`
IsConsumed bool `db:"is_consumed"`
CreatedAt int32 `db:"created_at"`
violog marked this conversation as resolved.
Show resolved Hide resolved
ID string `db:"id"`
Nullifier string `db:"nullifier"`
UsageLeft int32 `db:"usage_left"`
}

type ReferralsQ interface {
Expand All @@ -18,5 +17,5 @@ type ReferralsQ interface {
Count() (uint64, error)

FilterByNullifier(string) ReferralsQ
FilterByIsConsumed(bool) ReferralsQ
FilterConsumed() ReferralsQ
}
2 changes: 1 addition & 1 deletion internal/service/handlers/activate_balance.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ func ActivateBalance(w http.ResponseWriter, r *http.Request) {
return
}

referral, err := ReferralsQ(r).FilterByIsConsumed(false).Get(req.Data.Attributes.ReferredBy)
referral, err := ReferralsQ(r).FilterConsumed().Get(req.Data.Attributes.ReferredBy)
if err != nil {
Log(r).WithError(err).Error("Failed to get referral by ID")
ape.RenderErr(w, problems.InternalError())
Expand Down
2 changes: 1 addition & 1 deletion internal/service/handlers/create_balance.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ func CreateBalance(w http.ResponseWriter, r *http.Request) {
return
}

referral, err := ReferralsQ(r).FilterByIsConsumed(false).Get(req.Data.Attributes.ReferredBy)
referral, err := ReferralsQ(r).FilterConsumed().Get(req.Data.Attributes.ReferredBy)
if err != nil {
Log(r).WithError(err).Error("Failed to get referral by ID")
ape.RenderErr(w, problems.InternalError())
Expand Down
31 changes: 30 additions & 1 deletion internal/service/handlers/edit_referrals.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,34 @@ func EditReferrals(w http.ResponseWriter, r *http.Request) {
}
}

if req.Genesis {
count, err := ReferralsQ(r).FilterByNullifier(req.Nullifier).Count()
if err != nil {
Log(r).WithError(err).Error("Failed to get referrals count")
ape.RenderErr(w, problems.InternalError())
return
}

referral := referralid.New(req.Nullifier, count)

err = ReferralsQ(r).Insert(data.Referral{
ID: referral,
Nullifier: req.Nullifier,
UsageLeft: int32(*req.Count),
})
if err != nil {
Log(r).WithError(err).Error("Failed to insert genesis referral")
ape.RenderErr(w, problems.InternalError())
return
}

ape.Render(w, struct {
Ref string `json:"added_referrals"`
Zaptoss marked this conversation as resolved.
Show resolved Hide resolved
UsageLeft int `json:"usage_left"`
}{referral, int(*req.Count)})
return
}

added, err := adjustReferralsCount(req, r)
if err != nil {
Log(r).WithError(err).Error("Failed to adjust referrals count")
Expand All @@ -59,14 +87,15 @@ func prepareReferralsToAdd(nullifier string, count, index uint64) []data.Referra
refs[i] = data.Referral{
ID: code,
Nullifier: nullifier,
UsageLeft: 1,
}
}

return refs
}

func adjustReferralsCount(req requests.EditReferralsRequest, r *http.Request) (refsAdded []string, err error) {
active, err := ReferralsQ(r).FilterByNullifier(req.Nullifier).FilterByIsConsumed(false).Count()
active, err := ReferralsQ(r).FilterByNullifier(req.Nullifier).FilterConsumed().Count()
if err != nil {
return nil, fmt.Errorf("count active referrals: %w", err)
}
Expand Down
2 changes: 1 addition & 1 deletion internal/service/handlers/get_balance.go
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ func newBalanceResponse(balance data.Balance, referrals []data.Referral) resourc
resp.Data.Attributes.IsDisabled = !balance.ReferredBy.Valid

for _, ref := range referrals {
if ref.IsConsumed {
if ref.UsageLeft == 0 {
consumedCodes = append(consumedCodes, ref.ID)
continue
}
Expand Down
1 change: 1 addition & 0 deletions internal/service/requests/edit_referrals.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (
type EditReferralsRequest struct {
Nullifier string `json:"nullifier"`
Count *uint64 `json:"count"`
Genesis bool `json:"genesis"`
}

func NewEditReferrals(r *http.Request) (req EditReferralsRequest, err error) {
Expand Down
Loading