mirror of
https://github.com/chatmail/core.git
synced 2026-10-02 03:00:40 +03:00
feat: client version information
This commit is contained in:
@@ -64,6 +64,7 @@ use self::types::{
|
||||
JsonrpcMessageListItem, MessageNotificationInfo, MessageSearchResult, MessageViewtype,
|
||||
},
|
||||
};
|
||||
use crate::api::types::appversions::JsonrpcAppVersionInfo;
|
||||
use crate::api::types::chat_list::{ChatListItemFetchResult, get_chat_list_item_by_id};
|
||||
use crate::api::types::login_param::TransportListEntry;
|
||||
use crate::api::types::qr::{QrObject, SecurejoinSource, SecurejoinUiPath};
|
||||
@@ -2789,6 +2790,13 @@ impl CommandApi {
|
||||
Err(anyhow!("chat with id {chat_id} doesn't have draft message"))
|
||||
}
|
||||
}
|
||||
|
||||
/// Get version information of clients.
|
||||
async fn get_app_versions(&self, account_id: u32) -> Result<JsonrpcAppVersionInfo> {
|
||||
let ctx = self.get_context(account_id).await?;
|
||||
let info = deltachat::appversions::get_app_versions(&ctx).await?;
|
||||
JsonrpcAppVersionInfo::from_core_type(info)
|
||||
}
|
||||
}
|
||||
|
||||
// Helper functions (to prevent code duplication)
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
use anyhow::Result;
|
||||
use deltachat::appversions::AppVersionInfo;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use typescript_type_def::TypeDef;
|
||||
|
||||
/// Version information of clients.
|
||||
#[derive(Serialize, Deserialize, TypeDef, schemars::JsonSchema)]
|
||||
#[serde(rename = "AppVersionInfo", rename_all = "camelCase")]
|
||||
pub struct JsonrpcAppVersionInfo {
|
||||
/// Array of clients with version information.
|
||||
pub clients: Vec<JsonrpcAppClient>,
|
||||
}
|
||||
|
||||
/// Version information of a single client, eg. "deltachat" or "ubuntutouch".
|
||||
#[derive(Serialize, Deserialize, TypeDef, schemars::JsonSchema)]
|
||||
#[serde(rename = "AppClient", rename_all = "camelCase")]
|
||||
pub struct JsonrpcAppClient {
|
||||
/// ID how the client identifies itself, eg. "deltachat" or "ubuntutouch".
|
||||
pub client_id: String,
|
||||
|
||||
/// Array of sources for that client.
|
||||
pub sources: Vec<JsonrpcAppSource>,
|
||||
}
|
||||
|
||||
/// Version information of a single source of a client, eg. "gplay" or "fdroid".
|
||||
#[derive(Serialize, Deserialize, TypeDef, schemars::JsonSchema)]
|
||||
#[serde(rename = "AppSource", rename_all = "camelCase")]
|
||||
pub struct JsonrpcAppSource {
|
||||
/// ID how the client identifies a source, eg. "gplay" or "fdroid".
|
||||
pub app_id: String,
|
||||
|
||||
/// Always increasing version number.
|
||||
pub version_integer: u32,
|
||||
|
||||
/// Any version string.
|
||||
pub version_string: String,
|
||||
|
||||
/// Where to download that version.
|
||||
pub download_url: String,
|
||||
}
|
||||
|
||||
impl JsonrpcAppVersionInfo {
|
||||
pub fn from_core_type(info: AppVersionInfo) -> Result<Self> {
|
||||
let value = serde_json::to_value(info)?;
|
||||
Ok(serde_json::from_value(value)?)
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
pub mod account;
|
||||
pub mod appversions;
|
||||
pub mod calls;
|
||||
pub mod chat;
|
||||
pub mod chat_list;
|
||||
|
||||
@@ -0,0 +1,134 @@
|
||||
//! Get version information of clients.
|
||||
//!
|
||||
//! Used by clients to inform about updates.
|
||||
//! The version information comes in via IMAP METADATA,
|
||||
//! (as JSON) and is parsed to `AppVersionInfo`.
|
||||
use crate::context::Context;
|
||||
use anyhow::Result;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
/// Version information of clients as used on the wire.
|
||||
#[derive(Debug, Default, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase", default)]
|
||||
pub struct AppVersionInfo {
|
||||
/// Array clients with version information.
|
||||
clients: Vec<AppClient>,
|
||||
}
|
||||
|
||||
/// Version infomation of a single client, eg. "deltachat" or "ubuntutouch".
|
||||
#[derive(Debug, Default, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase", default)]
|
||||
pub struct AppClient {
|
||||
/// ID how the client identifies itself, eg. "deltachat" or "ubuntutouch"
|
||||
client_id: String,
|
||||
|
||||
/// Array of sources for that client.
|
||||
sources: Vec<AppSource>,
|
||||
}
|
||||
|
||||
/// Version information of a single source of a client, eg. "gplay" or "fdroid".
|
||||
#[derive(Debug, Default, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase", default)]
|
||||
pub struct AppSource {
|
||||
/// ID how the client identifies a source, eg. "gplay" or "fdroid"
|
||||
app_id: String,
|
||||
|
||||
/// Always increasing version number.
|
||||
version_integer: u32,
|
||||
|
||||
/// Any version string.
|
||||
version_string: String,
|
||||
|
||||
/// Where to download that version.
|
||||
download_url: String,
|
||||
}
|
||||
|
||||
/// Get version information of clients.
|
||||
///
|
||||
/// If no version information are available, `clients` is set to an empty array.
|
||||
///
|
||||
/// The information is coming from the relay via IMAP METADATA and is not cached.
|
||||
/// A call to `get_app_versions()` is cheap and does not involve network or database calls.
|
||||
pub async fn get_app_versions(context: &Context) -> Result<AppVersionInfo> {
|
||||
if let Some(metadata) = context.metadata.read().await.values().next()
|
||||
&& let Some(json) = &metadata.app_versions
|
||||
{
|
||||
let app_versions: AppVersionInfo = serde_json::from_str(json)?;
|
||||
return Ok(app_versions);
|
||||
}
|
||||
Ok(AppVersionInfo::default())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::test_utils::TestContextManager;
|
||||
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_app_version_info_deserialize() -> Result<()> {
|
||||
let json = r##"{
|
||||
"clients": [
|
||||
{
|
||||
"clientId": "deltachat",
|
||||
"sources": [
|
||||
{
|
||||
"appId": "gplay",
|
||||
"versionInteger": 754,
|
||||
"versionString": "2.57.0",
|
||||
"downloadUrl": "https://github.com/deltachat/deltachat-android/releases/download/v2.57.0/deltachat-gplay-release-2.57.0.apk"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}"##;
|
||||
let versions: AppVersionInfo = serde_json::from_str(json)?;
|
||||
assert_eq!(versions.clients.len(), 1);
|
||||
assert_eq!(versions.clients[0].client_id, "deltachat");
|
||||
assert_eq!(versions.clients[0].sources.len(), 1);
|
||||
assert_eq!(versions.clients[0].sources[0].app_id, "gplay");
|
||||
assert_eq!(versions.clients[0].sources[0].version_integer, 754);
|
||||
assert_eq!(versions.clients[0].sources[0].version_string, "2.57.0");
|
||||
assert_eq!(
|
||||
versions.clients[0].sources[0].download_url,
|
||||
"https://github.com/deltachat/deltachat-android/releases/download/v2.57.0/deltachat-gplay-release-2.57.0.apk"
|
||||
);
|
||||
|
||||
// missing fields are set to defaults, additional fields are ignored, errors are errors
|
||||
let json = r##"{
|
||||
"clients": [
|
||||
{
|
||||
"clientId": "deltachat",
|
||||
"xsource": "bang"
|
||||
}
|
||||
],
|
||||
"foo": "bar"
|
||||
}"##;
|
||||
let versions: AppVersionInfo = serde_json::from_str(json)?;
|
||||
assert_eq!(versions.clients.len(), 1);
|
||||
assert_eq!(versions.clients[0].client_id, "deltachat");
|
||||
assert_eq!(versions.clients[0].sources.len(), 0);
|
||||
|
||||
let json = "{}";
|
||||
let versions: AppVersionInfo = serde_json::from_str(json)?;
|
||||
assert_eq!(versions.clients.len(), 0);
|
||||
|
||||
let json = "";
|
||||
assert!(serde_json::from_str::<AppVersionInfo>(json).is_err());
|
||||
|
||||
let json = "bad json";
|
||||
assert!(serde_json::from_str::<AppVersionInfo>(json).is_err());
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_get_app_versions() -> Result<()> {
|
||||
let mut tcm = TestContextManager::new();
|
||||
let alice = &tcm.alice().await;
|
||||
|
||||
let versions = get_app_versions(alice).await?;
|
||||
assert!(versions.clients.is_empty());
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -140,6 +140,10 @@ pub(crate) struct ServerMetadata {
|
||||
/// should be fetched from the server
|
||||
/// to be ready for WebRTC calls.
|
||||
pub ice_servers_expiration_timestamp: i64,
|
||||
|
||||
/// App versions, as raw JSON string.
|
||||
/// Consumed by get_app_versions().
|
||||
pub app_versions: Option<String>,
|
||||
}
|
||||
|
||||
struct UidGrouper<T: Iterator<Item = (i64, u32, String)>> {
|
||||
@@ -1342,6 +1346,7 @@ impl Session {
|
||||
let mut max_smtp_rcpt_to = None;
|
||||
let mut ice_servers = None;
|
||||
let mut ice_servers_expiration_timestamp = 0;
|
||||
let mut app_versions = None;
|
||||
|
||||
let mailbox = "";
|
||||
let options = "";
|
||||
@@ -1397,6 +1402,9 @@ impl Session {
|
||||
}
|
||||
}
|
||||
}
|
||||
"/shared/vendor/deltachat/appversions" => {
|
||||
app_versions = m.value;
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
@@ -1418,6 +1426,7 @@ impl Session {
|
||||
supports_push: max_smtp_rcpt_to.is_some() || self.capabilities.has_xdeltapush,
|
||||
ice_servers,
|
||||
ice_servers_expiration_timestamp,
|
||||
app_versions,
|
||||
},
|
||||
);
|
||||
Ok(())
|
||||
|
||||
@@ -54,6 +54,7 @@ pub(crate) mod events;
|
||||
pub use events::*;
|
||||
|
||||
mod aheader;
|
||||
pub mod appversions;
|
||||
mod automatic_relay_management;
|
||||
pub mod blob;
|
||||
pub mod calls;
|
||||
|
||||
Reference in New Issue
Block a user