diff --git a/CHANGELOG.md b/CHANGELOG.md index 01143c1f..e4714cbc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - a separator for joining node values in `link` can be set with attribute `value_sep` - spreadsheet imports can now be configured with a fallback token column for annotation names not mentioned in a column map, an empty string means map to timeline directly +- graph_op `check` can now be configured to not let the entire processing chain fail, when a test fails, by setting `policy = "warn"` (default is `fail`) ### Fixed diff --git a/src/manipulator/check.rs b/src/manipulator/check.rs index 81441ca3..2228eb90 100644 --- a/src/manipulator/check.rs +++ b/src/manipulator/check.rs @@ -21,6 +21,16 @@ pub const MODULE_NAME: &str = "check"; pub struct Check { tests: Vec, report: Option, + #[serde(default)] + policy: FailurePolicy, +} + +#[derive(Default, Deserialize)] +#[serde(rename_all = "snake_case")] +enum FailurePolicy { + Warn, + #[default] + Fail, } #[derive(Deserialize)] @@ -53,7 +63,15 @@ impl Manipulator for Check { .map(|(d, _)| d) .collect_vec(); if !failed_checks.is_empty() { - let msg = StatusMessage::Failed(AnnattoError::ChecksFailed { failed_checks }); + let msg = match self.policy { + FailurePolicy::Warn => StatusMessage::Warning(format!( + "One or more checks failed:\n{}", + failed_checks.join("\n") + )), + FailurePolicy::Fail => { + StatusMessage::Failed(AnnattoError::ChecksFailed { failed_checks }) + } + }; if let Some(ref sender) = tx { sender.send(msg)?; } @@ -493,6 +511,47 @@ mod tests { Ok(()) } + #[test] + fn test_layer_check_fail_policy_warn() { + let gr = input_graph(false); + assert!(gr.is_ok()); + let mut g = gr.unwrap(); + let toml_path = "./tests/data/graph_op/check/serialized_layer_check_failing_warn.toml"; + if let Ok(s) = fs::read_to_string(toml_path) { + let processor_opt: Result = toml::from_str(s.as_str()); + assert!(processor_opt.is_ok()); + let check = processor_opt.unwrap(); + let (sender, receiver) = mpsc::channel(); + let dummy_value = temp_dir(); + let run = check.manipulate_corpus(&mut g, dummy_value.as_path(), Some(sender)); + assert!(run.is_ok()); + assert_eq!( + receiver + .into_iter() + .filter(|msg| matches!(msg, StatusMessage::Warning { .. })) + .count(), + 1 + ); + } + let toml_path_fail = "./tests/data/graph_op/check/serialized_layer_check_failing.toml"; + if let Ok(s) = fs::read_to_string(toml_path_fail) { + let processor_opt: Result = toml::from_str(s.as_str()); + assert!(processor_opt.is_ok()); + let check = processor_opt.unwrap(); + let (sender, receiver) = mpsc::channel(); + let dummy_value = temp_dir(); + let run = check.manipulate_corpus(&mut g, dummy_value.as_path(), Some(sender)); + assert!(run.is_ok()); + assert_eq!( + receiver + .into_iter() + .filter(|msg| matches!(msg, StatusMessage::Failed { .. })) + .count(), + 1 + ); + } + } + #[test] fn test_layer_test_to_aql_test() { let mut layers = BTreeMap::new(); diff --git a/tests/data/graph_op/check/serialized_layer_check_failing_warn.toml b/tests/data/graph_op/check/serialized_layer_check_failing_warn.toml new file mode 100644 index 00000000..04f2fbc3 --- /dev/null +++ b/tests/data/graph_op/check/serialized_layer_check_failing_warn.toml @@ -0,0 +1,15 @@ +# please note: This is NOT a workflow fragment, this is a serialization of `check`. +# In an annatto workflow the structure will vary due to flattenings and re-arrangments of data. +report = "list" +policy = "warn" + +[[tests]] +layers = { pos = [ "PRON" ], cat = [ "NP" ] } + +[[tests]] +layers = { deprel = ["subj"] } +edge = "->dep" + +[[tests]] +layers = { func = ["D0"] } +edge = ">"