Skip to content

Commit

Permalink
refactor(rust): use Terminal to print output of `tcp-connection cre…
Browse files Browse the repository at this point in the history
…ate` command
  • Loading branch information
0xkelvin authored and 0x61nas committed Oct 29, 2023
1 parent dc194db commit 5fe4fa1
Show file tree
Hide file tree
Showing 4 changed files with 123 additions and 77 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -158,4 +158,9 @@ impl BackgroundNode {
let route = self.create_route().await?;
Ok(Client::new(&route, timeout))
}

/// Get the node name
pub fn name(&self) -> &str {
&self.node_name
}
}
Original file line number Diff line number Diff line change
@@ -1,19 +1,17 @@
use clap::Args;
use colorful::Colorful;
use indoc::formatdoc;
use miette::IntoDiagnostic;
use serde_json::json;

use ockam_api::address::extract_address_value;
use ockam_api::nodes::models::transport::TransportStatus;
use ockam_api::nodes::BackgroundNode;
use ockam_node::Context;

use crate::node::{get_node_name, initialize_node_if_default};
use crate::util::is_tty;
use crate::{
docs,
util::{api, node_rpc},
CommandGlobalOpts, OutputFormat,
CommandGlobalOpts,
};

const AFTER_LONG_HELP: &str = include_str!("./static/create/after_long_help.txt");
Expand Down Expand Up @@ -43,48 +41,6 @@ impl CreateCommand {
initialize_node_if_default(&opts, &self.node_opts.from);
node_rpc(run_impl, (opts, self))
}

fn print_output(
&self,
opts: &CommandGlobalOpts,
response: &TransportStatus,
) -> miette::Result<()> {
// if output format is json, write json to stdout.
match opts.global_args.output_format {
OutputFormat::Plain => {
if !is_tty(std::io::stdout()) {
println!("{}", response.multiaddr().into_diagnostic()?);
return Ok(());
}
let from = get_node_name(&opts.state, &self.node_opts.from);
let to = response.socket_addr().into_diagnostic()?;
if opts.global_args.no_color {
println!("\n TCP Connection:");
println!(" From: /node/{from}");
println!(" To: {} (/ip4/{}/tcp/{})", to, to.ip(), to.port());
println!(" Address: {}", response.multiaddr().into_diagnostic()?);
} else {
println!("\n TCP Connection:");
println!("{}", format!(" From: /node/{from}").light_magenta());
println!(
"{}",
format!(" To: {} (/ip4/{}/tcp/{})", to, to.ip(), to.port())
.light_magenta()
);
println!(
"{}",
format!(" Address: {}", response.multiaddr().into_diagnostic()?)
.light_magenta()
);
}
}
OutputFormat::Json => {
let json = json!([{"route": response.multiaddr().into_diagnostic()? }]);
println!("{json}");
}
}
Ok(())
}
}

