Skip to content

Commit

Permalink
Merge pull request #24 from rarimo/feat/autoclaim
Browse files Browse the repository at this point in the history
Feat/autoclaim
  • Loading branch information
Zaptoss authored Jun 14, 2024
2 parents 5f88367 + 38b9254 commit 125b499
Show file tree
Hide file tree
Showing 12 changed files with 689 additions and 97 deletions.
15 changes: 15 additions & 0 deletions docs/spec/components/schemas/PassportEventState.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
allOf:
- $ref: '#/components/schemas/PassportEventStateKey'
- type: object
required:
- attributes
properties:
attributes:
required:
- claimed
type: object
properties:
claimed:
type: bool
example: true
description: If passport scan event was automatically claimed
13 changes: 13 additions & 0 deletions docs/spec/components/schemas/PassportEventStateKey.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
type: object
required:
- id
- type
properties:
id:
type: string
description: Nullifier of the points owner
example: "0x123...abc"
pattern: '^0x[0-9a-fA-F]{64}$'
type:
type: string
enum: [ passport_event_state ]
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,17 @@ post:
data:
$ref: '#/components/schemas/VerifyPassport'
responses:
204:
200:
description: Success
content:
application/vnd.api+json:
schema:
type: object
required:
- data
properties:
data:
$ref: '#/components/schemas/PassportEventState'
400:
$ref: '#/components/responses/invalidParameter'
401:
Expand Down
9 changes: 6 additions & 3 deletions internal/data/balances.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,17 +36,20 @@ type BalancesQ interface {
GetWithRank(nullifier string) (*Balance, error)
SelectWithRank() ([]Balance, error)

// Filters not applied. Return balances which already have scanned passport, but there no fulfilled or claimed events for this
// WithoutPassportEvent returns balances which already
// have scanned passport, but there no claimed events
// for this. Filters are not applied.
WithoutPassportEvent() ([]WithoutPassportEventBalance, error)
WithoutReferralEvent() ([]ReferredReferrer, error)

FilterByNullifier(string) BalancesQ
FilterByNullifier(...string) BalancesQ
FilterDisabled() BalancesQ
}

type WithoutPassportEventBalance struct {
Balance
EventID *string `db:"event_id"`
EventID string `db:"event_id"`
EventStatus EventStatus `db:"event_status"`
}

