API account#txlist filterby=from support

Why:

* For API users to be able to get a list of transactions in which a
given address is the 'sender' (address is the 'from' in transaction).

  Example usage:
    ```
      /api?module=account&action&txlist&address={someAddress}&filterby=from
    ```
* Issue link: https://github.com/poanetwork/blockscout/issues/635

This change addresses the need by:

* Editing `where_address_match/3` in `Explorer.Etherscan` to support
`filter_by: "from"` option.
* Editing `put_filter_by/2` in `API.RPC.AddressController` to support
'from' as an allowed value of the `filterby` parameter.
* Editing API docs page per changes mentioned above.
pull/649/head
Sebastian Abondano 6 years ago
parent 1f26961e59
commit a07998547f
  1. 2
      apps/block_scout_web/lib/block_scout_web/controllers/api/rpc/address_controller.ex
  2. 2
      apps/block_scout_web/lib/block_scout_web/etherscan.ex
  3. 73
      apps/block_scout_web/test/block_scout_web/controllers/api/rpc/address_controller_test.exs
  4. 4
      apps/explorer/lib/explorer/etherscan.ex
  5. 33
      apps/explorer/test/explorer/etherscan_test.exs

@ -322,7 +322,7 @@ defmodule BlockScoutWeb.API.RPC.AddressController do
defp put_filter_by(options, params) do defp put_filter_by(options, params) do
case params do case params do
%{"filterby" => filter_by} when filter_by in ["to"] -> %{"filterby" => filter_by} when filter_by in ["from", "to"] ->
Map.put(options, :filter_by, filter_by) Map.put(options, :filter_by, filter_by)
_ -> _ ->

@ -661,7 +661,7 @@ defmodule BlockScoutWeb.Etherscan do
description: """ description: """
A string representing the field to filter by. If none is given A string representing the field to filter by. If none is given
it returns transactions that match to, from, or contract address. it returns transactions that match to, from, or contract address.
Available values: to Available values: to, from
""" """
} }
], ],

@ -985,6 +985,63 @@ defmodule BlockScoutWeb.API.RPC.AddressControllerTest do
assert response["message"] == "OK" assert response["message"] == "OK"
end end
test "with filterby=to option", %{conn: conn} do
block = insert(:block)
address = insert(:address)
insert(:transaction, from_address: address)
|> with_block(block)
insert(:transaction, to_address: address)
|> with_block(block)
params = %{
"module" => "account",
"action" => "txlist",
"address" => "#{address.hash}",
"filterby" => "to"
}
assert response =
conn
|> get("/api", params)
|> json_response(200)
assert length(response["result"]) == 1
assert response["status"] == "1"
assert response["message"] == "OK"
end
test "with filterby=from option", %{conn: conn} do
block = insert(:block)
address = insert(:address)
insert(:transaction, from_address: address)
|> with_block(block)
insert(:transaction, from_address: address)
|> with_block(block)
insert(:transaction, to_address: address)
|> with_block(block)
params = %{
"module" => "account",
"action" => "txlist",
"address" => "#{address.hash}",
"filterby" => "from"
}
assert response =
conn
|> get("/api", params)
|> json_response(200)
assert length(response["result"]) == 2
assert response["status"] == "1"
assert response["message"] == "OK"
end
test "supports GET and POST requests", %{conn: conn} do test "supports GET and POST requests", %{conn: conn} do
address = insert(:address) address = insert(:address)
@ -1706,16 +1763,22 @@ defmodule BlockScoutWeb.API.RPC.AddressControllerTest do
assert AddressController.optional_params(params3) == %{} assert AddressController.optional_params(params3) == %{}
end end
test "'filterby' value can only be 'to'" do test "'filterby' value can be 'to' or 'from'" do
params1 = %{"filterby" => "to"} params1 = %{"filterby" => "to"}
optional_params = AddressController.optional_params(params1) optional_params1 = AddressController.optional_params(params1)
assert optional_params.filter_by == "to" assert optional_params1.filter_by == "to"
params2 = %{"filterby" => "from"}
params2 = %{"filterby" => "invalid"} optional_params2 = AddressController.optional_params(params2)
assert AddressController.optional_params(params2) == %{} assert optional_params2.filter_by == "from"
params3 = %{"filterby" => "invalid"}
assert AddressController.optional_params(params3) == %{}
end end
test "only includes optional params when they're given" do test "only includes optional params when they're given" do

@ -226,6 +226,10 @@ defmodule Explorer.Etherscan do
where(query, [t], t.to_address_hash == ^address_hash) where(query, [t], t.to_address_hash == ^address_hash)
end end
defp where_address_match(query, address_hash, %{filter_by: "from"}) do
where(query, [t], t.from_address_hash == ^address_hash)
end
defp where_address_match(query, address_hash, _) do defp where_address_match(query, address_hash, _) do
query query
|> where([t], t.to_address_hash == ^address_hash) |> where([t], t.to_address_hash == ^address_hash)

@ -379,6 +379,39 @@ defmodule Explorer.EtherscanTest do
assert length(found_transactions) == 0 assert length(found_transactions) == 0
end end
test "with filter_by: 'from' option with one matching transaction" do
address = insert(:address)
:transaction
|> insert(to_address: address)
|> with_block()
:transaction
|> insert(from_address: address)
|> with_block()
options = %{filter_by: "from"}
found_transactions = Etherscan.list_transactions(address.hash, options)
assert length(found_transactions) == 1
end
test "with filter_by: 'from' option with non-matching transaction" do
address = insert(:address)
other_address = insert(:address)
:transaction
|> insert(from_address: other_address, to_address: nil)
|> with_block()
options = %{filter_by: "from"}
found_transactions = Etherscan.list_transactions(address.hash, options)
assert length(found_transactions) == 0
end
end end
describe "list_internal_transactions/1" do describe "list_internal_transactions/1" do

Loading…
Cancel
Save