Skip to content

Commit b28dd12

Browse files
committed
Add a count methods to the Logins Store API
1 parent 35ea8f6 commit b28dd12

File tree

4 files changed

+132
-0
lines changed

4 files changed

+132
-0
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
### Logins
1010
- add checkpoint API: `set_checkpoint(checkpoint)` and `get_checkpoint()` for desktop's rolling migration
1111
- add `delete_many(ids)` for batch deletion within a single transaction
12+
- Add `count()`, `count_by_origin()` and `count_by_form_action_origin()` methods
1213

1314
### Sync Manager
1415
- Added sync settings metrics for mobile. [#6786](https://github.com/mozilla/application-services/pull/6786)

components/logins/src/db.rs

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,30 @@ impl LoginDb {
132132
Ok(())
133133
}
134134

135+
pub fn count_all(&self) -> Result<i64> {
136+
let mut stmt = self.db.prepare_cached(&COUNT_ALL_SQL)?;
137+
138+
let count: i64 = stmt.query_row([], |row| row.get(0))?;
139+
Ok(count)
140+
}
141+
142+
pub fn count_by_origin(&self, origin: &str) -> Result<i64> {
143+
let mut stmt = self.db.prepare_cached(&COUNT_BY_ORIGIN_SQL)?;
144+
145+
let count: i64 = stmt.query_row(named_params! { ":origin": origin }, |row| row.get(0))?;
146+
Ok(count)
147+
}
148+
149+
pub fn count_by_form_action_origin(&self, form_action_origin: &str) -> Result<i64> {
150+
let mut stmt = self.db.prepare_cached(&COUNT_BY_FORM_ACTION_ORIGIN_SQL)?;
151+
152+
let count: i64 = stmt.query_row(
153+
named_params! { ":form_action_origin": form_action_origin },
154+
|row| row.get(0),
155+
)?;
156+
Ok(count)
157+
}
158+
135159
pub fn get_all(&self) -> Result<Vec<EncryptedLogin>> {
136160
let mut stmt = self.db.prepare_cached(&GET_ALL_SQL)?;
137161
let rows = stmt.query_and_then([], EncryptedLogin::from_row)?;
@@ -835,6 +859,27 @@ lazy_static! {
835859
SELECT {common_cols} FROM loginsM WHERE is_overridden = 0",
836860
common_cols = schema::COMMON_COLS,
837861
);
862+
static ref COUNT_ALL_SQL: String = format!(
863+
"SELECT COUNT(*) FROM (
864+
SELECT guid FROM loginsL WHERE is_deleted = 0
865+
UNION ALL
866+
SELECT guid FROM loginsM WHERE is_overridden = 0
867+
)"
868+
);
869+
static ref COUNT_BY_ORIGIN_SQL: String = format!(
870+
"SELECT COUNT(*) FROM (
871+
SELECT guid FROM loginsL WHERE is_deleted = 0 AND origin = :origin
872+
UNION ALL
873+
SELECT guid FROM loginsM WHERE is_overridden = 0 AND origin = :origin
874+
)"
875+
);
876+
static ref COUNT_BY_FORM_ACTION_ORIGIN_SQL: String = format!(
877+
"SELECT COUNT(*) FROM (
878+
SELECT guid FROM loginsL WHERE is_deleted = 0 AND formActionOrigin = :form_action_origin
879+
UNION ALL
880+
SELECT guid FROM loginsM WHERE is_overridden = 0 AND formActionOrigin = :form_action_origin
881+
)"
882+
);
838883
static ref GET_BY_GUID_SQL: String = format!(
839884
"SELECT {common_cols}
840885
FROM loginsL
@@ -1118,6 +1163,66 @@ mod tests {
11181163
.expect("should get a record");
11191164

11201165
assert_eq!(fetched_b.fields.origin, login_b.origin);
1166+
1167+
assert_eq!(db.count_all().unwrap(), 2);
1168+
}
1169+
1170+
#[test]
1171+
fn test_count_by_origin() {
1172+
ensure_initialized();
1173+
1174+
let origin_a = "https://a.example.com";
1175+
let login_a = LoginEntry {
1176+
origin: origin_a.into(),
1177+
http_realm: Some("https://www.example.com".into()),
1178+
username: "test".into(),
1179+
password: "sekret".into(),
1180+
..LoginEntry::default()
1181+
};
1182+
1183+
let login_b = LoginEntry {
1184+
origin: "https://b.example.com".into(),
1185+
http_realm: Some("https://www.example.com".into()),
1186+
username: "test".into(),
1187+
password: "sekret".into(),
1188+
..LoginEntry::default()
1189+
};
1190+
1191+
let db = LoginDb::open_in_memory().unwrap();
1192+
db.add_many(vec![login_a.clone(), login_b.clone()], &*TEST_ENCDEC)
1193+
.expect("should be able to add logins");
1194+
1195+
assert_eq!(db.count_by_origin(origin_a).unwrap(), 1);
1196+
}
1197+
1198+
#[test]
1199+
fn test_count_by_form_action_origin() {
1200+
ensure_initialized();
1201+
1202+
let origin_a = "https://a.example.com";
1203+
let login_a = LoginEntry {
1204+
origin: origin_a.into(),
1205+
form_action_origin: Some(origin_a.into()),
1206+
http_realm: Some("https://www.example.com".into()),
1207+
username: "test".into(),
1208+
password: "sekret".into(),
1209+
..LoginEntry::default()
1210+
};
1211+
1212+
let login_b = LoginEntry {
1213+
origin: "https://b.example.com".into(),
1214+
form_action_origin: Some("https://b.example.com".into()),
1215+
http_realm: Some("https://www.example.com".into()),
1216+
username: "test".into(),
1217+
password: "sekret".into(),
1218+
..LoginEntry::default()
1219+
};
1220+
1221+
let db = LoginDb::open_in_memory().unwrap();
1222+
db.add_many(vec![login_a.clone(), login_b.clone()], &*TEST_ENCDEC)
1223+
.expect("should be able to add logins");
1224+
1225+
assert_eq!(db.count_by_form_action_origin(origin_a).unwrap(), 1);
11211226
}
11221227

11231228
#[test]

components/logins/src/logins.udl

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -223,6 +223,15 @@ interface LoginStore {
223223
[Throws=LoginsApiError]
224224
boolean is_empty();
225225

226+
[Throws=LoginsApiError]
227+
i64 count();
228+
229+
[Throws=LoginsApiError]
230+
i64 count_by_origin([ByRef] string origin);
231+
232+
[Throws=LoginsApiError]
233+
i64 count_by_form_action_origin([ByRef] string form_action_origin);
234+
226235
[Throws=LoginsApiError]
227236
sequence<Login> list();
228237

components/logins/src/store.rs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,23 @@ impl LoginStore {
117117
})
118118
}
119119

120+
#[handle_error(Error)]
121+
pub fn count(&self) -> ApiResult<i64> {
122+
self.db.lock().count_all()
123+
}
124+
125+
#[handle_error(Error)]
126+
pub fn count_by_origin(&self, origin: &str) -> ApiResult<i64> {
127+
self.db.lock().count_by_origin(origin)
128+
}
129+
130+
#[handle_error(Error)]
131+
pub fn count_by_form_action_origin(&self, form_action_origin: &str) -> ApiResult<i64> {
132+
self.db
133+
.lock()
134+
.count_by_form_action_origin(form_action_origin)
135+
}
136+
120137
#[handle_error(Error)]
121138
pub fn get(&self, id: &str) -> ApiResult<Option<Login>> {
122139
match self.db.lock().get_by_id(id) {

0 commit comments

Comments
 (0)