diff --git a/crates/brk_bindgen/src/generators/python/api.rs b/crates/brk_bindgen/src/generators/python/api.rs index b4e45c4ce..2e30fea73 100644 --- a/crates/brk_bindgen/src/generators/python/api.rs +++ b/crates/brk_bindgen/src/generators/python/api.rs @@ -101,7 +101,7 @@ pub fn generate_api_methods(output: &mut String, endpoints: &[Endpoint]) { .response_type .as_deref() .map(js_type_to_python) - .unwrap_or_else(|| "Any".to_string()), + .unwrap_or_else(|| "str".to_string()), ); let return_type = if endpoint.supports_csv { @@ -159,11 +159,19 @@ pub fn generate_api_methods(output: &mut String, endpoints: &[Endpoint]) { // Build path let path = build_path_template(&endpoint.path, &endpoint.path_params); + let fetch_method = if endpoint.returns_json() { + "get_json" + } else { + "get_text" + }; + if endpoint.query_params.is_empty() { if endpoint.path_params.is_empty() { - writeln!(output, " return self.get_json('{}')", path).unwrap(); + writeln!(output, " return self.{}('{}')", fetch_method, path) + .unwrap(); } else { - writeln!(output, " return self.get_json(f'{}')", path).unwrap(); + writeln!(output, " return self.{}(f'{}')", fetch_method, path) + .unwrap(); } } else { writeln!(output, " params = []").unwrap(); @@ -197,9 +205,9 @@ pub fn generate_api_methods(output: &mut String, endpoints: &[Endpoint]) { if endpoint.supports_csv { writeln!(output, " if format == 'csv':").unwrap(); writeln!(output, " return self.get_text(path)").unwrap(); - writeln!(output, " return self.get_json(path)").unwrap(); + writeln!(output, " return self.{}(path)", fetch_method).unwrap(); } else { - writeln!(output, " return self.get_json(path)").unwrap(); + writeln!(output, " return self.{}(path)", fetch_method).unwrap(); } } diff --git a/crates/brk_bindgen/src/generators/rust/api.rs b/crates/brk_bindgen/src/generators/rust/api.rs index fe9469166..d4f71267c 100644 --- a/crates/brk_bindgen/src/generators/rust/api.rs +++ b/crates/brk_bindgen/src/generators/rust/api.rs @@ -93,7 +93,7 @@ pub fn generate_api_methods(output: &mut String, endpoints: &[Endpoint]) { .response_type .as_deref() .map(js_type_to_rust) - .unwrap_or_else(|| "serde_json::Value".to_string()); + .unwrap_or_else(|| "String".to_string()); let return_type = if endpoint.supports_csv { format!("FormatResponse<{}>", base_return_type) @@ -132,12 +132,17 @@ pub fn generate_api_methods(output: &mut String, endpoints: &[Endpoint]) { .unwrap(); let (path, index_arg) = build_path_template(endpoint); + let fetch_method = if endpoint.returns_json() { + "get_json" + } else { + "get_text" + }; if endpoint.query_params.is_empty() { writeln!( output, - " self.base.get_json(&format!(\"{}\"{}))", - path, index_arg + " self.base.{}(&format!(\"{}\"{}))", + fetch_method, path, index_arg ) .unwrap(); } else { @@ -186,12 +191,14 @@ pub fn generate_api_methods(output: &mut String, endpoints: &[Endpoint]) { writeln!(output, " }} else {{").unwrap(); writeln!( output, - " self.base.get_json(&path).map(FormatResponse::Json)" + " self.base.{}(&path).map(FormatResponse::Json)", + fetch_method ) .unwrap(); writeln!(output, " }}").unwrap(); } else { - writeln!(output, " self.base.get_json(&path)").unwrap(); + writeln!(output, " self.base.{}(&path)", fetch_method) + .unwrap(); } } diff --git a/crates/brk_bindgen/src/openapi.rs b/crates/brk_bindgen/src/openapi.rs index f3bcb7b22..5833fa6ef 100644 --- a/crates/brk_bindgen/src/openapi.rs +++ b/crates/brk_bindgen/src/openapi.rs @@ -43,6 +43,11 @@ impl Endpoint { self.method == "GET" && !self.deprecated } + /// Returns true if this endpoint returns JSON (has a response_type extracted from application/json). + pub fn returns_json(&self) -> bool { + self.response_type.is_some() + } + /// Returns the operation ID or generates one from the path. /// The returned string uses the raw case from the spec (typically camelCase). pub fn operation_name(&self) -> String { diff --git a/crates/brk_client/src/lib.rs b/crates/brk_client/src/lib.rs index ac689e77a..707290c23 100644 --- a/crates/brk_client/src/lib.rs +++ b/crates/brk_client/src/lib.rs @@ -8253,8 +8253,8 @@ impl BrkClient { /// Compact OpenAPI specification optimized for LLM consumption. Removes redundant fields while preserving essential API information. Full spec available at `/openapi.json`. /// /// Endpoint: `GET /api.json` - pub fn get_api(&self) -> Result { - self.base.get_json(&format!("/api.json")) + pub fn get_api(&self) -> Result { + self.base.get_text(&format!("/api.json")) } /// Address information @@ -8327,8 +8327,8 @@ impl BrkClient { /// *[Mempool.space docs](https://mempool.space/docs/api/rest#get-block-height)* /// /// Endpoint: `GET /api/block-height/{height}` - pub fn get_block_by_height(&self, height: Height) -> Result { - self.base.get_json(&format!("/api/block-height/{height}")) + pub fn get_block_by_height(&self, height: Height) -> Result { + self.base.get_text(&format!("/api/block-height/{height}")) } /// Block information @@ -8349,8 +8349,8 @@ impl BrkClient { /// *[Mempool.space docs](https://mempool.space/docs/api/rest#get-block-header)* /// /// Endpoint: `GET /api/block/{hash}/header` - pub fn get_block_header(&self, hash: BlockHash) -> Result { - self.base.get_json(&format!("/api/block/{hash}/header")) + pub fn get_block_header(&self, hash: BlockHash) -> Result { + self.base.get_text(&format!("/api/block/{hash}/header")) } /// Raw block @@ -8382,8 +8382,8 @@ impl BrkClient { /// *[Mempool.space docs](https://mempool.space/docs/api/rest#get-block-transaction-id)* /// /// Endpoint: `GET /api/block/{hash}/txid/{index}` - pub fn get_block_txid(&self, hash: BlockHash, index: TxIndex) -> Result { - self.base.get_json(&format!("/api/block/{hash}/txid/{index}")) + pub fn get_block_txid(&self, hash: BlockHash, index: TxIndex) -> Result { + self.base.get_text(&format!("/api/block/{hash}/txid/{index}")) } /// Block transaction IDs @@ -8397,6 +8397,17 @@ impl BrkClient { self.base.get_json(&format!("/api/block/{hash}/txids")) } + /// Block transactions + /// + /// Retrieve transactions in a block by block hash. Returns up to 25 transactions starting from index 0. + /// + /// *[Mempool.space docs](https://mempool.space/docs/api/rest#get-block-transactions)* + /// + /// Endpoint: `GET /api/block/{hash}/txs` + pub fn get_block_txs(&self, hash: BlockHash) -> Result> { + self.base.get_json(&format!("/api/block/{hash}/txs")) + } + /// Block transactions (paginated) /// /// Retrieve transactions in a block by block hash, starting from the specified index. Returns up to 25 transactions at a time. @@ -8404,7 +8415,7 @@ impl BrkClient { /// *[Mempool.space docs](https://mempool.space/docs/api/rest#get-block-transactions)* /// /// Endpoint: `GET /api/block/{hash}/txs/{start_index}` - pub fn get_block_txs(&self, hash: BlockHash, start_index: TxIndex) -> Result> { + pub fn get_block_txs_from_index(&self, hash: BlockHash, start_index: TxIndex) -> Result> { self.base.get_json(&format!("/api/block/{hash}/txs/{start_index}")) } @@ -8426,8 +8437,8 @@ impl BrkClient { /// *[Mempool.space docs](https://mempool.space/docs/api/rest#get-block-tip-hash)* /// /// Endpoint: `GET /api/blocks/tip/hash` - pub fn get_block_tip_hash(&self) -> Result { - self.base.get_json(&format!("/api/blocks/tip/hash")) + pub fn get_block_tip_hash(&self) -> Result { + self.base.get_text(&format!("/api/blocks/tip/hash")) } /// Block tip height @@ -8437,8 +8448,8 @@ impl BrkClient { /// *[Mempool.space docs](https://mempool.space/docs/api/rest#get-block-tip-height)* /// /// Endpoint: `GET /api/blocks/tip/height` - pub fn get_block_tip_height(&self) -> Result { - self.base.get_json(&format!("/api/blocks/tip/height")) + pub fn get_block_tip_height(&self) -> Result { + self.base.get_text(&format!("/api/blocks/tip/height")) } /// Blocks from height @@ -8661,8 +8672,8 @@ impl BrkClient { /// Returns the single most recent value for a series, unwrapped (not inside a SeriesData object). /// /// Endpoint: `GET /api/series/{series}/{index}/latest` - pub fn get_series_latest(&self, series: SeriesName, index: Index) -> Result { - self.base.get_json(&format!("/api/series/{series}/{}/latest", index.name())) + pub fn get_series_latest(&self, series: SeriesName, index: Index) -> Result { + self.base.get_text(&format!("/api/series/{series}/{}/latest", index.name())) } /// Get series data length @@ -8719,8 +8730,8 @@ impl BrkClient { /// *[Mempool.space docs](https://mempool.space/docs/api/rest#get-transaction-hex)* /// /// Endpoint: `GET /api/tx/{txid}/hex` - pub fn get_tx_hex(&self, txid: Txid) -> Result { - self.base.get_json(&format!("/api/tx/{txid}/hex")) + pub fn get_tx_hex(&self, txid: Txid) -> Result { + self.base.get_text(&format!("/api/tx/{txid}/hex")) } /// Transaction merkle proof @@ -8742,7 +8753,7 @@ impl BrkClient { /// /// Endpoint: `GET /api/tx/{txid}/merkleblock-proof` pub fn get_tx_merkleblock_proof(&self, txid: Txid) -> Result { - self.base.get_json(&format!("/api/tx/{txid}/merkleblock-proof")) + self.base.get_text(&format!("/api/tx/{txid}/merkleblock-proof")) } /// Output spend status @@ -8899,8 +8910,8 @@ impl BrkClient { /// *[Mempool.space docs](https://mempool.space/docs/api/rest#get-block-feerates)* /// /// Endpoint: `GET /api/v1/mining/blocks/fee-rates/{time_period}` - pub fn get_block_fee_rates(&self, time_period: TimePeriod) -> Result { - self.base.get_json(&format!("/api/v1/mining/blocks/fee-rates/{time_period}")) + pub fn get_block_fee_rates(&self, time_period: TimePeriod) -> Result { + self.base.get_text(&format!("/api/v1/mining/blocks/fee-rates/{time_period}")) } /// Block fees @@ -9137,8 +9148,8 @@ impl BrkClient { /// Full OpenAPI 3.1 specification for this API. /// /// Endpoint: `GET /openapi.json` - pub fn get_openapi(&self) -> Result { - self.base.get_json(&format!("/openapi.json")) + pub fn get_openapi(&self) -> Result { + self.base.get_text(&format!("/openapi.json")) } /// API version diff --git a/crates/brk_server/src/api/addrs/mod.rs b/crates/brk_server/src/api/addrs/mod.rs index 6a911403f..bcf6c5a6e 100644 --- a/crates/brk_server/src/api/addrs/mod.rs +++ b/crates/brk_server/src/api/addrs/mod.rs @@ -37,7 +37,7 @@ impl AddrRoutes for ApiRouter { .addrs_tag() .summary("Address information") .description("Retrieve address information including balance and transaction counts. Supports all standard Bitcoin address types (P2PKH, P2SH, P2WPKH, P2WSH, P2TR).\n\n*[Mempool.space docs](https://mempool.space/docs/api/rest#get-address)*") - .ok_response::() + .json_response::() .not_modified() .bad_request() .not_found() @@ -59,7 +59,7 @@ impl AddrRoutes for ApiRouter { .addrs_tag() .summary("Address transactions") .description("Get transaction history for an address, sorted with newest first. Returns up to 50 mempool transactions plus the first 25 confirmed transactions. Use ?after_txid= for pagination.\n\n*[Mempool.space docs](https://mempool.space/docs/api/rest#get-address-transactions)*") - .ok_response::>() + .json_response::>() .not_modified() .bad_request() .not_found() @@ -81,7 +81,7 @@ impl AddrRoutes for ApiRouter { .addrs_tag() .summary("Address confirmed transactions") .description("Get confirmed transactions for an address, 25 per page. Use ?after_txid= for pagination.\n\n*[Mempool.space docs](https://mempool.space/docs/api/rest#get-address-transactions-chain)*") - .ok_response::>() + .json_response::>() .not_modified() .bad_request() .not_found() @@ -103,7 +103,7 @@ impl AddrRoutes for ApiRouter { .addrs_tag() .summary("Address mempool transactions") .description("Get unconfirmed transaction IDs for an address from the mempool (up to 50).\n\n*[Mempool.space docs](https://mempool.space/docs/api/rest#get-address-transactions-mempool)*") - .ok_response::>() + .json_response::>() .bad_request() .not_found() .server_error() @@ -123,7 +123,7 @@ impl AddrRoutes for ApiRouter { .addrs_tag() .summary("Address UTXOs") .description("Get unspent transaction outputs (UTXOs) for an address. Returns txid, vout, value, and confirmation status for each UTXO.\n\n*[Mempool.space docs](https://mempool.space/docs/api/rest#get-address-utxo)*") - .ok_response::>() + .json_response::>() .not_modified() .bad_request() .not_found() @@ -144,7 +144,7 @@ impl AddrRoutes for ApiRouter { .addrs_tag() .summary("Validate address") .description("Validate a Bitcoin address and get information about its type and scriptPubKey.\n\n*[Mempool.space docs](https://mempool.space/docs/api/rest#get-address-validate)*") - .ok_response::() + .json_response::() .not_modified() ), ) diff --git a/crates/brk_server/src/api/blocks/mod.rs b/crates/brk_server/src/api/blocks/mod.rs index e3ad82685..150826031 100644 --- a/crates/brk_server/src/api/blocks/mod.rs +++ b/crates/brk_server/src/api/blocks/mod.rs @@ -5,8 +5,8 @@ use axum::{ }; use brk_query::BLOCK_TXS_PAGE_SIZE; use brk_types::{ - BlockHash, BlockHashParam, BlockHashStartIndex, BlockHashTxIndex, BlockInfo, BlockInfoV1, - BlockStatus, BlockTimestamp, Height, HeightParam, Hex, TimestampParam, Transaction, Txid, + BlockHashParam, BlockHashStartIndex, BlockHashTxIndex, BlockInfo, BlockInfoV1, BlockStatus, + BlockTimestamp, HeightParam, TimestampParam, Transaction, TxIndex, Txid, }; use crate::{CacheStrategy, extended::TransformResponseExtended}; @@ -35,7 +35,7 @@ impl BlockRoutes for ApiRouter { .description( "Retrieve block information by block hash. Returns block metadata including height, timestamp, difficulty, size, weight, and transaction count.\n\n*[Mempool.space docs](https://mempool.space/docs/api/rest#get-block)*", ) - .ok_response::() + .json_response::() .not_modified() .bad_request() .not_found() @@ -57,7 +57,7 @@ impl BlockRoutes for ApiRouter { .blocks_tag() .summary("Block (v1)") .description("Returns block details with extras by hash.\n\n*[Mempool.space docs](https://mempool.space/docs/api/rest#get-block-v1)*") - .ok_response::() + .json_response::() .not_modified() .not_found() .server_error() @@ -75,7 +75,7 @@ impl BlockRoutes for ApiRouter { .blocks_tag() .summary("Block header") .description("Returns the hex-encoded block header.\n\n*[Mempool.space docs](https://mempool.space/docs/api/rest#get-block-header)*") - .ok_response::() + .text_response() .not_modified() .not_found() .server_error() @@ -98,7 +98,7 @@ impl BlockRoutes for ApiRouter { .description( "Retrieve the block hash at a given height. Returns the hash as plain text.\n\n*[Mempool.space docs](https://mempool.space/docs/api/rest#get-block-height)*", ) - .ok_response::() + .text_response() .not_modified() .bad_request() .not_found() @@ -120,7 +120,7 @@ impl BlockRoutes for ApiRouter { .blocks_tag() .summary("Block by timestamp") .description("Find the block closest to a given UNIX timestamp.\n\n*[Mempool.space docs](https://mempool.space/docs/api/rest#get-block-timestamp)*") - .ok_response::() + .json_response::() .not_modified() .bad_request() .not_found() @@ -144,7 +144,7 @@ impl BlockRoutes for ApiRouter { .description( "Returns the raw block data in binary format.\n\n*[Mempool.space docs](https://mempool.space/docs/api/rest#get-block-raw)*", ) - .ok_response::>() + .json_response::>() .not_modified() .bad_request() .not_found() @@ -168,7 +168,7 @@ impl BlockRoutes for ApiRouter { .description( "Retrieve the status of a block. Returns whether the block is in the best chain and, if so, its height and the hash of the next block.\n\n*[Mempool.space docs](https://mempool.space/docs/api/rest#get-block-status)*", ) - .ok_response::() + .json_response::() .not_modified() .bad_request() .not_found() @@ -187,7 +187,7 @@ impl BlockRoutes for ApiRouter { .blocks_tag() .summary("Block tip height") .description("Returns the height of the last block.\n\n*[Mempool.space docs](https://mempool.space/docs/api/rest#get-block-tip-height)*") - .ok_response::() + .text_response() .not_modified() .server_error() }, @@ -204,7 +204,7 @@ impl BlockRoutes for ApiRouter { .blocks_tag() .summary("Block tip hash") .description("Returns the hash of the last block.\n\n*[Mempool.space docs](https://mempool.space/docs/api/rest#get-block-tip-hash)*") - .ok_response::() + .text_response() .not_modified() .server_error() }, @@ -226,7 +226,7 @@ impl BlockRoutes for ApiRouter { .description( "Retrieve a single transaction ID at a specific index within a block. Returns plain text txid.\n\n*[Mempool.space docs](https://mempool.space/docs/api/rest#get-block-transaction-id)*", ) - .ok_response::() + .text_response() .not_modified() .bad_request() .not_found() @@ -250,7 +250,32 @@ impl BlockRoutes for ApiRouter { .description( "Retrieve all transaction IDs in a block. Returns an array of txids in block order.\n\n*[Mempool.space docs](https://mempool.space/docs/api/rest#get-block-transaction-ids)*", ) - .ok_response::>() + .json_response::>() + .not_modified() + .bad_request() + .not_found() + .server_error() + }, + ), + ) + .api_route( + "/api/block/{hash}/txs", + get_with( + async |uri: Uri, + headers: HeaderMap, + Path(path): Path, + State(state): State| { + state.cached_json(&headers, CacheStrategy::Static, &uri, move |q| q.block_txs(&path.hash, TxIndex::default())).await + }, + |op| { + op.id("get_block_txs") + .blocks_tag() + .summary("Block transactions") + .description(&format!( + "Retrieve transactions in a block by block hash. Returns up to {} transactions starting from index 0.\n\n*[Mempool.space docs](https://mempool.space/docs/api/rest#get-block-transactions)*", + BLOCK_TXS_PAGE_SIZE + )) + .json_response::>() .not_modified() .bad_request() .not_found() @@ -268,14 +293,14 @@ impl BlockRoutes for ApiRouter { state.cached_json(&headers, CacheStrategy::Static, &uri, move |q| q.block_txs(&path.hash, path.start_index)).await }, |op| { - op.id("get_block_txs") + op.id("get_block_txs_from_index") .blocks_tag() .summary("Block transactions (paginated)") .description(&format!( "Retrieve transactions in a block by block hash, starting from the specified index. Returns up to {} transactions at a time.\n\n*[Mempool.space docs](https://mempool.space/docs/api/rest#get-block-transactions)*", BLOCK_TXS_PAGE_SIZE )) - .ok_response::>() + .json_response::>() .not_modified() .bad_request() .not_found() @@ -296,7 +321,7 @@ impl BlockRoutes for ApiRouter { .blocks_tag() .summary("Recent blocks") .description("Retrieve the last 10 blocks. Returns block metadata for each block.\n\n*[Mempool.space docs](https://mempool.space/docs/api/rest#get-blocks)*") - .ok_response::>() + .json_response::>() .not_modified() .server_error() }, @@ -318,7 +343,7 @@ impl BlockRoutes for ApiRouter { .description( "Retrieve up to 10 blocks going backwards from the given height. For example, height=100 returns blocks 100, 99, 98, ..., 91. Height=0 returns only block 0.\n\n*[Mempool.space docs](https://mempool.space/docs/api/rest#get-blocks)*", ) - .ok_response::>() + .json_response::>() .not_modified() .bad_request() .server_error() @@ -338,7 +363,7 @@ impl BlockRoutes for ApiRouter { .blocks_tag() .summary("Recent blocks with extras") .description("Retrieve the last 10 blocks with extended data including pool identification and fee statistics.\n\n*[Mempool.space docs](https://mempool.space/docs/api/rest#get-blocks-v1)*") - .ok_response::>() + .json_response::>() .not_modified() .server_error() }, @@ -358,7 +383,7 @@ impl BlockRoutes for ApiRouter { .blocks_tag() .summary("Blocks from height with extras") .description("Retrieve up to 10 blocks with extended data going backwards from the given height.\n\n*[Mempool.space docs](https://mempool.space/docs/api/rest#get-blocks-v1)*") - .ok_response::>() + .json_response::>() .not_modified() .bad_request() .server_error() diff --git a/crates/brk_server/src/api/fees/mod.rs b/crates/brk_server/src/api/fees/mod.rs index fd7acfb2b..29f98b8b0 100644 --- a/crates/brk_server/src/api/fees/mod.rs +++ b/crates/brk_server/src/api/fees/mod.rs @@ -30,7 +30,7 @@ impl FeesRoutes for ApiRouter { .fees_tag() .summary("Projected mempool blocks") .description("Get projected blocks from the mempool for fee estimation.\n\n*[Mempool.space docs](https://mempool.space/docs/api/rest#get-mempool-blocks-fees)*") - .ok_response::>() + .json_response::>() .server_error() }, ), @@ -50,7 +50,7 @@ impl FeesRoutes for ApiRouter { .fees_tag() .summary("Recommended fees") .description("Get recommended fee rates for different confirmation targets.\n\n*[Mempool.space docs](https://mempool.space/docs/api/rest#get-recommended-fees)*") - .ok_response::() + .json_response::() .server_error() }, ), @@ -70,7 +70,7 @@ impl FeesRoutes for ApiRouter { .fees_tag() .summary("Precise recommended fees") .description("Get recommended fee rates with up to 3 decimal places.\n\n*[Mempool.space docs](https://mempool.space/docs/api/rest#get-recommended-fees-precise)*") - .ok_response::() + .json_response::() .server_error() }, ), diff --git a/crates/brk_server/src/api/general/mod.rs b/crates/brk_server/src/api/general/mod.rs index 0b6b06f80..1fd2bbb2b 100644 --- a/crates/brk_server/src/api/general/mod.rs +++ b/crates/brk_server/src/api/general/mod.rs @@ -32,7 +32,7 @@ impl GeneralRoutes for ApiRouter { .general_tag() .summary("Difficulty adjustment") .description("Get current difficulty adjustment progress and estimates.\n\n*[Mempool.space docs](https://mempool.space/docs/api/rest#get-difficulty-adjustment)*") - .ok_response::() + .json_response::() .not_modified() .server_error() }, @@ -56,7 +56,7 @@ impl GeneralRoutes for ApiRouter { .general_tag() .summary("Current BTC price") .description("Returns bitcoin latest price (on-chain derived, USD only).\n\n*[Mempool.space docs](https://mempool.space/docs/api/rest#get-price)*") - .ok_response::() + .json_response::() .server_error() }, ), @@ -79,7 +79,7 @@ impl GeneralRoutes for ApiRouter { .general_tag() .summary("Historical price") .description("Get historical BTC/USD price. Optionally specify a UNIX timestamp to get the price at that time.\n\n*[Mempool.space docs](https://mempool.space/docs/api/rest#get-historical-price)*") - .ok_response::() + .json_response::() .not_modified() .server_error() }, diff --git a/crates/brk_server/src/api/mempool/mod.rs b/crates/brk_server/src/api/mempool/mod.rs index 8bb8d2260..c1ebfe002 100644 --- a/crates/brk_server/src/api/mempool/mod.rs +++ b/crates/brk_server/src/api/mempool/mod.rs @@ -28,7 +28,7 @@ impl MempoolRoutes for ApiRouter { .mempool_tag() .summary("Mempool statistics") .description("Get current mempool statistics including transaction count, total vsize, total fees, and fee histogram.\n\n*[Mempool.space docs](https://mempool.space/docs/api/rest#get-mempool)*") - .ok_response::() + .json_response::() .server_error() }, ), @@ -46,7 +46,7 @@ impl MempoolRoutes for ApiRouter { .mempool_tag() .summary("Mempool transaction IDs") .description("Get all transaction IDs currently in the mempool.\n\n*[Mempool.space docs](https://mempool.space/docs/api/rest#get-mempool-transaction-ids)*") - .ok_response::>() + .json_response::>() .server_error() }, ), @@ -64,7 +64,7 @@ impl MempoolRoutes for ApiRouter { .mempool_tag() .summary("Recent mempool transactions") .description("Get the last 10 transactions to enter the mempool.\n\n*[Mempool.space docs](https://mempool.space/docs/api/rest#get-mempool-recent)*") - .ok_response::>() + .json_response::>() .server_error() }, ), @@ -86,7 +86,7 @@ impl MempoolRoutes for ApiRouter { on-chain round-dollar output patterns in the last 12 blocks \ plus mempool.", ) - .ok_response::() + .json_response::() .server_error() }, ), diff --git a/crates/brk_server/src/api/metrics_legacy/mod.rs b/crates/brk_server/src/api/metrics_legacy/mod.rs index b8fae5138..140506c0c 100644 --- a/crates/brk_server/src/api/metrics_legacy/mod.rs +++ b/crates/brk_server/src/api/metrics_legacy/mod.rs @@ -57,7 +57,7 @@ impl ApiMetricsLegacyRoutes for ApiRouter { "**DEPRECATED** - Use `/api/series` instead.\n\n\ Sunset date: 2027-01-01." ) - .ok_response::() + .json_response::() .not_modified(), ), ) @@ -80,7 +80,7 @@ impl ApiMetricsLegacyRoutes for ApiRouter { "**DEPRECATED** - Use `/api/series/count` instead.\n\n\ Sunset date: 2027-01-01." ) - .ok_response::() + .json_response::() .not_modified(), ), ) @@ -103,7 +103,7 @@ impl ApiMetricsLegacyRoutes for ApiRouter { "**DEPRECATED** - Use `/api/series/indexes` instead.\n\n\ Sunset date: 2027-01-01." ) - .ok_response::>() + .json_response::>() .not_modified(), ), ) @@ -127,7 +127,7 @@ impl ApiMetricsLegacyRoutes for ApiRouter { "**DEPRECATED** - Use `/api/series/list` instead.\n\n\ Sunset date: 2027-01-01." ) - .ok_response::() + .json_response::() .not_modified(), ), ) @@ -151,7 +151,7 @@ impl ApiMetricsLegacyRoutes for ApiRouter { "**DEPRECATED** - Use `/api/series/search` instead.\n\n\ Sunset date: 2027-01-01." ) - .ok_response::>() + .json_response::>() .not_modified() .server_error(), ), @@ -173,7 +173,7 @@ impl ApiMetricsLegacyRoutes for ApiRouter { "**DEPRECATED** - Use `/api/series/bulk` instead.\n\n\ Sunset date: 2027-01-01." ) - .ok_response::>() + .json_response::>() .csv_response() .not_modified(), ), @@ -201,7 +201,7 @@ impl ApiMetricsLegacyRoutes for ApiRouter { "**DEPRECATED** - Use `/api/series/{series}` instead.\n\n\ Sunset date: 2027-01-01." ) - .ok_response::() + .json_response::() .not_modified() .not_found() .server_error(), @@ -231,7 +231,7 @@ impl ApiMetricsLegacyRoutes for ApiRouter { "**DEPRECATED** - Use `/api/series/{series}/{index}` instead.\n\n\ Sunset date: 2027-01-01." ) - .ok_response::() + .json_response::() .csv_response() .not_modified() .not_found(), @@ -261,7 +261,7 @@ impl ApiMetricsLegacyRoutes for ApiRouter { "**DEPRECATED** - Use `/api/series/{series}/{index}/data` instead.\n\n\ Sunset date: 2027-01-01." ) - .ok_response::>() + .json_response::>() .csv_response() .not_modified() .not_found(), @@ -289,7 +289,7 @@ impl ApiMetricsLegacyRoutes for ApiRouter { "**DEPRECATED** - Use `/api/series/{series}/{index}/latest` instead.\n\n\ Sunset date: 2027-01-01." ) - .ok_response::() + .json_response::() .not_found(), ), ) @@ -315,7 +315,7 @@ impl ApiMetricsLegacyRoutes for ApiRouter { "**DEPRECATED** - Use `/api/series/{series}/{index}/len` instead.\n\n\ Sunset date: 2027-01-01." ) - .ok_response::() + .json_response::() .not_found(), ), ) @@ -341,7 +341,7 @@ impl ApiMetricsLegacyRoutes for ApiRouter { "**DEPRECATED** - Use `/api/series/{series}/{index}/version` instead.\n\n\ Sunset date: 2027-01-01." ) - .ok_response::() + .json_response::() .not_found(), ), ) @@ -363,7 +363,7 @@ impl ApiMetricsLegacyRoutes for ApiRouter { "**DEPRECATED** - Use `/api/series/cost-basis` instead.\n\n\ Sunset date: 2027-01-01." ) - .ok_response::>() + .json_response::>() .server_error() }, ), @@ -390,7 +390,7 @@ impl ApiMetricsLegacyRoutes for ApiRouter { "**DEPRECATED** - Use `/api/series/cost-basis/{cohort}/dates` instead.\n\n\ Sunset date: 2027-01-01." ) - .ok_response::>() + .json_response::>() .not_found() .server_error() }, @@ -424,7 +424,7 @@ impl ApiMetricsLegacyRoutes for ApiRouter { "**DEPRECATED** - Use `/api/series/cost-basis/{cohort}/{date}` instead.\n\n\ Sunset date: 2027-01-01." ) - .ok_response::() + .json_response::() .not_found() .server_error() }, @@ -471,7 +471,7 @@ impl ApiMetricsLegacyRoutes for ApiRouter { Returns raw data without the SeriesData wrapper." ) .deprecated() - .ok_response::() + .json_response::() .not_modified(), ), ) @@ -498,7 +498,7 @@ impl ApiMetricsLegacyRoutes for ApiRouter { Legacy endpoint for querying series. Returns raw data without the SeriesData wrapper." ) .deprecated() - .ok_response::() + .json_response::() .not_modified(), ), ) diff --git a/crates/brk_server/src/api/mining/mod.rs b/crates/brk_server/src/api/mining/mod.rs index 326f5f4bd..d73077b6a 100644 --- a/crates/brk_server/src/api/mining/mod.rs +++ b/crates/brk_server/src/api/mining/mod.rs @@ -38,7 +38,7 @@ impl MiningRoutes for ApiRouter { .mining_tag() .summary("List all mining pools") .description("Get list of all known mining pools with their identifiers.\n\n*[Mempool.space docs](https://mempool.space/docs/api/rest#get-mining-pools)*") - .ok_response::>() + .json_response::>() .not_modified() .server_error() }, @@ -55,7 +55,7 @@ impl MiningRoutes for ApiRouter { .mining_tag() .summary("Mining pool statistics") .description("Get mining pool statistics for a time period. Valid periods: 24h, 3d, 1w, 1m, 3m, 6m, 1y, 2y, 3y\n\n*[Mempool.space docs](https://mempool.space/docs/api/rest#get-mining-pools)*") - .ok_response::() + .json_response::() .not_modified() .server_error() }, @@ -72,7 +72,7 @@ impl MiningRoutes for ApiRouter { .mining_tag() .summary("Mining pool details") .description("Get detailed information about a specific mining pool including block counts and shares for different time periods.\n\n*[Mempool.space docs](https://mempool.space/docs/api/rest#get-mining-pool)*") - .ok_response::() + .json_response::() .not_modified() .not_found() .server_error() @@ -90,7 +90,7 @@ impl MiningRoutes for ApiRouter { .mining_tag() .summary("All pools hashrate (all time)") .description("Get hashrate data for all mining pools.\n\n*[Mempool.space docs](https://mempool.space/docs/api/rest#get-mining-pool-hashrates)*") - .ok_response::>() + .json_response::>() .not_modified() .server_error() }, @@ -107,7 +107,7 @@ impl MiningRoutes for ApiRouter { .mining_tag() .summary("All pools hashrate") .description("Get hashrate data for all mining pools for a time period. Valid periods: 1m, 3m, 6m, 1y, 2y, 3y\n\n*[Mempool.space docs](https://mempool.space/docs/api/rest#get-mining-pool-hashrates)*") - .ok_response::>() + .json_response::>() .not_modified() .server_error() }, @@ -124,7 +124,7 @@ impl MiningRoutes for ApiRouter { .mining_tag() .summary("Mining pool hashrate") .description("Get hashrate history for a specific mining pool.\n\n*[Mempool.space docs](https://mempool.space/docs/api/rest#get-mining-pool-hashrate)*") - .ok_response::>() + .json_response::>() .not_modified() .not_found() .server_error() @@ -142,7 +142,7 @@ impl MiningRoutes for ApiRouter { .mining_tag() .summary("Mining pool blocks") .description("Get the 10 most recent blocks mined by a specific pool.\n\n*[Mempool.space docs](https://mempool.space/docs/api/rest#get-mining-pool-blocks)*") - .ok_response::>() + .json_response::>() .not_modified() .not_found() .server_error() @@ -160,7 +160,7 @@ impl MiningRoutes for ApiRouter { .mining_tag() .summary("Mining pool blocks from height") .description("Get 10 blocks mined by a specific pool before (and including) the given height.\n\n*[Mempool.space docs](https://mempool.space/docs/api/rest#get-mining-pool-blocks)*") - .ok_response::>() + .json_response::>() .not_modified() .not_found() .server_error() @@ -178,7 +178,7 @@ impl MiningRoutes for ApiRouter { .mining_tag() .summary("Network hashrate (all time)") .description("Get network hashrate and difficulty data for all time.\n\n*[Mempool.space docs](https://mempool.space/docs/api/rest#get-hashrate)*") - .ok_response::() + .json_response::() .not_modified() .server_error() }, @@ -195,7 +195,7 @@ impl MiningRoutes for ApiRouter { .mining_tag() .summary("Network hashrate") .description("Get network hashrate and difficulty data for a time period. Valid periods: 24h, 3d, 1w, 1m, 3m, 6m, 1y, 2y, 3y\n\n*[Mempool.space docs](https://mempool.space/docs/api/rest#get-hashrate)*") - .ok_response::() + .json_response::() .not_modified() .server_error() }, @@ -212,7 +212,7 @@ impl MiningRoutes for ApiRouter { .mining_tag() .summary("Difficulty adjustments (all time)") .description("Get historical difficulty adjustments including timestamp, block height, difficulty value, and percentage change.\n\n*[Mempool.space docs](https://mempool.space/docs/api/rest#get-difficulty-adjustments)*") - .ok_response::>() + .json_response::>() .not_modified() .server_error() }, @@ -229,7 +229,7 @@ impl MiningRoutes for ApiRouter { .mining_tag() .summary("Difficulty adjustments") .description("Get historical difficulty adjustments for a time period. Valid periods: 24h, 3d, 1w, 1m, 3m, 6m, 1y, 2y, 3y.\n\n*[Mempool.space docs](https://mempool.space/docs/api/rest#get-difficulty-adjustments)*") - .ok_response::>() + .json_response::>() .not_modified() .server_error() }, @@ -246,7 +246,7 @@ impl MiningRoutes for ApiRouter { .mining_tag() .summary("Mining reward statistics") .description("Get mining reward statistics for the last N blocks including total rewards, fees, and transaction count.\n\n*[Mempool.space docs](https://mempool.space/docs/api/rest#get-reward-stats)*") - .ok_response::() + .json_response::() .not_modified() .server_error() }, @@ -263,7 +263,7 @@ impl MiningRoutes for ApiRouter { .mining_tag() .summary("Block fees") .description("Get average block fees for a time period. Valid periods: 24h, 3d, 1w, 1m, 3m, 6m, 1y, 2y, 3y\n\n*[Mempool.space docs](https://mempool.space/docs/api/rest#get-block-fees)*") - .ok_response::>() + .json_response::>() .not_modified() .server_error() }, @@ -280,7 +280,7 @@ impl MiningRoutes for ApiRouter { .mining_tag() .summary("Block rewards") .description("Get average block rewards (coinbase = subsidy + fees) for a time period. Valid periods: 24h, 3d, 1w, 1m, 3m, 6m, 1y, 2y, 3y\n\n*[Mempool.space docs](https://mempool.space/docs/api/rest#get-block-rewards)*") - .ok_response::>() + .json_response::>() .not_modified() .server_error() }, @@ -312,7 +312,7 @@ impl MiningRoutes for ApiRouter { .mining_tag() .summary("Block sizes and weights") .description("Get average block sizes and weights for a time period. Valid periods: 24h, 3d, 1w, 1m, 3m, 6m, 1y, 2y, 3y\n\n*[Mempool.space docs](https://mempool.space/docs/api/rest#get-sizes-weights)*") - .ok_response::() + .json_response::() .not_modified() .server_error() }, diff --git a/crates/brk_server/src/api/mod.rs b/crates/brk_server/src/api/mod.rs index b375f6e2c..c27b94c76 100644 --- a/crates/brk_server/src/api/mod.rs +++ b/crates/brk_server/src/api/mod.rs @@ -85,7 +85,7 @@ impl ApiRoutes for ApiRouter { Removes redundant fields while preserving essential API information. \ Full spec available at `/openapi.json`.", ) - .ok_response::() + .json_response::() }, ), ) diff --git a/crates/brk_server/src/api/series/mod.rs b/crates/brk_server/src/api/series/mod.rs index 6eadf12a5..f7d7edf15 100644 --- a/crates/brk_server/src/api/series/mod.rs +++ b/crates/brk_server/src/api/series/mod.rs @@ -59,7 +59,7 @@ impl ApiSeriesRoutes for ApiRouter { "Returns the complete hierarchical catalog of available series organized as a tree structure. \ Series are grouped by categories and subcategories." ) - .ok_response::() + .json_response::() .not_modified(), ), ) @@ -78,7 +78,7 @@ impl ApiSeriesRoutes for ApiRouter { .series_tag() .summary("Series count") .description("Returns the number of series available per index type.") - .ok_response::>() + .json_response::>() .not_modified(), ), ) @@ -99,7 +99,7 @@ impl ApiSeriesRoutes for ApiRouter { .description( "Returns all available indexes with their accepted query aliases. Use any alias when querying series." ) - .ok_response::>() + .json_response::>() .not_modified(), ), ) @@ -119,7 +119,7 @@ impl ApiSeriesRoutes for ApiRouter { .series_tag() .summary("Series list") .description("Paginated flat list of all available series names. Use `page` query param for pagination.") - .ok_response::() + .json_response::() .not_modified(), ), ) @@ -139,7 +139,7 @@ impl ApiSeriesRoutes for ApiRouter { .series_tag() .summary("Search series") .description("Fuzzy search for series by name. Supports partial matches and typos.") - .ok_response::>() + .json_response::>() .not_modified() .server_error(), ), @@ -164,7 +164,7 @@ impl ApiSeriesRoutes for ApiRouter { .description( "Returns the supported indexes and value type for the specified series." ) - .ok_response::() + .json_response::() .not_modified() .not_found() .server_error(), @@ -198,7 +198,7 @@ impl ApiSeriesRoutes for ApiRouter { "Fetch data for a specific series at the given index. \ Use query parameters to filter by date range and format (json/csv)." ) - .ok_response::() + .json_response::() .csv_response() .not_modified() .not_found(), @@ -232,7 +232,7 @@ impl ApiSeriesRoutes for ApiRouter { "Returns just the data array without the SeriesData wrapper. \ Supports the same range and format parameters as the standard endpoint." ) - .ok_response::>() + .json_response::>() .csv_response() .not_modified() .not_found(), @@ -258,7 +258,7 @@ impl ApiSeriesRoutes for ApiRouter { .description( "Returns the single most recent value for a series, unwrapped (not inside a SeriesData object)." ) - .ok_response::() + .json_response::() .not_found(), ), ) @@ -280,7 +280,7 @@ impl ApiSeriesRoutes for ApiRouter { .series_tag() .summary("Get series data length") .description("Returns the total number of data points for a series at the given index.") - .ok_response::() + .json_response::() .not_found(), ), ) @@ -302,7 +302,7 @@ impl ApiSeriesRoutes for ApiRouter { .series_tag() .summary("Get series version") .description("Returns the current version of a series. Changes when the series data is updated.") - .ok_response::() + .json_response::() .not_found(), ), ) @@ -320,7 +320,7 @@ impl ApiSeriesRoutes for ApiRouter { "Fetch multiple series in a single request. Supports filtering by index and date range. \ Returns an array of SeriesData objects. For a single series, use `get_series` instead." ) - .ok_response::>() + .json_response::>() .csv_response() .not_modified(), ), @@ -339,7 +339,7 @@ impl ApiSeriesRoutes for ApiRouter { .series_tag() .summary("Available cost basis cohorts") .description("List available cohorts for cost basis distribution.") - .ok_response::>() + .json_response::>() .server_error() }, ), @@ -362,7 +362,7 @@ impl ApiSeriesRoutes for ApiRouter { .series_tag() .summary("Available cost basis dates") .description("List available dates for a cohort's cost basis distribution.") - .ok_response::>() + .json_response::>() .not_found() .server_error() }, @@ -397,7 +397,7 @@ impl ApiSeriesRoutes for ApiRouter { - `bucket`: raw (default), lin200, lin500, lin1000, log10, log50, log100\n\ - `value`: supply (default, in BTC), realized (USD), unrealized (USD)", ) - .ok_response::() + .json_response::() .not_found() .server_error() }, diff --git a/crates/brk_server/src/api/server/mod.rs b/crates/brk_server/src/api/server/mod.rs index 8a2edfea3..dd58a42da 100644 --- a/crates/brk_server/src/api/server/mod.rs +++ b/crates/brk_server/src/api/server/mod.rs @@ -48,7 +48,7 @@ impl ServerRoutes for ApiRouter { .server_tag() .summary("Health check") .description("Returns the health status of the API server, including uptime information.") - .ok_response::() + .json_response::() }, ), ) @@ -67,7 +67,7 @@ impl ServerRoutes for ApiRouter { .server_tag() .summary("API version") .description("Returns the current version of the API server") - .ok_response::() + .json_response::() .not_modified() }, ), @@ -91,7 +91,7 @@ impl ServerRoutes for ApiRouter { "Returns the sync status of the indexer, including indexed height, \ tip height, blocks behind, and last indexed timestamp.", ) - .ok_response::() + .json_response::() .not_modified() }, ), @@ -116,7 +116,7 @@ impl ServerRoutes for ApiRouter { .description( "Returns the disk space used by BRK and Bitcoin data.", ) - .ok_response::() + .json_response::() .not_modified() }, ), diff --git a/crates/brk_server/src/api/transactions/mod.rs b/crates/brk_server/src/api/transactions/mod.rs index ec2adc551..8ba784ab5 100644 --- a/crates/brk_server/src/api/transactions/mod.rs +++ b/crates/brk_server/src/api/transactions/mod.rs @@ -7,7 +7,7 @@ use axum::{ http::{HeaderMap, Uri}, }; use brk_types::{ - CpfpInfo, Hex, MerkleProof, Transaction, TxOutspend, TxStatus, Txid, TxidParam, TxidVout, + CpfpInfo, MerkleProof, Transaction, TxOutspend, TxStatus, Txid, TxidParam, TxidVout, TxidsParam, }; @@ -33,7 +33,7 @@ impl TxRoutes for ApiRouter { .transactions_tag() .summary("CPFP info") .description("Returns ancestors and descendants for a CPFP transaction.\n\n*[Mempool.space docs](https://mempool.space/docs/api/rest#get-children-pay-for-parent)*") - .ok_response::() + .json_response::() .not_found() .server_error(), ), @@ -56,7 +56,7 @@ impl TxRoutes for ApiRouter { .description( "Retrieve complete transaction data by transaction ID (txid). Returns inputs, outputs, fee, size, and confirmation status.\n\n*[Mempool.space docs](https://mempool.space/docs/api/rest#get-transaction)*", ) - .ok_response::() + .json_response::() .not_modified() .bad_request() .not_found() @@ -81,7 +81,7 @@ impl TxRoutes for ApiRouter { .description( "Retrieve the raw transaction as a hex-encoded string. Returns the serialized transaction in hexadecimal format.\n\n*[Mempool.space docs](https://mempool.space/docs/api/rest#get-transaction-hex)*", ) - .ok_response::() + .text_response() .not_modified() .bad_request() .not_found() @@ -99,7 +99,7 @@ impl TxRoutes for ApiRouter { .transactions_tag() .summary("Transaction merkleblock proof") .description("Get the merkleblock proof for a transaction (BIP37 format, hex encoded).\n\n*[Mempool.space docs](https://mempool.space/docs/api/rest#get-transaction-merkleblock-proof)*") - .ok_response::() + .text_response() .not_modified() .bad_request() .not_found() @@ -117,7 +117,7 @@ impl TxRoutes for ApiRouter { .transactions_tag() .summary("Transaction merkle proof") .description("Get the merkle inclusion proof for a transaction.\n\n*[Mempool.space docs](https://mempool.space/docs/api/rest#get-transaction-merkle-proof)*") - .ok_response::() + .json_response::() .not_modified() .bad_request() .not_found() @@ -143,7 +143,7 @@ impl TxRoutes for ApiRouter { .description( "Get the spending status of a transaction output. Returns whether the output has been spent and, if so, the spending transaction details.\n\n*[Mempool.space docs](https://mempool.space/docs/api/rest#get-transaction-outspend)*", ) - .ok_response::() + .json_response::() .not_modified() .bad_request() .not_found() @@ -168,7 +168,7 @@ impl TxRoutes for ApiRouter { .description( "Get the spending status of all outputs in a transaction. Returns an array with the spend status for each output.\n\n*[Mempool.space docs](https://mempool.space/docs/api/rest#get-transaction-outspends)*", ) - .ok_response::>() + .json_response::>() .not_modified() .bad_request() .not_found() @@ -186,7 +186,7 @@ impl TxRoutes for ApiRouter { .transactions_tag() .summary("Transaction raw") .description("Returns a transaction as binary data.\n\n*[Mempool.space docs](https://mempool.space/docs/api/rest#get-transaction-raw)*") - .ok_response::>() + .json_response::>() .not_modified() .bad_request() .not_found() @@ -211,7 +211,7 @@ impl TxRoutes for ApiRouter { .description( "Retrieve the confirmation status of a transaction. Returns whether the transaction is confirmed and, if so, the block height, hash, and timestamp.\n\n*[Mempool.space docs](https://mempool.space/docs/api/rest#get-transaction-status)*", ) - .ok_response::() + .json_response::() .not_modified() .bad_request() .not_found() @@ -230,7 +230,7 @@ impl TxRoutes for ApiRouter { .transactions_tag() .summary("Transaction first-seen times") .description("Returns timestamps when transactions were first seen in the mempool. Returns 0 for mined or unknown transactions.\n\n*[Mempool.space docs](https://mempool.space/docs/api/rest#get-transaction-times)*") - .ok_response::>() + .json_response::>() .server_error(), ), ) @@ -248,7 +248,7 @@ impl TxRoutes for ApiRouter { .transactions_tag() .summary("Broadcast transaction") .description("Broadcast a raw transaction to the network. The transaction should be provided as hex in the request body. The txid will be returned on success.\n\n*[Mempool.space docs](https://mempool.space/docs/api/rest#post-transaction)*") - .ok_response::() + .json_response::() .bad_request() .server_error() }, diff --git a/crates/brk_server/src/extended/transform_operation.rs b/crates/brk_server/src/extended/transform_operation.rs index 8d3619b49..aa323f895 100644 --- a/crates/brk_server/src/extended/transform_operation.rs +++ b/crates/brk_server/src/extended/transform_operation.rs @@ -21,14 +21,16 @@ pub trait TransformResponseExtended<'t> { fn deprecated(self) -> Self; /// 200 - fn ok_response(self) -> Self + fn json_response(self) -> Self where R: JsonSchema; /// 200 - fn ok_response_with(self, f: F) -> Self + fn json_response_with(self, f: F) -> Self where R: JsonSchema, F: FnOnce(TransformResponse<'_, R>) -> TransformResponse<'_, R>; + /// 200 with text/plain content type + fn text_response(self) -> Self; /// 200 with text/csv content type (adds CSV as alternative response format) fn csv_response(self) -> Self; /// 400 @@ -82,11 +84,11 @@ impl<'t> TransformResponseExtended<'t> for TransformOperation<'t> { self.tag("Metrics") } - fn ok_response(self) -> Self + fn json_response(self) -> Self where R: JsonSchema, { - self.ok_response_with(|r: TransformResponse<'_, R>| r) + self.json_response_with(|r: TransformResponse<'_, R>| r) } fn deprecated(mut self) -> Self { @@ -94,7 +96,7 @@ impl<'t> TransformResponseExtended<'t> for TransformOperation<'t> { self } - fn ok_response_with(self, f: F) -> Self + fn json_response_with(self, f: F) -> Self where R: JsonSchema, F: FnOnce(TransformResponse<'_, R>) -> TransformResponse<'_, R>, @@ -102,6 +104,10 @@ impl<'t> TransformResponseExtended<'t> for TransformOperation<'t> { self.response_with::<200, Json, _>(|res| f(res.description("Successful response"))) } + fn text_response(self) -> Self { + self.response_with::<200, String, _>(|res| res.description("Successful response")) + } + fn csv_response(mut self) -> Self { // Add text/csv content type to existing 200 response if let Some(responses) = &mut self.inner_mut().responses diff --git a/modules/brk-client/index.js b/modules/brk-client/index.js index a3702bbe9..405c30ef2 100644 --- a/modules/brk-client/index.js +++ b/modules/brk-client/index.js @@ -521,11 +521,6 @@ * @typedef {Object} HeightParam * @property {Height} height */ -/** - * Hex-encoded string - * - * @typedef {string} Hex - */ /** * Highest price value for a time period * @@ -9521,7 +9516,7 @@ class BrkClient extends BrkClientBase { * Endpoint: `GET /api/block-height/{height}` * * @param {Height} height - * @returns {Promise} + * @returns {Promise<*>} */ async getBlockByHeight(height) { return this.getJson(`/api/block-height/${height}`); @@ -9553,7 +9548,7 @@ class BrkClient extends BrkClientBase { * Endpoint: `GET /api/block/{hash}/header` * * @param {BlockHash} hash - * @returns {Promise} + * @returns {Promise<*>} */ async getBlockHeader(hash) { return this.getJson(`/api/block/${hash}/header`); @@ -9602,7 +9597,7 @@ class BrkClient extends BrkClientBase { * * @param {BlockHash} hash - Bitcoin block hash * @param {TxIndex} index - Transaction index within the block (0-based) - * @returns {Promise} + * @returns {Promise<*>} */ async getBlockTxid(hash, index) { return this.getJson(`/api/block/${hash}/txid/${index}`); @@ -9624,6 +9619,22 @@ class BrkClient extends BrkClientBase { return this.getJson(`/api/block/${hash}/txids`); } + /** + * Block transactions + * + * Retrieve transactions in a block by block hash. Returns up to 25 transactions starting from index 0. + * + * *[Mempool.space docs](https://mempool.space/docs/api/rest#get-block-transactions)* + * + * Endpoint: `GET /api/block/{hash}/txs` + * + * @param {BlockHash} hash + * @returns {Promise} + */ + async getBlockTxs(hash) { + return this.getJson(`/api/block/${hash}/txs`); + } + /** * Block transactions (paginated) * @@ -9637,7 +9648,7 @@ class BrkClient extends BrkClientBase { * @param {TxIndex} start_index - Starting transaction index within the block (0-based) * @returns {Promise} */ - async getBlockTxs(hash, start_index) { + async getBlockTxsFromIndex(hash, start_index) { return this.getJson(`/api/block/${hash}/txs/${start_index}`); } @@ -9663,7 +9674,7 @@ class BrkClient extends BrkClientBase { * *[Mempool.space docs](https://mempool.space/docs/api/rest#get-block-tip-hash)* * * Endpoint: `GET /api/blocks/tip/hash` - * @returns {Promise} + * @returns {Promise<*>} */ async getBlockTipHash() { return this.getJson(`/api/blocks/tip/hash`); @@ -9677,7 +9688,7 @@ class BrkClient extends BrkClientBase { * *[Mempool.space docs](https://mempool.space/docs/api/rest#get-block-tip-height)* * * Endpoint: `GET /api/blocks/tip/height` - * @returns {Promise} + * @returns {Promise<*>} */ async getBlockTipHeight() { return this.getJson(`/api/blocks/tip/height`); @@ -10079,7 +10090,7 @@ class BrkClient extends BrkClientBase { * Endpoint: `GET /api/tx/{txid}/hex` * * @param {Txid} txid - * @returns {Promise} + * @returns {Promise<*>} */ async getTxHex(txid) { return this.getJson(`/api/tx/${txid}/hex`); @@ -10111,7 +10122,7 @@ class BrkClient extends BrkClientBase { * Endpoint: `GET /api/tx/{txid}/merkleblock-proof` * * @param {Txid} txid - * @returns {Promise} + * @returns {Promise<*>} */ async getTxMerkleblockProof(txid) { return this.getJson(`/api/tx/${txid}/merkleblock-proof`); diff --git a/packages/brk_client/brk_client/__init__.py b/packages/brk_client/brk_client/__init__.py index 9ef0f85b2..b2801ff46 100644 --- a/packages/brk_client/brk_client/__init__.py +++ b/packages/brk_client/brk_client/__init__.py @@ -112,8 +112,6 @@ Epoch = int ExchangeRates = dict FundedAddrIndex = TypeIndex Halving = int -# Hex-encoded string -Hex = str # Highest price value for a time period High = Dollars Hour1 = int @@ -7067,7 +7065,7 @@ class BrkClient(BrkClientBase): Compact OpenAPI specification optimized for LLM consumption. Removes redundant fields while preserving essential API information. Full spec available at `/openapi.json`. Endpoint: `GET /api.json`""" - return self.get_json('/api.json') + return self.get_text('/api.json') def get_address(self, address: Addr) -> AddrStats: """Address information. @@ -7127,7 +7125,7 @@ class BrkClient(BrkClientBase): Endpoint: `GET /api/address/{address}/utxo`""" return self.get_json(f'/api/address/{address}/utxo') - def get_block_by_height(self, height: Height) -> BlockHash: + def get_block_by_height(self, height: Height) -> Any: """Block hash by height. Retrieve the block hash at a given height. Returns the hash as plain text. @@ -7135,7 +7133,7 @@ class BrkClient(BrkClientBase): *[Mempool.space docs](https://mempool.space/docs/api/rest#get-block-height)* Endpoint: `GET /api/block-height/{height}`""" - return self.get_json(f'/api/block-height/{height}') + return self.get_text(f'/api/block-height/{height}') def get_block(self, hash: BlockHash) -> BlockInfo: """Block information. @@ -7147,7 +7145,7 @@ class BrkClient(BrkClientBase): Endpoint: `GET /api/block/{hash}`""" return self.get_json(f'/api/block/{hash}') - def get_block_header(self, hash: BlockHash) -> Hex: + def get_block_header(self, hash: BlockHash) -> Any: """Block header. Returns the hex-encoded block header. @@ -7155,7 +7153,7 @@ class BrkClient(BrkClientBase): *[Mempool.space docs](https://mempool.space/docs/api/rest#get-block-header)* Endpoint: `GET /api/block/{hash}/header`""" - return self.get_json(f'/api/block/{hash}/header') + return self.get_text(f'/api/block/{hash}/header') def get_block_raw(self, hash: BlockHash) -> List[float]: """Raw block. @@ -7177,7 +7175,7 @@ class BrkClient(BrkClientBase): Endpoint: `GET /api/block/{hash}/status`""" return self.get_json(f'/api/block/{hash}/status') - def get_block_txid(self, hash: BlockHash, index: TxIndex) -> Txid: + def get_block_txid(self, hash: BlockHash, index: TxIndex) -> Any: """Transaction ID at index. Retrieve a single transaction ID at a specific index within a block. Returns plain text txid. @@ -7185,7 +7183,7 @@ class BrkClient(BrkClientBase): *[Mempool.space docs](https://mempool.space/docs/api/rest#get-block-transaction-id)* Endpoint: `GET /api/block/{hash}/txid/{index}`""" - return self.get_json(f'/api/block/{hash}/txid/{index}') + return self.get_text(f'/api/block/{hash}/txid/{index}') def get_block_txids(self, hash: BlockHash) -> List[Txid]: """Block transaction IDs. @@ -7197,7 +7195,17 @@ class BrkClient(BrkClientBase): Endpoint: `GET /api/block/{hash}/txids`""" return self.get_json(f'/api/block/{hash}/txids') - def get_block_txs(self, hash: BlockHash, start_index: TxIndex) -> List[Transaction]: + def get_block_txs(self, hash: BlockHash) -> List[Transaction]: + """Block transactions. + + Retrieve transactions in a block by block hash. Returns up to 25 transactions starting from index 0. + + *[Mempool.space docs](https://mempool.space/docs/api/rest#get-block-transactions)* + + Endpoint: `GET /api/block/{hash}/txs`""" + return self.get_json(f'/api/block/{hash}/txs') + + def get_block_txs_from_index(self, hash: BlockHash, start_index: TxIndex) -> List[Transaction]: """Block transactions (paginated). Retrieve transactions in a block by block hash, starting from the specified index. Returns up to 25 transactions at a time. @@ -7217,7 +7225,7 @@ class BrkClient(BrkClientBase): Endpoint: `GET /api/blocks`""" return self.get_json('/api/blocks') - def get_block_tip_hash(self) -> BlockHash: + def get_block_tip_hash(self) -> Any: """Block tip hash. Returns the hash of the last block. @@ -7225,9 +7233,9 @@ class BrkClient(BrkClientBase): *[Mempool.space docs](https://mempool.space/docs/api/rest#get-block-tip-hash)* Endpoint: `GET /api/blocks/tip/hash`""" - return self.get_json('/api/blocks/tip/hash') + return self.get_text('/api/blocks/tip/hash') - def get_block_tip_height(self) -> Height: + def get_block_tip_height(self) -> Any: """Block tip height. Returns the height of the last block. @@ -7235,7 +7243,7 @@ class BrkClient(BrkClientBase): *[Mempool.space docs](https://mempool.space/docs/api/rest#get-block-tip-height)* Endpoint: `GET /api/blocks/tip/height`""" - return self.get_json('/api/blocks/tip/height') + return self.get_text('/api/blocks/tip/height') def get_blocks_from_height(self, height: Height) -> List[BlockInfo]: """Blocks from height. @@ -7435,7 +7443,7 @@ class BrkClient(BrkClientBase): Returns the single most recent value for a series, unwrapped (not inside a SeriesData object). Endpoint: `GET /api/series/{series}/{index}/latest`""" - return self.get_json(f'/api/series/{series}/{index}/latest') + return self.get_text(f'/api/series/{series}/{index}/latest') def get_series_len(self, series: SeriesName, index: Index) -> float: """Get series data length. @@ -7479,7 +7487,7 @@ class BrkClient(BrkClientBase): Endpoint: `GET /api/tx/{txid}`""" return self.get_json(f'/api/tx/{txid}') - def get_tx_hex(self, txid: Txid) -> Hex: + def get_tx_hex(self, txid: Txid) -> Any: """Transaction hex. Retrieve the raw transaction as a hex-encoded string. Returns the serialized transaction in hexadecimal format. @@ -7487,7 +7495,7 @@ class BrkClient(BrkClientBase): *[Mempool.space docs](https://mempool.space/docs/api/rest#get-transaction-hex)* Endpoint: `GET /api/tx/{txid}/hex`""" - return self.get_json(f'/api/tx/{txid}/hex') + return self.get_text(f'/api/tx/{txid}/hex') def get_tx_merkle_proof(self, txid: Txid) -> MerkleProof: """Transaction merkle proof. @@ -7499,7 +7507,7 @@ class BrkClient(BrkClientBase): Endpoint: `GET /api/tx/{txid}/merkle-proof`""" return self.get_json(f'/api/tx/{txid}/merkle-proof') - def get_tx_merkleblock_proof(self, txid: Txid) -> str: + def get_tx_merkleblock_proof(self, txid: Txid) -> Any: """Transaction merkleblock proof. Get the merkleblock proof for a transaction (BIP37 format, hex encoded). @@ -7507,7 +7515,7 @@ class BrkClient(BrkClientBase): *[Mempool.space docs](https://mempool.space/docs/api/rest#get-transaction-merkleblock-proof)* Endpoint: `GET /api/tx/{txid}/merkleblock-proof`""" - return self.get_json(f'/api/tx/{txid}/merkleblock-proof') + return self.get_text(f'/api/tx/{txid}/merkleblock-proof') def get_tx_outspend(self, txid: Txid, vout: Vout) -> TxOutspend: """Output spend status. @@ -7651,7 +7659,7 @@ class BrkClient(BrkClientBase): *[Mempool.space docs](https://mempool.space/docs/api/rest#get-block-feerates)* Endpoint: `GET /api/v1/mining/blocks/fee-rates/{time_period}`""" - return self.get_json(f'/api/v1/mining/blocks/fee-rates/{time_period}') + return self.get_text(f'/api/v1/mining/blocks/fee-rates/{time_period}') def get_block_fees(self, time_period: TimePeriod) -> List[BlockFeesEntry]: """Block fees. @@ -7867,7 +7875,7 @@ class BrkClient(BrkClientBase): Full OpenAPI 3.1 specification for this API. Endpoint: `GET /openapi.json`""" - return self.get_json('/openapi.json') + return self.get_text('/openapi.json') def get_version(self) -> str: """API version. diff --git a/packages/brk_client/uv.lock b/packages/brk_client/uv.lock index 19024f5a7..e95e9d27e 100644 --- a/packages/brk_client/uv.lock +++ b/packages/brk_client/uv.lock @@ -50,7 +50,7 @@ wheels = [ [[package]] name = "brk-client" -version = "0.2.2" +version = "0.2.5" source = { editable = "." } [package.dev-dependencies]