quicknode-sdk 0.1.0a15__tar.gz → 0.1.0a17__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (48) hide show
  1. {quicknode_sdk-0.1.0a15 → quicknode_sdk-0.1.0a17}/Cargo.lock +7 -7
  2. {quicknode_sdk-0.1.0a15 → quicknode_sdk-0.1.0a17}/Cargo.toml +1 -1
  3. {quicknode_sdk-0.1.0a15 → quicknode_sdk-0.1.0a17}/PKG-INFO +52 -4
  4. {quicknode_sdk-0.1.0a15 → quicknode_sdk-0.1.0a17}/crates/core/Cargo.toml +5 -1
  5. {quicknode_sdk-0.1.0a15 → quicknode_sdk-0.1.0a17}/crates/core/README.md +56 -3
  6. quicknode_sdk-0.1.0a17/crates/core/examples/admin.rs +59 -0
  7. {quicknode_sdk-0.1.0a15 → quicknode_sdk-0.1.0a17}/crates/core/examples/admin_e2e.rs +53 -2
  8. {quicknode_sdk-0.1.0a15 → quicknode_sdk-0.1.0a17}/crates/core/examples/streams_e2e.rs +1 -1
  9. quicknode_sdk-0.1.0a17/crates/core/src/admin/endpoint_metrics.rs +148 -0
  10. {quicknode_sdk-0.1.0a15 → quicknode_sdk-0.1.0a17}/crates/core/src/admin/endpoint_rate_limits.rs +44 -0
  11. quicknode_sdk-0.1.0a17/crates/core/src/admin/endpoint_urls.rs +47 -0
  12. {quicknode_sdk-0.1.0a15 → quicknode_sdk-0.1.0a17}/crates/core/src/admin/endpoints.rs +6 -0
  13. {quicknode_sdk-0.1.0a15 → quicknode_sdk-0.1.0a17}/crates/core/src/admin/mod.rs +308 -9
  14. {quicknode_sdk-0.1.0a15 → quicknode_sdk-0.1.0a17}/crates/core/src/kvstore/mod.rs +85 -1
  15. {quicknode_sdk-0.1.0a15 → quicknode_sdk-0.1.0a17}/crates/core/src/streams/mod.rs +2 -2
  16. {quicknode_sdk-0.1.0a15 → quicknode_sdk-0.1.0a17}/crates/core/src/streams/stream.rs +2 -3
  17. {quicknode_sdk-0.1.0a15 → quicknode_sdk-0.1.0a17}/crates/core/src/webhooks/mod.rs +28 -0
  18. {quicknode_sdk-0.1.0a15 → quicknode_sdk-0.1.0a17}/crates/python/src/lib.rs +64 -5
  19. {quicknode_sdk-0.1.0a15 → quicknode_sdk-0.1.0a17}/pyproject.toml +1 -1
  20. {quicknode_sdk-0.1.0a15 → quicknode_sdk-0.1.0a17}/python/README.md +51 -3
  21. {quicknode_sdk-0.1.0a15 → quicknode_sdk-0.1.0a17}/python/sdk/__init__.py +12 -0
  22. {quicknode_sdk-0.1.0a15 → quicknode_sdk-0.1.0a17}/python/sdk/__init__.pyi +12 -0
  23. {quicknode_sdk-0.1.0a15 → quicknode_sdk-0.1.0a17}/python/sdk/_core/__init__.pyi +241 -8
  24. {quicknode_sdk-0.1.0a15 → quicknode_sdk-0.1.0a17}/python/sdk/init_manual_override.pyi +12 -0
  25. quicknode_sdk-0.1.0a15/crates/core/examples/admin.rs +0 -35
  26. quicknode_sdk-0.1.0a15/crates/core/src/admin/endpoint_metrics.rs +0 -72
  27. {quicknode_sdk-0.1.0a15 → quicknode_sdk-0.1.0a17}/LICENSE +0 -0
  28. {quicknode_sdk-0.1.0a15 → quicknode_sdk-0.1.0a17}/crates/core/LICENSE +0 -0
  29. {quicknode_sdk-0.1.0a15 → quicknode_sdk-0.1.0a17}/crates/core/examples/kvstore_e2e.rs +0 -0
  30. {quicknode_sdk-0.1.0a15 → quicknode_sdk-0.1.0a17}/crates/core/examples/streams.rs +0 -0
  31. {quicknode_sdk-0.1.0a15 → quicknode_sdk-0.1.0a17}/crates/core/examples/webhooks_e2e.rs +0 -0
  32. {quicknode_sdk-0.1.0a15 → quicknode_sdk-0.1.0a17}/crates/core/src/admin/billing.rs +0 -0
  33. {quicknode_sdk-0.1.0a15 → quicknode_sdk-0.1.0a17}/crates/core/src/admin/bulk.rs +0 -0
  34. {quicknode_sdk-0.1.0a15 → quicknode_sdk-0.1.0a17}/crates/core/src/admin/chains.rs +0 -0
  35. {quicknode_sdk-0.1.0a15 → quicknode_sdk-0.1.0a17}/crates/core/src/admin/endpoint_security.rs +0 -0
  36. {quicknode_sdk-0.1.0a15 → quicknode_sdk-0.1.0a17}/crates/core/src/admin/logs.rs +0 -0
  37. {quicknode_sdk-0.1.0a15 → quicknode_sdk-0.1.0a17}/crates/core/src/admin/tags.rs +0 -0
  38. {quicknode_sdk-0.1.0a15 → quicknode_sdk-0.1.0a17}/crates/core/src/admin/teams.rs +0 -0
  39. {quicknode_sdk-0.1.0a15 → quicknode_sdk-0.1.0a17}/crates/core/src/admin/usage.rs +0 -0
  40. {quicknode_sdk-0.1.0a15 → quicknode_sdk-0.1.0a17}/crates/core/src/config.rs +0 -0
  41. {quicknode_sdk-0.1.0a15 → quicknode_sdk-0.1.0a17}/crates/core/src/errors.rs +0 -0
  42. {quicknode_sdk-0.1.0a15 → quicknode_sdk-0.1.0a17}/crates/core/src/lib.rs +0 -0
  43. {quicknode_sdk-0.1.0a15 → quicknode_sdk-0.1.0a17}/crates/core/src/webhooks/webhook.rs +0 -0
  44. {quicknode_sdk-0.1.0a15 → quicknode_sdk-0.1.0a17}/crates/python/Cargo.toml +0 -0
  45. {quicknode_sdk-0.1.0a15 → quicknode_sdk-0.1.0a17}/crates/python/src/errors.rs +0 -0
  46. {quicknode_sdk-0.1.0a15 → quicknode_sdk-0.1.0a17}/crates/python/src/streams_destination.rs +0 -0
  47. {quicknode_sdk-0.1.0a15 → quicknode_sdk-0.1.0a17}/crates/python/src/webhooks_template.rs +0 -0
  48. {quicknode_sdk-0.1.0a15 → quicknode_sdk-0.1.0a17}/python/sdk/py.typed +0 -0
