Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 6 additions & 4 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 4 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ code0-definition-reader = "0.0.10"
tonic-health = "0.14.1"
async-nats = "0.42.0"
futures-core = "0.3.31"
regex = "1.11.2"
serde_json = "1.0.143"

[lib]
doctest = true
Expand All @@ -26,4 +28,5 @@ default = ["all"]
flow_definition = []
flow_config = []
flow_health = []
all = ["flow_definition", "flow_config", "flow_health"]
flow_validator = []
all = ["flow_definition", "flow_config", "flow_health", "flow_validator"]
118 changes: 118 additions & 0 deletions src/flow_validator/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
mod rule;

use rule::{
contains_key::apply_contains_key,
contains_type::apply_contains_type,
item_of_collection::apply_item_of_collection,
number_range::apply_number_range,
regex::apply_regex,
violation::{DataTypeNotFoundRuleViolation, DataTypeRuleError, DataTypeRuleViolation},
};

use tucana::shared::{ExecutionDataType, ValidationFlow, Value, execution_data_type_rule::Config};
pub struct VerificationResult;

pub fn verify_flow(flow: ValidationFlow, body: Value) -> Result<(), DataTypeRuleError> {
let input_type = match &flow.input_type_identifier {
Some(r) => r.clone(),
None => return Ok(()), //Returns directly because no rule is given. The body is ok and will not be concidered
};

let data_type = match flow
.data_types
.iter()
.find(|dt| dt.identifier == input_type)
{
Some(dt) => dt.clone(),
None => {
return Err(DataTypeRuleError {
violations: vec![DataTypeRuleViolation::DataTypeNotFound(
DataTypeNotFoundRuleViolation {
data_type: input_type,
},
)],
});
}
};

verify_data_type_rules(body, data_type, &flow.data_types)
}

//Verifies the rules on the datatype of the body thats given
fn verify_data_type_rules(
body: Value,
data_type: ExecutionDataType,
availabe_data_types: &Vec<ExecutionDataType>,
) -> Result<(), DataTypeRuleError> {
let mut violations: Vec<DataTypeRuleViolation> = Vec::new();
for rule in data_type.rules {
let rule_config = match rule.config {
None => continue,
Some(config) => config,
};

match rule_config {
Config::NumberRange(config) => {
match apply_number_range(config, &body, &String::from("value")) {
Ok(_) => continue,
Err(violation) => {
violations.extend(violation.violations);
continue;
}
}
}
Config::ItemOfCollection(config) => {
match apply_item_of_collection(config, &body, "key") {
Ok(_) => continue,
Err(violation) => {
violations.extend(violation.violations);
continue;
}
}
}
Config::ContainsType(config) => {
match apply_contains_type(config, &availabe_data_types, &body) {
Ok(_) => continue,
Err(violation) => {
violations.extend(violation.violations);
continue;
}
}
}
Config::Regex(config) => {
match apply_regex(config, &body) {
Ok(_) => continue,
Err(violation) => {
violations.extend(violation.violations);
continue;
}
};
}
Config::ContainsKey(config) => {
match apply_contains_key(config, &body, &availabe_data_types) {
Ok(_) => continue,
Err(violation) => {
violations.extend(violation.violations);
continue;
}
};
}
}
}

if violations.is_empty() {
Ok(())
} else {
Err(DataTypeRuleError { violations })
}
}

fn get_data_type_by_id(
data_types: &Vec<ExecutionDataType>,
identifier: &String,
) -> Option<ExecutionDataType> {
data_types
.iter()
.find(|data_type| &data_type.identifier == identifier)
.cloned()
}
72 changes: 72 additions & 0 deletions src/flow_validator/rule/contains_key.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
use super::violation::ContainsKeyRuleViolation;
use super::violation::DataTypeRuleError;
use super::violation::DataTypeRuleViolation;
use super::violation::MissingDataTypeRuleDefinition;
use tucana::shared::ExecutionDataType;
use tucana::shared::ExecutionDataTypeContainsKeyRuleConfig;
use tucana::shared::Value;
use tucana::shared::helper::path::expect_kind;
use tucana::shared::value::Kind;
use crate::flow_validator::{get_data_type_by_id, verify_data_type_rules};

