Skip to content

Commit 09517fa

Browse files
committed
Posting a file with tedge http post --file
Signed-off-by: Didier Wenzek <[email protected]>
1 parent bb192d3 commit 09517fa

File tree

2 files changed

+102
-40
lines changed

2 files changed

+102
-40
lines changed

crates/core/tedge/src/cli/http/cli.rs

Lines changed: 52 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,17 @@
1+
use crate::cli::http::command::HttpAction;
12
use crate::cli::http::command::HttpCommand;
23
use crate::command::BuildCommand;
34
use crate::command::BuildContext;
45
use crate::command::Command;
56
use crate::ConfigError;
67
use anyhow::anyhow;
78
use anyhow::Error;
9+
use camino::Utf8PathBuf;
810
use certificate::CloudRootCerts;
11+
use clap::Args;
912
use reqwest::blocking;
1013
use reqwest::Identity;
14+
use std::fs::File;
1115
use tedge_config::OptionalConfig;
1216
use tedge_config::ProfileName;
1317

@@ -18,8 +22,9 @@ pub enum TEdgeHttpCli {
1822
/// Target URI
1923
uri: String,
2024

21-
/// Content to post
22-
content: String,
25+
/// Content to send
26+
#[command(flatten)]
27+
content: Content,
2328

2429
/// Optional c8y cloud profile
2530
#[clap(long)]
@@ -31,8 +36,9 @@ pub enum TEdgeHttpCli {
3136
/// Target URI
3237
uri: String,
3338

34-
/// Content to post
35-
content: String,
39+
/// Content to send
40+
#[command(flatten)]
41+
content: Content,
3642

3743
/// Optional c8y cloud profile
3844
#[clap(long)]
@@ -60,6 +66,40 @@ pub enum TEdgeHttpCli {
6066
},
6167
}
6268

69+
#[derive(Args, Clone, Debug)]
70+
#[group(required = true, multiple = false)]
71+
pub struct Content {
72+
/// Content to send
73+
#[arg(name = "content")]
74+
arg2: Option<String>,
75+
76+
/// Content to send
77+
#[arg(long)]
78+
data: Option<String>,
79+
80+
/// File which content is sent
81+
#[arg(long)]
82+
file: Option<Utf8PathBuf>,
83+
}
84+
85+
impl TryFrom<Content> for blocking::Body {
86+
type Error = std::io::Error;
87+
88+
fn try_from(content: Content) -> Result<Self, Self::Error> {
89+
let body: blocking::Body = if let Some(data) = content.arg2 {
90+
data.into()
91+
} else if let Some(data) = content.data {
92+
data.into()
93+
} else if let Some(file) = content.file {
94+
File::open(file)?.into()
95+
} else {
96+
"".into()
97+
};
98+
99+
Ok(body)
100+
}
101+
}
102+
63103
impl BuildCommand for TEdgeHttpCli {
64104
fn build_command(self, context: BuildContext) -> Result<Box<dyn Command>, ConfigError> {
65105
let config = context.load_config()?;
@@ -79,27 +119,20 @@ impl BuildCommand for TEdgeHttpCli {
79119
};
80120

81121
let url = format!("{protocol}://{host}:{port}{uri}");
82-
let verb_url = format!("{} {url}", self.verb());
83122
let identity = config.http.client.auth.identity()?;
84123
let client = http_client(config.cloud_root_certs(), identity.as_ref())?;
85124

86-
let request = match self {
87-
TEdgeHttpCli::Post { content, .. } => client
88-
.post(url)
89-
.header("Accept", "application/json")
90-
.header("Content-Type", "application/json")
91-
.body(content),
92-
TEdgeHttpCli::Put { content, .. } => client
93-
.put(url)
94-
.header("Content-Type", "application/json")
95-
.body(content),
96-
TEdgeHttpCli::Get { .. } => client.get(url).header("Accept", "application/json"),
97-
TEdgeHttpCli::Delete { .. } => client.delete(url),
125+
let action = match self {
126+
TEdgeHttpCli::Post { content, .. } => HttpAction::Post(content),
127+
TEdgeHttpCli::Put { content, .. } => HttpAction::Put(content),
128+
TEdgeHttpCli::Get { .. } => HttpAction::Get,
129+
TEdgeHttpCli::Delete { .. } => HttpAction::Delete,
98130
};
99131

100132
Ok(HttpCommand {
101-
url: verb_url,
102-
request,
133+
client,
134+
url,
135+
action,
103136
}
104137
.into_boxed())
105138
}
@@ -115,15 +148,6 @@ impl TEdgeHttpCli {
115148
}
116149
}
117150

118-
fn verb(&self) -> &str {
119-
match self {
120-
TEdgeHttpCli::Post { .. } => "POST",
121-
TEdgeHttpCli::Put { .. } => "PUT",
122-
TEdgeHttpCli::Get { .. } => "GET",
123-
TEdgeHttpCli::Delete { .. } => "DELETE",
124-
}
125-
}
126-
127151
fn c8y_profile(&self) -> Option<&ProfileName> {
128152
match self {
129153
TEdgeHttpCli::Post { profile, .. }
Lines changed: 50 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,35 +1,73 @@
1+
use crate::cli::http::cli::Content;
12
use crate::command::Command;
23
use crate::log::MaybeFancy;
34
use anyhow::Error;
45
use reqwest::blocking;
56

67
pub struct HttpCommand {
8+
/// HTTP client
9+
pub client: blocking::Client,
10+
711
/// Target url
812
pub url: String,
913

10-
/// HTTP request
11-
pub request: blocking::RequestBuilder,
14+
/// Action
15+
pub action: HttpAction,
16+
}
17+
18+
pub enum HttpAction {
19+
Post(Content),
20+
Put(Content),
21+
Get,
22+
Delete,
1223
}
1324

1425
impl Command for HttpCommand {
1526
fn description(&self) -> String {
16-
self.url.clone()
27+
let verb = match self.action {
28+
HttpAction::Post(_) => "POST",
29+
HttpAction::Put(_) => "PUT",
30+
HttpAction::Get => "GET",
31+
HttpAction::Delete => "DELETE",
32+
};
33+
format!("{verb} {}", self.url)
1734
}
1835

1936
fn execute(&self) -> Result<(), MaybeFancy<Error>> {
20-
Ok(self.send()?)
37+
let request = self.request()?;
38+
HttpCommand::send(request)?;
39+
Ok(())
2140
}
2241
}
2342

2443
impl HttpCommand {
25-
fn send(&self) -> Result<(), Error> {
26-
if let Some(request) = self.request.try_clone() {
27-
let http_result = request.send()?;
28-
let http_response = http_result.error_for_status()?;
29-
let bytes = http_response.bytes()?.to_vec();
30-
let content = String::from_utf8(bytes)?;
31-
println!("{content}");
32-
}
44+
fn request(&self) -> Result<blocking::RequestBuilder, Error> {
45+
let client = &self.client;
46+
let url = &self.url;
47+
let request = match &self.action {
48+
HttpAction::Post(content) => client
49+
.post(url)
50+
.header("Accept", "application/json")
51+
.header("Content-Type", "application/json")
52+
.body(blocking::Body::try_from(content.clone())?),
53+
HttpAction::Put(content) => client
54+
.put(url)
55+
.header("Content-Type", "application/json")
56+
.body(blocking::Body::try_from(content.clone())?),
57+
HttpAction::Get => client.get(url).header("Accept", "application/json"),
58+
HttpAction::Delete => client.delete(url),
59+
};
60+
61+
Ok(request)
62+
}
63+
64+
fn send(request: blocking::RequestBuilder) -> Result<(), Error> {
65+
let http_result = request.send()?;
66+
let http_response = http_result.error_for_status()?;
67+
let bytes = http_response.bytes()?.to_vec();
68+
let content = String::from_utf8(bytes)?;
69+
70+
println!("{content}");
3371
Ok(())
3472
}
3573
}

0 commit comments

Comments
 (0)