Skip to content
This repository has been archived by the owner on Jan 22, 2020. It is now read-only.

Commit

Permalink
Merge pull request #345 from stellar/timestamps
Browse files Browse the repository at this point in the history
Adds `created_at` to trade resource
  • Loading branch information
bartekn authored Mar 10, 2017
2 parents 84459eb + a4574bb commit f98b911
Show file tree
Hide file tree
Showing 7 changed files with 122 additions and 14 deletions.
47 changes: 46 additions & 1 deletion src/github.com/stellar/horizon/actions_trade.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
package horizon

import (
"errors"

"fmt"

"github.com/stellar/go/xdr"
"github.com/stellar/horizon/db2"
"github.com/stellar/horizon/db2/history"
Expand All @@ -18,6 +22,9 @@ type TradeIndexAction struct {
Buying xdr.Asset
PagingParams db2.PageQuery
Records []history.Effect
// LedgerRecords is a cache of loaded ledger data used to populate the time
// when a trade occurred.
LedgerRecords map[int32]history.Ledger
Page hal.Page
}

Expand All @@ -27,6 +34,7 @@ func (action *TradeIndexAction) JSON() {
action.EnsureHistoryFreshness,
action.loadParams,
action.loadRecords,
action.loadLedgers,
action.loadPage,
func() {
hal.Render(action.W, action.Page)
Expand Down Expand Up @@ -63,14 +71,51 @@ func (action *TradeIndexAction) loadRecords() {
action.Err = trades.Page(action.PagingParams).Select(&action.Records)
}

// loadLedgers collects the unique ledgers referenced in the loaded trades and loads the details for each.
func (action *TradeIndexAction) loadLedgers() {
if len(action.Records) == 0 {
return
}

ledgerSequences := make([]interface{}, len(action.Records))

// populate the unique sequences
for i, trade := range action.Records {
ledgerSequences[i] = trade.LedgerSequence()
}

var ledgers []history.Ledger
action.Err = action.HistoryQ().LedgersBySequence(
&ledgers,
ledgerSequences...,
)
if action.Err != nil {
return
}

action.LedgerRecords = map[int32]history.Ledger{}
for _, l := range ledgers {
action.LedgerRecords[l.Sequence] = l
}
}

// loadPage populates action.Page
func (action *TradeIndexAction) loadPage() {
for _, record := range action.Records {
var res resource.Trade
action.Err = res.Populate(action.Ctx, record)

ledger, found := action.LedgerRecords[record.LedgerSequence()]
if !found {
msg := fmt.Sprintf("could not find ledger data for sequence %d", record.LedgerSequence())
action.Err = errors.New(msg)
return
}

action.Err = res.Populate(action.Ctx, record, ledger)
if action.Err != nil {
return
}

action.Page.Add(res)
}

Expand Down
14 changes: 14 additions & 0 deletions src/github.com/stellar/horizon/actions_trade_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,10 @@ package horizon
import (
"net/url"
"testing"
"time"

"github.com/stellar/horizon/db2/history"
"github.com/stellar/horizon/resource"
)

func TestTradeActions_Index(t *testing.T) {
Expand All @@ -15,6 +19,16 @@ func TestTradeActions_Index(t *testing.T) {
ht.Assert.PageOf(1, w.Body)
}

// ensure created_at is populated correctly
records := []resource.Trade{}
ht.UnmarshalPage(w.Body, &records)

l := history.Ledger{}
hq := history.Q{Repo: ht.HorizonRepo()}
ht.Require.NoError(hq.LedgerBySequence(&l, 6))

ht.Assert.WithinDuration(l.ClosedAt, records[0].LedgerCloseTime, 1*time.Second)

// for order book
var q = make(url.Values)
q.Add("selling_asset_type", "credit_alphanum4")
Expand Down
6 changes: 6 additions & 0 deletions src/github.com/stellar/horizon/db2/history/effect.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,12 @@ func (r *Effect) ID() string {
return fmt.Sprintf("%019d-%010d", r.HistoryOperationID, r.Order)
}

// LedgerSequence return the ledger in which the effect occurred.
func (r *Effect) LedgerSequence() int32 {
id := toid.Parse(r.HistoryOperationID)
return id.LedgerSequence
}

// PagingToken returns a cursor for this effect
func (r *Effect) PagingToken() string {
return fmt.Sprintf("%d-%d", r.HistoryOperationID, r.Order)
Expand Down
15 changes: 15 additions & 0 deletions src/github.com/stellar/horizon/db2/history/ledger.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
package history

import (
"fmt"

sq "github.com/lann/squirrel"
"github.com/stellar/go/support/errors"
"github.com/stellar/horizon/db2"
)

Expand All @@ -23,6 +26,18 @@ func (q *Q) Ledgers() *LedgersQ {
}
}

// LedgersBySequence loads the a set of ledgers identified by the sequences
// `seqs` into `dest`.
func (q *Q) LedgersBySequence(dest interface{}, seqs ...interface{}) error {
if len(seqs) == 0 {
return errors.New("no sequence arguments provided")
}
in := fmt.Sprintf("sequence IN (%s)", sq.Placeholders(len(seqs)))
sql := selectLedger.Where(in, seqs...)

return q.Select(dest, sql)
}

// Page specifies the paging constraints for the query being built by `q`.
func (q *LedgersQ) Page(page db2.PageQuery) *LedgersQ {
if q.Err != nil {
Expand Down
16 changes: 16 additions & 0 deletions src/github.com/stellar/horizon/db2/history/ledger_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,4 +27,20 @@ func TestLedgerQueries(t *testing.T) {
if tt.Assert.NoError(err) {
tt.Assert.Len(ls, 3)
}

// LedgersBySequence
err = q.LedgersBySequence(&ls, 1, 2, 3)

if tt.Assert.NoError(err) {
tt.Assert.Len(ls, 3)

foundSeqs := make([]int32, len(ls))
for i := range ls {
foundSeqs[i] = ls[i].Sequence
}

tt.Assert.Contains(foundSeqs, int32(1))
tt.Assert.Contains(foundSeqs, int32(2))
tt.Assert.Contains(foundSeqs, int32(3))
}
}
25 changes: 13 additions & 12 deletions src/github.com/stellar/horizon/resource/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -193,18 +193,19 @@ type Trade struct {
Buyer hal.Link `json:"buyer"`
} `json:"_links"`

ID string `json:"id"`
PT string `json:"paging_token"`
Seller string `json:"seller"`
SoldAmount string `json:"sold_amount"`
SoldAssetType string `json:"sold_asset_type"`
SoldAssetCode string `json:"sold_asset_code,omitempty"`
SoldAssetIssuer string `json:"sold_asset_issuer,omitempty"`
Buyer string `json:"buyer"`
BoughtAmount string `json:"bought_amount"`
BoughtAssetType string `json:"bought_asset_type"`
BoughtAssetCode string `json:"bought_asset_code,omitempty"`
BoughtAssetIssuer string `json:"bought_asset_issuer,omitempty"`
ID string `json:"id"`
PT string `json:"paging_token"`
Seller string `json:"seller"`
SoldAmount string `json:"sold_amount"`
SoldAssetType string `json:"sold_asset_type"`
SoldAssetCode string `json:"sold_asset_code,omitempty"`
SoldAssetIssuer string `json:"sold_asset_issuer,omitempty"`
Buyer string `json:"buyer"`
BoughtAmount string `json:"bought_amount"`
BoughtAssetType string `json:"bought_asset_type"`
BoughtAssetCode string `json:"bought_asset_code,omitempty"`
BoughtAssetIssuer string `json:"bought_asset_issuer,omitempty"`
LedgerCloseTime time.Time `json:"created_at"`
}

// Transaction represents a single, successful transaction
Expand Down
13 changes: 12 additions & 1 deletion src/github.com/stellar/horizon/resource/trade.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,26 @@ import (
)

// Populate fills out the details
func (res *Trade) Populate(ctx context.Context, row history.Effect) (err error) {
func (res *Trade) Populate(
ctx context.Context,
row history.Effect,
ledger history.Ledger,
) (err error) {
if row.Type != history.EffectTrade {
err = errors.New("invalid effect; not a trade")
return
}

if row.LedgerSequence() != ledger.Sequence {
err = errors.New("invalid ledger; different sequence than trade")
return
}

row.UnmarshalDetails(res)
res.ID = row.PagingToken()
res.PT = row.PagingToken()
res.Buyer = row.Account
res.LedgerCloseTime = ledger.ClosedAt

lb := hal.LinkBuilder{httpx.BaseURL(ctx)}
res.Links.Self = lb.Link("/accounts", res.Seller)
Expand Down

0 comments on commit f98b911

Please sign in to comment.