From 06ba8ed413738417c9d394cad11b69603047377f Mon Sep 17 00:00:00 2001 From: Harry Bairstow Date: Thu, 20 Jul 2023 13:40:32 +0100 Subject: [PATCH 1/4] fix: ensure all data provided --- .env.example | 8 +++- Cargo.lock | 2 +- README.md | 6 +++ src/handlers/push_message.rs | 88 +++++++++++++++++++++--------------- src/lib.rs | 1 + 5 files changed, 67 insertions(+), 38 deletions(-) diff --git a/.env.example b/.env.example index e96afde2..5ab6e3b3 100644 --- a/.env.example +++ b/.env.example @@ -36,4 +36,10 @@ FCM_API_KEY= # APNS APNS_CERTIFICATE= # base64 encoded .p12 APNS Certificate APNS_CERTIFICATE_PASSWORD= # Password for provided certificate -APNS_TOPIC= # bundle ID/app ID \ No newline at end of file +APNS_TOPIC= # bundle ID/app ID + +# Analytics +ANALYTICS_S3_ENDPOINT= +ANALYTICS_EXPORT_BUCKET= +ANALYTICS_GEOIP_DB_BUCKET= +ANALYTICS_GEOIP_DB_KEY= diff --git a/Cargo.lock b/Cargo.lock index 4cea3076..f302c826 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1100,7 +1100,7 @@ checksum = "1aaf95b3e5c8f23aa320147307562d361db0ae0d51242340f558153b4eb2439b" [[package]] name = "echo-server" -version = "0.30.0" +version = "0.31.0" dependencies = [ "a2", "async-recursion", diff --git a/README.md b/README.md index 30afdfdc..da5977be 100644 --- a/README.md +++ b/README.md @@ -66,6 +66,12 @@ terraform -chdir=terraform apply -var-file="vars/$(terraform -chdir=terraform w There are two Dockerfiles, one `Dockerfile` is used in production by the hosted platform at WalletConnect while `slim.Dockerfile` is a stripped down version with no features enabled i.e. Single Tenant +## Analytics & Metrics +Echo Server is instrumented with analytics & metrics to help with debugging and monitoring. + +### Metrics + + ## Contact If you wish to integrate Push functionality into your Wallet (only available on v2), please contact us. diff --git a/src/handlers/push_message.rs b/src/handlers/push_message.rs index 68151324..8be71cea 100644 --- a/src/handlers/push_message.rs +++ b/src/handlers/push_message.rs @@ -4,7 +4,7 @@ use { blob::ENCRYPTED_FLAG, error::{ Error::{ClientNotFound, Store}, - Result, + }, handlers::DECENTRALIZED_IDENTIFIER_PREFIX, increment_counter, @@ -25,6 +25,7 @@ use { }; #[cfg(feature = "analytics")] use {axum::extract::ConnectInfo, std::net::SocketAddr}; +use crate::error::Error; #[derive(Serialize, Deserialize, Debug, Clone, Eq, PartialEq)] pub struct MessagePayload { @@ -51,7 +52,7 @@ pub async fn handler( StateExtractor(state): StateExtractor>, headers: HeaderMap, RequireValidSignature(Json(body)): RequireValidSignature>, -) -> Result { +) -> Result { let res = handler_internal( Path((tenant_id.clone(), id.clone())), StateExtractor(state.clone()), @@ -64,31 +65,19 @@ pub async fn handler( let (status, response, analytics_option) = match res { Ok((res, analytics_options_inner)) => (res.status().as_u16(), res, analytics_options_inner), - Err(error) => { + Err((error, analytics_option_inner)) => { #[cfg(feature = "analytics")] let error_str = format!("{:?}", &error); let res = error.into_response(); let status_code = res.status().clone().as_u16(); - #[cfg(feature = "analytics")] - let analytics_option = Some(MessageInfo { - msg_id: body.clone().id.into(), - region: None, - country: None, - continent: None, - project_id: tenant_id.clone().into(), - client_id: id.clone().into(), - topic: None, - push_provider: "unknown".into(), - encrypted: false, - flags: body.clone().payload.flags, - status: status_code, - response_message: Some(error_str.into()), - received_at: Default::default(), - }); - - #[cfg(not(feature = "analytics"))] - let analytics_option = None; + let mut analytics_option = None; + if let Some(analytics_unwrapped) = analytics_option_inner { + analytics_option = Some(MessageInfo { + response_message: Some(error_str.into()), + ..analytics_unwrapped + }); + } (status_code, res, analytics_option) } @@ -133,7 +122,7 @@ pub async fn handler_internal( StateExtractor(state): StateExtractor>, headers: HeaderMap, RequireValidSignature(Json(body)): RequireValidSignature>, -) -> Result<(axum::response::Response, Option)> { +) -> Result<(axum::response::Response, Option), (Error, Option)> { #[cfg(feature = "analytics")] let topic: Option> = body .payload @@ -149,10 +138,24 @@ pub async fn handler_internal( Ok(c) => Ok(c), Err(StoreError::NotFound(_, _)) => Err(ClientNotFound), Err(e) => Err(Store(e)), - }?; + }.map_err(|e| (e, Some(MessageInfo { + msg_id: body.id.clone().into(), + region: None, + country: None, + continent: None, + project_id: tenant_id.clone().into(), + client_id: id.clone().into(), + topic: topic.clone(), + push_provider: "unknown".into(), + encrypted, + flags, + status: 0, + response_message: None, + received_at: gorgon::time::now(), + })))?; #[cfg(feature = "analytics")] - let mut analytics = MessageInfo { + let mut analytics = Some(MessageInfo { msg_id: body.id.clone().into(), region: None, country: None, @@ -166,7 +169,10 @@ pub async fn handler_internal( status: 0, response_message: None, received_at: gorgon::time::now(), - }; + }); + + #[cfg(not(feature = "analytics"))] + let mut analytics = None; let request_id = get_req_id(&headers); @@ -199,20 +205,24 @@ pub async fn handler_internal( #[cfg(feature = "analytics")] { - analytics.response_message = Some("Notification has already been received".into()); + analytics = Some(MessageInfo { + response_message: Some("Notification has already been received".into()), + ..analytics.unwrap() + }); } #[cfg(not(feature = "analytics"))] return Ok(((StatusCode::OK).into_response(), None)); #[cfg(feature = "analytics")] - return Ok(((StatusCode::OK).into_response(), Some(analytics))); + return Ok(((StatusCode::OK).into_response(), analytics)); } let notification = state .notification_store .create_or_update_notification(&body.id, &tenant_id, &id, &body.payload) - .await?; + .await.map_err(|e| (Error::Store(e), analytics.clone()))?; + info!( %request_id, %tenant_id, @@ -235,17 +245,20 @@ pub async fn handler_internal( #[cfg(feature = "analytics")] { - analytics.response_message = Some("Notification has already been processed".into()); + analytics = Some(MessageInfo { + response_message: Some("Notification has already been processed".into()), + ..analytics.unwrap() + }); } #[cfg(not(feature = "analytics"))] return Ok(((StatusCode::OK).into_response(), None)); #[cfg(feature = "analytics")] - return Ok(((StatusCode::OK).into_response(), Some(analytics))); + return Ok(((StatusCode::OK).into_response(), analytics)); } - let tenant = state.tenant_store.get_tenant(&tenant_id).await?; + let tenant = state.tenant_store.get_tenant(&tenant_id).await.map_err(|e| (e, analytics.clone()))?; debug!( %request_id, %tenant_id, @@ -254,7 +267,7 @@ pub async fn handler_internal( "fetched tenant" ); - let mut provider = tenant.provider(&client.push_type)?; + let mut provider = tenant.provider(&client.push_type).map_err(|e| (e, analytics.clone()))?; debug!( %request_id, %tenant_id, @@ -266,7 +279,7 @@ pub async fn handler_internal( provider .send_notification(client.token, body.payload) - .await?; + .await.map_err(|e| (e, analytics.clone()))?; info!( %request_id, @@ -287,11 +300,14 @@ pub async fn handler_internal( #[cfg(feature = "analytics")] { - analytics.response_message = Some("Delivered".into()); + analytics = Some(MessageInfo { + response_message: Some("Delivered".into()), + ..analytics.unwrap() + }); } #[cfg(feature = "analytics")] - return Ok(((StatusCode::ACCEPTED).into_response(), Some(analytics))); + return Ok(((StatusCode::ACCEPTED).into_response(), analytics)); #[cfg(not(feature = "analytics"))] Ok(((StatusCode::ACCEPTED).into_response(), None)) diff --git a/src/lib.rs b/src/lib.rs index 16611266..af61099d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -36,6 +36,7 @@ use crate::stores::tenant::DefaultTenantStore; pub mod analytics; #[cfg(not(feature = "analytics"))] pub mod analytics { + #[derive(Debug, Clone, serde::Serialize)] pub mod message_info { pub struct MessageInfo; } From 0f9f3332aa3fb351df3da3bbda308f639d707352 Mon Sep 17 00:00:00 2001 From: Harry Bairstow Date: Thu, 20 Jul 2023 13:42:19 +0100 Subject: [PATCH 2/4] chore: fmt --- README.md | 6 ---- src/handlers/push_message.rs | 57 ++++++++++++++++++++++-------------- 2 files changed, 35 insertions(+), 28 deletions(-) diff --git a/README.md b/README.md index da5977be..30afdfdc 100644 --- a/README.md +++ b/README.md @@ -66,12 +66,6 @@ terraform -chdir=terraform apply -var-file="vars/$(terraform -chdir=terraform w There are two Dockerfiles, one `Dockerfile` is used in production by the hosted platform at WalletConnect while `slim.Dockerfile` is a stripped down version with no features enabled i.e. Single Tenant -## Analytics & Metrics -Echo Server is instrumented with analytics & metrics to help with debugging and monitoring. - -### Metrics - - ## Contact If you wish to integrate Push functionality into your Wallet (only available on v2), please contact us. diff --git a/src/handlers/push_message.rs b/src/handlers/push_message.rs index 8be71cea..fe2dac92 100644 --- a/src/handlers/push_message.rs +++ b/src/handlers/push_message.rs @@ -3,8 +3,8 @@ use { analytics::message_info::MessageInfo, blob::ENCRYPTED_FLAG, error::{ + Error, Error::{ClientNotFound, Store}, - }, handlers::DECENTRALIZED_IDENTIFIER_PREFIX, increment_counter, @@ -25,7 +25,6 @@ use { }; #[cfg(feature = "analytics")] use {axum::extract::ConnectInfo, std::net::SocketAddr}; -use crate::error::Error; #[derive(Serialize, Deserialize, Debug, Clone, Eq, PartialEq)] pub struct MessagePayload { @@ -138,24 +137,30 @@ pub async fn handler_internal( Ok(c) => Ok(c), Err(StoreError::NotFound(_, _)) => Err(ClientNotFound), Err(e) => Err(Store(e)), - }.map_err(|e| (e, Some(MessageInfo { - msg_id: body.id.clone().into(), - region: None, - country: None, - continent: None, - project_id: tenant_id.clone().into(), - client_id: id.clone().into(), - topic: topic.clone(), - push_provider: "unknown".into(), - encrypted, - flags, - status: 0, - response_message: None, - received_at: gorgon::time::now(), - })))?; + } + .map_err(|e| { + ( + e, + Some(MessageInfo { + msg_id: body.id.clone().into(), + region: None, + country: None, + continent: None, + project_id: tenant_id.clone().into(), + client_id: id.clone().into(), + topic: topic.clone(), + push_provider: "unknown".into(), + encrypted, + flags, + status: 0, + response_message: None, + received_at: gorgon::time::now(), + }), + ) + })?; #[cfg(feature = "analytics")] - let mut analytics = Some(MessageInfo { + let mut analytics = Some(MessageInfo { msg_id: body.id.clone().into(), region: None, country: None, @@ -221,7 +226,8 @@ pub async fn handler_internal( let notification = state .notification_store .create_or_update_notification(&body.id, &tenant_id, &id, &body.payload) - .await.map_err(|e| (Error::Store(e), analytics.clone()))?; + .await + .map_err(|e| (Error::Store(e), analytics.clone()))?; info!( %request_id, @@ -258,7 +264,11 @@ pub async fn handler_internal( return Ok(((StatusCode::OK).into_response(), analytics)); } - let tenant = state.tenant_store.get_tenant(&tenant_id).await.map_err(|e| (e, analytics.clone()))?; + let tenant = state + .tenant_store + .get_tenant(&tenant_id) + .await + .map_err(|e| (e, analytics.clone()))?; debug!( %request_id, %tenant_id, @@ -267,7 +277,9 @@ pub async fn handler_internal( "fetched tenant" ); - let mut provider = tenant.provider(&client.push_type).map_err(|e| (e, analytics.clone()))?; + let mut provider = tenant + .provider(&client.push_type) + .map_err(|e| (e, analytics.clone()))?; debug!( %request_id, %tenant_id, @@ -279,7 +291,8 @@ pub async fn handler_internal( provider .send_notification(client.token, body.payload) - .await.map_err(|e| (e, analytics.clone()))?; + .await + .map_err(|e| (e, analytics.clone()))?; info!( %request_id, From 17594d57daa651c76ee072dca4d2302d0435cd00 Mon Sep 17 00:00:00 2001 From: Harry Bairstow Date: Thu, 20 Jul 2023 14:20:24 +0100 Subject: [PATCH 3/4] fix: clippy --- src/handlers/push_message.rs | 6 +++++- src/lib.rs | 2 +- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/handlers/push_message.rs b/src/handlers/push_message.rs index fe2dac92..86393fdd 100644 --- a/src/handlers/push_message.rs +++ b/src/handlers/push_message.rs @@ -73,6 +73,7 @@ pub async fn handler( let mut analytics_option = None; if let Some(analytics_unwrapped) = analytics_option_inner { analytics_option = Some(MessageInfo { + #[cfg(feature = "analytics")] response_message: Some(error_str.into()), ..analytics_unwrapped }); @@ -141,6 +142,7 @@ pub async fn handler_internal( .map_err(|e| { ( e, + #[cfg(feature = "analytics")] Some(MessageInfo { msg_id: body.id.clone().into(), region: None, @@ -156,6 +158,8 @@ pub async fn handler_internal( response_message: None, received_at: gorgon::time::now(), }), + #[cfg(not(feature = "analytics"))] + None, ) })?; @@ -177,7 +181,7 @@ pub async fn handler_internal( }); #[cfg(not(feature = "analytics"))] - let mut analytics = None; + let analytics = None; let request_id = get_req_id(&headers); diff --git a/src/lib.rs b/src/lib.rs index af61099d..06567597 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -36,8 +36,8 @@ use crate::stores::tenant::DefaultTenantStore; pub mod analytics; #[cfg(not(feature = "analytics"))] pub mod analytics { - #[derive(Debug, Clone, serde::Serialize)] pub mod message_info { + #[derive(Debug, Clone, serde::Serialize)] pub struct MessageInfo; } } From 74a53c4cfa48b9cdc3d34a92c49ff1cbe62454d4 Mon Sep 17 00:00:00 2001 From: Harry Bairstow Date: Thu, 20 Jul 2023 14:26:51 +0100 Subject: [PATCH 4/4] chore: clippy --- src/handlers/push_message.rs | 17 ++++++++++++----- src/handlers/single_tenant_wrappers.rs | 2 +- 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/src/handlers/push_message.rs b/src/handlers/push_message.rs index 86393fdd..f4f320cd 100644 --- a/src/handlers/push_message.rs +++ b/src/handlers/push_message.rs @@ -72,11 +72,18 @@ pub async fn handler( let mut analytics_option = None; if let Some(analytics_unwrapped) = analytics_option_inner { - analytics_option = Some(MessageInfo { - #[cfg(feature = "analytics")] - response_message: Some(error_str.into()), - ..analytics_unwrapped - }); + #[cfg(feature = "analytics")] + { + analytics_option = Some(MessageInfo { + response_message: Some(error_str.into()), + ..analytics_unwrapped + }); + } + + #[cfg(not(feature = "analytics"))] + { + analytics_option = Some(analytics_unwrapped); + } } (status_code, res, analytics_option) diff --git a/src/handlers/single_tenant_wrappers.rs b/src/handlers/single_tenant_wrappers.rs index 1c3e2c3a..8d49388f 100644 --- a/src/handlers/single_tenant_wrappers.rs +++ b/src/handlers/single_tenant_wrappers.rs @@ -27,7 +27,7 @@ pub async fn delete_handler( #[cfg(feature = "multitenant")] return Err(MissingTenantId); - #[cfg(all(not(feature = "multitenant")))] + #[cfg(not(feature = "multitenant"))] crate::handlers::delete_client::handler( Path((DEFAULT_TENANT_ID.to_string(), id)), state,