diff --git a/Cargo.toml b/Cargo.toml index df087c1..67dac4c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "slack-morphism" -version = "2.2.0" +version = "2.3.0-alpha.1" authors = ["Abdulla Abdurakhmanov "] edition = "2021" license = "Apache-2.0" diff --git a/examples/client.rs b/examples/client.rs index 5b03e78..7ec0608 100644 --- a/examples/client.rs +++ b/examples/client.rs @@ -88,13 +88,34 @@ async fn test_file_upload() -> Result<(), Box ClientResult { + self.http_session_api + .http_get( + "files.getUploadURLExternal", + &vec![ + ("filename", Some(&req.filename)), + ("length", Some(&req.length.to_string())), + ("alt_txt", req.alt_txt.as_ref()), + ("snippet_type", req.snippet_type.as_ref().map(|v| v.value())), + ], + Some(&SLACK_TIER4_METHOD_CONFIG), + ) + .await + } + + pub async fn files_upload_via_url( + &self, + req: &SlackApiFilesUploadViaUrlRequest, + ) -> ClientResult { + self.http_session_api + .http_post_uri_binary( + req.upload_url.value().clone(), + req.content_type.clone(), + &req.content, + Some(&SLACK_TIER4_METHOD_CONFIG), + ) + .await + } + + /// + /// https://api.slack.com/methods/files.completeUploadExternal + /// + pub async fn files_complete_upload_external( + &self, + req: &SlackApiFilesCompleteUploadExternalRequest, + ) -> ClientResult { + self.http_session_api + .http_post( + "files.completeUploadExternal", + req, + Some(&SLACK_TIER4_METHOD_CONFIG), + ) + .await + } } #[skip_serializing_none] @@ -87,6 +145,56 @@ pub struct SlackApiFilesUploadResponse { pub file: SlackFile, } +#[skip_serializing_none] +#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)] +pub struct SlackApiFilesGetUploadUrlExternalRequest { + pub filename: String, + pub length: usize, + pub alt_txt: Option, + pub snippet_type: Option, +} + +#[skip_serializing_none] +#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)] +pub struct SlackApiFilesGetUploadUrlExternalResponse { + pub upload_url: SlackFileUploadUrl, + pub file_id: SlackFileId, +} + +#[skip_serializing_none] +#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)] +pub struct SlackApiFilesUploadViaUrlRequest { + pub upload_url: SlackFileUploadUrl, + pub content: Vec, + pub content_type: String, +} + +#[skip_serializing_none] +#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)] +pub struct SlackApiFilesUploadViaUrlResponse {} + +#[skip_serializing_none] +#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)] +pub struct SlackApiFilesCompleteUploadExternalRequest { + pub files: Vec, + pub channel_id: Option, + pub initial_comment: Option, + pub thread_ts: Option, +} + +#[skip_serializing_none] +#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)] +pub struct SlackApiFilesCompleteUploadExternalResponse { + pub files: Vec, +} + +#[skip_serializing_none] +#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)] +pub struct SlackApiFilesComplete { + pub id: SlackFileId, + pub title: Option, +} + fn to_csv(x: &Option>, s: S) -> Result { match x { None => s.serialize_none(), diff --git a/src/client.rs b/src/client.rs index 14a75d8..35a42c1 100644 --- a/src/client.rs +++ b/src/client.rs @@ -152,6 +152,16 @@ pub trait SlackClientHttpConnector { } } + fn http_post_uri_binary<'a, 'p, RS>( + &'a self, + full_uri: Url, + content_type: String, + data: &'a [u8], + context: SlackClientApiCallContext<'a>, + ) -> BoxFuture<'a, ClientResult> + where + RS: for<'de> serde::de::Deserialize<'de> + Send + 'a + Send + 'a; + fn create_method_uri_path(&self, method_relative_uri: &str) -> ClientResult { Ok(SlackClientHttpApiUri::create_method_uri_path(method_relative_uri).parse()?) } @@ -378,4 +388,54 @@ where .http_post_multipart_form(method_relative_uri, file, params, context) .await } + + pub async fn http_post_uri_multipart_form<'p, RS, PT, TS>( + &self, + full_uri: Url, + file: Option>, + params: &'p PT, + rate_control_params: Option<&'a SlackApiMethodRateControlConfig>, + ) -> ClientResult + where + RS: for<'de> serde::de::Deserialize<'de> + Send, + PT: std::iter::IntoIterator)> + Clone, + TS: AsRef + 'p + Send, + { + let context = SlackClientApiCallContext { + rate_control_params, + token: Some(self.token), + tracing_span: &self.span, + is_sensitive_url: false, + }; + + self.client + .http_api + .connector + .http_post_uri_multipart_form(full_uri, file, params, context) + .await + } + + pub async fn http_post_uri_binary<'p, RS>( + &self, + full_uri: Url, + content_type: String, + data: &'a [u8], + rate_control_params: Option<&'a SlackApiMethodRateControlConfig>, + ) -> ClientResult + where + RS: for<'de> serde::de::Deserialize<'de> + Send, + { + let context = SlackClientApiCallContext { + rate_control_params, + token: Some(self.token), + tracing_span: &self.span, + is_sensitive_url: true, + }; + + self.client + .http_api + .connector + .http_post_uri_binary(full_uri, content_type, data, context) + .await + } } diff --git a/src/hyper_tokio/connector.rs b/src/hyper_tokio/connector.rs index 7fccfac..60e9501 100644 --- a/src/hyper_tokio/connector.rs +++ b/src/hyper_tokio/connector.rs @@ -19,10 +19,12 @@ use crate::hyper_tokio::multipart_form::{ use crate::multipart_form::FileMultipartData; use crate::prelude::hyper_ext::HyperExtensions; use crate::ratectl::SlackApiRateControlConfig; +use bytes::BytesMut; use std::hash::Hash; use std::hash::Hasher; use std::sync::Arc; use std::time::Duration; + use tracing::*; use url::Url; @@ -448,4 +450,46 @@ impl SlackClientHttpConnect Err(err) => futures::future::err(err.into()).boxed(), } } + + fn http_post_uri_binary<'a, 'p, RS>( + &'a self, + full_uri: Url, + content_type: String, + data: &'a [u8], + context: SlackClientApiCallContext<'a>, + ) -> BoxFuture<'a, ClientResult> + where + RS: for<'de> serde::de::Deserialize<'de> + Send + 'a + Send + 'a, + { + let context_token = context.token; + let body_bytes = BytesMut::from(data).freeze(); + + async move { + let response_body = self + .send_rate_controlled_request( + move || { + let http_body = Full::new(body_bytes.clone()).boxed(); + let http_base_request = HyperExtensions::create_http_request( + full_uri.clone(), + hyper::http::Method::POST, + ) + .header("content-type", content_type.as_str()); + + let http_request = HyperExtensions::setup_token_auth_header( + http_base_request, + context_token, + ); + + http_request.body(http_body).map_err(|e| e.into()) + }, + context, + None, + 0, + ) + .await?; + + Ok(response_body) + } + .boxed() + } } diff --git a/src/models/files/mod.rs b/src/models/files/mod.rs index 061c48b..4f1a34f 100644 --- a/src/models/files/mod.rs +++ b/src/models/files/mod.rs @@ -7,6 +7,7 @@ use serde_with::skip_serializing_none; use url::Url; pub mod comments; + pub use comments::*; #[derive(Debug, Eq, PartialEq, Hash, Clone, Serialize, Deserialize, ValueStruct)] @@ -21,6 +22,12 @@ pub struct SlackFilePrettyType(pub String); #[derive(Debug, Eq, PartialEq, Hash, Clone, Serialize, Deserialize, ValueStruct)] pub struct SlackFileExternalType(pub String); +#[derive(Debug, Eq, PartialEq, Hash, Clone, Serialize, Deserialize, ValueStruct)] +pub struct SlackFileSnippetType(pub String); + +#[derive(Debug, Eq, PartialEq, Hash, Clone, Serialize, Deserialize, ValueStruct)] +pub struct SlackFileUploadUrl(pub Url); + #[skip_serializing_none] #[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)] pub struct SlackFile {