Skip to content

Commit d5b59b3

Browse files
Merge pull request #76 from code0-tech/add-flow-validation
add-flow-validation
2 parents 2b196b3 + c721726 commit d5b59b3

File tree

11 files changed

+1014
-5
lines changed

11 files changed

+1014
-5
lines changed

Cargo.lock

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

Cargo.toml

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ code0-definition-reader = "0.0.10"
1717
tonic-health = "0.14.1"
1818
async-nats = "0.42.0"
1919
futures-core = "0.3.31"
20+
regex = "1.11.2"
21+
serde_json = "1.0.143"
2022

2123
[lib]
2224
doctest = true
@@ -26,4 +28,5 @@ default = ["all"]
2628
flow_definition = []
2729
flow_config = []
2830
flow_health = []
29-
all = ["flow_definition", "flow_config", "flow_health"]
31+
flow_validator = []
32+
all = ["flow_definition", "flow_config", "flow_health", "flow_validator"]

src/flow_validator/mod.rs

Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
mod rule;
2+
3+
use rule::{
4+
contains_key::apply_contains_key,
5+
contains_type::apply_contains_type,
6+
item_of_collection::apply_item_of_collection,
7+
number_range::apply_number_range,
8+
regex::apply_regex,
9+
violation::{DataTypeNotFoundRuleViolation, DataTypeRuleError, DataTypeRuleViolation},
10+
};
11+
12+
use tucana::shared::{ExecutionDataType, ValidationFlow, Value, execution_data_type_rule::Config};
13+
pub struct VerificationResult;
14+
15+
pub fn verify_flow(flow: ValidationFlow, body: Value) -> Result<(), DataTypeRuleError> {
16+
let input_type = match &flow.input_type_identifier {
17+
Some(r) => r.clone(),
18+
None => return Ok(()), //Returns directly because no rule is given. The body is ok and will not be concidered
19+
};
20+
21+
let data_type = match flow
22+
.data_types
23+
.iter()
24+
.find(|dt| dt.identifier == input_type)
25+
{
26+
Some(dt) => dt.clone(),
27+
None => {
28+
return Err(DataTypeRuleError {
29+
violations: vec![DataTypeRuleViolation::DataTypeNotFound(
30+
DataTypeNotFoundRuleViolation {
31+
data_type: input_type,
32+
},
33+
)],
34+
});
35+
}
36+
};
37+
38+
verify_data_type_rules(body, data_type, &flow.data_types)
39+
}
40+
41+
//Verifies the rules on the datatype of the body thats given
42+
fn verify_data_type_rules(
43+
body: Value,
44+
data_type: ExecutionDataType,
45+
availabe_data_types: &Vec<ExecutionDataType>,
46+
) -> Result<(), DataTypeRuleError> {
47+
let mut violations: Vec<DataTypeRuleViolation> = Vec::new();
48+
for rule in data_type.rules {
49+
let rule_config = match rule.config {
50+
None => continue,
51+
Some(config) => config,
52+
};
53+
54+
match rule_config {
55+
Config::NumberRange(config) => {
56+
match apply_number_range(config, &body, &String::from("value")) {
57+
Ok(_) => continue,
58+
Err(violation) => {
59+
violations.extend(violation.violations);
60+
continue;
61+
}
62+
}
63+
}
64+
Config::ItemOfCollection(config) => {
65+
match apply_item_of_collection(config, &body, "key") {
66+
Ok(_) => continue,
67+
Err(violation) => {
68+
violations.extend(violation.violations);
69+
continue;
70+
}
71+
}
72+
}
73+
Config::ContainsType(config) => {
74+
match apply_contains_type(config, &availabe_data_types, &body) {
75+
Ok(_) => continue,
76+
Err(violation) => {
77+
violations.extend(violation.violations);
78+
continue;
79+
}
80+
}
81+
}
82+
Config::Regex(config) => {
83+
match apply_regex(config, &body) {
84+
Ok(_) => continue,
85+
Err(violation) => {
86+
violations.extend(violation.violations);
87+
continue;
88+
}
89+
};
90+
}
91+
Config::ContainsKey(config) => {
92+
match apply_contains_key(config, &body, &availabe_data_types) {
93+
Ok(_) => continue,
94+
Err(violation) => {
95+
violations.extend(violation.violations);
96+
continue;
97+
}
98+
};
99+
}
100+
}
101+
}
102+
103+
if violations.is_empty() {
104+
Ok(())
105+
} else {
106+
Err(DataTypeRuleError { violations })
107+
}
108+
}
109+
110+
fn get_data_type_by_id(
111+
data_types: &Vec<ExecutionDataType>,
112+
identifier: &String,
113+
) -> Option<ExecutionDataType> {
114+
data_types
115+
.iter()
116+
.find(|data_type| &data_type.identifier == identifier)
117+
.cloned()
118+
}
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
use super::violation::ContainsKeyRuleViolation;
2+
use super::violation::DataTypeRuleError;
3+
use super::violation::DataTypeRuleViolation;
4+
use super::violation::MissingDataTypeRuleDefinition;
5+
use tucana::shared::ExecutionDataType;
6+
use tucana::shared::ExecutionDataTypeContainsKeyRuleConfig;
7+
use tucana::shared::Value;
8+
use tucana::shared::helper::path::expect_kind;
9+
use tucana::shared::value::Kind;
10+
use crate::flow_validator::{get_data_type_by_id, verify_data_type_rules};
11+
12+
/// # Data Type Validation Behavior
13+
///
14+
/// This function checks if a specific key exists in the JSON body and validates
15+
/// if its value matches the expected data type.
16+
///
17+
/// ## Process:
18+
/// 1. Searches for the specified key in the JSON body
19+
/// 2. If the key is found, retrieves the associated data type definition from the flow
20+
/// 3. Validates that the value matches the expected data type
21+
///
22+
/// ## Error Handling:
23+
/// - Returns a `ContainsKeyRuleViolation` if the specified key is not found in the body
24+
/// - Returns a `MissingDataTypeRuleDefinition` if the referenced data type doesn't exist
25+
/// - Returns validation errors if the value doesn't match the expected data type
26+
pub fn apply_contains_key(
27+
rule: ExecutionDataTypeContainsKeyRuleConfig,
28+
body: &Value,
29+
available_data_types: &Vec<ExecutionDataType>,
30+
) -> Result<(), DataTypeRuleError> {
31+
let identifier = rule.data_type_identifier;
32+
33+
if let Some(Kind::StructValue(_)) = &body.kind {
34+
let value = match expect_kind(&identifier, &body) {
35+
Some(value) => Value {
36+
kind: Some(value.to_owned()),
37+
},
38+
None => {
39+
let error = ContainsKeyRuleViolation {
40+
missing_key: identifier,
41+
};
42+
43+
return Err(DataTypeRuleError {
44+
violations: vec![DataTypeRuleViolation::ContainsKey(error)],
45+
});
46+
}
47+
};
48+
49+
let data_type = match get_data_type_by_id(&available_data_types, &identifier) {
50+
Some(data_type) => data_type,
51+
None => {
52+
let error = MissingDataTypeRuleDefinition {
53+
missing_type: identifier,
54+
};
55+
56+
return Err(DataTypeRuleError {
57+
violations: vec![DataTypeRuleViolation::MissingDataType(error)],
58+
});
59+
}
60+
};
61+
62+
return verify_data_type_rules(value, data_type, available_data_types);
63+
} else {
64+
return Err(DataTypeRuleError {
65+
violations: vec![DataTypeRuleViolation::ContainsKey(
66+
ContainsKeyRuleViolation {
67+
missing_key: identifier,
68+
},
69+
)],
70+
});
71+
}
72+
}
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
use super::violation::{DataTypeRuleError, DataTypeRuleViolation, InvalidFormatRuleViolation};
2+
use tucana::shared::{
3+
ExecutionDataType, ExecutionDataTypeContainsTypeRuleConfig, Value, value::Kind,
4+
};
5+
use crate::flow_validator::{get_data_type_by_id, verify_data_type_rules};
6+
7+
/// # Item of Collection Validation
8+
///
9+
/// This function validates if a value is contained within a predefined collection of allowed items.
10+
///
11+
/// ## Process:
12+
/// 1. Checks if the provided value is present in the collection of allowed items
13+
///
14+
/// ## Error Handling:
15+
/// - Returns an `ItemOfCollectionRuleViolation` if the value is not found in the collection
16+
///
17+
pub fn apply_contains_type(
18+
rule: ExecutionDataTypeContainsTypeRuleConfig,
19+
available_data_types: &Vec<ExecutionDataType>,
20+
body: &Value,
21+
) -> Result<(), DataTypeRuleError> {
22+
let identifier = rule.data_type_identifier;
23+
let real_body = match &body.kind {
24+
Some(body) => body.clone(),
25+
None => {
26+
return Err(DataTypeRuleError {
27+
violations: vec![DataTypeRuleViolation::InvalidFormat(
28+
InvalidFormatRuleViolation {
29+
expected_format: identifier,
30+
value: String::from("other"),
31+
},
32+
)],
33+
});
34+
}
35+
};
36+
37+
match real_body {
38+
Kind::ListValue(list) => {
39+
let real_data_type = get_data_type_by_id(available_data_types, &identifier);
40+
41+
if let Some(data_type) = real_data_type {
42+
let mut rule_errors: Option<DataTypeRuleError> = None;
43+
44+
for value in list.values {
45+
match verify_data_type_rules(value, data_type.clone(), &available_data_types) {
46+
Ok(_) => {}
47+
Err(errors) => {
48+
rule_errors = Some(errors);
49+
}
50+
}
51+
}
52+
53+
if let Some(errors) = rule_errors {
54+
return Err(errors);
55+
} else {
56+
return Ok(());
57+
}
58+
}
59+
}
60+
_ => {
61+
return Err(DataTypeRuleError {
62+
violations: vec![DataTypeRuleViolation::InvalidFormat(
63+
InvalidFormatRuleViolation {
64+
expected_format: identifier,
65+
value: String::from("other"),
66+
},
67+
)],
68+
});
69+
}
70+
}
71+
72+
Ok(())
73+
}

0 commit comments

Comments
 (0)