Skip to content

Commit bf58b5c

Browse files
authored
Add endpoint to determine if there are active notifications (#596)
1 parent 25c00bf commit bf58b5c

File tree

11 files changed

+194
-3
lines changed

11 files changed

+194
-3
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@
66
* Add different file size limits for pictures (avatar/logo - 20k) and documents (invoices, registration, passport - 1mb) as well as an upper limit for bill files (100)
77
* This limit is checked at creation/update time, not at the time of uploading a temporary file
88
* Add the address of the signer for the calls to `endorsements` and `past_endorsees`
9+
* Add api call `active_notifications_for_node_ids` on `notification` API, which returns for a set of node ids, whether they have active notifications
10+
* If the set of node ids is empty, only the node ids that have active notifications are returned
911

1012
# 0.4.2
1113

crates/bcr-ebill-api/src/service/notification_service/default_service.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -750,6 +750,17 @@ impl NotificationServiceApi for DefaultNotificationService {
750750
.collect()
751751
}
752752

753+
async fn get_active_notification_status_for_node_ids(
754+
&self,
755+
node_ids: &[NodeId],
756+
) -> Result<HashMap<NodeId, bool>> {
757+
Ok(self
758+
.notification_store
759+
.get_active_status_for_node_ids(node_ids)
760+
.await
761+
.unwrap_or_default())
762+
}
763+
753764
async fn check_bill_notification_sent(
754765
&self,
755766
bill_id: &BillId,

crates/bcr-ebill-api/src/tests/mod.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -315,6 +315,10 @@ pub mod tests {
315315

316316
#[async_trait]
317317
impl NotificationStoreApi for NotificationStoreApiMock {
318+
async fn get_active_status_for_node_ids(
319+
&self,
320+
node_ids: &[NodeId],
321+
) -> Result<HashMap<NodeId, bool>>;
318322
async fn add(&self, notification: Notification) -> Result<Notification>;
319323
async fn list(&self, filter: NotificationFilter) -> Result<Vec<Notification>>;
320324
async fn get_latest_by_references(
@@ -426,6 +430,10 @@ pub mod tests {
426430
async fn mark_notification_as_done(&self, notification_id: &str) -> bcr_ebill_transport::Result<()>;
427431
async fn get_active_bill_notification(&self, bill_id: &BillId) -> Option<Notification>;
428432
async fn get_active_bill_notifications(&self, bill_ids: &[BillId]) -> HashMap<BillId, Notification>;
433+
async fn get_active_notification_status_for_node_ids(
434+
&self,
435+
node_ids: &[NodeId],
436+
) -> bcr_ebill_transport::Result<HashMap<NodeId, bool>>;
429437
async fn check_bill_notification_sent(
430438
&self,
431439
bill_id: &BillId,

crates/bcr-ebill-persistence/src/db/notification.rs

Lines changed: 119 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,39 @@ impl ServiceTraitBounds for SurrealNotificationStore {}
4040
#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
4141
#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
4242
impl NotificationStoreApi for SurrealNotificationStore {
43+
/// Returns node ids with an active notification for the given node ids
44+
async fn get_active_status_for_node_ids(
45+
&self,
46+
node_ids: &[NodeId],
47+
) -> Result<HashMap<NodeId, bool>> {
48+
let mut bindings = Bindings::default();
49+
bindings.add("table", Self::TABLE)?;
50+
bindings.add("node_ids", node_ids.to_owned())?;
51+
52+
let node_id_filter = if node_ids.is_empty() {
53+
""
54+
} else {
55+
"and node_id in $node_ids"
56+
};
57+
58+
let result: Vec<NodeIdDb> = self.db.query(&format!("SELECT node_id from notifications where active = true {node_id_filter} GROUP BY node_id"), bindings).await?;
59+
let mut res: HashMap<NodeId, bool> = HashMap::new();
60+
61+
if node_ids.is_empty() {
62+
for node_id_db in result {
63+
res.insert(node_id_db.node_id.to_owned(), true);
64+
}
65+
} else {
66+
for node_id in node_ids {
67+
res.insert(
68+
node_id.to_owned(),
69+
result.iter().any(|n| n.node_id == *node_id),
70+
);
71+
}
72+
}
73+
Ok(res)
74+
}
75+
4376
/// Stores a new notification into the database
4477
async fn add(&self, notification: Notification) -> Result<Notification> {
4578
let id = notification.id.to_owned();
@@ -205,6 +238,11 @@ impl NotificationStoreApi for SurrealNotificationStore {
205238
}
206239
}
207240

241+
#[derive(Debug, Clone, Serialize, Deserialize)]
242+
pub struct NodeIdDb {
243+
pub node_id: NodeId,
244+
}
245+
208246
#[derive(Debug, Clone, Serialize, Deserialize)]
209247
struct NotificationDb {
210248
pub id: Thing,
@@ -274,7 +312,7 @@ mod tests {
274312
use super::*;
275313
use crate::{
276314
db::get_memory_db,
277-
tests::tests::{bill_id_test, bill_id_test_other, node_id_test},
315+
tests::tests::{bill_id_test, bill_id_test_other, node_id_test, node_id_test_other},
278316
util::date::now,
279317
};
280318

@@ -533,6 +571,86 @@ mod tests {
533571
});
534572
}
535573

574+
#[tokio::test]
575+
async fn test_returns_active_status_for_node_ids() {
576+
let store = get_store().await;
577+
let notification1 = test_notification(&bill_id_test(), Some(test_payload()));
578+
let mut notification2 = test_notification(&bill_id_test_other(), Some(test_payload()));
579+
notification2.node_id = Some(node_id_test_other());
580+
let notification3 = test_general_notification();
581+
let _ = store
582+
.add(notification1.clone())
583+
.await
584+
.expect("notification created");
585+
let _ = store
586+
.add(notification2.clone())
587+
.await
588+
.expect("notification created");
589+
let _ = store
590+
.add(notification3.clone())
591+
.await
592+
.expect("notification created");
593+
594+
let status = store
595+
.get_active_status_for_node_ids(&[])
596+
.await
597+
.expect("returns status");
598+
599+
assert_eq!(status.len(), 2, "should have all node ids in list");
600+
assert!(status.get(&node_id_test()).unwrap());
601+
assert!(status.get(&node_id_test_other()).unwrap());
602+
603+
let status = store
604+
.get_active_status_for_node_ids(&[node_id_test()])
605+
.await
606+
.expect("returns status");
607+
608+
assert_eq!(
609+
status.len(),
610+
1,
611+
"should have all given node ids in the list"
612+
);
613+
assert!(status.get(&node_id_test()).unwrap());
614+
615+
store
616+
.mark_as_done(&notification2.clone().id)
617+
.await
618+
.expect("notification marked done");
619+
620+
let status = store
621+
.get_active_status_for_node_ids(&[node_id_test_other()])
622+
.await
623+
.expect("returns status");
624+
625+
assert_eq!(
626+
status.len(),
627+
1,
628+
"should have all given node ids in the list"
629+
);
630+
assert!(!status.get(&node_id_test_other()).unwrap());
631+
632+
let status = store
633+
.get_active_status_for_node_ids(&[node_id_test(), node_id_test_other()])
634+
.await
635+
.expect("returns status");
636+
637+
assert_eq!(status.len(), 2, "should have all given node ids in list");
638+
assert!(status.get(&node_id_test()).unwrap());
639+
assert!(!status.get(&node_id_test_other()).unwrap());
640+
641+
let status = store
642+
.get_active_status_for_node_ids(&[])
643+
.await
644+
.expect("returns status");
645+
646+
assert_eq!(
647+
status.len(),
648+
1,
649+
"should have all active notif node ids in list"
650+
);
651+
assert!(status.get(&node_id_test()).unwrap());
652+
}
653+
536654
fn test_notification(bill_id: &BillId, payload: Option<Value>) -> Notification {
537655
Notification::new_bill_notification(bill_id, &node_id_test(), "test_notification", payload)
538656
}

crates/bcr-ebill-persistence/src/notification.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,11 @@ use bcr_ebill_core::{
1212
#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
1313
#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
1414
pub trait NotificationStoreApi: ServiceTraitBounds {
15+
/// Returns node ids with an active notification for the given node ids
16+
async fn get_active_status_for_node_ids(
17+
&self,
18+
node_ids: &[NodeId],
19+
) -> Result<HashMap<NodeId, bool>>;
1520
/// Stores a new notification into the database
1621
async fn add(&self, notification: Notification) -> Result<Notification>;
1722
/// Returns all currently active notifications from the database

crates/bcr-ebill-transport/src/handler/mod.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -241,6 +241,10 @@ mod test_utils {
241241

242242
#[async_trait]
243243
impl NotificationStoreApi for NotificationStore {
244+
async fn get_active_status_for_node_ids(
245+
&self,
246+
node_ids: &[NodeId],
247+
) -> Result<HashMap<NodeId, bool>>;
244248
async fn add(&self, notification: Notification) -> Result<Notification>;
245249
async fn list(&self, filter: NotificationFilter) -> Result<Vec<Notification>>;
246250
async fn get_latest_by_references(

crates/bcr-ebill-transport/src/notification_service.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,11 @@ pub trait NotificationServiceApi: ServiceTraitBounds {
161161
bill_ids: &[BillId],
162162
) -> HashMap<BillId, Notification>;
163163

164+
async fn get_active_notification_status_for_node_ids(
165+
&self,
166+
node_ids: &[NodeId],
167+
) -> Result<HashMap<NodeId, bool>>;
168+
164169
/// Returns whether a notification was already sent for the given bill id and action
165170
async fn check_bill_notification_sent(
166171
&self,

crates/bcr-ebill-wasm/index.html

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ <h2>Uploads</h2>
2929
<h2>Notification Testing</h2>
3030
<div>
3131
<button type="button" id="notif">trigger local push notif</button>
32+
<button type="button" id="get_active_notif_status">Get Active Notification Status for Node Ids</button>
3233
</div>
3334
</div>
3435
<div>

crates/bcr-ebill-wasm/main.js

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import * as wasm from '../pkg/index.js';
22

33
document.getElementById("fileInput").addEventListener("change", uploadFile);
44
document.getElementById("notif").addEventListener("click", triggerNotif);
5+
document.getElementById("get_active_notif_status").addEventListener("click", getActiveNotif);
56
document.getElementById("company_create").addEventListener("click", createCompany);
67
document.getElementById("contact_test").addEventListener("click", triggerContact);
78
document.getElementById("contact_test_anon").addEventListener("click", triggerAnonContact);
@@ -591,3 +592,10 @@ async function deleteContact() {
591592
await measured();
592593
}
593594

595+
async function getActiveNotif() {
596+
let measured = measure(async () => {
597+
return await notificationTriggerApi.active_notifications_for_node_ids([]);
598+
});
599+
await measured();
600+
}
601+

crates/bcr-ebill-wasm/src/api/notification.rs

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,12 @@
11
use super::Result;
22
use crate::{
33
context::get_ctx,
4-
data::{NotificationFilters, notification::NotificationWeb},
4+
data::{
5+
NotificationFilters,
6+
notification::{NotificationStatusWeb, NotificationWeb},
7+
},
58
};
6-
use bcr_ebill_api::NotificationFilter;
9+
use bcr_ebill_api::{NotificationFilter, data::NodeId};
710
use log::{error, info};
811
use wasm_bindgen::prelude::*;
912

@@ -17,6 +20,24 @@ impl Notification {
1720
Notification
1821
}
1922

23+
#[wasm_bindgen(unchecked_return_type = "NotificationStatusWeb[]")]
24+
pub async fn active_notifications_for_node_ids(
25+
&self,
26+
#[wasm_bindgen(unchecked_param_type = "Vec<String>")] node_ids: JsValue,
27+
) -> Result<JsValue> {
28+
let node_ids_parsed: Vec<NodeId> = serde_wasm_bindgen::from_value(node_ids)?;
29+
let notification_status = get_ctx()
30+
.notification_service
31+
.get_active_notification_status_for_node_ids(&node_ids_parsed)
32+
.await?;
33+
let web: Vec<NotificationStatusWeb> = notification_status
34+
.into_iter()
35+
.map(|(node_id, active)| NotificationStatusWeb { node_id, active })
36+
.collect();
37+
let res = serde_wasm_bindgen::to_value(&web)?;
38+
Ok(res)
39+
}
40+
2041
#[wasm_bindgen]
2142
pub async fn subscribe(&self, callback: js_sys::Function) {
2243
wasm_bindgen_futures::spawn_local(async move {

0 commit comments

Comments
 (0)