Skip to content

Commit

Permalink
/exp/lighthorizon: new endpoints for tx and ops paged listing by acco…
Browse files Browse the repository at this point in the history
…unt id (#4453)
  • Loading branch information
sreuland authored Jul 22, 2022
1 parent 8c9eec3 commit bceaf07
Show file tree
Hide file tree
Showing 16 changed files with 979 additions and 774 deletions.
129 changes: 129 additions & 0 deletions exp/lighthorizon/actions/accounts.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
package actions

import (
"net/http"
"strconv"

"github.com/stellar/go/support/log"

"github.com/stellar/go/exp/lighthorizon/adapters"
"github.com/stellar/go/exp/lighthorizon/services"
hProtocol "github.com/stellar/go/protocols/horizon"
"github.com/stellar/go/protocols/horizon/operations"
"github.com/stellar/go/support/render/hal"
"github.com/stellar/go/toid"
)

const (
urlAccountId = "account_id"
)

func accountRequestParams(w http.ResponseWriter, r *http.Request) (string, pagination) {
var accountId string
var accountErr bool

if accountId, accountErr = getURLParam(r, urlAccountId); !accountErr {
sendErrorResponse(w, http.StatusBadRequest, "")
return "", pagination{}
}

paginate, err := paging(r)
if err != nil {
sendErrorResponse(w, http.StatusBadRequest, string(invalidPagingParameters))
return "", pagination{}
}

if paginate.Cursor < 1 {
paginate.Cursor = toid.New(1, 1, 1).ToInt64()
}

if paginate.Limit == 0 {
paginate.Limit = 10
}

return accountId, paginate
}

func NewTXByAccountHandler(lightHorizon services.LightHorizon) func(http.ResponseWriter, *http.Request) {
return func(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
var accountId string
var paginate pagination

if accountId, paginate = accountRequestParams(w, r); accountId == "" {
return
}

page := hal.Page{
Cursor: strconv.FormatInt(paginate.Cursor, 10),
Order: string(paginate.Order),
Limit: uint64(paginate.Limit),
}
page.Init()
page.FullURL = r.URL

txns, err := lightHorizon.Transactions.GetTransactionsByAccount(ctx, paginate.Cursor, paginate.Limit, accountId)
if err != nil {
log.Error(err)
sendErrorResponse(w, http.StatusInternalServerError, err.Error())
return
}

for _, txn := range txns {
var response hProtocol.Transaction
response, err = adapters.PopulateTransaction(r, &txn)
if err != nil {
log.Error(err)
sendErrorResponse(w, http.StatusInternalServerError, err.Error())
return
}

page.Add(response)
}

page.PopulateLinks()
sendPageResponse(w, page)
}
}

func NewOpsByAccountHandler(lightHorizon services.LightHorizon) func(http.ResponseWriter, *http.Request) {
return func(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
var accountId string
var paginate pagination

if accountId, paginate = accountRequestParams(w, r); accountId == "" {
return
}

page := hal.Page{
Cursor: strconv.FormatInt(paginate.Cursor, 10),
Order: string(paginate.Order),
Limit: uint64(paginate.Limit),
}
page.Init()
page.FullURL = r.URL

ops, err := lightHorizon.Operations.GetOperationsByAccount(ctx, paginate.Cursor, paginate.Limit, accountId)
if err != nil {
log.Error(err)
sendErrorResponse(w, http.StatusInternalServerError, err.Error())
return
}

for _, op := range ops {
var response operations.Operation
response, err = adapters.PopulateOperation(r, &op)
if err != nil {
log.Error(err)
sendErrorResponse(w, http.StatusInternalServerError, err.Error())
return
}

page.Add(response)
}

page.PopulateLinks()
sendPageResponse(w, page)
}
}
91 changes: 65 additions & 26 deletions exp/lighthorizon/actions/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,36 +3,55 @@ package actions
import (
"embed"
"encoding/json"
"fmt"
"net/http"
"net/url"
"strconv"

"github.com/go-chi/chi"
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/promauto"
"github.com/stellar/go/support/log"
"github.com/stellar/go/support/render/hal"
)

var (
//go:embed static
staticFiles embed.FS
//lint:ignore U1000 temporary
requestCount = promauto.NewCounter(prometheus.CounterOpts{
Name: "horizon_lite_request_count",
Help: "How many requests have occurred?",
})
//lint:ignore U1000 temporary
requestTime = promauto.NewHistogram(prometheus.HistogramOpts{
Name: "horizon_lite_request_duration",
Help: "How long do requests take?",
Buckets: append(
prometheus.LinearBuckets(0, 50, 20),
prometheus.LinearBuckets(1000, 1000, 8)...,
),
})
)

type Order string
type ErrorMessage string
type order string
type errorMessage string

const (
OrderAsc Order = "asc"
OrderDesc Order = "desc"
orderAsc order = "asc"
orderDesc order = "desc"
)

const (
ServerError ErrorMessage = "Error: A problem occurred on the server while processing request"
InvalidPagingParameters ErrorMessage = "Error: Invalid paging parameters"
//TODO - refactor to use horizon 'problems' package
serverError errorMessage = "Error: A problem occurred on the server while processing request"
invalidPagingParameters errorMessage = "Error: Invalid paging parameters"
)

type Pagination struct {
Limit int64
type pagination struct {
Limit uint64
Cursor int64
Order
Order order
}

func sendPageResponse(w http.ResponseWriter, page hal.Page) {
Expand All @@ -47,48 +66,68 @@ func sendPageResponse(w http.ResponseWriter, page hal.Page) {

func sendErrorResponse(w http.ResponseWriter, errorCode int, errorMsg string) {
if errorMsg != "" {
http.Error(w, errorMsg, errorCode)
http.Error(w, fmt.Sprintf("Error: %s", errorMsg), errorCode)
} else {
http.Error(w, string(ServerError), errorCode)
http.Error(w, string(serverError), errorCode)
}
}

func RequestUnaryParam(r *http.Request, paramName string) (string, error) {
func requestUnaryParam(r *http.Request, paramName string) (string, error) {
query, err := url.ParseQuery(r.URL.RawQuery)
if err != nil {
return "", err
}
return query.Get(paramName), nil
}

func Paging(r *http.Request) (Pagination, error) {
paginate := Pagination{
Order: OrderAsc,
func paging(r *http.Request) (pagination, error) {
paginate := pagination{
Order: orderAsc,
}

if cursorRequested, err := RequestUnaryParam(r, "cursor"); err != nil {
return Pagination{}, err
if cursorRequested, err := requestUnaryParam(r, "cursor"); err != nil {
return pagination{}, err
} else if cursorRequested != "" {
paginate.Cursor, err = strconv.ParseInt(cursorRequested, 10, 64)
if err != nil {
return Pagination{}, err
return pagination{}, err
}
}

if limitRequested, err := RequestUnaryParam(r, "limit"); err != nil {
return Pagination{}, err
if limitRequested, err := requestUnaryParam(r, "limit"); err != nil {
return pagination{}, err
} else if limitRequested != "" {
paginate.Limit, err = strconv.ParseInt(limitRequested, 10, 64)
paginate.Limit, err = strconv.ParseUint(limitRequested, 10, 64)
if err != nil {
return Pagination{}, err
return pagination{}, err
}
}

if orderRequested, err := RequestUnaryParam(r, "order"); err != nil {
return Pagination{}, err
} else if orderRequested != "" && orderRequested == string(OrderDesc) {
paginate.Order = OrderDesc
if orderRequested, err := requestUnaryParam(r, "order"); err != nil {
return pagination{}, err
} else if orderRequested != "" && orderRequested == string(orderDesc) {
paginate.Order = orderDesc
}

return paginate, nil
}

func getURLParam(r *http.Request, key string) (string, bool) {
rctx := chi.RouteContext(r.Context())

if rctx == nil {
return "", false
}

if len(rctx.URLParams.Keys) != len(rctx.URLParams.Values) {
return "", false
}

for k := len(rctx.URLParams.Keys) - 1; k >= 0; k-- {
if rctx.URLParams.Keys[k] == key {
return rctx.URLParams.Values[k], true
}
}

return "", false
}
105 changes: 0 additions & 105 deletions exp/lighthorizon/actions/operation.go

This file was deleted.

Loading

0 comments on commit bceaf07

Please sign in to comment.