Skip to content

Commit

Permalink
Merge pull request #21 from rarimo/feat/genesis-multiusage-referrals
Browse files Browse the repository at this point in the history
Now we can use flag genesis=true to specify that only one referral mu…
  • Loading branch information
Zaptoss authored Jun 3, 2024
2 parents 5d6bf2c + 965e0c3 commit 2c2b61c
Show file tree
Hide file tree
Showing 9 changed files with 108 additions and 21 deletions.
39 changes: 32 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,21 +39,46 @@ not allow it interactions with the system, although it is technically possible
to do other actions.

Path: `/integrations/rarime-points-svc/v1/private/referrals`
Query parameters:
- `did` - user DID to create or edit referrals for
- `count` - number of referrals to set

Example to set 200 referrals for user `did:example:123`:
```shell
curl -X POST "http://localhost/integrations/rarime-points-svc/v1/private/referrals?did=did:example:123&count=200"
Body:
```json
{
"nullifier": "0x0000000000000000000000000000000000000000000000000000000000000000",
"count": 2,
"genesis": true
}
```
Response variants:
```json
{
"added_ref": "kPRQYQUcWzW",
"usage_left": 2
}
```
or
```json
{
"added_referrals":[
"kPRQYQUcWzW",
"kPRQYQUcaaa",
"kPRQYQUcbbb",
"kPRQYQUcccc"
]
}
```
Parameters:
- `nullifier` - nullifier to create or edit referrals for
- `count` - number of referrals to set/number of referral usage for genesis
- `genesis` - specify add many referrals with one usage or one referral with many usage


Behavior:
a) User does not exist -> create a _System user_ with the specified number of
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"`
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_ref"`
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

0 comments on commit 2c2b61c

Please sign in to comment.