Skip to content

Commit

Permalink
feat: add account otx scroller API
Browse files Browse the repository at this point in the history
* force nonce order on tracking id get
  • Loading branch information
kamikazechaser committed Oct 15, 2024
1 parent 52693fe commit b1fd228
Show file tree
Hide file tree
Showing 11 changed files with 307 additions and 12 deletions.
62 changes: 62 additions & 0 deletions docs/docs.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,68 @@ const docTemplate = `{
}
}
},
"/account/otx/{address}": {
"get": {
"security": [
{
"ApiKeyAuth": []
}
],
"description": "Get an accounts OTX's (Origin transaction)",
"consumes": [
"*/*"
],
"produces": [
"application/json"
],
"tags": [
"Account"
],
"summary": "Get an accounts OTX's (Origin transaction)",
"parameters": [
{
"type": "string",
"description": "Account",
"name": "address",
"in": "path",
"required": true
},
{
"type": "boolean",
"description": "Next",
"name": "next",
"in": "query"
},
{
"type": "integer",
"description": "Cursor",
"name": "cursor",
"in": "query"
},
{
"type": "integer",
"description": "Per page",
"name": "perPage",
"in": "query",
"required": true
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/api.OKResponse"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/api.ErrResponse"
}
}
}
}
},
"/account/status/{address}": {
"get": {
"security": [
Expand Down
62 changes: 62 additions & 0 deletions docs/swagger.json
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,68 @@
}
}
},
"/account/otx/{address}": {
"get": {
"security": [
{
"ApiKeyAuth": []
}
],
"description": "Get an accounts OTX's (Origin transaction)",
"consumes": [
"*/*"
],
"produces": [
"application/json"
],
"tags": [
"Account"
],
"summary": "Get an accounts OTX's (Origin transaction)",
"parameters": [
{
"type": "string",
"description": "Account",
"name": "address",
"in": "path",
"required": true
},
{
"type": "boolean",
"description": "Next",
"name": "next",
"in": "query"
},
{
"type": "integer",
"description": "Cursor",
"name": "cursor",
"in": "query"
},
{
"type": "integer",
"description": "Per page",
"name": "perPage",
"in": "query",
"required": true
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/api.OKResponse"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/api.ErrResponse"
}
}
}
}
},
"/account/status/{address}": {
"get": {
"security": [
Expand Down
40 changes: 40 additions & 0 deletions docs/swagger.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,46 @@ paths:
summary: Create a new custodial account
tags:
- Account
/account/otx/{address}:
get:
consumes:
- '*/*'
description: Get an accounts OTX's (Origin transaction)
parameters:
- description: Account
in: path
name: address
required: true
type: string
- description: Next
in: query
name: next
type: boolean
- description: Cursor
in: query
name: cursor
type: integer
- description: Per page
in: query
name: perPage
required: true
type: integer
produces:
- application/json
responses:
"200":
description: OK
schema:
$ref: '#/definitions/api.OKResponse'
"500":
description: Internal Server Error
schema:
$ref: '#/definitions/api.ErrResponse'
security:
- ApiKeyAuth: []
summary: Get an accounts OTX's (Origin transaction)
tags:
- Account
/account/status/{address}:
get:
consumes:
Expand Down
1 change: 1 addition & 0 deletions internal/api/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,7 @@ func New(o APIOpts) *API {
apiGroup.GET("/system", api.systemInfoHandler)
apiGroup.POST("/account/create", api.accountCreateHandler)
apiGroup.GET("/account/status/:address", api.accountStatusHandler)
apiGroup.GET("/account/otx/:address", api.getOTXByAddressHandler)
apiGroup.GET("/otx/track/:trackingId", api.trackOTXHandler)
apiGroup.POST("/token/transfer", api.transferHandler)
apiGroup.POST("/pool/quote", api.poolQuoteHandler)
Expand Down
2 changes: 1 addition & 1 deletion internal/api/errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ func handleBindError(c echo.Context) error {
return c.JSON(http.StatusBadRequest, api.ErrResponse{
Ok: false,
ErrCode: api.ErrCodeInvalidJSON,
Description: "Invalid or malformed JSON structure",
Description: "Invalid or malformed request",
})
}

Expand Down
78 changes: 78 additions & 0 deletions internal/api/otx_handler.go
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
package api

import (
"fmt"
"net/http"

"github.com/grassrootseconomics/eth-custodial/internal/store"
apiresp "github.com/grassrootseconomics/eth-custodial/pkg/api"
"github.com/labstack/echo/v4"
)
Expand Down Expand Up @@ -53,3 +55,79 @@ func (a *API) trackOTXHandler(c echo.Context) error {
},
})
}

// getOTXByAddressHandler godoc
//
// @Summary Get an accounts OTX's (Origin transaction)
// @Description Get an accounts OTX's (Origin transaction)
// @Tags Account
// @Accept */*
// @Produce json
// @Param address path string true "Account"
// @Param next query bool false "Next"
// @Param cursor query int false "Cursor"
// @Param perPage query int true "Per page"
// @Success 200 {object} apiresp.OKResponse
// @Failure 500 {object} apiresp.ErrResponse
// @Security ApiKeyAuth
// @Router /account/otx/{address} [get]
func (a *API) getOTXByAddressHandler(c echo.Context) error {
req := apiresp.OTXByAccountRequest{}

if err := c.Bind(&req); err != nil {
return handleBindError(c)
}

if err := c.Validate(req); err != nil {
a.logg.Error("validation error", "error", err)
return handleValidateError(c)
}

pagination := validatePagination(req)

tx, err := a.store.Pool().Begin(c.Request().Context())
if err != nil {
return err
}
defer tx.Rollback(c.Request().Context())

var otx []*store.OTX

if pagination.FirstPage {
otx, err = a.store.GetOTXByAccount(c.Request().Context(), tx, req.Address, pagination.PerPage)
if err != nil {
return err
}
} else if pagination.Next {
otx, err = a.store.GetOTXByAccountNext(c.Request().Context(), tx, req.Address, pagination.Cursor, pagination.PerPage)
if err != nil {
return err
}
} else {
otx, err = a.store.GetOTXByAccountPrevious(c.Request().Context(), tx, req.Address, pagination.Cursor, pagination.PerPage)
if err != nil {
return err
}
}

if err := tx.Commit(c.Request().Context()); err != nil {
return err
}

var first, last uint64

if len(otx) > 0 {
first = otx[0].ID
last = otx[len(otx)-1].ID
}

return c.JSON(http.StatusOK, apiresp.OKResponse{
Ok: true,
Description: fmt.Sprintf("Successfully fetched OTX for %s", req.Address),
Result: map[string]any{
"otx": otx,
"first": first,
"last": last,
},
})
}
28 changes: 28 additions & 0 deletions internal/api/pagination.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package api

import (
"github.com/grassrootseconomics/eth-custodial/pkg/api"
)

type Pagination struct {
api.OTXByAccountRequest

FirstPage bool
}

func validatePagination(q api.OTXByAccountRequest) Pagination {
var firstPage = false

if q.PerPage > 100 {
q.PerPage = 100
}

if !q.Next && q.Cursor < 1 {
firstPage = true
}

return Pagination{
OTXByAccountRequest: q,
FirstPage: firstPage,
}
}
31 changes: 24 additions & 7 deletions internal/store/otx.go
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,6 @@ func (pg *Pg) GetOTXByTxHash(ctx context.Context, tx pgx.Tx, txHash string) (OTX
return otx, nil
}

// TODO: Sort by nonce
func (pg *Pg) GetOTXByTrackingID(ctx context.Context, tx pgx.Tx, trackingID string) ([]*OTX, error) {
var otx []*OTX

Expand All @@ -78,14 +77,32 @@ func (pg *Pg) GetOTXByTrackingID(ctx context.Context, tx pgx.Tx, trackingID stri
return otx, nil
}

func (pg *Pg) GetOTXByAccount(ctx context.Context, tx pgx.Tx, trackingID string, limit int) ([]OTX, error) {
return nil, nil
func (pg *Pg) GetOTXByAccount(ctx context.Context, tx pgx.Tx, publicKey string, limit int) ([]*OTX, error) {
var otx []*OTX

if err := pgxscan.Select(ctx, tx, &otx, pg.queries.GetOTXByAccount, publicKey, limit); err != nil {
return nil, err
}

return otx, nil
}

func (pg *Pg) GetOTXByAccountNext(ctx context.Context, tx pgx.Tx, trackingID string, cursor int, limit int) ([]OTX, error) {
return nil, nil
func (pg *Pg) GetOTXByAccountNext(ctx context.Context, tx pgx.Tx, publicKey string, cursor int, limit int) ([]*OTX, error) {
var otx []*OTX

if err := pgxscan.Select(ctx, tx, &otx, pg.queries.GetOTXByAccountNext, publicKey, cursor, limit); err != nil {
return nil, err
}

return otx, nil
}

func (pg *Pg) GetOTXByAccountPrevious(ctx context.Context, tx pgx.Tx, trackingID string, cursor int, limit int) ([]OTX, error) {
return nil, nil
func (pg *Pg) GetOTXByAccountPrevious(ctx context.Context, tx pgx.Tx, publicKey string, cursor int, limit int) ([]*OTX, error) {
var otx []*OTX

if err := pgxscan.Select(ctx, tx, &otx, pg.queries.GetOTXByAccountPrevious, publicKey, cursor, limit); err != nil {
return nil, err
}

return otx, nil
}
6 changes: 3 additions & 3 deletions internal/store/store.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,9 @@ type Store interface {
InsertOTX(context.Context, pgx.Tx, OTX) (uint64, error)
GetOTXByTxHash(context.Context, pgx.Tx, string) (OTX, error)
GetOTXByTrackingID(context.Context, pgx.Tx, string) ([]*OTX, error)
GetOTXByAccount(context.Context, pgx.Tx, string, int) ([]OTX, error)
GetOTXByAccountNext(context.Context, pgx.Tx, string, int, int) ([]OTX, error)
GetOTXByAccountPrevious(context.Context, pgx.Tx, string, int, int) ([]OTX, error)
GetOTXByAccount(context.Context, pgx.Tx, string, int) ([]*OTX, error)
GetOTXByAccountNext(context.Context, pgx.Tx, string, int, int) ([]*OTX, error)
GetOTXByAccountPrevious(context.Context, pgx.Tx, string, int, int) ([]*OTX, error)
// Dispatch
InsertDispatchTx(context.Context, pgx.Tx, DispatchTx) error
UpdateDispatchTxStatus(context.Context, pgx.Tx, DispatchTx) error
Expand Down
7 changes: 7 additions & 0 deletions pkg/api/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,13 @@ type (
TrackingIDParam struct {
TrackingID string `param:"trackingId" validate:"required,uuid"`
}

OTXByAccountRequest struct {
Address string `param:"address" validate:"required,eth_addr_checksum"`
PerPage int `query:"perPage" validate:"required,number,gt=0"`
Cursor int `query:"cursor" validate:"number"`
Next bool `query:"next"`
}
)

const (
Expand Down
Loading

0 comments on commit b1fd228

Please sign in to comment.