From e09d572440670e752f780a1e7a5f78ace85b2107 Mon Sep 17 00:00:00 2001 From: Martin Zink Date: Wed, 8 Jul 2026 10:30:49 +0200 Subject: [PATCH] MINIFICPP-2869 Improve rust api property validation and property getters --- .../ubuntu_22_04_clang_arm_manifest.json | 6 +- .../minifi_rs_playground/Cargo.toml | 1 + .../dog_controller_service.rs | 21 +-- .../lorem_ipsum_controller_service.rs | 12 +- .../src/processors/generate_flow_file.rs | 28 ++-- .../generate_flow_file/properties.rs | 23 +-- .../src/processors/get_file.rs | 39 ++--- .../src/processors/get_file/properties.rs | 47 ++---- .../src/processors/kamikaze_processor.rs | 24 +-- .../kamikaze_processor/properties.rs | 15 +- .../src/processors/log_attribute.rs | 27 +--- .../processors/log_attribute/properties.rs | 33 ++-- .../src/processors/lorem_ipsum_cs_user.rs | 11 +- .../lorem_ipsum_cs_user/properties.rs | 16 +- .../src/processors/put_file.rs | 32 ++-- .../src/processors/put_file/properties.rs | 20 +-- .../put_file/unix_only_properties.rs | 10 +- .../src/processors/zoo_processor.rs | 12 +- minifi_rust/minifi_native/Cargo.toml | 3 + minifi_rust/minifi_native/src/api.rs | 5 +- .../minifi_native/src/api/attribute.rs | 4 + minifi_rust/minifi_native/src/api/errors.rs | 27 +++- minifi_rust/minifi_native/src/api/logger.rs | 18 ++- .../minifi_native/src/api/process_context.rs | 79 +-------- .../processor_wrappers/flow_file_transform.rs | 52 ++++++ .../utils/context_session_flowfile_bundle.rs | 2 +- minifi_rust/minifi_native/src/api/property.rs | 152 +++++++++++++----- .../minifi_native/src/api/relationship.rs | 8 + .../c_ffi/c_ffi_controller_service_context.rs | 2 +- .../minifi_native/src/c_ffi/c_ffi_property.rs | 26 +-- minifi_rust/minifi_native/src/lib.rs | 6 +- .../mock/mock_controller_service_context.rs | 2 +- minifi_rust/minifi_native_macros/src/lib.rs | 35 ++++ minifi_rust/minifi_rs_behave/src/main.rs | 1 + 34 files changed, 410 insertions(+), 389 deletions(-) diff --git a/.github/references/ubuntu_22_04_clang_arm_manifest.json b/.github/references/ubuntu_22_04_clang_arm_manifest.json index 6743591212..8ae3301fdd 100644 --- a/.github/references/ubuntu_22_04_clang_arm_manifest.json +++ b/.github/references/ubuntu_22_04_clang_arm_manifest.json @@ -4069,7 +4069,7 @@ "name": "Keep Source File", "description": "If true, the file is not deleted after it has been copied to the Content Repository", "validator": "BOOLEAN_VALIDATOR", - "required": "false", + "required": "true", "sensitive": "false", "expressionLanguageScope": "NONE", "defaultValue": "false" @@ -4118,7 +4118,7 @@ "name": "Recurse Subdirectories", "description": "Indicates whether or not to pull files from subdirectories", "validator": "BOOLEAN_VALIDATOR", - "required": "false", + "required": "true", "sensitive": "false", "expressionLanguageScope": "NONE", "defaultValue": "true" @@ -6345,7 +6345,7 @@ "FlowFiles To Log": { "name": "FlowFiles To Log", "description": "Number of flow files to log. If set to zero all flow files will be logged. Please note that this may block other threads from running if not used judiciously.", - "validator": "VALID", + "validator": "NON_NEGATIVE_INTEGER_VALIDATOR", "required": "true", "sensitive": "false", "expressionLanguageScope": "NONE", diff --git a/minifi_rust/extensions/minifi_rs_playground/Cargo.toml b/minifi_rust/extensions/minifi_rs_playground/Cargo.toml index 8d8682660c..ec90190e37 100644 --- a/minifi_rust/extensions/minifi_rs_playground/Cargo.toml +++ b/minifi_rust/extensions/minifi_rs_playground/Cargo.toml @@ -16,6 +16,7 @@ hex = "0.4.3" strum_macros = "0.28.0" lipsum = "0.9.1" + [dev-dependencies] tempfile = "3.22.0" filetime = "0.2.26" diff --git a/minifi_rust/extensions/minifi_rs_playground/src/controller_services/dog_controller_service.rs b/minifi_rust/extensions/minifi_rs_playground/src/controller_services/dog_controller_service.rs index df25212130..40d2357b3b 100644 --- a/minifi_rust/extensions/minifi_rs_playground/src/controller_services/dog_controller_service.rs +++ b/minifi_rust/extensions/minifi_rs_playground/src/controller_services/dog_controller_service.rs @@ -18,11 +18,11 @@ use crate::controller_services::animal_controller_apis::{ CanFlyControllerApi, NumberOfLegsControllerApi, }; -use minifi_native::ControllerServiceApi; use minifi_native::macros::ComponentIdentifier; +use minifi_native::{ControllerServiceApi, property_constraint}; use minifi_native::{ ControllerServiceDefinition, EnableControllerService, GetProperty, Logger, MinifiError, - Property, ProvidedInterface, StandardPropertyValidator, create_provided_interface, + Property, ProvidedInterface, create_provided_interface, }; pub(crate) const HAS_JETPACK: Property = Property { @@ -32,9 +32,7 @@ pub(crate) const HAS_JETPACK: Property = Property { is_sensitive: false, supports_expr_lang: false, default_value: Some("false"), - validator: StandardPropertyValidator::BoolValidator, - allowed_values: &[], - allowed_type: None, + constraints: property_constraint::(), }; pub(crate) const EXTRA_INFO: Property = Property { @@ -44,9 +42,7 @@ pub(crate) const EXTRA_INFO: Property = Property { is_sensitive: false, supports_expr_lang: false, default_value: None, - validator: StandardPropertyValidator::AlwaysValidValidator, - allowed_values: &[], - allowed_type: None, + constraints: None, }; #[allow(dead_code)] // extra_info is only used by {:?} @@ -73,11 +69,10 @@ impl EnableControllerService for DogControllerRs { where Self: Sized, { - let has_jetpack = context.get_bool_property(&HAS_JETPACK)?.ok_or( - MinifiError::missing_required_property("Has jetpack is required"), - )?; - - let extra_info = context.get_property(&EXTRA_INFO)?.unwrap_or("".into()); + let has_jetpack = context.get_req_property::(&HAS_JETPACK)?; + let extra_info = context + .get_property::(&EXTRA_INFO)? + .unwrap_or("".into()); Ok(Self { has_jetpack, diff --git a/minifi_rust/extensions/minifi_rs_playground/src/controller_services/lorem_ipsum_controller_service.rs b/minifi_rust/extensions/minifi_rs_playground/src/controller_services/lorem_ipsum_controller_service.rs index 25341971ea..1f1ff57208 100644 --- a/minifi_rust/extensions/minifi_rs_playground/src/controller_services/lorem_ipsum_controller_service.rs +++ b/minifi_rust/extensions/minifi_rs_playground/src/controller_services/lorem_ipsum_controller_service.rs @@ -19,7 +19,7 @@ use lipsum::lipsum; use minifi_native::macros::ComponentIdentifier; use minifi_native::{ ControllerServiceDefinition, EnableControllerService, GetProperty, Logger, MinifiError, - Property, ProvidedInterface, StandardPropertyValidator, + Property, ProvidedInterface, property_constraint, }; const LENGTH: Property = Property { @@ -29,9 +29,7 @@ const LENGTH: Property = Property { is_sensitive: false, supports_expr_lang: false, default_value: Some("25"), - validator: StandardPropertyValidator::U64Validator, - allowed_values: &[], - allowed_type: None, + constraints: property_constraint::(), }; #[derive(Debug, ComponentIdentifier)] @@ -44,11 +42,7 @@ impl EnableControllerService for LoremIpsumControllerService { where Self: Sized, { - let length = context - .get_u64_property(&LENGTH)? - .ok_or(MinifiError::missing_required_property("Length is required"))?; - - let data = lipsum(length as usize); + let data = lipsum(context.get_req_property::(&LENGTH)?); Ok(Self { data }) } } diff --git a/minifi_rust/extensions/minifi_rs_playground/src/processors/generate_flow_file.rs b/minifi_rust/extensions/minifi_rs_playground/src/processors/generate_flow_file.rs index 55bb376da8..4464ef4e77 100644 --- a/minifi_rust/extensions/minifi_rs_playground/src/processors/generate_flow_file.rs +++ b/minifi_rust/extensions/minifi_rs_playground/src/processors/generate_flow_file.rs @@ -19,8 +19,8 @@ use minifi_native::macros::ComponentIdentifier; use minifi_native::{ - GetProperty, Logger, MinifiError, OnTriggerResult, ProcessContext, ProcessSession, Schedule, - Trigger, + DataSize, GetProperty, Logger, MinifiError, OnTriggerResult, ProcessContext, ProcessSession, + Schedule, Trigger, }; use rand::RngExt; use rand::distr::Alphanumeric; @@ -52,22 +52,14 @@ impl Schedule for GenerateFlowFileRs { where Self: Sized, { - let is_unique = context - .get_bool_property(&properties::UNIQUE_FLOW_FILES)? - .expect("Required property"); - let is_text = context - .get_property(&properties::DATA_FORMAT)? - .expect("Required property") - .as_str() - == "Text"; - let has_custom_text = context.get_property(&properties::CUSTOM_TEXT)?.is_some(); - - let file_size = context - .get_size_property(&properties::FILE_SIZE)? - .expect("Required property"); - let batch_size = context - .get_u64_property(&properties::BATCH_SIZE)? - .expect("Required property"); + let is_unique = context.get_req_property::(&properties::UNIQUE_FLOW_FILES)?; + let is_text = context.get_req_property::(&properties::DATA_FORMAT)? == "Text"; + let has_custom_text = context + .get_property::(&properties::CUSTOM_TEXT)? + .is_some(); + + let file_size = context.get_req_property::(&properties::FILE_SIZE)?; + let batch_size = context.get_req_property::(&properties::BATCH_SIZE)?; let mode = Self::get_mode(is_unique, is_text, has_custom_text, file_size); let data_generated_during_on_schedule = diff --git a/minifi_rust/extensions/minifi_rs_playground/src/processors/generate_flow_file/properties.rs b/minifi_rust/extensions/minifi_rs_playground/src/processors/generate_flow_file/properties.rs index 16d0b8a718..34e68d9049 100644 --- a/minifi_rust/extensions/minifi_rs_playground/src/processors/generate_flow_file/properties.rs +++ b/minifi_rust/extensions/minifi_rs_playground/src/processors/generate_flow_file/properties.rs @@ -15,7 +15,8 @@ // specific language governing permissions and limitations // under the License. -use minifi_native::{Property, StandardPropertyValidator}; +use minifi_native::PropertyConstraints::AllowedValues; +use minifi_native::{DataSize, Property, property_constraint}; pub(crate) const FILE_SIZE: Property = Property { name: "File Size", @@ -24,9 +25,7 @@ pub(crate) const FILE_SIZE: Property = Property { is_sensitive: false, supports_expr_lang: true, default_value: Some("1 kB"), - validator: StandardPropertyValidator::DataSizeValidator, - allowed_values: &[], - allowed_type: None, + constraints: property_constraint::(), }; pub(crate) const BATCH_SIZE: Property = Property { @@ -36,9 +35,7 @@ pub(crate) const BATCH_SIZE: Property = Property { is_sensitive: false, supports_expr_lang: false, default_value: Some("1"), - validator: StandardPropertyValidator::U64Validator, - allowed_values: &[], - allowed_type: None, + constraints: property_constraint::(), }; pub(crate) const DATA_FORMAT: Property = Property { @@ -48,9 +45,7 @@ pub(crate) const DATA_FORMAT: Property = Property { is_sensitive: false, supports_expr_lang: false, default_value: Some("Binary"), - validator: StandardPropertyValidator::AlwaysValidValidator, - allowed_values: &["Text", "Binary"], - allowed_type: None, + constraints: Some(AllowedValues(&["Text", "Binary"])), }; pub(crate) const UNIQUE_FLOW_FILES: Property = Property { @@ -60,9 +55,7 @@ pub(crate) const UNIQUE_FLOW_FILES: Property = Property { is_sensitive: false, supports_expr_lang: false, default_value: Some("true"), - validator: StandardPropertyValidator::BoolValidator, - allowed_values: &[], - allowed_type: None, + constraints: property_constraint::(), }; pub(crate) const CUSTOM_TEXT: Property = Property { @@ -72,7 +65,5 @@ pub(crate) const CUSTOM_TEXT: Property = Property { is_sensitive: false, supports_expr_lang: true, default_value: None, - validator: StandardPropertyValidator::AlwaysValidValidator, - allowed_values: &[], - allowed_type: None, + constraints: None, }; diff --git a/minifi_rust/extensions/minifi_rs_playground/src/processors/get_file.rs b/minifi_rust/extensions/minifi_rs_playground/src/processors/get_file.rs index 1847ea632e..dc365e9c40 100644 --- a/minifi_rust/extensions/minifi_rs_playground/src/processors/get_file.rs +++ b/minifi_rust/extensions/minifi_rs_playground/src/processors/get_file.rs @@ -26,8 +26,8 @@ use crate::processors::get_file::properties::{ }; use minifi_native::macros::ComponentIdentifier; use minifi_native::{ - GetProperty, IoState, Logger, MinifiError, OnTriggerResult, ProcessContext, ProcessSession, - Schedule, Trigger, debug, info, trace, warn, + DataSize, GetProperty, IoState, Logger, MinifiError, OnTriggerResult, ProcessContext, + ProcessSession, Schedule, Trigger, debug, info, trace, warn, }; use std::collections::VecDeque; use std::error; @@ -223,10 +223,7 @@ impl Schedule for GetFileRs { where Self: Sized, { - let input_directory: PathBuf = context - .get_property(&DIRECTORY)? - .expect("Required property") - .into(); + let input_directory = context.get_req_property::(&DIRECTORY)?; if !input_directory.is_dir() { return Err(MinifiError::schedule_err(format!( "{:?} is not a valid directory", @@ -234,25 +231,17 @@ impl Schedule for GetFileRs { ))); } - let recursive = context - .get_bool_property(&RECURSE)? - .expect("Required property"); - - let keep_source_file = context - .get_bool_property(&KEEP_SOURCE_FILE)? - .expect("Required property"); - - let poll_interval = context.get_duration_property(&properties::POLLING_INTERVAL)?; - let min_size = context.get_size_property(&MIN_SIZE)?; - let max_size = context.get_size_property(&MAX_SIZE)?; - let min_age = context.get_duration_property(&MIN_AGE)?; - let max_age = context.get_duration_property(&MAX_AGE)?; - let batch_size = context - .get_u64_property(&BATCH_SIZE)? - .expect("required property"); - let ignore_hidden_files = context - .get_bool_property(&IGNORE_HIDDEN_FILES)? - .expect("required property"); + let recursive = context.get_req_property::(&RECURSE)?; + + let keep_source_file = context.get_req_property::(&KEEP_SOURCE_FILE)?; + + let poll_interval = context.get_property::(&properties::POLLING_INTERVAL)?; + let min_size = context.get_property::(&MIN_SIZE)?; + let max_size = context.get_property::(&MAX_SIZE)?; + let min_age = context.get_property::(&MIN_AGE)?; + let max_age = context.get_property::(&MAX_AGE)?; + let batch_size = context.get_req_property::(&BATCH_SIZE)?; + let ignore_hidden_files = context.get_req_property::(&IGNORE_HIDDEN_FILES)?; Ok(GetFileRs { recursive, diff --git a/minifi_rust/extensions/minifi_rs_playground/src/processors/get_file/properties.rs b/minifi_rust/extensions/minifi_rs_playground/src/processors/get_file/properties.rs index b43009b28e..b413b842d6 100644 --- a/minifi_rust/extensions/minifi_rs_playground/src/processors/get_file/properties.rs +++ b/minifi_rust/extensions/minifi_rs_playground/src/processors/get_file/properties.rs @@ -15,7 +15,8 @@ // specific language governing permissions and limitations // under the License. -use minifi_native::{Property, StandardPropertyValidator}; +use minifi_native::{DataSize, Property, PropertyConstraints, property_constraint}; +use std::time::Duration; pub(crate) const DIRECTORY: Property = Property { name: "Input Directory", @@ -24,33 +25,27 @@ pub(crate) const DIRECTORY: Property = Property { is_sensitive: false, supports_expr_lang: true, default_value: None, - validator: StandardPropertyValidator::NonBlankValidator, - allowed_values: &[], - allowed_type: None, + constraints: PropertyConstraints::non_blank(), }; pub(crate) const RECURSE: Property = Property { name: "Recurse Subdirectories", description: "Indicates whether or not to pull files from subdirectories", - is_required: false, + is_required: true, is_sensitive: false, supports_expr_lang: false, default_value: Some("true"), - validator: StandardPropertyValidator::BoolValidator, - allowed_values: &[], - allowed_type: None, + constraints: property_constraint::(), }; pub(crate) const KEEP_SOURCE_FILE: Property = Property { name: "Keep Source File", description: "If true, the file is not deleted after it has been copied to the Content Repository", - is_required: false, + is_required: true, is_sensitive: false, supports_expr_lang: false, default_value: Some("false"), - validator: StandardPropertyValidator::BoolValidator, - allowed_values: &[], - allowed_type: None, + constraints: property_constraint::(), }; pub(crate) const MIN_AGE: Property = Property { @@ -60,9 +55,7 @@ pub(crate) const MIN_AGE: Property = Property { is_sensitive: false, supports_expr_lang: false, default_value: None, - validator: StandardPropertyValidator::TimePeriodValidator, - allowed_values: &[], - allowed_type: None, + constraints: property_constraint::(), }; pub(crate) const MAX_AGE: Property = Property { @@ -72,9 +65,7 @@ pub(crate) const MAX_AGE: Property = Property { is_sensitive: false, supports_expr_lang: false, default_value: None, - validator: StandardPropertyValidator::TimePeriodValidator, - allowed_values: &[], - allowed_type: None, + constraints: property_constraint::(), }; pub(crate) const MIN_SIZE: Property = Property { @@ -84,9 +75,7 @@ pub(crate) const MIN_SIZE: Property = Property { is_sensitive: false, supports_expr_lang: false, default_value: None, - validator: StandardPropertyValidator::DataSizeValidator, - allowed_values: &[], - allowed_type: None, + constraints: property_constraint::(), }; pub(crate) const MAX_SIZE: Property = Property { @@ -96,9 +85,7 @@ pub(crate) const MAX_SIZE: Property = Property { is_sensitive: false, supports_expr_lang: false, default_value: None, - validator: StandardPropertyValidator::DataSizeValidator, - allowed_values: &[], - allowed_type: None, + constraints: property_constraint::(), }; pub(crate) const IGNORE_HIDDEN_FILES: Property = Property { @@ -108,9 +95,7 @@ pub(crate) const IGNORE_HIDDEN_FILES: Property = Property { is_sensitive: false, supports_expr_lang: false, default_value: Some("true"), - validator: StandardPropertyValidator::BoolValidator, - allowed_values: &[], - allowed_type: None, + constraints: property_constraint::(), }; pub(crate) const POLLING_INTERVAL: Property = Property { @@ -120,9 +105,7 @@ pub(crate) const POLLING_INTERVAL: Property = Property { is_sensitive: false, supports_expr_lang: false, default_value: None, - validator: StandardPropertyValidator::TimePeriodValidator, - allowed_values: &[], - allowed_type: None, + constraints: property_constraint::(), }; pub(crate) const BATCH_SIZE: Property = Property { @@ -132,7 +115,5 @@ pub(crate) const BATCH_SIZE: Property = Property { is_sensitive: false, supports_expr_lang: false, default_value: Some("10"), - validator: StandardPropertyValidator::U64Validator, - allowed_values: &[], - allowed_type: None, + constraints: property_constraint::(), }; diff --git a/minifi_rust/extensions/minifi_rs_playground/src/processors/kamikaze_processor.rs b/minifi_rust/extensions/minifi_rs_playground/src/processors/kamikaze_processor.rs index 6686c0b400..f215d8c781 100644 --- a/minifi_rust/extensions/minifi_rs_playground/src/processors/kamikaze_processor.rs +++ b/minifi_rust/extensions/minifi_rs_playground/src/processors/kamikaze_processor.rs @@ -21,15 +21,19 @@ mod properties; mod relationships; use crate::controller_services::lorem_ipsum_controller_service::LoremIpsumControllerService; -use crate::processors::kamikaze_processor::properties::NOT_REGISTERED_PROPERTY; -use minifi_native::macros::ComponentIdentifier; +use crate::processors::kamikaze_processor::properties::{ + NOT_REGISTERED_PROPERTY, SCHEDULE_BEHAVIOUR, TRIGGER_BEHAVIOUR, +}; +use minifi_native::macros::{ComponentIdentifier, PropertyType}; use minifi_native::{ GetProperty, Logger, MinifiError, OnTriggerResult, ProcessContext, ProcessSession, Schedule, Trigger, }; use strum_macros::{Display, EnumString, IntoStaticStr, VariantNames}; -#[derive(Debug, Clone, Copy, PartialEq, Display, EnumString, VariantNames, IntoStaticStr)] +#[derive( + Debug, Clone, Copy, PartialEq, Display, EnumString, VariantNames, IntoStaticStr, PropertyType, +)] #[strum(serialize_all = "PascalCase", const_into_str)] enum KamikazeBehaviour { ReturnErr, @@ -49,15 +53,11 @@ impl Schedule for KamikazeProcessorRs { where Self: Sized, { - let trigger_behaviour = context - .get_property(&properties::TRIGGER_BEHAVIOUR)? - .expect("required property") - .parse::()?; + let trigger_behaviour = + context.get_req_property::(&TRIGGER_BEHAVIOUR)?; - let schedule_behaviour = context - .get_property(&properties::SCHEDULE_BEHAVIOUR)? - .expect("required property") - .parse::()?; + let schedule_behaviour = + context.get_req_property::(&SCHEDULE_BEHAVIOUR)?; match schedule_behaviour { KamikazeBehaviour::ReturnErr => Err(MinifiError::schedule_err( @@ -65,7 +65,7 @@ impl Schedule for KamikazeProcessorRs { )), KamikazeBehaviour::ReturnOk => Ok(KamikazeProcessorRs { trigger_behaviour }), KamikazeBehaviour::GetNotRegisteredProperty => { - let _ = context.get_property(&NOT_REGISTERED_PROPERTY)?; + let _ = context.get_property::(&NOT_REGISTERED_PROPERTY)?; Ok(KamikazeProcessorRs { trigger_behaviour }) } KamikazeBehaviour::Panic => { diff --git a/minifi_rust/extensions/minifi_rs_playground/src/processors/kamikaze_processor/properties.rs b/minifi_rust/extensions/minifi_rs_playground/src/processors/kamikaze_processor/properties.rs index 6ee86b1bda..5108907636 100644 --- a/minifi_rust/extensions/minifi_rs_playground/src/processors/kamikaze_processor/properties.rs +++ b/minifi_rust/extensions/minifi_rs_playground/src/processors/kamikaze_processor/properties.rs @@ -16,8 +16,7 @@ // under the License. use crate::processors::kamikaze_processor::KamikazeBehaviour; -use minifi_native::{Property, StandardPropertyValidator}; -use strum::VariantNames; +use minifi_native::{Property, property_constraint}; pub(crate) const SCHEDULE_BEHAVIOUR: Property = Property { name: "Schedule Behaviour", @@ -26,9 +25,7 @@ pub(crate) const SCHEDULE_BEHAVIOUR: Property = Property { is_sensitive: false, supports_expr_lang: false, default_value: Some(KamikazeBehaviour::ReturnOk.into_str()), - validator: StandardPropertyValidator::AlwaysValidValidator, - allowed_values: KamikazeBehaviour::VARIANTS, - allowed_type: None, + constraints: property_constraint::(), }; pub(crate) const TRIGGER_BEHAVIOUR: Property = Property { @@ -38,9 +35,7 @@ pub(crate) const TRIGGER_BEHAVIOUR: Property = Property { is_sensitive: false, supports_expr_lang: false, default_value: Some(KamikazeBehaviour::ReturnOk.into_str()), - validator: StandardPropertyValidator::AlwaysValidValidator, - allowed_values: KamikazeBehaviour::VARIANTS, - allowed_type: None, + constraints: property_constraint::(), }; pub(crate) const NOT_REGISTERED_PROPERTY: Property = Property { @@ -50,7 +45,5 @@ pub(crate) const NOT_REGISTERED_PROPERTY: Property = Property { is_sensitive: false, supports_expr_lang: false, default_value: None, - validator: StandardPropertyValidator::AlwaysValidValidator, - allowed_values: &[], - allowed_type: None, + constraints: None, }; diff --git a/minifi_rust/extensions/minifi_rs_playground/src/processors/log_attribute.rs b/minifi_rust/extensions/minifi_rs_playground/src/processors/log_attribute.rs index 3e59e2d522..c59eb50969 100644 --- a/minifi_rust/extensions/minifi_rs_playground/src/processors/log_attribute.rs +++ b/minifi_rust/extensions/minifi_rs_playground/src/processors/log_attribute.rs @@ -121,25 +121,15 @@ impl Trigger for LogAttributeRs { impl Schedule for LogAttributeRs { fn schedule(context: &P, _logger: &L) -> Result { - let log_level = context - .get_property(&LOG_LEVEL)? - .expect("required property") - .parse::()?; - - let log_payload = context - .get_bool_property(&LOG_PAYLOAD)? - .expect("required property"); - - let flow_files_to_log = context - .get_property(&FLOW_FILES_TO_LOG)? - .expect("required property") - .parse::()?; + let log_level = context.get_req_property::(&LOG_LEVEL)?; + let log_payload = context.get_req_property::(&LOG_PAYLOAD)?; + let flow_files_to_log = context.get_req_property::(&FLOW_FILES_TO_LOG)?; fn get_csv_property( context: &P, property: &Property, ) -> Result>, MinifiError> { - Ok(context.get_property(property)?.map(|s| { + Ok(context.get_property::(property)?.map(|s| { s.split(',') .map(|s| s.trim().to_string()) .collect::>() @@ -152,13 +142,12 @@ impl Schedule for LogAttributeRs { let dash_line = format!( "{:-^50}", context - .get_property(&properties::LOG_PREFIX)? - .unwrap_or(String::new()) + .get_property::(&properties::LOG_PREFIX)? + .unwrap_or_default() ); - let hex_encode_payload = context - .get_bool_property(&properties::HEX_ENCODE_PAYLOAD)? - .expect("required property"); + let hex_encode_payload = + context.get_req_property::(&properties::HEX_ENCODE_PAYLOAD)?; Ok(LogAttributeRs { log_level, diff --git a/minifi_rust/extensions/minifi_rs_playground/src/processors/log_attribute/properties.rs b/minifi_rust/extensions/minifi_rs_playground/src/processors/log_attribute/properties.rs index 48002c92fc..b4c5d47ade 100644 --- a/minifi_rust/extensions/minifi_rs_playground/src/processors/log_attribute/properties.rs +++ b/minifi_rust/extensions/minifi_rs_playground/src/processors/log_attribute/properties.rs @@ -15,8 +15,7 @@ // specific language governing permissions and limitations // under the License. -use minifi_native::{LogLevel, Property, StandardPropertyValidator}; -use strum::VariantNames; +use minifi_native::{LogLevel, Property, property_constraint}; pub(crate) const LOG_LEVEL: Property = Property { name: "Log Level", @@ -24,10 +23,8 @@ pub(crate) const LOG_LEVEL: Property = Property { is_required: true, is_sensitive: false, supports_expr_lang: false, - default_value: Some("Info"), // todo! it would be nicer to come from enum value but wasnt able to use into_const_str from another crate - validator: StandardPropertyValidator::AlwaysValidValidator, - allowed_values: LogLevel::VARIANTS, - allowed_type: None, + default_value: Some(LogLevel::Info.into_str()), + constraints: property_constraint::(), }; pub(crate) const ATTRIBUTES_TO_LOG: Property = Property { @@ -37,9 +34,7 @@ pub(crate) const ATTRIBUTES_TO_LOG: Property = Property { is_sensitive: false, supports_expr_lang: false, default_value: None, - validator: StandardPropertyValidator::AlwaysValidValidator, - allowed_values: &[], - allowed_type: None, + constraints: None, }; pub(crate) const ATTRIBUTES_TO_IGNORE: Property = Property { @@ -49,9 +44,7 @@ pub(crate) const ATTRIBUTES_TO_IGNORE: Property = Property { is_sensitive: false, supports_expr_lang: false, default_value: None, - validator: StandardPropertyValidator::AlwaysValidValidator, - allowed_values: &[], - allowed_type: None, + constraints: None, }; pub(crate) const LOG_PAYLOAD: Property = Property { @@ -61,9 +54,7 @@ pub(crate) const LOG_PAYLOAD: Property = Property { is_sensitive: false, supports_expr_lang: false, default_value: Some("false"), - validator: StandardPropertyValidator::BoolValidator, - allowed_values: &[], - allowed_type: None, + constraints: property_constraint::(), }; pub(crate) const LOG_PREFIX: Property = Property { @@ -73,9 +64,7 @@ pub(crate) const LOG_PREFIX: Property = Property { is_sensitive: false, supports_expr_lang: false, default_value: None, - validator: StandardPropertyValidator::AlwaysValidValidator, - allowed_values: &[], - allowed_type: None, + constraints: None, }; pub(crate) const FLOW_FILES_TO_LOG: Property = Property { @@ -85,9 +74,7 @@ pub(crate) const FLOW_FILES_TO_LOG: Property = Property { is_sensitive: false, supports_expr_lang: false, default_value: Some("1"), - validator: StandardPropertyValidator::AlwaysValidValidator, - allowed_values: &[], - allowed_type: None, + constraints: property_constraint::(), }; pub(crate) const HEX_ENCODE_PAYLOAD: Property = Property { @@ -97,7 +84,5 @@ pub(crate) const HEX_ENCODE_PAYLOAD: Property = Property { is_sensitive: false, supports_expr_lang: false, default_value: Some("false"), - validator: StandardPropertyValidator::BoolValidator, - allowed_values: &[], - allowed_type: None, + constraints: property_constraint::(), }; diff --git a/minifi_rust/extensions/minifi_rs_playground/src/processors/lorem_ipsum_cs_user.rs b/minifi_rust/extensions/minifi_rs_playground/src/processors/lorem_ipsum_cs_user.rs index 2c4cdb5170..b44fcc8112 100644 --- a/minifi_rust/extensions/minifi_rs_playground/src/processors/lorem_ipsum_cs_user.rs +++ b/minifi_rust/extensions/minifi_rs_playground/src/processors/lorem_ipsum_cs_user.rs @@ -21,7 +21,7 @@ mod properties; use crate::controller_services::lorem_ipsum_controller_service::LoremIpsumControllerService; use crate::processors::lorem_ipsum_cs_user::properties::CONTROLLER_SERVICE; use crate::processors::lorem_ipsum_cs_user::relationships::SUCCESS; -use minifi_native::macros::ComponentIdentifier; +use minifi_native::macros::{ComponentIdentifier, PropertyType}; use minifi_native::{ Content, FlowFileSource, GeneratedFlowFile, GetControllerService, GetProperty, Logger, MinifiError, Schedule, trace, @@ -29,7 +29,9 @@ use minifi_native::{ use std::collections::HashMap; use strum_macros::{Display, EnumString, IntoStaticStr, VariantNames}; -#[derive(Debug, Clone, Copy, PartialEq, Display, EnumString, VariantNames, IntoStaticStr)] +#[derive( + Debug, Clone, Copy, PartialEq, Display, EnumString, VariantNames, IntoStaticStr, PropertyType, +)] #[strum(serialize_all = "PascalCase", const_into_str)] enum WriteMethod { Buffer, @@ -46,10 +48,7 @@ impl Schedule for LoremIpsumCSUser { where Self: Sized, { - let write_method = context - .get_property(&properties::WRITE_METHOD)? - .expect("required property") - .parse::()?; + let write_method = context.get_req_property::(&properties::WRITE_METHOD)?; Ok(Self { write_method }) } } diff --git a/minifi_rust/extensions/minifi_rs_playground/src/processors/lorem_ipsum_cs_user/properties.rs b/minifi_rust/extensions/minifi_rs_playground/src/processors/lorem_ipsum_cs_user/properties.rs index b78ab6d23b..b424e5ad1c 100644 --- a/minifi_rust/extensions/minifi_rs_playground/src/processors/lorem_ipsum_cs_user/properties.rs +++ b/minifi_rust/extensions/minifi_rs_playground/src/processors/lorem_ipsum_cs_user/properties.rs @@ -16,9 +16,9 @@ // under the License. use crate::controller_services::lorem_ipsum_controller_service::LoremIpsumControllerService; -use minifi_native::ComponentIdentifier; -use minifi_native::{Property, StandardPropertyValidator}; -use strum::VariantNames; +use crate::processors::lorem_ipsum_cs_user::WriteMethod; +use minifi_native::Property; +use minifi_native::property_constraint; pub(crate) const CONTROLLER_SERVICE: Property = Property { name: "Lorem Ipsum Controller Service", @@ -27,9 +27,7 @@ pub(crate) const CONTROLLER_SERVICE: Property = Property { is_sensitive: false, supports_expr_lang: false, default_value: None, - validator: StandardPropertyValidator::AlwaysValidValidator, - allowed_values: &[], - allowed_type: Some(LoremIpsumControllerService::CLASS_NAME), + constraints: property_constraint::(), }; pub(crate) const WRITE_METHOD: Property = Property { @@ -38,8 +36,6 @@ pub(crate) const WRITE_METHOD: Property = Property { is_required: true, is_sensitive: false, supports_expr_lang: false, - default_value: Some(super::WriteMethod::Buffer.into_str()), - validator: StandardPropertyValidator::AlwaysValidValidator, - allowed_values: super::WriteMethod::VARIANTS, - allowed_type: None, + default_value: Some(WriteMethod::Buffer.into_str()), + constraints: property_constraint::(), }; diff --git a/minifi_rust/extensions/minifi_rs_playground/src/processors/put_file.rs b/minifi_rust/extensions/minifi_rs_playground/src/processors/put_file.rs index 4d554c132e..5fa35a63e2 100644 --- a/minifi_rust/extensions/minifi_rs_playground/src/processors/put_file.rs +++ b/minifi_rust/extensions/minifi_rs_playground/src/processors/put_file.rs @@ -18,10 +18,10 @@ // This is the (not production ready) reimplementation of the already existing standard PutFile processor use crate::processors::put_file::relationships::{FAILURE, SUCCESS}; -use minifi_native::macros::ComponentIdentifier; +use minifi_native::macros::{ComponentIdentifier, PropertyType}; use minifi_native::{ FlowFileTransform, GetAttribute, GetControllerService, GetId, GetProperty, InputStream, Logger, - MinifiError, Schedule, TransformedFlowFile, trace, warn, + MinifiError, Schedule, TransformedFlowFile, trace, unwrap_or_route, warn, }; use std::path::{Path, PathBuf}; use strum_macros::{Display, EnumString, IntoStaticStr, VariantNames}; @@ -32,7 +32,9 @@ mod relationships; #[cfg(unix)] mod unix_only_properties; -#[derive(Debug, Clone, Copy, PartialEq, Display, EnumString, VariantNames, IntoStaticStr)] +#[derive( + Debug, Clone, Copy, PartialEq, Display, EnumString, VariantNames, IntoStaticStr, PropertyType, +)] #[strum(serialize_all = "camelCase", const_into_str)] enum ConflictResolutionStrategy { Fail, @@ -109,14 +111,12 @@ impl PutFileRs { where Ctx: GetProperty + GetAttribute + GetId, { - let directory = context - .get_property(&properties::DIRECTORY)? - .expect("required property"); + let directory = context.get_req_property::(&properties::DIRECTORY)?; let file_name = context .get_attribute("filename")? .unwrap_or(context.get_id()?); - Ok(PathBuf::from(directory).join(file_name)) + Ok(directory.join(file_name)) } fn prepare_destination(&self, destination: &Path) -> std::io::Result<()> { @@ -163,7 +163,7 @@ impl PutFileRs { let parse_permission = |property: &minifi_native::Property| -> Result, MinifiError> { Ok(context - .get_property(property)? + .get_property::(property)? .map(|perm_str| u32::from_str_radix(&perm_str, 8)) .transpose()? .map(std::fs::Permissions::from_mode)) @@ -188,15 +188,11 @@ impl PutFileRs { impl Schedule for PutFileRs { fn schedule(context: &P, _logger: &L) -> Result { let conflict_resolution_strategy = context - .get_property(&properties::CONFLICT_RESOLUTION)? - .expect("required property") - .parse::()?; + .get_req_property::(&properties::CONFLICT_RESOLUTION)?; - let try_make_dirs = context - .get_bool_property(&properties::CREATE_DIRS)? - .expect("required property"); + let try_make_dirs = context.get_req_property::(&properties::CREATE_DIRS)?; - let maximum_file_count = context.get_u64_property(&properties::MAX_FILE_COUNT)?; + let maximum_file_count = context.get_property::(&properties::MAX_FILE_COUNT)?; let unix_permissions = Self::parse_unix_permissions(context)?; @@ -222,10 +218,8 @@ impl FlowFileTransform for PutFileRs { ) -> Result, MinifiError> { trace!(logger, "on_trigger: {:?}", self); - let Ok(destination_path) = Self::get_destination_path(context) else { - warn!(logger, "Invalid destination path"); - return Ok(TransformedFlowFile::route_without_changes(&FAILURE)); - }; + let destination_path = + unwrap_or_route!(Self::get_destination_path(context), &FAILURE, logger); if self.directory_is_full(&destination_path) { warn!(logger, "Directory is full"); diff --git a/minifi_rust/extensions/minifi_rs_playground/src/processors/put_file/properties.rs b/minifi_rust/extensions/minifi_rs_playground/src/processors/put_file/properties.rs index 4facd860f2..c931281729 100644 --- a/minifi_rust/extensions/minifi_rs_playground/src/processors/put_file/properties.rs +++ b/minifi_rust/extensions/minifi_rs_playground/src/processors/put_file/properties.rs @@ -15,10 +15,8 @@ // specific language governing permissions and limitations // under the License. -use minifi_native::{Property, StandardPropertyValidator}; -use strum::VariantNames; - use super::ConflictResolutionStrategy; +use minifi_native::{Property, PropertyConstraints, property_constraint}; pub(crate) const DIRECTORY: Property = Property { name: "Directory", description: "The output directory to which to put files", @@ -26,9 +24,7 @@ pub(crate) const DIRECTORY: Property = Property { is_sensitive: false, supports_expr_lang: true, default_value: Some("."), - validator: StandardPropertyValidator::NonBlankValidator, - allowed_values: &[], - allowed_type: None, + constraints: PropertyConstraints::non_blank(), }; pub(crate) const CONFLICT_RESOLUTION: Property = Property { @@ -38,9 +34,7 @@ pub(crate) const CONFLICT_RESOLUTION: Property = Property { is_sensitive: false, supports_expr_lang: false, default_value: Some(ConflictResolutionStrategy::Fail.into_str()), - validator: StandardPropertyValidator::AlwaysValidValidator, - allowed_values: ConflictResolutionStrategy::VARIANTS, - allowed_type: None, + constraints: property_constraint::(), }; pub(crate) const CREATE_DIRS: Property = Property { @@ -50,9 +44,7 @@ pub(crate) const CREATE_DIRS: Property = Property { is_sensitive: false, supports_expr_lang: false, default_value: Some("true"), - validator: StandardPropertyValidator::BoolValidator, - allowed_values: &[], - allowed_type: None, + constraints: property_constraint::(), }; pub(crate) const MAX_FILE_COUNT: Property = Property { @@ -62,7 +54,5 @@ pub(crate) const MAX_FILE_COUNT: Property = Property { is_sensitive: false, supports_expr_lang: false, default_value: None, // Diverged from the original implementation, u64 with no default describes the behavior better - validator: StandardPropertyValidator::U64Validator, - allowed_values: &[], - allowed_type: None, + constraints: property_constraint::(), }; diff --git a/minifi_rust/extensions/minifi_rs_playground/src/processors/put_file/unix_only_properties.rs b/minifi_rust/extensions/minifi_rs_playground/src/processors/put_file/unix_only_properties.rs index f7cbd7811b..d7a75da418 100644 --- a/minifi_rust/extensions/minifi_rs_playground/src/processors/put_file/unix_only_properties.rs +++ b/minifi_rust/extensions/minifi_rs_playground/src/processors/put_file/unix_only_properties.rs @@ -15,7 +15,7 @@ // specific language governing permissions and limitations // under the License. -use minifi_native::{Property, StandardPropertyValidator}; +use minifi_native::Property; pub(crate) const PERMISSIONS: Property = Property { name: "Permissions", @@ -24,9 +24,7 @@ pub(crate) const PERMISSIONS: Property = Property { is_sensitive: false, supports_expr_lang: false, default_value: None, - validator: StandardPropertyValidator::AlwaysValidValidator, - allowed_values: &[], - allowed_type: None, + constraints: None, }; pub(crate) const DIRECTORY_PERMISSIONS: Property = Property { @@ -36,7 +34,5 @@ pub(crate) const DIRECTORY_PERMISSIONS: Property = Property { is_sensitive: false, supports_expr_lang: false, default_value: None, - validator: StandardPropertyValidator::AlwaysValidValidator, - allowed_values: &[], - allowed_type: None, + constraints: None, }; diff --git a/minifi_rust/extensions/minifi_rs_playground/src/processors/zoo_processor.rs b/minifi_rust/extensions/minifi_rs_playground/src/processors/zoo_processor.rs index 2bbdb9a55d..20eb46a5bd 100644 --- a/minifi_rust/extensions/minifi_rs_playground/src/processors/zoo_processor.rs +++ b/minifi_rust/extensions/minifi_rs_playground/src/processors/zoo_processor.rs @@ -18,12 +18,12 @@ use crate::controller_services::animal_controller_apis::{ CanFlyControllerApi, NumberOfLegsControllerApi, }; -use minifi_native::ControllerServiceApi; use minifi_native::macros::ComponentIdentifier; +use minifi_native::property_constraint; use minifi_native::{ GetProperty, Logger, MinifiError, OnTriggerResult, OutputAttribute, ProcessContext, ProcessSession, ProcessorDefinition, ProcessorInputRequirement, Property, Relationship, - Schedule, StandardPropertyValidator, Trigger, critical, info, + Schedule, Trigger, critical, info, }; pub(crate) const CAN_FLY_SERVICE: Property = Property { @@ -33,9 +33,7 @@ pub(crate) const CAN_FLY_SERVICE: Property = Property { is_sensitive: false, supports_expr_lang: false, default_value: None, - validator: StandardPropertyValidator::AlwaysValidValidator, - allowed_values: &[], - allowed_type: Some(::INTERFACE_NAME), + constraints: property_constraint::(), }; pub(crate) const NUMBER_OF_LEGS: Property = Property { @@ -45,9 +43,7 @@ pub(crate) const NUMBER_OF_LEGS: Property = Property { is_sensitive: false, supports_expr_lang: false, default_value: None, - validator: StandardPropertyValidator::AlwaysValidValidator, - allowed_values: &[], - allowed_type: Some(::INTERFACE_NAME), + constraints: property_constraint::(), }; #[derive(Debug, ComponentIdentifier)] diff --git a/minifi_rust/minifi_native/Cargo.toml b/minifi_rust/minifi_native/Cargo.toml index 5998c4fd32..b3f75648bc 100644 --- a/minifi_rust/minifi_native/Cargo.toml +++ b/minifi_rust/minifi_native/Cargo.toml @@ -13,3 +13,6 @@ strum_macros = "0.28.0" humantime = "2.3.0" byte-unit = "5.1.6" itertools = "0.14.0" + +[features] +test-utils = [] diff --git a/minifi_rust/minifi_native/src/api.rs b/minifi_rust/minifi_native/src/api.rs index 6152b94ca5..37d62acd0f 100644 --- a/minifi_rust/minifi_native/src/api.rs +++ b/minifi_rust/minifi_native/src/api.rs @@ -38,6 +38,9 @@ pub use process_session::{InputStream, OutputStream, ProcessSession}; pub use raw_controller_service::RawControllerService; pub use raw_processor::{OnTriggerResult, ProcessorInputRequirement, RawProcessor, ThreadingModel}; -pub use property::StandardPropertyValidator; +pub use property::{ + DataSize, PropertyConstraints, PropertyType, ProvidesPropertyConstraint, + StandardPropertyValidator, property_constraint, +}; pub use relationship::Relationship; diff --git a/minifi_rust/minifi_native/src/api/attribute.rs b/minifi_rust/minifi_native/src/api/attribute.rs index 9e6b316ebd..571ea7ce74 100644 --- a/minifi_rust/minifi_native/src/api/attribute.rs +++ b/minifi_rust/minifi_native/src/api/attribute.rs @@ -25,4 +25,8 @@ pub struct OutputAttribute { pub trait GetAttribute { fn get_attribute(&self, name: &str) -> Result, MinifiError>; + fn get_required_attribute(&self, name: &str) -> Result { + self.get_attribute(name)? + .ok_or(MinifiError::missing_required_attribute(name.to_owned())) + } } diff --git a/minifi_rust/minifi_native/src/api/errors.rs b/minifi_rust/minifi_native/src/api/errors.rs index 633d413ad7..2c833a9976 100644 --- a/minifi_rust/minifi_native/src/api/errors.rs +++ b/minifi_rust/minifi_native/src/api/errors.rs @@ -17,9 +17,10 @@ use minifi_native_sys::minifi_status; use std::borrow::Cow; +use std::error::Error; use std::ffi::NulError; use std::fmt; -use std::num::{NonZeroU32, ParseIntError}; +use std::num::{NonZeroU32, ParseFloatError, ParseIntError}; use std::str::ParseBoolError; #[derive(Debug, Clone)] @@ -30,6 +31,7 @@ pub enum ParseError { Duration(humantime::DurationError), Size(byte_unit::ParseError), Nul(NulError), + Float(ParseFloatError), Other, } @@ -37,6 +39,7 @@ pub enum ParseError { pub enum MinifiError { UnknownError, StatusError((Cow<'static, str>, NonZeroU32)), + MissingRequiredAttribute(Cow<'static, str>), MissingRequiredProperty(Cow<'static, str>), ControllerServiceError(Cow<'static, str>), ValidationError(Cow<'static, str>), @@ -89,6 +92,18 @@ impl From for MinifiError { } } +impl From for MinifiError { + fn from(err: ParseFloatError) -> Self { + MinifiError::Parse(ParseError::Float(err)) + } +} + +impl From for MinifiError { + fn from(_: std::convert::Infallible) -> Self { + unreachable!("Infallible errors can never happen") + } +} + impl MinifiError { pub(crate) fn to_status(&self) -> minifi_status { match self { @@ -125,9 +140,17 @@ impl MinifiError { MinifiError::MissingRequiredProperty(msg.into()) } + pub fn missing_required_attribute>>(msg: S) -> Self { + MinifiError::MissingRequiredAttribute(msg.into()) + } + pub fn controller_service_err>>(msg: S) -> Self { MinifiError::ControllerServiceError(msg.into()) } + + pub fn parse_err() -> Self { + MinifiError::Parse(ParseError::Other) + } } impl fmt::Display for MinifiError { @@ -158,3 +181,5 @@ impl fmt::Display for MinifiError { } } } + +impl Error for MinifiError {} diff --git a/minifi_rust/minifi_native/src/api/logger.rs b/minifi_rust/minifi_native/src/api/logger.rs index b7df518176..80a20b6f65 100644 --- a/minifi_rust/minifi_native/src/api/logger.rs +++ b/minifi_rust/minifi_native/src/api/logger.rs @@ -18,9 +18,23 @@ use std::fmt; use std::fmt::Debug; -use strum_macros::{Display, EnumString, VariantNames}; +use minifi_native_macros::PropertyType; +use strum_macros::{Display, EnumString, IntoStaticStr, VariantNames}; -#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Display, EnumString, VariantNames)] +#[derive( + Debug, + Clone, + Copy, + PartialEq, + Eq, + PartialOrd, + Ord, + Display, + EnumString, + VariantNames, + IntoStaticStr, + PropertyType, +)] #[strum(serialize_all = "PascalCase", const_into_str)] pub enum LogLevel { Trace, diff --git a/minifi_rust/minifi_native/src/api/process_context.rs b/minifi_rust/minifi_native/src/api/process_context.rs index 0f114cf479..528dceb42e 100644 --- a/minifi_rust/minifi_native/src/api/process_context.rs +++ b/minifi_rust/minifi_native/src/api/process_context.rs @@ -15,7 +15,6 @@ // specific language governing permissions and limitations // under the License. -use crate::StandardPropertyValidator::*; use crate::api::RawControllerService; use crate::api::component_definition_traits::ComponentIdentifier; use crate::api::flow_file::FlowFile; @@ -24,8 +23,6 @@ use crate::{ ControllerServiceApi, ControllerServiceDefinition, EnableControllerService, GetProperty, MinifiError, Property, }; -use std::str::FromStr; -use std::time::Duration; pub trait ProcessContext { type FlowFile: FlowFile; @@ -36,80 +33,6 @@ pub trait ProcessContext { flow_file: Option<&Self::FlowFile>, ) -> Result, MinifiError>; - fn get_bool_property( - &self, - property: &Property, - flow_file: Option<&Self::FlowFile>, - ) -> Result, MinifiError> { - if property.validator != BoolValidator { - return Err(MinifiError::validation_err(format!( - "to use get_bool_property {:?} must have BoolValidator", - property - ))); - } - - if let Some(property_val) = self.get_property(property, flow_file)? { - Ok(Some(bool::from_str(&property_val)?)) - } else { - Ok(None) - } - } - - fn get_duration_property( - &self, - property: &Property, - flow_file: Option<&Self::FlowFile>, - ) -> Result, MinifiError> { - if property.validator != TimePeriodValidator { - return Err(MinifiError::validation_err(format!( - "to use get_duration_property {:?} must have TimePeriodValidator", - property - ))); - } - - if let Some(property_val) = self.get_property(property, flow_file)? { - Ok(Some(humantime::parse_duration(property_val.as_str())?)) - } else { - Ok(None) - } - } - - fn get_size_property( - &self, - property: &Property, - flow_file: Option<&Self::FlowFile>, - ) -> Result, MinifiError> { - if property.validator != DataSizeValidator { - return Err(MinifiError::validation_err(format!( - "to use get_size_property {:?} must have DataSizeValidator", - property - ))); - } - if let Some(property_val) = self.get_property(property, flow_file)? { - Ok(Some(byte_unit::Byte::from_str(&property_val)?.as_u64())) - } else { - Ok(None) - } - } - - fn get_u64_property( - &self, - property: &Property, - flow_file: Option<&Self::FlowFile>, - ) -> Result, MinifiError> { - if property.validator != U64Validator { - return Err(MinifiError::validation_err(format!( - "to use get_u64_property {:?} must have U64Validator", - property - ))); - } - if let Some(property_val) = self.get_property(property, flow_file)? { - Ok(Some(u64::from_str(&property_val)?)) - } else { - Ok(None) - } - } - fn get_raw_controller_service( &self, property: &Property, @@ -133,7 +56,7 @@ impl GetProperty for S where S: ProcessContext, { - fn get_property(&self, property: &Property) -> Result, MinifiError> { + fn get_raw_property(&self, property: &Property) -> Result, MinifiError> { self.get_property(property, None) } } diff --git a/minifi_rust/minifi_native/src/api/processor_wrappers/flow_file_transform.rs b/minifi_rust/minifi_native/src/api/processor_wrappers/flow_file_transform.rs index f856ca83d8..ef2342b5b6 100644 --- a/minifi_rust/minifi_native/src/api/processor_wrappers/flow_file_transform.rs +++ b/minifi_rust/minifi_native/src/api/processor_wrappers/flow_file_transform.rs @@ -67,6 +67,19 @@ impl<'a> TransformedFlowFile<'a> { pub fn attributes_to_add(&self) -> &HashMap { &self.attributes_to_add } + + #[cfg(any(test, feature = "test-utils"))] + pub fn into_bytes(self) -> std::io::Result>> { + match self.new_content { + Some(Content::Buffer(vec)) => Ok(Some(vec)), + Some(Content::Stream(mut stream)) => { + let mut buffer = Vec::new(); + stream.read_to_end(&mut buffer)?; + Ok(Some(buffer)) + } + None => Ok(None), + } + } } pub trait FlowFileTransform { @@ -200,3 +213,42 @@ where } } } + +#[macro_export] +macro_rules! unwrap_or_route { + ($result:expr, $route:expr) => { + match $result { + Ok(v) => v, + Err(_e) => { + return Ok(TransformedFlowFile::route_without_changes($route)); + } + } + }; + + ($result:expr, $route:expr, $custom_logger:expr) => { + match $result { + Ok(v) => v, + Err(e) => { + minifi_native::error!( + $custom_logger, + "Failed to unwrap due to {}. Routing flow file.", + e + ); + return Ok(TransformedFlowFile::route_without_changes($route)); + } + } + }; + + ($result:expr, $route:expr, $custom_logger:expr, $context:expr) => { + match $result { + Ok(v) => v, + Err(e) => { + error!( + $custom_logger, + "Failed to {} due to {}. Routing flow file.", $context, e + ); + return Ok(TransformedFlowFile::route_without_changes($route)); + } + } + }; +} diff --git a/minifi_rust/minifi_native/src/api/processor_wrappers/utils/context_session_flowfile_bundle.rs b/minifi_rust/minifi_native/src/api/processor_wrappers/utils/context_session_flowfile_bundle.rs index 8745b2fb73..c059420661 100644 --- a/minifi_rust/minifi_native/src/api/processor_wrappers/utils/context_session_flowfile_bundle.rs +++ b/minifi_rust/minifi_native/src/api/processor_wrappers/utils/context_session_flowfile_bundle.rs @@ -55,7 +55,7 @@ where PC: ProcessContext, PS: ProcessSession, { - fn get_property(&self, property: &Property) -> Result, MinifiError> { + fn get_raw_property(&self, property: &Property) -> Result, MinifiError> { self.context.get_property(property, self.flow_file) } } diff --git a/minifi_rust/minifi_native/src/api/property.rs b/minifi_rust/minifi_native/src/api/property.rs index 952b6eb688..97c414894d 100644 --- a/minifi_rust/minifi_native/src/api/property.rs +++ b/minifi_rust/minifi_native/src/api/property.rs @@ -26,7 +26,6 @@ use std::time::Duration; #[derive(Debug, Eq, PartialEq)] pub enum StandardPropertyValidator { - AlwaysValidValidator, NonBlankValidator, TimePeriodValidator, BoolValidator, @@ -36,6 +35,17 @@ pub enum StandardPropertyValidator { PortValidator, } +#[derive(Debug, PartialEq)] +pub enum PropertyConstraints { + Validator(StandardPropertyValidator), + AllowedValues(&'static [&'static str]), + ControllerService(&'static str), +} + +pub trait ProvidesPropertyConstraint { + const PROPERTY_CONSTRAINT: Option; +} + #[derive(Debug)] pub struct Property { pub name: &'static str, @@ -44,69 +54,123 @@ pub struct Property { pub is_sensitive: bool, pub supports_expr_lang: bool, pub default_value: Option<&'static str>, - pub validator: StandardPropertyValidator, - pub allowed_values: &'static [&'static str], - pub allowed_type: Option<&'static str>, + pub constraints: Option, } -pub trait GetProperty { - fn get_property(&self, property: &Property) -> Result, MinifiError>; - fn get_bool_property(&self, property: &Property) -> Result, MinifiError> { - if property.validator != BoolValidator { - return Err(MinifiError::validation_err(format!( - "to use get_bool_property {:?} must have BoolValidator", - property - ))); - } +pub trait PropertyType { + type Output; + const EXPECTED_CONSTRAINTS: Option = None; - if let Some(property_val) = self.get_property(property)? { - Ok(Some(bool::from_str(&property_val)?)) - } else { - Ok(None) - } + fn parse(s: &str) -> Result; +} + +impl PropertyConstraints { + pub const fn non_blank() -> Option { + Some(Self::Validator( + StandardPropertyValidator::NonBlankValidator, + )) } +} - fn get_duration_property(&self, property: &Property) -> Result, MinifiError> { - if property.validator != TimePeriodValidator { - return Err(MinifiError::validation_err(format!( - "to use get_duration_property {:?} must have TimePeriodValidator", - property - ))); - } +pub const fn property_constraint() +-> Option { + T::PROPERTY_CONSTRAINT +} - if let Some(property_val) = self.get_property(property)? { - Ok(Some(humantime::parse_duration(property_val.as_str())?)) - } else { - Ok(None) +macro_rules! impl_from_str_property { + ($t:ty, $validator:expr) => { + impl PropertyType for $t { + type Output = $t; + const EXPECTED_CONSTRAINTS: Option = $validator; + + fn parse(s: &str) -> Result { + s.parse::<$t>().map_err(Into::into) + } + } + impl ProvidesPropertyConstraint for $t { + const PROPERTY_CONSTRAINT: Option = $validator; } + }; + ($t:ty) => { + impl_from_str_property!($t, None); + }; +} + +impl_from_str_property!(String); +impl_from_str_property!(std::path::PathBuf); +impl_from_str_property!(f64); +impl_from_str_property!(f32); +impl_from_str_property!(i64); +impl_from_str_property!(i32); +impl_from_str_property!(bool, Some(PropertyConstraints::Validator(BoolValidator))); +impl_from_str_property!(u64, Some(PropertyConstraints::Validator(U64Validator))); +impl_from_str_property!(u32, Some(PropertyConstraints::Validator(U64Validator))); +impl_from_str_property!(usize, Some(PropertyConstraints::Validator(U64Validator))); + +impl PropertyType for Duration { + type Output = Duration; + const EXPECTED_CONSTRAINTS: Option = + Some(PropertyConstraints::Validator(TimePeriodValidator)); + fn parse(s: &str) -> Result { + humantime::parse_duration(s).map_err(Into::into) } +} +impl ProvidesPropertyConstraint for Duration { + const PROPERTY_CONSTRAINT: Option = Self::EXPECTED_CONSTRAINTS; +} - fn get_size_property(&self, property: &Property) -> Result, MinifiError> { - if property.validator != DataSizeValidator { +pub struct DataSize; +impl PropertyType for DataSize { + type Output = u64; + const EXPECTED_CONSTRAINTS: Option = + Some(PropertyConstraints::Validator(DataSizeValidator)); + fn parse(s: &str) -> Result { + byte_unit::Byte::from_str(s) + .map(|b| b.as_u64()) + .map_err(Into::into) + } +} + +impl ProvidesPropertyConstraint for DataSize { + const PROPERTY_CONSTRAINT: Option = Self::EXPECTED_CONSTRAINTS; +} + +pub trait GetProperty { + fn get_raw_property(&self, property: &Property) -> Result, MinifiError>; + + fn get_property( + &self, + property: &Property, + ) -> Result, MinifiError> { + if let Some(expected) = T::EXPECTED_CONSTRAINTS + && Some(expected) != property.constraints + { return Err(MinifiError::validation_err(format!( - "to use get_size_property {:?} must have DataSizeValidator", - property + "to use get_property for this type, {:?} must have validator {:?}", + property.name, + T::EXPECTED_CONSTRAINTS ))); } - if let Some(property_val) = self.get_property(property)? { - Ok(Some(byte_unit::Byte::from_str(&property_val)?.as_u64())) + + if let Some(property_val) = self.get_raw_property(property)? { + Ok(Some(T::parse(&property_val)?)) } else { Ok(None) } } - fn get_u64_property(&self, property: &Property) -> Result, MinifiError> { - if property.validator != U64Validator { + fn get_req_property( + &self, + property: &Property, + ) -> Result { + if !property.is_required { return Err(MinifiError::validation_err(format!( - "to use get_u64_property {:?} must have U64Validator", - property + "to use get_req_property, {:?} must be required", + property.name ))); } - if let Some(property_val) = self.get_property(property)? { - Ok(Some(u64::from_str(&property_val)?)) - } else { - Ok(None) - } + self.get_property::(property)? + .ok_or_else(|| MinifiError::missing_required_property(property.name)) } } diff --git a/minifi_rust/minifi_native/src/api/relationship.rs b/minifi_rust/minifi_native/src/api/relationship.rs index 45041a2ba5..df3e340816 100644 --- a/minifi_rust/minifi_native/src/api/relationship.rs +++ b/minifi_rust/minifi_native/src/api/relationship.rs @@ -15,8 +15,16 @@ // specific language governing permissions and limitations // under the License. +use std::fmt::{Display, Formatter}; + #[derive(Debug, Eq, PartialEq)] pub struct Relationship { pub name: &'static str, pub description: &'static str, } + +impl Display for Relationship { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + write!(f, "{}", self.name) + } +} diff --git a/minifi_rust/minifi_native/src/c_ffi/c_ffi_controller_service_context.rs b/minifi_rust/minifi_native/src/c_ffi/c_ffi_controller_service_context.rs index 2284c8dc75..2c285fd4f4 100644 --- a/minifi_rust/minifi_native/src/c_ffi/c_ffi_controller_service_context.rs +++ b/minifi_rust/minifi_native/src/c_ffi/c_ffi_controller_service_context.rs @@ -61,7 +61,7 @@ unsafe extern "C" fn property_callback( } impl<'a> GetProperty for CffiControllerServiceContext<'a> { - fn get_property(&self, property: &Property) -> Result, MinifiError> { + fn get_raw_property(&self, property: &Property) -> Result, MinifiError> { let mut result: Option = None; let property_name: StringView = StringView::new(property.name); diff --git a/minifi_rust/minifi_native/src/c_ffi/c_ffi_property.rs b/minifi_rust/minifi_native/src/c_ffi/c_ffi_property.rs index d2fe2c1b7d..ec2c0d3d7a 100644 --- a/minifi_rust/minifi_native/src/c_ffi/c_ffi_property.rs +++ b/minifi_rust/minifi_native/src/c_ffi/c_ffi_property.rs @@ -16,6 +16,7 @@ // under the License. use super::c_ffi_primitives::StaticStrAsMinifiCStr; +use crate::api::property::PropertyConstraints; use crate::{Property, StandardPropertyValidator}; use minifi_native_sys::{ minifi_property_definition, minifi_string_view, minifi_validator, @@ -76,11 +77,14 @@ impl Property { fn create_c_allowed_values_vec_vec(properties: &[Self]) -> Vec> { properties .iter() - .map(|p| { - p.allowed_values + .map(|p| match p.constraints { + Some(PropertyConstraints::AllowedValues(allowed_values)) => allowed_values .iter() .map(|av| av.as_minifi_c_type()) - .collect() + .collect(), + _ => { + vec![] + } }) .collect() } @@ -88,9 +92,11 @@ impl Property { fn create_c_allowed_types_vec(properties: &[Self]) -> Vec { properties .iter() - .map(|p| match p.allowed_type { - Some(dv) => dv.as_minifi_c_type(), - None => minifi_string_view { + .map(|p| match p.constraints { + Some(PropertyConstraints::ControllerService(allowed_type)) => { + allowed_type.as_minifi_c_type() + } + _ => minifi_string_view { data: ptr::null(), length: 0, }, @@ -125,7 +131,10 @@ impl Property { }, allowed_values_count: allowed_values.len(), allowed_values_ptr: allowed_values.as_ptr(), - validator: property.validator.as_minifi_c_type(), + validator: match &property.constraints { + Some(PropertyConstraints::Validator(s)) => s.as_minifi_c_type(), + _ => minifi_validator_MINIFI_VALIDATOR_ALWAYS_VALID, + }, allowed_type: if allowed_type.data.is_null() { std::ptr::null() } else { @@ -147,9 +156,6 @@ impl Property { impl StandardPropertyValidator { pub(crate) fn as_minifi_c_type(&self) -> minifi_validator { match self { - StandardPropertyValidator::AlwaysValidValidator => { - minifi_validator_MINIFI_VALIDATOR_ALWAYS_VALID - } StandardPropertyValidator::NonBlankValidator => { minifi_validator_MINIFI_VALIDATOR_NON_BLANK } diff --git a/minifi_rust/minifi_native/src/lib.rs b/minifi_rust/minifi_native/src/lib.rs index c467b68e87..4c710765da 100644 --- a/minifi_rust/minifi_native/src/lib.rs +++ b/minifi_rust/minifi_native/src/lib.rs @@ -14,6 +14,7 @@ // KIND, either express or implied. See the License for the // specific language governing permissions and limitations // under the License. +extern crate self as minifi_native; mod api; pub mod c_ffi; @@ -54,8 +55,9 @@ pub use api::process_session::IoState; pub use api::attribute::{GetAttribute, OutputAttribute}; pub use api::{ - FlowFile, GetId, InputStream, OnTriggerResult, OutputStream, ProcessContext, ProcessSession, - ProcessorInputRequirement, Relationship, StandardPropertyValidator, + DataSize, FlowFile, GetId, InputStream, OnTriggerResult, OutputStream, ProcessContext, + ProcessSession, ProcessorInputRequirement, PropertyConstraints, PropertyType, + ProvidesPropertyConstraint, Relationship, StandardPropertyValidator, property_constraint, }; pub use minifi_native_macros as macros; diff --git a/minifi_rust/minifi_native/src/mock/mock_controller_service_context.rs b/minifi_rust/minifi_native/src/mock/mock_controller_service_context.rs index 2a594ef8a9..f67bd50083 100644 --- a/minifi_rust/minifi_native/src/mock/mock_controller_service_context.rs +++ b/minifi_rust/minifi_native/src/mock/mock_controller_service_context.rs @@ -23,7 +23,7 @@ pub struct MockControllerServiceContext { } impl GetProperty for MockControllerServiceContext { - fn get_property(&self, property: &Property) -> Result, MinifiError> { + fn get_raw_property(&self, property: &Property) -> Result, MinifiError> { self.properties.get_property(property, None) } } diff --git a/minifi_rust/minifi_native_macros/src/lib.rs b/minifi_rust/minifi_native_macros/src/lib.rs index fa8e563361..8aff6eaf74 100644 --- a/minifi_rust/minifi_native_macros/src/lib.rs +++ b/minifi_rust/minifi_native_macros/src/lib.rs @@ -31,6 +31,11 @@ pub fn derive_component_identifier(input: TokenStream) -> TokenStream { const GROUP_NAME: &'static str = env!("CARGO_PKG_NAME"); const VERSION: &'static str = env!("CARGO_PKG_VERSION"); } + + impl ::minifi_native::ProvidesPropertyConstraint for #name { + const PROPERTY_CONSTRAINT: Option<::minifi_native::PropertyConstraints> = + Some(::minifi_native::PropertyConstraints::ControllerService(::CLASS_NAME)); + } }; TokenStream::from(expanded) @@ -48,6 +53,36 @@ pub fn controller_service_api(_attr: TokenStream, item: TokenStream) -> TokenStr impl ::minifi_native::ControllerServiceApi for dyn #name { const INTERFACE_NAME: &'static str = concat!(module_path!(), "::", #name_str); } + + impl ::minifi_native::ProvidesPropertyConstraint for dyn #name { + const PROPERTY_CONSTRAINT: Option<::minifi_native::PropertyConstraints> = + Some(::minifi_native::PropertyConstraints::ControllerService(::INTERFACE_NAME)); + } + }; + + TokenStream::from(expanded) +} + +#[proc_macro_derive(PropertyType)] +pub fn derive_property_type(input: TokenStream) -> TokenStream { + let input = parse_macro_input!(input as DeriveInput); + let name = &input.ident; + + let expanded = quote! { + impl ::minifi_native::PropertyType for #name { + type Output = #name; + const EXPECTED_CONSTRAINTS: Option<::minifi_native::PropertyConstraints> = + Some(::minifi_native::PropertyConstraints::AllowedValues( + <#name as ::strum::VariantNames>::VARIANTS + )); + fn parse(s: &str) -> Result { + s.parse::<#name>().map_err(Into::into) + } + } + + impl ::minifi_native::ProvidesPropertyConstraint for #name { + const PROPERTY_CONSTRAINT: Option<::minifi_native::PropertyConstraints> = ::EXPECTED_CONSTRAINTS; + } }; TokenStream::from(expanded) diff --git a/minifi_rust/minifi_rs_behave/src/main.rs b/minifi_rust/minifi_rs_behave/src/main.rs index f0d8c12bc4..2e8517f099 100644 --- a/minifi_rust/minifi_rs_behave/src/main.rs +++ b/minifi_rust/minifi_rs_behave/src/main.rs @@ -95,6 +95,7 @@ impl BehaveRunner { .arg("pip") .arg("install") .arg(self.minifi_behave_path.to_string_lossy().as_ref()) + .arg("certifi") .status() .expect("Failed to install dependencies") }