Skip to main content

made_api/
intervention_views.rs

1use serde::{Deserialize, Serialize};
2
3use crate::InterventionResponseView;
4
5/// One intervention — a question, investigation or proposed action put to the
6/// table — as a consumer sees it.
7///
8/// The table's conversational unit. A projection, never the entity: responses
9/// come with who answered and when, the request with who asked, and `open`
10/// with whether the table still owes an answer.
11#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
12pub struct InterventionView {
13    pub intervention_id: String,
14    /// `opinion`, `investigation` or `action`.
15    pub kind: String,
16    pub requested_by: String,
17    /// The seats asked. Empty means the whole table.
18    pub target_role_ids: Vec<String>,
19    pub request: String,
20    pub open: bool,
21    pub responses: Vec<InterventionResponseView>,
22    pub created_at_millis: i64,
23    pub closed_at_millis: Option<i64>,
24}
25
26#[cfg(test)]
27mod tests {
28    use super::*;
29    use crate::RaiseInterventionRequest;
30
31    #[test]
32    fn an_intervention_survives_the_wire() {
33        let view = InterventionView {
34            intervention_id: "iv-1".to_owned(),
35            kind: "opinion".to_owned(),
36            requested_by: "FACILITATOR".to_owned(),
37            target_role_ids: vec!["REVIEWER".to_owned()],
38            request: "Recommend the safest cleanup.".to_owned(),
39            open: true,
40            responses: vec![InterventionResponseView {
41                role_id: "REVIEWER".to_owned(),
42                content: "Rotate the certificate first.".to_owned(),
43                responded_at_millis: 1_700_000_000_000,
44            }],
45            created_at_millis: 1_700_000_000_000,
46            closed_at_millis: None,
47        };
48        let bytes = serde_json::to_vec(&view).expect("serializes");
49        assert_eq!(
50            serde_json::from_slice::<InterventionView>(&bytes).expect("deserializes"),
51            view
52        );
53    }
54
55    #[test]
56    fn an_empty_target_means_the_whole_table() {
57        let request = RaiseInterventionRequest {
58            ceremony_id: "c-1".to_owned(),
59            intervention_id: "iv-1".to_owned(),
60            role_id: "FACILITATOR".to_owned(),
61            role_kind: "human".to_owned(),
62            kind: "opinion".to_owned(),
63            target_role_ids: Vec::new(),
64            request: "Thoughts?".to_owned(),
65        };
66        assert!(
67            request.target_role_ids.is_empty(),
68            "no listed seat narrows the question; everyone at the table is asked"
69        );
70    }
71}