clients: snapshot

This commit is contained in:
nym21
2026-01-14 01:20:25 +01:00
parent 3a836ab0f4
commit 25a0ebe51e
10 changed files with 7218 additions and 3920 deletions

View File

@@ -120,6 +120,9 @@ pub struct PatternBaseResult {
/// Whether an outlier child was excluded to find the pattern.
/// If true, pattern factory should not be used.
pub has_outlier: bool,
/// Whether this instance uses suffix mode (common prefix) or prefix mode (common suffix).
/// Used to check compatibility with the pattern's mode.
pub is_suffix_mode: bool,
}
/// Get the metric base for a pattern instance by analyzing direct children.
@@ -137,12 +140,17 @@ pub fn get_pattern_instance_base(node: &TreeNode) -> PatternBaseResult {
return PatternBaseResult {
base: String::new(),
has_outlier: false,
is_suffix_mode: true, // default
};
}
// Try to find common base from leaf names
if let Some((base, has_outlier)) = try_find_base(&child_names, false) {
return PatternBaseResult { base, has_outlier };
if let Some(result) = try_find_base(&child_names, false) {
return PatternBaseResult {
base: result.base,
has_outlier: result.has_outlier,
is_suffix_mode: result.is_suffix_mode,
};
}
// If no common pattern found and we have enough children, try excluding outliers
@@ -155,10 +163,11 @@ pub fn get_pattern_instance_base(node: &TreeNode) -> PatternBaseResult {
.map(|(_, v)| v.clone())
.collect();
if let Some((base, _)) = try_find_base(&filtered, true) {
if let Some(result) = try_find_base(&filtered, true) {
return PatternBaseResult {
base,
base: result.base,
has_outlier: true,
is_suffix_mode: result.is_suffix_mode,
};
}
}
@@ -169,24 +178,40 @@ pub fn get_pattern_instance_base(node: &TreeNode) -> PatternBaseResult {
PatternBaseResult {
base: String::new(),
has_outlier: false,
is_suffix_mode: true, // default
}
}
/// Result of try_find_base: base name, has_outlier flag, and is_suffix_mode flag.
struct FindBaseResult {
base: String,
has_outlier: bool,
is_suffix_mode: bool,
}
/// Try to find a common base from child names using prefix/suffix detection.
/// Returns Some((base, has_outlier)) if found.
fn try_find_base(child_names: &[(String, String)], is_outlier_attempt: bool) -> Option<(String, bool)> {
/// Returns Some(FindBaseResult) if found.
fn try_find_base(child_names: &[(String, String)], is_outlier_attempt: bool) -> Option<FindBaseResult> {
let leaf_names: Vec<&str> = child_names.iter().map(|(_, n)| n.as_str()).collect();
// Try common prefix first (suffix mode)
if let Some(prefix) = find_common_prefix(&leaf_names) {
let base = prefix.trim_end_matches('_').to_string();
return Some((base, is_outlier_attempt));
return Some(FindBaseResult {
base,
has_outlier: is_outlier_attempt,
is_suffix_mode: true,
});
}
// Try common suffix (prefix mode)
if let Some(suffix) = find_common_suffix(&leaf_names) {
let base = suffix.trim_start_matches('_').to_string();
return Some((base, is_outlier_attempt));
return Some(FindBaseResult {
base,
has_outlier: is_outlier_attempt,
is_suffix_mode: false,
});
}
None
@@ -409,4 +434,64 @@ mod tests {
assert_eq!(result.base, "sopr");
assert!(result.has_outlier); // Pattern factory should NOT be used (inline instead)
}
#[test]
fn test_get_pattern_instance_base_suffix_mode_price_ago() {
// Simulates price_ago pattern: price_1d_ago, price_1w_ago, price_10y_ago
// Common prefix is "price_", so this is suffix mode
let tree = make_branch(vec![
("_1d", make_leaf("price_1d_ago")),
("_1w", make_leaf("price_1w_ago")),
("_1m", make_leaf("price_1m_ago")),
("_10y", make_leaf("price_10y_ago")),
]);
let result = get_pattern_instance_base(&tree);
assert_eq!(result.base, "price");
assert!(result.is_suffix_mode); // Suffix mode: _m(base, "1d_ago")
assert!(!result.has_outlier);
}
#[test]
fn test_get_pattern_instance_base_prefix_mode_price_returns() {
// Simulates price_returns pattern: 1d_price_returns, 1w_price_returns, 10y_price_returns
// Common suffix is "_price_returns", so this is prefix mode
let tree = make_branch(vec![
("_1d", make_leaf("1d_price_returns")),
("_1w", make_leaf("1w_price_returns")),
("_1m", make_leaf("1m_price_returns")),
("_10y", make_leaf("10y_price_returns")),
]);
let result = get_pattern_instance_base(&tree);
assert_eq!(result.base, "price_returns");
assert!(!result.is_suffix_mode); // Prefix mode: _p("1d_", base)
assert!(!result.has_outlier);
}
#[test]
fn test_mode_detection_distinguishes_similar_structures() {
// Two patterns with identical structure but different naming conventions
// should have different modes detected
// Suffix mode pattern
let suffix_tree = make_branch(vec![
("_1y", make_leaf("lump_sum_1y")),
("_2y", make_leaf("lump_sum_2y")),
("_5y", make_leaf("lump_sum_5y")),
]);
let suffix_result = get_pattern_instance_base(&suffix_tree);
assert_eq!(suffix_result.base, "lump_sum");
assert!(suffix_result.is_suffix_mode);
// Prefix mode pattern (same structure, different naming)
let prefix_tree = make_branch(vec![
("_1y", make_leaf("1y_returns")),
("_2y", make_leaf("2y_returns")),
("_5y", make_leaf("5y_returns")),
]);
let prefix_result = get_pattern_instance_base(&prefix_tree);
assert_eq!(prefix_result.base, "returns");
assert!(!prefix_result.is_suffix_mode);
}
}

View File

@@ -55,12 +55,17 @@ pub fn prepare_tree_node<'a>(
.map(|(f, _)| f.clone())
.collect();
// Skip if this matches a parameterizable pattern AND has no outlier
// Skip if this matches a parameterizable pattern AND has no outlier AND mode matches
let base_result = get_pattern_instance_base(node);
let mode_matches = pattern_lookup
.get(&fields)
.and_then(|name| metadata.find_pattern(name))
.is_none_or(|p| p.is_suffix_mode() == base_result.is_suffix_mode);
if let Some(pattern_name) = pattern_lookup.get(&fields)
&& pattern_name != name
&& metadata.is_parameterizable(pattern_name)
&& !base_result.has_outlier
&& mode_matches
{
return None;
}
@@ -84,9 +89,16 @@ pub fn prepare_tree_node<'a>(
.as_ref()
.is_some_and(|cf| metadata.matches_pattern(cf));
// Check if the pattern mode matches the instance mode
let mode_matches = child_fields
.as_ref()
.and_then(|cf| metadata.find_pattern_by_fields(cf))
.is_none_or(|p| p.is_suffix_mode() == base_result.is_suffix_mode);
// should_inline determines if we generate an inline struct type
// We inline only if it's a branch AND doesn't match any pattern
let should_inline = !is_leaf && !matches_any_pattern;
// We inline if: it's a branch AND (doesn't match any pattern OR mode doesn't match OR has outlier)
let should_inline =
!is_leaf && (!matches_any_pattern || !mode_matches || base_result.has_outlier);
// Inline type name (only used when should_inline is true)
let inline_type_name = if should_inline {

View File

@@ -6,9 +6,8 @@ use std::fmt::Write;
use brk_types::TreeNode;
use crate::{
ClientMetadata, Endpoint, GenericSyntax, JavaScriptSyntax, PatternField,
generate_leaf_field, get_first_leaf_name, get_node_fields, get_pattern_instance_base,
infer_accumulated_name, prepare_tree_node, to_camel_case,
ClientMetadata, Endpoint, GenericSyntax, JavaScriptSyntax, PatternField, generate_leaf_field,
prepare_tree_node, to_camel_case,
};
use super::api::generate_api_methods;
@@ -121,15 +120,36 @@ pub fn generate_main_client(
writeln!(output, " */").unwrap();
writeln!(output, " _buildTree(basePath) {{").unwrap();
writeln!(output, " return {{").unwrap();
generate_tree_initializer(output, catalog, "", 3, &pattern_lookup, metadata);
let mut generated = HashSet::new();
generate_tree_initializer(
output,
catalog,
"MetricsTree",
3,
&pattern_lookup,
metadata,
&mut generated,
);
writeln!(output, " }};").unwrap();
writeln!(output, " }}\n").unwrap();
writeln!(output, " /**").unwrap();
writeln!(output, " * Create a dynamic metric endpoint builder for any metric/index combination.").unwrap();
writeln!(
output,
" * Create a dynamic metric endpoint builder for any metric/index combination."
)
.unwrap();
writeln!(output, " *").unwrap();
writeln!(output, " * Use this for programmatic access when the metric name is determined at runtime.").unwrap();
writeln!(output, " * For type-safe access, use the `metrics` tree instead.").unwrap();
writeln!(
output,
" * Use this for programmatic access when the metric name is determined at runtime."
)
.unwrap();
writeln!(
output,
" * For type-safe access, use the `metrics` tree instead."
)
.unwrap();
writeln!(output, " *").unwrap();
writeln!(output, " * @param {{string}} metric - The metric name").unwrap();
writeln!(output, " * @param {{Index}} index - The index name").unwrap();
@@ -149,66 +169,55 @@ pub fn generate_main_client(
fn generate_tree_initializer(
output: &mut String,
node: &TreeNode,
accumulated_name: &str,
name: &str,
indent: usize,
pattern_lookup: &std::collections::HashMap<Vec<PatternField>, String>,
metadata: &ClientMetadata,
generated: &mut HashSet<String>,
) {
let indent_str = " ".repeat(indent);
let Some(ctx) = prepare_tree_node(node, name, pattern_lookup, metadata, generated) else {
return;
};
let syntax = JavaScriptSyntax;
if let TreeNode::Branch(children) = node {
for (child_name, child_node) in children.iter() {
match child_node {
TreeNode::Leaf(leaf) => {
// Use shared helper for leaf fields
generate_leaf_field(
output,
&syntax,
"this",
child_name,
leaf,
metadata,
&indent_str,
);
}
TreeNode::Branch(grandchildren) => {
let field_name = to_camel_case(child_name);
let child_fields = get_node_fields(grandchildren, pattern_lookup);
// Use pattern factory if ANY pattern matches (not just parameterizable)
let pattern_name = pattern_lookup.get(&child_fields);
for child in &ctx.children {
let field_name = to_camel_case(child.name);
let base_result = get_pattern_instance_base(child_node);
// Use pattern factory only if no outlier was detected
if let Some(pattern_name) = pattern_name.filter(|_| !base_result.has_outlier) {
writeln!(
output,
"{}{}: create{}(this, '{}'),",
indent_str, field_name, pattern_name, base_result.base
)
.unwrap();
} else {
let child_acc =
infer_child_accumulated_name(child_node, accumulated_name, child_name);
writeln!(output, "{}{}: {{", indent_str, field_name).unwrap();
generate_tree_initializer(
output,
child_node,
&child_acc,
indent + 1,
pattern_lookup,
metadata,
);
writeln!(output, "{}}},", indent_str).unwrap();
}
}
if child.is_leaf {
if let TreeNode::Leaf(leaf) = child.node {
generate_leaf_field(
output,
&syntax,
"this",
child.name,
leaf,
metadata,
&indent_str,
);
}
} else if child.should_inline {
// Inline object
writeln!(output, "{}{}: {{", indent_str, field_name).unwrap();
generate_tree_initializer(
output,
child.node,
&child.inline_type_name,
indent + 1,
pattern_lookup,
metadata,
generated,
);
writeln!(output, "{}}},", indent_str).unwrap();
} else {
// Use pattern factory
writeln!(
output,
"{}{}: create{}(this, '{}'),",
indent_str, field_name, child.field.rust_type, child.base_result.base
)
.unwrap();
}
}
}
fn infer_child_accumulated_name(node: &TreeNode, parent_acc: &str, field_name: &str) -> String {
let leaf_name = get_first_leaf_name(node).unwrap_or_default();
infer_accumulated_name(parent_acc, field_name, &leaf_name)
}

View File

@@ -392,7 +392,12 @@ pub fn generate_index_accessors(output: &mut String, patterns: &[IndexSetPattern
writeln!(output, "impl<T: DeserializeOwned> {}<T> {{", by_name).unwrap();
for index in &pattern.indexes {
let method_name = index_to_field_name(index);
writeln!(output, " pub fn {}(&self) -> MetricEndpointBuilder<T> {{", method_name).unwrap();
writeln!(
output,
" pub fn {}(&self) -> MetricEndpointBuilder<T> {{",
method_name
)
.unwrap();
writeln!(
output,
" MetricEndpointBuilder::new(self.client.clone(), self.name.clone(), Index::{})",
@@ -425,7 +430,12 @@ pub fn generate_index_accessors(output: &mut String, patterns: &[IndexSetPattern
writeln!(output, " let name: Arc<str> = name.into();").unwrap();
writeln!(output, " Self {{").unwrap();
writeln!(output, " name: name.clone(),").unwrap();
writeln!(output, " by: {} {{ client, name, _marker: std::marker::PhantomData }}", by_name).unwrap();
writeln!(
output,
" by: {} {{ client, name, _marker: std::marker::PhantomData }}",
by_name
)
.unwrap();
writeln!(output, " }}").unwrap();
writeln!(output, " }}").unwrap();
writeln!(output).unwrap();
@@ -436,7 +446,12 @@ pub fn generate_index_accessors(output: &mut String, patterns: &[IndexSetPattern
writeln!(output, "}}\n").unwrap();
// Implement AnyMetricPattern trait
writeln!(output, "impl<T> AnyMetricPattern for {}<T> {{", pattern.name).unwrap();
writeln!(
output,
"impl<T> AnyMetricPattern for {}<T> {{",
pattern.name
)
.unwrap();
writeln!(output, " fn name(&self) -> &str {{").unwrap();
writeln!(output, " &self.name").unwrap();
writeln!(output, " }}").unwrap();
@@ -451,12 +466,26 @@ pub fn generate_index_accessors(output: &mut String, patterns: &[IndexSetPattern
writeln!(output, "}}\n").unwrap();
// Implement MetricPattern<T> trait
writeln!(output, "impl<T: DeserializeOwned> MetricPattern<T> for {}<T> {{", pattern.name).unwrap();
writeln!(output, " fn get(&self, index: Index) -> Option<MetricEndpointBuilder<T>> {{").unwrap();
writeln!(
output,
"impl<T: DeserializeOwned> MetricPattern<T> for {}<T> {{",
pattern.name
)
.unwrap();
writeln!(
output,
" fn get(&self, index: Index) -> Option<MetricEndpointBuilder<T>> {{"
)
.unwrap();
writeln!(output, " match index {{").unwrap();
for index in &pattern.indexes {
let method_name = index_to_field_name(index);
writeln!(output, " Index::{} => Some(self.by.{}()),", index, method_name).unwrap();
writeln!(
output,
" Index::{} => Some(self.by.{}()),",
index, method_name
)
.unwrap();
}
writeln!(output, " _ => None,").unwrap();
writeln!(output, " }}").unwrap();
@@ -486,8 +515,12 @@ pub fn generate_pattern_structs(
for field in &pattern.fields {
let field_name = to_snake_case(&field.name);
let type_annotation =
metadata.field_type_annotation(field, pattern.is_generic, None, GenericSyntax::RUST);
let type_annotation = metadata.field_type_annotation(
field,
pattern.is_generic,
None,
GenericSyntax::RUST,
);
writeln!(output, " pub {}: {},", field_name, type_annotation).unwrap();
}

View File

@@ -95,6 +95,14 @@ impl ClientMetadata {
|| self.structural_patterns.iter().any(|p| p.fields == fields)
}
/// Find a pattern by its fields.
pub fn find_pattern_by_fields(&self, fields: &[PatternField]) -> Option<&StructuralPattern> {
self.concrete_to_pattern
.get(fields)
.and_then(|name| self.find_pattern(name))
.or_else(|| self.structural_patterns.iter().find(|p| p.fields == fields))
}
/// Resolve the type name for a tree field.
/// If the field matches ANY pattern (parameterizable or not), returns pattern type.
/// Otherwise returns the inline type name (parent_child format).

View File

@@ -52,40 +52,27 @@ fn main() -> brk_client::Result<()> {
println!("\nFound {} metrics", metrics.len());
let mut success = 0;
let mut failed = 0;
let mut errors: Vec<String> = Vec::new();
for metric in &metrics {
for index in &metric.indexes {
let index_str = index.serialize_long();
let full_path = format!("{}.by.{}", metric.path, index_str);
match client.get_metric(
metric.name.as_str().into(),
*index,
None,
Some(0),
None,
Some("-3"),
None,
) {
Ok(response) => {
let count = response.json().data.len();
if count != 3 {
failed += 1;
let error_msg = format!(
"FAIL: {}.by.{} -> expected 3, got {}",
metric.path, index_str, count
);
errors.push(error_msg.clone());
println!("{}", error_msg);
} else {
success += 1;
println!("OK: {}.by.{} -> {} items", metric.path, index_str, count);
}
Ok(_) => {
success += 1;
println!("OK: {}", full_path);
}
Err(e) => {
failed += 1;
let error_msg = format!("FAIL: {}.by.{} -> {}", metric.path, index_str, e);
errors.push(error_msg.clone());
println!("{}", error_msg);
println!("FAIL: {} -> {}", full_path, e);
return Err(e);
}
}
}
@@ -93,21 +80,6 @@ fn main() -> brk_client::Result<()> {
println!("\n=== Results ===");
println!("Success: {}", success);
println!("Failed: {}", failed);
if !errors.is_empty() {
println!("\nErrors:");
for err in errors.iter().take(10) {
println!(" {}", err);
}
if errors.len() > 10 {
println!(" ... and {} more", errors.len() - 10);
}
}
if failed > 0 {
std::process::exit(1);
}
Ok(())
}

File diff suppressed because it is too large Load Diff

View File

@@ -3155,7 +3155,7 @@ function createAaopoolPattern(client, acc) {
/**
* @template T
* @typedef {Object} PriceAgoPattern
* @typedef {Object} LookbackPattern
* @property {MetricPattern4<T>} _10y
* @property {MetricPattern4<T>} _1d
* @property {MetricPattern4<T>} _1m
@@ -3172,13 +3172,13 @@ function createAaopoolPattern(client, acc) {
*/
/**
* Create a PriceAgoPattern pattern node
* Create a LookbackPattern pattern node
* @template T
* @param {BrkClientBase} client
* @param {string} acc - Accumulated metric name
* @returns {PriceAgoPattern<T>}
* @returns {LookbackPattern<T>}
*/
function createPriceAgoPattern(client, acc) {
function createLookbackPattern(client, acc) {
return {
_10y: createMetricPattern4(client, _m(acc, "10y_ago")),
_1d: createMetricPattern4(client, _m(acc, "1d_ago")),
@@ -3338,17 +3338,17 @@ function createBitcoinPattern(client, acc) {
*/
function createClassAveragePricePattern(client, acc) {
return {
_2015: createMetricPattern4(client, _m(acc, "2015_returns")),
_2016: createMetricPattern4(client, _m(acc, "2016_returns")),
_2017: createMetricPattern4(client, _m(acc, "2017_returns")),
_2018: createMetricPattern4(client, _m(acc, "2018_returns")),
_2019: createMetricPattern4(client, _m(acc, "2019_returns")),
_2020: createMetricPattern4(client, _m(acc, "2020_returns")),
_2021: createMetricPattern4(client, _m(acc, "2021_returns")),
_2022: createMetricPattern4(client, _m(acc, "2022_returns")),
_2023: createMetricPattern4(client, _m(acc, "2023_returns")),
_2024: createMetricPattern4(client, _m(acc, "2024_returns")),
_2025: createMetricPattern4(client, _m(acc, "2025_returns")),
_2015: createMetricPattern4(client, _m(acc, "2015_average_price")),
_2016: createMetricPattern4(client, _m(acc, "2016_average_price")),
_2017: createMetricPattern4(client, _m(acc, "2017_average_price")),
_2018: createMetricPattern4(client, _m(acc, "2018_average_price")),
_2019: createMetricPattern4(client, _m(acc, "2019_average_price")),
_2020: createMetricPattern4(client, _m(acc, "2020_average_price")),
_2021: createMetricPattern4(client, _m(acc, "2021_average_price")),
_2022: createMetricPattern4(client, _m(acc, "2022_average_price")),
_2023: createMetricPattern4(client, _m(acc, "2023_average_price")),
_2024: createMetricPattern4(client, _m(acc, "2024_average_price")),
_2025: createMetricPattern4(client, _m(acc, "2025_average_price")),
};
}
@@ -3391,68 +3391,6 @@ function createDollarsPattern(client, acc) {
};
}
/**
* @typedef {Object} RelativePattern
* @property {MetricPattern1<StoredF32>} negUnrealizedLossRelToMarketCap
* @property {MetricPattern1<StoredF32>} netUnrealizedPnlRelToMarketCap
* @property {MetricPattern1<StoredF32>} nupl
* @property {MetricPattern1<StoredF64>} supplyInLossRelToCirculatingSupply
* @property {MetricPattern1<StoredF64>} supplyInLossRelToOwnSupply
* @property {MetricPattern1<StoredF64>} supplyInProfitRelToCirculatingSupply
* @property {MetricPattern1<StoredF64>} supplyInProfitRelToOwnSupply
* @property {MetricPattern4<StoredF64>} supplyRelToCirculatingSupply
* @property {MetricPattern1<StoredF32>} unrealizedLossRelToMarketCap
* @property {MetricPattern1<StoredF32>} unrealizedProfitRelToMarketCap
*/
/**
* Create a RelativePattern pattern node
* @param {BrkClientBase} client
* @param {string} acc - Accumulated metric name
* @returns {RelativePattern}
*/
function createRelativePattern(client, acc) {
return {
negUnrealizedLossRelToMarketCap: createMetricPattern1(
client,
_m(acc, "neg_unrealized_loss_rel_to_market_cap"),
),
netUnrealizedPnlRelToMarketCap: createMetricPattern1(
client,
_m(acc, "net_unrealized_pnl_rel_to_market_cap"),
),
nupl: createMetricPattern1(client, _m(acc, "nupl")),
supplyInLossRelToCirculatingSupply: createMetricPattern1(
client,
_m(acc, "supply_in_loss_rel_to_circulating_supply"),
),
supplyInLossRelToOwnSupply: createMetricPattern1(
client,
_m(acc, "supply_in_loss_rel_to_own_supply"),
),
supplyInProfitRelToCirculatingSupply: createMetricPattern1(
client,
_m(acc, "supply_in_profit_rel_to_circulating_supply"),
),
supplyInProfitRelToOwnSupply: createMetricPattern1(
client,
_m(acc, "supply_in_profit_rel_to_own_supply"),
),
supplyRelToCirculatingSupply: createMetricPattern4(
client,
_m(acc, "supply_rel_to_circulating_supply"),
),
unrealizedLossRelToMarketCap: createMetricPattern1(
client,
_m(acc, "unrealized_loss_rel_to_market_cap"),
),
unrealizedProfitRelToMarketCap: createMetricPattern1(
client,
_m(acc, "unrealized_profit_rel_to_market_cap"),
),
};
}
/**
* @typedef {Object} RelativePattern2
* @property {MetricPattern1<StoredF32>} negUnrealizedLossRelToOwnMarketCap
@@ -3518,6 +3456,68 @@ function createRelativePattern2(client, acc) {
};
}
/**
* @typedef {Object} RelativePattern
* @property {MetricPattern1<StoredF32>} negUnrealizedLossRelToMarketCap
* @property {MetricPattern1<StoredF32>} netUnrealizedPnlRelToMarketCap
* @property {MetricPattern1<StoredF32>} nupl
* @property {MetricPattern1<StoredF64>} supplyInLossRelToCirculatingSupply
* @property {MetricPattern1<StoredF64>} supplyInLossRelToOwnSupply
* @property {MetricPattern1<StoredF64>} supplyInProfitRelToCirculatingSupply
* @property {MetricPattern1<StoredF64>} supplyInProfitRelToOwnSupply
* @property {MetricPattern4<StoredF64>} supplyRelToCirculatingSupply
* @property {MetricPattern1<StoredF32>} unrealizedLossRelToMarketCap
* @property {MetricPattern1<StoredF32>} unrealizedProfitRelToMarketCap
*/
/**
* Create a RelativePattern pattern node
* @param {BrkClientBase} client
* @param {string} acc - Accumulated metric name
* @returns {RelativePattern}
*/
function createRelativePattern(client, acc) {
return {
negUnrealizedLossRelToMarketCap: createMetricPattern1(
client,
_m(acc, "neg_unrealized_loss_rel_to_market_cap"),
),
netUnrealizedPnlRelToMarketCap: createMetricPattern1(
client,
_m(acc, "net_unrealized_pnl_rel_to_market_cap"),
),
nupl: createMetricPattern1(client, _m(acc, "nupl")),
supplyInLossRelToCirculatingSupply: createMetricPattern1(
client,
_m(acc, "supply_in_loss_rel_to_circulating_supply"),
),
supplyInLossRelToOwnSupply: createMetricPattern1(
client,
_m(acc, "supply_in_loss_rel_to_own_supply"),
),
supplyInProfitRelToCirculatingSupply: createMetricPattern1(
client,
_m(acc, "supply_in_profit_rel_to_circulating_supply"),
),
supplyInProfitRelToOwnSupply: createMetricPattern1(
client,
_m(acc, "supply_in_profit_rel_to_own_supply"),
),
supplyRelToCirculatingSupply: createMetricPattern4(
client,
_m(acc, "supply_rel_to_circulating_supply"),
),
unrealizedLossRelToMarketCap: createMetricPattern1(
client,
_m(acc, "unrealized_loss_rel_to_market_cap"),
),
unrealizedProfitRelToMarketCap: createMetricPattern1(
client,
_m(acc, "unrealized_profit_rel_to_market_cap"),
),
};
}
/**
* @template T
* @typedef {Object} CountPattern2
@@ -3588,41 +3588,6 @@ function createAddrCountPattern(client, acc) {
};
}
/**
* @template T
* @typedef {Object} FeeRatePattern
* @property {MetricPattern1<T>} average
* @property {MetricPattern1<T>} max
* @property {MetricPattern11<T>} median
* @property {MetricPattern1<T>} min
* @property {MetricPattern11<T>} pct10
* @property {MetricPattern11<T>} pct25
* @property {MetricPattern11<T>} pct75
* @property {MetricPattern11<T>} pct90
* @property {MetricPattern27<T>} txindex
*/
/**
* Create a FeeRatePattern pattern node
* @template T
* @param {BrkClientBase} client
* @param {string} acc - Accumulated metric name
* @returns {FeeRatePattern<T>}
*/
function createFeeRatePattern(client, acc) {
return {
average: createMetricPattern1(client, _m(acc, "average")),
max: createMetricPattern1(client, _m(acc, "max")),
median: createMetricPattern11(client, _m(acc, "median")),
min: createMetricPattern1(client, _m(acc, "min")),
pct10: createMetricPattern11(client, _m(acc, "pct10")),
pct25: createMetricPattern11(client, _m(acc, "pct25")),
pct75: createMetricPattern11(client, _m(acc, "pct75")),
pct90: createMetricPattern11(client, _m(acc, "pct90")),
txindex: createMetricPattern27(client, acc),
};
}
/**
* @template T
* @typedef {Object} FullnessPattern
@@ -3658,6 +3623,41 @@ function createFullnessPattern(client, acc) {
};
}
/**
* @template T
* @typedef {Object} FeeRatePattern
* @property {MetricPattern1<T>} average
* @property {MetricPattern1<T>} max
* @property {MetricPattern11<T>} median
* @property {MetricPattern1<T>} min
* @property {MetricPattern11<T>} pct10
* @property {MetricPattern11<T>} pct25
* @property {MetricPattern11<T>} pct75
* @property {MetricPattern11<T>} pct90
* @property {MetricPattern27<T>} txindex
*/
/**
* Create a FeeRatePattern pattern node
* @template T
* @param {BrkClientBase} client
* @param {string} acc - Accumulated metric name
* @returns {FeeRatePattern<T>}
*/
function createFeeRatePattern(client, acc) {
return {
average: createMetricPattern1(client, _m(acc, "average")),
max: createMetricPattern1(client, _m(acc, "max")),
median: createMetricPattern11(client, _m(acc, "median")),
min: createMetricPattern1(client, _m(acc, "min")),
pct10: createMetricPattern11(client, _m(acc, "pct10")),
pct25: createMetricPattern11(client, _m(acc, "pct25")),
pct75: createMetricPattern11(client, _m(acc, "pct75")),
pct90: createMetricPattern11(client, _m(acc, "pct90")),
txindex: createMetricPattern27(client, acc),
};
}
/**
* @typedef {Object} _0satsPattern
* @property {ActivityPattern2} activity
@@ -3689,6 +3689,50 @@ function create_0satsPattern(client, acc) {
};
}
/**
* @typedef {Object} UnrealizedPattern
* @property {MetricPattern1<Dollars>} negUnrealizedLoss
* @property {MetricPattern1<Dollars>} netUnrealizedPnl
* @property {ActiveSupplyPattern} supplyInLoss
* @property {ActiveSupplyPattern} supplyInProfit
* @property {MetricPattern1<Dollars>} totalUnrealizedPnl
* @property {MetricPattern1<Dollars>} unrealizedLoss
* @property {MetricPattern1<Dollars>} unrealizedProfit
*/
/**
* Create a UnrealizedPattern pattern node
* @param {BrkClientBase} client
* @param {string} acc - Accumulated metric name
* @returns {UnrealizedPattern}
*/
function createUnrealizedPattern(client, acc) {
return {
negUnrealizedLoss: createMetricPattern1(
client,
_m(acc, "neg_unrealized_loss"),
),
netUnrealizedPnl: createMetricPattern1(
client,
_m(acc, "net_unrealized_pnl"),
),
supplyInLoss: createActiveSupplyPattern(client, _m(acc, "supply_in_loss")),
supplyInProfit: createActiveSupplyPattern(
client,
_m(acc, "supply_in_profit"),
),
totalUnrealizedPnl: createMetricPattern1(
client,
_m(acc, "total_unrealized_pnl"),
),
unrealizedLoss: createMetricPattern1(client, _m(acc, "unrealized_loss")),
unrealizedProfit: createMetricPattern1(
client,
_m(acc, "unrealized_profit"),
),
};
}
/**
* @typedef {Object} _100btcPattern
* @property {ActivityPattern2} activity
@@ -3719,29 +3763,29 @@ function create_100btcPattern(client, acc) {
}
/**
* @typedef {Object} _10yTo12yPattern
* @typedef {Object} _10yPattern
* @property {ActivityPattern2} activity
* @property {CostBasisPattern2} costBasis
* @property {CostBasisPattern} costBasis
* @property {OutputsPattern} outputs
* @property {RealizedPattern2} realized
* @property {RelativePattern2} relative
* @property {RealizedPattern4} realized
* @property {RelativePattern} relative
* @property {SupplyPattern2} supply
* @property {UnrealizedPattern} unrealized
*/
/**
* Create a _10yTo12yPattern pattern node
* Create a _10yPattern pattern node
* @param {BrkClientBase} client
* @param {string} acc - Accumulated metric name
* @returns {_10yTo12yPattern}
* @returns {_10yPattern}
*/
function create_10yTo12yPattern(client, acc) {
function create_10yPattern(client, acc) {
return {
activity: createActivityPattern2(client, acc),
costBasis: createCostBasisPattern2(client, acc),
costBasis: createCostBasisPattern(client, acc),
outputs: createOutputsPattern(client, _m(acc, "utxo_count")),
realized: createRealizedPattern2(client, acc),
relative: createRelativePattern2(client, acc),
realized: createRealizedPattern4(client, acc),
relative: createRelativePattern(client, acc),
supply: createSupplyPattern2(client, _m(acc, "supply")),
unrealized: createUnrealizedPattern(client, acc),
};
@@ -3806,73 +3850,29 @@ function create_0satsPattern2(client, acc) {
}
/**
* @typedef {Object} UnrealizedPattern
* @property {MetricPattern1<Dollars>} negUnrealizedLoss
* @property {MetricPattern1<Dollars>} netUnrealizedPnl
* @property {ActiveSupplyPattern} supplyInLoss
* @property {ActiveSupplyPattern} supplyInProfit
* @property {MetricPattern1<Dollars>} totalUnrealizedPnl
* @property {MetricPattern1<Dollars>} unrealizedLoss
* @property {MetricPattern1<Dollars>} unrealizedProfit
*/
/**
* Create a UnrealizedPattern pattern node
* @param {BrkClientBase} client
* @param {string} acc - Accumulated metric name
* @returns {UnrealizedPattern}
*/
function createUnrealizedPattern(client, acc) {
return {
negUnrealizedLoss: createMetricPattern1(
client,
_m(acc, "neg_unrealized_loss"),
),
netUnrealizedPnl: createMetricPattern1(
client,
_m(acc, "net_unrealized_pnl"),
),
supplyInLoss: createActiveSupplyPattern(client, _m(acc, "supply_in_loss")),
supplyInProfit: createActiveSupplyPattern(
client,
_m(acc, "supply_in_profit"),
),
totalUnrealizedPnl: createMetricPattern1(
client,
_m(acc, "total_unrealized_pnl"),
),
unrealizedLoss: createMetricPattern1(client, _m(acc, "unrealized_loss")),
unrealizedProfit: createMetricPattern1(
client,
_m(acc, "unrealized_profit"),
),
};
}
/**
* @typedef {Object} _10yPattern
* @typedef {Object} _10yTo12yPattern
* @property {ActivityPattern2} activity
* @property {CostBasisPattern} costBasis
* @property {CostBasisPattern2} costBasis
* @property {OutputsPattern} outputs
* @property {RealizedPattern4} realized
* @property {RelativePattern} relative
* @property {RealizedPattern2} realized
* @property {RelativePattern2} relative
* @property {SupplyPattern2} supply
* @property {UnrealizedPattern} unrealized
*/
/**
* Create a _10yPattern pattern node
* Create a _10yTo12yPattern pattern node
* @param {BrkClientBase} client
* @param {string} acc - Accumulated metric name
* @returns {_10yPattern}
* @returns {_10yTo12yPattern}
*/
function create_10yPattern(client, acc) {
function create_10yTo12yPattern(client, acc) {
return {
activity: createActivityPattern2(client, acc),
costBasis: createCostBasisPattern(client, acc),
costBasis: createCostBasisPattern2(client, acc),
outputs: createOutputsPattern(client, _m(acc, "utxo_count")),
realized: createRealizedPattern4(client, acc),
relative: createRelativePattern(client, acc),
realized: createRealizedPattern2(client, acc),
relative: createRelativePattern2(client, acc),
supply: createSupplyPattern2(client, _m(acc, "supply")),
unrealized: createUnrealizedPattern(client, acc),
};
@@ -3940,69 +3940,6 @@ function createSplitPattern2(client, acc) {
};
}
/**
* @typedef {Object} ActiveSupplyPattern
* @property {MetricPattern1<Bitcoin>} bitcoin
* @property {MetricPattern1<Dollars>} dollars
* @property {MetricPattern1<Sats>} sats
*/
/**
* Create a ActiveSupplyPattern pattern node
* @param {BrkClientBase} client
* @param {string} acc - Accumulated metric name
* @returns {ActiveSupplyPattern}
*/
function createActiveSupplyPattern(client, acc) {
return {
bitcoin: createMetricPattern1(client, _m(acc, "btc")),
dollars: createMetricPattern1(client, _m(acc, "usd")),
sats: createMetricPattern1(client, acc),
};
}
/**
* @typedef {Object} CoinbasePattern
* @property {BitcoinPattern} bitcoin
* @property {DollarsPattern<Dollars>} dollars
* @property {DollarsPattern<Sats>} sats
*/
/**
* Create a CoinbasePattern pattern node
* @param {BrkClientBase} client
* @param {string} acc - Accumulated metric name
* @returns {CoinbasePattern}
*/
function createCoinbasePattern(client, acc) {
return {
bitcoin: createBitcoinPattern(client, _m(acc, "btc")),
dollars: createDollarsPattern(client, _m(acc, "usd")),
sats: createDollarsPattern(client, acc),
};
}
/**
* @typedef {Object} CoinbasePattern2
* @property {BlockCountPattern<Bitcoin>} bitcoin
* @property {BlockCountPattern<Dollars>} dollars
* @property {BlockCountPattern<Sats>} sats
*/
/**
* Create a CoinbasePattern2 pattern node
* @param {BrkClientBase} client
* @param {string} acc - Accumulated metric name
* @returns {CoinbasePattern2}
*/
function createCoinbasePattern2(client, acc) {
return {
bitcoin: createBlockCountPattern(client, _m(acc, "btc")),
dollars: createBlockCountPattern(client, _m(acc, "usd")),
sats: createBlockCountPattern(client, acc),
};
}
/**
* @typedef {Object} SegwitAdoptionPattern
* @property {MetricPattern11<StoredF32>} base
@@ -4045,6 +3982,27 @@ function createCostBasisPattern2(client, acc) {
};
}
/**
* @typedef {Object} ActiveSupplyPattern
* @property {MetricPattern1<Bitcoin>} bitcoin
* @property {MetricPattern1<Dollars>} dollars
* @property {MetricPattern1<Sats>} sats
*/
/**
* Create a ActiveSupplyPattern pattern node
* @param {BrkClientBase} client
* @param {string} acc - Accumulated metric name
* @returns {ActiveSupplyPattern}
*/
function createActiveSupplyPattern(client, acc) {
return {
bitcoin: createMetricPattern1(client, _m(acc, "btc")),
dollars: createMetricPattern1(client, _m(acc, "usd")),
sats: createMetricPattern1(client, acc),
};
}
/**
* @typedef {Object} UnclaimedRewardsPattern
* @property {BitcoinPattern2<Bitcoin>} bitcoin
@@ -4088,21 +4046,44 @@ function create_2015Pattern(client, acc) {
}
/**
* @typedef {Object} _1dReturns1mSdPattern
* @property {MetricPattern4<StoredF32>} sd
* @property {MetricPattern4<StoredF32>} sma
* @typedef {Object} CoinbasePattern
* @property {BitcoinPattern} bitcoin
* @property {DollarsPattern<Dollars>} dollars
* @property {DollarsPattern<Sats>} sats
*/
/**
* Create a _1dReturns1mSdPattern pattern node
* Create a CoinbasePattern pattern node
* @param {BrkClientBase} client
* @param {string} acc - Accumulated metric name
* @returns {_1dReturns1mSdPattern}
* @returns {CoinbasePattern}
*/
function create_1dReturns1mSdPattern(client, acc) {
function createCoinbasePattern(client, acc) {
return {
sd: createMetricPattern4(client, _m(acc, "sd")),
sma: createMetricPattern4(client, _m(acc, "sma")),
bitcoin: createBitcoinPattern(client, _m(acc, "btc")),
dollars: createDollarsPattern(client, _m(acc, "usd")),
sats: createDollarsPattern(client, acc),
};
}
/**
* @typedef {Object} CoinbasePattern2
* @property {BlockCountPattern<Bitcoin>} bitcoin
* @property {BlockCountPattern<Dollars>} dollars
* @property {BlockCountPattern<Sats>} sats
*/
/**
* Create a CoinbasePattern2 pattern node
* @param {BrkClientBase} client
* @param {string} acc - Accumulated metric name
* @returns {CoinbasePattern2}
*/
function createCoinbasePattern2(client, acc) {
return {
bitcoin: createBlockCountPattern(client, _m(acc, "btc")),
dollars: createBlockCountPattern(client, _m(acc, "usd")),
sats: createBlockCountPattern(client, acc),
};
}
@@ -4131,6 +4112,44 @@ function createRelativePattern4(client, acc) {
};
}
/**
* @typedef {Object} CostBasisPattern
* @property {MetricPattern1<Dollars>} max
* @property {MetricPattern1<Dollars>} min
*/
/**
* Create a CostBasisPattern pattern node
* @param {BrkClientBase} client
* @param {string} acc - Accumulated metric name
* @returns {CostBasisPattern}
*/
function createCostBasisPattern(client, acc) {
return {
max: createMetricPattern1(client, _m(acc, "max_cost_basis")),
min: createMetricPattern1(client, _m(acc, "min_cost_basis")),
};
}
/**
* @typedef {Object} _1dReturns1mSdPattern
* @property {MetricPattern4<StoredF32>} sd
* @property {MetricPattern4<StoredF32>} sma
*/
/**
* Create a _1dReturns1mSdPattern pattern node
* @param {BrkClientBase} client
* @param {string} acc - Accumulated metric name
* @returns {_1dReturns1mSdPattern}
*/
function create_1dReturns1mSdPattern(client, acc) {
return {
sd: createMetricPattern4(client, _m(acc, "sd")),
sma: createMetricPattern4(client, _m(acc, "sma")),
};
}
/**
* @typedef {Object} SupplyPattern2
* @property {ActiveSupplyPattern} halved
@@ -4151,21 +4170,23 @@ function createSupplyPattern2(client, acc) {
}
/**
* @typedef {Object} CostBasisPattern
* @property {MetricPattern1<Dollars>} max
* @property {MetricPattern1<Dollars>} min
* @template T
* @typedef {Object} SatsPattern
* @property {MetricPattern1<T>} ohlc
* @property {SplitPattern2<T>} split
*/
/**
* Create a CostBasisPattern pattern node
* Create a SatsPattern pattern node
* @template T
* @param {BrkClientBase} client
* @param {string} acc - Accumulated metric name
* @returns {CostBasisPattern}
* @returns {SatsPattern<T>}
*/
function createCostBasisPattern(client, acc) {
function createSatsPattern(client, acc) {
return {
max: createMetricPattern1(client, _m(acc, "max_cost_basis")),
min: createMetricPattern1(client, _m(acc, "min_cost_basis")),
ohlc: createMetricPattern1(client, _m(acc, "ohlc_sats")),
split: createSplitPattern2(client, _m(acc, "sats")),
};
}
@@ -4211,27 +4232,6 @@ function createBitcoinPattern2(client, acc) {
};
}
/**
* @template T
* @typedef {Object} SatsPattern
* @property {MetricPattern1<T>} ohlc
* @property {SplitPattern2<T>} split
*/
/**
* Create a SatsPattern pattern node
* @template T
* @param {BrkClientBase} client
* @param {string} acc - Accumulated metric name
* @returns {SatsPattern<T>}
*/
function createSatsPattern(client, acc) {
return {
ohlc: createMetricPattern1(client, _m(acc, "ohlc_sats")),
split: createSplitPattern2(client, _m(acc, "sats")),
};
}
/**
* @typedef {Object} RealizedPriceExtraPattern
* @property {MetricPattern4<StoredF32>} ratio
@@ -4628,8 +4628,8 @@ function createOutputsPattern(client, acc) {
/**
* @typedef {Object} MetricsTree_Distribution_UtxoCohorts_All
* @property {ActivityPattern2} activity
* @property {CostBasisPattern2} costBasis
* @property {MetricsTree_Distribution_UtxoCohorts_All_Activity} activity
* @property {MetricsTree_Distribution_UtxoCohorts_All_CostBasis} costBasis
* @property {OutputsPattern} outputs
* @property {RealizedPattern3} realized
* @property {MetricsTree_Distribution_UtxoCohorts_All_Relative} relative
@@ -4637,6 +4637,22 @@ function createOutputsPattern(client, acc) {
* @property {UnrealizedPattern} unrealized
*/
/**
* @typedef {Object} MetricsTree_Distribution_UtxoCohorts_All_Activity
* @property {BlockCountPattern<StoredF64>} coinblocksDestroyed
* @property {BlockCountPattern<StoredF64>} coindaysDestroyed
* @property {MetricPattern11<Sats>} satblocksDestroyed
* @property {MetricPattern11<Sats>} satdaysDestroyed
* @property {UnclaimedRewardsPattern} sent
*/
/**
* @typedef {Object} MetricsTree_Distribution_UtxoCohorts_All_CostBasis
* @property {MetricPattern1<Dollars>} max
* @property {MetricPattern1<Dollars>} min
* @property {PercentilesPattern} percentiles
*/
/**
* @typedef {Object} MetricsTree_Distribution_UtxoCohorts_All_Relative
* @property {MetricPattern1<StoredF32>} negUnrealizedLossRelToOwnTotalUnrealizedPnl
@@ -5035,7 +5051,7 @@ function createOutputsPattern(client, acc) {
* @property {MetricsTree_Market_Ath} ath
* @property {MetricsTree_Market_Dca} dca
* @property {MetricsTree_Market_Indicators} indicators
* @property {MetricsTree_Market_Lookback} lookback
* @property {LookbackPattern<Dollars>} lookback
* @property {MetricsTree_Market_MovingAverage} movingAverage
* @property {MetricsTree_Market_Range} range
* @property {MetricsTree_Market_Returns} returns
@@ -5102,11 +5118,6 @@ function createOutputsPattern(client, acc) {
* @property {MetricPattern6<StoredF32>} stochRsiK
*/
/**
* @typedef {Object} MetricsTree_Market_Lookback
* @property {PriceAgoPattern<Dollars>} priceAgo
*/
/**
* @typedef {Object} MetricsTree_Market_MovingAverage
* @property {Price111dSmaPattern} price111dSma
@@ -5171,7 +5182,24 @@ function createOutputsPattern(client, acc) {
* @property {_1dReturns1mSdPattern} downside1wSd
* @property {_1dReturns1mSdPattern} downside1ySd
* @property {MetricPattern6<StoredF32>} downsideReturns
* @property {PriceAgoPattern<StoredF32>} priceReturns
* @property {MetricsTree_Market_Returns_PriceReturns} priceReturns
*/
/**
* @typedef {Object} MetricsTree_Market_Returns_PriceReturns
* @property {MetricPattern4<StoredF32>} _10y
* @property {MetricPattern4<StoredF32>} _1d
* @property {MetricPattern4<StoredF32>} _1m
* @property {MetricPattern4<StoredF32>} _1w
* @property {MetricPattern4<StoredF32>} _1y
* @property {MetricPattern4<StoredF32>} _2y
* @property {MetricPattern4<StoredF32>} _3m
* @property {MetricPattern4<StoredF32>} _3y
* @property {MetricPattern4<StoredF32>} _4y
* @property {MetricPattern4<StoredF32>} _5y
* @property {MetricPattern4<StoredF32>} _6m
* @property {MetricPattern4<StoredF32>} _6y
* @property {MetricPattern4<StoredF32>} _8y
*/
/**
@@ -7334,9 +7362,7 @@ class BrkClient extends BrkClientBase {
stochRsiD: createMetricPattern6(this, "stoch_rsi_d"),
stochRsiK: createMetricPattern6(this, "stoch_rsi_k"),
},
lookback: {
priceAgo: createPriceAgoPattern(this, "price"),
},
lookback: createLookbackPattern(this, "price"),
movingAverage: {
price111dSma: createPrice111dSmaPattern(this, "price_111d_sma"),
price12dEma: createPrice111dSmaPattern(this, "price_12d_ema"),
@@ -7402,7 +7428,21 @@ class BrkClient extends BrkClientBase {
downside1wSd: create_1dReturns1mSdPattern(this, "downside_1w_sd"),
downside1ySd: create_1dReturns1mSdPattern(this, "downside_1y_sd"),
downsideReturns: createMetricPattern6(this, "downside_returns"),
priceReturns: createPriceAgoPattern(this, "price_returns"),
priceReturns: {
_10y: createMetricPattern4(this, "10y_price_returns"),
_1d: createMetricPattern4(this, "1d_price_returns"),
_1m: createMetricPattern4(this, "1m_price_returns"),
_1w: createMetricPattern4(this, "1w_price_returns"),
_1y: createMetricPattern4(this, "1y_price_returns"),
_2y: createMetricPattern4(this, "2y_price_returns"),
_3m: createMetricPattern4(this, "3m_price_returns"),
_3y: createMetricPattern4(this, "3y_price_returns"),
_4y: createMetricPattern4(this, "4y_price_returns"),
_5y: createMetricPattern4(this, "5y_price_returns"),
_6m: createMetricPattern4(this, "6m_price_returns"),
_6y: createMetricPattern4(this, "6y_price_returns"),
_8y: createMetricPattern4(this, "8y_price_returns"),
},
},
volatility: {
price1mVolatility: createMetricPattern4(this, "price_1m_volatility"),
@@ -7618,7 +7658,7 @@ class BrkClient extends BrkClientBase {
},
oracle: {
ohlcCents: createMetricPattern6(this, "oracle_ohlc_cents"),
ohlcDollars: createMetricPattern6(this, "oracle_ohlc_dollars"),
ohlcDollars: createMetricPattern6(this, "oracle_ohlc"),
priceCents: createMetricPattern11(this, "orange_price_cents"),
txCount: createMetricPattern6(this, "oracle_tx_count"),
},

File diff suppressed because it is too large Load Diff

View File

@@ -1,14 +1,27 @@
# Run:
# uv run pytest tests/tree.py -s
"""Comprehensive test that fetches all endpoints in the tree."""
from brk_client import BrkClient
def is_metric_pattern(obj):
"""Check if an object is a metric pattern (has indexes() method and by attribute)."""
return (
hasattr(obj, "indexes")
and callable(getattr(obj, "indexes", None))
and hasattr(obj, "by")
)
def get_all_metrics(obj, path=""):
"""Recursively collect all MetricPattern instances from the tree."""
metrics = []
for attr_name in dir(obj):
if attr_name.startswith("_"):
# Skip dunder methods and internal attributes like _client
if attr_name.startswith("__") or attr_name == "_client":
continue
try:
@@ -16,74 +29,58 @@ def get_all_metrics(obj, path=""):
except Exception:
continue
if attr is None or callable(attr):
continue
current_path = f"{path}.{attr_name}" if path else attr_name
# Check if this is a metric pattern (has 'by' attribute with index methods)
if hasattr(attr, "by"):
by = attr.by
indexes = []
for idx_name in dir(by):
if not idx_name.startswith("_") and callable(
getattr(by, idx_name, None)
):
indexes.append(idx_name)
if indexes:
metrics.append((current_path, attr, indexes))
# Check if this is a metric pattern using the indexes() method
if is_metric_pattern(attr):
metrics.append((current_path, attr))
# Recurse into nested tree nodes
if hasattr(attr, "__dict__") and not callable(attr):
if hasattr(attr, "__dict__"):
metrics.extend(get_all_metrics(attr, current_path))
return metrics
def test_all_endpoints():
"""Test fetching last 3 values from all metric endpoints."""
"""Test fetching last value from all metric endpoints."""
client = BrkClient("http://localhost:3110")
metrics = get_all_metrics(client.metrics)
print(f"\nFound {len(metrics)} metrics")
success = 0
failed = 0
errors = []
for path, metric, indexes in metrics:
for path, metric in metrics:
# Use the indexes() method to get all available indexes
indexes = metric.indexes()
for idx_name in indexes:
full_path = f"{path}.by.{idx_name}"
try:
# Verify both access methods work: .by.index() and .get(index)
by = metric.by
endpoint = getattr(by, idx_name)()
# Use the new idiomatic API: tail(3).fetch() or [-3:].fetch()
res = endpoint.tail(3).fetch()
count = len(res["data"])
if count != 3:
failed += 1
error_msg = (
f"FAIL: {path}.by.{idx_name}() -> expected 3, got {count}"
)
errors.append(error_msg)
print(error_msg)
else:
success += 1
print(f"OK: {path}.by.{idx_name}() -> {count} items")
endpoint_by_property = getattr(by, idx_name)()
endpoint_by_get = metric.get(idx_name)
if endpoint_by_property is None:
raise Exception(f"metric.by.{idx_name}() returned None")
if endpoint_by_get is None:
raise Exception(f"metric.get('{idx_name}') returned None")
endpoint_by_property.tail(1).fetch()
success += 1
print(f"OK: {full_path}")
except Exception as e:
failed += 1
error_msg = f"FAIL: {path}.by.{idx_name}() -> {e}"
errors.append(error_msg)
print(error_msg)
print(f"FAIL: {full_path} -> {e}")
return
print("\n=== Results ===")
print(f"Success: {success}")
print(f"Failed: {failed}")
if errors:
print("\nErrors:")
for err in errors[:10]: # Show first 10 errors
print(f" {err}")
if len(errors) > 10:
print(f" ... and {len(errors) - 10} more")
assert failed == 0, f"{failed} endpoints failed"
if __name__ == "__main__":