diff --git a/services/identity/src/grpc_services/shared.rs b/services/identity/src/grpc_services/shared.rs index 2138857dc..5b0faf061 100644 --- a/services/identity/src/grpc_services/shared.rs +++ b/services/identity/src/grpc_services/shared.rs @@ -1,65 +1,59 @@ use grpc_clients::error::unsupported_version; use tonic::{Request, Status}; use tracing::trace; use crate::constants::{request_metadata, MIN_SUPPORTED_NATIVE_VERSION}; -#[derive(Clone, Debug)] -pub struct PlatformMetadata { - pub device_type: String, - pub code_version: u64, - pub state_version: Option, - pub major_desktop_version: Option, -} +pub use grpc_clients::identity::shared::PlatformMetadata; pub fn version_interceptor(req: Request<()>) -> Result, Status> { trace!("Intercepting request to check version: {:?}", req); match get_version_info(&req) { Some((version, platform)) if (platform == "ios" || platform == "android") && version < MIN_SUPPORTED_NATIVE_VERSION => { Err(unsupported_version()) } _ => Ok(req), } } fn get_version_info( req: &Request, ) -> Option<(u64, String)> { trace!("Retrieving version info for request: {:?}", req); let code_version: u64 = get_value(req, request_metadata::CODE_VERSION)? .parse() .ok()?; let device_type = get_value(req, request_metadata::DEVICE_TYPE)?; Some((code_version, device_type)) } pub fn get_platform_metadata( req: &Request, ) -> Result { let (code_version, device_type) = get_version_info(req).ok_or_else(|| { Status::invalid_argument("missing platform or code version metadata") })?; let state_version = get_value(req, request_metadata::STATE_VERSION) .and_then(|it| it.parse().ok()); let major_desktop_version = get_value(req, request_metadata::MAJOR_DESKTOP_VERSION) .and_then(|it| it.parse().ok()); Ok(PlatformMetadata { code_version, device_type, state_version, major_desktop_version, }) } pub fn get_value(req: &Request, key: &str) -> Option { let raw_value = req.metadata().get(key)?; raw_value.to_str().ok().map(|s| s.to_string()) } diff --git a/shared/grpc_clients/src/identity/shared.rs b/shared/grpc_clients/src/identity/shared.rs index 242b4a4c1..a86a45cd2 100644 --- a/shared/grpc_clients/src/identity/shared.rs +++ b/shared/grpc_clients/src/identity/shared.rs @@ -1,67 +1,98 @@ use tonic::{ metadata::{errors::InvalidMetadataValue, Ascii, MetadataValue}, service::Interceptor, Request, Status, }; +#[derive(Clone, Debug)] +pub struct PlatformMetadata { + pub device_type: String, + pub code_version: u64, + pub state_version: Option, + pub major_desktop_version: Option, +} + pub struct CodeVersionLayer { pub(crate) code_version: u64, pub(crate) device_type: String, pub(crate) state_version: Option, pub(crate) major_desktop_version: Option, } +impl PlatformMetadata { + /// Simplified constructor for basic params only + fn new(code_version: u64, device_type: impl Into) -> Self { + Self { + code_version, + device_type: device_type.into(), + state_version: None, + major_desktop_version: None, + } + } +} + +impl From for CodeVersionLayer { + fn from(value: PlatformMetadata) -> Self { + Self { + code_version: value.code_version, + device_type: value.device_type, + state_version: value.state_version, + major_desktop_version: value.major_desktop_version, + } + } +} + impl Interceptor for CodeVersionLayer { fn call(&mut self, mut request: Request<()>) -> Result, Status> { let metadata = request.metadata_mut(); metadata.insert("code_version", self.code_version.parse_to_ascii()?); metadata.insert("device_type", self.device_type.parse_to_ascii()?); if let Some(state_version) = self.state_version { metadata.insert("state_version", state_version.parse_to_ascii()?); } if let Some(desktop_version) = self.major_desktop_version { metadata .insert("major_desktop_version", desktop_version.parse_to_ascii()?); } Ok(request) } } pub trait ToMetadataValueAscii { fn parse_to_ascii(&self) -> Result, Status>; } impl ToMetadataValueAscii for u64 { fn parse_to_ascii(&self) -> Result, Status> { let ascii_string = self.to_string(); ascii_string.parse().map_err(|e: InvalidMetadataValue| { Status::invalid_argument(format!( "Non-Ascii character present in metadata value: {}", e )) }) } } pub struct ChainedInterceptor where A: Interceptor + Send + Sync + 'static, B: Interceptor + Send + Sync + 'static, { pub(crate) first: A, pub(crate) second: B, } impl Interceptor for ChainedInterceptor where A: Interceptor + Send + Sync + 'static, B: Interceptor + Send + Sync + 'static, { fn call(&mut self, request: Request<()>) -> Result, Status> { let request = self.first.call(request)?; self.second.call(request) } }