botx-api 0.1.6

Обертка над BotX api (eXpress)
Documentation
use std::collections::HashMap;

use serde::{Serialize, Deserialize};
use uuid::Uuid;

use crate::api::models::*;

#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct DirectNotificationResponse {
    /// Идентификатор отправляемого сообщения (тут или в result, не известно от чего зависит)
    pub sync_id: Option<Uuid>,
    pub result: DirectNotificationResult,
}

#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct DirectNotificationResult {
    /// Идентификатор отправляемого сообщения (тут или на уровень выше, не известно от чего зависит)
    pub sync_id: Option<Uuid>,
}

#[derive(Debug, Serialize, Deserialize, Clone)]
#[serde(tag = "reason")]
pub enum DirectNotificationExpressError {
    #[serde(rename(serialize = "chat_not_found", deserialize = "chat_not_found"))]
    ChatNotFound(ChatNotFoundWithEventId),
    #[serde(rename(serialize = "error_from_messaging_service", deserialize = "error_from_messaging_service"))]
    ErrorFromMessagingService(ErrorFromMessagingServiceWithEventId),
    #[serde(rename(serialize = "bot_is_not_a_chat_member", deserialize = "bot_is_not_a_chat_member"))]
    BotIsNotAChatMember(BotIsNotAChatMember),
    #[serde(rename(serialize = "stealth_mode_disabled", deserialize = "stealth_mode_disabled"))]
    StealthModeDisabled(StealthModeDisabled),
    #[serde(rename(serialize = "event_recipients_list_is_empty", deserialize = "event_recipients_list_is_empty"))]
    EventRecipientsListIsEmpty(EventRecipientsListIsEmpty),
    #[serde(rename(serialize = "flow_processing_error", deserialize = "flow_processing_error"))]
    FlowProcessingError(FlowProcessingError),
    #[serde(rename(serialize = "unexpected_error", deserialize = "unexpected_error"))]
    UnexpectedError(UnexpectedError),

    // TODO: добавить десериализацию в HashMap<string, string> когда завезут реализацию
    #[serde(other)]
    Other,
}

#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct StealthModeDisabled {
    pub sync_id: Uuid,
    pub errors: Vec<String>,
    pub error_data: StealthModeDisabledData,
}

#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct StealthModeDisabledData {
    pub group_chat_id: Uuid,
    pub bot_id: Uuid,
    pub error_description: String,
}

#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct FlowProcessingError {
    pub sync_id: Uuid,
    pub errors: Vec<String>,
    pub error_data: FlowProcessingErrorData,
}

#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct FlowProcessingErrorData {
    pub error_description: String,
}

#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct UnexpectedError {
    pub sync_id: Uuid,
    pub errors: Vec<String>,
    pub error_data: UnexpectedErrorData,
}

#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct UnexpectedErrorData {
    pub error_description: String,
}

#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct DirectNotificationExpressErrorData {

    pub bot_id: Option<Uuid>,

    pub group_chat_id: Option<Uuid>,

    /// Ожидается что будет всегда, но на всякий случай сделан опциональным
    pub error_description: Option<String>,

    pub reason: Option<String>,

    pub recipients_param: Option<Vec<Uuid>>,

    #[serde(flatten)]
    pub other: HashMap<String, String>,
}

#[derive(Debug, Serialize, Deserialize, Default, Clone, Builder)]
#[builder(setter(into, prefix = "with", strip_option))]
pub struct DirectNotificationRequest {
    /// идентификатор чата в который придет сообщение
    pub group_chat_id: Uuid,
    
    /// (Default: null) - huid получателей события. По умолчанию все участники чата
    // #[serde(skip_serializing_if = "Option::is_none")]
    #[builder(default)]
    pub recipients: Option<Vec<Uuid>>,

    /// Базовая часть нотификации
    #[builder(default)]
    pub notification: EventPayload,

    /// (Default: null) - файл в base64 представление.
    // #[serde(skip_serializing_if = "Option::is_none")]
    #[builder(default)]
    pub file: Option<File>,

    /// опции запроса
    #[builder(default)]
    pub opts: PayloadOptions,
}

#[cfg(test)]
mod tests {
    use crate::api::result::{ExpressResult, BotXApiResult, BotXApiError};

    use super::{DirectNotificationResponse, DirectNotificationExpressError};

