Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add search api of graphql #1234

Merged
merged 11 commits into from
Dec 28, 2022
10 changes: 10 additions & 0 deletions docs/schema.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -424,6 +424,16 @@ type Address {
""".bit alias."""
bit_alias: String
}
type Address {
"""ETH address hash"""
eth_address: HashFull

"""The address cached token transfer count."""
token_transfer_count: Int

""".bit alias."""
bit_alias: String
}

input BlockInput {
hash: HashFull
Expand Down
3 changes: 2 additions & 1 deletion lib/godwoken_explorer/chain.ex
Original file line number Diff line number Diff line change
Expand Up @@ -324,7 +324,8 @@ defmodule GodwokenExplorer.Chain do
end

@spec from_param(String.t()) ::
{:ok, Address.t() | Block.t() | Transaction.t()} | {:error, :not_found}
{:ok, Address.t() | Block.t() | Transaction.t() | Account.t() | UDT.t()}
| {:error, :not_found}
def from_param(param)

def from_param("0x" <> number_string = param)
Expand Down
4 changes: 2 additions & 2 deletions lib/godwoken_explorer/graphql/resolovers/account.ex
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,8 @@ defmodule GodwokenExplorer.Graphql.Resolvers.Account do
{:ok, account}

nil ->
address = Address.find_or_insert_from_hash(address)
{:ok, address}
Address.find_or_insert_from_hash(address)
# {:ok, address}
end
end
end
Expand Down
48 changes: 47 additions & 1 deletion lib/godwoken_explorer/graphql/resolovers/search.ex
Original file line number Diff line number Diff line change
@@ -1,9 +1,55 @@
defmodule GodwokenExplorer.Graphql.Resolvers.Search do
alias GodwokenExplorer.UDT
alias GodwokenExplorer.{UDT, Transaction, Block, Address, Account}
alias GodwokenExplorer.Chain
import Ecto.Query

import GodwokenExplorer.Graphql.Resolvers.Common, only: [paginate_query: 3]

def search_keyword(_parent, %{input: %{keyword: keyword}} = _args, _resolution) do
do_search_keyword(keyword)
end

def do_search_keyword(keyword) do
keyword
|> String.trim()
|> Chain.from_param()
|> case do
{:ok, item} ->
return = process_result(item)
{:ok, return}

{:error, :not_found} ->
{:error, :not_found}
end
end

defp process_result(%Account{} = item) do
id =
case item.type do
:eth_user -> item.eth_address
:polyjuice_contract -> item.eth_address
_ -> item.script_hash
end

%{type: :account, id: id}
end

defp process_result(%Address{} = item) do
%{type: :address, id: item.eth_address}
end

defp process_result(%Block{} = item) do
%{type: :block, id: item.number}
end

defp process_result(%Transaction{} = item) do
%{type: :transaction, id: item.eth_hash || item.hash}
end

defp process_result(%UDT{} = item) do
%{type: :udt, id: item.id}
end

def search_udt(_parent, %{input: input} = _args, _resolution) do
return =
from(u in UDT)
Expand Down
35 changes: 34 additions & 1 deletion lib/godwoken_explorer/graphql/types/search.ex
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,22 @@ defmodule GodwokenExplorer.Graphql.Types.Search do
alias GodwokenExplorer.Graphql.Resolvers, as: Resolvers

object :search_querys do
@desc """
keyword can be: udt name| account eth_address | address | transaction hash | block number
```graphql
query {
search_keyword(input: { keyword: "UDT"}){
type
id
}
}
```
"""
field :search_keyword, :search_result do
arg(:input, non_null(:search_keyword_input))
resolve(&Resolvers.Search.search_keyword/3)
end

@desc """
```graphql
search_udt example:
Expand Down Expand Up @@ -52,6 +68,11 @@ defmodule GodwokenExplorer.Graphql.Types.Search do
end
end

object :search_result do
field(:type, :search_type)
field(:id, :string)
end

object :paginate_search_udts do
field(:entries, list_of(:search_udt_result))
field(:metadata, :paginate_metadata)
Expand Down Expand Up @@ -80,7 +101,19 @@ defmodule GodwokenExplorer.Graphql.Types.Search do

field(:type, :udt_type, description: " Bridge means from layer1;Native means layer2 contract.")

field :eth_type, :eth_type, description: "EVM token type."
field(:eth_type, :eth_type, description: "EVM token type.")
end

enum :search_type do
value(:address)
value(:block)
value(:transaction)
value(:udt)
value(:account)
end

input_object :search_keyword_input do
field(:keyword, non_null(:string))
end

input_object :search_udt_input do
Expand Down
156 changes: 155 additions & 1 deletion test/graphql/search_test.exs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
defmodule GodwokenExplorer.Graphql.SearchTest do
use GodwokenExplorerWeb.ConnCase

import GodwokenExplorer.Factory, only: [insert: 2]
import GodwokenExplorer.Factory, only: [insert: 1, insert: 2, insert!: 1]

setup do
for _ <- 1..10 do
Expand All @@ -15,6 +15,160 @@ defmodule GodwokenExplorer.Graphql.SearchTest do
[second_last_udt: second_last_udt, last_udt: last_udt]
end

test "graphql: search_keyword with udt", %{conn: conn} do
# exact matching
name = "SSS_UDT"
fuzzy_name = "%UDT%"
insert(:native_udt, eth_type: :erc721, name: name)
insert(:native_udt, eth_type: :erc1155)

query = search_keyword(name)

conn =
post(conn, "/graphql", %{
"query" => query,
"variables" => %{}
})

assert match?(
%{
"data" => %{
"search_keyword" => %{
"type" => "UDT"
}
}
},
json_response(conn, 200)
)

# more than one matching
query = search_keyword(fuzzy_name)

conn =
post(conn, "/graphql", %{
"query" => query,
"variables" => %{}
})

assert match?(
%{
"data" => %{
"search_keyword" => nil
},
"errors" => _
},
json_response(conn, 200)
)

## polyjuice contract account
account = insert!(:polyjuice_contract_account)
keyword = to_string(account.eth_address)
query = search_keyword(keyword)

conn =
post(conn, "/graphql", %{
"query" => query,
"variables" => %{}
})

assert match?(
%{
"data" => %{
"search_keyword" => %{
"type" => "ACCOUNT"
}
}
},
json_response(conn, 200)
)

## not exist address
keyword = "0xbFbE23681D99A158f632e64A31288946770c7A9e"
query = search_keyword(keyword)

conn =
post(conn, "/graphql", %{
"query" => query,
"variables" => %{}
})

assert match?(
%{
"data" => %{
"search_keyword" => %{
"type" => "ADDRESS"
}
}
},
json_response(conn, 200)
)

block = insert(:block)
from_account = insert!(:user)
to_account = insert!(:polyjuice_contract_account)

transaction =
insert(:transaction,
from_account: from_account,
to_account: to_account,
block: block,
block_number: block.number
)

## test block keyword
keyword = block.number |> to_string()
query = search_keyword(keyword)

conn =
post(conn, "/graphql", %{
"query" => query,
"variables" => %{}
})

assert match?(
%{
"data" => %{
"search_keyword" => %{
"type" => "BLOCK"
}
}
},
json_response(conn, 200)
)

## test transaction keyword
keyword = transaction.hash |> to_string()
query = search_keyword(keyword)

conn =
post(conn, "/graphql", %{
"query" => query,
"variables" => %{}
})

assert match?(
%{
"data" => %{
"search_keyword" => %{
"type" => "TRANSACTION"
}
}
},
json_response(conn, 200)
)
end

defp search_keyword(keyword) do
"""
query {
search_keyword(input: { keyword: "#{keyword}"}){
type
id
}
}
"""
end

test "graphql: search_udt ", %{conn: conn} do
fuzzy_name = "%UD%"

Expand Down