iso_20022_sdk/
message.rs

1// Copyright 2023 Emergent Financial, LLC - All Rights Reserved
2//
3//
4// This software is licensed under the Emergent Financial Limited Public License Version 1.0
5// (EF-LPLv1). You may use, copy, modify, and distribute this software under the terms and
6// conditions of the EF-LPL. For more information, please refer to the full text of the license
7// at https://github.com/emergentfinancial/ef-lpl.
8//
9//
10// THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS
11// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
12// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
13// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
14// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
15// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
16//
17//! # Message Envelope
18//!
19//! The `Message` type is a high-level abstraction of the ISO-20022 message envelope `BizMsgEnvlp` and its child elements `Hdr`, `Doc`, `Ref` and `SplmtryData`.
20//!
21//! The `Message` type provides a builder pattern for constructing a message envelope and its child elements. The `Message` type also provides methods for serializing the message envelope to XML and deserializing the message envelope from XML.
22//!
23//! ```rust
24//!
25//! // Import common types and traits in the prelude
26//! use iso_20022_sdk::prelude::*;
27//!
28//!
29//! // The `builder` method will return `Message` instance
30//! // after setting default values, e.g. envelope namespace
31//! //
32//! // The initial `Doc` type of the `Message` can be elided
33//! // using `::<_>` turbofish syntax. The compiler will
34//! // infer the type of the `Doc` based on the `set_document()`
35//! // method.
36//! //
37//! // If a type is required from the compiler, the `::<Document>`
38//! // turbofish syntax can be used to specify the enumerable iso-20022 document
39//! // type of the `Doc` element. This value can then be later overridden
40//! // using the `set_document()` method.
41//! let msg = Message::<_>::builder()
42//!    
43//!    
44//!     // Setting the type of document is done using the `set_document()` method.
45//!     // In practice, the document type will likely be the result of the document
46//!     // builder for the target namespace, e.g.
47//!     //
48//!     // `documents::pacs::pacs_008_001_07::Document`
49//!     //
50//!     // The example below uses the default values for the document builder
51//!     // for the `pacs.008.001.07` namespace.
52//!     //
53//!     // NOTE: document namespaces are feature gated and must be enabled
54//!     // for the example to work, e.g. `pacs` feature must be enabled in
55//!     // Cargo.toml file.
56//!     .set_document(Document::from_namespace("pacs.008.001.07"))
57//!     // Call the `to_xml` method to serialize the `Message` type to XML
58//!     .to_xml();
59//!
60//!
61//! ```
62//!
63//! #### Example XML Output
64//! ```xml
65//! <!-- Example XML Instance -->
66//! <BizMsgEnvlp xmlns="urn:iso:std:iso:20022:tech:xsd:nvlp.001.001.01">
67//!     <Hdr>
68//!         <!-- Business Application Header (head.001.001.03) -->
69//!     </Hdr>
70//!     <Doc>
71//!         <!-- Document (ISO-20022 Message) -->
72//!     </Doc>
73//!     <Ref>
74//!         <!-- Reference Information -->
75//!     </Ref>
76//!     <SplmtryData>
77//!         <!-- Supplementary Data (Generic Type) -->
78//!     </SplmtryData>
79//! </BizMsgEnvlp>
80//! ```
81use std::io::BufReader;
82
83use crate::head::head_001_001_03::{self as head};
84use crate::nvlp::nvlp_001_001_01::{self as nvlp};
85
86use sxd_document::parser;
87use sxd_xpath::{evaluate_xpath};
88
89use xml::{reader::XmlEvent, EventReader};
90
91use crate::crypto::Signature;
92use crate::documents::{Dmkr, Document};
93
94/// Default Envelope Type
95pub type DefaultMsgEnvlp =
96    nvlp::BizMsgEnvlp<head::AppHdr<Signature, Signature>, Document, Dmkr, Dmkr>;
97
98#[derive(Debug, thiserror::Error)]
99pub enum Error {
100    /// Error Serializing / Deserializing XML
101    #[error(transparent)]
102    XmlSerDe(#[from] quick_xml::de::DeError),
103    /// SXD Document Error
104    #[error(transparent)]
105    XsdDocument(#[from] sxd_document::parser::Error),
106    /// SXD XPath Error
107    #[error(transparent)]
108    XsdXPath(#[from] sxd_xpath::Error),
109    /// Signing Error
110    #[error(transparent)]
111    Signing(#[from] signature::Error),
112}
113
114#[derive(Debug, Clone, Default)]
115pub struct Message<
116    'a,
117    Doc: std::fmt::Debug
118        + Default
119        + Clone
120        + PartialEq
121        + ::serde::Serialize
122        + ::serde::Deserialize<'a>
123        + ::validator::Validate,
124> {
125    /// XML string representing the inner type. Used internally to parse the inner type.
126    /// An incoming message will use this field for helping to determine what the
127    /// inner type is.
128    ///
129    /// use the `to_xml()` method to get the XML string representation of the message
130    /// inner type.
131    pub xml_string: &'a str,
132    /// Internal representation of the message envelope
133    pub inner: nvlp::BizMsgEnvlp<head::AppHdr<Signature, Signature>, Doc, Dmkr, Dmkr>,
134}
135
136impl<'a, Doc> Message<'a, Doc>
137where
138    Doc: std::fmt::Debug
139        + Default
140        + Clone
141        + PartialEq
142        + ::serde::Serialize
143        + ::serde::Deserialize<'a>
144        + ::validator::Validate,
145{
146    pub fn builder() -> Self {
147        let envlp = Self::default();
148
149        // Automatically set the envlp and header namespaces
150        envlp.set_namespace()
151    }
152
153    /// Return the application header from the message envelope
154    pub fn app_hdr(&self) -> Option<head::AppHdr<Signature, Signature>> {
155        self.inner.value.hdr.clone().map(|hdr| hdr.value)
156    }
157
158    /// Set the application header AppHdr of the message
159    /// Note, this will overwrite the existing AppHdr
160    pub fn set_app_hdr(self, app_hdr: head::AppHdr<Signature, Signature>) -> Self {
161        let mut msg = self;
162
163        // Set the AppHdr
164        msg.inner.value.hdr = Some(nvlp::LaxPayload { value: app_hdr });
165
166        msg
167    }
168
169    /// Set the recipient of the message
170    pub fn set_recipient(self, recipient: head::Party44Choice) -> Self {
171        let mut app_hdr = self.app_hdr().unwrap_or_default();
172        app_hdr.value.to = recipient;
173
174        self.set_app_hdr(app_hdr)
175    }
176
177    /// Set the recipient organization id of the message.
178    /// This is a simplified version of `set_recipient` that only takes an organization id.
179    /// Use the `set_recipient_fi_id()` method to set a financial institutiton id.
180    /// Note, this will overwrite any existing recipient.
181    pub fn set_recipient_org_id(self, org_id: head::OrganisationIdentification29) -> Self {
182        self.set_recipient(head::Party44Choice {
183            value: head::Party44ChoiceEnum {
184                org_id: Some(head::PartyIdentification135 {
185                    id: Some(head::Party38Choice {
186                        value: head::Party38ChoiceEnum {
187                            org_id: Some(org_id),
188                            ..Default::default()
189                        },
190                    }),
191                    ..Default::default()
192                }),
193                ..Default::default()
194            },
195        })
196    }
197
198    /// Set the recipient financial institution id of the message.
199    /// This is a simplified version of `set_recipient` that only takes a financial institution id.
200    /// Use the `set_recipient_org_id()` method to set an organization id.
201    /// Note, this will overwrite any existing recipient.
202    pub fn set_recipient_fi_id(
203        self,
204        fin_instn_id: head::FinancialInstitutionIdentification18,
205    ) -> Self {
206        self.set_recipient(head::Party44Choice {
207            value: head::Party44ChoiceEnum {
208                fi_id: Some(head::BranchAndFinancialInstitutionIdentification6 {
209                    fin_instn_id,
210                    ..Default::default()
211                }),
212                ..Default::default()
213            },
214        })
215    }
216
217    /// Set the recipient private individual id of the message.
218    /// This is a simplified version of `set_recipient` that only takes a private individual id.
219    /// Use the `set_recipient_org_id()` method to set an organization id or the `set_recipient_fi_id()`
220    /// method to set a financial institution id.
221    /// Note, this will overwrite any existing recipient.
222    pub fn set_recipient_prvt_id(self, prvt_id: head::PersonIdentification13) -> Self {
223        self.set_recipient(head::Party44Choice {
224            value: head::Party44ChoiceEnum {
225                org_id: Some(head::PartyIdentification135 {
226                    id: Some(head::Party38Choice {
227                        value: head::Party38ChoiceEnum {
228                            prvt_id: Some(prvt_id),
229                            ..Default::default()
230                        },
231                    }),
232                    ..Default::default()
233                }),
234                ..Default::default()
235            },
236        })
237    }
238
239    /// Set the sender of the message
240    pub fn set_sender(self, sender: head::Party44Choice) -> Self {
241        let mut app_hdr = self.app_hdr().unwrap_or_default();
242        app_hdr.value.fr = sender;
243
244        self.set_app_hdr(app_hdr)
245    }
246
247    /// Set the sender organization id of the message.
248    /// This is a simplified version of `set_sender` that only takes an organization id.
249    /// Use the `set_sender_fi_id()` method to set a financial institutiton id.
250    /// Note, this will overwrite any existing sender.
251    pub fn set_sender_org_id(self, org_id: head::OrganisationIdentification29) -> Self {
252        self.set_sender(head::Party44Choice {
253            value: head::Party44ChoiceEnum {
254                org_id: Some(head::PartyIdentification135 {
255                    id: Some(head::Party38Choice {
256                        value: head::Party38ChoiceEnum {
257                            org_id: Some(org_id),
258                            ..Default::default()
259                        },
260                    }),
261                    ..Default::default()
262                }),
263                ..Default::default()
264            },
265        })
266    }
267
268    /// Set the sender financial institution id of the message.
269    /// This is a simplified version of `set_sender` that only takes a financial institution id.
270    /// Use the `set_sender_org_id()` method to set an organization id.
271    /// Note, this will overwrite any existing sender.
272    pub fn set_sender_fi_id(
273        self,
274        fin_instn_id: head::FinancialInstitutionIdentification18,
275    ) -> Self {
276        self.set_sender(head::Party44Choice {
277            value: head::Party44ChoiceEnum {
278                fi_id: Some(head::BranchAndFinancialInstitutionIdentification6 {
279                    fin_instn_id,
280                    ..Default::default()
281                }),
282                ..Default::default()
283            },
284        })
285    }
286
287    /// Set the sender private individual id of the message.
288    /// This is a simplified version of `set_sender` that only takes a private individual id.
289    /// Use the `set_sender_org_id()` method to set an organization id or the `set_sender_fi_id()`
290    /// method to set a financial institution id.
291    /// Note, this will overwrite any existing sender.
292    pub fn set_sender_prvt_id(self, prvt_id: head::PersonIdentification13) -> Self {
293        self.set_sender(head::Party44Choice {
294            value: head::Party44ChoiceEnum {
295                org_id: Some(head::PartyIdentification135 {
296                    id: Some(head::Party38Choice {
297                        value: head::Party38ChoiceEnum {
298                            prvt_id: Some(prvt_id),
299                            ..Default::default()
300                        },
301                    }),
302                    ..Default::default()
303                }),
304                ..Default::default()
305            },
306        })
307    }
308
309    /// e.g. `Document`
310    pub fn set_biz_msg_idr(self, idr: head::Max35Text) -> Self {
311        let mut app_hdr = self.app_hdr().unwrap_or_default();
312        app_hdr.value.biz_msg_idr = idr;
313
314        self.set_app_hdr(app_hdr)
315    }
316
317    /// e.g. `pacs.008.001.07`
318    pub fn set_msg_def_idr(self, idr: head::Max35Text) -> Self {
319        let mut app_hdr = self.app_hdr().unwrap_or_default();
320        app_hdr.value.msg_def_idr = idr;
321
322        self.set_app_hdr(app_hdr)
323    }
324
325    /// Set the created date time of the message.
326    /// This will be set to the current UTC time.
327    pub fn set_cre_dt(self) -> Self {
328        let mut app_hdr = self.app_hdr().unwrap_or_default();
329        app_hdr.value.cre_dt = head::IsoDateTime {
330            value: chrono::Utc::now(),
331        };
332
333        self.set_app_hdr(app_hdr)
334    }
335
336    /// Set the xml namespace of the message and business header.
337    pub fn set_namespace(self) -> Self {
338        let mut envlp = self;
339
340        // Set the envelope namespace
341        envlp.inner.value.xmlns = nvlp::namespace();
342
343        // Set the header namespace
344        let mut app_hdr = envlp.app_hdr().unwrap_or_default();
345        app_hdr.value.xmlns = head::namespace();
346
347        envlp.set_app_hdr(app_hdr)
348    }
349
350    /// Set the document of the message.
351    /// Note, the document must set its own namespace value.
352    /// By default, all root iso-20022 message documents have
353    /// an attribute field, `xmlns`, that is used to set the document namespace.
354    /// The document namespace must be set before calling this method.
355    pub fn set_document(self, doc: Doc) -> Self {
356        let mut envlp = self;
357        envlp.inner.value.doc.value = doc;
358
359        envlp
360    }
361
362    /// Sign the document at an optional xpath, e.g. `/Document/AcctOpngInstr`
363    /// If no xpath is provided, the entire document will be signed, e.g. `/Document`
364    /// Note, this will overwrite any existing signature.
365    /// ```rust
366    /// use iso_20022_sdk::prelude::*;
367    ///
368    /// let msg = Message::<_>::builder()
369    ///     // Document must be set before signing
370    ///     .set_document(doc)
371    ///     // Sign the entire document
372    ///     .sign_document(&signer, None);
373    ///
374    ///
375    /// ```
376    pub fn sign_document(
377        self,
378        signer: impl signature::Signer<Signature>,
379        xpath: Option<&str>,
380    ) -> Result<Self, Error> {
381        let xml = quick_xml::se::to_string(&self.document())?;
382        let package = parser::parse(&xml)?;
383        let doc = package.as_document();
384
385        // By default, sign the entire document
386        let xpath = xpath.unwrap_or("/Document");
387        let data = evaluate_xpath(&doc, xpath)?.into_string();
388
389        // TODO, hash the data before signing
390        let data = data.as_bytes();
391
392        // Sign the xpath data
393        let signature = signer.try_sign(data)?;
394
395        // Set the signature in the application header
396        let mut app_hdr = self.app_hdr().unwrap_or_default();
397        app_hdr.value.sgntr = Some(head::SignatureEnvelope { value: signature });
398
399        // Set the application header and return the envelope
400        Ok(self.set_app_hdr(app_hdr))
401    }
402
403    /// Set the related business reference of the message.
404    pub fn set_rltd(self, rltd: head::BusinessApplicationHeader7<Signature>) -> Self {
405        let mut app_hdr = self.app_hdr().unwrap_or_default();
406        app_hdr.value.rltd.push(rltd);
407
408        self.set_app_hdr(app_hdr)
409    }
410
411    /// Return the envelope document.
412    pub fn document(&self) -> Doc {
413        self.inner.value.doc.value.clone()
414    }
415
416    /// Return the serialized xml string of the inner type.
417    pub fn to_xml(&self) -> Result<String, Error> {
418        let xml_string = quick_xml::se::to_string(&self.inner)?;
419
420        Ok(xml_string)
421    }
422
423    /// parse the header from the xml string
424    pub fn from_xml(xml_string: &'a str) -> Result<Self, Error> {
425        let inner = quick_xml::de::from_str(xml_string)?;
426
427        println!("inner: {:?}", inner);
428
429        let mut msg = Self { xml_string, inner };
430
431        println!("msg: {:?}", msg);
432
433        // Parse the msg into the inner type;
434        msg.parse()?;
435
436        Ok(msg)
437    }
438
439    fn parse(&mut self) -> Result<(), Error> {
440        // Use xml-reader to parse the xml string and find the `MsgDefIdr` element in the `head.001.001.03` namespace.
441        let buf_reader = BufReader::new(self.xml_string.as_bytes());
442        let event_reader = EventReader::new(buf_reader);
443
444        for e in event_reader {
445            match e {
446                Ok(XmlEvent::ProcessingInstruction { name, data }) => {
447                    println!("name: {:?}", name);
448                    println!("data: {:?}", data);
449                }
450                Ok(XmlEvent::StartElement {
451                    name:
452                        xml::name::OwnedName {
453                            local_name,
454                            namespace,
455                            ..
456                        },
457                    ..
458                }) => {
459                    println!("local_name: {:?}", local_name);
460                    println!("namespace: {:?}", namespace);
461                }
462                Ok(XmlEvent::Characters(data)) => {
463                    println!("data: {:?}", data);
464                }
465                _ => (),
466            }
467
468            // buf.clear();
469        }
470
471        Ok(())
472    }
473}
474
475#[cfg(test)]
476mod tests {
477    use super::*;
478
479    #[test]
480    fn test_message_builder() -> Result<(), Error> {
481        let msg = Message::<_>::builder()
482            .set_cre_dt()
483            .set_msg_def_idr(head::Max35Text {
484                value: "pacs.008.001.07".to_string(),
485            })
486            .set_biz_msg_idr(head::Max35Text {
487                value: "Document".to_string(),
488            })
489            .set_recipient_org_id(head::OrganisationIdentification29 {
490                othr: vec![head::GenericOrganisationIdentification1 {
491                    id: head::Max35Text {
492                        value: "b3033215-3a30-48ee-b194-5c02e08a5fb3".to_string(),
493                    },
494                    ..Default::default()
495                }],
496                ..Default::default()
497            })
498            .set_sender_org_id(head::OrganisationIdentification29 {
499                othr: vec![head::GenericOrganisationIdentification1 {
500                    id: head::Max35Text {
501                        value: "b3033215-3a30-48ee-b194-5c02e08a5fb3".to_string(),
502                    },
503                    ..Default::default()
504                }],
505                ..Default::default()
506            })
507            .set_document(Document::from_namespace("pacs.008.001.07"));
508
509        let xml = msg.to_xml()?;
510
511        println!("xml: {}", xml);
512
513        Ok(())
514    }
515
516    #[test]
517    fn test_parse_message() -> Result<(), Error> {
518        let file = std::fs::read_to_string("examples/nvlp.xml").expect("Unable to read file");
519
520        // println!("file: {}", file);
521        let _msg = Message::<Dmkr>::from_xml(&file)?;
522
523        Ok(())
524    }
525}