homestar_runtime/event_handler/notification/
receipt.rs

1//! Notification receipts.
2
3use const_format::formatcp;
4use homestar_invocation::{
5    ipld::{schema, DagJson},
6    Receipt,
7};
8use libipld::{ipld, Cid, Ipld};
9use schemars::{
10    gen::SchemaGenerator,
11    schema::{InstanceType, Metadata, ObjectValidation, Schema, SchemaObject, SingleOrVec},
12    JsonSchema,
13};
14use std::{
15    borrow::Cow,
16    collections::{BTreeMap, BTreeSet},
17    module_path,
18};
19
20/// A [Receipt] that is sent out for websocket notifications.
21#[derive(Debug, Clone, PartialEq)]
22pub struct ReceiptNotification(Ipld);
23
24impl ReceiptNotification {
25    /// Obtain a reference to the inner Ipld value.
26    #[allow(dead_code)]
27    pub(crate) fn inner(&self) -> &Ipld {
28        &self.0
29    }
30
31    /// Obtain ownership of the inner Ipld value.
32    #[allow(dead_code)]
33    pub(crate) fn into_inner(self) -> Ipld {
34        self.0.to_owned()
35    }
36
37    /// Create a new [ReceiptNotification].
38    pub(crate) fn with(receipt: Receipt<Ipld>, cid: Cid, metadata: Option<Ipld>) -> Self {
39        let receipt: Ipld = receipt.into();
40        let data = ipld!({
41            "receipt": receipt,
42            "metadata": metadata.as_ref().map(|m| m.to_owned()).map_or(Ipld::Null, |m| m),
43            "receipt_cid": cid,
44        });
45        ReceiptNotification(data)
46    }
47}
48
49impl DagJson for ReceiptNotification where Ipld: From<ReceiptNotification> {}
50
51impl From<ReceiptNotification> for Ipld {
52    fn from(receipt: ReceiptNotification) -> Self {
53        receipt.0
54    }
55}
56
57impl From<Ipld> for ReceiptNotification {
58    fn from(ipld: Ipld) -> Self {
59        ReceiptNotification(ipld)
60    }
61}
62
63impl JsonSchema for ReceiptNotification {
64    fn schema_name() -> String {
65        "receipt_notification".to_owned()
66    }
67
68    fn schema_id() -> Cow<'static, str> {
69        Cow::Borrowed(formatcp!("{}::ReceiptNotification", module_path!()))
70    }
71
72    fn json_schema(gen: &mut SchemaGenerator) -> Schema {
73        let metadata_schema = SchemaObject {
74            instance_type: Some(SingleOrVec::Single(InstanceType::Object.into())),
75            metadata: Some(Box::new(Metadata {
76                title: Some("Metadata".to_string()),
77                description: Some("Workflow metadata to contextualize the receipt".to_string()),
78                ..Default::default()
79            })),
80            object: Some(Box::new(ObjectValidation {
81                properties: BTreeMap::from([
82                    ("name".to_owned(), <String>::json_schema(gen)),
83                    ("replayed".to_owned(), <bool>::json_schema(gen)),
84                    (
85                        "workflow".to_owned(),
86                        gen.subschema_for::<schema::IpldLinkStub>(),
87                    ),
88                ]),
89                required: BTreeSet::from([
90                    "name".to_string(),
91                    "receipt".to_string(),
92                    "receipt_cid".to_string(),
93                ]),
94                ..Default::default()
95            })),
96            ..Default::default()
97        };
98
99        let schema = SchemaObject {
100            instance_type: Some(SingleOrVec::Single(InstanceType::Object.into())),
101            metadata: Some(Box::new(Metadata {
102                title: Some("Receipt notification".to_string()),
103                description: Some(
104                    "A receipt notification associated with a running workflow".to_string(),
105                ),
106                ..Default::default()
107            })),
108            object: Some(Box::new(ObjectValidation {
109                properties: BTreeMap::from([
110                    ("metadata".to_owned(), Schema::Object(metadata_schema)),
111                    ("receipt".to_owned(), gen.subschema_for::<Receipt<()>>()),
112                    (
113                        "receipt_cid".to_owned(),
114                        gen.subschema_for::<schema::IpldLinkStub>(),
115                    ),
116                ]),
117                required: BTreeSet::from(["receipt".to_string(), "receipt_cid".to_string()]),
118                ..Default::default()
119            })),
120            ..Default::default()
121        };
122
123        schema.into()
124    }
125}