MINIFICPP-2862 Add StandardValidator::NUMBER_VALIDATOR (#2213)
diff --git a/.github/references/ubuntu_22_04_clang_arm_manifest.json b/.github/references/ubuntu_22_04_clang_arm_manifest.json
index bcb495d..750f395 100644
--- a/.github/references/ubuntu_22_04_clang_arm_manifest.json
+++ b/.github/references/ubuntu_22_04_clang_arm_manifest.json
@@ -10268,7 +10268,7 @@
"Min P": {
"name": "Min P",
"description": "Sets a minimum base probability threshold for token selection. 0.0 = disabled.",
- "validator": "VALID",
+ "validator": "NUMBER_VALIDATOR",
"required": "false",
"sensitive": "false",
"expressionLanguageScope": "NONE"
diff --git a/core-framework/common/include/utils/ParsingUtils.h b/core-framework/common/include/utils/ParsingUtils.h
index 33236ed..85e18bd 100644
--- a/core-framework/common/include/utils/ParsingUtils.h
+++ b/core-framework/common/include/utils/ParsingUtils.h
@@ -40,7 +40,8 @@
std::expected<uint32_t, std::error_code> parseUnixOctalPermissions(std::string_view input);
-std::expected<float, std::error_code> parseFloat(std::string_view input);
+template<std::floating_point T>
+std::expected<T, std::error_code> parseFloatingPoint(std::string_view input);
template<std::integral T>
std::expected<T, std::error_code> parseIntegralMinMax(const std::string_view input, const T minimum, const T maximum) {
@@ -90,4 +91,22 @@
return *result;
}
+template<std::floating_point T>
+std::expected<T, std::error_code> parseFloatingPoint(const std::string_view input) {
+ const auto trimmed_input = utils::string::trim(input);
+ T value{};
+
+ const auto [ptr, ec] = std::from_chars(trimmed_input.data(), trimmed_input.data() + trimmed_input.size(), value);
+
+ if (ec != std::errc()) {
+ return std::unexpected{core::ParsingErrorCode::GeneralParsingError};
+ }
+
+ if (ptr != trimmed_input.data() + trimmed_input.size()) {
+ return std::unexpected{core::ParsingErrorCode::GeneralParsingError};
+ }
+
+ return value;
+}
+
} // namespace org::apache::nifi::minifi::parsing
diff --git a/core-framework/common/src/utils/ParsingUtils.cpp b/core-framework/common/src/utils/ParsingUtils.cpp
index 922704b..75f095e 100644
--- a/core-framework/common/src/utils/ParsingUtils.cpp
+++ b/core-framework/common/src/utils/ParsingUtils.cpp
@@ -126,12 +126,4 @@
return result;
}
-std::expected<float, std::error_code> parseFloat(std::string_view input) {
- try {
- return std::stof(std::string{input});
- } catch(const std::exception&) {
- return std::unexpected{core::ParsingErrorCode::GeneralParsingError};
- }
-}
-
} // namespace org::apache::nifi::minifi::parsing
diff --git a/extension-framework/cpp-extension-lib/include/api/utils/ProcessorConfigUtils.h b/extension-framework/cpp-extension-lib/include/api/utils/ProcessorConfigUtils.h
index 2ec9571..ea2c17d 100644
--- a/extension-framework/cpp-extension-lib/include/api/utils/ProcessorConfigUtils.h
+++ b/extension-framework/cpp-extension-lib/include/api/utils/ProcessorConfigUtils.h
@@ -134,7 +134,7 @@
if (property_str->empty()) {
return std::nullopt;
}
- return parsing::parseFloat(*property_str)
+ return parsing::parseFloatingPoint<float>(*property_str)
| minifi::utils::orThrow(fmt::format("Expected parsable float from \"{}\"", property.name));
}
return std::nullopt;
diff --git a/extension-framework/cpp-extension-lib/src/utils/minifi-c-utils.cpp b/extension-framework/cpp-extension-lib/src/utils/minifi-c-utils.cpp
index e11fd89..cf70d2c 100644
--- a/extension-framework/cpp-extension-lib/src/utils/minifi-c-utils.cpp
+++ b/extension-framework/cpp-extension-lib/src/utils/minifi-c-utils.cpp
@@ -55,6 +55,9 @@
if (validator->getEquivalentNifiStandardValidatorName() == minifi::core::StandardPropertyValidators::PORT_VALIDATOR.getEquivalentNifiStandardValidatorName()) {
return MINIFI_VALIDATOR_PORT;
}
+ if (validator->getEquivalentNifiStandardValidatorName() == minifi::core::StandardPropertyValidators::NUMBER_VALIDATOR.getEquivalentNifiStandardValidatorName()) {
+ return MINIFI_VALIDATOR_NUMBER;
+ }
gsl_FailFast();
}
diff --git a/extension-framework/include/utils/ProcessorConfigUtils.h b/extension-framework/include/utils/ProcessorConfigUtils.h
index b69ed40..b3b5071 100644
--- a/extension-framework/include/utils/ProcessorConfigUtils.h
+++ b/extension-framework/include/utils/ProcessorConfigUtils.h
@@ -55,6 +55,18 @@
| orThrow(fmt::format("Expected parsable int64_t from \"{}\"", property.name));
}
+inline double parseF64Property(const core::ProcessContext& ctx, const core::PropertyReference& property, const core::FlowFile* flow_file = nullptr) {
+ return ctx.getProperty(property.name, flow_file)
+ | andThen(parsing::parseFloatingPoint<double>)
+ | orThrow(fmt::format("Expected parsable double from \"{}\"", property.name));
+}
+
+inline float parseF32Property(const core::ProcessContext& ctx, const core::PropertyReference& property, const core::FlowFile* flow_file = nullptr) {
+ return ctx.getProperty(property.name, flow_file)
+ | andThen(parsing::parseFloatingPoint<float>)
+ | orThrow(fmt::format("Expected parsable float from \"{}\"", property.name));
+}
+
inline std::chrono::milliseconds parseDurationProperty(const core::ProcessContext& ctx, const core::PropertyReference& property, const core::FlowFile* flow_file = nullptr) {
return ctx.getProperty(property.name, flow_file)
| andThen(parsing::parseDuration<std::chrono::milliseconds>)
@@ -133,7 +145,7 @@
if (property_str->empty()) {
return std::nullopt;
}
- return parsing::parseFloat(*property_str)
+ return parsing::parseFloatingPoint<float>(*property_str)
| utils::orThrow(fmt::format("Expected parsable float from \"{}\"", property.name));
}
return std::nullopt;
diff --git a/extensions/llamacpp/processors/RunLlamaCppInference.h b/extensions/llamacpp/processors/RunLlamaCppInference.h
index 16cf7a9..67cd14a 100644
--- a/extensions/llamacpp/processors/RunLlamaCppInference.h
+++ b/extensions/llamacpp/processors/RunLlamaCppInference.h
@@ -83,6 +83,7 @@
.build();
EXTENSIONAPI static constexpr auto MinP = core::PropertyDefinitionBuilder<>::createProperty("Min P")
.withDescription("Sets a minimum base probability threshold for token selection. 0.0 = disabled.")
+ .withValidator(core::StandardPropertyValidators::NUMBER_VALIDATOR)
.build();
EXTENSIONAPI static constexpr auto MinKeep = core::PropertyDefinitionBuilder<>::createProperty("Min Keep")
.withDescription("If greater than 0, force samplers to return N possible tokens at minimum.")
diff --git a/extensions/llamacpp/tests/RunLlamaCppInferenceTests.cpp b/extensions/llamacpp/tests/RunLlamaCppInferenceTests.cpp
index 3582c21..fb22075 100644
--- a/extensions/llamacpp/tests/RunLlamaCppInferenceTests.cpp
+++ b/extensions/llamacpp/tests/RunLlamaCppInferenceTests.cpp
@@ -233,10 +233,6 @@
REQUIRE(controller.getProcessor()->setProperty(processors::RunLlamaCppInference::TopP.name, "invalid_value"));
property_name = processors::RunLlamaCppInference::TopP.name;
}
- SECTION("Invalid value for Min P property") {
- REQUIRE(controller.getProcessor()->setProperty(processors::RunLlamaCppInference::MinP.name, "invalid_value"));
- property_name = processors::RunLlamaCppInference::MinP.name;
- }
REQUIRE_THROWS(controller.trigger(minifi::test::InputFlowFileData{.content = "42", .attributes = {}}));
CHECK(minifi::test::utils::verifyLogLinePresenceInPollTime(1s,
diff --git a/extensions/python/ExecutePythonProcessor.cpp b/extensions/python/ExecutePythonProcessor.cpp
index 184acad..7ef4ae7 100644
--- a/extensions/python/ExecutePythonProcessor.cpp
+++ b/extensions/python/ExecutePythonProcessor.cpp
@@ -138,7 +138,8 @@
DATA_SIZE = 3,
TIME_PERIOD = 4,
NON_BLANK = 5,
- PORT = 6
+ PORT = 6,
+ NUMBER = 7
};
const core::PropertyValidator& translateCodeToPropertyValidator(const PropertyValidatorCode& code) {
@@ -156,6 +157,8 @@
return core::StandardPropertyValidators::NON_BLANK_VALIDATOR;
case PropertyValidatorCode::PORT:
return core::StandardPropertyValidators::PORT_VALIDATOR;
+ case PropertyValidatorCode::NUMBER:
+ return core::StandardPropertyValidators::NUMBER_VALIDATOR;
default:
throw std::invalid_argument("Unknown PropertyValidatorCode");
}
diff --git a/extensions/python/pythonprocessors/nifiapi/properties.py b/extensions/python/pythonprocessors/nifiapi/properties.py
index 3cbd4c5..fe0dab0 100644
--- a/extensions/python/pythonprocessors/nifiapi/properties.py
+++ b/extensions/python/pythonprocessors/nifiapi/properties.py
@@ -53,6 +53,9 @@
def createLongValidator(self, *args) -> int:
return StandardValidators.LONG_VALIDATOR
+ def createNumberValidator(self, *args) -> int:
+ return StandardValidators.NUMBER_VALIDATOR
+
class StandardValidators:
_standard_validators = ValidatorGenerator()
@@ -87,6 +90,7 @@
TIME_PERIOD_TYPE = 4
NON_BLANK_TYPE = 5
PORT_TYPE = 6
+ NUMBER_TYPE = 7
def translateStandardValidatorToMiNiFiPropertype(validators: List[int]) -> int:
@@ -108,6 +112,8 @@
return MinifiPropertyTypes.NON_BLANK_TYPE
if validator == StandardValidators.PORT_VALIDATOR:
return MinifiPropertyTypes.PORT_TYPE
+ if validator == StandardValidators.NUMBER_VALIDATOR:
+ return MinifiPropertyTypes.NUMBER_TYPE
return None
diff --git a/libminifi/src/minifi-api.cpp b/libminifi/src/minifi-api.cpp
index 1b48434..b1cc566 100644
--- a/libminifi/src/minifi-api.cpp
+++ b/libminifi/src/minifi-api.cpp
@@ -88,6 +88,7 @@
case MINIFI_VALIDATOR_UNSIGNED_INTEGER: return gsl::make_not_null(&minifi::core::StandardPropertyValidators::UNSIGNED_INTEGER_VALIDATOR);
case MINIFI_VALIDATOR_DATA_SIZE: return gsl::make_not_null(&minifi::core::StandardPropertyValidators::DATA_SIZE_VALIDATOR);
case MINIFI_VALIDATOR_PORT: return gsl::make_not_null(&minifi::core::StandardPropertyValidators::PORT_VALIDATOR);
+ case MINIFI_VALIDATOR_NUMBER: return gsl::make_not_null(&minifi::core::StandardPropertyValidators::NUMBER_VALIDATOR);
}
gsl_FailFast();
}
diff --git a/libminifi/test/unit/PropertyValidationTests.cpp b/libminifi/test/unit/PropertyValidationTests.cpp
index 62770fa..0749551 100644
--- a/libminifi/test/unit/PropertyValidationTests.cpp
+++ b/libminifi/test/unit/PropertyValidationTests.cpp
@@ -224,4 +224,16 @@
CHECK(component.setProperty(property.getName(), "20").error() == core::PropertyErrorCode::ValidationFailed);
}
+TEST_CASE("Number validator") {
+ static constexpr auto property_definition = PropertyDefinitionBuilder<>::createProperty("prop").withValidator(core::StandardPropertyValidators::NUMBER_VALIDATOR).build();
+ const Property property{property_definition};
+ TestConfigurableComponent component;
+ component.setSupportedProperties(std::array<PropertyReference, 1>{property_definition});
+ CHECK(component.setProperty(property.getName(), "20"));
+ CHECK(component.setProperty(property.getName(), "3.14"));
+ CHECK(component.setProperty(property.getName(), "0.0000000001"));
+ CHECK_FALSE(component.setProperty(property.getName(), "10 000"));
+ CHECK_FALSE(component.setProperty(property.getName(), "20 apples"));
+}
+
} // namespace org::apache::nifi::minifi::core
diff --git a/minifi-api/common/include/minifi-cpp/core/PropertyValidator.h b/minifi-api/common/include/minifi-cpp/core/PropertyValidator.h
index 2d91905..06f66e9 100644
--- a/minifi-api/common/include/minifi-cpp/core/PropertyValidator.h
+++ b/minifi-api/common/include/minifi-cpp/core/PropertyValidator.h
@@ -25,27 +25,31 @@
class PropertyValidator {
public:
virtual constexpr ~PropertyValidator() {} // NOLINT can't use = default because of gcc bug: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=93413
+ PropertyValidator() = default;
+ PropertyValidator(const PropertyValidator&) = delete;
+ PropertyValidator(PropertyValidator&&) = delete;
+ PropertyValidator& operator=(const PropertyValidator& other) = delete;
+ PropertyValidator& operator=(PropertyValidator&& other) = delete;
[[nodiscard]] virtual std::optional<std::string_view> getEquivalentNifiStandardValidatorName() const = 0;
[[nodiscard]] virtual bool validate(std::string_view input) const = 0;
};
-
class AlwaysValidValidator final : public PropertyValidator {
public:
- AlwaysValidValidator() = default;
- constexpr ~AlwaysValidValidator() override {} // NOLINT see comment at parent
-
- [[nodiscard]] std::optional<std::string_view> getEquivalentNifiStandardValidatorName() const override { return "VALID"; }
- [[nodiscard]] bool validate(std::string_view) const override { return true; }
+ [[nodiscard]] std::optional<std::string_view> getEquivalentNifiStandardValidatorName() const override {
+ return "VALID";
+ }
+ [[nodiscard]] bool validate(std::string_view) const override {
+ return true;
+ }
};
class NonBlankValidator final : public PropertyValidator {
public:
- NonBlankValidator() = default;
- constexpr ~NonBlankValidator() override {} // NOLINT see comment at parent
-
- [[nodiscard]] std::optional<std::string_view> getEquivalentNifiStandardValidatorName() const override { return "NON_BLANK_VALIDATOR"; }
+ [[nodiscard]] std::optional<std::string_view> getEquivalentNifiStandardValidatorName() const override {
+ return "NON_BLANK_VALIDATOR";
+ }
[[nodiscard]] bool validate(const std::string_view input) const override {
return !utils::string::trim(input).empty();
}
@@ -53,10 +57,9 @@
class TimePeriodValidator final : public PropertyValidator {
public:
- TimePeriodValidator() = default;
- constexpr ~TimePeriodValidator() override {} // NOLINT see comment at parent
-
- [[nodiscard]] std::optional<std::string_view> getEquivalentNifiStandardValidatorName() const override { return "TIME_PERIOD_VALIDATOR"; }
+ [[nodiscard]] std::optional<std::string_view> getEquivalentNifiStandardValidatorName() const override {
+ return "TIME_PERIOD_VALIDATOR";
+ }
[[nodiscard]] bool validate(const std::string_view input) const override {
const auto parsed_time = parsing::parseDuration<std::chrono::nanoseconds>(input);
return parsed_time.has_value();
@@ -65,10 +68,9 @@
class BooleanValidator final : public PropertyValidator {
public:
- BooleanValidator() = default;
- constexpr ~BooleanValidator() override {} // NOLINT see comment at parent
-
- [[nodiscard]] std::optional<std::string_view> getEquivalentNifiStandardValidatorName() const override { return "BOOLEAN_VALIDATOR"; }
+ [[nodiscard]] std::optional<std::string_view> getEquivalentNifiStandardValidatorName() const override {
+ return "BOOLEAN_VALIDATOR";
+ }
[[nodiscard]] bool validate(const std::string_view input) const override {
const auto parsed_bool = parsing::parseBool(input);
return parsed_bool.has_value();
@@ -77,10 +79,9 @@
class IntegerValidator final : public PropertyValidator {
public:
- IntegerValidator() = default;
- constexpr ~IntegerValidator() override {} // NOLINT see comment at parent
-
- [[nodiscard]] std::optional<std::string_view> getEquivalentNifiStandardValidatorName() const override { return "INTEGER_VALIDATOR"; }
+ [[nodiscard]] std::optional<std::string_view> getEquivalentNifiStandardValidatorName() const override {
+ return "INTEGER_VALIDATOR";
+ }
[[nodiscard]] bool validate(const std::string_view input) const override {
const auto parsed_integer = parsing::parseIntegral<int64_t>(input);
return parsed_integer.has_value();
@@ -89,10 +90,9 @@
class UnsignedIntegerValidator final : public PropertyValidator {
public:
- UnsignedIntegerValidator() = default;
- constexpr ~UnsignedIntegerValidator() override {} // NOLINT see comment at parent
-
- [[nodiscard]] std::optional<std::string_view> getEquivalentNifiStandardValidatorName() const override { return "NON_NEGATIVE_INTEGER_VALIDATOR"; }
+ [[nodiscard]] std::optional<std::string_view> getEquivalentNifiStandardValidatorName() const override {
+ return "NON_NEGATIVE_INTEGER_VALIDATOR";
+ }
[[nodiscard]] bool validate(const std::string_view input) const override {
const auto parsed_integer = parsing::parseIntegral<uint64_t>(input);
return parsed_integer.has_value();
@@ -101,10 +101,9 @@
class DataSizeValidator final : public PropertyValidator {
public:
- DataSizeValidator() = default;
- constexpr ~DataSizeValidator() override {} // NOLINT see comment at parent
-
- [[nodiscard]] std::optional<std::string_view> getEquivalentNifiStandardValidatorName() const override { return "DATA_SIZE_VALIDATOR"; }
+ [[nodiscard]] std::optional<std::string_view> getEquivalentNifiStandardValidatorName() const override {
+ return "DATA_SIZE_VALIDATOR";
+ }
[[nodiscard]] bool validate(const std::string_view input) const override {
const auto parsed_data_size = parsing::parseDataSize(input);
return parsed_data_size.has_value();
@@ -113,16 +112,26 @@
class PortValidator final : public core::PropertyValidator {
public:
- PortValidator() = default;
- constexpr ~PortValidator() override {} // NOLINT see comment at parent
-
- [[nodiscard]] std::optional<std::string_view> getEquivalentNifiStandardValidatorName() const override { return "PORT_VALIDATOR"; }
+ [[nodiscard]] std::optional<std::string_view> getEquivalentNifiStandardValidatorName() const override {
+ return "PORT_VALIDATOR";
+ }
[[nodiscard]] bool validate(const std::string_view input) const override {
const auto parsed_integer = parsing::parseIntegralMinMax<uint64_t>(input, 0, 65535);
return parsed_integer.has_value();
}
};
+class NumberValidator final : public core::PropertyValidator {
+ public:
+ [[nodiscard]] std::optional<std::string_view> getEquivalentNifiStandardValidatorName() const override {
+ return "NUMBER_VALIDATOR";
+ }
+ [[nodiscard]] bool validate(const std::string_view input) const override {
+ const auto parsed_number = parsing::parseFloatingPoint<double>(input);
+ return parsed_number.has_value();
+ }
+};
+
namespace StandardPropertyValidators {
inline constexpr auto ALWAYS_VALID_VALIDATOR = AlwaysValidValidator{};
inline constexpr auto NON_BLANK_VALIDATOR = NonBlankValidator{};
@@ -132,6 +141,7 @@
inline constexpr auto UNSIGNED_INTEGER_VALIDATOR = UnsignedIntegerValidator{};
inline constexpr auto DATA_SIZE_VALIDATOR = DataSizeValidator{};
inline constexpr auto PORT_VALIDATOR = PortValidator{};
-}
+inline constexpr auto NUMBER_VALIDATOR = NumberValidator{};
+} // namespace StandardPropertyValidators
} // namespace org::apache::nifi::minifi::core
diff --git a/minifi-api/include/minifi-api.h b/minifi-api/include/minifi-api.h
index 2ee6f87..6f4cf5b 100644
--- a/minifi-api/include/minifi-api.h
+++ b/minifi-api/include/minifi-api.h
@@ -119,6 +119,7 @@
MINIFI_VALIDATOR_UNSIGNED_INTEGER = 5,
MINIFI_VALIDATOR_DATA_SIZE = 6,
MINIFI_VALIDATOR_PORT = 7,
+ MINIFI_VALIDATOR_NUMBER = 8,
};
struct minifi_property_definition {