Skip to content

Commit eb6bc4e

Browse files
committed
[omdb] add webhook delivery info
1 parent 282ea4b commit eb6bc4e

File tree

1 file changed

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

1 file changed

+195
-0
lines changed

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

+195
Original file line numberDiff line numberDiff line change
@@ -963,6 +963,10 @@ enum WebhookDeliveryCommands {
963963
/// List webhook deliveries
964964
#[clap(alias = "ls")]
965965
List(WebhookDeliveryListArgs),
966+
967+
/// Show details on a webhook delivery, including its payload and attempt history.
968+
#[clap(alias = "show")]
969+
Info(WebhookDeliveryInfoArgs),
966970
}
967971

968972
#[derive(Debug, Args, Clone)]
@@ -992,6 +996,12 @@ struct WebhookDeliveryListArgs {
992996
after: Option<DateTime<Utc>>,
993997
}
994998

999+
#[derive(Debug, Args, Clone)]
1000+
struct WebhookDeliveryInfoArgs {
1001+
/// The ID of the delivery to show.
1002+
delivery_id: Uuid,
1003+
}
1004+
9951005
impl DbArgs {
9961006
/// Run a `omdb db` subcommand.
9971007
///
@@ -6967,6 +6977,9 @@ async fn cmd_db_webhook(
69676977
WebhookCommands::Delivery {
69686978
command: WebhookDeliveryCommands::List(args),
69696979
} => cmd_db_webhook_delivery_list(datastore, fetch_opts, args).await,
6980+
WebhookCommands::Delivery {
6981+
command: WebhookDeliveryCommands::Info(args),
6982+
} => cmd_db_webhook_delivery_info(datastore, fetch_opts, args).await,
69706983
WebhookCommands::Event => {
69716984
Err(anyhow::anyhow!("not yet implemented, sorry!"))
69726985
}
@@ -7285,6 +7298,7 @@ async fn cmd_db_webhook_delivery_list(
72857298
check_limit(&deliveries, fetch_opts.fetch_limit, ctx);
72867299

72877300
#[derive(Tabled)]
7301+
#[tabled(rename_all = "SCREAMING_SNAKE_CASE")]
72887302
struct DeliveryRow {
72897303
id: Uuid,
72907304
trigger: nexus_db_model::WebhookDeliveryTrigger,
@@ -7297,13 +7311,15 @@ async fn cmd_db_webhook_delivery_list(
72977311
}
72987312

72997313
#[derive(Tabled)]
7314+
#[tabled(rename_all = "SCREAMING_SNAKE_CASE")]
73007315
struct WithEventId<T: Tabled> {
73017316
#[tabled(inline)]
73027317
inner: T,
73037318
event_id: Uuid,
73047319
}
73057320

73067321
#[derive(Tabled)]
7322+
#[tabled(rename_all = "SCREAMING_SNAKE_CASE")]
73077323
struct WithRxId<T: Tabled> {
73087324
#[tabled(inline)]
73097325
inner: T,
@@ -7418,6 +7434,177 @@ async fn lookup_webhook_rx(
74187434
.with_context(|| format!("loading webhook_receiver {name_or_id}"))
74197435
}
74207436

7437+
async fn cmd_db_webhook_delivery_info(
7438+
datastore: &DataStore,
7439+
fetch_opts: &DbFetchOptions,
7440+
args: &WebhookDeliveryInfoArgs,
7441+
) -> anyhow::Result<()> {
7442+
use db::model::WebhookDeliveryAttempt;
7443+
use db::model::schema::webhook_delivery::dsl;
7444+
use db::model::schema::webhook_delivery_attempt::dsl as attempt_dsl;
7445+
7446+
let WebhookDeliveryInfoArgs { delivery_id } = args;
7447+
let conn = datastore.pool_connection_for_tests().await?;
7448+
let delivery = dsl::webhook_delivery
7449+
.filter(dsl::id.eq(*delivery_id))
7450+
.limit(1)
7451+
.select(WebhookDelivery::as_select())
7452+
.get_result_async(&*conn)
7453+
.await
7454+
.optional()
7455+
.with_context(|| format!("loading webhook delivery {delivery_id}"))?
7456+
.ok_or_else(|| {
7457+
anyhow::anyhow!("no webhook delivery {delivery_id} exists")
7458+
})?;
7459+
7460+
const ID: &'static str = "ID";
7461+
const EVENT_ID: &'static str = "event ID";
7462+
const RECEIVER_ID: &'static str = "receiver ID";
7463+
const STATE: &'static str = "state";
7464+
const TRIGGER: &'static str = "triggered by";
7465+
const ATTEMPTS: &'static str = "attempts";
7466+
const TIME_CREATED: &'static str = "created at";
7467+
const TIME_COMPLETED: &'static str = "completed at";
7468+
7469+
const DELIVERATOR_ID: &'static str = "by Nexus";
7470+
const TIME_DELIVERY_STARTED: &'static str = "started at";
7471+
7472+
const WIDTH: usize = const_max_len(&[
7473+
ID,
7474+
EVENT_ID,
7475+
RECEIVER_ID,
7476+
TRIGGER,
7477+
STATE,
7478+
TIME_CREATED,
7479+
TIME_COMPLETED,
7480+
DELIVERATOR_ID,
7481+
TIME_DELIVERY_STARTED,
7482+
ATTEMPTS,
7483+
]);
7484+
7485+
let WebhookDelivery {
7486+
id,
7487+
event_id,
7488+
rx_id,
7489+
trigger,
7490+
payload,
7491+
attempts,
7492+
time_created,
7493+
time_completed,
7494+
state,
7495+
deliverator_id,
7496+
time_delivery_started,
7497+
} = delivery;
7498+
println!("\n{:=<80}", "== DELIVERY ");
7499+
println!(" {ID:>WIDTH$}: {id}");
7500+
println!(" {EVENT_ID:>WIDTH$}: {event_id}");
7501+
println!(" {RECEIVER_ID:>WIDTH$}: {rx_id}");
7502+
println!(" {STATE:>WIDTH$}: {state}");
7503+
println!(" {TRIGGER:>WIDTH$}: {trigger}");
7504+
println!(" {TIME_CREATED:>WIDTH$}: {time_created}");
7505+
println!(" {ATTEMPTS}: {}", attempts.0);
7506+
7507+
if let Some(completed) = time_completed {
7508+
println!("\n{:=<80}", "== DELIVERY COMPLETED ");
7509+
println!(" {TIME_COMPLETED:>WIDTH$}: {completed}");
7510+
if let Some(started) = time_delivery_started {
7511+
println!(" {TIME_DELIVERY_STARTED:>WIDTH$}: {started}");
7512+
} else {
7513+
println!(
7514+
"/!\\ WEIRD: delivery is completed but has no start timestamp?"
7515+
);
7516+
}
7517+
if let Some(nexus) = deliverator_id {
7518+
println!(" {DELIVERATOR_ID:>WIDTH$}: {nexus}");
7519+
} else {
7520+
println!("/!\\ WEIRD: delivery is completed but has no Nexus ID?");
7521+
}
7522+
} else if let Some(started) = time_delivery_started {
7523+
println!("\n{:=<80}", "== DELIVERY IN PROGRESS ");
7524+
println!(" {TIME_DELIVERY_STARTED:>WIDTH$}: {started}");
7525+
7526+
if let Some(nexus) = deliverator_id {
7527+
println!(" {DELIVERATOR_ID:>WIDTH$}: {nexus}");
7528+
} else {
7529+
println!(
7530+
"/!\\ WEIRD: delivery is in progress but has no Nexus ID?"
7531+
);
7532+
}
7533+
} else if let Some(deliverator) = deliverator_id {
7534+
println!(
7535+
"/!\\ WEIRD: delivery is not completed or in progress but has \
7536+
Nexus ID {deliverator:?}"
7537+
);
7538+
}
7539+
7540+
println!("\n{:=<80}", "== JSON PAYLOAD ");
7541+
match serde_json::to_string_pretty(&payload) {
7542+
Ok(payload_str) => println!("{payload_str}"),
7543+
Err(e) => eprintln!(
7544+
"/!\\ payload JSON did not serialize: {e}\npayload: {payload:?}",
7545+
),
7546+
}
7547+
7548+
// Okay, now go get attempts for this delivery.
7549+
let ctx = || format!("listing delivery attempts for {delivery_id}");
7550+
let attempts = attempt_dsl::webhook_delivery_attempt
7551+
.filter(attempt_dsl::delivery_id.eq(*delivery_id))
7552+
.order_by(attempt_dsl::attempt.desc())
7553+
.limit(fetch_opts.fetch_limit.get().into())
7554+
.select(WebhookDeliveryAttempt::as_select())
7555+
.load_async(&*conn)
7556+
.await
7557+
.with_context(ctx)?;
7558+
7559+
check_limit(&attempts, fetch_opts.fetch_limit, ctx);
7560+
7561+
if !attempts.is_empty() {
7562+
println!("\n{:=<80}", "== DELIVERY ATTEMPT HISTORY ");
7563+
7564+
#[derive(Tabled)]
7565+
#[tabled(rename_all = "SCREAMING_SNAKE_CASE")]
7566+
struct DeliveryAttemptRow {
7567+
#[tabled(rename = "#")]
7568+
attempt: u8,
7569+
#[tabled(display_with = "datetime_rfc3339_concise")]
7570+
time_created: DateTime<Utc>,
7571+
nexus_id: Uuid,
7572+
result: db::model::WebhookDeliveryAttemptResult,
7573+
#[tabled(display_with = "display_i16_opt")]
7574+
status: Option<i16>,
7575+
#[tabled(display_with = "display_time_delta_opt")]
7576+
duration: Option<chrono::TimeDelta>,
7577+
}
7578+
7579+
let rows = attempts.into_iter().map(
7580+
|WebhookDeliveryAttempt {
7581+
delivery_id: _,
7582+
rx_id: _,
7583+
attempt,
7584+
result,
7585+
response_status,
7586+
response_duration,
7587+
time_created,
7588+
deliverator_id,
7589+
}| DeliveryAttemptRow {
7590+
attempt: attempt.0,
7591+
time_created,
7592+
nexus_id: deliverator_id.into_untyped_uuid(),
7593+
result,
7594+
status: response_status,
7595+
duration: response_duration,
7596+
},
7597+
);
7598+
let mut table = tabled::Table::new(rows);
7599+
table
7600+
.with(tabled::settings::Style::empty())
7601+
.with(tabled::settings::Padding::new(0, 1, 0, 0));
7602+
println!("{table}");
7603+
}
7604+
7605+
Ok(())
7606+
}
7607+
74217608
// Format a `chrono::DateTime` in RFC3339 with milliseconds precision and using
74227609
// `Z` rather than the UTC offset for UTC timestamps, to save a few characters
74237610
// of line width in tabular output.
@@ -7432,3 +7619,11 @@ fn datetime_opt_rfc3339_concise(t: &Option<DateTime<Utc>>) -> String {
74327619
t.map(|t| t.to_rfc3339_opts(chrono::format::SecondsFormat::Millis, true))
74337620
.unwrap_or_else(|| "-".to_string())
74347621
}
7622+
7623+
fn display_time_delta_opt(t: &Option<chrono::TimeDelta>) -> String {
7624+
t.map(|t| t.to_string()).unwrap_or_else(|| "-".to_string())
7625+
}
7626+
7627+
fn display_i16_opt(u: &Option<i16>) -> String {
7628+
u.map(|u| u.to_string()).unwrap_or_else(|| "-".to_string())
7629+
}

0 commit comments

Comments
 (0)