Skip to content

Commit

Permalink
Merge pull request #2 from rarimo/feature/withdrawal-history
Browse files Browse the repository at this point in the history
Feature: withdrawal history
  • Loading branch information
violog authored Feb 6, 2024
2 parents d39a7d1 + 4da13d8 commit ba95827
Show file tree
Hide file tree
Showing 23 changed files with 436 additions and 43 deletions.
18 changes: 18 additions & 0 deletions docs/spec/components/schemas/PointPrice.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
allOf:
- $ref: '#/components/schemas/PointPriceKey'
- type: object
required:
- attributes
properties:
type:
type: string
enum: [point_price]
attributes:
type: object
required:
- urmo
properties:
urmo:
type: integer
description: Amount of `urmo` tokens for one point
example: 1000
7 changes: 7 additions & 0 deletions docs/spec/components/schemas/PointPriceKey.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
type: object
required:
- type
properties:
type:
type: string
enum: [point_price]
13 changes: 13 additions & 0 deletions docs/spec/components/schemas/Withdrawal.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -23,3 +23,16 @@ allOf:
type: integer
description: Unix timestamp of withdrawal creation
example: 1706531218
relationships:
type: object
required:
- balance
properties:
balance:
type: object
description: # hack for openapi-go-generator
required:
- data
properties:
data:
$ref: '#/components/schemas/BalanceKey'
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,9 @@ post:
tags:
- Points balance
summary: Withdraw points
description: Convert points to RMO by exchange rate and withdraw to user wallet
description: |
Convert points to RMO by exchange rate and withdraw to user wallet.
Updated balance with new rank is returned.
operationId: withdrawPoints
parameters:
- $ref: '#/components/parameters/pathDID'
Expand Down
20 changes: 20 additions & 0 deletions docs/spec/paths/integrations@rarime-points-svc@v1@point_price.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
get:
tags:
- Points balance
summary: Get point price
description: How many `urmo` tokens cost one point.
operationId: getPointPrice
responses:
200:
description: Success
content:
application/vnd.api+json:
schema:
type: object
required:
- data
properties:
data:
$ref: '#/components/schemas/PointPrice'
500:
$ref: '#/components/responses/internalError'
14 changes: 14 additions & 0 deletions internal/assets/migrations/001_initial.sql
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,21 @@ CREATE TRIGGER set_updated_at
FOR EACH ROW
EXECUTE FUNCTION trigger_set_updated_at();

CREATE TABLE IF NOT EXISTS withdrawals
(
id uuid PRIMARY KEY default gen_random_uuid(),
user_did text not null REFERENCES balances (did),
amount integer not null,
address text not null,
created_at integer not null default EXTRACT('EPOCH' FROM NOW())
);

CREATE INDEX IF NOT EXISTS withdrawals_user_did_index ON withdrawals using btree (user_did);

-- +migrate Down
DROP INDEX IF EXISTS withdrawals_user_did_index;
DROP TABLE IF EXISTS withdrawals;

DROP TRIGGER IF EXISTS set_updated_at ON events;
DROP INDEX IF EXISTS events_type_index;
DROP INDEX IF EXISTS events_user_did_index;
Expand Down
26 changes: 26 additions & 0 deletions internal/data/balances.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package data

import (
"gitlab.com/distributed_lab/kit/pgdb"
)

type Balance struct {
DID string `db:"did"`
Amount int32 `db:"amount"`
CreatedAt int32 `db:"created_at"`
UpdatedAt int32 `db:"updated_at"`
Rank *int `db:"rank"`
}

type BalancesQ interface {
New() BalancesQ
Insert(did string) error
UpdateAmountBy(points int32) error

Page(*pgdb.OffsetPageParams) BalancesQ
Select() ([]Balance, error)
Get() (*Balance, error)
WithRank() BalancesQ

FilterByDID(string) BalancesQ
}
43 changes: 11 additions & 32 deletions internal/data/main.go → internal/data/events.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,17 @@ func (s EventStatus) String() string {
return string(s)
}