async fn run_impl(
Expand All @@ -96,5 +52,18 @@ async fn run_impl(
let node = BackgroundNode::create(&ctx, &opts.state, &node_name).await?;
let request = api::create_tcp_connection(&cmd);
let transport_status: TransportStatus = node.ask(&ctx, request).await?;
cmd.print_output(&opts, &transport_status)
let to = transport_status.socket_addr().into_diagnostic()?;
let plain = formatdoc! {r#"
TCP Connection:
From: /node/{from}
To: {to} (/ip4/{}/tcp/{})
Address: {}
"#, to.ip(), to.port(), transport_status.multiaddr().into_diagnostic()?};
let json = serde_json::json!([{"route": transport_status.multiaddr().into_diagnostic()? }]);
opts.terminal
.stdout()
.plain(plain)
.json(json)
.write_line()?;
Ok(())
}
96 changes: 66 additions & 30 deletions implementations/rust/ockam/ockam_command/src/tcp/inlet/delete.rs
Original file line number Diff line number Diff line change
@@ -1,17 +1,18 @@
use clap::Args;
use colorful::Colorful;
use indoc::formatdoc;
use miette::{miette, IntoDiagnostic};

use ockam::Context;
use ockam_api::nodes::models::portal::InletStatus;
use ockam_api::nodes::models::portal::{InletList, InletStatus};
use ockam_api::nodes::BackgroundNode;
use ockam_core::api::Request;

use crate::fmt_ok;
use crate::node::{get_node_name, initialize_node_if_default, NodeOpts};
use crate::tcp::util::alias_parser;
use crate::tcp::util::{alias_parser, fetch_list};
use crate::util::{node_rpc, parse_node_name};
use crate::{docs, CommandGlobalOpts};
use crate::{fmt_err, fmt_ok};

const AFTER_LONG_HELP: &str = include_str!("./static/delete/after_long_help.txt");

Expand All @@ -20,8 +21,8 @@ const AFTER_LONG_HELP: &str = include_str!("./static/delete/after_long_help.txt"
#[command(after_long_help = docs::after_help(AFTER_LONG_HELP))]
pub struct DeleteCommand {
/// Delete the inlet with this alias
#[arg(display_order = 900, required = true, id = "ALIAS", value_parser = alias_parser)]
alias: String,
#[arg(display_order = 900, id = "ALIAS", value_parser = alias_parser)]
alias: Option<String>,

/// Node on which to stop the tcp inlet. If none are provided, the default node will be used
#[command(flatten)]
Expand All @@ -43,37 +44,72 @@ pub async fn run_impl(
ctx: Context,
(opts, cmd): (CommandGlobalOpts, DeleteCommand),
) -> miette::Result<()> {
let mut selected_aliases = Vec::with_capacity(1);
let node_name = get_node_name(&opts.state, &cmd.node_opts.at_node);
let node_name = parse_node_name(&node_name)?;
let node = BackgroundNode::create(&ctx, &opts.state, &node_name).await?;

// Check if alias exists
let alias = cmd.alias;
node.ask_and_get_reply::<_, InletStatus>(&ctx, Request::get(format!("/node/inlet/{alias}")))
.await?
.found()
.into_diagnostic()?
.ok_or(miette!(
"TCP inlet with alias {alias} was not found on Node {node_name}"
))?;
if let Some(alias) = cmd.alias.as_ref() {
// Check if alias exists
let inlet_staus = node
.ask_and_get_reply::<_, InletStatus>(&ctx, Request::get(format!("/node/inlet/{alias}")))
.await?
.found()
.into_diagnostic()?
.ok_or(miette!(
"TCP inlet with alias {alias} was not found on Node {node_name}"
))?;
selected_aliases.push(inlet_staus.alias);
} else {
if !opts.terminal.can_ask_for_user_input() {
return Err(miette!("No alias was provided"));
}
// Get all the avilable aliases on the node
let aliases: Vec<String> =
fetch_list::<_, InletList>("/node/inlet", &ctx, &node, &opts.terminal)
.await?
.list
.into_iter()
.map(|status| status.alias)
.collect();
if aliases.is_empty() {
return Err(miette!("There's no TCP inlents to choose from"));
}
// Show the multible selection menu
selected_aliases = opts
.terminal
.select_multiple("Select an inlet/s to delete".to_string(), aliases);
if selected_aliases.is_empty() {
return Err(miette!("User did not select anything"));
};
}

// Proceed with the deletion
if opts
.terminal
.confirmed_with_flag_or_prompt(cmd.yes, "Are you sure you want to delete this TCP inlet?")?
{
node.tell(&ctx, Request::delete(format!("/node/inlet/{alias}")))
.await?;
// Confirm that the user know what he do
let know = if cmd.alias.is_none() {
opts.terminal.confirm_interactively(formatdoc!(
"Are you sure that you like to delete these items : {:?}?",
selected_aliases
))
} else {
opts.terminal.confirmed_with_flag_or_prompt(
cmd.yes,
"Are you sure that you want to delete this TCP inlet?",
)?
};

opts.terminal
.stdout()
.plain(fmt_ok!(
"TCP inlet with alias {alias} on Node {node_name} has been deleted"
))
.machine(&alias)
.json(serde_json::json!({ "alias": alias, "node": node_name }))
.write_line()
.unwrap();
if know {
for alias in selected_aliases {
let msg = match node
.tell(&ctx, Request::delete(format!("/node/inlet/{alias}")))
.await
{
Ok(_) => fmt_ok!(
"✅ TCP inlet with alias `{alias}` on Node {node_name} has been deleted"
),
Err(e) => fmt_err!("⚠️ Can't delete `{alias}` becuse: {e}"),
};
opts.terminal.clone().stdout().plain(msg).write_line()?;
}
}
Ok(())
}
36 changes: 36 additions & 0 deletions implementations/rust/ockam/ockam_command/src/tcp/util.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,17 @@
use crate::terminal::{OckamColor, Terminal, TerminalWriter};
use crate::Result;
use colorful::Colorful;
use miette::miette;
use ockam::Context;
use ockam_api::nodes::BackgroundNode;
use ockam_core::api::Request;





use tokio::sync::Mutex;
use tokio::try_join;

pub fn alias_parser(arg: &str) -> Result<String> {
if arg.contains(':') {
Expand All @@ -8,3 +20,27 @@ pub fn alias_parser(arg: &str) -> Result<String> {
Ok(arg.to_string())
}
}

pub async fn fetch_list<T: TerminalWriter, L: for<'b> minicbor::Decode<'b, ()>>(
endpoint: &str,
ctx: &Context,
node: &BackgroundNode,
terminal: &Terminal<T>,
) -> miette::Result<L> {
let is_finished: Mutex<bool> = Mutex::new(false);

let get_list = async {
let items: L = node.ask(ctx, Request::get(endpoint)).await?;
*is_finished.lock().await = true;
Ok(items)
};
let output_messages = vec![format!(
"Listing TCP Inlets on {}...\n",
node.name().color(OckamColor::PrimaryResource.color())
)];

let progress_output = terminal.progress_output(&output_messages, &is_finished);

let (items, _) = try_join!(get_list, progress_output)?;
Ok(items)
}

0 comments on commit 5fe4fa1

Please sign in to comment.