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: 2 additions & 2 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"] }
Expand Down
16 changes: 6 additions & 10 deletions src/commands/endpoint/security.rs
Original file line number Diff line number Diff line change
Expand Up @@ -170,7 +170,7 @@ pub struct JwtAddArgs {
pub kid: String,
/// Human-readable name.
#[arg(long)]
pub name: Option<String>,
pub name: String,
}

#[derive(Debug, Subcommand)]
Expand Down Expand Up @@ -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
Expand All @@ -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;
Expand All @@ -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(),
Expand Down Expand Up @@ -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())
Expand Down
77 changes: 76 additions & 1 deletion tests/endpoint.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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)
Expand All @@ -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;
Expand Down
Loading