From af360d18b3c0305571614205441eb4c70986ae0d Mon Sep 17 00:00:00 2001 From: John Mitsch Date: Wed, 24 Jun 2026 11:06:21 -0400 Subject: [PATCH] fix(admin)!: require all create-security body params (DX-5868) The POST /endpoints/:id/security/* create endpoints reject requests with a 400 when a required body param is missing or blank. Reflect that in the typed surface so callers get a compile/type error instead of a server-side rejection, extending the same change already applied to create_jwt's kid. - core: CreateReferrerRequest.referrer, CreateIpRequest.ip, CreateJwtRequest.public_key, CreateJwtRequest.name are now required String; CreateRequestFilterRequest.method is now required Vec - python: positional required args (no None defaults) - node: required struct fields (regenerated index.d.ts) - ruby: hash_require_string / hash_require_vec_string; rbs updated - examples and all four per-language READMEs updated create_token is unchanged (token is server-generated). --- crates/core/README.md | 8 +++---- crates/core/examples/admin_e2e.rs | 13 +++++----- crates/core/src/admin/endpoint_security.rs | 15 ++++-------- crates/core/src/admin/mod.rs | 2 +- crates/python/src/lib.rs | 14 ++++------- crates/ruby/src/lib.rs | 10 ++++---- npm/README.md | 8 +++---- npm/index.d.ts | 10 ++++---- python/README.md | 8 +++---- python/quicknode_sdk/_core/__init__.pyi | 28 +++++++++++----------- ruby/README.md | 8 +++---- ruby/sig/quicknode_sdk.rbs | 8 +++---- 12 files changed, 61 insertions(+), 71 deletions(-) diff --git a/crates/core/README.md b/crates/core/README.md index d15f5b0..baa7618 100644 --- a/crates/core/README.md +++ b/crates/core/README.md @@ -570,7 +570,7 @@ qn.admin.delete_token("ep-123", "tok-1").await?; Whitelists a referrer URL or domain on an endpoint. -**Parameters**: `id` (endpoint id, required); body: `referrer` (string, optional). +**Parameters**: `id` (endpoint id, required); body: `referrer` (string, required). **Returns**: nothing. @@ -599,7 +599,7 @@ qn.admin.delete_referrer("ep-123", "ref-1").await?; Whitelists an IP address on an endpoint. -**Parameters**: `id` (endpoint id, required); body: `ip` (string, optional). +**Parameters**: `id` (endpoint id, required); body: `ip` (string, required). **Returns**: nothing. @@ -659,7 +659,7 @@ qn.admin.delete_domain_mask("ep-123", "dm-1").await?; Configures JWT validation on an endpoint. -**Parameters**: `id` (endpoint id, required); body: `public_key` (string, optional), `kid` (string, required), `name` (string, optional). +**Parameters**: `id` (endpoint id, required); body: `public_key` (string, required), `kid` (string, required), `name` (string, required). **Returns**: nothing. @@ -692,7 +692,7 @@ Whitelist specific RPC methods on an endpoint. Requests for methods not on the l ##### `create_request_filter` / `createRequestFilter` -**Parameters**: `id` (endpoint id, required); body: `method` (string[], optional). Ruby's Hash key is `methods` (plural). +**Parameters**: `id` (endpoint id, required); body: `method` (string[], required). Ruby's Hash key is `methods` (plural). **Returns**: `CreateRequestFilterResponse` with `data.id`. diff --git a/crates/core/examples/admin_e2e.rs b/crates/core/examples/admin_e2e.rs index 0ab7302..9b7e6e2 100644 --- a/crates/core/examples/admin_e2e.rs +++ b/crates/core/examples/admin_e2e.rs @@ -341,7 +341,7 @@ async fn main() { .create_referrer( &endpoint_id, &CreateReferrerRequest { - referrer: Some("https://example.com".to_string()), + referrer: "https://example.com".to_string(), }, ) .await @@ -378,7 +378,7 @@ async fn main() { .create_ip( &endpoint_id, &CreateIpRequest { - ip: Some("192.0.2.1".to_string()), + ip: "192.0.2.1".to_string(), }, ) .await @@ -452,11 +452,10 @@ async fn main() { .create_jwt( &endpoint_id, &CreateJwtRequest { - public_key: Some( - "-----BEGIN PUBLIC KEY-----\nPLACEHOLDER\n-----END PUBLIC KEY-----".to_string(), - ), + public_key: "-----BEGIN PUBLIC KEY-----\nPLACEHOLDER\n-----END PUBLIC KEY-----" + .to_string(), kid: "kid1".to_string(), - name: Some("example-jwt".to_string()), + name: "example-jwt".to_string(), }, ) .await @@ -492,7 +491,7 @@ async fn main() { .create_request_filter( &endpoint_id, &CreateRequestFilterRequest { - method: Some(vec!["eth_getBalance".to_string()]), + method: vec!["eth_getBalance".to_string()], }, ) .await diff --git a/crates/core/src/admin/endpoint_security.rs b/crates/core/src/admin/endpoint_security.rs index 30241f2..537c93d 100644 --- a/crates/core/src/admin/endpoint_security.rs +++ b/crates/core/src/admin/endpoint_security.rs @@ -104,8 +104,7 @@ pub struct UpdateSecurityOptionsResponse { #[derive(Debug, Clone, Default, Serialize)] pub struct CreateReferrerRequest { /// Allowed referrer URL or domain. - #[serde(skip_serializing_if = "Option::is_none")] - pub referrer: Option, + pub referrer: String, } /// Parameters for `create_ip`. @@ -116,8 +115,7 @@ pub struct CreateReferrerRequest { #[derive(Debug, Clone, Default, Serialize)] pub struct CreateIpRequest { /// IP address to whitelist. - #[serde(skip_serializing_if = "Option::is_none")] - pub ip: Option, + pub ip: String, } /// Parameters for `create_domain_mask`. @@ -140,13 +138,11 @@ pub struct CreateDomainMaskRequest { #[derive(Debug, Clone, Default, Serialize)] pub struct CreateJwtRequest { /// Public key used to verify signed JWTs. - #[serde(skip_serializing_if = "Option::is_none")] - pub public_key: Option, + pub public_key: String, /// Key identifier (`kid`) embedded in JWT headers. pub kid: String, /// Human-readable name for the JWT configuration. - #[serde(skip_serializing_if = "Option::is_none")] - pub name: Option, + pub name: String, } /// Parameters for `create_request_filter`. @@ -157,8 +153,7 @@ pub struct CreateJwtRequest { #[derive(Debug, Clone, Default, Serialize)] pub struct CreateRequestFilterRequest { /// Whitelisted RPC methods; other methods will be blocked. - #[serde(skip_serializing_if = "Option::is_none")] - pub method: Option>, + pub method: Vec, } /// Response from `create_request_filter`. diff --git a/crates/core/src/admin/mod.rs b/crates/core/src/admin/mod.rs index d47be76..301d459 100644 --- a/crates/core/src/admin/mod.rs +++ b/crates/core/src/admin/mod.rs @@ -2548,7 +2548,7 @@ mod tests { .create_referrer( "ep123", &CreateReferrerRequest { - referrer: Some("example.com".to_string()), + referrer: "example.com".to_string(), }, ) .await diff --git a/crates/python/src/lib.rs b/crates/python/src/lib.rs index 70950d3..09351ec 100644 --- a/crates/python/src/lib.rs +++ b/crates/python/src/lib.rs @@ -550,13 +550,12 @@ impl AdminApiClient { /// Adds a referrer to an endpoint's security settings, specifying which /// external URL or domain is permitted to call the endpoint. - #[pyo3(signature = (id, referrer=None))] #[gen_stub(override_return_type(type_repr = "typing.Coroutine[typing.Any, typing.Any, None]"))] fn create_referrer<'py>( &self, py: Python<'py>, id: String, - referrer: Option, + referrer: String, ) -> PyResult> { let client = self.inner.clone(); let params = core::admin::CreateReferrerRequest { referrer }; @@ -588,13 +587,12 @@ impl AdminApiClient { } /// Adds an IP address to an endpoint's security whitelist. - #[pyo3(signature = (id, ip=None))] #[gen_stub(override_return_type(type_repr = "typing.Coroutine[typing.Any, typing.Any, None]"))] fn create_ip<'py>( &self, py: Python<'py>, id: String, - ip: Option, + ip: String, ) -> PyResult> { let client = self.inner.clone(); let params = core::admin::CreateIpRequest { ip }; @@ -667,15 +665,14 @@ impl AdminApiClient { /// Creates a new JWT for endpoint authentication. Accepts a public key, /// key id (`kid`), and token name. - #[pyo3(signature = (id, kid, public_key=None, name=None))] #[gen_stub(override_return_type(type_repr = "typing.Coroutine[typing.Any, typing.Any, None]"))] fn create_jwt<'py>( &self, py: Python<'py>, id: String, kid: String, - public_key: Option, - name: Option, + public_key: String, + name: String, ) -> PyResult> { let client = self.inner.clone(); let params = core::admin::CreateJwtRequest { @@ -712,7 +709,6 @@ impl AdminApiClient { /// Creates a request filter on an endpoint — a method whitelist that /// restricts which RPC methods may be called. Accepts an array of method /// names; other methods are blocked. - #[pyo3(signature = (id, method=None))] #[gen_stub(override_return_type( type_repr = "typing.Coroutine[typing.Any, typing.Any, CreateRequestFilterResponse]" ))] @@ -720,7 +716,7 @@ impl AdminApiClient { &self, py: Python<'py>, id: String, - method: Option>, + method: Vec, ) -> PyResult> { let client = self.inner.clone(); let params = core::admin::CreateRequestFilterRequest { method }; diff --git a/crates/ruby/src/lib.rs b/crates/ruby/src/lib.rs index 4ced74a..2641375 100644 --- a/crates/ruby/src/lib.rs +++ b/crates/ruby/src/lib.rs @@ -601,7 +601,7 @@ impl AdminApiClient { let client = self.inner.clone(); let id = hash_require_string(&opts, "id")?; let params = core::admin::CreateReferrerRequest { - referrer: hash_get_string(&opts, "referrer")?, + referrer: hash_require_string(&opts, "referrer")?, }; runtime() .block_on(client.create_referrer(&id, ¶ms)) @@ -624,7 +624,7 @@ impl AdminApiClient { let client = self.inner.clone(); let id = hash_require_string(&opts, "id")?; let params = core::admin::CreateIpRequest { - ip: hash_get_string(&opts, "ip")?, + ip: hash_require_string(&opts, "ip")?, }; runtime() .block_on(client.create_ip(&id, ¶ms)) @@ -670,9 +670,9 @@ impl AdminApiClient { let client = self.inner.clone(); let id = hash_require_string(&opts, "id")?; let params = core::admin::CreateJwtRequest { - public_key: hash_get_string(&opts, "public_key")?, + public_key: hash_require_string(&opts, "public_key")?, kid: hash_require_string(&opts, "kid")?, - name: hash_get_string(&opts, "name")?, + name: hash_require_string(&opts, "name")?, }; runtime() .block_on(client.create_jwt(&id, ¶ms)) @@ -694,7 +694,7 @@ impl AdminApiClient { let client = self.inner.clone(); let id = hash_require_string(&opts, "id")?; let params = core::admin::CreateRequestFilterRequest { - method: hash_get_vec_string(&opts, "methods")?, + method: hash_require_vec_string(&opts, "methods")?, }; runtime() .block_on(client.create_request_filter(&id, ¶ms)) diff --git a/npm/README.md b/npm/README.md index 12afdf7..233d258 100644 --- a/npm/README.md +++ b/npm/README.md @@ -553,7 +553,7 @@ await qn.admin.deleteToken("ep-123", "tok-1"); Whitelists a referrer URL or domain on an endpoint. -**Parameters**: `id` (endpoint id, required); body: `referrer` (string, optional). +**Parameters**: `id` (endpoint id, required); body: `referrer` (string, required). **Returns**: nothing. @@ -581,7 +581,7 @@ await qn.admin.deleteReferrer("ep-123", "ref-1"); Whitelists an IP address on an endpoint. -**Parameters**: `id` (endpoint id, required); body: `ip` (string, optional). +**Parameters**: `id` (endpoint id, required); body: `ip` (string, required). **Returns**: nothing. @@ -637,7 +637,7 @@ await qn.admin.deleteDomainMask("ep-123", "dm-1"); Configures JWT validation on an endpoint. -**Parameters**: `id` (endpoint id, required); body: `public_key` (string, optional), `kid` (string, required), `name` (string, optional). +**Parameters**: `id` (endpoint id, required); body: `public_key` (string, required), `kid` (string, required), `name` (string, required). **Returns**: nothing. @@ -669,7 +669,7 @@ Whitelist specific RPC methods on an endpoint. Requests for methods not on the l ##### `create_request_filter` / `createRequestFilter` -**Parameters**: `id` (endpoint id, required); body: `method` (string[], optional). Ruby's Hash key is `methods` (plural). +**Parameters**: `id` (endpoint id, required); body: `method` (string[], required). Ruby's Hash key is `methods` (plural). **Returns**: `CreateRequestFilterResponse` with `data.id`. diff --git a/npm/index.d.ts b/npm/index.d.ts index 13dd1fe..7f5ecca 100644 --- a/npm/index.d.ts +++ b/npm/index.d.ts @@ -234,17 +234,17 @@ export interface CreateEndpointResponse { /** Parameters for `create_ip`. */ export interface CreateIpRequest { /** IP address to whitelist. */ - ip?: string + ip: string } /** Parameters for `create_jwt`. */ export interface CreateJwtRequest { /** Public key used to verify signed JWTs. */ - publicKey?: string + publicKey: string /** Key identifier (`kid`) embedded in JWT headers. */ kid: string /** Human-readable name for the JWT configuration. */ - name?: string + name: string } /** Parameters for `create_list`. */ @@ -290,7 +290,7 @@ export interface CreateOrUpdateIpCustomHeaderResponse { /** Parameters for `create_referrer`. */ export interface CreateReferrerRequest { /** Allowed referrer URL or domain. */ - referrer?: string + referrer: string } /** Data wrapper for a created request filter. */ @@ -302,7 +302,7 @@ export interface CreateRequestFilterData { /** Parameters for `create_request_filter`. */ export interface CreateRequestFilterRequest { /** Whitelisted RPC methods; other methods will be blocked. */ - method?: Array + method: Array } /** Response from `create_request_filter`. */ diff --git a/python/README.md b/python/README.md index c26a39f..2be0dca 100644 --- a/python/README.md +++ b/python/README.md @@ -556,7 +556,7 @@ await qn.admin.delete_token("ep-123", "tok-1") Whitelists a referrer URL or domain on an endpoint. -**Parameters**: `id` (endpoint id, required); body: `referrer` (string, optional). +**Parameters**: `id` (endpoint id, required); body: `referrer` (string, required). **Returns**: nothing. @@ -584,7 +584,7 @@ await qn.admin.delete_referrer("ep-123", "ref-1") Whitelists an IP address on an endpoint. -**Parameters**: `id` (endpoint id, required); body: `ip` (string, optional). +**Parameters**: `id` (endpoint id, required); body: `ip` (string, required). **Returns**: nothing. @@ -640,7 +640,7 @@ await qn.admin.delete_domain_mask("ep-123", "dm-1") Configures JWT validation on an endpoint. -**Parameters**: `id` (endpoint id, required); body: `public_key` (string, optional), `kid` (string, required), `name` (string, optional). +**Parameters**: `id` (endpoint id, required); body: `public_key` (string, required), `kid` (string, required), `name` (string, required). **Returns**: nothing. @@ -673,7 +673,7 @@ Whitelist specific RPC methods on an endpoint. Requests for methods not on the l ##### `create_request_filter` / `createRequestFilter` -**Parameters**: `id` (endpoint id, required); body: `method` (string[], optional). Ruby's Hash key is `methods` (plural). +**Parameters**: `id` (endpoint id, required); body: `method` (string[], required). Ruby's Hash key is `methods` (plural). **Returns**: `CreateRequestFilterResponse` with `data.id`. diff --git a/python/quicknode_sdk/_core/__init__.pyi b/python/quicknode_sdk/_core/__init__.pyi index 318a1f6..1a0260c 100644 --- a/python/quicknode_sdk/_core/__init__.pyi +++ b/python/quicknode_sdk/_core/__init__.pyi @@ -385,7 +385,7 @@ class AdminApiClient: r""" Revokes a token on an endpoint by token id. """ - def create_referrer(self, id: builtins.str, referrer: typing.Optional[builtins.str] = None) -> typing.Coroutine[typing.Any, typing.Any, None]: + def create_referrer(self, id: builtins.str, referrer: builtins.str) -> typing.Coroutine[typing.Any, typing.Any, None]: r""" Adds a referrer to an endpoint's security settings, specifying which external URL or domain is permitted to call the endpoint. @@ -394,7 +394,7 @@ class AdminApiClient: r""" Removes a referrer from an endpoint's security settings by referrer id. """ - def create_ip(self, id: builtins.str, ip: typing.Optional[builtins.str] = None) -> typing.Coroutine[typing.Any, typing.Any, None]: + def create_ip(self, id: builtins.str, ip: builtins.str) -> typing.Coroutine[typing.Any, typing.Any, None]: r""" Adds an IP address to an endpoint's security whitelist. """ @@ -412,7 +412,7 @@ class AdminApiClient: r""" Removes a domain mask from an endpoint by domain mask id. """ - def create_jwt(self, id: builtins.str, kid: builtins.str, public_key: typing.Optional[builtins.str] = None, name: typing.Optional[builtins.str] = None) -> typing.Coroutine[typing.Any, typing.Any, None]: + def create_jwt(self, id: builtins.str, kid: builtins.str, public_key: builtins.str, name: builtins.str) -> typing.Coroutine[typing.Any, typing.Any, None]: r""" Creates a new JWT for endpoint authentication. Accepts a public key, key id (`kid`), and token name. @@ -422,7 +422,7 @@ class AdminApiClient: Removes a JWT from an endpoint's security configuration by jwt id, revoking its access. """ - def create_request_filter(self, id: builtins.str, method: typing.Optional[typing.Sequence[builtins.str]] = None) -> typing.Coroutine[typing.Any, typing.Any, CreateRequestFilterResponse]: + def create_request_filter(self, id: builtins.str, method: typing.Sequence[builtins.str]) -> typing.Coroutine[typing.Any, typing.Any, CreateRequestFilterResponse]: r""" Creates a request filter on an endpoint — a method whitelist that restricts which RPC methods may be called. Accepts an array of method @@ -1293,12 +1293,12 @@ class CreateIpRequest: Parameters for `create_ip`. """ @property - def ip(self) -> typing.Optional[builtins.str]: + def ip(self) -> builtins.str: r""" IP address to whitelist. """ @ip.setter - def ip(self, value: typing.Optional[builtins.str]) -> None: + def ip(self, value: builtins.str) -> None: r""" IP address to whitelist. """ @@ -1309,12 +1309,12 @@ class CreateJwtRequest: Parameters for `create_jwt`. """ @property - def public_key(self) -> typing.Optional[builtins.str]: + def public_key(self) -> builtins.str: r""" Public key used to verify signed JWTs. """ @public_key.setter - def public_key(self, value: typing.Optional[builtins.str]) -> None: + def public_key(self, value: builtins.str) -> None: r""" Public key used to verify signed JWTs. """ @@ -1329,12 +1329,12 @@ class CreateJwtRequest: Key identifier (`kid`) embedded in JWT headers. """ @property - def name(self) -> typing.Optional[builtins.str]: + def name(self) -> builtins.str: r""" Human-readable name for the JWT configuration. """ @name.setter - def name(self, value: typing.Optional[builtins.str]) -> None: + def name(self, value: builtins.str) -> None: r""" Human-readable name for the JWT configuration. """ @@ -1449,12 +1449,12 @@ class CreateReferrerRequest: Parameters for `create_referrer`. """ @property - def referrer(self) -> typing.Optional[builtins.str]: + def referrer(self) -> builtins.str: r""" Allowed referrer URL or domain. """ @referrer.setter - def referrer(self, value: typing.Optional[builtins.str]) -> None: + def referrer(self, value: builtins.str) -> None: r""" Allowed referrer URL or domain. """ @@ -1481,12 +1481,12 @@ class CreateRequestFilterRequest: Parameters for `create_request_filter`. """ @property - def method(self) -> typing.Optional[builtins.list[builtins.str]]: + def method(self) -> builtins.list[builtins.str]: r""" Whitelisted RPC methods; other methods will be blocked. """ @method.setter - def method(self, value: typing.Optional[builtins.list[builtins.str]]) -> None: + def method(self, value: builtins.list[builtins.str]) -> None: r""" Whitelisted RPC methods; other methods will be blocked. """ diff --git a/ruby/README.md b/ruby/README.md index 7e9f54a..f804bc6 100644 --- a/ruby/README.md +++ b/ruby/README.md @@ -547,7 +547,7 @@ qn.admin.delete_token(id: "ep-123", token_id: "tok-1") Whitelists a referrer URL or domain on an endpoint. -**Parameters**: `id` (endpoint id, required); body: `referrer` (string, optional). +**Parameters**: `id` (endpoint id, required); body: `referrer` (string, required). **Returns**: nothing. @@ -575,7 +575,7 @@ qn.admin.delete_referrer(id: "ep-123", referrer_id: "ref-1") Whitelists an IP address on an endpoint. -**Parameters**: `id` (endpoint id, required); body: `ip` (string, optional). +**Parameters**: `id` (endpoint id, required); body: `ip` (string, required). **Returns**: nothing. @@ -631,7 +631,7 @@ qn.admin.delete_domain_mask(id: "ep-123", domain_mask_id: "dm-1") Configures JWT validation on an endpoint. -**Parameters**: `id` (endpoint id, required); body: `public_key` (string, optional), `kid` (string, required), `name` (string, optional). +**Parameters**: `id` (endpoint id, required); body: `public_key` (string, required), `kid` (string, required), `name` (string, required). **Returns**: nothing. @@ -664,7 +664,7 @@ Whitelist specific RPC methods on an endpoint. Requests for methods not on the l ##### `create_request_filter` / `createRequestFilter` -**Parameters**: `id` (endpoint id, required); body: `method` (string[], optional). Ruby's Hash key is `methods` (plural). +**Parameters**: `id` (endpoint id, required); body: `method` (string[], required). Ruby's Hash key is `methods` (plural). **Returns**: `CreateRequestFilterResponse` with `data.id`. diff --git a/ruby/sig/quicknode_sdk.rbs b/ruby/sig/quicknode_sdk.rbs index e188c54..2db34fd 100644 --- a/ruby/sig/quicknode_sdk.rbs +++ b/ruby/sig/quicknode_sdk.rbs @@ -64,15 +64,15 @@ module QuicknodeSdk def update_security_options: (id: String, ?tokens: String, ?referrers: String, ?jwts: String, ?ips: String, ?domain_masks: String, ?hsts: String, ?cors: String, ?request_filters: String, ?ip_custom_header: String) -> untyped def create_token: (id: String) -> void def delete_token: (id: String, token_id: String) -> untyped - def create_referrer: (id: String, ?referrer: String) -> void + def create_referrer: (id: String, referrer: String) -> void def delete_referrer: (id: String, referrer_id: String) -> untyped - def create_ip: (id: String, ?ip: String) -> void + def create_ip: (id: String, ip: String) -> void def delete_ip: (id: String, ip_id: String) -> untyped def create_domain_mask: (id: String, ?domain_mask: String) -> void def delete_domain_mask: (id: String, domain_mask_id: String) -> untyped - def create_jwt: (id: String, kid: String, ?public_key: String, ?name: String) -> void + def create_jwt: (id: String, kid: String, public_key: String, name: String) -> void def delete_jwt: (id: String, jwt_id: String) -> void - def create_request_filter: (id: String, ?methods: Array[String]) -> untyped + def create_request_filter: (id: String, methods: Array[String]) -> untyped def update_request_filter: (id: String, request_filter_id: String, ?methods: Array[String]) -> void def delete_request_filter: (id: String, request_filter_id: String) -> void def enable_multichain: (id: String) -> void