Skip to content

Commit 92232ec

Browse files
committed
[omdb] add webhook delivery info
1 parent 619cd1a commit 92232ec

File tree

1 file changed

+189
-0
lines changed
  • dev-tools/omdb/src/bin/omdb

1 file changed

+189
-0
lines changed

dev-tools/omdb/src/bin/omdb/db.rs

+189
Original file line numberDiff line numberDiff line change
@@ -1207,6 +1207,10 @@ enum WebhookDeliveryCommands {
12071207
/// List webhook deliveries
12081208
#[clap(alias = "ls")]
12091209
List(WebhookDeliveryListArgs),
1210+
1211+
/// Show details on a webhook delivery, including its payload and attempt history.
1212+
#[clap(alias = "show")]
1213+
Info(WebhookDeliveryInfoArgs),
12101214
}
12111215

12121216
#[derive(Debug, Args, Clone)]
@@ -1236,6 +1240,12 @@ struct WebhookDeliveryListArgs {
12361240
after: Option<DateTime<Utc>>,
12371241
}
12381242

1243+
#[derive(Debug, Args, Clone)]
1244+
struct WebhookDeliveryInfoArgs {
1245+
/// The ID of the delivery to show.
1246+
delivery_id: Uuid,
1247+
}
1248+
12391249
impl DbArgs {
12401250
/// Run a `omdb db` subcommand.
12411251
///
@@ -8130,6 +8140,9 @@ async fn cmd_db_webhook(
81308140
WebhookCommands::Delivery {
81318141
command: WebhookDeliveryCommands::List(args),
81328142
} => cmd_db_webhook_delivery_list(datastore, fetch_opts, args).await,
8143+
WebhookCommands::Delivery {
8144+
command: WebhookDeliveryCommands::Info(args),
8145+
} => cmd_db_webhook_delivery_info(datastore, fetch_opts, args).await,
81338146
WebhookCommands::Event => {
81348147
Err(anyhow::anyhow!("not yet implemented, sorry!"))
81358148
}
@@ -8456,6 +8469,7 @@ async fn cmd_db_webhook_delivery_list(
84568469
check_limit(&deliveries, fetch_opts.fetch_limit, ctx);
84578470

84588471
#[derive(Tabled)]
8472+
#[tabled(rename_all = "SCREAMING_SNAKE_CASE")]
84598473
struct DeliveryRow {
84608474
id: Uuid,
84618475
trigger: nexus_db_model::WebhookDeliveryTrigger,
@@ -8468,13 +8482,15 @@ async fn cmd_db_webhook_delivery_list(
84688482
}
84698483

84708484
#[derive(Tabled)]
8485+
#[tabled(rename_all = "SCREAMING_SNAKE_CASE")]
84718486
struct WithEventId<T: Tabled> {
84728487
#[tabled(inline)]
84738488
inner: T,
84748489
event_id: Uuid,
84758490
}
84768491

84778492
#[derive(Tabled)]
8493+
#[tabled(rename_all = "SCREAMING_SNAKE_CASE")]
84788494
struct WithRxId<T: Tabled> {
84798495
#[tabled(inline)]
84808496
inner: T,
@@ -8585,6 +8601,171 @@ async fn lookup_webhook_rx(
85858601
.with_context(|| format!("loading webhook_receiver {name_or_id}"))
85868602
}
85878603

8604+
async fn cmd_db_webhook_delivery_info(
8605+
datastore: &DataStore,
8606+
fetch_opts: &DbFetchOptions,
8607+
args: &WebhookDeliveryInfoArgs,
8608+
) -> anyhow::Result<()> {
8609+
use db::model::WebhookDeliveryAttempt;
8610+
use nexus_db_schema::schema::webhook_delivery::dsl;
8611+
use nexus_db_schema::schema::webhook_delivery_attempt::dsl as attempt_dsl;
8612+
8613+
let WebhookDeliveryInfoArgs { delivery_id } = args;
8614+
let conn = datastore.pool_connection_for_tests().await?;
8615+
let delivery = dsl::webhook_delivery
8616+
.filter(dsl::id.eq(*delivery_id))
8617+
.limit(1)
8618+
.select(WebhookDelivery::as_select())
8619+
.get_result_async(&*conn)
8620+
.await
8621+
.optional()
8622+
.with_context(|| format!("loading webhook delivery {delivery_id}"))?
8623+
.ok_or_else(|| {
8624+
anyhow::anyhow!("no webhook delivery {delivery_id} exists")
8625+
})?;
8626+
8627+
const ID: &'static str = "ID";
8628+
const EVENT_ID: &'static str = "event ID";
8629+
const RECEIVER_ID: &'static str = "receiver ID";
8630+
const STATE: &'static str = "state";
8631+
const TRIGGER: &'static str = "triggered by";
8632+
const ATTEMPTS: &'static str = "attempts";
8633+
const TIME_CREATED: &'static str = "created at";
8634+
const TIME_COMPLETED: &'static str = "completed at";
8635+
8636+
const DELIVERATOR_ID: &'static str = "by Nexus";
8637+
const TIME_LEASED: &'static str = "leased at";
8638+
8639+
const WIDTH: usize = const_max_len(&[
8640+
ID,
8641+
EVENT_ID,
8642+
RECEIVER_ID,
8643+
TRIGGER,
8644+
STATE,
8645+
TIME_CREATED,
8646+
TIME_COMPLETED,
8647+
DELIVERATOR_ID,
8648+
TIME_LEASED,
8649+
ATTEMPTS,
8650+
]);
8651+
8652+
let WebhookDelivery {
8653+
id,
8654+
event_id,
8655+
rx_id,
8656+
triggered_by,
8657+
attempts,
8658+
time_created,
8659+
time_completed,
8660+
state,
8661+
deliverator_id,
8662+
time_leased,
8663+
} = delivery;
8664+
println!("\n{:=<80}", "== DELIVERY ");
8665+
println!(" {ID:>WIDTH$}: {id}");
8666+
println!(" {EVENT_ID:>WIDTH$}: {event_id}");
8667+
println!(" {RECEIVER_ID:>WIDTH$}: {rx_id}");
8668+
println!(" {STATE:>WIDTH$}: {state}");
8669+
println!(" {TRIGGER:>WIDTH$}: {triggered_by}");
8670+
println!(" {TIME_CREATED:>WIDTH$}: {time_created}");
8671+
println!(" {ATTEMPTS}: {}", attempts.0);
8672+
8673+
if let Some(completed) = time_completed {
8674+
println!("\n{:=<80}", "== DELIVERY COMPLETED ");
8675+
println!(" {TIME_COMPLETED:>WIDTH$}: {completed}");
8676+
if let Some(leased) = time_leased {
8677+
println!(" {TIME_LEASED:>WIDTH$}: {leased}");
8678+
} else {
8679+
println!(
8680+
"/!\\ WEIRD: delivery is completed but has no start timestamp?"
8681+
);
8682+
}
8683+
if let Some(nexus) = deliverator_id {
8684+
println!(" {DELIVERATOR_ID:>WIDTH$}: {nexus}");
8685+
} else {
8686+
println!("/!\\ WEIRD: delivery is completed but has no Nexus ID?");
8687+
}
8688+
} else if let Some(leased) = time_leased {
8689+
println!("\n{:=<80}", "== DELIVERY IN PROGRESS ");
8690+
println!(" {TIME_LEASED:>WIDTH$}: {leased}");
8691+
8692+
if let Some(nexus) = deliverator_id {
8693+
println!(" {DELIVERATOR_ID:>WIDTH$}: {nexus}");
8694+
} else {
8695+
println!(
8696+
"/!\\ WEIRD: delivery is in progress but has no Nexus ID?"
8697+
);
8698+
}
8699+
} else if let Some(deliverator) = deliverator_id {
8700+
println!(
8701+
"/!\\ WEIRD: delivery is not completed or in progress but has \
8702+
Nexus ID {deliverator:?}"
8703+
);
8704+
}
8705+
8706+
// Okay, now go get attempts for this delivery.
8707+
let ctx = || format!("listing delivery attempts for {delivery_id}");
8708+
let attempts = attempt_dsl::webhook_delivery_attempt
8709+
.filter(attempt_dsl::delivery_id.eq(*delivery_id))
8710+
.order_by(attempt_dsl::attempt.desc())
8711+
.limit(fetch_opts.fetch_limit.get().into())
8712+
.select(WebhookDeliveryAttempt::as_select())
8713+
.load_async(&*conn)
8714+
.await
8715+
.with_context(ctx)?;
8716+
8717+
check_limit(&attempts, fetch_opts.fetch_limit, ctx);
8718+
8719+
if !attempts.is_empty() {
8720+
println!("\n{:=<80}", "== DELIVERY ATTEMPT HISTORY ");
8721+
8722+
#[derive(Tabled)]
8723+
#[tabled(rename_all = "SCREAMING_SNAKE_CASE")]
8724+
struct DeliveryAttemptRow {
8725+
id: Uuid,
8726+
#[tabled(rename = "#")]
8727+
attempt: u8,
8728+
#[tabled(display_with = "datetime_rfc3339_concise")]
8729+
time_created: DateTime<Utc>,
8730+
nexus_id: Uuid,
8731+
result: db::model::WebhookDeliveryAttemptResult,
8732+
#[tabled(display_with = "display_u16_opt")]
8733+
status: Option<u16>,
8734+
#[tabled(display_with = "display_time_delta_opt")]
8735+
duration: Option<chrono::TimeDelta>,
8736+
}
8737+
8738+
let rows = attempts.into_iter().map(
8739+
|WebhookDeliveryAttempt {
8740+
id,
8741+
delivery_id: _,
8742+
rx_id: _,
8743+
attempt,
8744+
result,
8745+
response_status,
8746+
response_duration,
8747+
time_created,
8748+
deliverator_id,
8749+
}| DeliveryAttemptRow {
8750+
id: id.into_untyped_uuid(),
8751+
attempt: attempt.0,
8752+
time_created,
8753+
nexus_id: deliverator_id.into_untyped_uuid(),
8754+
result,
8755+
status: response_status.map(|u| u.into()),
8756+
duration: response_duration,
8757+
},
8758+
);
8759+
let mut table = tabled::Table::new(rows);
8760+
table
8761+
.with(tabled::settings::Style::empty())
8762+
.with(tabled::settings::Padding::new(0, 1, 0, 0));
8763+
println!("{table}");
8764+
}
8765+
8766+
Ok(())
8767+
}
8768+
85888769
// Format a `chrono::DateTime` in RFC3339 with milliseconds precision and using
85898770
// `Z` rather than the UTC offset for UTC timestamps, to save a few characters
85908771
// of line width in tabular output.
@@ -8698,3 +8879,11 @@ async fn cmd_db_zpool_set_storage_buffer(
86988879

86998880
Ok(())
87008881
}
8882+
8883+
fn display_time_delta_opt(t: &Option<chrono::TimeDelta>) -> String {
8884+
t.map(|t| t.to_string()).unwrap_or_else(|| "-".to_string())
8885+
}
8886+
8887+
fn display_u16_opt(u: &Option<u16>) -> String {
8888+
u.map(|u| u.to_string()).unwrap_or_else(|| "-".to_string())
8889+
}

0 commit comments

Comments
 (0)