use rsb_derive::Builder;
use serde::{Deserialize, Serialize};
use serde_with::{serde_as, skip_serializing_none};
use crate::errors::{map_serde_error, SlackClientError};
use crate::models::blocks::*;
use crate::models::*;
use crate::SlackClientSession;
use crate::{ClientResult, SlackClientHttpConnector};
const BLOCKS_VALIDATE_FAILURE_CODES: [&str; 3] =
["invalid_blocks", "invalid_message", "invalid_view"];
impl<'a, SCHC> SlackClientSession<'a, SCHC>
where
SCHC: SlackClientHttpConnector + Send,
{
pub async fn blocks_validate(
&self,
req: &SlackApiBlocksValidateRequest,
) -> ClientResult<SlackApiBlocksValidateResponse> {
match self
.http_session_api
.http_post("blocks.validate", req, None)
.await
{
Err(SlackClientError::ApiError(api_error))
if BLOCKS_VALIDATE_FAILURE_CODES.contains(&api_error.code.as_str()) =>
{
match api_error.http_response_body.as_deref() {
Some(body) => {
serde_json::from_str(body).map_err(|e| map_serde_error(e, Some(body)))
}
None => Err(SlackClientError::ApiError(api_error)),
}
}
other => other,
}
}
}
#[serde_as]
#[skip_serializing_none]
#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
pub struct SlackApiBlocksValidateRequest {
#[serde_as(as = "Option<serde_with::json::JsonString>")]
pub blocks: Option<Vec<SlackBlock>>,
#[serde_as(as = "Option<serde_with::json::JsonString>")]
pub message: Option<SlackMessageContent>,
#[serde_as(as = "Option<serde_with::json::JsonString>")]
pub view: Option<SlackView>,
}
#[skip_serializing_none]
#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
pub struct SlackApiBlocksValidateResponse {
pub ok: bool,
pub error: Option<SlackApiBlocksValidateErrorKind>,
pub errors: Option<Vec<SlackApiBlocksValidateError>>,
}
#[derive(Debug, PartialEq, Eq, Hash, Clone, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum SlackApiBlocksValidateErrorKind {
InvalidBlocks,
InvalidMessage,
InvalidView,
#[serde(untagged)]
Other(String),
}
#[skip_serializing_none]
#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
pub struct SlackApiBlocksValidateError {
pub pointer: String,
pub code: SlackApiBlocksValidateErrorCode,
pub message: String,
pub constraint: Option<serde_json::Value>,
pub field: Option<String>,
}
#[derive(Debug, PartialEq, Eq, Hash, Clone, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum SlackApiBlocksValidateErrorCode {
FailedConstraint,
MissingField,
#[serde(untagged)]
Other(String),
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn test_slack_api_blocks_validate_request_encodes_payload_as_json_string() {
let req =
SlackApiBlocksValidateRequest::new().with_blocks(vec![SlackDividerBlock::new().into()]);
let json = serde_json::to_value(&req).unwrap();
assert_eq!(
json,
serde_json::json!({ "blocks": "[{\"type\":\"divider\"}]" })
);
let decoded: SlackApiBlocksValidateRequest = serde_json::from_value(json).unwrap();
assert_eq!(decoded, req);
}
#[test]
fn test_slack_api_blocks_validate_error_response_round_trip() {
let payload = r#"{"ok":false,"error":"invalid_message","errors":[{"code":"failed_constraint","constraint":{"type":"enum","expected":["divider","image","section"],"got":"markdown"},"message":"unsupported type: markdown","pointer":"/blocks/0/child_blocks/0/type"}]}"#;
let envelope: crate::SlackEnvelopeMessage = serde_json::from_str(payload).unwrap();
assert_eq!(envelope.error.as_deref(), Some("invalid_message"));
assert_eq!(
envelope.errors.as_ref().unwrap()[0]["code"],
"failed_constraint"
);
assert!(envelope.errors_as_strings().unwrap()[0].contains("\"failed_constraint\""));
let response: SlackApiBlocksValidateResponse = serde_json::from_str(payload).unwrap();
assert!(!response.ok);
assert_eq!(
response.error,
Some(SlackApiBlocksValidateErrorKind::InvalidMessage)
);
let errors = response.errors.as_ref().unwrap();
assert_eq!(errors[0].pointer, "/blocks/0/child_blocks/0/type");
assert_eq!(
errors[0].code,
SlackApiBlocksValidateErrorCode::FailedConstraint
);
assert_eq!(errors[0].constraint.as_ref().unwrap()["got"], "markdown");
assert_eq!(errors[0].field, None);
assert_eq!(
serde_json::to_value(&response).unwrap(),
serde_json::from_str::<serde_json::Value>(payload).unwrap()
);
let ok: SlackApiBlocksValidateResponse = serde_json::from_str(r#"{"ok":true}"#).unwrap();
assert!(ok.ok && ok.errors.is_none());
let other_code: SlackApiBlocksValidateErrorCode =
serde_json::from_str(r#""something_new""#).unwrap();
assert_eq!(
other_code,
SlackApiBlocksValidateErrorCode::Other("something_new".into())
);
assert_eq!(
serde_json::to_string(&other_code).unwrap(),
r#""something_new""#
);
let other: SlackApiBlocksValidateErrorKind =
serde_json::from_str(r#""something_new""#).unwrap();
assert_eq!(
other,
SlackApiBlocksValidateErrorKind::Other("something_new".into())
);
assert_eq!(serde_json::to_string(&other).unwrap(), r#""something_new""#);
}
}