Merge branch 'master' into frg-fix-token-transfers-performance-issue

pull/1124/head
Andrew Cravenho 6 years ago committed by GitHub
commit 3b6519cf9a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 39
      apps/block_scout_web/lib/block_scout_web/controllers/address_internal_transaction_controller.ex
  2. 46
      apps/block_scout_web/lib/block_scout_web/templates/address_internal_transaction/index.html.eex
  3. 9
      apps/block_scout_web/priv/gettext/default.pot
  4. 9
      apps/block_scout_web/priv/gettext/en/LC_MESSAGES/default.po
  5. 145
      apps/block_scout_web/test/block_scout_web/controllers/address_internal_transaction_controller_test.exs
  6. 85
      apps/block_scout_web/test/block_scout_web/features/viewing_addresses_test.exs

@ -8,10 +8,12 @@ defmodule BlockScoutWeb.AddressInternalTransactionController do
import BlockScoutWeb.AddressController, only: [transaction_count: 1, validation_count: 1]
import BlockScoutWeb.Chain, only: [current_filter: 1, paging_options: 1, next_page_params: 3, split_list_by_page: 1]
alias BlockScoutWeb.InternalTransactionView
alias Explorer.{Chain, Market}
alias Explorer.ExchangeRates.Token
alias Phoenix.View
def index(conn, %{"address_id" => address_hash_string} = params) do
def index(conn, %{"address_id" => address_hash_string, "type" => "JSON"} = params) do
with {:ok, address_hash} <- Chain.string_to_address_hash(address_hash_string),
{:ok, address} <- Chain.hash_to_address(address_hash) do
full_options =
@ -28,14 +30,45 @@ defmodule BlockScoutWeb.AddressInternalTransactionController do
internal_transactions_plus_one = Chain.address_to_internal_transactions(address, full_options)
{internal_transactions, next_page} = split_list_by_page(internal_transactions_plus_one)
next_page_path =
case next_page_params(next_page, internal_transactions, params) do
nil ->
nil
next_page_params ->
address_internal_transaction_path(conn, :index, address_hash, Map.delete(next_page_params, "type"))
end
internal_transactions_json =
Enum.map(internal_transactions, fn internal_transaction ->
View.render_to_string(
InternalTransactionView,
"_tile.html",
current_address: address,
internal_transaction: internal_transaction
)
end)
json(conn, %{items: internal_transactions_json, next_page_path: next_page_path})
else
:error ->
not_found(conn)
{:error, :not_found} ->
not_found(conn)
end
end
def 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
render(
conn,
"index.html",
address: address,
next_page_params: next_page_params(next_page, internal_transactions, params),
current_path: current_path(conn),
exchange_rate: Market.get_exchange_rate(Explorer.coin()) || Token.null(),
filter: params["filter"],
internal_transactions: internal_transactions,
transaction_count: transaction_count(address),
validation_count: validation_count(address)
)

@ -7,7 +7,7 @@
<%= render BlockScoutWeb.AddressView, "_tabs.html", assigns %>
</div>
<div class="card-body">
<div class="card-body" data-async-listing="<%= @current_path %>">
<div data-selector="channel-batching-message" style="display: none;">
<div data-selector="reload-button" class="alert alert-info">
<a href="#" class="alert-link"><span data-selector="channel-batching-count"></span> <%= gettext "More internal transactions have come in" %></a>
@ -54,31 +54,33 @@
) %>
</div>
</div>
<h2 class="card-title"><%= gettext "Internal Transactions" %></h2>
<%= if Enum.count(@internal_transactions) > 0 do %>
<span data-selector="internal-transactions-list">
<%= for internal_transaction <- @internal_transactions do %>
<%= render BlockScoutWeb.InternalTransactionView, "_tile.html", current_address: @address, internal_transaction: internal_transaction %>
<% end %>
</span>
<% else %>
<button data-error-message class="alert alert-danger col-12 text-left" style="display: none;">
<span href="#" class="alert-link"><%= gettext("Something went wrong, click to reload.") %></span>
</button>
<div data-empty-response-message style="display: none;">
<div class="tile tile-muted text-center">
<span data-selector="empty-internal-transactions-list"><%= gettext "There are no internal transactions for this address." %></span>
</div>
<% end %>
<div>
<%= if @next_page_params do %>
<%= link(
gettext("Older"),
class: "button button-secondary button-sm float-right mt-3",
to: address_internal_transaction_path(
@conn,
:index,
@address,
@next_page_params
)
) %>
<% end %>
</div>
<div data-loading-message class="tile tile-muted text-center mt-3">
<span class="loading-spinner-small mr-2">
<span class="loading-spinner-block-1"></span>
<span class="loading-spinner-block-2"></span>
</span>
<%= gettext("Loading") %>...
</div>
<div data-items></div>
<a href="#" class="button button-secondary button-small float-right mt-4" data-next-page-button style="display: none;">
<%= gettext("Older") %>
</a>
<div class="button button-secondary button-small float-right mt-4" data-loading-button style="display: none;">
<span class="loading-spinner-small mr-2">
<span class="loading-spinner-block-1"></span>
<span class="loading-spinner-block-2"></span>
</span>
<%= gettext("Loading") %>...
</div>
</div>
</div>

