Skip to content

Commit 0043482

Browse files
committed
feat: enhance validation, error handling, and testing infrastructure
- Add comprehensive input validation for XML and SWIFT MT messages - Implement robust error extraction and reporting from workflow engines - Enhance transformation endpoints with detailed success/failure validation - Update swift-mt-message dependency to 2.3.7 for improved parsing - Add round-trip testing framework with configuration support - Improve .gitignore patterns for better repository hygiene - Strengthen error propagation throughout parse/publish modules
1 parent fd9a357 commit 0043482

12 files changed

Lines changed: 1596 additions & 214 deletions

.gitignore

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,12 @@ target/
1818

1919
# Added by cargo
2020

21-
/target
21+
target/
2222
.DS_Store
2323
specification/
2424
logs/
2525
.claude/
26+
test/data/logs/
27+
__pycache__/
28+
2629
CLAUDE.md
27-
test/data/logs/

Cargo.lock

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ quick-xml = { version = "0.37", features = ["serialize"] } # For XML serializat
1818

1919
dataflow-rs = "0.1.28"
2020
# dataflow-rs = { path = "../dataflow-rs" }
21-
swift-mt-message = { version = "2.3.6" }
21+
swift-mt-message = { version = "2.3.7" }
2222
# swift-mt-message = { path = "../SwiftMTMessage/swift-mt-message" }
2323
mx-message = "0.1.9"
2424

src/handlers.rs

Lines changed: 425 additions & 33 deletions
Large diffs are not rendered by default.

src/parse_mt.rs

Lines changed: 41 additions & 134 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ use dataflow_rs::engine::{
77
};
88
use serde_json::{Value, json};
99
use swift_mt_message::SwiftParser;
10-
use tracing::{debug, error, info, instrument, warn};
10+
use tracing::{debug, error, info, instrument};
1111

1212
pub struct ParseMT;
1313