type Event struct {
ID string `db:"id"`
UserDID string `db:"user_did"`
Type string `db:"type"`
Status EventStatus `db:"status"`
CreatedAt int32 `db:"created_at"`
UpdatedAt int32 `db:"updated_at"`
Meta Jsonb `db:"meta"`
PointsAmount sql.NullInt32 `db:"points_amount"`
}

type EventsQ interface {
New() EventsQ
Insert(...Event) error
Expand All @@ -37,35 +48,3 @@ type EventsQ interface {
FilterByType(...string) EventsQ
FilterByUpdatedAtBefore(int64) EventsQ
}

type BalancesQ interface {
New() BalancesQ
Insert(did string) error
UpdateAmountBy(points int32) error

Page(*pgdb.OffsetPageParams) BalancesQ
Select() ([]Balance, error)
Get() (*Balance, error)
WithRank() BalancesQ

FilterByDID(string) BalancesQ
}

type Event struct {
ID string `db:"id"`
UserDID string `db:"user_did"`
Type string `db:"type"`
Status EventStatus `db:"status"`
CreatedAt int32 `db:"created_at"`
UpdatedAt int32 `db:"updated_at"`
Meta Jsonb `db:"meta"`
PointsAmount sql.NullInt32 `db:"points_amount"`
}

type Balance struct {
DID string `db:"did"`
Amount int32 `db:"amount"`
CreatedAt int32 `db:"created_at"`
UpdatedAt int32 `db:"updated_at"`
Rank *int `db:"rank"`
}
62 changes: 62 additions & 0 deletions internal/data/pg/withdrawals.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
package pg

import (
"fmt"

"github.com/Masterminds/squirrel"
"github.com/rarimo/rarime-points-svc/internal/data"
"gitlab.com/distributed_lab/kit/pgdb"
)

const withdrawalsTable = "withdrawals"

type withdrawals struct {
db *pgdb.DB
selector squirrel.SelectBuilder
}

func NewWithdrawals(db *pgdb.DB) data.WithdrawalsQ {
return &withdrawals{
db: db,
selector: squirrel.Select("*").From(withdrawalsTable),
}
}

func (q *withdrawals) New() data.WithdrawalsQ {
return NewWithdrawals(q.db.Clone())
}

func (q *withdrawals) Insert(w data.Withdrawal) (*data.Withdrawal, error) {
var res data.Withdrawal
stmt := squirrel.Insert(withdrawalsTable).SetMap(map[string]interface{}{
"user_did": w.UserDID,
"amount": w.Amount,
"address": w.Address,
}).Suffix("RETURNING *")

if err := q.db.Get(&res, stmt); err != nil {
return nil, fmt.Errorf("insert withdrawal [%+v]: %w", w, err)
}

return &res, nil
}

func (q *withdrawals) Page(page *pgdb.CursorPageParams) data.WithdrawalsQ {
q.selector = page.ApplyTo(q.selector, "created_at")
return q
}

func (q *withdrawals) Select() ([]data.Withdrawal, error) {
var res []data.Withdrawal

if err := q.db.Select(&res, q.selector); err != nil {
return nil, fmt.Errorf("select withdrawals: %w", err)
}

return res, nil
}

func (q *withdrawals) FilterByUserDID(did string) data.WithdrawalsQ {
q.selector = q.selector.Where(squirrel.Eq{"user_did": did})
return q
}
23 changes: 23 additions & 0 deletions internal/data/withdrawals.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package data

import (
"gitlab.com/distributed_lab/kit/pgdb"
)

type Withdrawal struct {
ID string `db:"id"`
UserDID string `db:"user_did"`
Amount int32 `db:"amount"`
Address string `db:"address"`
CreatedAt int32 `db:"created_at"`
}

type WithdrawalsQ interface {
New() WithdrawalsQ
Insert(Withdrawal) (*Withdrawal, error)

Page(*pgdb.CursorPageParams) WithdrawalsQ
Select() ([]Withdrawal, error)

FilterByUserDID(string) WithdrawalsQ
}
5 changes: 5 additions & 0 deletions internal/service/handlers/create_balance.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,11 @@ func CreateBalance(w http.ResponseWriter, r *http.Request) {
}
return nil
})
if err != nil {
Log(r).WithError(err).Error("Failed to add balance with open events")
ape.RenderErr(w, problems.InternalError())
return
}

