From b54603c57b35e5af478c8607d0881dfe88d35082 Mon Sep 17 00:00:00 2001 From: Sahkal Poddar Date: Thu, 11 Jan 2024 17:00:21 +0530 Subject: [PATCH] refactor(router): flagged order_details validation to skip validation (#3324) Co-authored-by: Chethan Rao <70657455+Chethan-rao@users.noreply.github.com> --- crates/common_utils/src/request.rs | 3 +- crates/kgraph_utils/src/mca.rs | 2 +- crates/masking/src/diesel.rs | 1 - crates/masking/src/serde.rs | 2 +- crates/redis_interface/src/lib.rs | 2 +- crates/router/src/configs/settings.rs | 2 +- .../braintree_graphql_transformers.rs | 4 +- .../src/connector/checkout/transformers.rs | 5 +- .../src/connector/stripe/transformers.rs | 2 +- crates/router/src/connector/utils.rs | 160 +++++++++--------- crates/router/src/connector/wise.rs | 4 +- crates/router/src/core/fraud_check.rs | 10 +- crates/router/src/core/payments.rs | 5 +- crates/router/src/core/payments/helpers.rs | 23 ++- .../payments/operations/payment_confirm.rs | 1 + .../payments/operations/payment_create.rs | 1 + .../payments/operations/payment_update.rs | 1 + .../router/src/core/payments/transformers.rs | 2 +- crates/router/src/db/dispute.rs | 4 +- crates/router/src/macros.rs | 5 +- crates/router/src/services/api.rs | 28 +-- .../types/api/connector_onboarding/paypal.rs | 2 +- crates/router/src/types/storage.rs | 1 - crates/router/src/types/storage/query.rs | 1 - crates/router/src/utils.rs | 20 +-- crates/router/src/utils/user/sample_data.rs | 4 +- crates/router/tests/connectors/utils.rs | 2 +- crates/scheduler/src/consumer.rs | 6 + crates/scheduler/src/producer.rs | 6 + 29 files changed, 158 insertions(+), 151 deletions(-) delete mode 100644 crates/router/src/types/storage/query.rs diff --git a/crates/common_utils/src/request.rs b/crates/common_utils/src/request.rs index e3aecb4b5695..f063a1940bef 100644 --- a/crates/common_utils/src/request.rs +++ b/crates/common_utils/src/request.rs @@ -138,8 +138,7 @@ impl RequestBuilder { } pub fn headers(mut self, headers: Vec<(String, Maskable)>) -> Self { - let mut h = headers.into_iter().map(|(h, v)| (h, v)); - self.headers.extend(&mut h); + self.headers.extend(headers); self } diff --git a/crates/kgraph_utils/src/mca.rs b/crates/kgraph_utils/src/mca.rs index 0e224a8f3d9d..a04e052514d6 100644 --- a/crates/kgraph_utils/src/mca.rs +++ b/crates/kgraph_utils/src/mca.rs @@ -156,7 +156,7 @@ fn compile_request_pm_types( let or_node_neighbor_id = if amount_nodes.len() == 1 { amount_nodes - .get(0) + .first() .copied() .ok_or(KgraphError::IndexingError)? } else { diff --git a/crates/masking/src/diesel.rs b/crates/masking/src/diesel.rs index 307f083d27fb..f3576298bdb1 100644 --- a/crates/masking/src/diesel.rs +++ b/crates/masking/src/diesel.rs @@ -2,7 +2,6 @@ //! Diesel-related. //! -pub use diesel::Expression; use diesel::{ backend::Backend, deserialize::{self, FromSql, Queryable}, diff --git a/crates/masking/src/serde.rs b/crates/masking/src/serde.rs index 944392e693ff..bb81717fd670 100644 --- a/crates/masking/src/serde.rs +++ b/crates/masking/src/serde.rs @@ -3,7 +3,7 @@ //! pub use erased_serde::Serialize as ErasedSerialize; -pub use serde::{de, ser, Deserialize, Serialize, Serializer}; +pub use serde::{de, Deserialize, Serialize, Serializer}; use serde_json::{value::Serializer as JsonValueSerializer, Value}; use crate::{Secret, Strategy, StrongSecret, ZeroizableSecret}; diff --git a/crates/redis_interface/src/lib.rs b/crates/redis_interface/src/lib.rs index 7111869a5c03..33d40ebe155d 100644 --- a/crates/redis_interface/src/lib.rs +++ b/crates/redis_interface/src/lib.rs @@ -28,7 +28,7 @@ pub use fred::interfaces::PubsubInterface; use fred::{interfaces::ClientLike, prelude::EventInterface}; use router_env::logger; -pub use self::{commands::*, types::*}; +pub use self::types::*; pub struct RedisConnectionPool { pub pool: fred::prelude::RedisPool, diff --git a/crates/router/src/configs/settings.rs b/crates/router/src/configs/settings.rs index b62831950856..db59d7f29148 100644 --- a/crates/router/src/configs/settings.rs +++ b/crates/router/src/configs/settings.rs @@ -556,7 +556,7 @@ impl From for storage_impl::config::Database { dbname: val.dbname, pool_size: val.pool_size, connection_timeout: val.connection_timeout, - queue_strategy: val.queue_strategy.into(), + queue_strategy: val.queue_strategy, min_idle: val.min_idle, max_lifetime: val.max_lifetime, } diff --git a/crates/router/src/connector/braintree/braintree_graphql_transformers.rs b/crates/router/src/connector/braintree/braintree_graphql_transformers.rs index 9bdbd4392f7c..e0baf034f72a 100644 --- a/crates/router/src/connector/braintree/braintree_graphql_transformers.rs +++ b/crates/router/src/connector/braintree/braintree_graphql_transformers.rs @@ -297,11 +297,11 @@ fn build_error_response( get_error_response( response - .get(0) + .first() .and_then(|err_details| err_details.extensions.as_ref()) .and_then(|extensions| extensions.legacy_code.clone()), response - .get(0) + .first() .map(|err_details| err_details.message.clone()), reason, http_code, diff --git a/crates/router/src/connector/checkout/transformers.rs b/crates/router/src/connector/checkout/transformers.rs index 5bd80a10c4b5..6e7656c38a6f 100644 --- a/crates/router/src/connector/checkout/transformers.rs +++ b/crates/router/src/connector/checkout/transformers.rs @@ -1026,10 +1026,7 @@ impl utils::MultipleCaptureSyncResponse for Box { self.status == CheckoutPaymentStatus::Captured } fn get_amount_captured(&self) -> Option { - match self.amount { - Some(amount) => amount.try_into().ok(), - None => None, - } + self.amount.map(Into::into) } } diff --git a/crates/router/src/connector/stripe/transformers.rs b/crates/router/src/connector/stripe/transformers.rs index 3a28f777907f..e273093ba061 100644 --- a/crates/router/src/connector/stripe/transformers.rs +++ b/crates/router/src/connector/stripe/transformers.rs @@ -2336,7 +2336,7 @@ pub fn get_connector_metadata( let next_action_response = next_action .and_then(|next_action_response| match next_action_response { StripeNextActionResponse::DisplayBankTransferInstructions(response) => { - let bank_instructions = response.financial_addresses.get(0); + let bank_instructions = response.financial_addresses.first(); let (sepa_bank_instructions, bacs_bank_instructions) = bank_instructions.map_or((None, None), |financial_address| { ( diff --git a/crates/router/src/connector/utils.rs b/crates/router/src/connector/utils.rs index 9a538a7207e9..b4484557f32a 100644 --- a/crates/router/src/connector/utils.rs +++ b/crates/router/src/connector/utils.rs @@ -1508,86 +1508,6 @@ pub fn get_error_code_error_message_based_on_priority( .cloned() } -#[cfg(test)] -mod error_code_error_message_tests { - #![allow(clippy::unwrap_used)] - use super::*; - - struct TestConnector; - - impl ConnectorErrorTypeMapping for TestConnector { - fn get_connector_error_type( - &self, - error_code: String, - error_message: String, - ) -> ConnectorErrorType { - match (error_code.as_str(), error_message.as_str()) { - ("01", "INVALID_MERCHANT") => ConnectorErrorType::BusinessError, - ("03", "INVALID_CVV") => ConnectorErrorType::UserError, - ("04", "04") => ConnectorErrorType::TechnicalError, - _ => ConnectorErrorType::UnknownError, - } - } - } - - #[test] - fn test_get_error_code_error_message_based_on_priority() { - let error_code_message_list_unknown = vec![ - ErrorCodeAndMessage { - error_code: "01".to_string(), - error_message: "INVALID_MERCHANT".to_string(), - }, - ErrorCodeAndMessage { - error_code: "05".to_string(), - error_message: "05".to_string(), - }, - ErrorCodeAndMessage { - error_code: "03".to_string(), - error_message: "INVALID_CVV".to_string(), - }, - ErrorCodeAndMessage { - error_code: "04".to_string(), - error_message: "04".to_string(), - }, - ]; - let error_code_message_list_user = vec![ - ErrorCodeAndMessage { - error_code: "01".to_string(), - error_message: "INVALID_MERCHANT".to_string(), - }, - ErrorCodeAndMessage { - error_code: "03".to_string(), - error_message: "INVALID_CVV".to_string(), - }, - ]; - let error_code_error_message_unknown = get_error_code_error_message_based_on_priority( - TestConnector, - error_code_message_list_unknown, - ); - let error_code_error_message_user = get_error_code_error_message_based_on_priority( - TestConnector, - error_code_message_list_user, - ); - let error_code_error_message_none = - get_error_code_error_message_based_on_priority(TestConnector, vec![]); - assert_eq!( - error_code_error_message_unknown, - Some(ErrorCodeAndMessage { - error_code: "05".to_string(), - error_message: "05".to_string(), - }) - ); - assert_eq!( - error_code_error_message_user, - Some(ErrorCodeAndMessage { - error_code: "03".to_string(), - error_message: "INVALID_CVV".to_string(), - }) - ); - assert_eq!(error_code_error_message_none, None); - } -} - pub trait MultipleCaptureSyncResponse { fn get_connector_capture_id(&self) -> String; fn get_capture_attempt_status(&self) -> enums::AttemptStatus; @@ -1781,3 +1701,83 @@ impl FrmTransactionRouterDataRequest for fraud_check::FrmTransactionRouterData { } } } + +#[cfg(test)] +mod error_code_error_message_tests { + #![allow(clippy::unwrap_used)] + use super::*; + + struct TestConnector; + + impl ConnectorErrorTypeMapping for TestConnector { + fn get_connector_error_type( + &self, + error_code: String, + error_message: String, + ) -> ConnectorErrorType { + match (error_code.as_str(), error_message.as_str()) { + ("01", "INVALID_MERCHANT") => ConnectorErrorType::BusinessError, + ("03", "INVALID_CVV") => ConnectorErrorType::UserError, + ("04", "04") => ConnectorErrorType::TechnicalError, + _ => ConnectorErrorType::UnknownError, + } + } + } + + #[test] + fn test_get_error_code_error_message_based_on_priority() { + let error_code_message_list_unknown = vec![ + ErrorCodeAndMessage { + error_code: "01".to_string(), + error_message: "INVALID_MERCHANT".to_string(), + }, + ErrorCodeAndMessage { + error_code: "05".to_string(), + error_message: "05".to_string(), + }, + ErrorCodeAndMessage { + error_code: "03".to_string(), + error_message: "INVALID_CVV".to_string(), + }, + ErrorCodeAndMessage { + error_code: "04".to_string(), + error_message: "04".to_string(), + }, + ]; + let error_code_message_list_user = vec![ + ErrorCodeAndMessage { + error_code: "01".to_string(), + error_message: "INVALID_MERCHANT".to_string(), + }, + ErrorCodeAndMessage { + error_code: "03".to_string(), + error_message: "INVALID_CVV".to_string(), + }, + ]; + let error_code_error_message_unknown = get_error_code_error_message_based_on_priority( + TestConnector, + error_code_message_list_unknown, + ); + let error_code_error_message_user = get_error_code_error_message_based_on_priority( + TestConnector, + error_code_message_list_user, + ); + let error_code_error_message_none = + get_error_code_error_message_based_on_priority(TestConnector, vec![]); + assert_eq!( + error_code_error_message_unknown, + Some(ErrorCodeAndMessage { + error_code: "05".to_string(), + error_message: "05".to_string(), + }) + ); + assert_eq!( + error_code_error_message_user, + Some(ErrorCodeAndMessage { + error_code: "03".to_string(), + error_message: "INVALID_CVV".to_string(), + }) + ); + assert_eq!(error_code_error_message_none, None); + } +} diff --git a/crates/router/src/connector/wise.rs b/crates/router/src/connector/wise.rs index 0674cc2ff8bc..d2ec08c607c4 100644 --- a/crates/router/src/connector/wise.rs +++ b/crates/router/src/connector/wise.rs @@ -90,7 +90,7 @@ impl ConnectorCommon for Wise { let default_status = response.status.unwrap_or_default().to_string(); match response.errors { Some(errs) => { - if let Some(e) = errs.get(0) { + if let Some(e) = errs.first() { Ok(types::ErrorResponse { status_code: res.status_code, code: e.code.clone(), @@ -301,7 +301,7 @@ impl services::ConnectorIntegration = list.into_iter().map(ForeignFrom::foreign_from).collect(); diff --git a/crates/router/src/core/payments/helpers.rs b/crates/router/src/core/payments/helpers.rs index 2cd62fbd4914..b4bd677df5b2 100644 --- a/crates/router/src/core/payments/helpers.rs +++ b/crates/router/src/core/payments/helpers.rs @@ -3799,17 +3799,22 @@ pub async fn get_gsm_record( pub fn validate_order_details_amount( order_details: Vec, amount: i64, + should_validate: bool, ) -> Result<(), errors::ApiErrorResponse> { - let total_order_details_amount: i64 = order_details - .iter() - .map(|order| order.amount * i64::from(order.quantity)) - .sum(); + if should_validate { + let total_order_details_amount: i64 = order_details + .iter() + .map(|order| order.amount * i64::from(order.quantity)) + .sum(); - if total_order_details_amount != amount { - Err(errors::ApiErrorResponse::InvalidRequestData { - message: "Total sum of order details doesn't match amount in payment request" - .to_string(), - }) + if total_order_details_amount != amount { + Err(errors::ApiErrorResponse::InvalidRequestData { + message: "Total sum of order details doesn't match amount in payment request" + .to_string(), + }) + } else { + Ok(()) + } } else { Ok(()) } diff --git a/crates/router/src/core/payments/operations/payment_confirm.rs b/crates/router/src/core/payments/operations/payment_confirm.rs index 58983f264c70..36b258bf5f0f 100644 --- a/crates/router/src/core/payments/operations/payment_confirm.rs +++ b/crates/router/src/core/payments/operations/payment_confirm.rs @@ -105,6 +105,7 @@ impl helpers::validate_order_details_amount( order_details.to_owned(), payment_intent.amount, + false, )?; } diff --git a/crates/router/src/core/payments/operations/payment_create.rs b/crates/router/src/core/payments/operations/payment_create.rs index 798678e64df3..3314f09c8c9c 100644 --- a/crates/router/src/core/payments/operations/payment_create.rs +++ b/crates/router/src/core/payments/operations/payment_create.rs @@ -190,6 +190,7 @@ impl helpers::validate_order_details_amount( order_details.to_owned(), payment_intent.amount, + false, )?; } diff --git a/crates/router/src/core/payments/operations/payment_update.rs b/crates/router/src/core/payments/operations/payment_update.rs index 153ded14f4b8..530a19a4d6b1 100644 --- a/crates/router/src/core/payments/operations/payment_update.rs +++ b/crates/router/src/core/payments/operations/payment_update.rs @@ -64,6 +64,7 @@ impl helpers::validate_order_details_amount( order_details.to_owned(), payment_intent.amount, + false, )?; } diff --git a/crates/router/src/core/payments/transformers.rs b/crates/router/src/core/payments/transformers.rs index 577480d311d7..ff8b95d4ab44 100644 --- a/crates/router/src/core/payments/transformers.rs +++ b/crates/router/src/core/payments/transformers.rs @@ -547,7 +547,7 @@ where if third_party_sdk_session_next_action(&payment_attempt, operation) { next_action_response = Some( api_models::payments::NextActionData::ThirdPartySdkSessionToken { - session_token: payment_data.sessions_token.get(0).cloned(), + session_token: payment_data.sessions_token.first().cloned(), }, ) } diff --git a/crates/router/src/db/dispute.rs b/crates/router/src/db/dispute.rs index c63585205bb3..4529b121fa24 100644 --- a/crates/router/src/db/dispute.rs +++ b/crates/router/src/db/dispute.rs @@ -572,7 +572,7 @@ mod tests { assert_eq!(1, found_disputes.len()); - assert_eq!(created_dispute, found_disputes.get(0).unwrap().clone()); + assert_eq!(created_dispute, found_disputes.first().unwrap().clone()); } #[tokio::test] @@ -611,7 +611,7 @@ mod tests { assert_eq!(1, found_disputes.len()); - assert_eq!(created_dispute, found_disputes.get(0).unwrap().clone()); + assert_eq!(created_dispute, found_disputes.first().unwrap().clone()); } mod update_dispute { diff --git a/crates/router/src/macros.rs b/crates/router/src/macros.rs index e6c9dba7d6e2..efe71e49bb04 100644 --- a/crates/router/src/macros.rs +++ b/crates/router/src/macros.rs @@ -1,4 +1 @@ -pub use common_utils::{ - async_spawn, collect_missing_value_keys, fallback_reverse_lookup_not_found, newtype, - newtype_impl, -}; +pub use common_utils::{collect_missing_value_keys, newtype}; diff --git a/crates/router/src/services/api.rs b/crates/router/src/services/api.rs index a9fa574800ea..8f109c703832 100644 --- a/crates/router/src/services/api.rs +++ b/crates/router/src/services/api.rs @@ -1529,16 +1529,16 @@ pub fn build_redirection_form( var responseForm = document.createElement('form'); responseForm.action=window.location.pathname.replace(/payments\\/redirect\\/(\\w+)\\/(\\w+)\\/\\w+/, \"payments/$1/$2/redirect/complete/nmi\"); responseForm.method='POST'; - + const threeDSsecureInterface = threeDS.createUI(options); threeDSsecureInterface.start('body'); - + threeDSsecureInterface.on('challenge', function(e) {{ console.log('Challenged'); }}); - + threeDSsecureInterface.on('complete', function(e) {{ - + var item1=document.createElement('input'); item1.type='hidden'; item1.name='cavv'; @@ -1566,11 +1566,11 @@ pub fn build_redirection_form( document.body.appendChild(responseForm); responseForm.submit(); }}); - + threeDSsecureInterface.on('failure', function(e) {{ responseForm.submit(); }}); - + " ))) } @@ -1578,14 +1578,6 @@ pub fn build_redirection_form( } } -#[cfg(test)] -mod tests { - #[test] - fn test_mime_essence() { - assert_eq!(mime::APPLICATION_JSON.essence_str(), "application/json"); - } -} - pub fn build_payment_link_html( payment_link_data: PaymentLinkFormData, ) -> CustomResult { @@ -1615,3 +1607,11 @@ pub fn build_payment_link_html( fn get_hyper_loader_sdk(sdk_url: &str) -> String { format!("") } + +#[cfg(test)] +mod tests { + #[test] + fn test_mime_essence() { + assert_eq!(mime::APPLICATION_JSON.essence_str(), "application/json"); + } +} diff --git a/crates/router/src/types/api/connector_onboarding/paypal.rs b/crates/router/src/types/api/connector_onboarding/paypal.rs index 0cc026d4d7ad..dbfdd6f50075 100644 --- a/crates/router/src/types/api/connector_onboarding/paypal.rs +++ b/crates/router/src/types/api/connector_onboarding/paypal.rs @@ -177,7 +177,7 @@ pub enum VettingStatus { impl SellerStatusResponse { pub fn extract_merchant_details_url(self, paypal_base_url: &str) -> RouterResult { self.links - .get(0) + .first() .and_then(|link| link.href.strip_prefix('/')) .map(|link| format!("{}{}", paypal_base_url, link)) .ok_or(ApiErrorResponse::InternalServerError) diff --git a/crates/router/src/types/storage.rs b/crates/router/src/types/storage.rs index 1dc241cde20c..56d3272b9471 100644 --- a/crates/router/src/types/storage.rs +++ b/crates/router/src/types/storage.rs @@ -26,7 +26,6 @@ pub mod payment_link; pub mod payment_method; pub mod payout_attempt; pub mod payouts; -mod query; pub mod refund; pub mod reverse_lookup; pub mod routing_algorithm; diff --git a/crates/router/src/types/storage/query.rs b/crates/router/src/types/storage/query.rs deleted file mode 100644 index 1483dec3eab3..000000000000 --- a/crates/router/src/types/storage/query.rs +++ /dev/null @@ -1 +0,0 @@ -pub use diesel_models::query::*; diff --git a/crates/router/src/utils.rs b/crates/router/src/utils.rs index 42116e1ecbf0..aaa145099e4e 100644 --- a/crates/router/src/utils.rs +++ b/crates/router/src/utils.rs @@ -196,16 +196,6 @@ impl QrImage { } } -#[cfg(test)] -mod tests { - use crate::utils; - #[test] - fn test_image_data_source_url() { - let qr_image_data_source_url = utils::QrImage::new_from_data("Hyperswitch".to_string()); - assert!(qr_image_data_source_url.is_ok()); - } -} - pub async fn find_payment_intent_from_payment_id_type( db: &dyn StorageInterface, payment_id_type: payments::PaymentIdType, @@ -804,3 +794,13 @@ pub async fn flatten_join_error(handle: Handle) -> RouterResult { .attach_printable("Join Error"), } } + +#[cfg(test)] +mod tests { + use crate::utils; + #[test] + fn test_image_data_source_url() { + let qr_image_data_source_url = utils::QrImage::new_from_data("Hyperswitch".to_string()); + assert!(qr_image_data_source_url.is_ok()); + } +} diff --git a/crates/router/src/utils/user/sample_data.rs b/crates/router/src/utils/user/sample_data.rs index 9f95e2d078dd..5ce0818b82b3 100644 --- a/crates/router/src/utils/user/sample_data.rs +++ b/crates/router/src/utils/user/sample_data.rs @@ -48,9 +48,9 @@ pub async fn generate_sample_data( .change_context(SampleDataError::InternalServerError) .attach_printable("Error while parsing primary business details")?; - let business_country_default = merchant_parsed_details.get(0).map(|x| x.country); + let business_country_default = merchant_parsed_details.first().map(|x| x.country); - let business_label_default = merchant_parsed_details.get(0).map(|x| x.business.clone()); + let business_label_default = merchant_parsed_details.first().map(|x| x.business.clone()); let profile_id = crate::core::utils::get_profile_id_from_business_details( business_country_default, diff --git a/crates/router/tests/connectors/utils.rs b/crates/router/tests/connectors/utils.rs index 62fce84f1f9d..c27a648a3051 100644 --- a/crates/router/tests/connectors/utils.rs +++ b/crates/router/tests/connectors/utils.rs @@ -458,7 +458,7 @@ pub trait ConnectorActions: Connector { customer_details: Some(payments::CustomerDetails { customer_id: core_utils::get_or_generate_id("customer_id", &None, "cust_").ok(), name: Some(Secret::new("John Doe".to_string())), - email: TryFrom::try_from(Secret::new("john.doe@example".to_string())).ok(), + email: TryFrom::try_from("john.doe@example".to_string()).ok(), phone: Some(Secret::new("620874518".to_string())), phone_country_code: Some("+31".to_string()), }), diff --git a/crates/scheduler/src/consumer.rs b/crates/scheduler/src/consumer.rs index 08899552704a..3e44ef557652 100644 --- a/crates/scheduler/src/consumer.rs +++ b/crates/scheduler/src/consumer.rs @@ -40,9 +40,15 @@ pub async fn start_consumer( use rand::distributions::{Distribution, Uniform}; let mut rng = rand::thread_rng(); + + // TODO: this can be removed once rand-0.9 is released + // reference - https://github.com/rust-random/rand/issues/1326#issuecomment-1635331942 + #[allow(unknown_lints)] + #[allow(clippy::unnecessary_fallible_conversions)] let timeout = Uniform::try_from(0..=settings.loop_interval) .into_report() .change_context(errors::ProcessTrackerError::ConfigurationError)?; + tokio::time::sleep(Duration::from_millis(timeout.sample(&mut rng))).await; let mut interval = tokio::time::interval(Duration::from_millis(settings.loop_interval)); diff --git a/crates/scheduler/src/producer.rs b/crates/scheduler/src/producer.rs index 52510e1842e0..bcf37cdf6f22 100644 --- a/crates/scheduler/src/producer.rs +++ b/crates/scheduler/src/producer.rs @@ -30,9 +30,15 @@ where use rand::distributions::{Distribution, Uniform}; let mut rng = rand::thread_rng(); + + // TODO: this can be removed once rand-0.9 is released + // reference - https://github.com/rust-random/rand/issues/1326#issuecomment-1635331942 + #[allow(unknown_lints)] + #[allow(clippy::unnecessary_fallible_conversions)] let timeout = Uniform::try_from(0..=scheduler_settings.loop_interval) .into_report() .change_context(errors::ProcessTrackerError::ConfigurationError)?; + tokio::time::sleep(Duration::from_millis(timeout.sample(&mut rng))).await; let mut interval = tokio::time::interval(std::time::Duration::from_millis(