From 9b457564bbba67fb1978e1436b405d1db04b8012 Mon Sep 17 00:00:00 2001 From: trivernis Date: Tue, 2 Nov 2021 19:38:48 +0100 Subject: [PATCH] Add API version validation Signed-off-by: trivernis --- mediarepo-api/src/client_api/error.rs | 7 +++- mediarepo-api/src/client_api/mod.rs | 13 +++++-- mediarepo-api/src/tauri_plugin/error.rs | 1 + mediarepo-api/src/types/misc.rs | 52 ++++++++++++++++++++++++- 4 files changed, 66 insertions(+), 7 deletions(-) diff --git a/mediarepo-api/src/client_api/error.rs b/mediarepo-api/src/client_api/error.rs index bd8f8df..0e88754 100644 --- a/mediarepo-api/src/client_api/error.rs +++ b/mediarepo-api/src/client_api/error.rs @@ -5,5 +5,8 @@ pub type ApiResult = Result; #[derive(Debug, Error)] pub enum ApiError { #[error(transparent)] - IPC(#[from] rmp_ipc::error::Error) -} \ No newline at end of file + IPC(#[from] rmp_ipc::error::Error), + + #[error("The servers api version is incompatible with the api client")] + VersionMismatch, +} diff --git a/mediarepo-api/src/client_api/mod.rs b/mediarepo-api/src/client_api/mod.rs index 3f27ac5..fb60401 100644 --- a/mediarepo-api/src/client_api/mod.rs +++ b/mediarepo-api/src/client_api/mod.rs @@ -2,10 +2,10 @@ pub mod error; pub mod file; pub mod tag; -use crate::client_api::error::ApiResult; +use crate::client_api::error::{ApiError, ApiResult}; use crate::client_api::file::FileApi; use crate::client_api::tag::TagApi; -use crate::types::misc::InfoResponse; +use crate::types::misc::{check_apis_compatible, get_api_version, InfoResponse}; use async_trait::async_trait; use rmp_ipc::context::{PoolGuard, PooledContext}; use rmp_ipc::ipc::context::Context; @@ -72,8 +72,15 @@ impl ApiClient { .address(address) .build_pooled_client(8) .await?; + let client = Self::new(ctx); + let info = client.info().await?; + let server_api_version = info.api_version(); - Ok(Self::new(ctx)) + if !check_apis_compatible(get_api_version(), server_api_version) { + Err(ApiError::VersionMismatch) + } else { + Ok(client) + } } /// Returns information about the connected ipc server diff --git a/mediarepo-api/src/tauri_plugin/error.rs b/mediarepo-api/src/tauri_plugin/error.rs index 5abf4e3..1480239 100644 --- a/mediarepo-api/src/tauri_plugin/error.rs +++ b/mediarepo-api/src/tauri_plugin/error.rs @@ -40,6 +40,7 @@ impl From for PluginError { format!("{:?}", e) } }, + ApiError::VersionMismatch => {String::from("The servers API version is not supported by the client. Please make sure both are up to date.")} }; Self { message } } diff --git a/mediarepo-api/src/types/misc.rs b/mediarepo-api/src/types/misc.rs index 137524f..73ca70d 100644 --- a/mediarepo-api/src/types/misc.rs +++ b/mediarepo-api/src/types/misc.rs @@ -1,7 +1,55 @@ -use serde::{Serialize, Deserialize}; +use serde::{Deserialize, Serialize}; #[derive(Clone, Debug, Serialize, Deserialize)] pub struct InfoResponse { pub name: String, pub version: String, -} \ No newline at end of file + pub(crate) api_version: (u32, u32, u32), +} + +impl InfoResponse { + /// Creates a new info response + pub fn new(name: String, version: String) -> Self { + Self { + name, + version, + api_version: get_api_version(), + } + } + + /// Returns the api version of the crate + pub fn api_version(&self) -> (u32, u32, u32) { + self.api_version + } +} + +/// Retrieves the api version of the crate version in numbers +pub fn get_api_version() -> (u32, u32, u32) { + let mut major = env!("CARGO_PKG_VERSION_MAJOR").to_string(); + let mut minor = env!("CARGO_PKG_VERSION_MINOR").to_string(); + let mut patch = env!("CARGO_PKG_VERSION_PATCH").to_string(); + major.retain(char::is_numeric); + minor.retain(char::is_numeric); + patch.retain(char::is_numeric); + let major = major + .parse::() + .expect("Failed to parse major crate version"); + let minor = minor + .parse::() + .expect("Failed to parse minor crate version"); + let patch = patch + .parse::() + .expect("Failed to parse patch crate version"); + + (major, minor, patch) +} + +/// Checks if the api the client consumes is compatible to the one the server provides +pub fn check_apis_compatible( + client_version: (u32, u32, u32), + server_version: (u32, u32, u32), +) -> bool { + // the major version must be the same while the client minor version can be lower than the servers + // so that the client has access to all its supported functionality + client_version.0 == server_version.0 && client_version.1 <= server_version.1 +}