// We can't return inserted balance in a single query, because we can't calculate
// rank in transaction: RANK() is a window function allowed on a set of rows,
Expand Down
11 changes: 11 additions & 0 deletions internal/service/handlers/ctx.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ const (
logCtxKey ctxKey = iota
eventsQCtxKey
balancesQCtxKey
withdrawalsQCtxKey
eventTypesCtxKey
userClaimsCtxKey
broadcasterCtxKey
Expand Down Expand Up @@ -53,6 +54,16 @@ func BalancesQ(r *http.Request) data.BalancesQ {
return r.Context().Value(balancesQCtxKey).(data.BalancesQ).New()
}

func CtxWithdrawalsQ(q data.WithdrawalsQ) func(context.Context) context.Context {
return func(ctx context.Context) context.Context {
return context.WithValue(ctx, withdrawalsQCtxKey, q)
}
}

func WithdrawalsQ(r *http.Request) data.WithdrawalsQ {
return r.Context().Value(withdrawalsQCtxKey).(data.WithdrawalsQ).New()
}

func CtxEventTypes(types evtypes.Types) func(context.Context) context.Context {
return func(ctx context.Context) context.Context {
return context.WithValue(ctx, eventTypesCtxKey, types)
Expand Down
62 changes: 62 additions & 0 deletions internal/service/handlers/list_withdrawals.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
package handlers

import (
"net/http"

"github.com/rarimo/rarime-auth-svc/pkg/auth"
"github.com/rarimo/rarime-points-svc/internal/data"
"github.com/rarimo/rarime-points-svc/internal/service/requests"
"github.com/rarimo/rarime-points-svc/resources"
"gitlab.com/distributed_lab/ape"
"gitlab.com/distributed_lab/ape/problems"
)

func ListWithdrawals(w http.ResponseWriter, r *http.Request) {
req, err := requests.NewListWithdrawals(r)
if err != nil {
ape.RenderErr(w, problems.BadRequest(err)...)
return
}

if !auth.Authenticates(UserClaims(r), auth.UserGrant(req.DID)) {
ape.RenderErr(w, problems.Unauthorized())
return
}

withdrawals, err := WithdrawalsQ(r).FilterByUserDID(req.DID).Page(&req.CursorPageParams).Select()
if err != nil {
Log(r).WithError(err).Error("Failed to get withdrawal list")
ape.RenderErr(w, problems.InternalError())
return
}

var last string
if len(withdrawals) > 0 {
last = withdrawals[len(withdrawals)-1].ID
}

resp := newWithdrawalsResponse(withdrawals)
resp.Links = req.CursorParams.GetLinks(r, last)
}

func newWithdrawalsResponse(withdrawals []data.Withdrawal) resources.WithdrawalListResponse {
list := make([]resources.Withdrawal, len(withdrawals))
for i, w := range withdrawals {
list[i] = newWithdrawalModel(w)
}
return resources.WithdrawalListResponse{Data: list}
}

func newWithdrawalModel(w data.Withdrawal) resources.Withdrawal {
return resources.Withdrawal{
Key: resources.Key{
ID: w.ID,
Type: resources.WITHDRAWAL,
},
Attributes: resources.WithdrawalAttributes{
Amount: w.Amount,
Address: w.Address,
CreatedAt: w.CreatedAt,
},
}
}
21 changes: 21 additions & 0 deletions internal/service/handlers/point_price.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package handlers

import (
"net/http"

"github.com/rarimo/rarime-points-svc/resources"
"gitlab.com/distributed_lab/ape"
)

func GetPointPrice(w http.ResponseWriter, r *http.Request) {
ape.Render(w, resources.PointPriceResponse{
Data: resources.PointPrice{
Key: resources.Key{
Type: resources.POINT_PRICE,
},
Attributes: resources.PointPriceAttributes{
Urmo: PointPrice(r),
},
},
})
}
Loading

0 comments on commit ba95827

Please sign in to comment.