feat: Add /api/v2/proxy/metadata/addresses endpoint (#10585)

* Init

* feat: Add /api/v2/proxy/metadata/addresses endpoint

* Process review comments
pull/10603/head
nikitosing 3 months ago committed by GitHub
parent 6caf8148f3
commit c9ddc19b22
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 30
      apps/block_scout_web/lib/block_scout_web/controllers/api/v2/proxy/metadata_controller.ex
  2. 4
      apps/block_scout_web/lib/block_scout_web/routers/api_router.ex
  3. 13
      apps/block_scout_web/lib/block_scout_web/views/api/v2/proxy/metadata_view.ex
  4. 71
      apps/explorer/lib/explorer/microservice_interfaces/metadata.ex

@ -0,0 +1,30 @@
defmodule BlockScoutWeb.API.V2.Proxy.MetadataController do
@moduledoc """
Controller for the metadata service
"""
use BlockScoutWeb, :controller
alias Explorer.MicroserviceInterfaces.Metadata
action_fallback(BlockScoutWeb.API.V2.FallbackController)
@doc """
Function to handle GET requests to `/api/v2/proxy/metadata/addresses` endpoint.
Proxies request to the metadata service. Preloads additional info to the addresses in response.
If some addresses are not found, they'll be discarded.
"""
def addresses(conn, params) do
with {code, body} <- Metadata.get_addresses(params) do
case code do
200 ->
conn
|> render(:addresses, %{result: body})
status_code ->
conn
|> put_status(status_code)
|> json(body)
end
end
end
end

@ -314,6 +314,10 @@ defmodule BlockScoutWeb.Routers.ApiRouter do
scope "/zerion" do
get("/wallets/:address_hash_param/portfolio", V2.Proxy.ZerionController, :wallet_portfolio)
end
scope "/metadata" do
get("/addresses", V2.Proxy.MetadataController, :addresses)
end
end
scope "/blobs" do

@ -0,0 +1,13 @@
defmodule BlockScoutWeb.API.V2.Proxy.MetadataView do
use BlockScoutWeb, :view
alias BlockScoutWeb.API.V2.AddressView
def render("addresses.json", %{result: {:ok, %{"addresses" => addresses} = body}}) do
Map.put(body, "addresses", Enum.map(addresses, &AddressView.prepare_address/1))
end
def render("addresses.json", %{result: :error}) do
%{error: "Decoding error"}
end
end

@ -3,6 +3,7 @@ defmodule Explorer.MicroserviceInterfaces.Metadata do
Module to interact with Metadata microservice
"""
alias Explorer.Chain
alias Explorer.Chain.{Address.MetadataPreloader, Transaction}
alias Explorer.Utility.Microservice
alias HTTPoison.Response
@ -10,9 +11,10 @@ defmodule Explorer.MicroserviceInterfaces.Metadata do
import Explorer.Chain.Address.MetadataPreloader, only: [maybe_preload_meta: 3]
require Logger
@post_timeout :timer.seconds(5)
@request_timeout :timer.seconds(5)
@tags_per_address_limit 5
@page_size 50
@request_error_msg "Error while sending request to Metadata microservice"
@spec get_addresses_tags([String.t()]) :: {:error, :disabled | <<_::416>> | Jason.DecodeError.t()} | {:ok, any()}
@ -28,12 +30,27 @@ defmodule Explorer.MicroserviceInterfaces.Metadata do
end
end
defp http_get_request(url, params) do
headers = [{"Content-Type", "application/json"}]
@doc """
Get addresses list from Metadata microservice. Then preloads addresses from local DB.
"""
@spec get_addresses(map()) :: {:error | integer(), any()}
def get_addresses(params) do
with :ok <- Microservice.check_enabled(__MODULE__) do
params =
params
|> Map.put("pageSize", @page_size)
|> Map.put("chainId", Application.get_env(:block_scout_web, :chain_id))
http_get_request_for_proxy_method(addresses_url(), params, &prepare_addresses_response/1)
end
end
case HTTPoison.get(url, headers, params: params, recv_timeout: @post_timeout) do
defp http_get_request(url, params, parsing_function \\ &decode_meta/1) do
headers = []
case HTTPoison.get(url, headers, params: params, recv_timeout: @request_timeout) do
{:ok, %Response{body: body, status_code: 200}} ->
body |> Jason.decode() |> decode_meta()
body |> Jason.decode() |> parsing_function.()
{_, error} ->
Logger.error(fn ->
@ -47,10 +64,39 @@ defmodule Explorer.MicroserviceInterfaces.Metadata do
end
end
defp http_get_request_for_proxy_method(url, params, parsing_function) do
case HTTPoison.get(url, [], params: params) do
{:ok, %Response{body: body, status_code: 200}} ->
{200, body |> Jason.decode() |> parsing_function.()}
{_, %Response{body: body, status_code: status_code} = error} ->
old_truncate = Application.get_env(:logger, :truncate)
Logger.configure(truncate: :infinity)
Logger.error(fn ->
[
"Error while sending request to Metadata microservice url: #{url}: ",
inspect(error, limit: :infinity, printable_limit: :infinity)
]
end)
Logger.configure(truncate: old_truncate)
{:ok, response_json} = Jason.decode(body)
{status_code, response_json}
{:error, %HTTPoison.Error{reason: reason}} ->
{500, %{error: reason}}
end
end
defp addresses_metadata_url do
"#{base_url()}/metadata"
end
defp addresses_url do
"#{base_url()}/addresses"
end
defp base_url do
"#{Microservice.base_url(__MODULE__)}/api/v1"
end
@ -89,4 +135,19 @@ defmodule Explorer.MicroserviceInterfaces.Metadata do
defp decode_meta_in_tag(%{"meta" => meta} = tag) do
Map.put(tag, "meta", Jason.decode!(meta))
end
defp prepare_addresses_response({:ok, %{"addresses" => addresses} = response}) do
{:ok,
Map.put(
response,
"addresses",
addresses
|> Chain.hashes_to_addresses(
necessity_by_association: %{names: :optional, smart_contract: :optional, proxy_implementations: :optional}
)
|> Enum.map(fn address -> {address, address.transactions_count} end)
)}
end
defp prepare_addresses_response(_), do: :error
end

Loading…
Cancel
Save