    #[test]
    fn deserialize_result_ok()
    {
        let res = serde_json::from_str::<ExpressResult<DirectNotificationResponse, DirectNotificationExpressError>>(r#"{
            "status": "ok",
            "result": {
              "sync_id": "f0f105d2-101f-59b0-9e10-e432efce2c36"
            }
          }"#).unwrap();

        let res: BotXApiResult<DirectNotificationResponse, DirectNotificationExpressError> = res.into();
        let res = res.ok().unwrap();

        assert_eq!(res.result.sync_id.unwrap(), uuid::Uuid::parse_str("f0f105d2-101f-59b0-9e10-e432efce2c36").unwrap())
    }

    #[test]
    fn deserialize_error_chat_not_found_ok()
    {
        let res = serde_json::from_str::<ExpressResult<DirectNotificationResponse, DirectNotificationExpressError>>(r#"{
            "sync_id": "f0f105d2-101f-59b0-9e10-e432efce2c36",
            "status": "error",
            "reason": "chat_not_found",
            "errors": [],
            "error_data": {
              "group_chat_id": "705df263-6bfd-536a-9d51-13524afaab5c",
              "error_description": "Chat with specified id not found"
            }
          }"#).unwrap();

        let res: BotXApiResult<DirectNotificationResponse, DirectNotificationExpressError> = res.into();
        let err = res.err().unwrap();

        let BotXApiError::ExpressError(err) = err else {
            panic!("Некорректное десериализация ошибки {:#?}", err);
        };

        let DirectNotificationExpressError::ChatNotFound(err) = err else {
            panic!("Некорректное определение типа ошибки");
        };

        assert_eq!(err.error_data.group_chat_id, uuid::Uuid::parse_str("705df263-6bfd-536a-9d51-13524afaab5c").unwrap())
    }

    #[test]
    fn deserialize_error_error_from_messaging_service_ok()
    {
        let res = serde_json::from_str::<ExpressResult<DirectNotificationResponse, DirectNotificationExpressError>>(r#"{
            "sync_id": "f0f105d2-101f-59b0-9e10-e432efce2c36",
            "status": "error",
            "reason": "error_from_messaging_service",
            "errors": [],
            "error_data": {
              "group_chat_id": "705df263-6bfd-536a-9d51-13524afaab5c",
              "reason": "some_error",
              "error_description": "Messaging service returns error. Check BotX container logs (level :warn or upper) for more info."
            }
          }"#).unwrap();

        let res: BotXApiResult<DirectNotificationResponse, DirectNotificationExpressError> = res.into();
        let err = res.err().unwrap();

        let BotXApiError::ExpressError(err) = err else {
            panic!("Некорректное десериализация ошибки {:#?}", err);
        };

        let DirectNotificationExpressError::ErrorFromMessagingService(err) = err else {
            panic!("Некорректное определение типа ошибки");
        };

        assert_eq!(err.error_data.group_chat_id, uuid::Uuid::parse_str("705df263-6bfd-536a-9d51-13524afaab5c").unwrap())
    }

    #[test]
    fn deserialize_error_bot_is_not_a_chat_member_ok()
    {
        let res = serde_json::from_str::<ExpressResult<DirectNotificationResponse, DirectNotificationExpressError>>(r#"{
            "sync_id": "f0f105d2-101f-59b0-9e10-e432efce2c36",
            "status": "error",
            "reason": "bot_is_not_a_chat_member",
            "errors": [],
            "error_data": {
              "group_chat_id": "705df263-6bfd-536a-9d51-13524afaab5c",
              "bot_id": "b165f00f-3154-412c-7f11-c120164257da",
              "error_description": "Bot is not a chat member"
            }
          }"#).unwrap();

        let res: BotXApiResult<DirectNotificationResponse, DirectNotificationExpressError> = res.into();
        let err = res.err().unwrap();

        let BotXApiError::ExpressError(err) = err else {
            panic!("Некорректное десериализация ошибки {:#?}", err);
        };

        let DirectNotificationExpressError::BotIsNotAChatMember(err) = err else {
            panic!("Некорректное определение типа ошибки");
        };

        assert_eq!(err.error_data.group_chat_id, uuid::Uuid::parse_str("705df263-6bfd-536a-9d51-13524afaab5c").unwrap())
    }

