diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 01:13:27 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 01:13:27 +0000 |
commit | 40a355a42d4a9444dc753c04c6608dade2f06a23 (patch) | |
tree | 871fc667d2de662f171103ce5ec067014ef85e61 /third_party/rust/glean-core/src | |
parent | Adding upstream version 124.0.1. (diff) | |
download | firefox-adbda400be353e676059e335c3c0aaf99e719475.tar.xz firefox-adbda400be353e676059e335c3c0aaf99e719475.zip |
Adding upstream version 125.0.1.upstream/125.0.1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'third_party/rust/glean-core/src')
35 files changed, 899 insertions, 190 deletions
diff --git a/third_party/rust/glean-core/src/core/mod.rs b/third_party/rust/glean-core/src/core/mod.rs index 5a8dd56cde..30f9a34f11 100644 --- a/third_party/rust/glean-core/src/core/mod.rs +++ b/third_party/rust/glean-core/src/core/mod.rs @@ -118,11 +118,11 @@ where /// trim_data_to_registered_pings: false, /// log_level: None, /// rate_limit: None, -/// enable_event_timestamps: false, +/// enable_event_timestamps: true, /// experimentation_id: None, /// }; /// let mut glean = Glean::new(cfg).unwrap(); -/// let ping = PingType::new("sample", true, false, true, vec![]); +/// let ping = PingType::new("sample", true, false, true, true, vec![]); /// glean.register_ping_type(&ping); /// /// let call_counter: CounterMetric = CounterMetric::new(CommonMetricData { @@ -318,7 +318,7 @@ impl Glean { trim_data_to_registered_pings: false, log_level: None, rate_limit: None, - enable_event_timestamps: false, + enable_event_timestamps: true, experimentation_id: None, }; diff --git a/third_party/rust/glean-core/src/glean.udl b/third_party/rust/glean-core/src/glean.udl index e531f64a26..e68a57ea4c 100644 --- a/third_party/rust/glean-core/src/glean.udl +++ b/third_party/rust/glean-core/src/glean.udl @@ -201,6 +201,10 @@ dictionary PingRequest { sequence<u8> body; // A map with all the headers to be sent with the request. record<DOMString, string> headers; + // Whether the body has {client|ping}_info sections. + boolean body_has_info_sections; + // The ping's name. Likely also somewhere in `path`. + string ping_name; }; // An enum representing the possible upload tasks to be performed by an uploader. @@ -287,7 +291,7 @@ enum ErrorType { }; interface PingType { - constructor(string name, boolean include_client_id, boolean send_if_empty, boolean precise_timestamps, sequence<string> reason_codes); + constructor(string name, boolean include_client_id, boolean send_if_empty, boolean precise_timestamps, boolean include_info_sections, sequence<string> reason_codes); void submit(optional string? reason = null); }; @@ -480,6 +484,8 @@ interface TimingDistributionMetric { void accumulate_samples(sequence<i64> samples); + void accumulate_single_sample(i64 sample); + DistributionData? test_get_value(optional string? ping_name = null); i32 test_get_num_recorded_errors(ErrorType error); @@ -523,6 +529,8 @@ interface CustomDistributionMetric { void accumulate_samples(sequence<i64> samples); + void accumulate_single_sample(i64 sample); + DistributionData? test_get_value(optional string? ping_name = null); i32 test_get_num_recorded_errors(ErrorType error); diff --git a/third_party/rust/glean-core/src/internal_pings.rs b/third_party/rust/glean-core/src/internal_pings.rs index 076a1f7485..07c3849006 100644 --- a/third_party/rust/glean-core/src/internal_pings.rs +++ b/third_party/rust/glean-core/src/internal_pings.rs @@ -26,6 +26,7 @@ impl InternalPings { true, true, true, + true, vec![ "active".to_string(), "dirty_startup".to_string(), @@ -37,6 +38,7 @@ impl InternalPings { true, false, true, + true, vec![ "overdue".to_string(), "reschedule".to_string(), @@ -50,6 +52,7 @@ impl InternalPings { true, false, true, + true, vec![ "startup".to_string(), "inactive".to_string(), @@ -61,6 +64,7 @@ impl InternalPings { true, true, true, + true, vec!["at_init".to_string(), "set_upload_enabled".to_string()], ), } diff --git a/third_party/rust/glean-core/src/lib.rs b/third_party/rust/glean-core/src/lib.rs index a54e57a95b..b7f9d73beb 100644 --- a/third_party/rust/glean-core/src/lib.rs +++ b/third_party/rust/glean-core/src/lib.rs @@ -91,12 +91,12 @@ pub(crate) const DELETION_REQUEST_PINGS_DIRECTORY: &str = "deletion_request"; static INITIALIZE_CALLED: AtomicBool = AtomicBool::new(false); /// Keep track of the debug features before Glean is initialized. -static PRE_INIT_DEBUG_VIEW_TAG: OnceCell<Mutex<String>> = OnceCell::new(); +static PRE_INIT_DEBUG_VIEW_TAG: Mutex<String> = Mutex::new(String::new()); static PRE_INIT_LOG_PINGS: AtomicBool = AtomicBool::new(false); -static PRE_INIT_SOURCE_TAGS: OnceCell<Mutex<Vec<String>>> = OnceCell::new(); +static PRE_INIT_SOURCE_TAGS: Mutex<Vec<String>> = Mutex::new(Vec::new()); /// Keep track of pings registered before Glean is initialized. -static PRE_INIT_PING_REGISTRATION: OnceCell<Mutex<Vec<metrics::PingType>>> = OnceCell::new(); +static PRE_INIT_PING_REGISTRATION: Mutex<Vec<metrics::PingType>> = Mutex::new(Vec::new()); /// Global singleton of the handles of the glean.init threads. /// For joining. For tests. @@ -396,11 +396,9 @@ fn initialize_inner( core::with_glean_mut(|glean| { // The debug view tag might have been set before initialize, // get the cached value and set it. - if let Some(tag) = PRE_INIT_DEBUG_VIEW_TAG.get() { - let lock = tag.try_lock(); - if let Ok(ref debug_tag) = lock { - glean.set_debug_view_tag(debug_tag); - } + let debug_tag = PRE_INIT_DEBUG_VIEW_TAG.lock().unwrap(); + if debug_tag.len() > 0 { + glean.set_debug_view_tag(&debug_tag); } // The log pings debug option might have been set before initialize, @@ -412,11 +410,9 @@ fn initialize_inner( // The source tags might have been set before initialize, // get the cached value and set them. - if let Some(tags) = PRE_INIT_SOURCE_TAGS.get() { - let lock = tags.try_lock(); - if let Ok(ref source_tags) = lock { - glean.set_source_tags(source_tags.to_vec()); - } + let source_tags = PRE_INIT_SOURCE_TAGS.lock().unwrap(); + if source_tags.len() > 0 { + glean.set_source_tags(source_tags.to_vec()); } // Get the current value of the dirty flag so we know whether to @@ -428,13 +424,9 @@ fn initialize_inner( // Perform registration of pings that were attempted to be // registered before init. - if let Some(tags) = PRE_INIT_PING_REGISTRATION.get() { - let lock = tags.try_lock(); - if let Ok(pings) = lock { - for ping in &*pings { - glean.register_ping_type(ping); - } - } + let pings = PRE_INIT_PING_REGISTRATION.lock().unwrap(); + for ping in pings.iter() { + glean.register_ping_type(ping); } // If this is the first time ever the Glean SDK runs, make sure to set @@ -861,7 +853,7 @@ pub(crate) fn register_ping_type(ping: &PingType) { // if ping registration is attempted before Glean initializes. // This state is kept across Glean resets, which should only ever happen in test mode. // It's a set and keeping them around forever should not have much of an impact. - let m = PRE_INIT_PING_REGISTRATION.get_or_init(Default::default); + let m = &PRE_INIT_PING_REGISTRATION; let mut lock = m.lock().unwrap(); lock.push(ping.clone()); } @@ -956,7 +948,7 @@ pub fn glean_set_debug_view_tag(tag: String) -> bool { true } else { // Glean has not been initialized yet. Cache the provided tag value. - let m = PRE_INIT_DEBUG_VIEW_TAG.get_or_init(Default::default); + let m = &PRE_INIT_DEBUG_VIEW_TAG; let mut lock = m.lock().unwrap(); *lock = tag; // When setting the debug view tag before initialization, @@ -984,7 +976,7 @@ pub fn glean_set_source_tags(tags: Vec<String>) -> bool { true } else { // Glean has not been initialized yet. Cache the provided source tags. - let m = PRE_INIT_SOURCE_TAGS.get_or_init(Default::default); + let m = &PRE_INIT_SOURCE_TAGS; let mut lock = m.lock().unwrap(); *lock = tags; // When setting the source tags before initialization, diff --git a/third_party/rust/glean-core/src/lib_unit_tests.rs b/third_party/rust/glean-core/src/lib_unit_tests.rs index 0fc85b4602..cb1e4129d8 100644 --- a/third_party/rust/glean-core/src/lib_unit_tests.rs +++ b/third_party/rust/glean-core/src/lib_unit_tests.rs @@ -197,7 +197,7 @@ fn experimentation_id_is_set_correctly() { trim_data_to_registered_pings: false, log_level: None, rate_limit: None, - enable_event_timestamps: false, + enable_event_timestamps: true, experimentation_id: Some(experimentation_id.to_string()), }) .unwrap(); @@ -423,6 +423,7 @@ fn correct_order() { Jwe("eyJhbGciOiJSU0EtT0FFUCIsImVuYyI6IkEyNTZHQ00ifQ.OKOawDo13gRp2ojaHV7LFpZcgV7T6DVZKTyKOMTYUmKoTCVJRgckCL9kiMT03JGeipsEdY3mx_etLbbWSrFr05kLzcSr4qKAq7YN7e9jwQRb23nfa6c9d-StnImGyFDbSv04uVuxIp5Zms1gNxKKK2Da14B8S4rzVRltdYwam_lDp5XnZAYpQdb76FdIKLaVmqgfwX7XWRxv2322i-vDxRfqNzo_tETKzpVLzfiwQyeyPGLBIO56YJ7eObdv0je81860ppamavo35UgoRdbYaBcoh9QcfylQr66oc6vFWXRcZ_ZT2LawVCWTIy3brGPi6UklfCpIMfIjf7iGdXKHzg.48V1_ALb6US04U3b.5eym8TW_c8SuK0ltJ3rpYIzOeDQz7TALvtu6UG9oMo4vpzs9tX_EFShS8iB7j6jiSdiwkIr3ajwQzaBtQD_A.XFBoMYUZodetZdvTiFvSkQ".into()), Rate(0, 0), Text(long_string), + Object("{}".into()), ]; for metric in all_metrics { @@ -451,6 +452,7 @@ fn correct_order() { Rate(..) => assert_eq!(14, disc), Url(..) => assert_eq!(15, disc), Text(..) => assert_eq!(16, disc), + Object(..) => assert_eq!(17, disc), } } } diff --git a/third_party/rust/glean-core/src/metrics/boolean.rs b/third_party/rust/glean-core/src/metrics/boolean.rs index 71ed2372c2..ade4a22bfc 100644 --- a/third_party/rust/glean-core/src/metrics/boolean.rs +++ b/third_party/rust/glean-core/src/metrics/boolean.rs @@ -74,7 +74,6 @@ impl BooleanMetric { /// /// # Arguments /// - /// * `glean` - the Glean instance this metric belongs to. /// * `value` - the value to set. pub fn set(&self, value: bool) { let metric = self.clone(); @@ -106,6 +105,15 @@ impl BooleanMetric { /// Gets the currently stored value as an integer. /// /// This doesn't clear the stored value. + /// + /// # Arguments + /// + /// * `ping_name` - the optional name of the ping to retrieve the metric + /// for. Defaults to the first value in `send_in_pings`. + /// + /// # Returns + /// + /// The stored value or `None` if nothing stored. pub fn test_get_value(&self, ping_name: Option<String>) -> Option<bool> { crate::block_on_dispatcher(); crate::core::with_glean(|glean| self.get_value(glean, ping_name.as_deref())) @@ -118,8 +126,6 @@ impl BooleanMetric { /// # Arguments /// /// * `error` - The type of error - /// * `ping_name` - represents the optional name of the ping to retrieve the - /// metric for. inner to the first value in `send_in_pings`. /// /// # Returns /// diff --git a/third_party/rust/glean-core/src/metrics/counter.rs b/third_party/rust/glean-core/src/metrics/counter.rs index 8f0a01cc3e..7e262c7d68 100644 --- a/third_party/rust/glean-core/src/metrics/counter.rs +++ b/third_party/rust/glean-core/src/metrics/counter.rs @@ -105,7 +105,6 @@ impl CounterMetric { /// /// # Arguments /// - /// * `glean` - The Glean instance this metric belongs to. /// * `amount` - The amount to increase by. Should be positive. /// /// ## Notes @@ -143,6 +142,15 @@ impl CounterMetric { /// Gets the currently stored value as an integer. /// /// This doesn't clear the stored value. + /// + /// # Arguments + /// + /// * `ping_name` - the optional name of the ping to retrieve the metric + /// for. Defaults to the first value in `send_in_pings`. + /// + /// # Returns + /// + /// The stored value or `None` if nothing stored. pub fn test_get_value(&self, ping_name: Option<String>) -> Option<i32> { crate::block_on_dispatcher(); crate::core::with_glean(|glean| self.get_value(glean, ping_name.as_deref())) @@ -155,8 +163,6 @@ impl CounterMetric { /// # Arguments /// /// * `error` - The type of error - /// * `ping_name` - represents the optional name of the ping to retrieve the - /// metric for. inner to the first value in `send_in_pings`. /// /// # Returns /// diff --git a/third_party/rust/glean-core/src/metrics/custom_distribution.rs b/third_party/rust/glean-core/src/metrics/custom_distribution.rs index 929e4863ec..c7f3fbc56f 100644 --- a/third_party/rust/glean-core/src/metrics/custom_distribution.rs +++ b/third_party/rust/glean-core/src/metrics/custom_distribution.rs @@ -84,14 +84,33 @@ impl CustomDistributionMetric { /// for each of them. pub fn accumulate_samples(&self, samples: Vec<i64>) { let metric = self.clone(); - crate::launch_with_glean(move |glean| metric.accumulate_samples_sync(glean, samples)) + crate::launch_with_glean(move |glean| metric.accumulate_samples_sync(glean, &samples)) + } + + /// Accumulates precisely one signed sample and appends it to the metric. + /// + /// Signed is required so that the platform-specific code can provide us with a + /// 64 bit signed integer if no `u64` comparable type is available. This + /// will take care of filtering and reporting errors. + /// + /// # Arguments + /// + /// - `sample` - The singular sample to be recorded by the metric. + /// + /// ## Notes + /// + /// Discards any negative value of `sample` and reports an + /// [`ErrorType::InvalidValue`](crate::ErrorType::InvalidValue). + pub fn accumulate_single_sample(&self, sample: i64) { + let metric = self.clone(); + crate::launch_with_glean(move |glean| metric.accumulate_samples_sync(glean, &[sample])) } /// Accumulates the provided sample in the metric synchronously. /// /// See [`accumulate_samples`](Self::accumulate_samples) for details. #[doc(hidden)] - pub fn accumulate_samples_sync(&self, glean: &Glean, samples: Vec<i64>) { + pub fn accumulate_samples_sync(&self, glean: &Glean, samples: &[i64]) { if !self.should_record(glean) { return; } @@ -132,7 +151,7 @@ impl CustomDistributionMetric { self.bucket_count as usize, ) }; - accumulate(&samples, hist, Metric::CustomDistributionLinear) + accumulate(samples, hist, Metric::CustomDistributionLinear) } HistogramType::Exponential => { let hist = if let Some(Metric::CustomDistributionExponential(hist)) = old_value @@ -145,7 +164,7 @@ impl CustomDistributionMetric { self.bucket_count as usize, ) }; - accumulate(&samples, hist, Metric::CustomDistributionExponential) + accumulate(samples, hist, Metric::CustomDistributionExponential) } }; @@ -194,6 +213,15 @@ impl CustomDistributionMetric { /// Gets the currently stored value as an integer. /// /// This doesn't clear the stored value. + /// + /// # Arguments + /// + /// * `ping_name` - the optional name of the ping to retrieve the metric + /// for. Defaults to the first value in `send_in_pings`. + /// + /// # Returns + /// + /// The stored value or `None` if nothing stored. pub fn test_get_value(&self, ping_name: Option<String>) -> Option<DistributionData> { crate::block_on_dispatcher(); crate::core::with_glean(|glean| self.get_value(glean, ping_name.as_deref())) @@ -206,8 +234,6 @@ impl CustomDistributionMetric { /// # Arguments /// /// * `error` - The type of error - /// * `ping_name` - represents the optional name of the ping to retrieve the - /// metric for. inner to the first value in `send_in_pings`. /// /// # Returns /// diff --git a/third_party/rust/glean-core/src/metrics/datetime.rs b/third_party/rust/glean-core/src/metrics/datetime.rs index 3ef846a32c..e04f7fc051 100644 --- a/third_party/rust/glean-core/src/metrics/datetime.rs +++ b/third_party/rust/glean-core/src/metrics/datetime.rs @@ -262,8 +262,8 @@ impl DatetimeMetric { /// /// # Arguments /// - /// * `glean` - the Glean instance this metric belongs to. - /// * `storage_name` - the storage name to look into. + /// * `ping_name` - the optional name of the ping to retrieve the metric + /// for. Defaults to the first value in `send_in_pings`. /// /// # Returns /// @@ -284,8 +284,8 @@ impl DatetimeMetric { /// /// # Arguments /// - /// * `glean` - the Glean instance this metric belongs to. - /// * `storage_name` - the storage name to look into. + /// * `ping_name` - the optional name of the ping to retrieve the metric + /// for. Defaults to the first value in `send_in_pings`. /// /// # Returns /// @@ -311,8 +311,6 @@ impl DatetimeMetric { /// # Arguments /// /// * `error` - The type of error - /// * `ping_name` - represents the optional name of the ping to retrieve the - /// metric for. inner to the first value in `send_in_pings`. /// /// # Returns /// diff --git a/third_party/rust/glean-core/src/metrics/denominator.rs b/third_party/rust/glean-core/src/metrics/denominator.rs index fb80874924..3083d6e78a 100644 --- a/third_party/rust/glean-core/src/metrics/denominator.rs +++ b/third_party/rust/glean-core/src/metrics/denominator.rs @@ -91,6 +91,15 @@ impl DenominatorMetric { /// Gets the currently stored value as an integer. /// /// This doesn't clear the stored value. + /// + /// # Arguments + /// + /// * `ping_name` - the optional name of the ping to retrieve the metric + /// for. Defaults to the first value in `send_in_pings`. + /// + /// # Returns + /// + /// The stored value or `None` if nothing stored. pub fn test_get_value(&self, ping_name: Option<String>) -> Option<i32> { crate::block_on_dispatcher(); crate::core::with_glean(|glean| self.get_value(glean, ping_name.as_deref())) @@ -124,8 +133,6 @@ impl DenominatorMetric { /// # Arguments /// /// * `error` - The type of error - /// * `ping_name` - the optional name of the ping to retrieve the metric - /// for. inner to the first value in `send_in_pings`. /// /// # Returns /// diff --git a/third_party/rust/glean-core/src/metrics/event.rs b/third_party/rust/glean-core/src/metrics/event.rs index 5ad6e6d50c..c7aefd9cd6 100644 --- a/third_party/rust/glean-core/src/metrics/event.rs +++ b/third_party/rust/glean-core/src/metrics/event.rs @@ -185,6 +185,11 @@ impl EventMetric { /// Get the vector of currently stored events for this event metric. /// /// This doesn't clear the stored value. + /// + /// # Arguments + /// + /// * `ping_name` - the optional name of the ping to retrieve the metric + /// for. Defaults to the first value in `send_in_pings`. pub fn test_get_value(&self, ping_name: Option<String>) -> Option<Vec<RecordedEvent>> { crate::block_on_dispatcher(); crate::core::with_glean(|glean| self.get_value(glean, ping_name.as_deref())) @@ -197,8 +202,6 @@ impl EventMetric { /// # Arguments /// /// * `error` - The type of error - /// * `ping_name` - represents the optional name of the ping to retrieve the - /// metric for. inner to the first value in `send_in_pings`. /// /// # Returns /// diff --git a/third_party/rust/glean-core/src/metrics/experiment.rs b/third_party/rust/glean-core/src/metrics/experiment.rs index 23e6c41ce2..5695bf942e 100644 --- a/third_party/rust/glean-core/src/metrics/experiment.rs +++ b/third_party/rust/glean-core/src/metrics/experiment.rs @@ -195,6 +195,15 @@ impl ExperimentMetric { /// the RecordedExperiment. /// /// This doesn't clear the stored value. + /// + /// # Arguments + /// + /// * `ping_name` - the optional name of the ping to retrieve the metric + /// for. Defaults to the first value in `send_in_pings`. + /// + /// # Returns + /// + /// The stored value or `None` if nothing stored. pub fn test_get_value(&self, glean: &Glean) -> Option<RecordedExperiment> { match StorageManager.snapshot_metric_for_test( glean.storage(), diff --git a/third_party/rust/glean-core/src/metrics/labeled.rs b/third_party/rust/glean-core/src/metrics/labeled.rs index fa3e6a6a75..f9f6a28880 100644 --- a/third_party/rust/glean-core/src/metrics/labeled.rs +++ b/third_party/rust/glean-core/src/metrics/labeled.rs @@ -205,8 +205,6 @@ where /// # Arguments /// /// * `error` - The type of error - /// * `ping_name` - represents the optional name of the ping to retrieve the - /// metric for. Defaults to the first value in `send_in_pings`. /// /// # Returns /// diff --git a/third_party/rust/glean-core/src/metrics/memory_distribution.rs b/third_party/rust/glean-core/src/metrics/memory_distribution.rs index ac9eda1a90..7b5e5ee192 100644 --- a/third_party/rust/glean-core/src/metrics/memory_distribution.rs +++ b/third_party/rust/glean-core/src/metrics/memory_distribution.rs @@ -254,6 +254,15 @@ impl MemoryDistributionMetric { /// Gets the currently stored value. /// /// This doesn't clear the stored value. + /// + /// # Arguments + /// + /// * `ping_name` - the optional name of the ping to retrieve the metric + /// for. Defaults to the first value in `send_in_pings`. + /// + /// # Returns + /// + /// The stored value or `None` if nothing stored. pub fn test_get_value(&self, ping_name: Option<String>) -> Option<DistributionData> { crate::block_on_dispatcher(); crate::core::with_glean(|glean| self.get_value(glean, ping_name.as_deref())) @@ -266,8 +275,6 @@ impl MemoryDistributionMetric { /// # Arguments /// /// * `error` - The type of error - /// * `ping_name` - represents the optional name of the ping to retrieve the - /// metric for. Defaults to the first value in `send_in_pings`. /// /// # Returns /// diff --git a/third_party/rust/glean-core/src/metrics/mod.rs b/third_party/rust/glean-core/src/metrics/mod.rs index 43253b9aa7..92001efd2a 100644 --- a/third_party/rust/glean-core/src/metrics/mod.rs +++ b/third_party/rust/glean-core/src/metrics/mod.rs @@ -9,7 +9,8 @@ use std::sync::atomic::Ordering; use chrono::{DateTime, FixedOffset}; use serde::{Deserialize, Serialize}; -use serde_json::{json, Value as JsonValue}; +use serde_json::json; +pub use serde_json::Value as JsonValue; mod boolean; mod counter; @@ -23,6 +24,7 @@ mod memory_distribution; mod memory_unit; mod metrics_enabled_config; mod numerator; +mod object; mod ping; mod quantity; mod rate; @@ -54,6 +56,7 @@ pub use self::labeled::{LabeledBoolean, LabeledCounter, LabeledMetric, LabeledSt pub use self::memory_distribution::MemoryDistributionMetric; pub use self::memory_unit::MemoryUnit; pub use self::numerator::NumeratorMetric; +pub use self::object::ObjectMetric; pub use self::ping::PingType; pub use self::quantity::QuantityMetric; pub use self::rate::{Rate, RateMetric}; @@ -141,6 +144,8 @@ pub enum Metric { Url(String), /// A Text metric. See [`TextMetric`] for more information. Text(String), + /// An Object metric. See [`ObjectMetric`] for more information. + Object(String), } /// A [`MetricType`] describes common behavior across all metrics. @@ -251,6 +256,7 @@ impl Metric { Metric::MemoryDistribution(_) => "memory_distribution", Metric::Jwe(_) => "jwe", Metric::Text(_) => "text", + Metric::Object(_) => "object", } } @@ -280,6 +286,9 @@ impl Metric { Metric::MemoryDistribution(hist) => json!(memory_distribution::snapshot(hist)), Metric::Jwe(s) => json!(s), Metric::Text(s) => json!(s), + Metric::Object(s) => { + serde_json::from_str(s).expect("object storage should have been json") + } } } } diff --git a/third_party/rust/glean-core/src/metrics/numerator.rs b/third_party/rust/glean-core/src/metrics/numerator.rs index 3c340cab1d..de29338a5c 100644 --- a/third_party/rust/glean-core/src/metrics/numerator.rs +++ b/third_party/rust/glean-core/src/metrics/numerator.rs @@ -55,12 +55,16 @@ impl NumeratorMetric { /// /// Gets the currently stored value as a pair of integers. /// + /// This doesn't clear the stored value. + /// /// # Arguments /// /// * `ping_name` - the optional name of the ping to retrieve the metric /// for. Defaults to the first value in `send_in_pings`. /// - /// This doesn't clear the stored value. + /// # Returns + /// + /// The stored value or `None` if nothing stored. pub fn test_get_value(&self, ping_name: Option<String>) -> Option<Rate> { crate::block_on_dispatcher(); crate::core::with_glean(|glean| self.get_value(glean, ping_name.as_deref())) @@ -82,8 +86,6 @@ impl NumeratorMetric { /// # Arguments /// /// * `error` - The type of error - /// * `ping_name` - the optional name of the ping to retrieve the metric - /// for. Defaults to the first value in `send_in_pings`. /// /// # Returns /// diff --git a/third_party/rust/glean-core/src/metrics/object.rs b/third_party/rust/glean-core/src/metrics/object.rs new file mode 100644 index 0000000000..6071e2b33a --- /dev/null +++ b/third_party/rust/glean-core/src/metrics/object.rs @@ -0,0 +1,135 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. + +use std::sync::Arc; + +use crate::common_metric_data::CommonMetricDataInternal; +use crate::error_recording::{record_error, test_get_num_recorded_errors, ErrorType}; +use crate::metrics::JsonValue; +use crate::metrics::Metric; +use crate::metrics::MetricType; +use crate::storage::StorageManager; +use crate::CommonMetricData; +use crate::Glean; + +/// An object metric. +/// +/// Record structured data. +/// The value must adhere to a predefined structure and is serialized into JSON. +#[derive(Clone, Debug)] +pub struct ObjectMetric { + meta: Arc<CommonMetricDataInternal>, +} + +impl MetricType for ObjectMetric { + fn meta(&self) -> &CommonMetricDataInternal { + &self.meta + } +} + +// IMPORTANT: +// +// When changing this implementation, make sure all the operations are +// also declared in the related trait in `../traits/`. +impl ObjectMetric { + /// Creates a new object metric. + pub fn new(meta: CommonMetricData) -> Self { + Self { + meta: Arc::new(meta.into()), + } + } + + /// Sets to the specified structure. + /// + /// # Arguments + /// + /// * `glean` - the Glean instance this metric belongs to. + /// * `value` - the value to set. + #[doc(hidden)] + pub fn set_sync(&self, glean: &Glean, value: JsonValue) { + let value = Metric::Object(serde_json::to_string(&value).unwrap()); + glean.storage().record(glean, &self.meta, &value) + } + + /// Sets to the specified structure. + /// + /// No additional verification is done. + /// The shape needs to be externally verified. + /// + /// # Arguments + /// + /// * `value` - the value to set. + pub fn set(&self, value: JsonValue) { + let metric = self.clone(); + crate::launch_with_glean(move |glean| metric.set_sync(glean, value)) + } + + /// Record an `InvalidValue` error for this metric. + /// + /// Only to be used by the RLB. + // TODO(bug 1691073): This can probably go once we have a more generic mechanism to record + // errors + pub fn record_schema_error(&self) { + let metric = self.clone(); + crate::launch_with_glean(move |glean| { + let msg = "Value did not match predefined schema"; + record_error(glean, &metric.meta, ErrorType::InvalidValue, msg, None); + }); + } + + /// Get current value + #[doc(hidden)] + pub fn get_value<'a, S: Into<Option<&'a str>>>( + &self, + glean: &Glean, + ping_name: S, + ) -> Option<String> { + let queried_ping_name = ping_name + .into() + .unwrap_or_else(|| &self.meta().inner.send_in_pings[0]); + + match StorageManager.snapshot_metric_for_test( + glean.storage(), + queried_ping_name, + &self.meta.identifier(glean), + self.meta.inner.lifetime, + ) { + Some(Metric::Object(o)) => Some(o), + _ => None, + } + } + + /// **Test-only API (exported for FFI purposes).** + /// + /// Gets the currently stored value as JSON. + /// + /// This doesn't clear the stored value. + pub fn test_get_value(&self, ping_name: Option<String>) -> Option<JsonValue> { + crate::block_on_dispatcher(); + let value = crate::core::with_glean(|glean| self.get_value(glean, ping_name.as_deref())); + // We only store valid JSON + value.map(|val| serde_json::from_str(&val).unwrap()) + } + + /// **Exported for test purposes.** + /// + /// Gets the number of recorded errors for the given metric and error type. + /// + /// # Arguments + /// + /// * `error` - The type of error + /// * `ping_name` - represents the optional name of the ping to retrieve the + /// metric for. inner to the first value in `send_in_pings`. + /// + /// # Returns + /// + /// The number of errors reported. + pub fn test_get_num_recorded_errors(&self, error: ErrorType) -> i32 { + crate::block_on_dispatcher(); + + crate::core::with_glean(|glean| { + test_get_num_recorded_errors(glean, self.meta(), error).unwrap_or(0) + }) + } +} diff --git a/third_party/rust/glean-core/src/metrics/ping.rs b/third_party/rust/glean-core/src/metrics/ping.rs index dc37d76a45..e60284b1e2 100644 --- a/third_party/rust/glean-core/src/metrics/ping.rs +++ b/third_party/rust/glean-core/src/metrics/ping.rs @@ -6,6 +6,7 @@ use std::fmt; use std::sync::Arc; use crate::ping::PingMaker; +use crate::upload::PingPayload; use crate::Glean; use uuid::Uuid; @@ -26,6 +27,8 @@ struct InnerPing { pub send_if_empty: bool, /// Whether to use millisecond-precise start/end times. pub precise_timestamps: bool, + /// Whether to include the {client|ping}_info sections on assembly. + pub include_info_sections: bool, /// The "reason" codes that this ping can send pub reason_codes: Vec<String>, } @@ -37,6 +40,7 @@ impl fmt::Debug for PingType { .field("include_client_id", &self.0.include_client_id) .field("send_if_empty", &self.0.send_if_empty) .field("precise_timestamps", &self.0.precise_timestamps) + .field("include_info_sections", &self.0.include_info_sections) .field("reason_codes", &self.0.reason_codes) .finish() } @@ -61,6 +65,7 @@ impl PingType { include_client_id: bool, send_if_empty: bool, precise_timestamps: bool, + include_info_sections: bool, reason_codes: Vec<String>, ) -> Self { let this = Self(Arc::new(InnerPing { @@ -68,6 +73,7 @@ impl PingType { include_client_id, send_if_empty, precise_timestamps, + include_info_sections, reason_codes, })); @@ -94,6 +100,10 @@ impl PingType { self.0.precise_timestamps } + pub(crate) fn include_info_sections(&self) -> bool { + self.0.include_info_sections + } + /// Submits the ping for eventual uploading. /// /// The ping content is assembled as soon as possible, but upload is not @@ -186,13 +196,17 @@ impl PingType { // so both scenarios should be impossible. let content = ::serde_json::to_string(&ping.content).expect("ping serialization failed"); - glean.upload_manager.enqueue_ping( - glean, - ping.doc_id, - ping.url_path, - &content, - Some(ping.headers), - ); + // TODO: Shouldn't we consolidate on a single collected Ping representation? + let ping = PingPayload { + document_id: ping.doc_id.to_string(), + upload_path: ping.url_path.to_string(), + json_body: content, + headers: Some(ping.headers), + body_has_info_sections: self.0.include_info_sections, + ping_name: self.0.name.to_string(), + }; + + glean.upload_manager.enqueue_ping(glean, ping); return true; } diff --git a/third_party/rust/glean-core/src/metrics/quantity.rs b/third_party/rust/glean-core/src/metrics/quantity.rs index c59d3a4a21..92216625d6 100644 --- a/third_party/rust/glean-core/src/metrics/quantity.rs +++ b/third_party/rust/glean-core/src/metrics/quantity.rs @@ -98,6 +98,15 @@ impl QuantityMetric { /// Gets the currently stored value as an integer. /// /// This doesn't clear the stored value. + /// + /// # Arguments + /// + /// * `ping_name` - the optional name of the ping to retrieve the metric + /// for. Defaults to the first value in `send_in_pings`. + /// + /// # Returns + /// + /// The stored value or `None` if nothing stored. pub fn test_get_value(&self, ping_name: Option<String>) -> Option<i64> { crate::block_on_dispatcher(); crate::core::with_glean(|glean| self.get_value(glean, ping_name.as_deref())) @@ -110,8 +119,6 @@ impl QuantityMetric { /// # Arguments /// /// * `error` - The type of error - /// * `ping_name` - represents the optional name of the ping to retrieve the - /// metric for. Defaults to the first value in `send_in_pings`. /// /// # Returns /// diff --git a/third_party/rust/glean-core/src/metrics/rate.rs b/third_party/rust/glean-core/src/metrics/rate.rs index ba7f085b55..843d35002e 100644 --- a/third_party/rust/glean-core/src/metrics/rate.rs +++ b/third_party/rust/glean-core/src/metrics/rate.rs @@ -141,6 +141,15 @@ impl RateMetric { /// Gets the currently stored value as a pair of integers. /// /// This doesn't clear the stored value. + /// + /// # Arguments + /// + /// * `ping_name` - the optional name of the ping to retrieve the metric + /// for. Defaults to the first value in `send_in_pings`. + /// + /// # Returns + /// + /// The stored value or `None` if nothing stored. pub fn test_get_value(&self, ping_name: Option<String>) -> Option<Rate> { crate::block_on_dispatcher(); crate::core::with_glean(|glean| self.get_value(glean, ping_name.as_deref())) @@ -175,8 +184,6 @@ impl RateMetric { /// # Arguments /// /// * `error` - The type of error - /// * `ping_name` - represents the optional name of the ping to retrieve the - /// metric for. Defaults to the first value in `send_in_pings`. /// /// # Returns /// diff --git a/third_party/rust/glean-core/src/metrics/string.rs b/third_party/rust/glean-core/src/metrics/string.rs index 5ed7b2c7f1..4aa30a8d7e 100644 --- a/third_party/rust/glean-core/src/metrics/string.rs +++ b/third_party/rust/glean-core/src/metrics/string.rs @@ -112,6 +112,15 @@ impl StringMetric { /// Gets the currently stored value as a string. /// /// This doesn't clear the stored value. + /// + /// # Arguments + /// + /// * `ping_name` - the optional name of the ping to retrieve the metric + /// for. Defaults to the first value in `send_in_pings`. + /// + /// # Returns + /// + /// The stored value or `None` if nothing stored. pub fn test_get_value(&self, ping_name: Option<String>) -> Option<String> { crate::block_on_dispatcher(); crate::core::with_glean(|glean| self.get_value(glean, ping_name.as_deref())) @@ -124,8 +133,6 @@ impl StringMetric { /// # Arguments /// /// * `error` - The type of error - /// * `ping_name` - represents the optional name of the ping to retrieve the - /// metric for. Defaults to the first value in `send_in_pings`. /// /// # Returns /// diff --git a/third_party/rust/glean-core/src/metrics/string_list.rs b/third_party/rust/glean-core/src/metrics/string_list.rs index 75b2df7f80..cd4e71b885 100644 --- a/third_party/rust/glean-core/src/metrics/string_list.rs +++ b/third_party/rust/glean-core/src/metrics/string_list.rs @@ -171,6 +171,15 @@ impl StringListMetric { /// Gets the currently-stored values. /// /// This doesn't clear the stored value. + /// + /// # Arguments + /// + /// * `ping_name` - the optional name of the ping to retrieve the metric + /// for. Defaults to the first value in `send_in_pings`. + /// + /// # Returns + /// + /// The stored value or `None` if nothing stored. pub fn test_get_value(&self, ping_name: Option<String>) -> Option<Vec<String>> { crate::block_on_dispatcher(); crate::core::with_glean(|glean| self.get_value(glean, ping_name.as_deref())) @@ -183,8 +192,6 @@ impl StringListMetric { /// # Arguments /// /// * `error` - The type of error - /// * `ping_name` - represents the optional name of the ping to retrieve the - /// metric for. Defaults to the first value in `send_in_pings`. /// /// # Returns /// diff --git a/third_party/rust/glean-core/src/metrics/text.rs b/third_party/rust/glean-core/src/metrics/text.rs index 06ad5c0d78..baa8e88d75 100644 --- a/third_party/rust/glean-core/src/metrics/text.rs +++ b/third_party/rust/glean-core/src/metrics/text.rs @@ -116,6 +116,15 @@ impl TextMetric { /// Gets the currently stored value as a string. /// /// This doesn't clear the stored value. + /// + /// # Arguments + /// + /// * `ping_name` - the optional name of the ping to retrieve the metric + /// for. Defaults to the first value in `send_in_pings`. + /// + /// # Returns + /// + /// The stored value or `None` if nothing stored. pub fn test_get_value(&self, ping_name: Option<String>) -> Option<String> { crate::block_on_dispatcher(); crate::core::with_glean(|glean| self.get_value(glean, ping_name.as_deref())) @@ -128,8 +137,6 @@ impl TextMetric { /// # Arguments /// /// * `error` - The type of error - /// * `ping_name` - represents the optional name of the ping to retrieve the - /// metric for. Defaults to the first value in `send_in_pings`. /// /// # Returns /// diff --git a/third_party/rust/glean-core/src/metrics/timespan.rs b/third_party/rust/glean-core/src/metrics/timespan.rs index b4d3bd5902..ee63fb52f8 100644 --- a/third_party/rust/glean-core/src/metrics/timespan.rs +++ b/third_party/rust/glean-core/src/metrics/timespan.rs @@ -253,6 +253,15 @@ impl TimespanMetric { /// Gets the currently stored value as an integer. /// /// This doesn't clear the stored value. + /// + /// # Arguments + /// + /// * `ping_name` - the optional name of the ping to retrieve the metric + /// for. Defaults to the first value in `send_in_pings`. + /// + /// # Returns + /// + /// The stored value or `None` if nothing stored. pub fn test_get_value(&self, ping_name: Option<String>) -> Option<i64> { crate::block_on_dispatcher(); crate::core::with_glean(|glean| { @@ -292,8 +301,6 @@ impl TimespanMetric { /// # Arguments /// /// * `error` - The type of error - /// * `ping_name` - represents the optional name of the ping to retrieve the - /// metric for. Defaults to the first value in `send_in_pings`. /// /// # Returns /// diff --git a/third_party/rust/glean-core/src/metrics/timing_distribution.rs b/third_party/rust/glean-core/src/metrics/timing_distribution.rs index e339ef8882..3293be9518 100644 --- a/third_party/rust/glean-core/src/metrics/timing_distribution.rs +++ b/third_party/rust/glean-core/src/metrics/timing_distribution.rs @@ -293,7 +293,35 @@ impl TimingDistributionMetric { /// are longer than `MAX_SAMPLE_TIME`. pub fn accumulate_samples(&self, samples: Vec<i64>) { let metric = self.clone(); - crate::launch_with_glean(move |glean| metric.accumulate_samples_sync(glean, samples)) + crate::launch_with_glean(move |glean| metric.accumulate_samples_sync(glean, &samples)) + } + + /// Accumulates precisely one signed sample and appends it to the metric. + /// + /// Precludes the need for a collection in the most common use case. + /// + /// Sign is required so that the platform-specific code can provide us with + /// a 64 bit signed integer if no `u64` comparable type is available. This + /// will take care of filtering and reporting errors for any provided negative + /// sample. + /// + /// Please note that this assumes that the provided sample is already in + /// the "unit" declared by the instance of the metric type (e.g. if the + /// instance this method was called on is using [`crate::TimeUnit::Second`], then + /// `sample` is assumed to be in that unit). + /// + /// # Arguments + /// + /// * `sample` - The singular sample to be recorded by the metric. + /// + /// ## Notes + /// + /// Discards any negative value and reports an [`ErrorType::InvalidValue`]. + /// Reports an [`ErrorType::InvalidOverflow`] error if the sample is longer than + /// `MAX_SAMPLE_TIME`. + pub fn accumulate_single_sample(&self, sample: i64) { + let metric = self.clone(); + crate::launch_with_glean(move |glean| metric.accumulate_samples_sync(glean, &[sample])) } /// **Test-only API (exported for testing purposes).** @@ -301,7 +329,7 @@ impl TimingDistributionMetric { /// /// Use [`accumulate_samples`](Self::accumulate_samples) #[doc(hidden)] - pub fn accumulate_samples_sync(&self, glean: &Glean, samples: Vec<i64>) { + pub fn accumulate_samples_sync(&self, glean: &Glean, samples: &[i64]) { if !self.should_record(glean) { return; } @@ -464,6 +492,15 @@ impl TimingDistributionMetric { /// Gets the currently stored value as an integer. /// /// This doesn't clear the stored value. + /// + /// # Arguments + /// + /// * `ping_name` - the optional name of the ping to retrieve the metric + /// for. Defaults to the first value in `send_in_pings`. + /// + /// # Returns + /// + /// The stored value or `None` if nothing stored. pub fn test_get_value(&self, ping_name: Option<String>) -> Option<DistributionData> { crate::block_on_dispatcher(); crate::core::with_glean(|glean| self.get_value(glean, ping_name.as_deref())) @@ -476,8 +513,6 @@ impl TimingDistributionMetric { /// # Arguments /// /// * `error` - The type of error - /// * `ping_name` - represents the optional name of the ping to retrieve the - /// metric for. Defaults to the first value in `send_in_pings`. /// /// # Returns /// diff --git a/third_party/rust/glean-core/src/metrics/url.rs b/third_party/rust/glean-core/src/metrics/url.rs index c9eb824a3e..48b3f9e7ae 100644 --- a/third_party/rust/glean-core/src/metrics/url.rs +++ b/third_party/rust/glean-core/src/metrics/url.rs @@ -131,6 +131,15 @@ impl UrlMetric { /// Gets the currently stored value as a string. /// /// This doesn't clear the stored value. + /// + /// # Arguments + /// + /// * `ping_name` - the optional name of the ping to retrieve the metric + /// for. Defaults to the first value in `send_in_pings`. + /// + /// # Returns + /// + /// The stored value or `None` if nothing stored. pub fn test_get_value(&self, ping_name: Option<String>) -> Option<String> { crate::block_on_dispatcher(); crate::core::with_glean(|glean| self.get_value(glean, ping_name.as_deref())) @@ -143,8 +152,6 @@ impl UrlMetric { /// # Arguments /// /// * `error` - The type of error - /// * `ping_name` - represents the optional name of the ping to retrieve the - /// metric for. Defaults to the first value in `send_in_pings`. /// /// # Returns /// diff --git a/third_party/rust/glean-core/src/metrics/uuid.rs b/third_party/rust/glean-core/src/metrics/uuid.rs index e78d15ad3b..77d0f82320 100644 --- a/third_party/rust/glean-core/src/metrics/uuid.rs +++ b/third_party/rust/glean-core/src/metrics/uuid.rs @@ -128,6 +128,15 @@ impl UuidMetric { /// Gets the currently stored value as a string. /// /// This doesn't clear the stored value. + /// + /// # Arguments + /// + /// * `ping_name` - the optional name of the ping to retrieve the metric + /// for. Defaults to the first value in `send_in_pings`. + /// + /// # Returns + /// + /// The stored value or `None` if nothing stored. pub fn test_get_value(&self, ping_name: Option<String>) -> Option<String> { crate::block_on_dispatcher(); crate::core::with_glean(|glean| { @@ -143,8 +152,6 @@ impl UuidMetric { /// # Arguments /// /// * `error` - The type of error - /// * `ping_name` - represents the optional name of the ping to retrieve the - /// metric for. Defaults to the first value in `send_in_pings`. /// /// # Returns /// diff --git a/third_party/rust/glean-core/src/ping/mod.rs b/third_party/rust/glean-core/src/ping/mod.rs index c22a890aa2..d1a67ae360 100644 --- a/third_party/rust/glean-core/src/ping/mod.rs +++ b/third_party/rust/glean-core/src/ping/mod.rs @@ -14,7 +14,7 @@ use serde_json::{json, Value as JsonValue}; use crate::common_metric_data::{CommonMetricData, Lifetime}; use crate::metrics::{CounterMetric, DatetimeMetric, Metric, MetricType, PingType, TimeUnit}; use crate::storage::{StorageManager, INTERNAL_STORAGE}; -use crate::upload::HeaderMap; +use crate::upload::{HeaderMap, PingMetadata}; use crate::util::{get_iso_time_string, local_now_with_offset}; use crate::{Glean, Result, DELETION_REQUEST_PINGS_DIRECTORY, PENDING_PINGS_DIRECTORY}; @@ -30,6 +30,8 @@ pub struct Ping<'a> { pub content: JsonValue, /// The headers to upload with the payload. pub headers: HeaderMap, + /// Whether the content contains {client|ping}_info sections. + pub includes_info_sections: bool, } /// Collect a ping's data, assemble it into its full payload and store it on disk. @@ -237,9 +239,9 @@ impl PingMaker { .snapshot_as_json(glean, ping.name(), true); // Due to the way the experimentation identifier could link datasets that are intentionally unlinked, - // it will not be included in pings that specifically exclude the Glean client-id and those pings that - // should not be sent if empty. - if (!ping.include_client_id() || !ping.send_if_empty()) + // it will not be included in pings that specifically exclude the Glean client-id, those pings that + // should not be sent if empty, or pings that exclude the {client|ping}_info sections wholesale. + if (!ping.include_client_id() || !ping.send_if_empty() || !ping.include_info_sections()) && glean.test_get_experimentation_id().is_some() && metrics_data.is_some() { @@ -285,13 +287,18 @@ impl PingMaker { TimeUnit::Minute }; - let ping_info = self.get_ping_info(glean, ping.name(), reason, precision); - let client_info = self.get_client_info(glean, ping.include_client_id()); + let mut json = if ping.include_info_sections() { + let ping_info = self.get_ping_info(glean, ping.name(), reason, precision); + let client_info = self.get_client_info(glean, ping.include_client_id()); + + json!({ + "ping_info": ping_info, + "client_info": client_info + }) + } else { + json!({}) + }; - let mut json = json!({ - "ping_info": ping_info, - "client_info": client_info - }); let json_obj = json.as_object_mut()?; if let Some(metrics_data) = metrics_data { json_obj.insert("metrics".to_string(), metrics_data); @@ -306,6 +313,7 @@ impl PingMaker { doc_id, url_path, headers: self.get_headers(glean), + includes_info_sections: ping.include_info_sections(), }) } @@ -355,11 +363,17 @@ impl PingMaker { file.write_all(ping.url_path.as_bytes())?; file.write_all(b"\n")?; file.write_all(::serde_json::to_string(&ping.content)?.as_bytes())?; - if !ping.headers.is_empty() { - file.write_all(b"\n{\"headers\":")?; - file.write_all(::serde_json::to_string(&ping.headers)?.as_bytes())?; - file.write_all(b"}")?; - } + file.write_all(b"\n")?; + let metadata = PingMetadata { + // We don't actually need to clone the headers except to match PingMetadata's ownership. + // But since we're going to write a file to disk in a sec, + // and HeaderMaps tend to have only like two things in them, tops, + // the cost is bearable. + headers: Some(ping.headers.clone()), + body_has_info_sections: Some(ping.includes_info_sections), + ping_name: Some(ping.name.to_string()), + }; + file.write_all(::serde_json::to_string(&metadata)?.as_bytes())?; } if let Err(e) = std::fs::rename(&temp_ping_path, &ping_path) { diff --git a/third_party/rust/glean-core/src/traits/custom_distribution.rs b/third_party/rust/glean-core/src/traits/custom_distribution.rs index c0c80c028b..43dfdb7da8 100644 --- a/third_party/rust/glean-core/src/traits/custom_distribution.rs +++ b/third_party/rust/glean-core/src/traits/custom_distribution.rs @@ -28,6 +28,22 @@ pub trait CustomDistribution { /// them. fn accumulate_samples_signed(&self, samples: Vec<i64>); + /// Accumulates precisely one signed sample in the metric. + /// + /// This is required so that the platform-specific code can provide us with a + /// 64 bit signed integer if no `u64` comparable type is available. This + /// will take care of filtering and reporting errors. + /// + /// # Arguments + /// + /// - `sample` - The singular sample to be recorded by the metric. + /// + /// ## Notes + /// + /// Discards any negative value of `sample` and reports an + /// [`ErrorType::InvalidValue`](crate::ErrorType::InvalidValue). + fn accumulate_single_sample_signed(&self, sample: i64); + /// **Exported for test purposes.** /// /// Gets the currently stored histogram. diff --git a/third_party/rust/glean-core/src/traits/mod.rs b/third_party/rust/glean-core/src/traits/mod.rs index c4bcf7cdd6..4115609fdd 100644 --- a/third_party/rust/glean-core/src/traits/mod.rs +++ b/third_party/rust/glean-core/src/traits/mod.rs @@ -7,6 +7,10 @@ //! Individual metric types implement this trait to expose the specific metrics API. //! It can be used by wrapping implementations to guarantee API conformance. +/// Re-export for use in generated code. +#[doc(hidden)] +pub extern crate serde as __serde; + mod boolean; mod counter; mod custom_distribution; @@ -15,6 +19,7 @@ mod event; mod labeled; mod memory_distribution; mod numerator; +mod object; mod ping; mod quantity; mod rate; @@ -37,6 +42,7 @@ pub use self::event::NoExtraKeys; pub use self::labeled::Labeled; pub use self::memory_distribution::MemoryDistribution; pub use self::numerator::Numerator; +pub use self::object::{ObjectError, ObjectSerialize}; pub use self::ping::Ping; pub use self::quantity::Quantity; pub use self::rate::Rate; diff --git a/third_party/rust/glean-core/src/traits/object.rs b/third_party/rust/glean-core/src/traits/object.rs new file mode 100644 index 0000000000..c579efeac7 --- /dev/null +++ b/third_party/rust/glean-core/src/traits/object.rs @@ -0,0 +1,53 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. + +use std::fmt::Display; + +use serde::{Deserialize, Serialize}; +use serde_json::Value as JsonValue; + +/// This type represents all possible errors that can occur when serializing or deserializing an object from/to JSON. +#[derive(Debug)] +pub struct ObjectError(serde_json::Error); + +impl Display for ObjectError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + Display::fmt(&self.0, f) + } +} + +impl std::error::Error for ObjectError {} + +/// An object that can be serialized into JSON. +/// +/// Objects are defined by their structure in the metrics definition. +/// +/// This is essentially a wrapper around serde's `Serialize`/`Deserialize`, +/// but in a way we can name it for our JSON (de)serialization. +pub trait ObjectSerialize { + /// Deserialize the object from its JSON representation. + /// + /// Returns an error if deserialization fails. + /// This should not happen for glean_parser-generated and later serialized objects. + fn from_str(obj: &str) -> Result<Self, ObjectError> + where + Self: Sized; + + /// Serialize this object into a JSON string. + fn into_serialized_object(self) -> Result<JsonValue, ObjectError>; +} + +impl<V> ObjectSerialize for V +where + V: Serialize, + V: for<'de> Deserialize<'de>, +{ + fn from_str(obj: &str) -> Result<Self, ObjectError> { + serde_json::from_str(obj).map_err(ObjectError) + } + + fn into_serialized_object(self) -> Result<JsonValue, ObjectError> { + serde_json::to_value(self).map_err(ObjectError) + } +} diff --git a/third_party/rust/glean-core/src/traits/timing_distribution.rs b/third_party/rust/glean-core/src/traits/timing_distribution.rs index 03083753c6..ba618d2b4b 100644 --- a/third_party/rust/glean-core/src/traits/timing_distribution.rs +++ b/third_party/rust/glean-core/src/traits/timing_distribution.rs @@ -66,6 +66,31 @@ pub trait TimingDistribution { /// are longer than `MAX_SAMPLE_TIME`. fn accumulate_samples(&self, samples: Vec<i64>); + /// Accumulates precisely one signed sample in the metric. + /// + /// Precludes the need for a collection in the most common use case. + /// + /// Sign is required so that the platform-specific code can provide us with + /// a 64 bit signed integer if no `u64` comparable type is available. This + /// will take care of filtering and reporting errors for any provided negative + /// sample. + /// + /// Please note that this assumes that the provided sample is already in + /// the "unit" declared by the instance of the metric type (e.g. if the + /// instance this method was called on is using [`crate::TimeUnit::Second`], then + /// `sample` is assumed to be in that unit). + /// + /// # Arguments + /// + /// * `sample` - The singular sample to be recorded by the metric. + /// + /// ## Notes + /// + /// Discards any negative value and reports an [`ErrorType::InvalidValue`]. + /// Reports an [`ErrorType::InvalidOverflow`] error if the sample is longer than + /// `MAX_SAMPLE_TIME`. + fn accumulate_single_sample(&self, sample: i64); + /// Accumulates the provided samples in the metric. /// /// # Arguments diff --git a/third_party/rust/glean-core/src/upload/directory.rs b/third_party/rust/glean-core/src/upload/directory.rs index a78bbf0bdb..706550fe6c 100644 --- a/third_party/rust/glean-core/src/upload/directory.rs +++ b/third_party/rust/glean-core/src/upload/directory.rs @@ -9,15 +9,28 @@ use std::fs::{self, File}; use std::io::{BufRead, BufReader}; use std::path::{Path, PathBuf}; -use serde::Deserialize; +use serde::{Deserialize, Serialize}; use uuid::Uuid; use super::request::HeaderMap; use crate::{DELETION_REQUEST_PINGS_DIRECTORY, PENDING_PINGS_DIRECTORY}; /// A representation of the data extracted from a ping file, -/// this will contain the document_id, path, JSON encoded body of a ping and the persisted headers. -pub type PingPayload = (String, String, String, Option<HeaderMap>); +#[derive(Clone, Debug, Default)] +pub struct PingPayload { + /// The ping's doc_id. + pub document_id: String, + /// The path to upload the ping to. + pub upload_path: String, + /// The ping body as JSON-encoded string. + pub json_body: String, + /// HTTP headers to include in the upload request. + pub headers: Option<HeaderMap>, + /// Whether the ping body contains {client|ping}_info + pub body_has_info_sections: bool, + /// The ping's name. (Also likely in the upload_path.) + pub ping_name: String, +} /// A struct to hold the result of scanning all pings directories. #[derive(Clone, Debug, Default)] @@ -62,20 +75,28 @@ fn get_file_name_as_str(path: &Path) -> Option<&str> { } } +/// A ping's metadata, as (optionally) represented on disk. +/// +/// Anything that isn't the upload path or the ping body. +#[derive(Default, Deserialize, Serialize)] +pub struct PingMetadata { + /// HTTP headers to include when uploading the ping. + pub headers: Option<HeaderMap>, + /// Whether the body has {client|ping}_info sections. + pub body_has_info_sections: Option<bool>, + /// The name of the ping. + pub ping_name: Option<String>, +} + /// Processes a ping's metadata. /// /// The metadata is an optional third line in the ping file, /// currently it contains only additonal headers to be added to each ping request. /// Therefore, we will process the contents of this line /// and return a HeaderMap of the persisted headers. -fn process_metadata(path: &str, metadata: &str) -> Option<HeaderMap> { - #[derive(Deserialize)] - struct PingMetadata { - pub headers: HeaderMap, - } - +fn process_metadata(path: &str, metadata: &str) -> Option<PingMetadata> { if let Ok(metadata) = serde_json::from_str::<PingMetadata>(metadata) { - return Some(metadata.headers); + return Some(metadata); } else { log::warn!("Error while parsing ping metadata: {}", path); } @@ -171,8 +192,23 @@ impl PingDirectoryManager { if let (Some(Ok(path)), Some(Ok(body)), Ok(metadata)) = (lines.next(), lines.next(), lines.next().transpose()) { - let headers = metadata.and_then(|m| process_metadata(&path, &m)); - return Some((document_id.into(), path, body, headers)); + let PingMetadata { + headers, + body_has_info_sections, + ping_name, + } = metadata + .and_then(|m| process_metadata(&path, &m)) + .unwrap_or_default(); + let ping_name = + ping_name.unwrap_or_else(|| path.split('/').nth(3).unwrap_or("").into()); + return Some(PingPayload { + document_id: document_id.into(), + upload_path: path, + json_body: body, + headers, + body_has_info_sections: body_has_info_sections.unwrap_or(true), + ping_name, + }); } else { log::warn!( "Error processing ping file: {}. Ping file is not formatted as expected.", @@ -303,7 +339,7 @@ mod test { let (mut glean, dir) = new_glean(None); // Register a ping for testing - let ping_type = PingType::new("test", true, true, true, vec![]); + let ping_type = PingType::new("test", true, true, true, true, vec![]); glean.register_ping_type(&ping_type); // Submit the ping to populate the pending_pings directory @@ -320,7 +356,8 @@ mod test { // Verify request was returned for the "test" ping let ping = &data.pending_pings[0].1; - let request_ping_type = ping.1.split('/').nth(3).unwrap(); + let request_ping_type = ping.upload_path.split('/').nth(3).unwrap(); + assert_eq!(request_ping_type, ping.ping_name); assert_eq!(request_ping_type, "test"); } @@ -329,7 +366,7 @@ mod test { let (mut glean, dir) = new_glean(None); // Register a ping for testing - let ping_type = PingType::new("test", true, true, true, vec![]); + let ping_type = PingType::new("test", true, true, true, true, vec![]); glean.register_ping_type(&ping_type); // Submit the ping to populate the pending_pings directory @@ -352,7 +389,8 @@ mod test { // Verify request was returned for the "test" ping let ping = &data.pending_pings[0].1; - let request_ping_type = ping.1.split('/').nth(3).unwrap(); + let request_ping_type = ping.upload_path.split('/').nth(3).unwrap(); + assert_eq!(request_ping_type, ping.ping_name); assert_eq!(request_ping_type, "test"); // Verify that file was indeed deleted @@ -364,7 +402,7 @@ mod test { let (mut glean, dir) = new_glean(None); // Register a ping for testing - let ping_type = PingType::new("test", true, true, true, vec![]); + let ping_type = PingType::new("test", true, true, true, true, vec![]); glean.register_ping_type(&ping_type); // Submit the ping to populate the pending_pings directory @@ -387,7 +425,8 @@ mod test { // Verify request was returned for the "test" ping let ping = &data.pending_pings[0].1; - let request_ping_type = ping.1.split('/').nth(3).unwrap(); + let request_ping_type = ping.upload_path.split('/').nth(3).unwrap(); + assert_eq!(request_ping_type, ping.ping_name); assert_eq!(request_ping_type, "test"); // Verify that file was indeed deleted @@ -414,7 +453,8 @@ mod test { // Verify request was returned for the "deletion-request" ping let ping = &data.deletion_request_pings[0].1; - let request_ping_type = ping.1.split('/').nth(3).unwrap(); + let request_ping_type = ping.upload_path.split('/').nth(3).unwrap(); + assert_eq!(request_ping_type, ping.ping_name); assert_eq!(request_ping_type, "deletion-request"); } } diff --git a/third_party/rust/glean-core/src/upload/mod.rs b/third_party/rust/glean-core/src/upload/mod.rs index d764dcd29e..e51a9d9508 100644 --- a/third_party/rust/glean-core/src/upload/mod.rs +++ b/third_party/rust/glean-core/src/upload/mod.rs @@ -30,6 +30,7 @@ use directory::{PingDirectoryManager, PingPayloadsByDirectory}; use policy::Policy; use request::create_date_header_value; +pub use directory::{PingMetadata, PingPayload}; pub use request::{HeaderMap, PingRequest}; pub use result::{UploadResult, UploadTaskAction}; @@ -322,21 +323,24 @@ impl PingUploadManager { /// /// Returns the `PingRequest` or `None` if unable to build, /// in which case it will delete the ping file and record an error. - fn build_ping_request( - &self, - glean: &Glean, - document_id: &str, - path: &str, - body: &str, - headers: Option<HeaderMap>, - ) -> Option<PingRequest> { + fn build_ping_request(&self, glean: &Glean, ping: PingPayload) -> Option<PingRequest> { + let PingPayload { + document_id, + upload_path: path, + json_body: body, + headers, + body_has_info_sections, + ping_name, + } = ping; let mut request = PingRequest::builder( &self.language_binding_name, self.policy.max_ping_body_size(), ) - .document_id(document_id) + .document_id(&document_id) .path(path) - .body(body); + .body(body) + .body_has_info_sections(body_has_info_sections) + .ping_name(ping_name); if let Some(headers) = headers { request = request.headers(headers); @@ -346,7 +350,7 @@ impl PingUploadManager { Ok(request) => Some(request), Err(e) => { log::warn!("Error trying to build ping request: {}", e); - self.directory_manager.delete_file(document_id); + self.directory_manager.delete_file(&document_id); // Record the error. // Currently the only possible error is PingBodyOverflow. @@ -362,23 +366,21 @@ impl PingUploadManager { } /// Enqueue a ping for upload. - pub fn enqueue_ping( - &self, - glean: &Glean, - document_id: &str, - path: &str, - body: &str, - headers: Option<HeaderMap>, - ) { + pub fn enqueue_ping(&self, glean: &Glean, ping: PingPayload) { let mut queue = self .queue .write() .expect("Can't write to pending pings queue."); + let PingPayload { + ref document_id, + upload_path: ref path, + .. + } = ping; // Checks if a ping with this `document_id` is already enqueued. if queue .iter() - .any(|request| request.document_id == document_id) + .any(|request| request.document_id.as_str() == document_id) { log::warn!( "Attempted to enqueue a duplicate ping {} at {}.", @@ -404,7 +406,7 @@ impl PingUploadManager { } log::trace!("Enqueuing ping {} at {}", document_id, path); - if let Some(request) = self.build_ping_request(glean, document_id, path, body, headers) { + if let Some(request) = self.build_ping_request(glean, ping) { queue.push_back(request) } } @@ -455,7 +457,7 @@ impl PingUploadManager { // Thus, we reverse the order of the pending pings vector, // so that we iterate in descending order (newest -> oldest). cached_pings.pending_pings.reverse(); - cached_pings.pending_pings.retain(|(file_size, (document_id, _, _, _))| { + cached_pings.pending_pings.retain(|(file_size, PingPayload {document_id, ..})| { pending_pings_count += 1; pending_pings_directory_size += file_size; @@ -493,14 +495,14 @@ impl PingUploadManager { // Enqueue the remaining pending pings and // enqueue all deletion-request pings. - let deletion_request_pings = cached_pings.deletion_request_pings.drain(..); - for (_, (document_id, path, body, headers)) in deletion_request_pings { - self.enqueue_ping(glean, &document_id, &path, &body, headers); - } - let pending_pings = cached_pings.pending_pings.drain(..); - for (_, (document_id, path, body, headers)) in pending_pings { - self.enqueue_ping(glean, &document_id, &path, &body, headers); - } + cached_pings + .deletion_request_pings + .drain(..) + .for_each(|(_, ping)| self.enqueue_ping(glean, ping)); + cached_pings + .pending_pings + .drain(..) + .for_each(|(_, ping)| self.enqueue_ping(glean, ping)); } } @@ -532,10 +534,8 @@ impl PingUploadManager { /// * `glean` - The Glean object holding the database. /// * `document_id` - The UUID of the ping in question. pub fn enqueue_ping_from_file(&self, glean: &Glean, document_id: &str) { - if let Some((doc_id, path, body, headers)) = - self.directory_manager.process_file(document_id) - { - self.enqueue_ping(glean, &doc_id, &path, &body, headers) + if let Some(ping) = self.directory_manager.process_file(document_id) { + self.enqueue_ping(glean, ping); } } @@ -883,7 +883,17 @@ mod test { let upload_manager = PingUploadManager::no_policy(dir.path()); // Enqueue a ping - upload_manager.enqueue_ping(&glean, &Uuid::new_v4().to_string(), PATH, "", None); + upload_manager.enqueue_ping( + &glean, + PingPayload { + document_id: Uuid::new_v4().to_string(), + upload_path: PATH.into(), + json_body: "".into(), + headers: None, + body_has_info_sections: true, + ping_name: "ping-name".into(), + }, + ); // Try and get the next request. // Verify request was returned @@ -900,7 +910,17 @@ mod test { // Enqueue a ping multiple times let n = 10; for _ in 0..n { - upload_manager.enqueue_ping(&glean, &Uuid::new_v4().to_string(), PATH, "", None); + upload_manager.enqueue_ping( + &glean, + PingPayload { + document_id: Uuid::new_v4().to_string(), + upload_path: PATH.into(), + json_body: "".into(), + headers: None, + body_has_info_sections: true, + ping_name: "ping-name".into(), + }, + ); } // Verify a request is returned for each submitted ping @@ -928,7 +948,17 @@ mod test { // Enqueue the max number of pings allowed per uploading window for _ in 0..max_pings_per_interval { - upload_manager.enqueue_ping(&glean, &Uuid::new_v4().to_string(), PATH, "", None); + upload_manager.enqueue_ping( + &glean, + PingPayload { + document_id: Uuid::new_v4().to_string(), + upload_path: PATH.into(), + json_body: "".into(), + headers: None, + body_has_info_sections: true, + ping_name: "ping-name".into(), + }, + ); } // Verify a request is returned for each submitted ping @@ -938,7 +968,17 @@ mod test { } // Enqueue just one more ping - upload_manager.enqueue_ping(&glean, &Uuid::new_v4().to_string(), PATH, "", None); + upload_manager.enqueue_ping( + &glean, + PingPayload { + document_id: Uuid::new_v4().to_string(), + upload_path: PATH.into(), + json_body: "".into(), + headers: None, + body_has_info_sections: true, + ping_name: "ping-name".into(), + }, + ); // Verify that we are indeed told to wait because we are at capacity match upload_manager.get_upload_task(&glean, false) { @@ -961,7 +1001,17 @@ mod test { // Enqueue a ping multiple times for _ in 0..10 { - upload_manager.enqueue_ping(&glean, &Uuid::new_v4().to_string(), PATH, "", None); + upload_manager.enqueue_ping( + &glean, + PingPayload { + document_id: Uuid::new_v4().to_string(), + upload_path: PATH.into(), + json_body: "".into(), + headers: None, + body_has_info_sections: true, + ping_name: "ping-name".into(), + }, + ); } // Clear the queue @@ -979,7 +1029,14 @@ mod test { let (mut glean, _t) = new_glean(None); // Register a ping for testing - let ping_type = PingType::new("test", true, /* send_if_empty */ true, true, vec![]); + let ping_type = PingType::new( + "test", + true, + /* send_if_empty */ true, + true, + true, + vec![], + ); glean.register_ping_type(&ping_type); // Submit the ping multiple times @@ -1011,7 +1068,14 @@ mod test { let (mut glean, dir) = new_glean(None); // Register a ping for testing - let ping_type = PingType::new("test", true, /* send_if_empty */ true, true, vec![]); + let ping_type = PingType::new( + "test", + true, + /* send_if_empty */ true, + true, + true, + vec![], + ); glean.register_ping_type(&ping_type); // Submit the ping multiple times @@ -1041,7 +1105,14 @@ mod test { let (mut glean, dir) = new_glean(None); // Register a ping for testing - let ping_type = PingType::new("test", true, /* send_if_empty */ true, true, vec![]); + let ping_type = PingType::new( + "test", + true, + /* send_if_empty */ true, + true, + true, + vec![], + ); glean.register_ping_type(&ping_type); // Submit a ping @@ -1071,7 +1142,14 @@ mod test { let (mut glean, dir) = new_glean(None); // Register a ping for testing - let ping_type = PingType::new("test", true, /* send_if_empty */ true, true, vec![]); + let ping_type = PingType::new( + "test", + true, + /* send_if_empty */ true, + true, + true, + vec![], + ); glean.register_ping_type(&ping_type); // Submit a ping @@ -1101,7 +1179,14 @@ mod test { let (mut glean, _t) = new_glean(None); // Register a ping for testing - let ping_type = PingType::new("test", true, /* send_if_empty */ true, true, vec![]); + let ping_type = PingType::new( + "test", + true, + /* send_if_empty */ true, + true, + true, + vec![], + ); glean.register_ping_type(&ping_type); // Submit a ping @@ -1133,7 +1218,14 @@ mod test { let (mut glean, dir) = new_glean(None); // Register a ping for testing - let ping_type = PingType::new("test", true, /* send_if_empty */ true, true, vec![]); + let ping_type = PingType::new( + "test", + true, + /* send_if_empty */ true, + true, + true, + vec![], + ); glean.register_ping_type(&ping_type); // Submit a ping @@ -1174,7 +1266,17 @@ mod test { let path2 = format!("/submit/app_id/test-ping/1/{}", doc2); // Enqueue a ping - upload_manager.enqueue_ping(&glean, &doc1, &path1, "", None); + upload_manager.enqueue_ping( + &glean, + PingPayload { + document_id: doc1.clone(), + upload_path: path1, + json_body: "".into(), + headers: None, + body_has_info_sections: true, + ping_name: "test-ping".into(), + }, + ); // Try and get the first request. let req = match upload_manager.get_upload_task(&glean, false) { @@ -1184,7 +1286,17 @@ mod test { assert_eq!(doc1, req.document_id); // Schedule the next one while the first one is "in progress" - upload_manager.enqueue_ping(&glean, &doc2, &path2, "", None); + upload_manager.enqueue_ping( + &glean, + PingPayload { + document_id: doc2.clone(), + upload_path: path2, + json_body: "".into(), + headers: None, + body_has_info_sections: true, + ping_name: "test-ping".into(), + }, + ); // Mark as processed upload_manager.process_ping_upload_response( @@ -1221,7 +1333,14 @@ mod test { glean.set_debug_view_tag("valid-tag"); // Register a ping for testing - let ping_type = PingType::new("test", true, /* send_if_empty */ true, true, vec![]); + let ping_type = PingType::new( + "test", + true, + /* send_if_empty */ true, + true, + true, + vec![], + ); glean.register_ping_type(&ping_type); // Submit a ping @@ -1248,8 +1367,28 @@ mod test { let path = format!("/submit/app_id/test-ping/1/{}", doc_id); // Try to enqueue a ping with the same doc_id twice - upload_manager.enqueue_ping(&glean, &doc_id, &path, "", None); - upload_manager.enqueue_ping(&glean, &doc_id, &path, "", None); + upload_manager.enqueue_ping( + &glean, + PingPayload { + document_id: doc_id.clone(), + upload_path: path.clone(), + json_body: "".into(), + headers: None, + body_has_info_sections: true, + ping_name: "test-ping".into(), + }, + ); + upload_manager.enqueue_ping( + &glean, + PingPayload { + document_id: doc_id, + upload_path: path, + json_body: "".into(), + headers: None, + body_has_info_sections: true, + ping_name: "test-ping".into(), + }, + ); // Get a task once let task = upload_manager.get_upload_task(&glean, false); @@ -1267,7 +1406,14 @@ mod test { let (mut glean, dir) = new_glean(None); // Register a ping for testing - let ping_type = PingType::new("test", true, /* send_if_empty */ true, true, vec![]); + let ping_type = PingType::new( + "test", + true, + /* send_if_empty */ true, + true, + true, + vec![], + ); glean.register_ping_type(&ping_type); // Submit the ping multiple times @@ -1317,7 +1463,14 @@ mod test { let (mut glean, dir) = new_glean(None); // Register a ping for testing - let ping_type = PingType::new("test", true, /* send_if_empty */ true, true, vec![]); + let ping_type = PingType::new( + "test", + true, + /* send_if_empty */ true, + true, + true, + vec![], + ); glean.register_ping_type(&ping_type); // Submit the ping multiple times @@ -1331,7 +1484,10 @@ mod test { // The pending pings array is sorted by date in ascending order, // the newest element is the last one. let (_, newest_ping) = &pending_pings.last().unwrap(); - let (newest_ping_id, _, _, _) = &newest_ping; + let PingPayload { + document_id: newest_ping_id, + .. + } = &newest_ping; // Create a new upload manager pointing to the same data_path as the glean instance. let mut upload_manager = PingUploadManager::no_policy(dir.path()); @@ -1385,7 +1541,14 @@ mod test { let (mut glean, dir) = new_glean(None); // Register a ping for testing - let ping_type = PingType::new("test", true, /* send_if_empty */ true, true, vec![]); + let ping_type = PingType::new( + "test", + true, + /* send_if_empty */ true, + true, + true, + vec![], + ); glean.register_ping_type(&ping_type); // How many pings we allow at maximum @@ -1406,7 +1569,7 @@ mod test { .iter() .rev() .take(count_quota) - .map(|(_, ping)| ping.0.clone()) + .map(|(_, ping)| ping.document_id.clone()) .collect::<Vec<_>>(); // Create a new upload manager pointing to the same data_path as the glean instance. @@ -1457,7 +1620,14 @@ mod test { let (mut glean, dir) = new_glean(None); // Register a ping for testing - let ping_type = PingType::new("test", true, /* send_if_empty */ true, true, vec![]); + let ping_type = PingType::new( + "test", + true, + /* send_if_empty */ true, + true, + true, + vec![], + ); glean.register_ping_type(&ping_type); let expected_number_of_pings = 3; @@ -1477,7 +1647,7 @@ mod test { .iter() .rev() .take(expected_number_of_pings) - .map(|(_, ping)| ping.0.clone()) + .map(|(_, ping)| ping.document_id.clone()) .collect::<Vec<_>>(); // Create a new upload manager pointing to the same data_path as the glean instance. @@ -1531,7 +1701,14 @@ mod test { let (mut glean, dir) = new_glean(None); // Register a ping for testing - let ping_type = PingType::new("test", true, /* send_if_empty */ true, true, vec![]); + let ping_type = PingType::new( + "test", + true, + /* send_if_empty */ true, + true, + true, + vec![], + ); glean.register_ping_type(&ping_type); let expected_number_of_pings = 2; @@ -1551,7 +1728,7 @@ mod test { .iter() .rev() .take(expected_number_of_pings) - .map(|(_, ping)| ping.0.clone()) + .map(|(_, ping)| ping.document_id.clone()) .collect::<Vec<_>>(); // Create a new upload manager pointing to the same data_path as the glean instance. @@ -1622,8 +1799,28 @@ mod test { upload_manager.set_rate_limiter(secs_per_interval, max_pings_per_interval); // Enqueue two pings - upload_manager.enqueue_ping(&glean, &Uuid::new_v4().to_string(), PATH, "", None); - upload_manager.enqueue_ping(&glean, &Uuid::new_v4().to_string(), PATH, "", None); + upload_manager.enqueue_ping( + &glean, + PingPayload { + document_id: Uuid::new_v4().to_string(), + upload_path: PATH.into(), + json_body: "".into(), + headers: None, + body_has_info_sections: true, + ping_name: "ping-name".into(), + }, + ); + upload_manager.enqueue_ping( + &glean, + PingPayload { + document_id: Uuid::new_v4().to_string(), + upload_path: PATH.into(), + json_body: "".into(), + headers: None, + body_has_info_sections: true, + ping_name: "ping-name".into(), + }, + ); // Get the first ping, it should be returned normally. match upload_manager.get_upload_task(&glean, false) { @@ -1679,12 +1876,28 @@ mod test { let upload_manager = PingUploadManager::no_policy(dir.path()); // Enqueue a ping and start processing it - let identifier = &Uuid::new_v4().to_string(); - upload_manager.enqueue_ping(&glean, identifier, PATH, "", None); + let identifier = &Uuid::new_v4(); + let ping = PingPayload { + document_id: identifier.to_string(), + upload_path: PATH.into(), + json_body: "".into(), + headers: None, + body_has_info_sections: true, + ping_name: "ping-name".into(), + }; + upload_manager.enqueue_ping(&glean, ping); assert!(upload_manager.get_upload_task(&glean, false).is_upload()); // Attempt to re-enqueue the same ping - upload_manager.enqueue_ping(&glean, identifier, PATH, "", None); + let ping = PingPayload { + document_id: identifier.to_string(), + upload_path: PATH.into(), + json_body: "".into(), + headers: None, + body_has_info_sections: true, + ping_name: "ping-name".into(), + }; + upload_manager.enqueue_ping(&glean, ping); // No new pings should have been enqueued so the upload task is Done. assert_eq!( @@ -1695,7 +1908,7 @@ mod test { // Process the upload response upload_manager.process_ping_upload_response( &glean, - identifier, + &identifier.to_string(), UploadResult::http_status(200), ); } diff --git a/third_party/rust/glean-core/src/upload/request.rs b/third_party/rust/glean-core/src/upload/request.rs index 0fd5ec5713..b4ac6eba97 100644 --- a/third_party/rust/glean-core/src/upload/request.rs +++ b/third_party/rust/glean-core/src/upload/request.rs @@ -62,6 +62,8 @@ pub struct Builder { body: Option<Vec<u8>>, headers: HeaderMap, body_max_size: usize, + body_has_info_sections: Option<bool>, + ping_name: Option<String>, } impl Builder { @@ -87,6 +89,8 @@ impl Builder { body: None, headers, body_max_size, + body_has_info_sections: None, + ping_name: None, } } @@ -138,6 +142,18 @@ impl Builder { self } + /// Sets whether the request body has {client|ping}_info sections. + pub fn body_has_info_sections(mut self, body_has_info_sections: bool) -> Self { + self.body_has_info_sections = Some(body_has_info_sections); + self + } + + /// Sets the ping's name aka doctype. + pub fn ping_name<S: Into<String>>(mut self, ping_name: S) -> Self { + self.ping_name = Some(ping_name.into()); + self + } + /// Sets a header for this request. pub fn header<S: Into<String>>(mut self, key: S, value: S) -> Self { self.headers.insert(key.into(), value.into()); @@ -174,6 +190,12 @@ impl Builder { .expect("path must be set before attempting to build PingRequest"), body, headers: self.headers, + body_has_info_sections: self.body_has_info_sections.expect( + "body_has_info_sections must be set before attempting to build PingRequest", + ), + ping_name: self + .ping_name + .expect("ping_name must be set before attempting to build PingRequest"), }) } } @@ -192,6 +214,10 @@ pub struct PingRequest { pub body: Vec<u8>, /// A map with all the headers to be sent with the request. pub headers: HeaderMap, + /// Whether the body has {client|ping}_info sections. + pub body_has_info_sections: bool, + /// The ping's name. Likely also somewhere in `path`. + pub ping_name: String, } impl PingRequest { @@ -208,12 +234,7 @@ impl PingRequest { /// Verifies if current request is for a deletion-request ping. pub fn is_deletion_request(&self) -> bool { - // The path format should be `/submit/<app_id>/<ping_name>/<schema_version/<doc_id>` - self.path - .split('/') - .nth(3) - .map(|url| url == "deletion-request") - .unwrap_or(false) + self.ping_name == "deletion-request" } /// Decompresses and pretty-format the ping payload @@ -257,11 +278,15 @@ mod test { .document_id("woop") .path("/random/path/doesnt/matter") .body("{}") + .body_has_info_sections(false) + .ping_name("whatevs") .build() .unwrap(); assert_eq!(request.document_id, "woop"); assert_eq!(request.path, "/random/path/doesnt/matter"); + assert!(!request.body_has_info_sections); + assert_eq!(request.ping_name, "whatevs"); // Make sure all the expected headers were added. assert!(request.headers.contains_key("X-Telemetry-Agent")); |