1use crate::{ParseError, SwiftParser, SwiftValidationError};
2use async_trait::async_trait;
3use dataflow_rs::engine::error::DataflowError;
4use dataflow_rs::engine::{
5 AsyncFunctionHandler, FunctionConfig,
6 error::Result,
7 message::{Change, Message},
8};
9use datalogic_rs::DataLogic;
10use serde_json::{Value, json};
11use std::sync::Arc;
12use tracing::{debug, instrument};
13
14pub struct Validate;
15
16#[async_trait]
17impl AsyncFunctionHandler for Validate {
18 #[instrument(skip(self, message, config, _datalogic))]
19 async fn execute(
20 &self,
21 message: &mut Message,
22 config: &FunctionConfig,
23 _datalogic: Arc<DataLogic>,
24 ) -> Result<(usize, Vec<Change>)> {
25 debug!("Starting MT message validation");
26
27 let input = match config {
29 FunctionConfig::Custom { input, .. } => input,
30 _ => {
31 return Err(DataflowError::Validation(
32 "Invalid configuration type".to_string(),
33 ));
34 }
35 };
36
37 let source_field = input.get("source").and_then(Value::as_str).ok_or_else(|| {
38 DataflowError::Validation("'source' parameter is required".to_string())
39 })?;
40
41 let target_field = input.get("target").and_then(Value::as_str).ok_or_else(|| {
42 DataflowError::Validation("'target' parameter is required".to_string())
43 })?;
44
45 let mt_content = if source_field == "payload" {
47 if let Some(s) = message.payload.as_str() {
49 s.to_string()
50 } else {
51 message.payload.to_string().trim_matches('"').to_string()
53 }
54 } else {
55 let field_value = message.data().get(source_field).ok_or_else(|| {
57 DataflowError::Validation(format!(
58 "MT message field '{}' not found in message data",
59 source_field
60 ))
61 })?;
62
63 if let Some(mt_msg) = field_value.get("mt_message").and_then(Value::as_str) {
65 mt_msg.to_string()
66 } else if let Some(s) = field_value.as_str() {
67 s.to_string()
69 } else {
70 return Err(DataflowError::Validation(format!(
71 "Field '{}' does not contain a valid MT message",
72 source_field
73 )));
74 }
75 };
76
77 debug!(
78 source_field = %source_field,
79 target_field = %target_field,
80 "Validating MT message"
81 );
82
83 let validation_result = self.validate_mt_message(&mt_content)?;
85
86 message
88 .data_mut()
89 .as_object_mut()
90 .unwrap()
91 .insert(target_field.to_string(), validation_result.clone());
92
93 message.metadata_mut().as_object_mut().unwrap().insert(
95 "validation".to_string(),
96 json!({
97 "validated": true,
98 "timestamp": chrono::Utc::now().to_rfc3339(),
99 }),
100 );
101
102 message.invalidate_context_cache();
103
104 Ok((
105 200,
106 vec![Change {
107 path: Arc::from(format!("data.{}", target_field)),
108 old_value: Arc::new(Value::Null),
109 new_value: Arc::new(validation_result),
110 }],
111 ))
112 }
113}
114
115impl Validate {
116 fn validate_mt_message(&self, mt_content: &str) -> Result<Value> {
117 let mut errors = Vec::new();
118 let mut message_type: Option<String> = None;
119
120 match SwiftParser::parse_auto(mt_content) {
122 Ok(parsed_message) => {
123 message_type = Some(parsed_message.message_type().to_string());
125
126 let validation_errors = self.validate_network_rules(&parsed_message);
128
129 if !validation_errors.is_empty() {
130 for validation_error in validation_errors {
132 errors.push(self.format_validation_error(&validation_error));
133 }
134 }
135 }
136 Err(parse_error) => {
137 errors.push(format!("Parse error: {}", parse_error));
139
140 match &parse_error {
142 ParseError::InvalidFieldFormat(e) => {
143 errors.push(format!(
144 "Field {} - {}: Invalid value '{}', expected format: {}",
145 e.field_tag, e.component_name, e.value, e.format_spec
146 ));
147 }
148 ParseError::MissingRequiredField {
149 field_tag,
150 field_name,
151 ..
152 } => {
153 errors.push(format!(
154 "Missing required field: {} ({})",
155 field_tag, field_name
156 ));
157 }
158 ParseError::InvalidFormat { message } => {
159 errors.push(format!("Invalid message format: {}", message));
160 }
161 ParseError::ValidationFailed {
162 errors: validation_errors,
163 } => {
164 for validation_error in validation_errors {
165 errors.push(validation_error.to_string());
166 }
167 }
168 _ => {
169 }
171 }
172 }
173 }
174
175 let is_valid = errors.is_empty();
176
177 let mut result = json!({
178 "valid": is_valid,
179 "errors": errors,
180 "timestamp": chrono::Utc::now().to_rfc3339(),
181 });
182
183 if let Some(msg_type) = message_type {
185 result
186 .as_object_mut()
187 .unwrap()
188 .insert("message_type".to_string(), json!(msg_type));
189 }
190
191 Ok(result)
192 }
193
194 fn validate_network_rules(
196 &self,
197 parsed_message: &crate::parsed_message::ParsedSwiftMessage,
198 ) -> Vec<SwiftValidationError> {
199 use crate::parsed_message::ParsedSwiftMessage;
200
201 match parsed_message {
203 ParsedSwiftMessage::MT101(msg) => msg.fields.validate_network_rules(false),
204 ParsedSwiftMessage::MT103(msg) => msg.fields.validate_network_rules(false),
205 ParsedSwiftMessage::MT104(msg) => msg.fields.validate_network_rules(false),
206 ParsedSwiftMessage::MT107(msg) => msg.fields.validate_network_rules(false),
207 ParsedSwiftMessage::MT110(msg) => msg.fields.validate_network_rules(false),
208 ParsedSwiftMessage::MT111(msg) => msg.fields.validate_network_rules(false),
209 ParsedSwiftMessage::MT112(msg) => msg.fields.validate_network_rules(false),
210 ParsedSwiftMessage::MT190(msg) => msg.fields.validate_network_rules(false),
211 ParsedSwiftMessage::MT191(msg) => msg.fields.validate_network_rules(false),
212 ParsedSwiftMessage::MT192(msg) => msg.fields.validate_network_rules(false),
213 ParsedSwiftMessage::MT196(msg) => msg.fields.validate_network_rules(false),
214 ParsedSwiftMessage::MT199(msg) => msg.fields.validate_network_rules(false),
215 ParsedSwiftMessage::MT200(msg) => msg.fields.validate_network_rules(false),
216 ParsedSwiftMessage::MT202(msg) => msg.fields.validate_network_rules(false),
217 ParsedSwiftMessage::MT204(msg) => msg.fields.validate_network_rules(false),
218 ParsedSwiftMessage::MT205(msg) => msg.fields.validate_network_rules(false),
219 ParsedSwiftMessage::MT210(msg) => msg.fields.validate_network_rules(false),
220 ParsedSwiftMessage::MT290(msg) => msg.fields.validate_network_rules(false),
221 ParsedSwiftMessage::MT291(msg) => msg.fields.validate_network_rules(false),
222 ParsedSwiftMessage::MT292(msg) => msg.fields.validate_network_rules(false),
223 ParsedSwiftMessage::MT296(msg) => msg.fields.validate_network_rules(false),
224 ParsedSwiftMessage::MT299(msg) => msg.fields.validate_network_rules(false),
225 ParsedSwiftMessage::MT900(msg) => msg.fields.validate_network_rules(false),
226 ParsedSwiftMessage::MT910(msg) => msg.fields.validate_network_rules(false),
227 ParsedSwiftMessage::MT920(msg) => msg.fields.validate_network_rules(false),
228 ParsedSwiftMessage::MT935(msg) => msg.fields.validate_network_rules(false),
229 ParsedSwiftMessage::MT940(msg) => msg.fields.validate_network_rules(false),
230 ParsedSwiftMessage::MT941(msg) => msg.fields.validate_network_rules(false),
231 ParsedSwiftMessage::MT942(msg) => msg.fields.validate_network_rules(false),
232 ParsedSwiftMessage::MT950(msg) => msg.fields.validate_network_rules(false),
233 }
234 }
235
236 fn format_validation_error(&self, error: &SwiftValidationError) -> String {
238 use crate::errors::SwiftValidationError;
239
240 match error {
241 SwiftValidationError::Format(err) => {
242 format!(
243 "[{}] Field {}: {} - Invalid value '{}' (expected: {})",
244 err.code, err.field, err.message, err.value, err.expected
245 )
246 }
247 SwiftValidationError::Content(err) => {
248 format!(
249 "[{}] Field {}: {} - Content '{}' violates requirement: {}",
250 err.code, err.field, err.message, err.content, err.requirements
251 )
252 }
253 SwiftValidationError::Relation(err) => {
254 let related = if !err.related_fields.is_empty() {
255 format!(" (related: {})", err.related_fields.join(", "))
256 } else {
257 String::new()
258 };
259 let instruction = err
260 .instruction_context
261 .as_ref()
262 .map(|ctx| format!(" [{}]", ctx))
263 .unwrap_or_default();
264 format!(
265 "[{}] Field {}: {}{}{}",
266 err.code, err.field, err.message, related, instruction
267 )
268 }
269 SwiftValidationError::Business(err) => {
270 let related = if !err.related_fields.is_empty() {
271 format!(" (related: {})", err.related_fields.join(", "))
272 } else {
273 String::new()
274 };
275 format!(
276 "[{}] Field {}: {} - Rule: {}{}",
277 err.code, err.field, err.message, err.rule_description, related
278 )
279 }
280 SwiftValidationError::General(err) => {
281 let category = err
282 .category
283 .as_ref()
284 .map(|c| format!(" [{}]", c))
285 .unwrap_or_default();
286 format!(
287 "[{}] Field {}: {} - Value '{}'{}",
288 err.code, err.field, err.message, err.value, category
289 )
290 }
291 }
292 }
293}