    #[test]
    fn deserialize_error_stealth_mode_disabled_ok()
    {
        let res = serde_json::from_str::<ExpressResult<DirectNotificationResponse, DirectNotificationExpressError>>(r#"{
            "sync_id": "f0f105d2-101f-59b0-9e10-e432efce2c36",
            "status": "error",
            "reason": "stealth_mode_disabled",
            "errors": [],
            "error_data": {
              "group_chat_id": "705df263-6bfd-536a-9d51-13524afaab5c",
              "bot_id": "b165f00f-3154-412c-7f11-c120164257da",
              "error_description": "Stealth mode disabled in specified chat"
            }
          }"#).unwrap();

        let res: BotXApiResult<DirectNotificationResponse, DirectNotificationExpressError> = res.into();
        let err = res.err().unwrap();

        let BotXApiError::ExpressError(err) = err else {
            panic!("Некорректное десериализация ошибки {:#?}", err);
        };

        let DirectNotificationExpressError::StealthModeDisabled(err) = err else {
            panic!("Некорректное определение типа ошибки");
        };

        assert_eq!(err.error_data.group_chat_id, uuid::Uuid::parse_str("705df263-6bfd-536a-9d51-13524afaab5c").unwrap())
    }

    #[test]
    fn deserialize_error_event_recipients_list_is_empty_ok()
    {
        let res = serde_json::from_str::<ExpressResult<DirectNotificationResponse, DirectNotificationExpressError>>(r#"{
            "sync_id": "f0f105d2-101f-59b0-9e10-e432efce2c36",
            "status": "error",
            "reason": "event_recipients_list_is_empty",
            "errors": [],
            "error_data": {
              "group_chat_id": "705df263-6bfd-536a-9d51-13524afaab5c",
              "bot_id": "b165f00f-3154-412c-7f11-c120164257da",
              "recipients_param": ["b165f00f-3154-412c-7f11-c120164257da"],
              "error_description": "Event recipients list is empty"
            }
          }"#).unwrap();

        let res: BotXApiResult<DirectNotificationResponse, DirectNotificationExpressError> = res.into();
        let err = res.err().unwrap();

        let BotXApiError::ExpressError(err) = err else {
            panic!("Некорректное десериализация ошибки");
        };

        let DirectNotificationExpressError::EventRecipientsListIsEmpty(err) = err else {
            panic!("Некорректное определение типа ошибки {:#?}", err);
        };

        assert_eq!(err.error_data.bot_id, uuid::Uuid::parse_str("b165f00f-3154-412c-7f11-c120164257da").unwrap())
    }

    #[test]
    fn deserialize_error_flow_processing_error_ok()
    {
        let res = serde_json::from_str::<ExpressResult<DirectNotificationResponse, DirectNotificationExpressError>>(r#"{
            "sync_id": "f0f105d2-101f-59b0-9e10-e432efce2c36",
            "status": "error",
            "reason": "flow_processing_error",
            "errors": ["{error1}", "{error2}"],
            "error_data": {
              "error_description": "Got error on flow processing. Check BotX container logs (level :info or upper) for more info"
            }
          }"#).unwrap();

        let res: BotXApiResult<DirectNotificationResponse, DirectNotificationExpressError> = res.into();
        let err = res.err().unwrap();

        let BotXApiError::ExpressError(err) = err else {
            panic!("Некорректное десериализация ошибки {:#?}", err);
        };

        let DirectNotificationExpressError::FlowProcessingError(err) = err else {
            panic!("Некорректное определение типа ошибки");
        };

        assert_eq!(err.error_data.error_description, "Got error on flow processing. Check BotX container logs (level :info or upper) for more info".to_string())
    }

    #[test]
    fn deserialize_error_unexpected_error_ok()
    {
        let res = serde_json::from_str::<ExpressResult<DirectNotificationResponse, DirectNotificationExpressError>>(r#"{
            "sync_id": "f0f105d2-101f-59b0-9e10-e432efce2c36",
            "status": "error",
            "reason": "unexpected_error",
            "errors": ["{error1}", "{error2}"],
            "error_data": {
              "error_description": "Got unexpected error. Check BotX container logs (level :error or upper) for more info"
            }
          }"#).unwrap();

        let res: BotXApiResult<DirectNotificationResponse, DirectNotificationExpressError> = res.into();
        let err = res.err().unwrap();

        let BotXApiError::ExpressError(err) = err else {
            panic!("Некорректное десериализация ошибки {:#?}", err);
        };

        let DirectNotificationExpressError::UnexpectedError(err) = err else {
            panic!("Некорректное определение типа ошибки");
        };

        assert_eq!(err.error_data.error_description, "Got unexpected error. Check BotX container logs (level :error or upper) for more info".to_string())
    }
}