type ReferredReferrer struct {
Expand Down
1 change: 1 addition & 0 deletions internal/data/evtypes/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ type EventConfig struct {
StartsAt *time.Time `fig:"starts_at"`
ExpiresAt *time.Time `fig:"expires_at"`
NoAutoOpen bool `fig:"no_auto_open"`
AutoClaim bool `fig:"auto_claim"`
Disabled bool `fig:"disabled"`
ActionURL *url.URL `fig:"action_url"`
Logo *url.URL `fig:"logo"`
Expand Down
8 changes: 4 additions & 4 deletions internal/data/pg/balances.go
Original file line number Diff line number Diff line change
Expand Up @@ -145,10 +145,10 @@ func (q *balances) GetWithRank(nullifier string) (*data.Balance, error) {
func (q *balances) WithoutPassportEvent() ([]data.WithoutPassportEventBalance, error) {
var res []data.WithoutPassportEventBalance
stmt := fmt.Sprintf(`
SELECT b.*, e.id AS event_id
FROM %s AS b LEFT JOIN %s AS e
SELECT b.*, e.id AS event_id, e.status AS event_status
FROM %s AS b INNER JOIN %s AS e
ON b.nullifier = e.nullifier AND e.type='passport_scan'
WHERE (e.status NOT IN ('fulfilled', 'claimed') OR e.nullifier IS NULL)
WHERE e.status NOT IN ('claimed')
AND b.referred_by IS NOT NULL
AND b.country IS NOT NULL
`, balancesTable, eventsTable)
Expand Down Expand Up @@ -188,7 +188,7 @@ func (q *balances) WithoutReferralEvent() ([]data.ReferredReferrer, error) {

}

func (q *balances) FilterByNullifier(nullifier string) data.BalancesQ {
func (q *balances) FilterByNullifier(nullifier ...string) data.BalancesQ {
return q.applyCondition(squirrel.Eq{"nullifier": nullifier})
}

Expand Down
113 changes: 75 additions & 38 deletions internal/service/handlers/claim_event.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@ import (
"net/http"

"github.com/rarimo/decentralized-auth-svc/pkg/auth"
"github.com/rarimo/rarime-points-svc/internal/config"
"github.com/rarimo/rarime-points-svc/internal/data"
"github.com/rarimo/rarime-points-svc/internal/data/evtypes"
"github.com/rarimo/rarime-points-svc/internal/data/pg"
"github.com/rarimo/rarime-points-svc/internal/service/requests"
"github.com/rarimo/rarime-points-svc/resources"
Expand Down Expand Up @@ -82,7 +84,13 @@ func ClaimEvent(w http.ResponseWriter, r *http.Request) {
return
}

event, err = claimEventWithPoints(r, *event, evType.Reward, balance)
err = EventsQ(r).Transaction(func() error {
event, err = claimEvent(r, event, balance)
if err != nil {
return err
}
return nil
})
if err != nil {
Log(r).WithError(err).Errorf("Failed to claim event %s and accrue %d points to the balance %s",
event.ID, evType.Reward, event.Nullifier)
Expand All @@ -101,53 +109,82 @@ func ClaimEvent(w http.ResponseWriter, r *http.Request) {
ape.Render(w, newClaimEventResponse(*event, evType.Resource(), *balance))
}

// claimEventWithPoints requires event to exist
func claimEventWithPoints(r *http.Request, event data.Event, reward int64, balance *data.Balance) (claimed *data.Event, err error) {
err = EventsQ(r).Transaction(func() error {
// Upgrade level logic when threshold is reached
refsCount, level := Levels(r).LvlUp(balance.Level, reward+balance.Amount)
if level != balance.Level {
count, err := ReferralsQ(r).FilterByNullifier(balance.Nullifier).Count()
if err != nil {
return fmt.Errorf("failed to get referral count: %w", err)
}

refToAdd := prepareReferralsToAdd(balance.Nullifier, uint64(refsCount), count)
if err = ReferralsQ(r).Insert(refToAdd...); err != nil {
return fmt.Errorf("failed to insert referrals: %w", err)
}

err = BalancesQ(r).FilterByNullifier(balance.Nullifier).Update(map[string]any{
data.ColLevel: level,
})
if err != nil {
return fmt.Errorf("failed to update level: %w", err)
}
}
// claimEvent requires event to exist
// call in transaction to prevent unexpected changes
func claimEvent(r *http.Request, event *data.Event, balance *data.Balance) (claimed *data.Event, err error) {
if event == nil {
return nil, nil
}

claimed, err = EventsQ(r).FilterByID(event.ID).Update(data.EventClaimed, nil, &reward)
if err != nil {
return fmt.Errorf("update event status: %w", err)
}
evType := EventTypes(r).Get(event.Type, evtypes.FilterInactive)
if evType == nil {
return event, nil
}

claimed, err = EventsQ(r).FilterByID(event.ID).Update(data.EventClaimed, nil, &evType.Reward)
if err != nil {
return nil, fmt.Errorf("update event status: %w", err)
}

err = DoClaimEventUpdates(
Levels(r),
ReferralsQ(r),
BalancesQ(r),
CountriesQ(r),
*balance,
evType.Reward)
if err != nil {
return nil, fmt.Errorf("failed to do claim event updates: %w", err)
}

return claimed, nil
}

err = BalancesQ(r).FilterByNullifier(balance.Nullifier).Update(map[string]any{
data.ColAmount: pg.AddToValue(data.ColAmount, reward),
})
// DoClaimEventUpdates do updates which link to claim event:
// update reserved amount in country;
// lvlup and update referrals count;
// accruing points;
//
// Balance must be active and with verified passport
func DoClaimEventUpdates(
levels config.Levels,
referralsQ data.ReferralsQ,
balancesQ data.BalancesQ,
countriesQ data.CountriesQ,
balance data.Balance,
reward int64) (err error) {

refsCount, level := levels.LvlUp(balance.Level, reward+balance.Amount)
if refsCount > 0 {
count, err := referralsQ.New().FilterByNullifier(balance.Nullifier).Count()
if err != nil {
return fmt.Errorf("update balance amount: %w", err)
return fmt.Errorf("failed to get referral count: %w", err)
}

err = CountriesQ(r).FilterByCodes(*balance.Country).Update(map[string]any{
data.ColReserved: pg.AddToValue(data.ColReserved, reward),
})
if err != nil {
return fmt.Errorf("increase country reserve: %w", err)
refToAdd := prepareReferralsToAdd(balance.Nullifier, uint64(refsCount), count)
if err = referralsQ.New().Insert(refToAdd...); err != nil {
return fmt.Errorf("failed to insert referrals: %w", err)
}

return nil
}

err = balancesQ.FilterByNullifier(balance.Nullifier).Update(map[string]any{
data.ColAmount: pg.AddToValue(data.ColAmount, reward),
data.ColLevel: level,
})
if err != nil {
return fmt.Errorf("update balance amount and level: %w", err)
}

return claimed, nil
err = countriesQ.FilterByCodes(*balance.Country).Update(map[string]any{
data.ColReserved: pg.AddToValue(data.ColReserved, reward),
})
if err != nil {
return fmt.Errorf("increase country reserve: %w", err)
}

return nil
}

func newClaimEventResponse(
Expand Down
Loading

0 comments on commit 125b499

Please sign in to comment.