1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
use std::str::FromStr;
use clap::Parser;
use colorful::Colorful;
use serde_json::json;
use ockam::{route, Context};
use ockam_api::address::extract_address_value;
use ockam_api::nodes::models::secure_channel::DeleteSecureChannelResponse;
use ockam_api::nodes::BackgroundNodeClient;
use ockam_api::ReverseLocalConverter;
use ockam_core::{Address, AddressParseError};
use crate::util::{api, exitcode};
use crate::{docs, CommandGlobalOpts};
const LONG_ABOUT: &str = include_str!("./static/delete/long_about.txt");
const AFTER_LONG_HELP: &str = include_str!("./static/delete/after_long_help.txt");
/// Delete Secure Channels
#[derive(Clone, Debug, Parser)]
#[command(
arg_required_else_help = true,
long_about = docs::about(LONG_ABOUT),
after_long_help = docs::after_help(AFTER_LONG_HELP),
)]
pub struct DeleteCommand {
/// Node at which the secure channel was initiated
#[arg(value_name = "NODE", long, display_order = 800, value_parser = extract_address_value)]
at: Option<String>,
/// Address at which the channel to be deleted is running
#[arg(value_parser(parse_address), display_order = 800)]
address: Address,
/// Confirm the deletion without prompting
#[arg(display_order = 901, long, short)]
yes: bool,
}
impl DeleteCommand {
pub fn name(&self) -> String {
"secure-channel delete".into()
}
fn print_output(
&self,
node_name: &str,
address: &Address,
options: &CommandGlobalOpts,
response: DeleteSecureChannelResponse,
) -> miette::Result<()> {
match response.channel {
Some(address) => {
let route = &route![address];
match ReverseLocalConverter::convert_route(route) {
Ok(multiaddr) => {
// if stdout is not interactive/tty write the secure channel address to it
// in case some other program is trying to read it as piped input
if !options.terminal.is_tty() {
println!("{multiaddr}")
}
// if output format is json, write json to stdout.
if options.global_args.output_format().is_json() {
let json = json!([{ "address": multiaddr.to_string() }]);
println!("{json}");
}
// if stderr is interactive/tty and we haven't been asked to be quiet
// and output format is plain then write a plain info to stderr.
if options.terminal.is_tty()
&& !options.global_args.quiet
&& options.global_args.output_format().is_json()
{
if options.global_args.no_color {
eprintln!("\n Deleted Secure Channel:");
eprintln!(" • At: /node/{}", &node_name);
eprintln!(" • Address: {}", &self.address);
} else {
eprintln!("\n Deleted Secure Channel:");
// At:
eprintln!("{}", " • At: ".light_magenta());
eprintln!("{}", format!("/node/{node_name}").light_yellow());
// Address:
eprintln!("{}", " • Address: ".light_magenta());
eprintln!("{}", &self.address.to_string().light_yellow());
}
}
}
Err(_err) => {
// if stderr is interactive/tty and we haven't been asked to be quiet
// and output format is plain then write a plain info to stderr.
if options.terminal.is_tty()
&& !options.global_args.quiet
&& options.global_args.output_format().is_plain()
{
eprintln!(
"Could not convert returned secure channel route {route} into a multiaddr"
);
}
// return the exitcode::PROTOCOL since if things are going as expected
// a route in the response should be convertible to multiaddr.
std::process::exit(exitcode::PROTOCOL);
}
}
}
None => {
// if stderr is interactive/tty and we haven't been asked to be quiet
// and output format is plain then write a plain info to stderr.
if options.terminal.is_tty()
&& !options.global_args.quiet
&& options.global_args.output_format().is_plain()
{
eprintln!(
"Could not find secure channel with address {} at node {}",
address, &node_name
);
}
println!("channel with address {address} not found")
}
}
Ok(())
}
pub async fn run(&self, ctx: &Context, opts: CommandGlobalOpts) -> miette::Result<()> {
if opts.terminal.confirmed_with_flag_or_prompt(
self.yes,
"Are you sure you want to delete this secure channel?",
)? {
let node = BackgroundNodeClient::create(ctx, &opts.state, &self.at).await?;
let address = &self.address;
let response: DeleteSecureChannelResponse =
node.ask(ctx, api::delete_secure_channel(address)).await?;
self.print_output(node.node_name(), address, &opts, response)?;
}
Ok(())
}
}
fn parse_address(input: &str) -> Result<Address, AddressParseError> {
let buf: String = input.into();
if buf.contains("/service/") {
let service_vec: Vec<_> = buf.split('/').collect();
// If /service/<some value> was passed, we will have split len greater than or equal to 3
// ["", "service", "228003f018d277a7e53f15475d111052"]
// we will pass index 2 to from_str
// EG: /service/228003f018d277a7e53f15475d111052
// /service/228003f018d277a7e53f15475d111052/
if service_vec.len() >= 3 && !service_vec[2].is_empty() {
return Address::from_str(service_vec[2]);
}
}
Address::from_str(&buf)
}