diff --git a/Cargo.lock b/Cargo.lock index 4251b8a..6aedcff 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1508,9 +1508,9 @@ dependencies = [ [[package]] name = "quicknode-sdk" -version = "0.3.0" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f91ad3da8e75511dfbb2ae1d81eb914c70a8187f58369164c56a321cb1a20799" +checksum = "2493a477a7bbc09fadf8f0eef58d3407b8739465b30b40af5e27e5d37cbf40f1" dependencies = [ "config", "reqwest 0.13.4", diff --git a/Cargo.toml b/Cargo.toml index ec91e7f..1871464 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -21,7 +21,7 @@ name = "qn" path = "src/lib.rs" [dependencies] -quicknode-sdk = "0.3" +quicknode-sdk = "0.4" clap = { version = "4", features = ["derive", "env", "wrap_help"] } clap_complete = "4" tokio = { version = "1", features = ["macros", "rt-multi-thread", "time"] } diff --git a/src/commands/endpoint/security.rs b/src/commands/endpoint/security.rs index 45f449c..89d7507 100644 --- a/src/commands/endpoint/security.rs +++ b/src/commands/endpoint/security.rs @@ -170,7 +170,7 @@ pub struct JwtAddArgs { pub kid: String, /// Human-readable name. #[arg(long)] - pub name: Option, + pub name: String, } #[derive(Debug, Subcommand)] @@ -358,7 +358,7 @@ async fn referrer(cmd: ReferrerCmd, ctx: Ctx) -> Result<(), CliError> { match cmd { ReferrerCmd::Add { id, referrer } => { let req = CreateReferrerRequest { - referrer: Some(referrer.clone()), + referrer: referrer.clone(), }; ctx.sdk.admin.create_referrer(&id, &req).await?; ctx.out @@ -381,9 +381,7 @@ async fn referrer(cmd: ReferrerCmd, ctx: Ctx) -> Result<(), CliError> { async fn ip(cmd: IpCmd, ctx: Ctx) -> Result<(), CliError> { match cmd { IpCmd::Add { id, ip } => { - let req = CreateIpRequest { - ip: Some(ip.clone()), - }; + let req = CreateIpRequest { ip: ip.clone() }; ctx.sdk.admin.create_ip(&id, &req).await?; ctx.out.note(&format!("✓ Whitelisted IP {ip} on {id}")); warn_if_option_disabled(&ctx, &id, "ips", "ips", "this IP").await; @@ -404,8 +402,8 @@ async fn jwt(cmd: JwtCmd, ctx: Ctx) -> Result<(), CliError> { match cmd { JwtCmd::Add(a) => { let public_key = match (a.public_key, a.public_key_file) { - (Some(s), None) => Some(s), - (None, Some(p)) => Some(std::fs::read_to_string(&p)?), + (Some(s), None) => s, + (None, Some(p)) => std::fs::read_to_string(&p)?, (None, None) => { return Err(CliError::Arg( "supply --public-key or --public-key-file".to_string(), @@ -471,9 +469,7 @@ async fn request_filter(cmd: RequestFilterCmd, ctx: Ctx) -> Result<(), CliError> if methods.is_empty() { return Err(CliError::Arg("supply at least one --method".to_string())); } - let req = CreateRequestFilterRequest { - method: Some(methods), - }; + let req = CreateRequestFilterRequest { method: methods }; let resp = ctx.sdk.admin.create_request_filter(&a.id, &req).await?; let d = resp.data.as_ref().ok_or_else(|| { CliError::Format("API returned success but no data; nothing was created".into()) diff --git a/tests/endpoint.rs b/tests/endpoint.rs index 9260c32..002d75f 100644 --- a/tests/endpoint.rs +++ b/tests/endpoint.rs @@ -745,6 +745,50 @@ async fn endpoint_security_ip_remove_without_yes_sends_nothing() { assert_eq!(out.exit_code, 5, "stderr={}", out.stderr); } +#[tokio::test] +async fn endpoint_security_ip_add() { + let server = MockServer::start().await; + Mock::given(method("POST")) + .and(path("/v0/endpoints/ep-1/security/ips")) + .and(body_json(json!({ "ip": "1.2.3.4" }))) + .respond_with(ResponseTemplate::new(200)) + .expect(1) + .mount(&server) + .await; + let out = run_qn( + &server.uri(), + &["endpoint", "security", "ip", "add", "ep-1", "1.2.3.4"], + ) + .await; + assert_eq!(out.exit_code, 0, "stderr={}", out.stderr); +} + +#[tokio::test] +async fn endpoint_security_request_filter_create() { + let server = MockServer::start().await; + Mock::given(method("POST")) + .and(path("/v0/endpoints/ep-1/security/request_filters")) + .and(body_json(json!({ "method": ["eth_call"] }))) + .respond_with(ResponseTemplate::new(200).set_body_json(json!({ "data": { "id": "rf-1" } }))) + .expect(1) + .mount(&server) + .await; + let out = run_qn( + &server.uri(), + &[ + "endpoint", + "security", + "request-filter", + "create", + "ep-1", + "--method", + "eth_call", + ], + ) + .await; + assert_eq!(out.exit_code, 0, "stderr={}", out.stderr); +} + #[tokio::test] async fn endpoint_security_jwt_remove_with_yes() { let server = MockServer::start().await; @@ -787,7 +831,7 @@ async fn endpoint_security_jwt_add_with_kid() { Mock::given(method("POST")) .and(path("/v0/endpoints/ep-1/security/jwts")) .and(body_partial_json( - json!({ "public_key": "pk", "kid": "kid-1" }), + json!({ "public_key": "pk", "kid": "kid-1", "name": "my-jwt" }), )) .respond_with(ResponseTemplate::new(200)) .expect(1) @@ -805,12 +849,43 @@ async fn endpoint_security_jwt_add_with_kid() { "pk", "--kid", "kid-1", + "--name", + "my-jwt", ], ) .await; assert_eq!(out.exit_code, 0, "stderr={}", out.stderr); } +#[tokio::test] +async fn endpoint_security_jwt_add_requires_name() { + let server = MockServer::start().await; + // --name is required (quicknode-sdk 0.4); the request never fires. + Mock::given(method("POST")) + .and(path("/v0/endpoints/ep-1/security/jwts")) + .respond_with(ResponseTemplate::new(200)) + .expect(0) + .mount(&server) + .await; + let out = run_qn( + &server.uri(), + &[ + "endpoint", + "security", + "jwt", + "add", + "ep-1", + "--public-key", + "pk", + "--kid", + "kid-1", + ], + ) + .await; + assert_ne!(out.exit_code, 0, "stderr={}", out.stderr); + assert!(out.stderr.contains("name"), "stderr={}", out.stderr); +} + #[tokio::test] async fn endpoint_security_jwt_add_requires_kid() { let server = MockServer::start().await;