Refactor token transfers' pagination

We changed the token transfer's pagination uses the block_number instead
of the inserted_at. It will make the pagination consistent since the
list is sorted by the block_number.
pull/1124/head
Felipe Renan 6 years ago
parent c486837ba8
commit da72f83b35
  1. 12
      apps/block_scout_web/lib/block_scout_web/chain.ex
  2. 18
      apps/block_scout_web/test/block_scout_web/controllers/transaction_token_transfer_controller_test.exs
  3. 19
      apps/explorer/lib/explorer/chain/token_transfer.ex
  4. 43
      apps/explorer/test/explorer/chain/token_transfer_test.exs
  5. 10
      apps/explorer/test/explorer/chain_test.exs

@ -130,9 +130,6 @@ defmodule BlockScoutWeb.Chain do
end end
end end
def paging_options(%{"inserted_at" => inserted_at}),
do: [paging_options: %{@default_paging_options | key: inserted_at}]
def paging_options(%{"token_name" => name, "token_type" => type, "token_inserted_at" => inserted_at}), def paging_options(%{"token_name" => name, "token_type" => type, "token_inserted_at" => inserted_at}),
do: [paging_options: %{@default_paging_options | key: {name, type, inserted_at}}] do: [paging_options: %{@default_paging_options | key: {name, type, inserted_at}}]
@ -180,13 +177,8 @@ defmodule BlockScoutWeb.Chain do
%{"block_number" => block_number, "index" => index} %{"block_number" => block_number, "index" => index}
end end
defp paging_params(%TokenTransfer{inserted_at: inserted_at}) do defp paging_params(%TokenTransfer{block_number: block_number, log_index: index}) do
inserted_at_datetime = %{"block_number" => block_number, "index" => index}
inserted_at
|> DateTime.from_naive!("Etc/UTC")
|> DateTime.to_iso8601()
%{"inserted_at" => inserted_at_datetime}
end end
defp paging_params(%Address.Token{name: name, type: type, inserted_at: inserted_at}) do defp paging_params(%Address.Token{name: name, type: type, inserted_at: inserted_at}) do

@ -82,25 +82,21 @@ defmodule BlockScoutWeb.TransactionTokenTransferControllerTest do
|> insert() |> insert()
|> with_block() |> with_block()
{:ok, first_transfer_time} = NaiveDateTime.new(2000, 1, 1, 0, 0, 5) token_transfer = insert(:token_transfer, transaction: transaction, block_number: 1000, log_index: 1)
{:ok, remaining_transfers_time} = NaiveDateTime.new(1999, 1, 1, 0, 0, 0)
insert(:token_transfer, transaction: transaction, inserted_at: first_transfer_time)
1..5 Enum.each(2..5, fn item ->
|> Enum.each(fn log_index -> insert(:token_transfer, transaction: transaction, block_number: item + 1001, log_index: item + 1)
insert(:token_transfer, transaction: transaction, inserted_at: remaining_transfers_time, log_index: log_index)
end) end)
conn = conn =
get(conn, transaction_token_transfer_path(BlockScoutWeb.Endpoint, :index, transaction.hash), %{ get(conn, transaction_token_transfer_path(BlockScoutWeb.Endpoint, :index, transaction.hash), %{
"inserted_at" => first_transfer_time |> DateTime.from_naive!("Etc/UTC") |> DateTime.to_iso8601() "block_number" => "1000",
"index" => "1"
}) })
actual_times = actual_log_indexes = Enum.map(conn.assigns.token_transfers, & &1.log_index)
conn.assigns.token_transfers
|> Enum.map(& &1.inserted_at)
refute Enum.any?(actual_times, fn time -> first_transfer_time == time end) refute Enum.any?(actual_log_indexes, fn log_index -> log_index == token_transfer.log_index end)
end end
test "next_page_params exist if not on last page", %{conn: conn} do test "next_page_params exist if not on last page", %{conn: conn} do

@ -24,7 +24,8 @@ defmodule Explorer.Chain.TokenTransfer do
use Ecto.Schema use Ecto.Schema
import Ecto.{Changeset, Query} import Ecto.Changeset
import Ecto.Query, only: [from: 2, dynamic: 2, limit: 2, where: 3]
alias Explorer.Chain.{Address, Hash, Token, TokenTransfer, Transaction} alias Explorer.Chain.{Address, Hash, Token, TokenTransfer, Transaction}
alias Explorer.{PagingOptions, Repo} alias Explorer.{PagingOptions, Repo}
@ -123,7 +124,7 @@ defmodule Explorer.Chain.TokenTransfer do
tt in TokenTransfer, tt in TokenTransfer,
where: tt.token_contract_address_hash == ^token_address_hash, where: tt.token_contract_address_hash == ^token_address_hash,
preload: [{:transaction, :block}, :token, :from_address, :to_address], preload: [{:transaction, :block}, :token, :from_address, :to_address],
order_by: [desc: tt.block_number] order_by: [desc: tt.block_number, desc: tt.log_index]
) )
query query
@ -134,19 +135,11 @@ defmodule Explorer.Chain.TokenTransfer do
def page_token_transfer(query, %PagingOptions{key: nil}), do: query def page_token_transfer(query, %PagingOptions{key: nil}), do: query
def page_token_transfer(query, %PagingOptions{key: {token_id}}) do def page_token_transfer(query, %PagingOptions{key: {block_number, log_index}}) do
where( where(
query, query,
[token_transfer], [tt],
token_transfer.token_id > ^token_id tt.block_number < ^block_number or (tt.block_number == ^block_number and tt.log_index < ^log_index)
)
end
def page_token_transfer(query, %PagingOptions{key: inserted_at}) do
where(
query,
[token_transfer],
token_transfer.inserted_at < ^inserted_at
) )
end end

