Properly parse telegram answer

This commit is contained in:
Waffle 2019-09-08 02:43:58 +03:00
parent c3052ed036
commit dc755b6ec0
8 changed files with 82 additions and 46 deletions

View file

@ -1,10 +1,19 @@
use apply::Apply;
use reqwest::r#async::{multipart::Form, Client};
use serde::de::DeserializeOwned;
use serde_json::Value;
use crate::core::{
requests::{RequestError, ResponseResult},
types::ResponseParameters,
};
use apply::Apply;
use serde_json::Value;
use serde::{
Serialize,
de::DeserializeOwned
};
use reqwest::{
StatusCode,
r#async::{Client, multipart::Form},
};
use crate::core::requests::{RequestError, ResponseResult};
use serde::Serialize;
const TELEGRAM_API_URL: &str = "https://api.telegram.org";
@ -48,21 +57,22 @@ pub async fn request_multipart<T: DeserializeOwned>(
.await
.map_err(RequestError::NetworkError)?;
let response_json = serde_json::from_str::<Value>(
let response = serde_json::from_str::<TelegramResponse<T>>(
&response
.text()
.await
.map_err(RequestError::NetworkError)?,
)
.map_err(RequestError::InvalidJson)?;
.map_err(RequestError::InvalidJson)?;
if response_json["ok"] == "false" {
Err(RequestError::ApiError {
status_code: response.status(),
description: response_json["description"].to_string(),
})
} else {
Ok(serde_json::from_value(response_json["result"].clone()).unwrap())
match response {
TelegramResponse::Ok { result, .. } => Ok(result),
TelegramResponse::Err {
description,
error_code,
response_parameters,
..
} => Err(RequestError::ApiError { description, status_code: StatusCode::from_u16(error_code).unwrap() })
}
}
@ -79,24 +89,40 @@ pub async fn request_json<T: DeserializeOwned, P: Serialize>(
.await
.map_err(RequestError::NetworkError)?;
let response_json = serde_json::from_str::<Value>(
let response = serde_json::from_str::<TelegramResponse<T>>(
&response
.text()
.await
.map_err(RequestError::NetworkError)?,
)
.map_err(RequestError::InvalidJson)?;
.map_err(RequestError::InvalidJson)?;
if response_json["ok"] == "false" {
Err(RequestError::ApiError {
status_code: response.status(),
description: response_json["description"].to_string(),
})
} else {
Ok(serde_json::from_value(response_json["result"].clone()).unwrap())
match response {
TelegramResponse::Ok { result, .. } => Ok(result),
TelegramResponse::Err {
description,
error_code,
response_parameters,
..
} => Err(RequestError::ApiError { description, status_code: StatusCode::from_u16(error_code).unwrap() })
}
}
#[derive(Deserialize)]
#[serde(untagged)]
enum TelegramResponse<R> {
Ok {
ok: bool, // true
result: R,
},
Err {
ok: bool, // false
description: String,
error_code: u16,
response_parameters: Option<ResponseParameters>,
},
}
#[cfg(test)]
mod tests {
use super::*;

View file

@ -37,14 +37,14 @@ impl<'a> Request<'a> for ForwardMessage<'a> {
type ReturnValue = Message;
fn send(self) -> RequestFuture<'a, ResponseResult<Self::ReturnValue>> {
Box::pin(
Box::pin(async move {
network::request_json(
self.ctx.client,
self.ctx.token,
"forwardMessage",
&self,
)
)
).await
})
}
}

View file

@ -13,7 +13,7 @@ mod utils;
#[derive(Debug, Display)]
pub enum RequestError {
#[display(fmt = "Telegram error #{}: {}", status_code, description)]
ApiError {
ApiError { // TODO: add response parameters
status_code: StatusCode,
description: String,
},

View file

@ -52,25 +52,25 @@ impl<'a> Request<'a> for SendMessage<'a> {
type ReturnValue = Message;
fn send(self) -> RequestFuture<'a, ResponseResult<Self::ReturnValue>> {
Box::pin(
Box::pin(async move {
network::request_json(
self.ctx.client,
self.ctx.token,
"sendMessage",
&self,
)
)
).await
})
}
}
impl<'a> SendMessage<'a> {
pub(crate) fn new(
info: RequestContext<'a>,
ctx: RequestContext<'a>,
chat_id: ChatId,
text: String,
) -> Self {
SendMessage {
ctx: info,
ctx,
chat_id,
text,
parse_mode: None,

View file

@ -3,7 +3,6 @@ use crate::core::types::{ChatPermissions, ChatPhoto, Message};
#[derive(Debug, Deserialize, Eq, Hash, PartialEq, Clone)]
pub struct Chat {
#[serde(rename = "chat_id")]
pub id: i64,
#[serde(flatten)]
pub kind: ChatKind,
@ -103,7 +102,7 @@ mod tests {
photo: None,
};
let actual = from_str(
r#"{"chat_id":-1,"type":"channel","username":"channelname"}"#,
r#"{"id":-1,"type":"channel","username":"channelname"}"#,
)
.unwrap();
assert_eq!(expected, actual);
@ -123,12 +122,12 @@ mod tests {
photo: None
},
from_str(
r#"{"chat_id":0,"type":"private","username":"username","first_name":"Anon"}"#
r#"{"id":0,"type":"private","username":"username","first_name":"Anon"}"#
).unwrap());
}
#[test]
fn private_chat_de_wrong_type_field() {
assert!(from_str::<Chat>(r#"{"chat_id":0,"type":"WRONG"}"#).is_err());
assert!(from_str::<Chat>(r#"{"id":0,"type":"WRONG"}"#).is_err());
}
}

View file

@ -190,7 +190,7 @@ mod tests {
reply_markup: None,
},
};
let actual = from_str::<Message>(r#"{"message_id":0,"date":0,"chat":{"chat_id":0,"type":"private"},"text":"Hello","entities":[]}"#).unwrap();
let actual = from_str::<Message>(r#"{"message_id":0,"date":0,"chat":{"id":0,"type":"private"},"text":"Hello","entities":[]}"#).unwrap();
assert_eq!(expected, actual);
}
@ -230,9 +230,17 @@ mod tests {
},
};
let actual = from_str::<Message>(
r#"{"message_id":1,"date":1,"chat":{"chat_id":1,"type":"private"},"forward_date":1,"forward_from":{"id":123,"is_bot":false,"first_name":"Name"},"text":"Message","entities":[]}"#,
r#"{"message_id":1,"date":1,"chat":{"id":1,"type":"private"},"forward_date":1,"forward_from":{"id":123,"is_bot":false,"first_name":"Name"},"text":"Message","entities":[]}"#,
)
.unwrap();
assert_eq!(expected, actual);
}
#[test]
fn sent_message_de() {
// actual message from telegram
let json = "{\"message_id\":6534,\"from\":{\"id\":457569668,\"is_bot\":true,\"first_name\":\"\\u0424\\u044b\\u0440\\u044c\\u043a\",\"username\":\"BloodyTestBot\"},\"chat\":{\"id\":218485655,\"first_name\":\"\\u0412\\u0430\\u0444\\u0435\\u043b\\u044c\",\"username\":\"WaffleLapkin\",\"type\":\"private\"},\"date\":1567898953,\"text\":\"text\"}";
let actual: Result<Message, _>= from_str(json);
assert!(actual.is_ok());
}
}

View file

@ -26,6 +26,7 @@ pub use self::{
successful_payment::SuccessfulPayment,
user::User,
video::Video,
response_parameters::ResponseParameters
};
mod answer_pre_checkout_query;
@ -55,3 +56,4 @@ mod sticker;
mod successful_payment;
mod user;
mod video;
mod response_parameters;

View file

@ -1,5 +1,4 @@
#[derive(Debug, Deserialize, Hash, PartialEq, Eq, Clone)]
#[serde(tag = "type")]
#[serde(rename_all = "snake_case")]
pub enum ResponseParameters {
MigrateToChatId(i64),
@ -8,23 +7,25 @@ pub enum ResponseParameters {
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn migrate_to_chat_id_deserialization() {
let expected_struct = ResponseParameters::MigrateToChatId(123456);
let actual_json: ResponseParameters = serde_json::from_str(
let expected = ResponseParameters::MigrateToChatId(123456);
let actual: ResponseParameters = serde_json::from_str(
r#"{"migrate_to_chat_id":123456}"#
).unwrap();
assert_eq!(expected_json, actual_json);
assert_eq!(expected, actual);
}
#[test]
fn retry_after_deserialization() {
let expected_struct = ResponseParameters::RetryAfter(123456);
let actual_json: ResponseParameters = serde_json::from_str(
let expected = ResponseParameters::RetryAfter(123456);
let actual: ResponseParameters = serde_json::from_str(
r#"{"retry_after":123456}"#
).unwrap();
assert_eq!(expected_json, actual_json);
assert_eq!(expected, actual);
}
}