Skip to content

Commit

Permalink
Merge pull request #12 from rarimo/work/Roman
Browse files Browse the repository at this point in the history
Work/roman
  • Loading branch information
violog authored May 22, 2024
2 parents 5cd89e3 + b1b3967 commit 3da2db4
Show file tree
Hide file tree
Showing 51 changed files with 1,081 additions and 257 deletions.
28 changes: 28 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,34 @@ functionality to interact with these endpoints.

The path for internal endpoints is `/integrations/rarime-points-svc/v1/private/*`.

### Add referrals

Private endpoint to set number of available referral codes or create a new
_System user_ with referrals. _System user_ is unable to claim events or
withdraw, it has `is_disabled` attribute set to `true`, so the client app should
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"
```

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

Where `N` is the current number of active referrals for the user, `count` is
query parameter value.

### Local build

We do use openapi:json standard for API. We use swagger for documenting our API.
Expand Down
52 changes: 52 additions & 0 deletions config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -17,41 +17,93 @@ event_types:
title: Points for passport scan
reward: 200
description: Get points for scan passport and share data
short_description: Short description
frequency: one-time
action_url: https://...
logo: https://...
- name: get_poh
title: Get PoH credential
reward: 50
description: Prove that you are human
short_description: Short description
frequency: one-time
expires_at: 2020-01-01T00:00:00Z
- name: free_weekly
title: Free weekly points
reward: 100
frequency: weekly
description: Get free points every week by visiting the platform and claiming your reward
short_description: Short description
- name: daily_login
title: Daily login
reward: 5
frequency: daily
description: Login every day
short_description: Short description
disabled: true
- name: be_referred
title: Referral welcome bonus
reward: 5
frequency: one-time
description: Be referred by a friend and get a welcome bonus
short_description: Short description
no_auto_open: true
- name: referral_common
title: Refer new users
reward: 25
frequency: one-time
description: Refer friends and get a reward for each friend who verifies the passport
short_description: Short description
- name: referral_specific
title: Refer user {:did}
reward: 25
frequency: unlimited
description: The user {:did} has verified the passport. Claim the reward!
short_description: Short description
no_auto_open: true
- name: planned
title: Planned event
reward: 25
frequency: unlimited
description: Event that start at specified time
short_description: Short description
starts_at: 2020-01-01T00:00:00Z
- name: generate_proof_age
title: Generate proof age
reward: 25
frequency: one-time
description: Event that become fulfilled when user create proof that prove age
short_description: Short description
- name: generate_proof_nationality
title: Generate proof nationality
reward: 50
frequency: one-time
description: Event that become fulfilled when user create proof that prove nationality
short_description: Short description
- name: verify_proof_age
title: Verify proof age
reward: 25
frequency: one-time
description: Event that become fulfilled when user verify someone else's proof age
short_description: Short description
- name: verify_proof_nationality
title: Verify proof nationality
reward: 50
frequency: one-time
description: Event that become fulfilled when user verify someone else's proof nationality
short_description: Short description
- name: verified_proof_age
title: Have proof age verified
reward: 25
frequency: one-time
description: Event that become fulfilled when another user verify you proof age (user that verify must have verified passport)
short_description: Short description
- name: verified_proof_nationality
title: Have proof nationality verified
reward: 50
frequency: one-time
description: Event that become fulfilled when another user verify you proof nationality (user that verify must have verified passport)
short_description: Short description

auth:
addr: http://rarime-auth
Expand Down
16 changes: 14 additions & 2 deletions docs/spec/components/schemas/Balance.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -42,9 +42,21 @@ allOf:
format: int
description: Rank of the user in the full leaderboard. Returned only for the single user.
example: 294
referral_codes:
active_referral_codes:
type: array
description: Referral codes used to build a referral link and send it to friends. Required if a balance is created
description: |
Referral codes which can be used to build a referral link and send it
to friends. Returned only for the single user.
example: ["zgsScguZ", "jerUsmac"]
items:
type: string
consumed_referral_codes:
type: array
description: Referral codes used by invited users. Returned only for the single user.
example: ["73k3bdYaFWM", "9csIL7dW65m"]
items:
type: string
is_withdrawal_allowed:
type: boolean
description: Whether the user can withdraw tokens. Returned only for the single user.
example: true
2 changes: 1 addition & 1 deletion docs/spec/components/schemas/CreateBalanceKey.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,4 @@ properties:
example: "did:iden3:readonly:tUDjWxnVJNi7t3FudukqrUcNwF5KVGoWgim5pp2jV"
type:
type: string
enum: [ create_balance ]
enum: [ create_balance, update_balance ]
17 changes: 17 additions & 0 deletions docs/spec/components/schemas/EventStaticMeta.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ required:
- reward
- title
- description
- short_description
- frequency
- no_auto_open
properties:
Expand All @@ -25,14 +26,30 @@ properties:
description:
type: string
example: Lorem ipsum dolor sit amet
short_description:
type: string
example: Short description
frequency:
type: string
description: |
Event frequency, which means how often you can fulfill
certain task and claim the reward.
enum: [one-time, daily, weekly, unlimited]
starts_at:
type: string
format: time.Time
description: General event starting date (UTC RFC3339)
example: 2020-01-01T00:00:00Z
expires_at:
type: string
format: time.Time
description: General event expiration date (UTC RFC3339)
example: 2020-01-01T00:00:00Z
action_url:
type: string
description: Page where you can fulfill the event
example: https://robotornot.rarimo.com
logo:
type: string
description: Event logo
example: https://logo.com/some_logo.svg
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ post:
the new account synchronously (to display them right after the request).
If balance already exists, but it is disabled (it was not referred by another user,
but has fulfilled some event), you should use this endpoint as well.
but has fulfilled some event), you should use PATCH balances/{did} endpoint as well.
operationId: createPointsBalance
requestBody:
content:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,3 +40,51 @@ get:
$ref: '#/components/responses/invalidAuth'
500:
$ref: '#/components/responses/internalError'

patch:
tags:
- Points balance
summary: Activate points balance
description: |
Activate inactive balance. Balance is inactive when referred_by field is null.
did in query must match with id in request body
operationId: activatePointsBalance
parameters:
- $ref: '#/components/parameters/pathDID'
requestBody:
content:
application/vnd.api+json:
schema:
type: object
required:
- data
properties:
data:
$ref: '#/components/schemas/CreateBalance'

responses:
200:
description: Success
content:
application/vnd.api+json:
schema:
type: object
required:
- data
properties:
data:
$ref: '#/components/schemas/Balance'
400:
$ref: '#/components/responses/invalidParameter'
401:
$ref: '#/components/responses/invalidAuth'
404:
$ref: '#/components/responses/notFound'
409:
description: Balance already activated
content:
application/vnd.api+json:
schema:
$ref: '#/components/schemas/Errors'
500:
$ref: '#/components/responses/internalError'
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,8 @@ get:
schema:
type: boolean
example: true
- $ref: '#/components/parameters/pageCursor'
- $ref: '#/components/parameters/pageLimit'
- $ref: '#/components/parameters/pageNumber'
- $ref: '#/components/parameters/pageOrder'
responses:
200:
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,30 @@
get:
tags:
- Events
summary: Get event
description: Returns a single event by ID.
operationId: getEvent
responses:
200:
description: Success
content:
application/vnd.api+json:
schema:
type: object
required:
- data
properties:
data:
$ref: '#/components/schemas/Event'
400:
$ref: '#/components/responses/invalidParameter'
401:
$ref: '#/components/responses/invalidAuth'
404:
$ref: '#/components/responses/notFound'
500:
$ref: '#/components/responses/internalError'

patch:
tags:
- Events
Expand Down
15 changes: 8 additions & 7 deletions internal/assets/migrations/001_initial.sql
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,14 @@ AS $$ BEGIN NEW.updated_at = EXTRACT('EPOCH' FROM NOW()); RETURN NEW; END; $$;

CREATE TABLE IF NOT EXISTS balances
(
did text PRIMARY KEY,
amount bigint NOT NULL default 0,
created_at integer NOT NULL default EXTRACT('EPOCH' FROM NOW()),
updated_at integer NOT NULL default EXTRACT('EPOCH' FROM NOW()),
referred_by text UNIQUE,
passport_hash text UNIQUE,
passport_expires timestamp without time zone
did text PRIMARY KEY,
amount bigint NOT NULL default 0,
created_at integer NOT NULL default EXTRACT('EPOCH' FROM NOW()),
updated_at integer NOT NULL default EXTRACT('EPOCH' FROM NOW()),
referred_by text UNIQUE,
passport_hash text UNIQUE,
passport_expires timestamp without time zone,
is_withdrawal_allowed boolean NOT NULL default false
);

CREATE INDEX IF NOT EXISTS balances_page_index ON balances (amount, updated_at) WHERE referred_by IS NOT NULL;
Expand Down
20 changes: 11 additions & 9 deletions internal/data/balances.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,21 +8,22 @@ import (
)

type Balance struct {
DID string `db:"did"`
Amount int64 `db:"amount"`
CreatedAt int32 `db:"created_at"`
UpdatedAt int32 `db:"updated_at"`
ReferredBy sql.NullString `db:"referred_by"`
PassportHash sql.NullString `db:"passport_hash"`
PassportExpires sql.NullTime `db:"passport_expires"`
Rank *int `db:"rank"`
DID string `db:"did"`
Amount int64 `db:"amount"`
CreatedAt int32 `db:"created_at"`
UpdatedAt int32 `db:"updated_at"`
ReferredBy sql.NullString `db:"referred_by"`
PassportHash sql.NullString `db:"passport_hash"`
PassportExpires sql.NullTime `db:"passport_expires"`
Rank *int `db:"rank"`
IsWithdrawalAllowed bool `db:"is_withdrawal_allowed"`
}

type BalancesQ interface {
New() BalancesQ
Insert(Balance) error
UpdateAmountBy(points int64) error
SetPassport(hash string, exp time.Time) error
SetPassport(hash string, exp time.Time, isWithdrawalAllowed bool) error
SetReferredBy(referralCode string) error

Page(*pgdb.OffsetPageParams) BalancesQ
Expand All @@ -32,5 +33,6 @@ type BalancesQ interface {
GetWithRank(did string) (*Balance, error)

FilterByDID(string) BalancesQ
FilterByPassportHash(string) BalancesQ
FilterDisabled() BalancesQ
}
2 changes: 1 addition & 1 deletion internal/data/events.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ type EventsQ interface {
Delete() (rowsAffected int64, err error)
Transaction(func() error) error

Page(*pgdb.CursorPageParams) EventsQ
Page(*pgdb.OffsetPageParams) EventsQ
Select() ([]Event, error)
Get() (*Event, error)
// Count returns the total number of events that match filters. Page is not
Expand Down
6 changes: 6 additions & 0 deletions internal/data/evtypes/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,12 @@ func (c *config) EventTypes() Types {
if !checkFreqValue(t.Frequency) {
panic(fmt.Errorf("invalid frequency: %s", t.Frequency))
}

if t.ExpiresAt != nil && t.StartsAt != nil && !t.StartsAt.Before(*t.ExpiresAt) {
panic(fmt.Errorf("starts_at must be before expires_at: %s > %s",
t.StartsAt, t.ExpiresAt))
}

m[t.Name] = t
}

Expand Down
6 changes: 5 additions & 1 deletion internal/data/evtypes/filters.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,12 @@ func FilterExpired(ev EventConfig) bool {
return ev.ExpiresAt != nil && ev.ExpiresAt.Before(time.Now().UTC())
}

func FilterNotStarted(ev EventConfig) bool {
return ev.StartsAt != nil && ev.StartsAt.After(time.Now().UTC())
}

func FilterInactive(ev EventConfig) bool {
return ev.Disabled || FilterExpired(ev)
return ev.Disabled || FilterExpired(ev) || FilterNotStarted(ev)
}

func FilterNotOpenable(ev EventConfig) bool {
Expand Down
Loading

0 comments on commit 3da2db4

Please sign in to comment.