From fe50f93989dbe4fe18d62ca30ee9d269f6a2bca6 Mon Sep 17 00:00:00 2001 From: Zaptoss Date: Mon, 3 Jun 2024 19:03:47 +0300 Subject: [PATCH 1/3] Now we can use flag genesis=true to specify that only one referral must be added which usage count = count --- README.md | 2 ++ .../migrations/002_genesis_referrals.sql | 33 +++++++++++++++++++ internal/data/pg/referrals.go | 10 +++--- internal/data/referrals.go | 9 +++-- internal/service/handlers/activate_balance.go | 2 +- internal/service/handlers/create_balance.go | 2 +- internal/service/handlers/edit_referrals.go | 31 ++++++++++++++++- internal/service/handlers/get_balance.go | 2 +- internal/service/requests/edit_referrals.go | 1 + 9 files changed, 78 insertions(+), 14 deletions(-) create mode 100644 internal/assets/migrations/002_genesis_referrals.sql diff --git a/README.md b/README.md index 2948385..6a10281 100644 --- a/README.md +++ b/README.md @@ -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. diff --git a/internal/assets/migrations/002_genesis_referrals.sql b/internal/assets/migrations/002_genesis_referrals.sql new file mode 100644 index 0000000..487bb15 --- /dev/null +++ b/internal/assets/migrations/002_genesis_referrals.sql @@ -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; diff --git a/internal/data/pg/referrals.go b/internal/data/pg/referrals.go index 9cce7ec..8d0603c 100644 --- a/internal/data/pg/referrals.go +++ b/internal/data/pg/referrals.go @@ -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), } } @@ -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 { @@ -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 { diff --git a/internal/data/referrals.go b/internal/data/referrals.go index d4ec3e6..2581f86 100644 --- a/internal/data/referrals.go +++ b/internal/data/referrals.go @@ -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 { @@ -18,5 +17,5 @@ type ReferralsQ interface { Count() (uint64, error) FilterByNullifier(string) ReferralsQ - FilterByIsConsumed(bool) ReferralsQ + FilterConsumed() ReferralsQ } diff --git a/internal/service/handlers/activate_balance.go b/internal/service/handlers/activate_balance.go index a487d47..d95e3b3 100644 --- a/internal/service/handlers/activate_balance.go +++ b/internal/service/handlers/activate_balance.go @@ -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()) diff --git a/internal/service/handlers/create_balance.go b/internal/service/handlers/create_balance.go index 4976553..0801be3 100644 --- a/internal/service/handlers/create_balance.go +++ b/internal/service/handlers/create_balance.go @@ -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()) diff --git a/internal/service/handlers/edit_referrals.go b/internal/service/handlers/edit_referrals.go index eb759a5..bb867c0 100644 --- a/internal/service/handlers/edit_referrals.go +++ b/internal/service/handlers/edit_referrals.go @@ -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"` + 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") @@ -59,6 +87,7 @@ func prepareReferralsToAdd(nullifier string, count, index uint64) []data.Referra refs[i] = data.Referral{ ID: code, Nullifier: nullifier, + UsageLeft: 1, } } @@ -66,7 +95,7 @@ func prepareReferralsToAdd(nullifier string, count, index uint64) []data.Referra } 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) } diff --git a/internal/service/handlers/get_balance.go b/internal/service/handlers/get_balance.go index 12aab99..9db8a8e 100644 --- a/internal/service/handlers/get_balance.go +++ b/internal/service/handlers/get_balance.go @@ -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 } diff --git a/internal/service/requests/edit_referrals.go b/internal/service/requests/edit_referrals.go index 7db765e..76ac1a2 100644 --- a/internal/service/requests/edit_referrals.go +++ b/internal/service/requests/edit_referrals.go @@ -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) { From 3cc53b0e0a3fc05588125bc230d2ab08e23e631d Mon Sep 17 00:00:00 2001 From: Zaptoss Date: Mon, 3 Jun 2024 19:26:05 +0300 Subject: [PATCH 2/3] Reduce var name --- internal/service/handlers/edit_referrals.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/service/handlers/edit_referrals.go b/internal/service/handlers/edit_referrals.go index bb867c0..916efb0 100644 --- a/internal/service/handlers/edit_referrals.go +++ b/internal/service/handlers/edit_referrals.go @@ -61,7 +61,7 @@ func EditReferrals(w http.ResponseWriter, r *http.Request) { } ape.Render(w, struct { - Ref string `json:"added_referrals"` + Ref string `json:"added_ref"` UsageLeft int `json:"usage_left"` }{referral, int(*req.Count)}) return From 965e0c394168928a431db407ef3b3393ebf027ad Mon Sep 17 00:00:00 2001 From: Zaptoss Date: Mon, 3 Jun 2024 19:34:22 +0300 Subject: [PATCH 3/3] Docs --- README.md | 37 ++++++++++++++++++++++++++++++------- 1 file changed, 30 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 6a10281..9458220 100644 --- a/README.md +++ b/README.md @@ -39,14 +39,37 @@ 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