@ -8,7 +8,7 @@ defmodule Explorer.Chain.TokenTransferTest do
doctest Explorer.Chain.TokenTransfer doctest Explorer.Chain.TokenTransfer
describe "fetch_token_transfers/2" do describe "fetch_token_transfers_from_token_hash/2" do
test "returns token transfers for the given address" do test "returns token transfers for the given address" do
token_contract_address = insert(:contract_address) token_contract_address = insert(:contract_address)
@ -87,6 +87,7 @@ defmodule Explorer.Chain.TokenTransferTest do
second_page = second_page =
insert( insert(
:token_transfer, :token_transfer,
block_number: 999,
to_address: build(:address), to_address: build(:address),
transaction: transaction, transaction: transaction,
token_contract_address: token_contract_address, token_contract_address: token_contract_address,
@ -96,23 +97,53 @@ defmodule Explorer.Chain.TokenTransferTest do
first_page = first_page =
insert( insert(
:token_transfer, :token_transfer,
block_number: 1000,
to_address: build(:address), to_address: build(:address),
transaction: transaction, transaction: transaction,
token_contract_address: token_contract_address, token_contract_address: token_contract_address,
token: token token: token
) )
paging_options = %PagingOptions{key: first_page.inserted_at, page_size: 1} paging_options = %PagingOptions{key: {first_page.block_number, first_page.log_index}, page_size: 1}
token_transfers_primary_keys_paginated = token_transfers_primary_keys_paginated =
TokenTransfer.fetch_token_transfers_from_token_hash( token_contract_address.hash
token_contract_address.hash, |> TokenTransfer.fetch_token_transfers_from_token_hash(paging_options: paging_options)
paging_options: paging_options
)
|> Enum.map(&{&1.transaction_hash, &1.log_index}) |> Enum.map(&{&1.transaction_hash, &1.log_index})
assert token_transfers_primary_keys_paginated == [{second_page.transaction_hash, second_page.log_index}] assert token_transfers_primary_keys_paginated == [{second_page.transaction_hash, second_page.log_index}]
end end
test "paginates considering the log_index when there are repeated block numbers" do
token_contract_address = insert(:contract_address)
transaction =
:transaction
|> insert()
|> with_block()
token = insert(:token)
token_transfer =
insert(
:token_transfer,
block_number: 1000,
log_index: 0,
to_address: build(:address),
transaction: transaction,
token_contract_address: token_contract_address,
token: token
)
paging_options = %PagingOptions{key: {token_transfer.block_number, token_transfer.log_index + 1}, page_size: 1}
token_transfers_primary_keys_paginated =
token_contract_address.hash
|> TokenTransfer.fetch_token_transfers_from_token_hash(paging_options: paging_options)
|> Enum.map(&{&1.transaction_hash, &1.log_index})
assert token_transfers_primary_keys_paginated == [{token_transfer.transaction_hash, token_transfer.log_index}]
end
end end
describe "count_token_transfers/0" do describe "count_token_transfers/0" do

@ -3112,6 +3112,7 @@ defmodule Explorer.ChainTest do
first_page = first_page =
insert( insert(
:token_transfer, :token_transfer,
block_number: 1000,
to_address: build(:address), to_address: build(:address),
transaction: transaction, transaction: transaction,
token_contract_address: token_contract_address, token_contract_address: token_contract_address,
@ -3122,6 +3123,7 @@ defmodule Explorer.ChainTest do
second_page = second_page =
insert( insert(
:token_transfer, :token_transfer,
block_number: 999,
to_address: build(:address), to_address: build(:address),
transaction: transaction, transaction: transaction,
token_contract_address: token_contract_address, token_contract_address: token_contract_address,
@ -3129,13 +3131,11 @@ defmodule Explorer.ChainTest do
token_id: 29 token_id: 29
) )
paging_options = %PagingOptions{key: {first_page.token_id}, page_size: 1} paging_options = %PagingOptions{key: {first_page.block_number, first_page.log_index}, page_size: 1}
unique_tokens_ids_paginated = unique_tokens_ids_paginated =
Chain.address_to_unique_tokens( token_contract_address.hash
token_contract_address.hash, |> Chain.address_to_unique_tokens(paging_options: paging_options)
paging_options: paging_options
)
|> Enum.map(& &1.token_id) |> Enum.map(& &1.token_id)
assert unique_tokens_ids_paginated == [second_page.token_id] assert unique_tokens_ids_paginated == [second_page.token_id]

Loading…
Cancel
Save