glean/private/ping.rs
1// This Source Code Form is subject to the terms of the Mozilla Public
2// License, v. 2.0. If a copy of the MPL was not distributed with this
3// file, You can obtain one at https://mozilla.org/MPL/2.0/.
4
5use std::{
6 mem,
7 sync::{Arc, Mutex},
8};
9
10use malloc_size_of::MallocSizeOf;
11
12type BoxedCallback = Box<dyn FnOnce(Option<&str>) + Send + 'static>;
13
14/// A ping is a bundle of related metrics, gathered in a payload to be transmitted.
15///
16/// The ping payload will be encoded in JSON format and contains shared information data.
17#[derive(Clone)]
18pub struct PingType {
19 pub(crate) inner: glean_core::metrics::PingType,
20
21 /// **Test-only API**
22 ///
23 /// A function to be called right before a ping is submitted.
24 test_callback: Arc<Mutex<Option<BoxedCallback>>>,
25}
26
27impl MallocSizeOf for PingType {
28 fn size_of(&self, ops: &mut malloc_size_of::MallocSizeOfOps) -> usize {
29 self.inner.size_of(ops)
30 + self
31 .test_callback
32 .lock()
33 .unwrap()
34 .as_ref()
35 .map(|cb| mem::size_of_val(cb))
36 .unwrap_or(0)
37 }
38}
39
40impl PingType {
41 /// Creates a new ping type.
42 ///
43 /// # Arguments
44 ///
45 /// * `name` - The name of the ping.
46 /// * `include_client_id` - Whether to include the client ID in the assembled ping when.
47 /// * `send_if_empty` - Whether the ping should be sent empty or not.
48 /// * `precise_timestamps` - Whether the ping should use precise timestamps for the start and end time.
49 /// * `include_info_sections` - Whether the ping should include the client/ping_info sections.
50 /// * `enabled` - Whether or not this ping is enabled. Note: Data that would be sent on a disabled
51 /// ping will still be collected and is discarded instead of being submitted.
52 /// * `schedules_pings` - A list of pings which are triggered for submission when this ping is
53 /// submitted.
54 /// * `reason_codes` - The valid reason codes for this ping.
55 /// * `uploader_capabilities` - The capabilities required during this ping's upload.
56 #[allow(clippy::too_many_arguments)]
57 pub fn new<A: Into<String>>(
58 name: A,
59 include_client_id: bool,
60 send_if_empty: bool,
61 precise_timestamps: bool,
62 include_info_sections: bool,
63 enabled: bool,
64 schedules_pings: Vec<String>,
65 reason_codes: Vec<String>,
66 follows_collection_enabled: bool,
67 uploader_capabilities: Vec<String>,
68 ) -> Self {
69 let inner = glean_core::metrics::PingType::new(
70 name.into(),
71 include_client_id,
72 send_if_empty,
73 precise_timestamps,
74 include_info_sections,
75 enabled,
76 schedules_pings,
77 reason_codes,
78 follows_collection_enabled,
79 uploader_capabilities,
80 );
81
82 Self {
83 inner,
84 test_callback: Arc::new(Default::default()),
85 }
86 }
87
88 /// Enable or disable a ping.
89 ///
90 /// Disabling a ping causes all data for that ping to be removed from storage
91 /// and all pending pings of that type to be deleted.
92 pub fn set_enabled(&self, enabled: bool) {
93 self.inner.set_enabled(enabled)
94 }
95
96 /// Submits the ping for eventual uploading.
97 ///
98 /// The ping content is assembled as soon as possible, but upload is not
99 /// guaranteed to happen immediately, as that depends on the upload policies.
100 ///
101 /// If the ping currently contains no content, it will not be sent,
102 /// unless it is configured to be sent if empty.
103 ///
104 /// # Arguments
105 ///
106 /// * `reason` - the reason the ping was triggered. Included in the
107 /// `ping_info.reason` part of the payload.
108 pub fn submit(&self, reason: Option<&str>) {
109 let mut cb = self.test_callback.lock().unwrap();
110 let cb = cb.take();
111 if let Some(cb) = cb {
112 cb(reason)
113 }
114
115 self.inner.submit(reason.map(|s| s.to_string()))
116 }
117
118 /// Get the name of this Ping
119 pub fn name(&self) -> &str {
120 self.inner.name()
121 }
122
123 /// Whether the client ID will be included in the assembled ping when submitting.
124 pub fn include_client_id(&self) -> bool {
125 self.inner.include_client_id()
126 }
127
128 /// Whether the ping should be sent if empty.
129 pub fn send_if_empty(&self) -> bool {
130 self.inner.send_if_empty()
131 }
132
133 /// Whether the ping will include precise timestamps for the start/end time.
134 pub fn precise_timestamps(&self) -> bool {
135 self.inner.precise_timestamps()
136 }
137
138 /// Whether client/ping_info sections will be included in this ping.
139 pub fn include_info_sections(&self) -> bool {
140 self.inner.include_info_sections()
141 }
142
143 /// Whether the `enabled` field of this ping is set. Note that whether or
144 /// not a ping is actually enabled is dependent upon the underlying glean
145 /// instance settings, and `follows_collection_enabled`. In other words,
146 /// a Ping might actually be enabled even if the enabled field is not
147 /// set (with this function returning `false`).
148 pub fn naively_enabled(&self) -> bool {
149 self.inner.naively_enabled()
150 }
151
152 /// Whether this ping follows the `collection_enabled` (aka `upload_enabled`) flag.
153 pub fn follows_collection_enabled(&self) -> bool {
154 self.inner.follows_collection_enabled()
155 }
156
157 /// Other pings that should be scheduled when this ping is sent.
158 pub fn schedules_pings(&self) -> &[String] {
159 self.inner.schedules_pings()
160 }
161
162 /// Reason codes that this ping can send.
163 pub fn reason_codes(&self) -> &[String] {
164 self.inner.reason_codes()
165 }
166
167 /// **Test-only API**
168 ///
169 /// Attach a callback to be called right before a new ping is submitted.
170 /// The provided function is called exactly once before submitting a ping.
171 ///
172 /// Note: The callback will be called on any call to submit.
173 /// A ping might not be sent afterwards, e.g. if the ping is otherwise empty (and
174 /// `send_if_empty` is `false`).
175 pub fn test_before_next_submit(&self, cb: impl FnOnce(Option<&str>) + Send + 'static) {
176 let mut test_callback = self.test_callback.lock().unwrap();
177
178 let cb = Box::new(cb);
179 *test_callback = Some(cb);
180 }
181}