@@ -441,9 +441,9 @@ checksum = "92773504d58c093f6de2459af4af33faa518c13451eb8f2b5698ed3d36e7c813"
441
441
 
442
442
  [[package]]
443
443
  name = "either"
444
- version = "1.15.0"
444
+ version = "1.16.0"
445
445
  source = "registry+https://github.com/rust-lang/crates.io-index"
446
- checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719"
446
+ checksum = "91622ff5e7162018101f2fea40d6ebf4a78bbe5a49736a2020649edf9693679e"
447
447
 
448
448
  [[package]]
449
449
  name = "encoding_rs"
@@ -1679,7 +1679,7 @@ dependencies = [
1679
1679
 
1680
1680
  [[package]]
1681
1681
  name = "quicknode-sdk"
1682
- version = "0.1.0-alpha.15"
1682
+ version = "0.1.0-alpha.17"
1683
1683
  dependencies = [
1684
1684
  "bon",
1685
1685
  "config",
@@ -2156,7 +2156,7 @@ dependencies = [
2156
2156
 
2157
2157
  [[package]]
2158
2158
  name = "sdk-node"
2159
- version = "0.1.0-alpha.15"
2159
+ version = "0.1.0-alpha.17"
2160
2160
  dependencies = [
2161
2161
  "napi",
2162
2162
  "napi-build",
@@ -2168,7 +2168,7 @@ dependencies = [
2168
2168
 
2169
2169
  [[package]]
2170
2170
  name = "sdk-python"
2171
- version = "0.1.0-alpha.15"
2171
+ version = "0.1.0-alpha.17"
2172
2172
  dependencies = [
2173
2173
  "pyo3",
2174
2174
  "pyo3-async-runtimes",
@@ -2179,7 +2179,7 @@ dependencies = [
2179
2179
 
2180
2180
  [[package]]
2181
2181
  name = "sdk-python-stubs"
2182
- version = "0.1.0-alpha.15"
2182
+ version = "0.1.0-alpha.17"
2183
2183
  dependencies = [
2184
2184
  "pyo3-stub-gen",
2185
2185
  "sdk-python",
@@ -2187,7 +2187,7 @@ dependencies = [
2187
2187
 
2188
2188
  [[package]]
2189
2189
  name = "sdk-ruby"
2190
- version = "0.1.0-alpha.15"
2190
+ version = "0.1.0-alpha.17"
2191
2191
  dependencies = [
2192
2192
  "magnus",
2193
2193
  "quicknode-sdk",
@@ -3,7 +3,7 @@ resolver = "2"
3
3
  members = ["crates/core", "crates/python"]
4
4
 
5
5
  [workspace.package]
6
- version = "0.1.0-alpha.15"
6
+ version = "0.1.0-alpha.17"
7
7
  edition = "2021"
8
8
  license = "MIT"
9
9
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: quicknode-sdk
3
- Version: 0.1.0a15
3
+ Version: 0.1.0a17
4
4
  Classifier: Intended Audience :: Developers
5
5
  Classifier: License :: OSI Approved :: MIT License
6
6
  Classifier: Operating System :: POSIX :: Linux
@@ -52,6 +52,7 @@ This is one of four language bindings published from the same Rust core. See the
52
52
  - [IP Custom Headers](#ip-custom-headers)
53
53
  - [Method Rate Limits](#method-rate-limits)
54
54
  - [Endpoint Rate Limits](#endpoint-rate-limits)
55
+ - [Endpoint URLs](#endpoint-urls)
55
56
  - [Metrics](#metrics)
56
57
  - [Chains](#chains)
57
58
  - [Billing](#billing)
@@ -807,7 +808,7 @@ await qn.admin.delete_method_rate_limit("ep-123", "rl-1")
807
808
 
808
809
  ##### `update_rate_limits` / `updateRateLimits`
809
810
 
810
- Updates the endpoint-level RPS / RPM / RPD caps.
811
+ 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`.
811
812
 
812
813
  **Parameters**: `id` (endpoint id, required); `rate_limits`: `RateLimitSettings` (`rps`, `rpm`, `rpd`, all optional).
813
814
 
@@ -818,6 +819,53 @@ Updates the endpoint-level RPS / RPM / RPD caps.
818
819
  await qn.admin.update_rate_limits("ep-123", rps=100, rpm=5000)
819
820
  ```
820
821
 
822
+ ##### `get_rate_limits` / `getRateLimits`
823
+
824
+ 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` (camelCased `id` in Node) you can pass to `delete_rate_limit_override`.
825
+
826
+ **Parameters**: `id` (endpoint id, required).
827
+
828
+ **Returns**: `GetRateLimitsResponse` with `data.rate_limits: RateLimitEntry[]`.
829
+
830
+ ```python
831
+ # Python
832
+ resp = await qn.admin.get_rate_limits("123")
833
+ for row in resp.data.rate_limits:
834
+ print(row.bucket, row.rate_limit, row.source, row.id)
835
+ ```
836
+
837
+ ##### `delete_rate_limit_override` / `deleteRateLimitOverride`
838
+
839
+ 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.
840
+
841
+ **Parameters**: `id` (endpoint id, required); `override_id` / `overrideId` (UUID returned by `get_rate_limits`, required).
842
+
843
+ **Returns**: nothing.
844
+
845
+ ```python
846
+ # Python
847
+ await qn.admin.delete_rate_limit_override("123", "ovr-uuid")
848
+ ```
849
+
850
+ #### Endpoint URLs
851
+
852
+ ##### `get_endpoint_urls` / `getEndpointUrls`
853
+
854
+ Returns the HTTP and WebSocket URLs for the endpoint without fetching the full endpoint record. For multichain endpoints, `multichain_urls` / `multichainUrls` is a per-network map of additional URLs; for single-chain endpoints it is `None` / `null`.
855
+
856
+ **Parameters**: `id` (endpoint id, required).
857
+
858
+ **Returns**: `GetEndpointUrlsResponse` with `data.http_url`, `data.wss_url`, and `data.multichain_urls`.
859
+
860
+ ```python
861
+ # Python
862
+ resp = await qn.admin.get_endpoint_urls("123")
863
+ print(resp.data.http_url)
864
+ if resp.data.multichain_urls:
865
+ for network, urls in resp.data.multichain_urls.items():
866
+ print(network, urls.http_url)
867
+ ```
868
+
821
869
  #### Metrics
822
870
 
823
871
  ##### `get_endpoint_metrics` / `getEndpointMetrics`
@@ -826,7 +874,7 @@ Returns metric series for an endpoint over a time period.
826
874
 
827
875
  **Parameters**: `id` (endpoint id, required); body: `period` (`"hour"` | `"day"` | `"week"` | `"month"`), `metric` (e.g. `"method_calls_over_time"`, `"response_status_breakdown"`).
828
876
 
829
- **Returns**: `GetEndpointMetricsResponse` with `data: EndpointMetric[]`.
877
+ **Returns**: `GetEndpointMetricsResponse` with `data: list[EndpointMetric]`. Each `EndpointMetric` has a `tag: list[str]` and a `data: list[list[int]]` of `[timestamp, value]` pairs. Single-axis series (e.g. `response_time_over_time` with a percentile) come back as a one-element tag like `["p95"]`; multi-axis series come back as `["network", "arbitrum-mainnet"]`.
830
878
 
831
879
  ```python
832
880
  # Python
@@ -843,7 +891,7 @@ Returns account-level metric series. Supports an optional `percentile` (e.g. `"p
843
891
 
844
892
  **Parameters**: `period` (required), `metric` (required), `percentile` (string, optional).
845
893
 
846
- **Returns**: `GetAccountMetricsResponse` with `data: EndpointMetric[]`.
894
+ **Returns**: `GetAccountMetricsResponse` with `data: list[EndpointMetric]`. See `get_endpoint_metrics` above for the `tag: list[str]` shape.
847
895
 
848
896
  ```python
849
897
  # Python
@@ -1,6 +1,6 @@
1
1
  [package]
2
2
  name = "quicknode-sdk"
3
- version = "0.1.0-alpha.15"
3
+ version = "0.1.0-alpha.17"
4
4
  edition.workspace = true
5
5
  license.workspace = true
6
6
  description = "Core library for quicknode sdk"
@@ -47,6 +47,10 @@ required-features = ["rust"]
47
47
  name = "streams"
48
48
  required-features = ["rust"]
49
49
 
50
+ [[example]]
51
+ name = "streams_e2e"
52
+ required-features = ["rust"]
53
+
50
54
  [[example]]
51
55
  name = "webhooks_e2e"
52
56
  required-features = ["rust"]
@@ -28,6 +28,7 @@ This is one of four language bindings published from the same Rust core. See the
28
28
  - [IP Custom Headers](#ip-custom-headers)
29
29
  - [Method Rate Limits](#method-rate-limits)
30
30
  - [Endpoint Rate Limits](#endpoint-rate-limits)
31
+ - [Endpoint URLs](#endpoint-urls)
31
32
  - [Metrics](#metrics)
32
33
  - [Chains](#chains)
33
34
  - [Billing](#billing)
@@ -815,7 +816,7 @@ qn.admin.delete_method_rate_limit("ep-123", "rl-1").await?;
815
816
 
816
817
  ##### `update_rate_limits` / `updateRateLimits`
817
818
 
818
- Updates the endpoint-level RPS / RPM / RPD caps.
819
+ 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`.
819
820
 
820
821
  **Parameters**: `id` (endpoint id, required); `rate_limits`: `RateLimitSettings` (`rps`, `rpm`, `rpd`, all optional).
821
822
 
@@ -828,6 +829,58 @@ let params = UpdateRateLimitsRequest { rate_limits };
828
829
  qn.admin.update_rate_limits("ep-123", &params).await?;
829
830
  ```
830
831
 
832
+ ##### `get_rate_limits` / `getRateLimits`
833
+
834
+ 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`.
835
+
836
+ **Parameters**: `id` (endpoint id, required).
837
+
838
+ **Returns**: `GetRateLimitsResponse` with `data.rate_limits: Vec<RateLimitEntry>`.
839
+
840
+ ```rust
841
+ // Rust
842
+ let resp = qn.admin.get_rate_limits("123").await?;
843
+ for row in resp.data.unwrap().rate_limits {
844
+ println!("{} {} {} {:?}", row.bucket, row.rate_limit, row.source, row.id);
845
+ }
846
+ ```
847
+
848
+ ##### `delete_rate_limit_override` / `deleteRateLimitOverride`
849
+
850
+ 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.
851
+
852
+ **Parameters**: `id` (endpoint id, required); `override_id` (UUID returned by `get_rate_limits`, required).
853
+
854
+ **Returns**: nothing.
855
+
856
+ ```rust
857
+ // Rust
858
+ qn.admin.delete_rate_limit_override("123", "ovr-uuid").await?;
859
+ ```
860
+
861
+ #### Endpoint URLs
862
+
863
+ ##### `get_endpoint_urls` / `getEndpointUrls`
864
+
865
+ 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`.
866
+
867
+ **Parameters**: `id` (endpoint id, required).
868
+
869
+ **Returns**: `GetEndpointUrlsResponse` with `data.http_url`, `data.wss_url`, and `data.multichain_urls`.
870
+
871
+ ```rust
872
+ // Rust
873
+ let resp = qn.admin.get_endpoint_urls("123").await?;
874
+ if let Some(data) = resp.data {
875
+ println!("{}", data.http_url);
876
+ if let Some(mc) = data.multichain_urls {
877
+ for (network, urls) in mc {
878
+ println!("{network} {}", urls.http_url);
879
+ }
880
+ }
881
+ }
882
+ ```
883
+
831
884
  #### Metrics
832
885
 
833
886
  ##### `get_endpoint_metrics` / `getEndpointMetrics`
@@ -836,7 +889,7 @@ Returns metric series for an endpoint over a time period.
836
889
 
837
890
  **Parameters**: `id` (endpoint id, required); body: `period` (`"hour"` | `"day"` | `"week"` | `"month"`), `metric` (e.g. `"method_calls_over_time"`, `"response_status_breakdown"`).
838
891
 
839
- **Returns**: `GetEndpointMetricsResponse` with `data: EndpointMetric[]`.
892
+ **Returns**: `GetEndpointMetricsResponse` with `data: Vec<EndpointMetric>`. Each `EndpointMetric` has `tag: Vec<String>` and `data: Vec<Vec<i64>>` of `[timestamp, value]` pairs. Single-axis series (e.g. `response_time_over_time` with a percentile) come back as a one-element tag like `vec!["p95"]`; multi-axis series come back as `vec!["network", "arbitrum-mainnet"]`.
840
893
 
841
894
  ```rust
842
895
  // Rust
@@ -853,7 +906,7 @@ Returns account-level metric series. Supports an optional `percentile` (e.g. `"p
853
906
 
854
907
  **Parameters**: `period` (required), `metric` (required), `percentile` (string, optional).
855
908
 
856
- **Returns**: `GetAccountMetricsResponse` with `data: EndpointMetric[]`.
909
+ **Returns**: `GetAccountMetricsResponse` with `data: Vec<EndpointMetric>`. See `get_endpoint_metrics` above for the `tag: Vec<String>` shape.
857
910
 
858
911
  ```rust
859
912
  // Rust
@@ -0,0 +1,59 @@
1
+ use quicknode_sdk::{admin::GetEndpointsRequest, QuicknodeSdk, SdkFullConfig};
2
+
3
+ #[tokio::main]
4
+ #[allow(clippy::unwrap_used, clippy::expect_used)]
5
+ async fn main() {
6
+ let config = SdkFullConfig::from_env().expect("Config from env failed");
7
+ let qn = QuicknodeSdk::new(&config).expect("sdk failed to initialize");
8
+
9
+ let params = GetEndpointsRequest::builder()
10
+ .limit(20)
11
+ .sort_by("created_at".to_string())
12
+ .sort_direction("desc".to_string())
13
+ .build();
14
+
15
+ let first_endpoint_id = match qn.admin.get_endpoints(&params).await {
16
+ Ok(resp) => {
17
+ if let Some(p) = &resp.pagination {
18
+ println!(
19
+ "{} of {} (offset {}, limit {})",
20
+ resp.data.len(),
21
+ p.total,
22
+ p.offset,
23
+ p.limit
24
+ );
25
+ }
26
+ for ep in &resp.data {
27
+ println!(
28
+ "{} | {} | {} | {} | dedicated={} flat={} multichain={}",
29
+ ep.id,
30
+ ep.name,
31
+ ep.status,
32
+ ep.chain,
33
+ ep.is_dedicated,
34
+ ep.is_flat_rate,
35
+ ep.is_multichain
36
+ );
37
+ }
38
+ resp.data.first().map(|ep| ep.id.clone())
39
+ }
40
+ Err(e) => {
41
+ eprintln!("get_endpoints error: {e}");
42
+ None
43
+ }
44
+ };
45
+
46
+ let Some(endpoint_id) = first_endpoint_id else {
47
+ return;
48
+ };
49
+
50
+ match qn.admin.get_rate_limits(&endpoint_id).await {
51
+ Ok(resp) => println!("get_rate_limits: {:?}", resp.data),
52
+ Err(e) => eprintln!("get_rate_limits error: {e}"),
53
+ }
54
+
55
+ match qn.admin.get_endpoint_urls(&endpoint_id).await {
56
+ Ok(resp) => println!("get_endpoint_urls: {:?}", resp.data),
57
+ Err(e) => eprintln!("get_endpoint_urls error: {e}"),
58
+ }
59
+ }
@@ -97,7 +97,17 @@ async fn main() {
97
97
  })
98
98
  .await
99
99
  {
100
- Ok(resp) => println!("get_account_metrics: {} series", resp.data.len()),
100
+ Ok(resp) => {
101
+ let first = resp
102
+ .data
103
+ .first()
104
+ .map(|m| m.tag.join(":"))
105
+ .unwrap_or_else(|| "<none>".to_string());
106
+ println!(
107
+ "get_account_metrics: {} series, first tag: {first}",
108
+ resp.data.len()
109
+ );
110
+ }
101
111
  Err(e) => eprintln!("get_account_metrics error: {e}"),
102
112
  }
103
113
 
@@ -254,7 +264,17 @@ async fn main() {
254
264
  )
255
265
  .await
256
266
  {
257
- Ok(resp) => println!("get_endpoint_metrics: {} series", resp.data.len()),
267
+ Ok(resp) => {
268
+ let first = resp
269
+ .data
270
+ .first()
271
+ .map(|m| m.tag.join(":"))
272
+ .unwrap_or_else(|| "<none>".to_string());
273
+ println!(
274
+ "get_endpoint_metrics: {} series, first tag: {first}",
275
+ resp.data.len()
276
+ );
277
+ }
258
278
  Err(e) => eprintln!("get_endpoint_metrics error: {e}"),
259
279
  }
260
280
 
@@ -534,6 +554,11 @@ async fn main() {
534
554
 
535
555
  // --- Rate limits ---
536
556
 
557
+ match qn.admin.get_rate_limits(&endpoint_id).await {
558
+ Ok(resp) => println!("get_rate_limits before PATCH: {:?}", resp.data),
559
+ Err(e) => eprintln!("get_rate_limits error: {e}"),
560
+ }
561
+
537
562
  match qn
538
563
  .admin
539
564
  .update_rate_limits(
@@ -551,6 +576,16 @@ async fn main() {
551
576
  Err(e) => eprintln!("update_rate_limits error: {e}"),
552
577
  }
553
578
 
579
+ match qn.admin.get_rate_limits(&endpoint_id).await {
580
+ Ok(resp) => println!("get_rate_limits after PATCH: {:?}", resp.data),
581
+ Err(e) => eprintln!("get_rate_limits error: {e}"),
582
+ }
583
+
584
+ match qn.admin.get_endpoint_urls(&endpoint_id).await {
585
+ Ok(resp) => println!("get_endpoint_urls: {:?}", resp.data),
586
+ Err(e) => eprintln!("get_endpoint_urls error: {e}"),
587
+ }
588
+
554
589
  match qn.admin.get_method_rate_limits(&endpoint_id).await {
555
590
  Ok(resp) => println!("get_method_rate_limits: {:?}", resp.data),
556
591
  Err(e) => eprintln!("get_method_rate_limits error: {e}"),
@@ -806,6 +841,22 @@ async fn main() {
806
841
  other => eprintln!("expected Api 404, got {other:?}"),
807
842
  }
808
843
 
844
+ // 1b) Rate-limit override delete with a bogus override id — also a 404.
845
+ match qn
846
+ .admin
847
+ .delete_rate_limit_override("does-not-exist", "00000000-0000-0000-0000-000000000000")
848
+ .await
849
+ {
850
+ Err(SdkError::Api { status, body }) => {
851
+ println!(
852
+ "delete_rate_limit_override api error {status}: {}",
853
+ &body[..body.len().min(80)]
854
+ );
855
+ assert_eq!(status.as_u16(), 404);
856
+ }
857
+ other => eprintln!("expected Api 404 from delete_rate_limit_override, got {other:?}"),
858
+ }
859
+
809
860
  // 2) Timeout path — unreachable base URL + 1s timeout forces a timeout
810
861
  // from reqwest, which maps to SdkError::Http with http_kind() == Timeout.
811
862
  let blackhole = SdkFullConfig {
@@ -33,7 +33,7 @@ async fn main() {
33
33
  network: "ethereum-mainnet".to_string(),
34
34
  dataset: StreamDataset::Block,
35
35
  block: "17811625".to_string(),
36
- filter_function: Some("ZnVuY3Rpb24gbWFpbihkYXRhKSB7IHJldHVybiBkYXRhOyB9".to_string()),
36
+ filter_function: "ZnVuY3Rpb24gbWFpbihkYXRhKSB7IHJldHVybiBkYXRhOyB9".to_string(),
37
37
  filter_language: None,
38
38
  address_book_config: None,
39
39
  };
@@ -0,0 +1,148 @@
1
+ #[cfg(feature = "node")]
2
+ use napi_derive::napi;
3
+ #[cfg(feature = "python")]
4
+ use pyo3::pyclass;
5
+ #[cfg(feature = "python")]
6
+ use pyo3_stub_gen::derive::gen_stub_pyclass;
7
+ use serde::{Deserialize, Deserializer, Serialize};
8
+
9
+ // The metrics endpoints return `tag` as either a plain string (single-axis
10
+ // series like `"total"` or `"p95"`) or a tuple like `["network", "mainnet"]`
11
+ // (multi-axis series). Normalise both to a `Vec<String>` so callers always
12
+ // see an array.
13
+ fn tag_as_vec<'de, D>(deserializer: D) -> Result<Vec<String>, D::Error>
14
+ where
15
+ D: Deserializer<'de>,
16
+ {
17
+ use serde::de::Error;
18
+ match serde_json::Value::deserialize(deserializer)? {
19
+ serde_json::Value::String(s) => Ok(vec![s]),
20
+ serde_json::Value::Array(items) => items
21
+ .into_iter()
22
+ .map(|v| match v {
23
+ serde_json::Value::String(s) => Ok(s),
24
+ other => Err(D::Error::custom(format!(
25
+ "expected string in tag array, got {other}"
26
+ ))),
27
+ })
28
+ .collect(),
29
+ serde_json::Value::Null => Ok(Vec::new()),
30
+ other => Err(D::Error::custom(format!(
31
+ "expected string or array of strings for tag, got {other}"
32
+ ))),
33
+ }
34
+ }
35
+
36
+ /// Parameters for `get_endpoint_metrics`.
37
+ #[cfg_attr(feature = "python", gen_stub_pyclass)]
38
+ #[cfg_attr(feature = "python", pyclass(get_all, set_all))]
39
+ #[cfg_attr(feature = "node", napi(object))]
40
+ #[derive(Debug, Clone, Default, Serialize)]
41
+ pub struct GetEndpointMetricsRequest {
42
+ /// Time period (`hour`, `day`, `week`, or `month`).
43
+ pub period: String,
44
+ /// Metric name (e.g. `method_calls_over_time`, `response_status_breakdown`).
45
+ pub metric: String,
46
+ }
47
+
48
+ /// Parameters for `get_account_metrics`.
49
+ #[cfg_attr(feature = "python", gen_stub_pyclass)]
50
+ #[cfg_attr(feature = "python", pyclass(get_all, set_all))]
51
+ #[cfg_attr(feature = "node", napi(object))]
52
+ #[derive(Debug, Clone, Default, Serialize)]
53
+ pub struct GetAccountMetricsRequest {
54
+ /// Time period (`hour`, `day`, `week`, or `month`).
55
+ pub period: String,
56
+ /// Metric name (e.g. `method_calls_over_time`, `credits_over_time`).
57
+ pub metric: String,
58
+ /// Optional percentile for latency metrics (e.g. `p50`, `p95`, `p99`).
59
+ #[serde(skip_serializing_if = "Option::is_none")]
60
+ pub percentile: Option<String>,
61
+ }
62
+
63
+ /// A single metric series, consisting of a descriptive tag and timestamped data points.
64
+ #[cfg_attr(feature = "python", gen_stub_pyclass)]
65
+ #[cfg_attr(feature = "python", pyclass(get_all, set_all))]
66
+ #[cfg_attr(feature = "node", napi(object))]
67
+ #[derive(Debug, Clone, Serialize, Deserialize)]
68
+ pub struct EndpointMetric {
69
+ /// Data points, each as `[timestamp, value]`.
70
+ pub data: Vec<Vec<i64>>,
71
+ /// Tag identifying the series. Single-axis metrics return a one-element
72
+ /// vector (e.g. `["total"]`, `["p95"]`); multi-axis metrics return the
73
+ /// key/value pair (e.g. `["network", "arbitrum-mainnet"]`).
74
+ #[serde(deserialize_with = "tag_as_vec")]
75
+ pub tag: Vec<String>,
76
+ }
77
+
78
+ /// Response from `get_endpoint_metrics`.
79
+ #[cfg_attr(feature = "python", gen_stub_pyclass)]
80
+ #[cfg_attr(feature = "python", pyclass(get_all, set_all))]
81
+ #[cfg_attr(feature = "node", napi(object))]
82
+ #[derive(Debug, Clone, Serialize, Deserialize)]
83
+ pub struct GetEndpointMetricsResponse {
84
+ /// Metric series returned for the endpoint.
85
+ #[serde(default)]
86
+ pub data: Vec<EndpointMetric>,
87
+ /// Error message when the request did not succeed.
88
+ pub error: Option<String>,
89
+ }
90
+
91
+ /// Response from `get_account_metrics`.
92
+ #[cfg_attr(feature = "python", gen_stub_pyclass)]
93
+ #[cfg_attr(feature = "python", pyclass(get_all, set_all))]
94
+ #[cfg_attr(feature = "node", napi(object))]
95
+ #[derive(Debug, Clone, Serialize, Deserialize)]
96
+ pub struct GetAccountMetricsResponse {
97
+ /// Metric series returned for the account.
98
+ #[serde(default)]
99
+ pub data: Vec<EndpointMetric>,
100
+ /// Error message when the request did not succeed.
101
+ pub error: Option<String>,
102
+ }
103
+
104
+ #[cfg(test)]
105
+ #[allow(clippy::unwrap_used, clippy::expect_used, clippy::panic)]
106
+ mod tests {
107
+ use super::EndpointMetric;
108
+
109
+ #[test]
110
+ fn tag_deserializes_from_string() {
111
+ let m: EndpointMetric =
112
+ serde_json::from_str(r#"{"data": [[1, 2]], "tag": "total"}"#).unwrap();
113
+ assert_eq!(m.tag, vec!["total".to_string()]);
114
+ }
115
+
116
+ #[test]
117
+ fn tag_deserializes_from_tuple() {
118
+ let m: EndpointMetric =
119
+ serde_json::from_str(r#"{"data": [[1, 2]], "tag": ["network", "arbitrum-mainnet"]}"#)
120
+ .unwrap();
121
+ assert_eq!(
122
+ m.tag,
123
+ vec!["network".to_string(), "arbitrum-mainnet".to_string()]
124
+ );
125
+ }
126
+
127
+ #[test]
128
+ fn tag_deserializes_from_null() {
129
+ let m: EndpointMetric = serde_json::from_str(r#"{"data": [[1, 2]], "tag": null}"#).unwrap();
130
+ assert!(m.tag.is_empty());
131
+ }
132
+
133
+ #[test]
134
+ fn tag_rejects_mixed_array() {
135
+ let err =
136
+ serde_json::from_str::<EndpointMetric>(r#"{"data": [], "tag": ["x", 5]}"#).unwrap_err();
137
+ assert!(err.to_string().contains("expected string in tag array"));
138
+ }
139
+
140
+ #[test]
141
+ fn tag_rejects_object() {
142
+ let err = serde_json::from_str::<EndpointMetric>(r#"{"data": [], "tag": {"k": "v"}}"#)
143
+ .unwrap_err();
144
+ assert!(err
145
+ .to_string()
146
+ .contains("expected string or array of strings for tag"));
147
+ }
148
+ }
@@ -137,3 +137,47 @@ pub struct UpdateRateLimitsRequest {
137
137
  /// Rate limit values to apply.
138
138
  pub rate_limits: RateLimitSettings,
139
139
  }
140
+
141
+ /// A single rate-limit row returned by `get_rate_limits`, identifying the
142
+ /// bucket (`rps`/`rpm`/`rpd`), the value enforced, and whether the value comes
143
+ /// from the plan default or a user-set override.
144
+ #[cfg_attr(feature = "python", gen_stub_pyclass)]
145
+ #[cfg_attr(feature = "python", pyclass(get_all, set_all))]
146
+ #[cfg_attr(feature = "node", napi(object))]
147
+ #[derive(Debug, Clone, Serialize, Deserialize)]
148
+ pub struct RateLimitEntry {
149
+ /// Which bucket this row applies to: `rps`, `rpm`, or `rpd`.
150
+ pub bucket: String,
151
+ /// The enforced value for this bucket.
152
+ pub rate_limit: i32,
153
+ /// Where the value comes from: `plan_default` or `user_override`.
154
+ pub source: String,
155
+ /// Row identifier. Present on `user_override` rows — pass it to
156
+ /// `delete_rate_limit_override` to remove the override. May be absent on
157
+ /// `plan_default` rows and cannot be deleted there.
158
+ #[serde(default)]
159
+ pub id: Option<String>,
160
+ }
161
+
162
+ /// Inner data for `get_rate_limits`.
163
+ #[cfg_attr(feature = "python", gen_stub_pyclass)]
164
+ #[cfg_attr(feature = "python", pyclass(get_all, set_all))]
165
+ #[cfg_attr(feature = "node", napi(object))]
166
+ #[derive(Debug, Clone, Serialize, Deserialize)]
167
+ pub struct GetRateLimitsData {
168
+ /// One row per enforced bucket.
169
+ #[serde(default)]
170
+ pub rate_limits: Vec<RateLimitEntry>,
171
+ }
172
+
173
+ /// Response from `get_rate_limits`.
174
+ #[cfg_attr(feature = "python", gen_stub_pyclass)]
175
+ #[cfg_attr(feature = "python", pyclass(get_all, set_all))]
176
+ #[cfg_attr(feature = "node", napi(object))]
177
+ #[derive(Debug, Clone, Serialize, Deserialize)]
178
+ pub struct GetRateLimitsResponse {
179
+ /// Rate-limit rows with their source.
180
+ pub data: Option<GetRateLimitsData>,
181
+ /// Error message when the request did not succeed.
182
+ pub error: Option<String>,
183
+ }
@@ -0,0 +1,47 @@
1
+ #[cfg(feature = "node")]
2
+ use napi_derive::napi;
3
+ #[cfg(feature = "python")]
4
+ use pyo3::pyclass;
5
+ #[cfg(feature = "python")]
6
+ use pyo3_stub_gen::derive::gen_stub_pyclass;
7
+ use serde::{Deserialize, Serialize};
8
+ use std::collections::HashMap;
9
+
10
+ /// HTTP/WSS URL pair for a single network on a multichain endpoint.
11
+ #[cfg_attr(feature = "python", gen_stub_pyclass)]
12
+ #[cfg_attr(feature = "python", pyclass(get_all, set_all))]
13
+ #[cfg_attr(feature = "node", napi(object))]
14
+ #[derive(Debug, Clone, Serialize, Deserialize)]
15
+ pub struct EndpointUrl {
16
+ /// HTTP RPC URL.
17
+ pub http_url: String,
18
+ /// WebSocket RPC URL, when available.
19
+ pub wss_url: Option<String>,
20
+ }
21
+
22
+ /// Inner data for `get_endpoint_urls` — the http/wss URLs for the endpoint and,
23
+ /// when the endpoint is multichain, a per-network map of additional URLs.
24
+ #[cfg_attr(feature = "python", gen_stub_pyclass)]
25
+ #[cfg_attr(feature = "python", pyclass(get_all, set_all))]
26
+ #[cfg_attr(feature = "node", napi(object))]
27
+ #[derive(Debug, Clone, Serialize, Deserialize)]
28
+ pub struct GetEndpointUrlsData {
29
+ /// HTTP RPC URL.
30
+ pub http_url: String,
31
+ /// WebSocket RPC URL, when available.
32
+ pub wss_url: Option<String>,
33
+ /// Per-network URLs for multichain endpoints; `None` for single-chain endpoints.
34
+ pub multichain_urls: Option<HashMap<String, EndpointUrl>>,
35
+ }
36
+
37
+ /// Response from `get_endpoint_urls`.
38
+ #[cfg_attr(feature = "python", gen_stub_pyclass)]
39
+ #[cfg_attr(feature = "python", pyclass(get_all, set_all))]
40
+ #[cfg_attr(feature = "node", napi(object))]
41
+ #[derive(Debug, Clone, Serialize, Deserialize)]
42
+ pub struct GetEndpointUrlsResponse {
43
+ /// URLs for the endpoint.
44
+ pub data: Option<GetEndpointUrlsData>,
45
+ /// Error message when the request did not succeed.
46
+ pub error: Option<String>,
47
+ }