@ -505,7 +505,7 @@ msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address/_tabs.html.eex:21
#: lib/block_scout_web/templates/address/_tabs.html.eex:81
#: lib/block_scout_web/templates/address_internal_transaction/index.html.eex:57
#: lib/block_scout_web/templates/address_internal_transaction/index.html.eex:58
#: lib/block_scout_web/templates/address_validation/index.html.eex:24
#: lib/block_scout_web/templates/address_validation/index.html.eex:75
#: lib/block_scout_web/templates/transaction/_tabs.html.eex:14
@ -638,7 +638,7 @@ msgid "OUT"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_internal_transaction/index.html.eex:72
#: lib/block_scout_web/templates/address_internal_transaction/index.html.eex:76
#: lib/block_scout_web/templates/address_validation/index.html.eex:117
#: lib/block_scout_web/templates/block/index.html.eex:30
#: lib/block_scout_web/templates/block_transaction/index.html.eex:50
@ -815,7 +815,7 @@ msgid "There are no holders for this Token."
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_internal_transaction/index.html.eex:66
#: lib/block_scout_web/templates/address_internal_transaction/index.html.eex:64
msgid "There are no internal transactions for this address."
msgstr ""
@ -1214,6 +1214,8 @@ msgid "GraphQL"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_internal_transaction/index.html.eex:72
#: lib/block_scout_web/templates/address_internal_transaction/index.html.eex:83
#: lib/block_scout_web/templates/address_transaction/index.html.eex:61
#: lib/block_scout_web/templates/block/index.html.eex:22
#: lib/block_scout_web/templates/pending_transaction/index.html.eex:33
@ -1409,6 +1411,7 @@ msgid "Log Data"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_internal_transaction/index.html.eex:60
#: lib/block_scout_web/templates/tokens/transfer/index.html.eex:21
msgid "Something went wrong, click to reload."
msgstr ""

@ -505,7 +505,7 @@ msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address/_tabs.html.eex:21
#: lib/block_scout_web/templates/address/_tabs.html.eex:81
#: lib/block_scout_web/templates/address_internal_transaction/index.html.eex:57
#: lib/block_scout_web/templates/address_internal_transaction/index.html.eex:58
#: lib/block_scout_web/templates/address_validation/index.html.eex:24
#: lib/block_scout_web/templates/address_validation/index.html.eex:75
#: lib/block_scout_web/templates/transaction/_tabs.html.eex:14
@ -638,7 +638,7 @@ msgid "OUT"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_internal_transaction/index.html.eex:72
#: lib/block_scout_web/templates/address_internal_transaction/index.html.eex:76
#: lib/block_scout_web/templates/address_validation/index.html.eex:117
#: lib/block_scout_web/templates/block/index.html.eex:30
#: lib/block_scout_web/templates/block_transaction/index.html.eex:50
@ -815,7 +815,7 @@ msgid "There are no holders for this Token."
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_internal_transaction/index.html.eex:66
#: lib/block_scout_web/templates/address_internal_transaction/index.html.eex:64
msgid "There are no internal transactions for this address."
msgstr ""
@ -1214,6 +1214,8 @@ msgid "GraphQL"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_internal_transaction/index.html.eex:72
#: lib/block_scout_web/templates/address_internal_transaction/index.html.eex:83
#: lib/block_scout_web/templates/address_transaction/index.html.eex:61
#: lib/block_scout_web/templates/block/index.html.eex:22
#: lib/block_scout_web/templates/pending_transaction/index.html.eex:33
@ -1409,6 +1411,7 @@ msgid "Log Data"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_internal_transaction/index.html.eex:60
#: lib/block_scout_web/templates/tokens/transfer/index.html.eex:21
msgid "Something went wrong, click to reload."
msgstr ""

