Skip to content

Commit

Permalink
feat: add paginated search for tokens
Browse files Browse the repository at this point in the history
  • Loading branch information
lok52 committed Dec 25, 2024
1 parent 002f740 commit 3f8ef40
Show file tree
Hide file tree
Showing 11 changed files with 141 additions and 15 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,13 @@ pub struct TokenInfo {
#[derive(Debug, Deserialize)]
pub struct TokenInfoSearchResponse {
pub token_infos: Vec<TokenInfo>,
pub next_page_params: Option<String>,
pub next_page_params: Option<Pagination>,
}

#[derive(Debug, Deserialize)]
pub struct Pagination {
pub page_token: String,
pub page_size: u32,
}

impl TokenInfoClient {
Expand Down
3 changes: 2 additions & 1 deletion multichain-aggregator/multichain-aggregator-logic/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,6 @@ mod types;

pub use import::batch_import;
pub use types::{
api_keys::ApiKey, batch_import_request::BatchImportRequest, chains::Chain, ChainId,
api_keys::ApiKey, batch_import_request::BatchImportRequest, chains::Chain, token_info::Token,
ChainId,
};
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ use alloy_primitives::Address as AddressAlloy;
use entity::addresses::{ActiveModel, Column, Entity, Model};
use regex::Regex;
use sea_orm::{
prelude::Expr, sea_query::OnConflict, ActiveValue::NotSet, ConnectionTrait, DbErr, EntityTrait,
IntoSimpleExpr, Iterable, QueryFilter, QueryOrder, QuerySelect,
prelude::Expr, sea_query::OnConflict, ActiveValue::NotSet, ColumnTrait, ConnectionTrait, DbErr,
EntityTrait, IntoSimpleExpr, Iterable, QueryFilter, QueryOrder, QuerySelect,
};
use std::sync::OnceLock;

Expand Down Expand Up @@ -54,14 +54,15 @@ pub async fn search_by_query<C>(db: &C, q: &str) -> Result<Vec<Address>, Service
where
C: ConnectionTrait,
{
search_by_query_paginated(db, q, None, 100)
search_by_query_paginated(db, q, None, None, 100)
.await
.map(|(addresses, _)| addresses)
}

pub async fn search_by_query_paginated<C>(
db: &C,
q: &str,
chain_id: Option<ChainId>,
page_token: Option<(AddressAlloy, ChainId)>,
limit: u64,
) -> Result<(Vec<Address>, Option<(AddressAlloy, ChainId)>), ServiceError>
Expand All @@ -84,6 +85,10 @@ where
.order_by_asc(Column::ChainId)
.limit(limit + 1);

if let Some(chain_id) = chain_id {
query = query.filter(Column::ChainId.eq(chain_id));
}

if hex_regex().is_match(q) {
query = query.filter(Expr::cust_with_expr(
"encode(hash, 'hex') LIKE $1",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ impl From<Address> for proto::Address {
is_contract: Some(v.is_contract),
is_verified_contract: Some(v.is_verified_contract),
is_token: Some(v.is_token),
chain_id: v.chain_id.to_string(),
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ impl From<MarketplaceDapp> for proto::MarketplaceDapp {
title: v.title,
logo: v.logo,
short_description: v.short_description,
chain_id: v.chain_id.to_string(),
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ impl From<Hash> for proto::Hash {
Self {
hash: v.hash.to_string(),
hash_type: db_hash_type_to_proto_hash_type(v.hash_type).into(),
chain_id: v.chain_id.to_string(),
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ impl From<Token> for proto::Token {
name: v.name,
symbol: v.symbol,
icon_url: v.icon_url,
chain_id: v.chain_id.to_string(),
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@ http:
- selector: blockscout.multichainAggregator.v1.MultichainAggregatorService.ListAddresses
get: /api/v1/addresses

- selector: blockscout.multichainAggregator.v1.MultichainAggregatorService.ListTokens
get: /api/v1/tokens

#################### Health ####################

- selector: blockscout.multichainAggregator.v1.Health.Check
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ service MultichainAggregatorService {
rpc BatchImport(BatchImportRequest) returns (BatchImportResponse) {}
rpc QuickSearch(QuickSearchRequest) returns (QuickSearchResponse) {}
rpc ListAddresses(ListAddressesRequest) returns (ListAddressesResponse) {}
rpc ListTokens(ListTokensRequest) returns (ListTokensResponse) {}
}

message Pagination {
Expand All @@ -33,11 +34,13 @@ message Address {
optional bool is_contract = 6;
optional bool is_verified_contract = 7;
optional bool is_token = 8;
string chain_id = 9;
}

message BlockRange {
uint64 min_block_number = 1;
uint64 max_block_number = 2;
string chain_id = 3;
}

message Hash {
Expand All @@ -49,20 +52,23 @@ message Hash {
}

HashType hash_type = 2;
string chain_id = 3;
}

message MarketplaceDapp {
string id = 1;
string title = 2;
string logo = 3;
string short_description = 4;
string chain_id = 5;
}

message Token {
string address = 1;
string icon_url = 2;
string name = 3;
string symbol = 4;
string chain_id = 5;
}

message BatchImportRequest {
Expand Down Expand Up @@ -98,11 +104,24 @@ message QuickSearchResponse {

message ListAddressesRequest {
string q = 1;
optional uint32 page_size = 2;
optional string page_token = 3;
optional string chain_id = 2;
optional uint32 page_size = 3;
optional string page_token = 4;
}

message ListAddressesResponse {
repeated Address addresses = 1;
Pagination pagination = 2;
}

message ListTokensRequest {
string q = 1;
optional string chain_id = 2;
optional uint32 page_size = 3;
optional string page_token = 4;
}

message ListTokensResponse {
repeated Token tokens = 1;
Pagination pagination = 2;
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,10 @@ paths:
in: query
required: false
type: string
- name: chain_id
in: query
required: false
type: string
- name: page_size
in: query
required: false
Expand Down Expand Up @@ -77,6 +81,38 @@ paths:
type: string
tags:
- MultichainAggregatorService
/api/v1/tokens:
get:
operationId: MultichainAggregatorService_ListTokens
responses:
"200":
description: A successful response.
schema:
$ref: '#/definitions/v1ListTokensResponse'
default:
description: An unexpected error response.
schema:
$ref: '#/definitions/rpcStatus'
parameters:
- name: q
in: query
required: false
type: string
- name: chain_id
in: query
required: false
type: string
- name: page_size
in: query
required: false
type: integer
format: int64
- name: page_token
in: query
required: false
type: string
tags:
- MultichainAggregatorService
/health:
get:
summary: |-
Expand Down Expand Up @@ -206,6 +242,8 @@ definitions:
type: boolean
is_token:
type: boolean
chain_id:
type: string
v1BatchImportRequest:
type: object
properties:
Expand Down Expand Up @@ -242,13 +280,17 @@ definitions:
max_block_number:
type: string
format: uint64
chain_id:
type: string
v1Hash:
type: object
properties:
hash:
type: string
hash_type:
$ref: '#/definitions/HashHashType'
chain_id:
type: string
v1HealthCheckResponse:
type: object
properties:
Expand All @@ -264,6 +306,16 @@ definitions:
$ref: '#/definitions/v1Address'
pagination:
$ref: '#/definitions/v1Pagination'
v1ListTokensResponse:
type: object
properties:
tokens:
type: array
items:
type: object
$ref: '#/definitions/v1Token'
pagination:
$ref: '#/definitions/v1Pagination'
v1MarketplaceDapp:
type: object
properties:
Expand All @@ -275,6 +327,8 @@ definitions:
type: string
short_description:
type: string
chain_id:
type: string
v1Pagination:
type: object
properties:
Expand All @@ -301,3 +355,5 @@ definitions:
type: string
symbol:
type: string
chain_id:
type: string
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,10 @@ use multichain_aggregator_logic::{
api_key_manager::ApiKeyManager,
clients::{dapp::DappClient, token_info::TokenInfoClient},
error::ServiceError,
Chain,
Chain, Token,
};
use multichain_aggregator_proto::blockscout::multichain_aggregator::v1::{
ListTokensRequest, ListTokensResponse,
};
use sea_orm::DatabaseConnection;
use std::str::FromStr;
Expand Down Expand Up @@ -71,9 +74,8 @@ impl MultichainAggregatorService for MultichainAggregator {

logic::batch_import(&self.db, import_request)
.await
.map_err(|err| {
.inspect_err(|err| {
tracing::error!(error = ?err, "failed to batch import");
Status::internal("failed to batch import")
})?;

Ok(Response::new(BatchImportResponse {
Expand All @@ -90,17 +92,17 @@ impl MultichainAggregatorService for MultichainAggregator {
let page_token: Option<(alloy_primitives::Address, logic::ChainId)> =
inner.page_token.map(parse_query_2).transpose()?;
let page_size = self.normalize_page_size(inner.page_size);

let chain_id = inner.chain_id.map(parse_query).transpose()?;
let (addresses, next_page_token) = logic::repository::addresses::search_by_query_paginated(
&self.db,
&inner.q,
chain_id,
page_token,
page_size as u64,
)
.await
.map_err(|err| {
.inspect_err(|err| {
tracing::error!(error = ?err, "failed to list addresses");
Status::internal("failed to list addresses")
})?;

Ok(Response::new(ListAddressesResponse {
Expand All @@ -112,6 +114,37 @@ impl MultichainAggregatorService for MultichainAggregator {
}))
}

async fn list_tokens(
&self,
request: Request<ListTokensRequest>,
) -> Result<Response<ListTokensResponse>, Status> {
let inner = request.into_inner();

let chain_id = inner.chain_id.map(parse_query).transpose()?;
let res = self
.token_info_client
.search_tokens(&inner.q, chain_id, inner.page_size, inner.page_token)
.await
.inspect_err(|err| {
tracing::error!(error = ?err, "failed to list tokens");
})?;

let tokens = res
.token_infos
.into_iter()
.map(|t| Token::try_from(t).map(|t| t.into()))
.collect::<Result<Vec<_>, _>>()
.map_err(ServiceError::from)?;

Ok(Response::new(ListTokensResponse {
tokens,
pagination: res.next_page_params.map(|p| Pagination {
page_token: p.page_token,
page_size: p.page_size,
}),
}))
}

async fn quick_search(
&self,
request: Request<QuickSearchRequest>,
Expand All @@ -126,9 +159,8 @@ impl MultichainAggregatorService for MultichainAggregator {
&self.chains,
)
.await
.map_err(|err| {
.inspect_err(|err| {
tracing::error!(error = ?err, "failed to quick search");
Status::internal("failed to quick search")
})?;

Ok(Response::new(results.into()))
Expand Down

0 comments on commit 3f8ef40

Please sign in to comment.