global: snapshot

This commit is contained in:
nym21
2026-03-11 13:43:46 +01:00
parent c5d63b3090
commit 984122f394
82 changed files with 3962 additions and 3412 deletions

View File

@@ -7,7 +7,9 @@ use axum::{
http::{HeaderMap, StatusCode, Uri},
response::{IntoResponse, Response},
};
use brk_types::{Format, MetricSelection, Output};
use brk_error::Result as BrkResult;
use brk_query::{Query as BrkQuery, ResolvedQuery};
use brk_types::{Format, MetricOutput, MetricSelection, Output};
use crate::{
Result,
@@ -20,9 +22,30 @@ use super::AppState;
pub async fn handler(
uri: Uri,
headers: HeaderMap,
Extension(addr): Extension<SocketAddr>,
addr: Extension<SocketAddr>,
Query(params): Query<MetricSelection>,
State(state): State<AppState>,
state: State<AppState>,
) -> Result<Response> {
format_and_respond(uri, headers, addr, params, state, |q, r| q.format(r)).await
}
pub async fn raw_handler(
uri: Uri,
headers: HeaderMap,
addr: Extension<SocketAddr>,
Query(params): Query<MetricSelection>,
state: State<AppState>,
) -> Result<Response> {
format_and_respond(uri, headers, addr, params, state, |q, r| q.format_raw(r)).await
}
async fn format_and_respond(
uri: Uri,
headers: HeaderMap,
Extension(addr): Extension<SocketAddr>,
params: MetricSelection,
state: State<AppState>,
formatter: fn(&BrkQuery, ResolvedQuery) -> BrkResult<MetricOutput>,
) -> Result<Response> {
// Phase 1: Search and resolve metadata (cheap)
let resolved = state
@@ -51,7 +74,7 @@ pub async fn handler(
.get_or_insert(&cache_key, async move {
query
.run(move |q| {
let out = q.format(resolved)?;
let out = formatter(q, resolved)?;
let raw = match out.output {
Output::CSV(s) => Bytes::from(s),
Output::Json(v) => Bytes::from(v),

View File

@@ -10,8 +10,9 @@ use axum::{
use brk_traversable::TreeNode;
use brk_types::{
CostBasisCohortParam, CostBasisFormatted, CostBasisParams, CostBasisQuery, DataRangeFormat,
Date, Index, IndexInfo, LimitParam, Metric, MetricCount, MetricData, MetricParam,
Date, Index, IndexInfo, Metric, MetricCount, MetricData, MetricInfo, MetricParam,
MetricSelection, MetricSelectionLegacy, MetricWithIndex, Metrics, PaginatedMetrics, Pagination,
SearchQuery,
};
use crate::{CacheStrategy, Error, extended::TransformResponseExtended};
@@ -124,16 +125,15 @@ impl ApiMetricsRoutes for ApiRouter<AppState> {
),
)
.api_route(
"/api/metrics/search/{metric}",
"/api/metrics/search",
get_with(
async |
uri: Uri,
headers: HeaderMap,
State(state): State<AppState>,
Path(path): Path<MetricParam>,
Query(query): Query<LimitParam>
Query(query): Query<SearchQuery>
| {
state.cached_json(&headers, CacheStrategy::Static, &uri, move |q| Ok(q.match_metric(&path.metric, query.limit))).await
state.cached_json(&headers, CacheStrategy::Static, &uri, move |q| Ok(q.search_metrics(&query))).await
},
|op| op
.id("search_metrics")
@@ -155,26 +155,80 @@ impl ApiMetricsRoutes for ApiRouter<AppState> {
Path(path): Path<MetricParam>
| {
state.cached_json(&headers, CacheStrategy::Static, &uri, move |q| {
if let Some(indexes) = q.metric_to_indexes(path.metric.clone()) {
return Ok(indexes.clone())
}
Err(q.metric_not_found_error(&path.metric))
q.metric_info(&path.metric).ok_or_else(|| q.metric_not_found_error(&path.metric))
}).await
},
|op| op
.id("get_metric_info")
.metrics_tag()
.summary("Get supported indexes for a metric")
.summary("Get metric info")
.description(
"Returns the list of indexes supported by the specified metric. \
For example, `realized_price` might be available on day1, week1, and month1."
"Returns the supported indexes and value type for the specified metric."
)
.ok_response::<Vec<Index>>()
.ok_response::<MetricInfo>()
.not_modified()
.not_found()
.server_error(),
),
)
.api_route(
"/api/metric/{metric}/{index}/latest",
get_with(
async |uri: Uri,
headers: HeaderMap,
State(state): State<AppState>,
Path(path): Path<MetricWithIndex>| {
state
.cached_json(&headers, CacheStrategy::Height, &uri, move |q| {
q.latest(&path.metric, path.index)
})
.await
},
|op| op
.id("get_metric_latest")
.metrics_tag()
.summary("Get latest metric value")
.description(
"Returns the single most recent value for a metric, unwrapped (not inside a MetricData object)."
)
.ok_response::<serde_json::Value>()
.not_found(),
),
)
.api_route(
"/api/metric/{metric}/{index}/data",
get_with(
async |uri: Uri,
headers: HeaderMap,
addr: Extension<SocketAddr>,
state: State<AppState>,
Path(path): Path<MetricWithIndex>,
Query(range): Query<DataRangeFormat>|
-> Response {
data::raw_handler(
uri,
headers,
addr,
Query(MetricSelection::from((path.index, path.metric, range))),
state,
)
.await
.into_response()
},
|op| op
.id("get_metric_data")
.metrics_tag()
.summary("Get raw metric data")
.description(
"Returns just the data array without the MetricData wrapper. \
Supports the same range and format parameters as the standard endpoint."
)
.ok_response::<Vec<serde_json::Value>>()
.csv_response()
.not_modified()
.not_found(),
),
)
.api_route(
"/api/metric/{metric}/{index}",
get_with(

View File

@@ -35,8 +35,9 @@ pub fn create_openapi() -> OpenApi {
```bash
curl -s https://bitview.space/api/block-height/0
curl -s https://bitview.space/api/metrics/search/price
curl -s https://bitview.space/api/metric/price/day1
curl -s https://bitview.space/api/metrics/search?q=price
curl -s https://bitview.space/api/metric/price/day
curl -s https://bitview.space/api/metric/price/day/latest
```
### Errors

View File

@@ -57,6 +57,7 @@ fn error_status(e: &BrkError) -> StatusCode {
BrkError::UnknownAddress
| BrkError::UnknownTxid
| BrkError::NotFound(_)
| BrkError::NoData
| BrkError::MetricNotFound(_) => StatusCode::NOT_FOUND,
BrkError::AuthFailed => StatusCode::FORBIDDEN,
@@ -79,6 +80,7 @@ fn error_code(e: &BrkError) -> &'static str {
BrkError::UnknownAddress => "unknown_address",
BrkError::UnknownTxid => "unknown_txid",
BrkError::NotFound(_) => "not_found",
BrkError::NoData => "no_data",
BrkError::MetricNotFound(_) => "metric_not_found",
BrkError::MempoolNotAvailable => "mempool_not_available",
BrkError::AuthFailed => "auth_failed",