/// # Data Type Validation Behavior
///
/// This function checks if a specific key exists in the JSON body and validates
/// if its value matches the expected data type.
///
/// ## Process:
/// 1. Searches for the specified key in the JSON body
/// 2. If the key is found, retrieves the associated data type definition from the flow
/// 3. Validates that the value matches the expected data type
///
/// ## Error Handling:
/// - Returns a `ContainsKeyRuleViolation` if the specified key is not found in the body
/// - Returns a `MissingDataTypeRuleDefinition` if the referenced data type doesn't exist
/// - Returns validation errors if the value doesn't match the expected data type
pub fn apply_contains_key(
rule: ExecutionDataTypeContainsKeyRuleConfig,
body: &Value,
available_data_types: &Vec<ExecutionDataType>,
) -> Result<(), DataTypeRuleError> {
let identifier = rule.data_type_identifier;

if let Some(Kind::StructValue(_)) = &body.kind {
let value = match expect_kind(&identifier, &body) {
Some(value) => Value {
kind: Some(value.to_owned()),
},
None => {
let error = ContainsKeyRuleViolation {
missing_key: identifier,
};

return Err(DataTypeRuleError {
violations: vec![DataTypeRuleViolation::ContainsKey(error)],
});
}
};

let data_type = match get_data_type_by_id(&available_data_types, &identifier) {
Some(data_type) => data_type,
None => {
let error = MissingDataTypeRuleDefinition {
missing_type: identifier,
};

return Err(DataTypeRuleError {
violations: vec![DataTypeRuleViolation::MissingDataType(error)],
});
}
};

return verify_data_type_rules(value, data_type, available_data_types);
} else {
return Err(DataTypeRuleError {
violations: vec![DataTypeRuleViolation::ContainsKey(
ContainsKeyRuleViolation {
missing_key: identifier,
},
)],
});
}
}
73 changes: 73 additions & 0 deletions src/flow_validator/rule/contains_type.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
use super::violation::{DataTypeRuleError, DataTypeRuleViolation, InvalidFormatRuleViolation};
use tucana::shared::{
ExecutionDataType, ExecutionDataTypeContainsTypeRuleConfig, Value, value::Kind,
};
use crate::flow_validator::{get_data_type_by_id, verify_data_type_rules};

/// # Item of Collection Validation
///
/// This function validates if a value is contained within a predefined collection of allowed items.
///
/// ## Process:
/// 1. Checks if the provided value is present in the collection of allowed items
///
/// ## Error Handling:
/// - Returns an `ItemOfCollectionRuleViolation` if the value is not found in the collection
///
pub fn apply_contains_type(
rule: ExecutionDataTypeContainsTypeRuleConfig,
available_data_types: &Vec<ExecutionDataType>,
body: &Value,
) -> Result<(), DataTypeRuleError> {
let identifier = rule.data_type_identifier;
let real_body = match &body.kind {
Some(body) => body.clone(),
None => {
return Err(DataTypeRuleError {
violations: vec![DataTypeRuleViolation::InvalidFormat(
InvalidFormatRuleViolation {
expected_format: identifier,
value: String::from("other"),
},
)],
});
}
};

match real_body {
Kind::ListValue(list) => {
let real_data_type = get_data_type_by_id(available_data_types, &identifier);

if let Some(data_type) = real_data_type {
let mut rule_errors: Option<DataTypeRuleError> = None;

for value in list.values {
match verify_data_type_rules(value, data_type.clone(), &available_data_types) {
Ok(_) => {}
Err(errors) => {
rule_errors = Some(errors);
}
}
}

if let Some(errors) = rule_errors {
return Err(errors);
} else {
return Ok(());
}
}
}
_ => {
return Err(DataTypeRuleError {
violations: vec![DataTypeRuleViolation::InvalidFormat(
InvalidFormatRuleViolation {
expected_format: identifier,
value: String::from("other"),
},
)],
});
}
}

Ok(())
}
Loading