Change to last seen paging method for address internal transaction

Co-authored-by: Stamates <stamates@hotmail.com>
pull/306/head
jimmay5469 7 years ago committed by Stamates
parent c807fa539a
commit e23f25c50e
  1. 25
      apps/explorer/lib/explorer/chain.ex
  2. 21
      apps/explorer/test/explorer/chain_test.exs
  3. 64
      apps/explorer_web/lib/explorer_web/controllers/address_internal_transaction_controller.ex
  4. 30
      apps/explorer_web/lib/explorer_web/templates/address_internal_transaction/index.html.eex
  5. 60
      apps/explorer_web/test/explorer_web/controllers/address_internal_transaction_controller_test.exs

@ -91,13 +91,17 @@ defmodule Explorer.Chain do
* `:necessity_by_association` - use to load `t:association/0` as `:required` or `:optional`. If an association is
`:required`, and the `t:Explorer.Chain.InternalTransaction.t/0` has no associated record for that association,
then the `t:Explorer.Chain.InternalTransaction.t/0` will not be included in the page `entries`.
* `:pagination` - pagination params to pass to scrivener.
* `:paging_options` - a `t:Explorer.PagingOptions.t/0` used to specify the `:page_size` and
`:key` (a tuple of the lowest/oldest {block_number, transaction_index, index}) and. Results will be the internal
transactions older than the block number, transaction index, and index that are passed.
"""
@spec address_to_internal_transactions(Address.t(), [paging_options | necessity_by_association_option]) ::
InternalTransaction.t()
def address_to_internal_transactions(%Address{hash: hash}, options \\ []) do
necessity_by_association = Keyword.get(options, :necessity_by_association, %{})
direction = Keyword.get(options, :direction)
pagination = Keyword.get(options, :pagination, %{})
paging_options = Keyword.get(options, :paging_options, %PagingOptions{page_size: 50})
InternalTransaction
|> join(
@ -108,6 +112,8 @@ defmodule Explorer.Chain do
|> join(:left, [internal_transaction, transaction], block in assoc(transaction, :block))
|> where_address_fields_match(hash, direction)
|> where_transaction_has_multiple_internal_transactions()
|> page_internal_transaction(paging_options)
|> limit(^paging_options.page_size)
|> order_by(
[it, transaction, block],
desc: block.number,
@ -116,7 +122,7 @@ defmodule Explorer.Chain do
)
|> preload(transaction: :block)
|> join_associations(necessity_by_association)
|> Repo.paginate(pagination)
|> Repo.all()
end
@doc """
@ -2525,6 +2531,19 @@ defmodule Explorer.Chain do
)
end
defp page_internal_transaction(query, %PagingOptions{key: nil}), do: query
defp page_internal_transaction(query, %PagingOptions{key: {block_number, transaction_index, index}}) do
query
|> where(
[internal_transaction, transaction],
transaction.block_number < ^block_number or
(transaction.block_number == ^block_number and transaction.index < ^transaction_index) or
(transaction.block_number == ^block_number and transaction.index == ^transaction_index and
internal_transaction.index < ^index)
)
end
defp page_transaction(query, %PagingOptions{key: nil}), do: query
defp page_transaction(query, %PagingOptions{key: {block_number, index}}) do