@ -1,7 +1,8 @@
defmodule BlockScoutWeb.AddressInternalTransactionControllerTest do
use BlockScoutWeb.ConnCase
import BlockScoutWeb.Router.Helpers, only: [address_internal_transaction_path: 3]
import BlockScoutWeb.Router.Helpers,
only: [address_internal_transaction_path: 3, address_internal_transaction_path: 4]
alias Explorer.Chain.{Block, InternalTransaction, Transaction}
alias Explorer.ExchangeRates.Token
@ -21,6 +22,14 @@ defmodule BlockScoutWeb.AddressInternalTransactionControllerTest do
assert html_response(conn, 404)
end
test "includes USD exchange rate value for address in assigns", %{conn: conn} do
address = insert(:address)
conn = get(conn, address_internal_transaction_path(BlockScoutWeb.Endpoint, :index, address.hash))
assert %Token{} = conn.assigns.exchange_rate
end
test "returns internal transactions for the address", %{conn: conn} do
address = insert(:address)
@ -47,29 +56,101 @@ defmodule BlockScoutWeb.AddressInternalTransactionControllerTest do
transaction_index: transaction.index
)
path = address_internal_transaction_path(conn, :index, address)
path = address_internal_transaction_path(conn, :index, address, %{"type" => "JSON"})
conn = get(conn, path)
actual_internal_transaction_primary_keys =
Enum.map(conn.assigns.internal_transactions, &{&1.transaction_hash, &1.index})
internal_transaction_tiles = json_response(conn, 200)["items"]
assert Enum.all?([from_internal_transaction, to_internal_transaction], fn internal_transaction ->
Enum.any?(internal_transaction_tiles, fn tile ->
String.contains?(tile, to_string(internal_transaction.transaction_hash)) &&
String.contains?(tile, "data-internal-transaction-index=\"#{internal_transaction.index}\"")
end)
end)
end
test "returns internal transactions coming from the address", %{conn: conn} do
address = insert(:address)
transaction =
:transaction
|> insert()
|> with_block(insert(:block, number: 1))
assert Enum.member?(
actual_internal_transaction_primary_keys,
{from_internal_transaction.transaction_hash, from_internal_transaction.index}
from_internal_transaction =
insert(:internal_transaction,
transaction: transaction,
from_address: address,
index: 1,
block_number: transaction.block_number,
transaction_index: transaction.index
)
assert Enum.member?(
actual_internal_transaction_primary_keys,
{to_internal_transaction.transaction_hash, to_internal_transaction.index}
to_internal_transaction =
insert(:internal_transaction,
transaction: transaction,
to_address: address,
index: 2,
block_number: transaction.block_number,
transaction_index: transaction.index
)
path = address_internal_transaction_path(conn, :index, address, %{"filter" => "from", "type" => "JSON"})
conn = get(conn, path)
internal_transaction_tiles = json_response(conn, 200)["items"]
assert Enum.any?(internal_transaction_tiles, fn tile ->
String.contains?(tile, to_string(from_internal_transaction.transaction_hash)) &&
String.contains?(tile, "data-internal-transaction-index=\"#{from_internal_transaction.index}\"")
end)
refute Enum.any?(internal_transaction_tiles, fn tile ->
String.contains?(tile, to_string(to_internal_transaction.transaction_hash)) &&
String.contains?(tile, "data-internal-transaction-index=\"#{to_internal_transaction.index}\"")
end)
end
test "includes USD exchange rate value for address in assigns", %{conn: conn} do
test "returns internal transactions going to the address", %{conn: conn} do
address = insert(:address)
conn = get(conn, address_internal_transaction_path(BlockScoutWeb.Endpoint, :index, address.hash))
transaction =
:transaction
|> insert()
|> with_block(insert(:block, number: 1))
assert %Token{} = conn.assigns.exchange_rate
from_internal_transaction =
insert(:internal_transaction,
transaction: transaction,
from_address: address,
index: 1,
block_number: transaction.block_number,
transaction_index: transaction.index
)
to_internal_transaction =
insert(:internal_transaction,
transaction: transaction,
to_address: address,
index: 2,
block_number: transaction.block_number,
transaction_index: transaction.index
)
path = address_internal_transaction_path(conn, :index, address, %{"filter" => "to", "type" => "JSON"})
conn = get(conn, path)
internal_transaction_tiles = json_response(conn, 200)["items"]
assert Enum.any?(internal_transaction_tiles, fn tile ->
String.contains?(tile, to_string(to_internal_transaction.transaction_hash)) &&
String.contains?(tile, "data-internal-transaction-index=\"#{to_internal_transaction.index}\"")
end)
refute Enum.any?(internal_transaction_tiles, fn tile ->
String.contains?(tile, to_string(from_internal_transaction.transaction_hash)) &&
String.contains?(tile, "data-internal-transaction-index=\"#{from_internal_transaction.index}\"")
end)
end
test "returns next page of results based on last seen internal transaction", %{conn: conn} do
@ -105,7 +186,6 @@ defmodule BlockScoutWeb.AddressInternalTransactionControllerTest do
transaction_index: transaction_1.index
)
end)
|> Enum.map(&"#{&1.transaction_hash}.#{&1.index}")
transaction_2_hashes =
1..20
@ -119,7 +199,6 @@ defmodule BlockScoutWeb.AddressInternalTransactionControllerTest do
transaction_index: transaction_2.index
)
end)
|> Enum.map(&"#{&1.transaction_hash}.#{&1.index}")
transaction_3_hashes =
1..10
@ -133,9 +212,8 @@ defmodule BlockScoutWeb.AddressInternalTransactionControllerTest do
transaction_index: transaction_3.index
)
end)
|> Enum.map(&"#{&1.transaction_hash}.#{&1.index}")
second_page_hashes = transaction_1_hashes ++ transaction_2_hashes ++ transaction_3_hashes
second_page = transaction_1_hashes ++ transaction_2_hashes ++ transaction_3_hashes
%InternalTransaction{index: index} =
insert(
@ -151,15 +229,18 @@ defmodule BlockScoutWeb.AddressInternalTransactionControllerTest do
get(conn, address_internal_transaction_path(BlockScoutWeb.Endpoint, :index, address.hash), %{
"block_number" => Integer.to_string(b_block.number),
"transaction_index" => Integer.to_string(transaction_3.index),
"index" => Integer.to_string(index)
"index" => Integer.to_string(index),
"type" => "JSON"
})
actual_hashes =
conn.assigns.internal_transactions
|> Enum.map(&"#{&1.transaction_hash}.#{&1.index}")
|> Enum.reverse()
internal_transaction_tiles = json_response(conn, 200)["items"]
assert second_page_hashes == actual_hashes
assert Enum.all?(second_page, fn internal_transaction ->
Enum.any?(internal_transaction_tiles, fn tile ->
String.contains?(tile, to_string(internal_transaction.transaction_hash)) &&
String.contains?(tile, "data-internal-transaction-index=\"#{internal_transaction.index}\"")
end)
end)
end
test "next_page_params exist if not on last page", %{conn: conn} do
@ -184,10 +265,17 @@ defmodule BlockScoutWeb.AddressInternalTransactionControllerTest do
)
end)
conn = get(conn, address_internal_transaction_path(BlockScoutWeb.Endpoint, :index, address.hash))
conn =
get(conn, address_internal_transaction_path(BlockScoutWeb.Endpoint, :index, address.hash, %{"type" => "JSON"}))
assert %{"block_number" => ^number, "index" => 11, "transaction_index" => ^transaction_index} =
conn.assigns.next_page_params
expected_response =
address_internal_transaction_path(BlockScoutWeb.Endpoint, :index, address.hash, %{
"block_number" => number,
"index" => 11,
"transaction_index" => transaction_index
})
assert expected_response == json_response(conn, 200)["next_page_path"]
end
test "next_page_params are empty if on last page", %{conn: conn} do
@ -208,9 +296,10 @@ defmodule BlockScoutWeb.AddressInternalTransactionControllerTest do
)
end)
conn = get(conn, address_internal_transaction_path(BlockScoutWeb.Endpoint, :index, address.hash))
conn =
get(conn, address_internal_transaction_path(BlockScoutWeb.Endpoint, :index, address.hash, %{"type" => "JSON"}))
refute conn.assigns.next_page_params
assert %{"next_page_path" => nil} = json_response(conn, 200)
end
end
end

