From a1628f5324840718aeecb46143de0e90e5666852 Mon Sep 17 00:00:00 2001 From: Jiyuan Zheng Date: Tue, 7 May 2024 15:54:30 +0800 Subject: [PATCH] feat(cli): add validate subcommand (#165) * feat(cli): add validate subcommand * fix(config): fix test.yml * feat(config): support middleware and extensions mapping validation * revert(config): remove unergonomic middleware and extension mappings validation * refactor(config): move test configs --- .github/workflows/test.yml | 38 ++-- Cargo.lock | 188 +++++++++++++++++- Cargo.toml | 1 + src/cli.rs | 23 ++- src/config/mod.rs | 67 ++++--- src/config/rpc.rs | 120 ++++++++++- src/extensions/client/mod.rs | 54 ++++- src/extensions/mod.rs | 9 +- src/main.rs | 9 +- tests/configs/broken_endpoints.yml | 57 ++++++ .../configs}/config_with_env.yml | 0 11 files changed, 500 insertions(+), 66 deletions(-) create mode 100644 tests/configs/broken_endpoints.yml rename {configs => tests/configs}/config_with_env.yml (100%) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index a53e899..00c0a10 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -2,11 +2,11 @@ name: Test on: push: - branches: [ "master" ] + branches: ["master"] paths-ignore: - - '**/README.md' + - "**/README.md" pull_request: - branches: [ "master" ] + branches: ["master"] env: CARGO_TERM_COLOR: always @@ -19,19 +19,19 @@ jobs: build: runs-on: ubuntu-latest steps: - - name: Install toolchain - uses: dtolnay/rust-toolchain@stable - with: - toolchain: nightly-2023-09-29 - components: rustfmt, clippy - - uses: actions/checkout@v3 - with: - submodules: recursive - - name: Check format - run: cargo +nightly-2023-09-29 fmt --all -- --check - - name: Check clippy - run: cargo +nightly-2023-09-29 clippy --all-targets --all-features -- -D warnings - - name: Build - run: cargo build --verbose - - name: Run tests - run: cargo test --verbose + - name: Install toolchain + uses: dtolnay/rust-toolchain@stable + with: + toolchain: nightly-2023-09-29 + components: rustfmt, clippy + - uses: actions/checkout@v3 + with: + submodules: recursive + - name: Check format + run: cargo +nightly-2023-09-29 fmt --all -- --check + - name: Check clippy + run: cargo +nightly-2023-09-29 clippy --all-targets --all-features -- -D warnings + - name: Build + run: cargo build --verbose + - name: Run tests + run: cargo test --verbose diff --git a/Cargo.lock b/Cargo.lock index 57d940f..45faf00 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -291,6 +291,15 @@ dependencies = [ "serde", ] +[[package]] +name = "bincode" +version = "1.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad" +dependencies = [ + "serde", +] + [[package]] name = "bitflags" version = "1.3.2" @@ -437,6 +446,16 @@ dependencies = [ "serde", ] +[[package]] +name = "card-validate" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "655fa70596e2a38372c0c0c4449ec0166ad9cc43d91558bbecc1a6f38bf9eb91" +dependencies = [ + "lazy_static", + "regex", +] + [[package]] name = "cargo-platform" version = "0.1.7" @@ -465,6 +484,15 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" +[[package]] +name = "castaway" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a17ed5635fc8536268e5d4de1e22e81ac34419e5f052d4d51f4e01dcc263fcc" +dependencies = [ + "rustversion", +] + [[package]] name = "cc" version = "1.0.90" @@ -574,6 +602,20 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" +[[package]] +name = "compact_str" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f86b9c4c00838774a6d902ef931eff7470720c51d90c2e32cfe15dc304737b3f" +dependencies = [ + "castaway", + "cfg-if 1.0.0", + "itoa", + "ryu", + "serde", + "static_assertions", +] + [[package]] name = "concurrent-queue" version = "2.4.0" @@ -1054,6 +1096,37 @@ dependencies = [ "slab", ] +[[package]] +name = "garde" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d5fa8fb3ffe035745c6194540b2064b2fe275f32367fbb4eb026024b7921e2e5" +dependencies = [ + "card-validate", + "compact_str", + "garde_derive", + "idna 0.3.0", + "once_cell", + "phonenumber", + "regex", + "serde", + "smallvec", + "unicode-segmentation", + "url", +] + +[[package]] +name = "garde_derive" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9cf62650515830c41553b72bd49ec20fb120226f9277c7f2847f071cf998325b" +dependencies = [ + "proc-macro2", + "quote", + "regex", + "syn 2.0.53", +] + [[package]] name = "generic-array" version = "0.12.4" @@ -1375,6 +1448,16 @@ dependencies = [ "cc", ] +[[package]] +name = "idna" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e14ddfc70884202db2244c223200c204c2bda1bc6e0998d11b5e024d657209e6" +dependencies = [ + "unicode-bidi", + "unicode-normalization", +] + [[package]] name = "idna" version = "0.5.0" @@ -1418,7 +1501,7 @@ dependencies = [ "log", "num-format", "once_cell", - "quick-xml", + "quick-xml 0.26.0", "rgb", "str_stack", ] @@ -1767,6 +1850,12 @@ version = "0.2.153" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd" +[[package]] +name = "linked-hash-map" +version = "0.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f" + [[package]] name = "linux-raw-sys" version = "0.4.13" @@ -1789,6 +1878,15 @@ version = "0.4.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c" +[[package]] +name = "lru-cache" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31e24f1ad8321ca0e8a1e0ac13f23cb668e6f5466c2c57319f6a5cf1cc8e3b1c" +dependencies = [ + "linked-hash-map", +] + [[package]] name = "mach2" version = "0.4.2" @@ -2035,6 +2133,12 @@ version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" +[[package]] +name = "oncemutex" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44d11de466f4a3006fe8a5e7ec84e93b79c70cb992ae0aa0eb631ad2df8abfe2" + [[package]] name = "oorandom" version = "11.1.3" @@ -2263,6 +2367,27 @@ version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" +[[package]] +name = "phonenumber" +version = "0.3.4+8.13.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d888d375f2963bf06c5079665fbe53db69860879ff5a78524fe3c93c54fb7b8" +dependencies = [ + "bincode", + "either", + "fnv", + "itertools 0.10.5", + "lazy_static", + "nom", + "quick-xml 0.31.0", + "regex", + "regex-cache", + "serde", + "serde_derive", + "strum", + "thiserror", +] + [[package]] name = "pin-project" version = "1.1.5" @@ -2459,6 +2584,15 @@ dependencies = [ "memchr", ] +[[package]] +name = "quick-xml" +version = "0.31.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1004a344b30a54e2ee58d66a71b32d2db2feb0a31f9a2d302bf0536f15de2a33" +dependencies = [ + "memchr", +] + [[package]] name = "quote" version = "1.0.35" @@ -2627,6 +2761,18 @@ dependencies = [ "regex-syntax 0.8.2", ] +[[package]] +name = "regex-cache" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f7b62d69743b8b94f353b6b7c3deb4c5582828328bcb8d5fedf214373808793" +dependencies = [ + "lru-cache", + "oncemutex", + "regex", + "regex-syntax 0.6.29", +] + [[package]] name = "regex-syntax" version = "0.6.29" @@ -3005,6 +3151,9 @@ name = "smallvec" version = "1.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" +dependencies = [ + "serde", +] [[package]] name = "socket2" @@ -3044,6 +3193,12 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" +[[package]] +name = "static_assertions" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" + [[package]] name = "str_stack" version = "0.1.0" @@ -3056,6 +3211,28 @@ version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5ee073c9e4cd00e28217186dbe12796d692868f432bf2e97ee73bed0c56dfa01" +[[package]] +name = "strum" +version = "0.25.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "290d54ea6f91c969195bdbcd7442c8c2a2ba87da8bf60a7ee86a235d4bc1e125" +dependencies = [ + "strum_macros", +] + +[[package]] +name = "strum_macros" +version = "0.25.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23dc1fa9ac9c169a78ba62f0b841814b7abae11bdd047b9c58f893439e309ea0" +dependencies = [ + "heck 0.4.1", + "proc-macro2", + "quote", + "rustversion", + "syn 2.0.53", +] + [[package]] name = "subtle" version = "2.5.0" @@ -3076,6 +3253,7 @@ dependencies = [ "enumflags2", "futures", "futures-util", + "garde", "governor", "http", "hyper", @@ -3590,6 +3768,12 @@ dependencies = [ "tinyvec", ] +[[package]] +name = "unicode-segmentation" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4c87d22b6e3f4a18d4d40ef354e97c90fcb14dd91d7dc0aa9d8a1172ebf7202" + [[package]] name = "unsafe-libyaml" version = "0.2.11" @@ -3609,7 +3793,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "31e6302e3bb753d46e83516cae55ae196fc0c309407cf11ab35cc51a4c2a4633" dependencies = [ "form_urlencoded", - "idna", + "idna 0.5.0", "percent-encoding", ] diff --git a/Cargo.toml b/Cargo.toml index ca0e77f..7e9b72b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -42,6 +42,7 @@ tower-http = { version = "0.4", features = ["full"] } tracing = "0.1.40" tracing-serde = "0.1.3" tracing-subscriber = { version = "0.3.18", features = ["env-filter", "json"] } +garde = { version = "0.18", features = ["full"] } jsonrpsee = { path = "./vendor/jsonrpsee/jsonrpsee", features = ["full"] } governor = { path = "./vendor/governor/governor" } diff --git a/src/cli.rs b/src/cli.rs index b935284..d70b6fb 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -1,12 +1,27 @@ -use clap::Parser; +use clap::{Parser, Subcommand}; use std::path::PathBuf; #[derive(Parser, Debug)] #[command(version, about)] -pub struct Command { +pub struct Cli { /// The config file to use #[arg(short, long, default_value = "configs/config.yml")] pub config: PathBuf, + + #[command(subcommand)] + pub command: Option, } -pub fn parse_args() -> Command { - Command::parse() + +#[derive(Subcommand, Debug)] +pub enum Command { + Validate, +} + +pub fn parse_args() -> Cli { + Cli::parse() +} + +impl Cli { + pub fn is_validate(&self) -> bool { + matches!(self.command, Some(Command::Validate)) + } } diff --git a/src/config/mod.rs b/src/config/mod.rs index 7366721..1d30aec 100644 --- a/src/config/mod.rs +++ b/src/config/mod.rs @@ -1,9 +1,10 @@ -use anyhow::{bail, Context}; +use anyhow::Context; use regex::{Captures, Regex}; use std::env; use std::fs; use std::path; +use garde::Validate; use serde::Deserialize; use crate::extensions::ExtensionsConfig; @@ -117,10 +118,13 @@ pub struct MiddlewaresConfig { pub subscriptions: Vec, } -#[derive(Debug)] +#[derive(Debug, Validate)] +#[garde(allow_unvalidated)] pub struct Config { + #[garde(dive)] pub extensions: ExtensionsConfig, pub middlewares: MiddlewaresConfig, + #[garde(dive)] pub rpcs: RpcDefinitions, } @@ -154,9 +158,6 @@ pub fn read_config(path: impl AsRef) -> Result Result Ok(config_str) } -fn validate_config(config: &Config) -> Result<(), anyhow::Error> { - // TODO: validate logic should be in each individual extensions - // validate endpoints - for endpoint in &config.extensions.client.as_ref().unwrap().endpoints { - if endpoint.parse::().is_err() { - bail!("Invalid endpoint {}", endpoint); - } - } - - // ensure each method has only one param with inject=true - for method in &config.rpcs.methods { - if method.params.iter().filter(|x| x.inject).count() > 1 { - bail!("Method {} has more than one inject param", method.method); +pub async fn validate(config: &Config) -> Result<(), anyhow::Error> { + // validate use garde::Validate + config.validate(&())?; + // since endpoints connection test is async + // we can't intergrate it into garde::Validate + // and it's not a static validation like format, length, .etc + if let Some(client_config) = &config.extensions.client { + if !client_config.all_endpoints_can_be_connected().await { + anyhow::bail!("Unable to connect to all endpoints"); } } - - // ensure there is no required param after optional param - for method in &config.rpcs.methods { - let mut has_optional = false; - for param in &method.params { - if param.optional { - has_optional = true; - } else if has_optional { - bail!("Method {} has required param after optional param", method.method); - } - } - } - Ok(()) } @@ -291,6 +275,25 @@ mod tests { fn read_config_with_render_template_works() { // It's enough to check the replacement works // if config itself has proper data validation - let _config = read_config("configs/config_with_env.yml").unwrap(); + let _config = read_config("tests/configs/config_with_env.yml").unwrap(); + } + + #[tokio::test] + async fn validate_config_succeeds_for_correct_config() { + let config = read_config("configs/config.yml").expect("Unable to read config file"); + let result = validate(&config).await; + assert!(result.is_ok()); + } + + #[tokio::test] + async fn validate_config_fails_for_broken_endpoints() { + let config = read_config("tests/configs/broken_endpoints.yml").expect("Unable to read config file"); + let result = validate(&config).await; + assert!(result.is_err()); + assert!(result + .err() + .unwrap() + .to_string() + .contains("Unable to connect to all endpoints")); } } diff --git a/src/config/rpc.rs b/src/config/rpc.rs index 8598a90..06c8d05 100644 --- a/src/config/rpc.rs +++ b/src/config/rpc.rs @@ -1,3 +1,4 @@ +use garde::Validate; use jsonrpsee::core::JsonValue; use serde::Deserialize; @@ -20,13 +21,15 @@ pub struct MethodParam { pub inject: bool, } -#[derive(Deserialize, Debug)] +#[derive(Deserialize, Validate, Debug)] +#[garde(allow_unvalidated)] pub struct RpcMethod { pub method: String, #[serde(default)] pub cache: Option, + #[garde(custom(validate_params_with_name(&self.method)))] #[serde(default)] pub params: Vec, @@ -48,6 +51,31 @@ pub struct RpcMethod { pub rate_limit_weight: u32, } +fn validate_params_with_name(method_name: &str) -> impl FnOnce(&[MethodParam], &()) -> garde::Result + '_ { + move |params, _| { + // ensure each method has only one param with inject=true + if params.iter().filter(|x| x.inject).count() > 1 { + return Err(garde::Error::new(format!( + "method {} has more than one inject param", + method_name + ))); + } + // ensure there is no required param after optional param + let mut has_optional = false; + for param in params { + if param.optional { + has_optional = true; + } else if has_optional { + return Err(garde::Error::new(format!( + "method {} has required param after optional param", + method_name + ))); + } + } + Ok(()) + } +} + fn default_rate_limit_weight() -> u32 { 1 } @@ -71,11 +99,99 @@ pub struct RpcSubscription { pub merge_strategy: Option, } -#[derive(Deserialize, Debug)] +#[derive(Deserialize, Validate, Debug)] +#[garde(allow_unvalidated)] pub struct RpcDefinitions { + #[garde(dive)] pub methods: Vec, #[serde(default)] pub subscriptions: Vec, #[serde(default)] pub aliases: Vec<(String, String)>, } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn validate_params_succeeds_for_valid_params() { + let valid_params = vec![ + MethodParam { + name: "param1".to_string(), + ty: "u64".to_string(), + optional: false, + inject: false, + }, + MethodParam { + name: "param2".to_string(), + ty: "u64".to_string(), + optional: true, + inject: false, + }, + MethodParam { + name: "param3".to_string(), + ty: "u64".to_string(), + optional: true, + inject: false, + }, + ]; + let method_name = "test"; + let test_fn = validate_params_with_name(method_name); + assert!(test_fn(&valid_params, &()).is_ok()); + } + + #[test] + fn validate_params_fails_for_more_than_one_param_has_inject_equals_true() { + let another_invalid_params = vec![ + MethodParam { + name: "param1".to_string(), + ty: "u64".to_string(), + optional: false, + inject: true, + }, + MethodParam { + name: "param2".to_string(), + ty: "u64".to_string(), + optional: false, + inject: true, + }, + MethodParam { + name: "param3".to_string(), + ty: "u64".to_string(), + optional: false, + inject: true, + }, + ]; + let method_name = "test"; + let test_fn = validate_params_with_name(method_name); + assert!(test_fn(&another_invalid_params, &()).is_err()); + } + + #[test] + fn validate_params_fails_for_optional_params_are_not_the_last() { + let method_name = "test"; + let invalid_params = vec![ + MethodParam { + name: "param1".to_string(), + ty: "u64".to_string(), + optional: false, + inject: false, + }, + MethodParam { + name: "param2".to_string(), + ty: "u64".to_string(), + optional: true, + inject: false, + }, + MethodParam { + name: "param3".to_string(), + ty: "u64".to_string(), + optional: false, + inject: true, + }, + ]; + let test_fn = validate_params_with_name(method_name); + assert!(test_fn(&invalid_params, &()).is_err()); + } +} diff --git a/src/extensions/client/mod.rs b/src/extensions/client/mod.rs index 9b7a5e6..837c17f 100644 --- a/src/extensions/client/mod.rs +++ b/src/extensions/client/mod.rs @@ -6,7 +6,9 @@ use std::{ use anyhow::anyhow; use async_trait::async_trait; use futures::FutureExt as Boxed; +use garde::Validate; use jsonrpsee::core::{client::Subscription, Error, JsonValue}; +use jsonrpsee::ws_client::WsClientBuilder; use opentelemetry::trace::FutureExt; use rand::{seq::SliceRandom, thread_rng}; use serde::{Deserialize, Serialize}; @@ -44,14 +46,64 @@ impl Drop for Client { } } -#[derive(Deserialize, Debug)] +#[derive(Deserialize, Validate, Debug)] +#[garde(allow_unvalidated)] pub struct ClientConfig { + #[garde(inner(custom(validate_endpoint)))] pub endpoints: Vec, #[serde(default = "bool_true")] pub shuffle_endpoints: bool, pub health_check: Option, } +fn validate_endpoint(endpoint: &str, _context: &()) -> garde::Result { + endpoint + .parse::() + .map_err(|_| garde::Error::new(format!("Invalid endpoint format: {}", endpoint)))?; + + Ok(()) +} + +impl ClientConfig { + pub async fn all_endpoints_can_be_connected(&self) -> bool { + let join_handles: Vec<_> = self + .endpoints + .iter() + .map(|endpoint| { + let endpoint = endpoint.clone(); + tokio::spawn(async move { + match check_endpoint_connection(&endpoint).await { + Ok(_) => { + tracing::info!("Connected to endpoint: {endpoint}"); + true + } + Err(err) => { + tracing::error!("Failed to connect to endpoint: {endpoint}, error: {err:?}",); + false + } + } + }) + }) + .collect(); + let mut ok_all = true; + for join_handle in join_handles { + let ok = join_handle.await.unwrap_or_else(|e| { + tracing::error!("Failed to join: {e:?}"); + false + }); + if !ok { + ok_all = false + } + } + ok_all + } +} +// simple connection check with default client params and no retries +async fn check_endpoint_connection(endpoint: &str) -> Result<(), anyhow::Error> { + let _ = WsClientBuilder::default().build(&endpoint).await?; + Ok(()) +} + pub fn bool_true() -> bool { true } diff --git a/src/extensions/mod.rs b/src/extensions/mod.rs index 4959199..babb055 100644 --- a/src/extensions/mod.rs +++ b/src/extensions/mod.rs @@ -72,13 +72,15 @@ impl ExtensionRegistry { macro_rules! define_all_extensions { ( $( - $ext_name:ident: $ext_type:ty + $(#[$attr:meta])* $ext_name:ident: $ext_type:ty ),* $(,)? ) => { - #[derive(Deserialize, Debug, Default)] + use garde::Validate; + #[derive(Deserialize, Debug, Validate, Default)] + #[garde(allow_unvalidated)] pub struct ExtensionsConfig { $( - #[serde(default)] + $(#[$attr])* pub $ext_name: Option<<$ext_type as Extension>::Config>, )* } @@ -132,6 +134,7 @@ macro_rules! define_all_extensions { define_all_extensions! { telemetry: telemetry::Telemetry, cache: cache::Cache, + #[garde(dive)] client: client::Client, merge_subscription: merge_subscription::MergeSubscription, substrate_api: api::SubstrateApi, diff --git a/src/main.rs b/src/main.rs index 046ee2d..9eed3b5 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,11 +1,14 @@ #[tokio::main] async fn main() -> anyhow::Result<()> { - // read config from file + subway::logger::enable_logger(); let cli = subway::cli::parse_args(); let config = subway::config::read_config(&cli.config)?; - - subway::logger::enable_logger(); tracing::trace!("{:#?}", config); + subway::config::validate(&config).await?; + // early return if we're just validating the config + if cli.is_validate() { + return Ok(()); + } let subway_server = subway::server::build(config).await?; tracing::info!("Server running at {}", subway_server.addr); diff --git a/tests/configs/broken_endpoints.yml b/tests/configs/broken_endpoints.yml new file mode 100644 index 0000000..cfe6313 --- /dev/null +++ b/tests/configs/broken_endpoints.yml @@ -0,0 +1,57 @@ +extensions: + client: + endpoints: + - wss://acala-rpc.dwellir.com + - wss://acala-rpc-0.aca-api.network + - wss://example.com + health_check: + interval_sec: 10 # check interval, default is 10s + healthy_response_time_ms: 500 # max response time to be considered healthy, default is 500ms + health_method: system_health + response: # response contains { isSyncing: false } + !contains + - - isSyncing + - !eq false + event_bus: + substrate_api: + stale_timeout_seconds: 180 # rotate endpoint if no new blocks for 3 minutes + telemetry: + provider: none + cache: + default_ttl_seconds: 60 + default_size: 500 + merge_subscription: + keep_alive_seconds: 60 + server: + port: 9944 + listen_address: '0.0.0.0' + max_connections: 2000 + http_methods: + - path: /health + method: system_health + - path: /liveness + method: chain_getBlockHash + cors: all + rate_limit: # these are for demo purpose only, please adjust to your needs + connection: # 20 RPC requests per second per connection + burst: 20 + period_secs: 1 + ip: # 500 RPC requests per 10 seconds per ip + burst: 500 + period_secs: 10 + # use X-Forwarded-For header to get real ip, if available (e.g. behind a load balancer). + # WARNING: Use with caution, as this xff header can be forged. + use_xff: true # default is false + +middlewares: + methods: + - delay + - response + - inject_params + - cache + - upstream + subscriptions: + - merge_subscription + - upstream + +rpcs: substrate diff --git a/configs/config_with_env.yml b/tests/configs/config_with_env.yml similarity index 100% rename from configs/config_with_env.yml rename to tests/configs/config_with_env.yml