From b216dbef76a8286cbadcbbee0e1718729b3d81f1 Mon Sep 17 00:00:00 2001 From: Sebastien Guillemot Date: Wed, 23 Mar 2022 02:35:09 +0900 Subject: [PATCH] graphql: add user-selected ordering to transactions for address --- CHANGELOG.md | 1 + .../block_scout_web/resolvers/transaction.ex | 6 +- .../lib/block_scout_web/schema/scalars.ex | 5 ++ .../lib/block_scout_web/schema/types.ex | 1 + .../schema/query/address_test.exs | 62 +++++++++++++++++++ apps/explorer/lib/explorer/graphql.ex | 8 +-- apps/explorer/test/explorer/graphql_test.exs | 10 +-- 7 files changed, 82 insertions(+), 11 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6a066d9f1a..f7dab6e7f5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ - [#5732](https://github.com/blockscout/blockscout/pull/5732) - Manage testnet label (right to the navbar logo) - [#5699](https://github.com/blockscout/blockscout/pull/5699) - Switch to basic (non-pro) API endpoint for Coingecko requests, if API key is not provided - [#5542](https://github.com/blockscout/blockscout/pull/5542) - Add `jq` in docker image +- [#5345](https://github.com/blockscout/blockscout/pull/5345) - Graphql: add user-selected ordering to transactions for address query ### Fixes - [#5768](https://github.com/blockscout/blockscout/pull/5768) - Outstanding rows limit for missing blocks query (catchup fetcher) diff --git a/apps/block_scout_web/lib/block_scout_web/resolvers/transaction.ex b/apps/block_scout_web/lib/block_scout_web/resolvers/transaction.ex index fe96463794..adfe1b793d 100644 --- a/apps/block_scout_web/lib/block_scout_web/resolvers/transaction.ex +++ b/apps/block_scout_web/lib/block_scout_web/resolvers/transaction.ex @@ -13,9 +13,11 @@ defmodule BlockScoutWeb.Resolvers.Transaction do end def get_by(%Address{hash: address_hash}, args, _) do + connection_args = Map.take(args, [:after, :before, :first, :last]) + address_hash - |> GraphQL.address_to_transactions_query() - |> Connection.from_query(&Repo.all/1, args, options(args)) + |> GraphQL.address_to_transactions_query(args.order) + |> Connection.from_query(&Repo.all/1, connection_args, options(args)) end defp options(%{before: _}), do: [] diff --git a/apps/block_scout_web/lib/block_scout_web/schema/scalars.ex b/apps/block_scout_web/lib/block_scout_web/schema/scalars.ex index b7a8939a39..51ebae097d 100644 --- a/apps/block_scout_web/lib/block_scout_web/schema/scalars.ex +++ b/apps/block_scout_web/lib/block_scout_web/schema/scalars.ex @@ -113,4 +113,9 @@ defmodule BlockScoutWeb.Schema.Scalars do value(:reward) value(:selfdestruct) end + + enum :sort_order do + value(:asc) + value(:desc) + end end diff --git a/apps/block_scout_web/lib/block_scout_web/schema/types.ex b/apps/block_scout_web/lib/block_scout_web/schema/types.ex index f1ecbbbe20..bc77abf1fc 100644 --- a/apps/block_scout_web/lib/block_scout_web/schema/types.ex +++ b/apps/block_scout_web/lib/block_scout_web/schema/types.ex @@ -33,6 +33,7 @@ defmodule BlockScoutWeb.Schema.Types do connection field(:transactions, node_type: :transaction) do arg(:count, :integer) + arg(:order, type: :sort_order, default_value: :desc) resolve(&Transaction.get_by/3) complexity(fn diff --git a/apps/block_scout_web/test/block_scout_web/schema/query/address_test.exs b/apps/block_scout_web/test/block_scout_web/schema/query/address_test.exs index 64adae6fb9..134d1b7f34 100644 --- a/apps/block_scout_web/test/block_scout_web/schema/query/address_test.exs +++ b/apps/block_scout_web/test/block_scout_web/schema/query/address_test.exs @@ -326,6 +326,68 @@ defmodule BlockScoutWeb.Schema.Query.AddressTest do assert Enum.all?(transactions, &(&1["node"]["block_number"] == third_block.number)) end + test "transactions are ordered by ascending block and index", %{conn: conn} do + first_block = insert(:block) + second_block = insert(:block) + third_block = insert(:block) + + address = insert(:address) + + 3 + |> insert_list(:transaction, from_address: address) + |> with_block(second_block) + + 3 + |> insert_list(:transaction, from_address: address) + |> with_block(third_block) + + 3 + |> insert_list(:transaction, from_address: address) + |> with_block(first_block) + + query = """ + query ($hash: AddressHash!, $first: Int!) { + address(hash: $hash) { + transactions(first: $first, order: ASC) { + edges { + node { + hash + block_number + index + } + } + } + } + } + """ + + variables = %{ + "hash" => to_string(address.hash), + "first" => 3 + } + + conn = post(conn, "/graphql", query: query, variables: variables) + + %{ + "data" => %{ + "address" => %{ + "transactions" => %{ + "edges" => transactions + } + } + } + } = json_response(conn, 200) + + block_number_and_index_order = + Enum.map(transactions, fn transaction -> + {transaction["node"]["block_number"], transaction["node"]["index"]} + end) + + assert block_number_and_index_order == Enum.sort(block_number_and_index_order, &(&1 < &2)) + assert length(transactions) == 3 + assert Enum.all?(transactions, &(&1["node"]["block_number"] == first_block.number)) + end + test "complexity correlates to 'first' or 'last' arguments", %{conn: conn} do address = build(:address) diff --git a/apps/explorer/lib/explorer/graphql.ex b/apps/explorer/lib/explorer/graphql.ex index e892ef584a..aa341e095f 100644 --- a/apps/explorer/lib/explorer/graphql.ex +++ b/apps/explorer/lib/explorer/graphql.ex @@ -24,15 +24,15 @@ defmodule Explorer.GraphQL do Returns a query to fetch transactions with a matching `to_address_hash`, `from_address_hash`, or `created_contract_address_hash` field for a given address hash. - Orders transactions by descending block number and index. + Orders transactions by `block_number` and `index` according to to `order` """ - @spec address_to_transactions_query(Hash.Address.t()) :: Ecto.Query.t() - def address_to_transactions_query(address_hash) do + @spec address_to_transactions_query(Hash.Address.t(), :desc | :asc) :: Ecto.Query.t() + def address_to_transactions_query(address_hash, order) do Transaction - |> order_by([transaction], desc: transaction.block_number, desc: transaction.index) |> where([transaction], transaction.to_address_hash == ^address_hash) |> or_where([transaction], transaction.from_address_hash == ^address_hash) |> or_where([transaction], transaction.created_contract_address_hash == ^address_hash) + |> order_by([transaction], [{^order, transaction.block_number}, {^order, transaction.index}]) end @doc """ diff --git a/apps/explorer/test/explorer/graphql_test.exs b/apps/explorer/test/explorer/graphql_test.exs index 2d8ffd1a8f..72408bbb07 100644 --- a/apps/explorer/test/explorer/graphql_test.exs +++ b/apps/explorer/test/explorer/graphql_test.exs @@ -12,7 +12,7 @@ defmodule Explorer.GraphQLTest do :address |> insert() |> Map.get(:hash) - |> GraphQL.address_to_transactions_query() + |> GraphQL.address_to_transactions_query(:desc) |> Repo.replica().all() assert result == [] @@ -25,7 +25,7 @@ defmodule Explorer.GraphQLTest do [found_transaction] = address_hash - |> GraphQL.address_to_transactions_query() + |> GraphQL.address_to_transactions_query(:desc) |> Repo.replica().all() assert found_transaction.hash == transaction.hash @@ -38,7 +38,7 @@ defmodule Explorer.GraphQLTest do [found_transaction] = address_hash - |> GraphQL.address_to_transactions_query() + |> GraphQL.address_to_transactions_query(:desc) |> Repo.replica().all() assert found_transaction.hash == transaction.hash @@ -51,7 +51,7 @@ defmodule Explorer.GraphQLTest do [found_transaction] = address_hash - |> GraphQL.address_to_transactions_query() + |> GraphQL.address_to_transactions_query(:desc) |> Repo.replica().all() assert found_transaction.hash == transaction.hash @@ -78,7 +78,7 @@ defmodule Explorer.GraphQLTest do found_transactions = address_hash - |> GraphQL.address_to_transactions_query() + |> GraphQL.address_to_transactions_query(:desc) |> Repo.replica().all() block_number_and_index_order =