@ -147,35 +147,6 @@ defmodule BlockScoutWeb.ViewingAddressesTest do
|> assert_has(AddressPage.transaction(transactions.from_taft))
end
test "contract creation is shown for to_address on list page", %{
addresses: addresses,
block: block,
session: session
} do
lincoln = addresses.lincoln
contract_address = insert(:contract_address)
from_lincoln =
:transaction
|> insert(from_address: lincoln, to_address: nil)
|> with_contract_creation(contract_address)
|> with_block(block)
internal_transaction =
:internal_transaction_create
|> insert(
transaction: from_lincoln,
from_address: lincoln,
index: 1
)
|> with_contract_creation(contract_address)
session
|> AddressPage.visit_page(addresses.lincoln)
|> assert_has(AddressPage.contract_creation(internal_transaction))
end
test "only addresses not matching the page are links", %{
addresses: addresses,
session: session,
@ -213,29 +184,7 @@ defmodule BlockScoutWeb.ViewingAddressesTest do
{:ok, %{internal_transaction_lincoln_to_address: internal_transaction_lincoln_to_address}}
end
test "can see internal transactions for an address", %{addresses: addresses, session: session} do
session
|> AddressPage.visit_page(addresses.lincoln)
|> AddressPage.click_internal_transactions()
|> assert_has(AddressPage.internal_transactions(count: 2))
end
test "can filter to only see internal transactions from an address", %{addresses: addresses, session: session} do
session
|> AddressPage.visit_page(addresses.lincoln)
|> AddressPage.click_internal_transactions()
|> AddressPage.apply_filter("From")
|> assert_has(AddressPage.internal_transactions(count: 1))
end
test "can filter to only see internal transactions to an address", %{addresses: addresses, session: session} do
session
|> AddressPage.visit_page(addresses.lincoln)
|> AddressPage.click_internal_transactions()
|> AddressPage.apply_filter("To")
|> assert_has(AddressPage.internal_transactions(count: 1))
end
@tag :skip
test "only addresses not matching the page are links", %{
addresses: addresses,
internal_transaction_lincoln_to_address: internal_transaction,
@ -248,6 +197,7 @@ defmodule BlockScoutWeb.ViewingAddressesTest do
|> refute_has(AddressPage.internal_transaction_address_link(internal_transaction, :to))
end
@tag :skip
test "viewing new internal transactions via live update", %{addresses: addresses, session: session} do
transaction =
:transaction
@ -276,37 +226,6 @@ defmodule BlockScoutWeb.ViewingAddressesTest do
end
end
test "contract creation is shown for to_address on list page", %{
addresses: addresses,
block: block,
session: session
} do
lincoln = addresses.lincoln
contract_address = insert(:contract_address)
from_lincoln =
:transaction
|> insert(from_address: lincoln, to_address: nil)
|> with_block(block)
|> with_contract_creation(contract_address)
internal_transaction =
:internal_transaction_create
|> insert(
transaction: from_lincoln,
from_address: lincoln,
index: 1,
block_number: from_lincoln.block_number,
transaction_index: from_lincoln.index
)
|> with_contract_creation(contract_address)
session
|> AddressPage.visit_page(addresses.lincoln)
|> AddressPage.click_internal_transactions()
|> assert_has(AddressPage.contract_creation(internal_transaction))
end
describe "viewing token transfers" do
test "contributor can see all token transfers that he sent", %{
addresses: addresses,

Loading…
Cancel
Save