@ -405,7 +405,7 @@ defmodule Explorer.ChainTest do
transaction: %Transaction{}
}
| _
] = Map.get(Chain.address_to_internal_transactions(address), :entries, [])
] = Chain.address_to_internal_transactions(address)
assert [
%InternalTransaction{
@ -415,17 +415,13 @@ defmodule Explorer.ChainTest do
}
| _
] =
Map.get(
Chain.address_to_internal_transactions(
address,
necessity_by_association: %{
from_address: :optional,
to_address: :optional,
transaction: :optional
}
),
:entries,
[]
Chain.address_to_internal_transactions(
address,
necessity_by_association: %{
from_address: :optional,
to_address: :optional,
transaction: :optional
}
)
end
@ -520,7 +516,6 @@ defmodule Explorer.ChainTest do
result =
address
|> Chain.address_to_internal_transactions()
|> Map.get(:entries, [])
|> Enum.map(& &1.id)
assert [second_pending, first_pending, sixth, fifth, fourth, third, second, first] == result

@ -7,33 +7,49 @@ defmodule ExplorerWeb.AddressInternalTransactionController do
import ExplorerWeb.AddressController, only: [transaction_count: 1]
alias Explorer.{Chain, Market}
alias Explorer.{Chain, Market, PagingOptions}
alias Explorer.ExchangeRates.Token
def index(conn, %{"address_id" => address_hash_string} = params) do
@default_paging_options %PagingOptions{page_size: 50}
def index(conn, %{"block_number" => block_number_string, "transaction_index" => transaction_index_string, "index" => index_string} = params) do
with {block_number, ""} <- Integer.parse(block_number_string),
{transaction_index, ""} <- Integer.parse(transaction_index_string),
{index, ""} <- Integer.parse(index_string) do
do_index(conn, Map.put(params, :paging_options, %{@default_paging_options | key: {block_number, transaction_index, index}}))
else
_ ->
unprocessable_entity(conn)
end
end
def index(conn, params), do: do_index(conn, params)
def do_index(conn, %{"address_id" => address_hash_string} = params) do
with {:ok, address_hash} <- Chain.string_to_address_hash(address_hash_string),
{:ok, address} <- Chain.hash_to_address(address_hash) do
options = [
necessity_by_association: %{
from_address: :optional,
to_address: :optional
},
pagination: params
]
page =
Chain.address_to_internal_transactions(
address,
Keyword.merge(options, current_filter(params))
full_options =
Keyword.merge(
[
necessity_by_association: %{
from_address: :optional,
to_address: :optional
},
paging_options: @default_paging_options
],
current_filter(params)
)
internal_transactions = Chain.address_to_internal_transactions(address, full_options)
render(
conn,
"index.html",
address: address,
earliest: earliest(internal_transactions),
exchange_rate: Market.get_exchange_rate(Explorer.coin()) || Token.null(),
filter: params["filter"],
page: page,
internal_transactions: internal_transactions,
transaction_count: transaction_count(address)
)
else
@ -45,6 +61,24 @@ defmodule ExplorerWeb.AddressInternalTransactionController do
end
end
defp earliest([]), do: nil
defp earliest(internal_transactions) do
last = List.last(internal_transactions)
{:ok, last_transaction} = Chain.hash_to_transaction(last.transaction_hash)
%{block_number: last_transaction.block_number, transaction_index: last_transaction.index, index: last.index}
end
defp current_filter(%{paging_options: paging_options} = params) do
params
|> Map.get("filter")
|> case do
"to" -> [direction: :to, paging_options: paging_options]
"from" -> [direction: :from, paging_options: paging_options]
_ -> [paging_options: paging_options]
end
end
defp current_filter(params) do
params
|> Map.get("filter")

@ -38,7 +38,7 @@
</div>
<div class="card-body">
<%= if Enum.count(@page) > 0 do %>
<%= if Enum.count(@internal_transactions) > 0 do %>
<div class="dropdown u-float-right u-push-sm">
<button data-test="filter_dropdown" class="button button--secondary button--xsmall dropdown-toggle" type="button"
id="dropdownMenu2" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
@ -87,7 +87,7 @@
<th><%= gettext "To" %></th>
<th><%= gettext "Value" %> (<%= gettext "Ether" %>)</th>
</thead>
<%= for internal_transaction <- @page do %>
<%= for internal_transaction <- @internal_transactions do %>
<tgroup>
<tr data-test="internal_transaction">
<td>
@ -115,22 +115,18 @@
<% end %>
</div>
</div>
<%= if Enum.count(@page) > 0 do %>
<div class="address__pagination">
<%= pagination_links(
<%= if @earliest do %>
<%= link(
gettext("Older"),
class: "button button--secondary button--sm u-float-right mt-3",
to: address_internal_transaction_path(
@conn,
@page,
["en", @conn.params["address_id"]],
distance: 1,
filter: @conn.params["filter"],
first: true,
next: Phoenix.HTML.raw("&rsaquo;"),
path: &address_internal_transaction_path/5,
previous: Phoenix.HTML.raw("&lsaquo;"),
view_style: :bulma
) %>
</div>
:index,
@conn.assigns.locale,
@address,
%{"block_number" => @earliest.block_number, "transaction_index" => @earliest.transaction_index, "index" => @earliest.index}
)
) %>
<% end %>
</section>
</section>

@ -3,6 +3,7 @@ defmodule ExplorerWeb.AddressInternalTransactionControllerTest do
import ExplorerWeb.Router.Helpers, only: [address_internal_transaction_path: 4]
alias Explorer.Chain.InternalTransaction
alias Explorer.ExchangeRates.Token
describe "GET index/3" do
@ -38,7 +39,7 @@ defmodule ExplorerWeb.AddressInternalTransactionControllerTest do
conn = get(conn, path)
actual_transaction_ids =
conn.assigns.page
conn.assigns.internal_transactions
|> Enum.map(fn internal_transaction -> internal_transaction.id end)
assert Enum.member?(actual_transaction_ids, from_internal_transaction.id)
@ -52,5 +53,62 @@ defmodule ExplorerWeb.AddressInternalTransactionControllerTest do
assert %Token{} = conn.assigns.exchange_rate
end
test "returns next page of results based on last seen internal transaction", %{conn: conn} do
address = insert(:address)
a_block = insert(:block, number: 1000)
b_block = insert(:block, number: 2000)
transaction_1 =
:transaction
|> insert()
|> with_block(a_block)
transaction_2 =
:transaction
|> insert()
|> with_block(a_block)
transaction_3 =
:transaction
|> insert()
|> with_block(b_block)
transaction_1_hashes =
1..20
|> Enum.map(fn index -> insert(:internal_transaction, transaction_hash: transaction_1.hash, from_address_hash: address.hash, index: index) end)
|> Enum.map(& "#{&1.transaction_hash}.#{&1.index}")
transaction_2_hashes =
1..20
|> Enum.map(fn index -> insert(:internal_transaction, transaction_hash: transaction_2.hash, from_address_hash: address.hash, index: index) end)
|> Enum.map(& "#{&1.transaction_hash}.#{&1.index}")
transaction_3_hashes =
1..10
|> Enum.map(fn index -> insert(:internal_transaction, transaction_hash: transaction_3.hash, from_address_hash: address.hash, index: index) end)
|> Enum.map(& "#{&1.transaction_hash}.#{&1.index}")
second_page_hashes = transaction_1_hashes ++ transaction_2_hashes ++ transaction_3_hashes
%InternalTransaction{index: index} =
:internal_transaction
|> insert(transaction_hash: transaction_3.hash, from_address_hash: address.hash, index: 11)
conn =
get(conn, address_internal_transaction_path(ExplorerWeb.Endpoint, :index, :en, address.hash), %{
"block_number" => Integer.to_string(b_block.number),
"transaction_index" => Integer.to_string(transaction_3.index),
"index" => Integer.to_string(index)
})
actual_hashes =
conn.assigns.internal_transactions
|> Enum.map(& "#{&1.transaction_hash}.#{&1.index}")
|> Enum.reverse()
assert second_page_hashes == actual_hashes
end
end
end

Loading…
Cancel
Save