-
Notifications
You must be signed in to change notification settings - Fork 12
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #222 from anoma/feat/return-block-by-timestamp
feat: return blocks by timestamp or height
- Loading branch information
Showing
15 changed files
with
286 additions
and
4 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
use axum::http::StatusCode; | ||
use axum::response::{IntoResponse, Response}; | ||
use thiserror::Error; | ||
|
||
use crate::response::api::ApiErrorResponse; | ||
|
||
#[derive(Error, Debug)] | ||
pub enum BlockError { | ||
#[error("Block not found error at {0}: {1}")] | ||
NotFound(String, String), | ||
#[error("Database error: {0}")] | ||
Database(String), | ||
#[error("Unknown error: {0}")] | ||
Unknown(String), | ||
} | ||
|
||
impl IntoResponse for BlockError { | ||
fn into_response(self) -> Response { | ||
let status_code = match self { | ||
BlockError::Unknown(_) | BlockError::Database(_) => { | ||
StatusCode::INTERNAL_SERVER_ERROR | ||
} | ||
BlockError::NotFound(_, _) => StatusCode::NOT_FOUND, | ||
}; | ||
|
||
ApiErrorResponse::send(status_code.as_u16(), Some(self.to_string())) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,6 @@ | ||
pub mod api; | ||
pub mod balance; | ||
pub mod block; | ||
pub mod chain; | ||
pub mod crawler_state; | ||
pub mod gas; | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
use axum::extract::{Path, State}; | ||
use axum::http::HeaderMap; | ||
use axum::Json; | ||
use axum_macros::debug_handler; | ||
|
||
use crate::error::api::ApiError; | ||
use crate::response::block::Block; | ||
use crate::state::common::CommonState; | ||
|
||
#[debug_handler] | ||
pub async fn get_block_by_height( | ||
_headers: HeaderMap, | ||
Path(value): Path<i32>, | ||
State(state): State<CommonState>, | ||
) -> Result<Json<Block>, ApiError> { | ||
let block = state.block_service.get_block_by_height(value).await?; | ||
|
||
Ok(Json(block)) | ||
} | ||
|
||
#[debug_handler] | ||
pub async fn get_block_by_timestamp( | ||
_headers: HeaderMap, | ||
Path(value): Path<i64>, | ||
State(state): State<CommonState>, | ||
) -> Result<Json<Block>, ApiError> { | ||
let block = state.block_service.get_block_by_timestamp(value).await?; | ||
|
||
Ok(Json(block)) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,5 @@ | ||
pub mod balance; | ||
pub mod block; | ||
pub mod chain; | ||
pub mod crawler_state; | ||
pub mod gas; | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,72 @@ | ||
use axum::async_trait; | ||
use diesel::{ExpressionMethods, QueryDsl, RunQueryDsl, SelectableHelper}; | ||
use orm::blocks::BlockDb; | ||
use orm::schema::blocks; | ||
|
||
use crate::appstate::AppState; | ||
|
||
#[derive(Clone)] | ||
pub struct BlockRepository { | ||
pub(crate) app_state: AppState, | ||
} | ||
|
||
#[async_trait] | ||
pub trait BlockRepositoryTrait { | ||
fn new(app_state: AppState) -> Self; | ||
|
||
async fn find_block_by_height( | ||
&self, | ||
height: i32, | ||
) -> Result<Option<BlockDb>, String>; | ||
|
||
async fn find_block_by_timestamp( | ||
&self, | ||
timestamp: i64, | ||
) -> Result<Option<BlockDb>, String>; | ||
} | ||
|
||
#[async_trait] | ||
impl BlockRepositoryTrait for BlockRepository { | ||
fn new(app_state: AppState) -> Self { | ||
Self { app_state } | ||
} | ||
|
||
async fn find_block_by_height( | ||
&self, | ||
height: i32, | ||
) -> Result<Option<BlockDb>, String> { | ||
let conn = self.app_state.get_db_connection().await; | ||
|
||
conn.interact(move |conn| { | ||
blocks::table | ||
.filter(blocks::dsl::height.eq(height)) | ||
.select(BlockDb::as_select()) | ||
.first(conn) | ||
.ok() | ||
}) | ||
.await | ||
.map_err(|e| e.to_string()) | ||
} | ||
|
||
/// Gets the last block preceeding the given timestamp | ||
async fn find_block_by_timestamp( | ||
&self, | ||
timestamp: i64, | ||
) -> Result<Option<BlockDb>, String> { | ||
let conn = self.app_state.get_db_connection().await; | ||
let timestamp = chrono::DateTime::from_timestamp(timestamp, 0) | ||
.expect("Invalid timestamp") | ||
.naive_utc(); | ||
|
||
conn.interact(move |conn| { | ||
blocks::table | ||
.filter(blocks::timestamp.le(timestamp)) | ||
.order(blocks::timestamp.desc()) | ||
.select(BlockDb::as_select()) | ||
.first(conn) | ||
.ok() | ||
}) | ||
.await | ||
.map_err(|e| e.to_string()) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,5 @@ | ||
pub mod balance; | ||
pub mod block; | ||
pub mod chain; | ||
pub mod gas; | ||
pub mod governance; | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
use orm::blocks::BlockDb; | ||
use serde::{Deserialize, Serialize}; | ||
|
||
#[derive(Clone, Debug, Deserialize, Serialize)] | ||
#[serde(rename_all = "camelCase")] | ||
pub struct Block { | ||
pub height: i32, | ||
pub hash: Option<String>, | ||
pub app_hash: Option<String>, | ||
pub timestamp: Option<String>, | ||
pub proposer: Option<String>, | ||
pub epoch: Option<String>, | ||
} | ||
|
||
impl From<BlockDb> for Block { | ||
fn from(block_db: BlockDb) -> Self { | ||
Self { | ||
height: block_db.height, | ||
hash: block_db.hash, | ||
app_hash: block_db.app_hash, | ||
timestamp: block_db | ||
.timestamp | ||
.map(|t| t.and_utc().timestamp().to_string()), | ||
proposer: block_db.proposer, | ||
epoch: block_db.epoch.map(|e| e.to_string()), | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,6 @@ | ||
pub mod api; | ||
pub mod balance; | ||
pub mod block; | ||
pub mod chain; | ||
pub mod crawler_state; | ||
pub mod gas; | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
use crate::appstate::AppState; | ||
use crate::error::block::BlockError; | ||
use crate::repository::block::{BlockRepository, BlockRepositoryTrait}; | ||
use crate::response::block::Block; | ||
|
||
#[derive(Clone)] | ||
pub struct BlockService { | ||
block_repo: BlockRepository, | ||
} | ||
|
||
impl BlockService { | ||
pub fn new(app_state: AppState) -> Self { | ||
Self { | ||
block_repo: BlockRepository::new(app_state), | ||
} | ||
} | ||
|
||
pub async fn get_block_by_height( | ||
&self, | ||
height: i32, | ||
) -> Result<Block, BlockError> { | ||
let block = self | ||
.block_repo | ||
.find_block_by_height(height) | ||
.await | ||
.map_err(BlockError::Database)?; | ||
let block = block.ok_or(BlockError::NotFound( | ||
"height".to_string(), | ||
height.to_string(), | ||
))?; | ||
|
||
Ok(Block::from(block)) | ||
} | ||
|
||
pub async fn get_block_by_timestamp( | ||
&self, | ||
timestamp: i64, | ||
) -> Result<Block, BlockError> { | ||
let block = self | ||
.block_repo | ||
.find_block_by_timestamp(timestamp) | ||
.await | ||
.map_err(BlockError::Database)?; | ||
|
||
let block = block.ok_or(BlockError::NotFound( | ||
"timestamp".to_string(), | ||
timestamp.to_string(), | ||
))?; | ||
|
||
Ok(Block::from(block)) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,5 @@ | ||
pub mod balance; | ||
pub mod block; | ||
pub mod chain; | ||
pub mod crawler_state; | ||
pub mod gas; | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters