Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions crates/core/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,10 @@ required-features = ["rust"]
name = "streams"
required-features = ["rust"]

[[example]]
name = "streams_e2e"
required-features = ["rust"]

[[example]]
name = "webhooks_e2e"
required-features = ["rust"]
Expand Down
55 changes: 54 additions & 1 deletion crates/core/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ This is one of four language bindings published from the same Rust core. See the
- [IP Custom Headers](#ip-custom-headers)
- [Method Rate Limits](#method-rate-limits)
- [Endpoint Rate Limits](#endpoint-rate-limits)
- [Endpoint URLs](#endpoint-urls)
- [Metrics](#metrics)
- [Chains](#chains)
- [Billing](#billing)
Expand Down Expand Up @@ -815,7 +816,7 @@ qn.admin.delete_method_rate_limit("ep-123", "rl-1").await?;

##### `update_rate_limits` / `updateRateLimits`

Updates the endpoint-level RPS / RPM / RPD caps.
Partial update of the endpoint-level RPS / RPM / RPD caps. Only buckets included in the request are modified — omitted buckets are left unchanged. Values are capped by the account's plan tier. Sends `PATCH`.

**Parameters**: `id` (endpoint id, required); `rate_limits`: `RateLimitSettings` (`rps`, `rpm`, `rpd`, all optional).

Expand All @@ -828,6 +829,58 @@ let params = UpdateRateLimitsRequest { rate_limits };
qn.admin.update_rate_limits("ep-123", &params).await?;
```

##### `get_rate_limits` / `getRateLimits`

Returns the rate-limit rows currently enforced on the endpoint, each identifying its `bucket` (`"rps"` / `"rpm"` / `"rpd"`), `rate_limit`, and `source` (`"plan_default"` or `"user_override"`). User-set overrides expose an `id` you can pass to `delete_rate_limit_override`.

**Parameters**: `id` (endpoint id, required).

**Returns**: `GetRateLimitsResponse` with `data.rate_limits: Vec<RateLimitEntry>`.

```rust
// Rust
let resp = qn.admin.get_rate_limits("123").await?;
for row in resp.data.unwrap().rate_limits {
println!("{} {} {} {:?}", row.bucket, row.rate_limit, row.source, row.id);
}
```

##### `delete_rate_limit_override` / `deleteRateLimitOverride`

Deletes a user-set rate-limit override by UUID. Plan defaults are not deletable — passing a UUID that does not match a user-set override on the endpoint returns 404.

**Parameters**: `id` (endpoint id, required); `override_id` (UUID returned by `get_rate_limits`, required).

**Returns**: nothing.

```rust
// Rust
qn.admin.delete_rate_limit_override("123", "ovr-uuid").await?;
```

#### Endpoint URLs

##### `get_endpoint_urls` / `getEndpointUrls`

Returns the HTTP and WebSocket URLs for the endpoint without fetching the full endpoint record. For multichain endpoints, `multichain_urls` is a per-network map of additional URLs; for single-chain endpoints it is `None`.

**Parameters**: `id` (endpoint id, required).

**Returns**: `GetEndpointUrlsResponse` with `data.http_url`, `data.wss_url`, and `data.multichain_urls`.

```rust
// Rust
let resp = qn.admin.get_endpoint_urls("123").await?;
if let Some(data) = resp.data {
println!("{}", data.http_url);
if let Some(mc) = data.multichain_urls {
for (network, urls) in mc {
println!("{network} {}", urls.http_url);
}
}
}
```

#### Metrics

##### `get_endpoint_metrics` / `getEndpointMetrics`
Expand Down
32 changes: 28 additions & 4 deletions crates/core/examples/admin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ async fn main() {
.sort_direction("desc".to_string())
.build();

match qn.admin.get_endpoints(&params).await {
let first_endpoint_id = match qn.admin.get_endpoints(&params).await {
Ok(resp) => {
if let Some(p) = &resp.pagination {
println!(
Expand All @@ -25,11 +25,35 @@ async fn main() {
}
for ep in &resp.data {
println!(
"{} | {} | {} | {} | dedicated={} flat={}",
ep.id, ep.name, ep.status, ep.chain, ep.is_dedicated, ep.is_flat_rate
"{} | {} | {} | {} | dedicated={} flat={} multichain={}",
ep.id,
ep.name,
ep.status,
ep.chain,
ep.is_dedicated,
ep.is_flat_rate,
ep.is_multichain
);
}
resp.data.first().map(|ep| ep.id.clone())
}
Err(e) => eprintln!("Error: {e}"),
Err(e) => {
eprintln!("get_endpoints error: {e}");
None
}
};

let Some(endpoint_id) = first_endpoint_id else {
return;
};

match qn.admin.get_rate_limits(&endpoint_id).await {
Ok(resp) => println!("get_rate_limits: {:?}", resp.data),
Err(e) => eprintln!("get_rate_limits error: {e}"),
}

match qn.admin.get_endpoint_urls(&endpoint_id).await {
Ok(resp) => println!("get_endpoint_urls: {:?}", resp.data),
Err(e) => eprintln!("get_endpoint_urls error: {e}"),
}
}
31 changes: 31 additions & 0 deletions crates/core/examples/admin_e2e.rs
Original file line number Diff line number Diff line change
Expand Up @@ -554,6 +554,11 @@ async fn main() {

// --- Rate limits ---

match qn.admin.get_rate_limits(&endpoint_id).await {
Ok(resp) => println!("get_rate_limits before PATCH: {:?}", resp.data),
Err(e) => eprintln!("get_rate_limits error: {e}"),
}

match qn
.admin
.update_rate_limits(
Expand All @@ -571,6 +576,16 @@ async fn main() {
Err(e) => eprintln!("update_rate_limits error: {e}"),
}

match qn.admin.get_rate_limits(&endpoint_id).await {
Ok(resp) => println!("get_rate_limits after PATCH: {:?}", resp.data),
Err(e) => eprintln!("get_rate_limits error: {e}"),
}

match qn.admin.get_endpoint_urls(&endpoint_id).await {
Ok(resp) => println!("get_endpoint_urls: {:?}", resp.data),
Err(e) => eprintln!("get_endpoint_urls error: {e}"),
}

match qn.admin.get_method_rate_limits(&endpoint_id).await {
Ok(resp) => println!("get_method_rate_limits: {:?}", resp.data),
Err(e) => eprintln!("get_method_rate_limits error: {e}"),
Expand Down Expand Up @@ -826,6 +841,22 @@ async fn main() {
other => eprintln!("expected Api 404, got {other:?}"),
}

// 1b) Rate-limit override delete with a bogus override id — also a 404.
match qn
.admin
.delete_rate_limit_override("does-not-exist", "00000000-0000-0000-0000-000000000000")
.await
{
Err(SdkError::Api { status, body }) => {
println!(
"delete_rate_limit_override api error {status}: {}",
&body[..body.len().min(80)]
);
assert_eq!(status.as_u16(), 404);
}
other => eprintln!("expected Api 404 from delete_rate_limit_override, got {other:?}"),
}

// 2) Timeout path — unreachable base URL + 1s timeout forces a timeout
// from reqwest, which maps to SdkError::Http with http_kind() == Timeout.
let blackhole = SdkFullConfig {
Expand Down
44 changes: 44 additions & 0 deletions crates/core/src/admin/endpoint_rate_limits.rs
Original file line number Diff line number Diff line change
Expand Up @@ -137,3 +137,47 @@ pub struct UpdateRateLimitsRequest {
/// Rate limit values to apply.
pub rate_limits: RateLimitSettings,
}

/// A single rate-limit row returned by `get_rate_limits`, identifying the
/// bucket (`rps`/`rpm`/`rpd`), the value enforced, and whether the value comes
/// from the plan default or a user-set override.
#[cfg_attr(feature = "python", gen_stub_pyclass)]
#[cfg_attr(feature = "python", pyclass(get_all, set_all))]
#[cfg_attr(feature = "node", napi(object))]
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct RateLimitEntry {
/// Which bucket this row applies to: `rps`, `rpm`, or `rpd`.
pub bucket: String,
/// The enforced value for this bucket.
pub rate_limit: i32,
/// Where the value comes from: `plan_default` or `user_override`.
pub source: String,
/// Row identifier. Present on `user_override` rows — pass it to
/// `delete_rate_limit_override` to remove the override. May be absent on
/// `plan_default` rows and cannot be deleted there.
#[serde(default)]
pub id: Option<String>,
}

/// Inner data for `get_rate_limits`.
#[cfg_attr(feature = "python", gen_stub_pyclass)]
#[cfg_attr(feature = "python", pyclass(get_all, set_all))]
#[cfg_attr(feature = "node", napi(object))]
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct GetRateLimitsData {
/// One row per enforced bucket.
#[serde(default)]
pub rate_limits: Vec<RateLimitEntry>,
}

/// Response from `get_rate_limits`.
#[cfg_attr(feature = "python", gen_stub_pyclass)]
#[cfg_attr(feature = "python", pyclass(get_all, set_all))]
#[cfg_attr(feature = "node", napi(object))]
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct GetRateLimitsResponse {
/// Rate-limit rows with their source.
pub data: Option<GetRateLimitsData>,
/// Error message when the request did not succeed.
pub error: Option<String>,
}
47 changes: 47 additions & 0 deletions crates/core/src/admin/endpoint_urls.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
#[cfg(feature = "node")]
use napi_derive::napi;
#[cfg(feature = "python")]
use pyo3::pyclass;
#[cfg(feature = "python")]
use pyo3_stub_gen::derive::gen_stub_pyclass;
use serde::{Deserialize, Serialize};
use std::collections::HashMap;

/// HTTP/WSS URL pair for a single network on a multichain endpoint.
#[cfg_attr(feature = "python", gen_stub_pyclass)]
#[cfg_attr(feature = "python", pyclass(get_all, set_all))]
#[cfg_attr(feature = "node", napi(object))]
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct EndpointUrl {
/// HTTP RPC URL.
pub http_url: String,
/// WebSocket RPC URL, when available.
pub wss_url: Option<String>,
}

/// Inner data for `get_endpoint_urls` — the http/wss URLs for the endpoint and,
/// when the endpoint is multichain, a per-network map of additional URLs.
#[cfg_attr(feature = "python", gen_stub_pyclass)]
#[cfg_attr(feature = "python", pyclass(get_all, set_all))]
#[cfg_attr(feature = "node", napi(object))]
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct GetEndpointUrlsData {
/// HTTP RPC URL.
pub http_url: String,
/// WebSocket RPC URL, when available.
pub wss_url: Option<String>,
/// Per-network URLs for multichain endpoints; `None` for single-chain endpoints.
pub multichain_urls: Option<HashMap<String, EndpointUrl>>,
}

/// Response from `get_endpoint_urls`.
#[cfg_attr(feature = "python", gen_stub_pyclass)]
#[cfg_attr(feature = "python", pyclass(get_all, set_all))]
#[cfg_attr(feature = "node", napi(object))]
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct GetEndpointUrlsResponse {
/// URLs for the endpoint.
pub data: Option<GetEndpointUrlsData>,
/// Error message when the request did not succeed.
pub error: Option<String>,
}
6 changes: 6 additions & 0 deletions crates/core/src/admin/endpoints.rs
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,9 @@ pub struct Endpoint {
/// Tags applied to the endpoint.
#[serde(default)]
pub tags: Vec<EndpointTag>,
/// Whether the endpoint is configured to serve multiple chains/networks.
#[serde(default)]
pub is_multichain: bool,
}

/// Tag reference as returned on an endpoint.
Expand Down Expand Up @@ -179,6 +182,9 @@ pub struct SingleEndpoint {
/// Tags applied to the endpoint.
#[serde(default)]
pub tags: Vec<EndpointTag>,
/// Whether the endpoint is configured to serve multiple chains/networks.
#[serde(default)]
pub is_multichain: bool,
}

/// Rate limits applied to an endpoint.
Expand Down
Loading
Loading