@@ -88,20 +88,10 @@ impl ParseMT {
8888

8989
debug!(method = %method, "Determined MT103 processing method");
9090

91-
match serde_json::to_value(&mt103_message) {
92-
Ok(json_value) => {
93-
debug!("MT103 JSON conversion successful");
94-
json_value
95-
}
96-
Err(e) => {
97-
error!(error = ?e, "MT103 JSON conversion failed");
98-
json!({
99-
"conversion_error": format!("{:?}", e),
100-
"message_type": message_type,
101-
"raw_payload": payload
102-
})
103-
}
104-
}
91+
serde_json::to_value(&mt103_message).map_err(|e| {
92+
error!(error = ?e, "MT103 JSON conversion failed");
93+
DataflowError::Validation(format!("MT103 JSON conversion failed: {e}"))
94+
})?
10595
} else if message_type == "202" {
10696
let Some(mt202_message) = parsed_message.into_mt202() else {
10797
error!("Failed to convert SwiftMessage to MT202");
@@ -122,20 +112,10 @@ impl ParseMT {
122112

123113
debug!(method = %method, "Determined MT202 processing method");
124114

125-
match serde_json::to_value(&mt202_message) {
126-
Ok(json_value) => {
127-
debug!("MT202 JSON conversion successful");
128-
json_value
129-
}
130-
Err(e) => {
131-
error!(error = ?e, "MT202 JSON conversion failed");
132-
json!({
133-
"conversion_error": format!("{:?}", e),
134-
"message_type": message_type,
135-
"raw_payload": payload
136-
})
137-
}
138-
}
115+
serde_json::to_value(&mt202_message).map_err(|e| {
116+
error!(error = ?e, "MT202 JSON conversion failed");
117+
DataflowError::Validation(format!("MT202 JSON conversion failed: {e}"))
118+
})?
139119
} else if message_type == "205" {
140120
let Some(mt205_message) = parsed_message.into_mt205() else {
141121
error!("Failed to convert SwiftMessage to MT205");
@@ -156,20 +136,10 @@ impl ParseMT {
156136

157137
debug!(method = %method, "Determined MT205 processing method");
158138

159-
match serde_json::to_value(&mt205_message) {
160-
Ok(json_value) => {
161-
debug!("MT205 JSON conversion successful");
162-
json_value
163-
}
164-
Err(e) => {
165-
error!(error = ?e, "MT205 JSON conversion failed");
166-
json!({
167-
"conversion_error": format!("{:?}", e),
168-
"message_type": message_type,
169-
"raw_payload": payload
170-
})
171-
}
172-
}
139+
serde_json::to_value(&mt205_message).map_err(|e| {
140+
error!(error = ?e, "MT205 JSON conversion failed");
141+
DataflowError::Validation(format!("MT205 JSON conversion failed: {e}"))
142+
})?
173143
} else if message_type == "900" {
174144
let Some(mt900_message) = parsed_message.into_mt900() else {
175145
error!("Failed to convert SwiftMessage to MT900");
@@ -181,20 +151,10 @@ impl ParseMT {
181151
method = "normal".to_string();
182152
debug!("Processing MT900 with normal method");
183153

184-
match serde_json::to_value(&mt900_message) {
185-
Ok(json_value) => {
186-
debug!("MT900 JSON conversion successful");
187-
json_value
188-
}
189-
Err(e) => {
190-
error!(error = ?e, "MT900 JSON conversion failed");
191-
json!({
192-
"conversion_error": format!("{:?}", e),
193-
"message_type": message_type,
194-
"raw_payload": payload
195-
})
196-
}
197-
}
154+
serde_json::to_value(&mt900_message).map_err(|e| {
155+
error!(error = ?e, "MT900 JSON conversion failed");
156+
DataflowError::Validation(format!("MT900 JSON conversion failed: {e}"))
157+
})?
198158
} else if message_type == "910" {
199159
let Some(mt910_message) = parsed_message.into_mt910() else {
200160
error!("Failed to convert SwiftMessage to MT910");
@@ -206,20 +166,10 @@ impl ParseMT {
206166
method = "normal".to_string();
207167
debug!("Processing MT910 with normal method");
208168

209-
match serde_json::to_value(&mt910_message) {
210-
Ok(json_value) => {
211-
debug!("MT910 JSON conversion successful");
212-
json_value
213-
}
214-
Err(e) => {
215-
error!(error = ?e, "MT910 JSON conversion failed");
216-
json!({
217-
"conversion_error": format!("{:?}", e),
218-
"message_type": message_type,
219-
"raw_payload": payload
220-
})
221-
}
222-
}
169+
serde_json::to_value(&mt910_message).map_err(|e| {
170+
error!(error = ?e, "MT910 JSON conversion failed");
171+
DataflowError::Validation(format!("MT910 JSON conversion failed: {e}"))
172+
})?
223173
} else if message_type == "192" {
224174
let Some(mt192_message) = parsed_message.into_mt192() else {
225175
error!("Failed to convert SwiftMessage to MT192");
@@ -231,20 +181,10 @@ impl ParseMT {
231181
method = "normal".to_string();
232182
debug!("Processing MT192 with normal method");
233183

234-
match serde_json::to_value(&mt192_message) {
235-
Ok(json_value) => {
236-
debug!("MT192 JSON conversion successful");
237-
json_value
238-
}
239-
Err(e) => {
240-
error!(error = ?e, "MT192 JSON conversion failed");
241-
json!({
242-
"conversion_error": format!("{:?}", e),
243-
"message_type": message_type,
244-
"raw_payload": payload
245-
})
246-
}
247-
}
184+
serde_json::to_value(&mt192_message).map_err(|e| {
185+
error!(error = ?e, "MT192 JSON conversion failed");
186+
DataflowError::Validation(format!("MT192 JSON conversion failed: {e}"))
187+
})?
248188
} else if message_type == "292" {
249189
let Some(mt292_message) = parsed_message.into_mt292() else {
250190
error!("Failed to convert SwiftMessage to MT292");
@@ -256,20 +196,10 @@ impl ParseMT {
256196
method = "normal".to_string();
257197
debug!("Processing MT292 with normal method");
258198

259-
match serde_json::to_value(&mt292_message) {
260-
Ok(json_value) => {
261-
debug!("MT292 JSON conversion successful");
262-
json_value
263-
}
264-
Err(e) => {
265-
error!(error = ?e, "MT292 JSON conversion failed");
266-
json!({
267-
"conversion_error": format!("{:?}", e),
268-
"message_type": message_type,
269-
"raw_payload": payload
270-
})
271-
}
272-
}
199+
serde_json::to_value(&mt292_message).map_err(|e| {
200+
error!(error = ?e, "MT292 JSON conversion failed");
201+
DataflowError::Validation(format!("MT292 JSON conversion failed: {e}"))
202+
})?
273203
} else if message_type == "196" {
274204
let Some(mt196_message) = parsed_message.into_mt196() else {
275205
error!("Failed to convert SwiftMessage to MT196");
@@ -281,20 +211,10 @@ impl ParseMT {
281211
method = "normal".to_string();
282212
debug!("Processing MT196 with normal method");
283213

284-
match serde_json::to_value(&mt196_message) {
285-
Ok(json_value) => {
286-
debug!("MT196 JSON conversion successful");
287-
json_value
288-
}
289-
Err(e) => {
290-
error!(error = ?e, "MT196 JSON conversion failed");
291-
json!({
292-
"conversion_error": format!("{:?}", e),
293-
"message_type": message_type,
294-
"raw_payload": payload
295-
})
296-
}
297-
}
214+
serde_json::to_value(&mt196_message).map_err(|e| {
215+
error!(error = ?e, "MT196 JSON conversion failed");
216+
DataflowError::Validation(format!("MT196 JSON conversion failed: {e}"))
217+
})?
298218
} else if message_type == "296" {
299219
let Some(mt296_message) = parsed_message.into_mt296() else {
300220
error!("Failed to convert SwiftMessage to MT296");
@@ -306,28 +226,15 @@ impl ParseMT {
306226
method = "normal".to_string();
307227
debug!("Processing MT296 with normal method");
308228

309-
match serde_json::to_value(&mt296_message) {
310-
Ok(json_value) => {
311-
debug!("MT296 JSON conversion successful");
312-
json_value
313-
}
314-
Err(e) => {
315-
error!(error = ?e, "MT296 JSON conversion failed");
316-
json!({
317-
"conversion_error": format!("{:?}", e),
318-
"message_type": message_type,
319-
"raw_payload": payload
320-
})
321-
}
322-
}
229+
serde_json::to_value(&mt296_message).map_err(|e| {
230+
error!(error = ?e, "MT296 JSON conversion failed");
231+
DataflowError::Validation(format!("MT296 JSON conversion failed: {e}"))
232+
})?
323233
} else {
324-
method = "normal".to_string();
325-
warn!(message_type = %message_type, "Unsupported message type encountered");
326-
json!({
327-
"conversion_error": "Unsupported message type",
328-
"message_type": message_type,
329-
"raw_payload": payload
330-
})
234+
error!(message_type = %message_type, "Unsupported message type encountered");
235+
return Err(DataflowError::Validation(format!(
236+
"Unsupported message type: {message_type}"
237+
)));
331238
};
332239

333240
// Store the parsed result in message data

src/parse_mx.rs

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,11 @@ impl AsyncFunctionHandler for ParseMX {
3838
.data
3939
.get(input_field_name)
4040
.and_then(Value::as_str)
41-
.unwrap_or("")
41+
.ok_or_else(|| {
42+
DataflowError::Validation(format!(
43+
"Field {input_field_name} not found or not a string in message data for MX parsing"
44+
))
45+
})?
4246
.to_string()
4347
};
4448

@@ -51,11 +55,19 @@ impl AsyncFunctionHandler for ParseMX {
5155
let message_type = Self::extract_message_type(document_xmlns, app_hdr_content.clone())?;
5256
info!("Message type: {:?}", message_type);
5357

54-
let header = Self::parse_header(&message_type, &app_hdr_content.unwrap_or("".to_string()))
55-
.unwrap_or(Value::Null);
56-
let document =
57-
Self::parse_document(&message_type, &document_content.unwrap_or("".to_string()))
58-
.unwrap_or(Value::Null);
58+
let app_hdr_content = app_hdr_content.ok_or_else(|| {
59+
DataflowError::Validation(
60+
"Failed to extract application header content from MX message".to_string(),
61+
)
62+
})?;
63+
let document_content = document_content.ok_or_else(|| {
64+
DataflowError::Validation(
65+
"Failed to extract document content from MX message".to_string(),
66+
)
67+
})?;
68+
69+
let header = Self::parse_header(&message_type, &app_hdr_content)?;
70+
let document = Self::parse_document(&message_type, &document_content)?;
5971

6072
let parsed_result = json!({
6173
"header": header,

src/publish_mt.rs

Lines changed: 21 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,9 @@ use dataflow_rs::engine::{
88
};
99
use serde_json::Value;
1010
use swift_mt_message::SwiftMessage;
11-
use swift_mt_message::messages::{MT103, MT110, MT111, MT112, MT199, MT202, MT205, MT940, MT942};
11+
use swift_mt_message::messages::{
12+
MT103, MT110, MT111, MT112, MT199, MT202, MT205, MT299, MT940, MT942,
13+
};
1214
use tracing::{debug, error, instrument};
1315

1416
pub struct PublishMT;
@@ -122,12 +124,24 @@ impl AsyncFunctionHandler for PublishMT {
122124

123125
debug!(target_message_type = %target_message_type, "Processing pacs.002 to MT199/MT299 transformation");
124126

125-
// Both MT199 and MT299 use the same structure, so we can use MT199 for both
126-
let data: SwiftMessage<MT199> = serde_json::from_str(&json_str).map_err(|e| {
127-
error!(error = ?e, target_message_type = %target_message_type, "Failed to parse JSON string for pacs.002");
128-
DataflowError::Validation(format!("Failed to parse JSON string for pacs.002 (target: {target_message_type}): {e}"))
129-
})?;
130-
data.to_mt_message()
127+
if target_message_type == "199" {
128+
let data: SwiftMessage<MT199> = serde_json::from_str(&json_str).map_err(|e| {
129+
error!(error = ?e, target_message_type = %target_message_type, "Failed to parse JSON string for pacs.002 MT199");
130+
DataflowError::Validation(format!("Failed to parse JSON string for pacs.002 MT199: {e}"))
131+
})?;
132+
data.to_mt_message()
133+
} else if target_message_type == "299" {
134+
let data: SwiftMessage<MT299> = serde_json::from_str(&json_str).map_err(|e| {
135+
error!(error = ?e, target_message_type = %target_message_type, "Failed to parse JSON string for pacs.002 MT299");
136+
DataflowError::Validation(format!("Failed to parse JSON string for pacs.002 MT299: {e}"))
137+
})?;
138+
data.to_mt_message()
139+
} else {
140+
error!(target_message_type = %target_message_type, "Invalid target message type for pacs.002");
141+
return Err(DataflowError::Validation(format!(
142+
"Invalid target message type for pacs.002: {target_message_type}"
143+
)));
144+
}
131145
} else if source_format == "camt.107.001.01" {
132146
debug!("Processing camt.107 to MT110 transformation");
133147

0 commit comments

Comments
 (0)