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
2 changes: 1 addition & 1 deletion .github/references/ubuntu_22_04_clang_arm_manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
21 changes: 20 additions & 1 deletion core-framework/common/include/utils/ParsingUtils.h
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,8 @@ std::expected<uint64_t, std::error_code> parseDataSize(std::string_view input);

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) {
Expand Down Expand Up @@ -90,4 +91,22 @@ std::expected<T, std::error_code> parseEnum(const std::string_view input) {
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);

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unlike other parsing functions in C++ and C libraries, std::from_chars is locale-independent, non-allocating, and non-throwing.

source: https://en.cppreference.com/cpp/utility/from_chars


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
8 changes: 0 additions & 8 deletions core-framework/common/src/utils/ParsingUtils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -126,12 +126,4 @@ std::expected<uint32_t, std::error_code> parseUnixOctalPermissions(const std::st
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
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,7 @@ inline std::optional<float> parseOptionalFloatProperty(const core::ProcessContex
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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,9 @@ minifi_validator toStandardPropertyValidator(const minifi::core::PropertyValidat
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();
}

Expand Down
14 changes: 13 additions & 1 deletion extension-framework/include/utils/ProcessorConfigUtils.h
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,18 @@ inline int64_t parseI64Property(const core::ProcessContext& ctx, const core::Pro
| 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>)
Expand Down Expand Up @@ -133,7 +145,7 @@ inline std::optional<float> parseOptionalFloatProperty(const core::ProcessContex
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;
Expand Down
1 change: 1 addition & 0 deletions extensions/llamacpp/processors/RunLlamaCppInference.h
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ class RunLlamaCppInference : public api::core::ProcessorImpl {
.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.")
Expand Down
4 changes: 0 additions & 4 deletions extensions/llamacpp/tests/RunLlamaCppInferenceTests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -233,10 +233,6 @@ TEST_CASE("Invalid values for optional double type properties throw exception")
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,
Expand Down
5 changes: 4 additions & 1 deletion extensions/python/ExecutePythonProcessor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,8 @@ enum class PropertyValidatorCode : int64_t {
DATA_SIZE = 3,
TIME_PERIOD = 4,
NON_BLANK = 5,
PORT = 6
PORT = 6,
NUMBER = 7
};

const core::PropertyValidator& translateCodeToPropertyValidator(const PropertyValidatorCode& code) {
Expand All @@ -156,6 +157,8 @@ const core::PropertyValidator& translateCodeToPropertyValidator(const PropertyVa
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");
}
Expand Down
6 changes: 6 additions & 0 deletions extensions/python/pythonprocessors/nifiapi/properties.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,9 @@ def createRegexValidator(self, *args) -> int:
def createLongValidator(self, *args) -> int:
return StandardValidators.LONG_VALIDATOR

def createNumberValidator(self, *args) -> int:
return StandardValidators.NUMBER_VALIDATOR


class StandardValidators:
_standard_validators = ValidatorGenerator()
Expand Down Expand Up @@ -87,6 +90,7 @@ class MinifiPropertyTypes:
TIME_PERIOD_TYPE = 4
NON_BLANK_TYPE = 5
PORT_TYPE = 6
NUMBER_TYPE = 7


def translateStandardValidatorToMiNiFiPropertype(validators: List[int]) -> int:
Expand All @@ -108,6 +112,8 @@ def translateStandardValidatorToMiNiFiPropertype(validators: List[int]) -> int:
return MinifiPropertyTypes.NON_BLANK_TYPE
if validator == StandardValidators.PORT_VALIDATOR:
return MinifiPropertyTypes.PORT_TYPE
if validator == StandardValidators.NUMBER_VALIDATOR:
return MinifiPropertyTypes.NUMBER_TYPE
Comment thread
fgerlits marked this conversation as resolved.
return None


Expand Down
1 change: 1 addition & 0 deletions libminifi/src/minifi-api.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ gsl::not_null<const minifi::core::PropertyValidator*> toPropertyValidator(minifi
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();
Comment on lines 92 to 93

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if we crash on unknown enum values, then adding another validator later can't be done without breaking API

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah i hoped we can merge this in faster than the release 🤞

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we could just fallback on ALWAYS_VALID, for c api extensions, the parsing happens on the extension side anyway

}
Expand Down
12 changes: 12 additions & 0 deletions libminifi/test/unit/PropertyValidationTests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -224,4 +224,16 @@ TEST_CASE("TimePeriodValue Property") {
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
80 changes: 45 additions & 35 deletions minifi-api/common/include/minifi-cpp/core/PropertyValidator.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,38 +25,41 @@ namespace org::apache::nifi::minifi::core {
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();
}
};

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();
Expand All @@ -65,10 +68,9 @@ class TimePeriodValidator final : public PropertyValidator {

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();
Expand All @@ -77,10 +79,9 @@ class BooleanValidator final : public PropertyValidator {

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();
Expand All @@ -89,10 +90,9 @@ class IntegerValidator final : public PropertyValidator {

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();
Expand All @@ -101,10 +101,9 @@ class UnsignedIntegerValidator final : public PropertyValidator {

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();
Expand All @@ -113,16 +112,26 @@ class DataSizeValidator final : public PropertyValidator {

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{};
Expand All @@ -132,6 +141,7 @@ inline constexpr auto INTEGER_VALIDATOR = IntegerValidator{};
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
1 change: 1 addition & 0 deletions minifi-api/include/minifi-api.h
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,7 @@ enum minifi_validator : uint32_t {
MINIFI_VALIDATOR_UNSIGNED_INTEGER = 5,
MINIFI_VALIDATOR_DATA_SIZE = 6,
MINIFI_VALIDATOR_PORT = 7,
MINIFI_VALIDATOR_NUMBER = 8,
};

struct minifi_property_definition {
Expand Down
Loading