Skip to content

Commit 5fcbf27

Browse files
authored
fix(security)!: require create-security params for quicknode-sdk 0.4 (DX-5869) (#45)
quicknode-sdk 0.4.0 flips five previously-optional fields to required across the four create-security request structs (referrer, ip, jwt public_key/name, request-filter method). Drop the `Some(...)` wrappers at the call sites in endpoint/security.rs so the CLI compiles against 0.4. `endpoint security jwt add --name` becomes a required flag, matching the API contract: users get a clear arg-parser error instead of a server 400. Empty-but-present values still round-trip to the server's 400. Updates the jwt-add test to pass --name, adds a test asserting --name is required (zero requests reach the mock), and backfills the previously untested create paths for `ip add` and `request-filter create`.
1 parent f2153df commit 5fcbf27

4 files changed

Lines changed: 85 additions & 14 deletions

File tree

Cargo.lock

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ name = "qn"
2121
path = "src/lib.rs"
2222

2323
[dependencies]
24-
quicknode-sdk = "0.3"
24+
quicknode-sdk = "0.4"
2525
clap = { version = "4", features = ["derive", "env", "wrap_help"] }
2626
clap_complete = "4"
2727
tokio = { version = "1", features = ["macros", "rt-multi-thread", "time"] }

src/commands/endpoint/security.rs

Lines changed: 6 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -170,7 +170,7 @@ pub struct JwtAddArgs {
170170
pub kid: String,
171171
/// Human-readable name.
172172
#[arg(long)]
173-
pub name: Option<String>,
173+
pub name: String,
174174
}
175175

176176
#[derive(Debug, Subcommand)]
@@ -358,7 +358,7 @@ async fn referrer(cmd: ReferrerCmd, ctx: Ctx) -> Result<(), CliError> {
358358
match cmd {
359359
ReferrerCmd::Add { id, referrer } => {
360360
let req = CreateReferrerRequest {
361-
referrer: Some(referrer.clone()),
361+
referrer: referrer.clone(),
362362
};
363363
ctx.sdk.admin.create_referrer(&id, &req).await?;
364364
ctx.out
@@ -381,9 +381,7 @@ async fn referrer(cmd: ReferrerCmd, ctx: Ctx) -> Result<(), CliError> {
381381
async fn ip(cmd: IpCmd, ctx: Ctx) -> Result<(), CliError> {
382382
match cmd {
383383
IpCmd::Add { id, ip } => {
384-
let req = CreateIpRequest {
385-
ip: Some(ip.clone()),
386-
};
384+
let req = CreateIpRequest { ip: ip.clone() };
387385
ctx.sdk.admin.create_ip(&id, &req).await?;
388386
ctx.out.note(&format!("✓ Whitelisted IP {ip} on {id}"));
389387
warn_if_option_disabled(&ctx, &id, "ips", "ips", "this IP").await;
@@ -404,8 +402,8 @@ async fn jwt(cmd: JwtCmd, ctx: Ctx) -> Result<(), CliError> {
404402
match cmd {
405403
JwtCmd::Add(a) => {
406404
let public_key = match (a.public_key, a.public_key_file) {
407-
(Some(s), None) => Some(s),
408-
(None, Some(p)) => Some(std::fs::read_to_string(&p)?),
405+
(Some(s), None) => s,
406+
(None, Some(p)) => std::fs::read_to_string(&p)?,
409407
(None, None) => {
410408
return Err(CliError::Arg(
411409
"supply --public-key or --public-key-file".to_string(),
@@ -471,9 +469,7 @@ async fn request_filter(cmd: RequestFilterCmd, ctx: Ctx) -> Result<(), CliError>
471469
if methods.is_empty() {
472470
return Err(CliError::Arg("supply at least one --method".to_string()));
473471
}
474-
let req = CreateRequestFilterRequest {
475-
method: Some(methods),
476-
};
472+
let req = CreateRequestFilterRequest { method: methods };
477473
let resp = ctx.sdk.admin.create_request_filter(&a.id, &req).await?;
478474
let d = resp.data.as_ref().ok_or_else(|| {
479475
CliError::Format("API returned success but no data; nothing was created".into())

tests/endpoint.rs

Lines changed: 76 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -745,6 +745,50 @@ async fn endpoint_security_ip_remove_without_yes_sends_nothing() {
745745
assert_eq!(out.exit_code, 5, "stderr={}", out.stderr);
746746
}
747747

748+
#[tokio::test]
749+
async fn endpoint_security_ip_add() {
750+
let server = MockServer::start().await;
751+
Mock::given(method("POST"))
752+
.and(path("/v0/endpoints/ep-1/security/ips"))
753+
.and(body_json(json!({ "ip": "1.2.3.4" })))
754+
.respond_with(ResponseTemplate::new(200))
755+
.expect(1)
756+
.mount(&server)
757+
.await;
758+
let out = run_qn(
759+
&server.uri(),
760+
&["endpoint", "security", "ip", "add", "ep-1", "1.2.3.4"],
761+
)
762+
.await;
763+
assert_eq!(out.exit_code, 0, "stderr={}", out.stderr);
764+
}
765+
766+
#[tokio::test]
767+
async fn endpoint_security_request_filter_create() {
768+
let server = MockServer::start().await;
769+
Mock::given(method("POST"))
770+
.and(path("/v0/endpoints/ep-1/security/request_filters"))
771+
.and(body_json(json!({ "method": ["eth_call"] })))
772+
.respond_with(ResponseTemplate::new(200).set_body_json(json!({ "data": { "id": "rf-1" } })))
773+
.expect(1)
774+
.mount(&server)
775+
.await;
776+
let out = run_qn(
777+
&server.uri(),
778+
&[
779+
"endpoint",
780+
"security",
781+
"request-filter",
782+
"create",
783+
"ep-1",
784+
"--method",
785+
"eth_call",
786+
],
787+
)
788+
.await;
789+
assert_eq!(out.exit_code, 0, "stderr={}", out.stderr);
790+
}
791+
748792
#[tokio::test]
749793
async fn endpoint_security_jwt_remove_with_yes() {
750794
let server = MockServer::start().await;
@@ -787,7 +831,7 @@ async fn endpoint_security_jwt_add_with_kid() {
787831
Mock::given(method("POST"))
788832
.and(path("/v0/endpoints/ep-1/security/jwts"))
789833
.and(body_partial_json(
790-
json!({ "public_key": "pk", "kid": "kid-1" }),
834+
json!({ "public_key": "pk", "kid": "kid-1", "name": "my-jwt" }),
791835
))
792836
.respond_with(ResponseTemplate::new(200))
793837
.expect(1)
@@ -805,12 +849,43 @@ async fn endpoint_security_jwt_add_with_kid() {
805849
"pk",
806850
"--kid",
807851
"kid-1",
852+
"--name",
853+
"my-jwt",
808854
],
809855
)
810856
.await;
811857
assert_eq!(out.exit_code, 0, "stderr={}", out.stderr);
812858
}
813859

860+
#[tokio::test]
861+
async fn endpoint_security_jwt_add_requires_name() {
862+
let server = MockServer::start().await;
863+
// --name is required (quicknode-sdk 0.4); the request never fires.
864+
Mock::given(method("POST"))
865+
.and(path("/v0/endpoints/ep-1/security/jwts"))
866+
.respond_with(ResponseTemplate::new(200))
867+
.expect(0)
868+
.mount(&server)
869+
.await;
870+
let out = run_qn(
871+
&server.uri(),
872+
&[
873+
"endpoint",
874+
"security",
875+
"jwt",
876+
"add",
877+
"ep-1",
878+
"--public-key",
879+
"pk",
880+
"--kid",
881+
"kid-1",
882+
],
883+
)
884+
.await;
885+
assert_ne!(out.exit_code, 0, "stderr={}", out.stderr);
886+
assert!(out.stderr.contains("name"), "stderr={}", out.stderr);
887+
}
888+
814889
#[tokio::test]
815890
async fn endpoint_security_jwt_add_requires_kid() {
816891
let server = MockServer::start().await;

0 commit comments

Comments
 (0)