This is an automated email from the ASF dual-hosted git repository.

martinzink pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/nifi-minifi-cpp.git


The following commit(s) were added to refs/heads/main by this push:
     new 8be29c104 MINIFICPP-2885 Allow explictily unsetting properties with 
default value (#2241)
8be29c104 is described below

commit 8be29c10489631bba464fc8c09f012659b08403a
Author: Martin Zink <[email protected]>
AuthorDate: Tue Aug 25 14:10:20 2026 +0200

    MINIFICPP-2885 Allow explictily unsetting properties with default value 
(#2241)
---
 .../ubuntu_22_04_clang_arm_manifest.json           |  45 +++++
 .../include/core/ConfigurableComponentImpl.h       |   1 +
 .../src/core/ConfigurableComponentImpl.cpp         |  10 +
 .../stable-api-testing/ExtensionInitializer.cpp    |   2 +
 extensions/stable-api-testing/PropertyTester.h     |  97 +++++++++
 .../tests/PropertyTesterTests.cpp                  | 221 +++++++++++++++++++++
 .../src/core/flow/StructuredConfiguration.cpp      |  21 +-
 .../resources/TestC2DescribeCoreComponentState.yml |   4 +
 .../minifi-cpp/core/ConfigurableComponent.h        |   1 +
 minifi-api/include/minifi-cpp/core/Property.h      |   1 +
 10 files changed, 400 insertions(+), 3 deletions(-)

diff --git a/.github/references/ubuntu_22_04_clang_arm_manifest.json 
b/.github/references/ubuntu_22_04_clang_arm_manifest.json
index 87d17440d..2d71ae28c 100644
--- a/.github/references/ubuntu_22_04_clang_arm_manifest.json
+++ b/.github/references/ubuntu_22_04_clang_arm_manifest.json
@@ -8415,6 +8415,51 @@
     "bundles": {
         "componentManifest": {
             "processors": [
+                {
+                    "propertyDescriptors": {
+                        "OptionalPropertyWithDefaultValue": {
+                            "name": "OptionalPropertyWithDefaultValue",
+                            "description": "Test 
OptionalPropertyWithDefaultValue",
+                            "validator": "VALID",
+                            "required": "false",
+                            "sensitive": "false",
+                            "expressionLanguageScope": "NONE",
+                            "defaultValue": "default_val"
+                        },
+                        "OptionalPropertyWithoutDefaultValue": {
+                            "name": "OptionalPropertyWithoutDefaultValue",
+                            "description": "Test 
OptionalPropertyWithoutDefaultValue",
+                            "validator": "VALID",
+                            "required": "false",
+                            "sensitive": "false",
+                            "expressionLanguageScope": "NONE"
+                        },
+                        "RequiredPropertyWithDefaultValue": {
+                            "name": "RequiredPropertyWithDefaultValue",
+                            "description": "Test 
RequiredPropertyWithDefaultValue",
+                            "validator": "VALID",
+                            "required": "true",
+                            "sensitive": "false",
+                            "expressionLanguageScope": "NONE",
+                            "defaultValue": "default_val"
+                        },
+                        "RequiredPropertyWithoutDefaultValue": {
+                            "name": "RequiredPropertyWithoutDefaultValue",
+                            "description": "Test 
RequiredPropertyWithoutDefaultValue",
+                            "validator": "VALID",
+                            "required": "true",
+                            "sensitive": "false",
+                            "expressionLanguageScope": "NONE"
+                        }
+                    },
+                    "inputRequirement": "INPUT_FORBIDDEN",
+                    "isSingleThreaded": "true",
+                    "supportedRelationships": [],
+                    "typeDescription": "Test processor to test the parsing of 
properties in the flow configuration",
+                    "supportsDynamicRelationships": "false",
+                    "supportsDynamicProperties": "false",
+                    "type": "org.apache.nifi.minifi.api_testing.PropertyTester"
+                },
                 {
                     "propertyDescriptors": {
                         "Can fly service": {
diff --git a/core-framework/include/core/ConfigurableComponentImpl.h 
b/core-framework/include/core/ConfigurableComponentImpl.h
index 90725d271..577c125b4 100644
--- a/core-framework/include/core/ConfigurableComponentImpl.h
+++ b/core-framework/include/core/ConfigurableComponentImpl.h
@@ -33,6 +33,7 @@ class ConfigurableComponentImpl : public virtual 
ConfigurableComponent {
   [[nodiscard]] std::expected<std::string, std::error_code> 
getProperty(std::string_view name) const override;
   std::expected<void, std::error_code> setProperty(std::string_view name, 
std::string value) override;
   std::expected<void, std::error_code> clearProperty(std::string_view name) 
override;
+  std::expected<void, std::error_code> 
clearPropertyDefaultValue(std::string_view name) override;
 
   [[nodiscard]] std::expected<std::string, std::error_code> 
getDynamicProperty(std::string_view name) const override;
   std::expected<void, std::error_code> setDynamicProperty(std::string name, 
std::string value) override;
diff --git a/core-framework/src/core/ConfigurableComponentImpl.cpp 
b/core-framework/src/core/ConfigurableComponentImpl.cpp
index 1dd09d1e1..924af40ff 100644
--- a/core-framework/src/core/ConfigurableComponentImpl.cpp
+++ b/core-framework/src/core/ConfigurableComponentImpl.cpp
@@ -67,6 +67,16 @@ std::expected<void, std::error_code> 
ConfigurableComponentImpl::clearProperty(co
   return {};
 }
 
+std::expected<void, std::error_code> 
ConfigurableComponentImpl::clearPropertyDefaultValue(const std::string_view 
name) {
+  const std::lock_guard<std::mutex> lock(configuration_mutex_);
+  const auto it = supported_properties_.find(name);
+  if (it == supported_properties_.end()) { return 
std::unexpected{PropertyErrorCode::NotSupportedProperty}; }
+  Property& prop = it->second;
+
+  prop.clearDefaultValue();
+  return {};
+}
+
 std::expected<void, std::error_code> 
ConfigurableComponentImpl::appendProperty(const std::string_view name, 
std::string value) {
   const std::lock_guard<std::mutex> lock(configuration_mutex_);
   const auto it = supported_properties_.find(name);
diff --git a/extensions/stable-api-testing/ExtensionInitializer.cpp 
b/extensions/stable-api-testing/ExtensionInitializer.cpp
index 9150b6e20..d2c58764d 100644
--- a/extensions/stable-api-testing/ExtensionInitializer.cpp
+++ b/extensions/stable-api-testing/ExtensionInitializer.cpp
@@ -16,6 +16,7 @@
  */
 
 #include "AnimalControllerServices.h"
+#include "PropertyTester.h"
 #include "ZooProcessor.h"
 #include "api/core/Resource.h"
 #include "api/utils/minifi-c-utils.h"
@@ -35,5 +36,6 @@ CEXTENSIONAPI void 
minifi_init_extension(minifi_extension_context* extension_con
       .user_data = nullptr};
   auto* extension = minifi_register_extension(extension_context, 
&extension_definition);
   
minifi::api::core::registerProcessors<minifi::api_testing::ZooProcessor>(extension);
+  
minifi::api::core::registerProcessors<minifi::api_testing::PropertyTester>(extension);
   
minifi::api::core::registerControllerServices<minifi::api_testing::DogController,
 minifi::api_testing::DuckController>(extension);
 }
diff --git a/extensions/stable-api-testing/PropertyTester.h 
b/extensions/stable-api-testing/PropertyTester.h
new file mode 100644
index 000000000..93f421815
--- /dev/null
+++ b/extensions/stable-api-testing/PropertyTester.h
@@ -0,0 +1,97 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include "api/core/ProcessorImpl.h"
+#include "api/utils/Export.h"
+#include "api/utils/ProcessorConfigUtils.h"
+#include "core/PropertyDefinitionBuilder.h"
+#include "minifi-api.h"
+#include "minifi-cpp/core/Annotation.h"
+
+namespace org::apache::nifi::minifi::api_testing {
+
+class PropertyTester : public api::core::ProcessorImpl {
+ public:
+  EXTENSIONAPI static constexpr const char* Description = "Test processor to 
test the parsing of properties in the flow configuration";
+
+  EXTENSIONAPI static constexpr auto OptionalPropertyWithDefaultValue =
+      
core::PropertyDefinitionBuilder<>::createProperty("OptionalPropertyWithDefaultValue")
+          .withDescription("Test OptionalPropertyWithDefaultValue")
+          .withDefaultValue("default_val")
+          .isRequired(false)
+          .build();
+
+  EXTENSIONAPI static constexpr auto OptionalPropertyWithoutDefaultValue =
+      
core::PropertyDefinitionBuilder<>::createProperty("OptionalPropertyWithoutDefaultValue")
+          .withDescription("Test OptionalPropertyWithoutDefaultValue")
+          .isRequired(false)
+          .build();
+
+  EXTENSIONAPI static constexpr auto RequiredPropertyWithDefaultValue =
+      
core::PropertyDefinitionBuilder<>::createProperty("RequiredPropertyWithDefaultValue")
+          .withDescription("Test RequiredPropertyWithDefaultValue")
+          .withDefaultValue("default_val")
+          .isRequired(true)
+          .build();
+
+  EXTENSIONAPI static constexpr auto RequiredPropertyWithoutDefaultValue =
+      
core::PropertyDefinitionBuilder<>::createProperty("RequiredPropertyWithoutDefaultValue")
+          .withDescription("Test RequiredPropertyWithoutDefaultValue")
+          .isRequired(true)
+          .build();
+
+  EXTENSIONAPI static constexpr auto Properties = 
std::to_array<core::PropertyReference>(
+      {OptionalPropertyWithDefaultValue, OptionalPropertyWithoutDefaultValue, 
RequiredPropertyWithDefaultValue, RequiredPropertyWithoutDefaultValue});
+  EXTENSIONAPI static constexpr auto Relationships = 
std::array<core::RelationshipDefinition, 0>{};
+  EXTENSIONAPI static constexpr bool SupportsDynamicProperties = false;
+  EXTENSIONAPI static constexpr bool SupportsDynamicRelationships = false;
+  EXTENSIONAPI static constexpr core::annotation::Input InputRequirement = 
core::annotation::Input::INPUT_FORBIDDEN;
+  EXTENSIONAPI static constexpr bool IsSingleThreaded = true;
+
+  using ProcessorImpl::ProcessorImpl;
+
+ protected:
+  minifi_status onTriggerImpl(api::core::ProcessContext& process_context, 
api::core::ProcessSession&) override {
+    {
+      const std::optional<std::string> optional_value_with_default = 
api::utils::parseOptionalProperty(process_context,
+          OptionalPropertyWithDefaultValue);
+      logger_->log_critical("OptionalPropertyWithDefaultValue: {}", 
optional_value_with_default);
+    }
+    {
+      const std::optional<std::string> optional_value_without_default = 
api::utils::parseOptionalProperty(process_context,
+          OptionalPropertyWithoutDefaultValue);
+      logger_->log_critical("OptionalPropertyWithoutDefaultValue: {}", 
optional_value_without_default);
+    }
+    {
+      const std::string required_value_with_default = 
api::utils::parseProperty(process_context, RequiredPropertyWithDefaultValue);
+      logger_->log_critical("RequiredPropertyWithDefaultValue: {}", 
required_value_with_default);
+    }
+    {
+      const std::string required_value_without_default = 
api::utils::parseProperty(process_context, RequiredPropertyWithoutDefaultValue);
+      logger_->log_critical("RequiredPropertyWithoutDefaultValue: {}", 
required_value_without_default);
+    }
+
+    return MINIFI_STATUS_SUCCESS;
+  }
+  minifi_status onScheduleImpl(api::core::ProcessContext&) override {
+    return MINIFI_STATUS_SUCCESS;
+  }
+};
+
+}  // namespace org::apache::nifi::minifi::api_testing
diff --git a/extensions/stable-api-testing/tests/PropertyTesterTests.cpp 
b/extensions/stable-api-testing/tests/PropertyTesterTests.cpp
new file mode 100644
index 000000000..3aacdb2ef
--- /dev/null
+++ b/extensions/stable-api-testing/tests/PropertyTesterTests.cpp
@@ -0,0 +1,221 @@
+/**
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "core/flow/AdaptiveConfiguration.h"
+#include "unit/Catch.h"
+#include "unit/ConfigurationTestController.h"
+#include "unit/TestBase.h"
+#include "unit/TestUtils.h"
+
+using namespace std::literals::chrono_literals;
+
+TEST_CASE("Required property without Properties Entry (adaptive yaml)") {
+  const ConfigurationTestController controller;
+  core::flow::AdaptiveConfiguration config{controller.getContext()};
+  const auto config_yaml = R"(
+MiNiFi Config Version: 3
+Flow Controller:
+  name: MiNiFi Flow
+Processors:
+  - name: My processor
+    id: 00000000-0000-0000-0000-000000000001
+    class: PropertyTester
+Connections: [ ]
+Remote Process Groups: [ ]
+)";
+  REQUIRE_THROWS(config.getRootFromPayload(config_yaml));
+  REQUIRE(minifi::test::utils::verifyLogLinePresenceInPollTime(100ms,
+      "[error] Error while processing configuration file: Unable to parse 
configuration file for component named 'My processor' because required "
+      "property 'RequiredPropertyWithoutDefaultValue' is not set"));
+}
+
+TEST_CASE("Required property without Properties Entry (adaptive json)") {
+  const ConfigurationTestController controller;
+  core::flow::AdaptiveConfiguration config{controller.getContext()};
+  const auto config_json = R"(
+{
+  "Flow Controller": {"name": "root"},
+  "Processors": [
+    {
+      "id": "00000000-0000-0000-0000-000000000001",
+      "class": "PropertyTester",
+      "name": "My processor"
+    }
+  ],
+  "Connections": []
+}
+  )";
+  REQUIRE_THROWS(config.getRootFromPayload(config_json));
+  REQUIRE(minifi::test::utils::verifyLogLinePresenceInPollTime(100ms,
+      "[error] Error while processing configuration file: Unable to parse 
configuration file for component named 'My processor' because required "
+      "property 'RequiredPropertyWithoutDefaultValue' is not set"));
+}
+
+TEST_CASE("Explicitly unsetting required property (adaptive yaml)") {
+  const ConfigurationTestController controller;
+  core::flow::AdaptiveConfiguration config{controller.getContext()};
+  const auto config_yaml = R"(
+MiNiFi Config Version: 3
+Flow Controller:
+  name: MiNiFi Flow
+Processors:
+  - name: My processor
+    id: 00000000-0000-0000-0000-000000000001
+    class: PropertyTester
+    Properties:
+      RequiredPropertyWithDefaultValue: ~
+Connections: [ ]
+Remote Process Groups: [ ]
+)";
+  REQUIRE_THROWS(config.getRootFromPayload(config_yaml));
+  REQUIRE(minifi::test::utils::verifyLogLinePresenceInPollTime(100ms,
+      "[error] Error while processing configuration file: Unable to parse 
configuration file for component named 'My processor' because Can't "
+      "explicitly unset required property"));
+}
+
+TEST_CASE("Explicitly unsetting required property (adaptive json)") {
+  const ConfigurationTestController controller;
+  core::flow::AdaptiveConfiguration config{controller.getContext()};
+  const auto config_json = R"(
+{
+  "Flow Controller": {"name": "root"},
+  "Processors": [
+    {
+      "id": "00000000-0000-0000-0000-000000000001",
+      "class": "PropertyTester",
+      "name": "My processor",
+      "Properties": {
+          "RequiredPropertyWithDefaultValue": null,
+      }
+    }
+  ],
+  "Connections": []
+})";
+  REQUIRE_THROWS(config.getRootFromPayload(config_json));
+  REQUIRE(minifi::test::utils::verifyLogLinePresenceInPollTime(100ms,
+      "[error] Error while processing configuration file: Unable to parse 
configuration file for component named 'My processor' because Can't "
+      "explicitly unset required property"));
+}
+
+TEST_CASE("Omitting optional property (adaptive yaml)") {
+  const ConfigurationTestController controller;
+  core::flow::AdaptiveConfiguration config{controller.getContext()};
+  const auto config_yaml = R"(
+MiNiFi Config Version: 3
+Flow Controller:
+  name: MiNiFi Flow
+Processors:
+  - name: My processor
+    id: 00000000-0000-0000-0000-000000000001
+    class: PropertyTester
+    Properties:
+      RequiredPropertyWithoutDefaultValue: 'bar'
+Connections: [ ]
+Remote Process Groups: [ ]
+)";
+  const auto root = config.getRootFromPayload(config_yaml);
+  REQUIRE(root);
+  const auto my_proc = root->findProcessorByName("My processor");
+  REQUIRE(my_proc);
+  const auto opt_val = 
my_proc->getProperty("OptionalPropertyWithDefaultValue");
+  CHECK(opt_val == "default_val");
+}
+
+TEST_CASE("Omitting optional property (adaptive json)") {
+  const ConfigurationTestController controller;
+  core::flow::AdaptiveConfiguration config{controller.getContext()};
+  const auto config_json = R"(
+{
+  "Flow Controller": {"name": "root"},
+  "Processors": [
+    {
+      "id": "00000000-0000-0000-0000-000000000001",
+      "class": "PropertyTester",
+      "name": "My processor",
+      "Properties": {
+          "RequiredPropertyWithoutDefaultValue": "foo"
+      }
+    }
+  ],
+  "Connections": []
+})";
+  const auto root = config.getRootFromPayload(config_json);
+  REQUIRE(root);
+  const auto my_proc = root->findProcessorByName("My processor");
+  REQUIRE(my_proc);
+  const auto opt_val = 
my_proc->getProperty("OptionalPropertyWithDefaultValue");
+  CHECK(opt_val == "default_val");
+}
+
+TEST_CASE("Explicitly unsetting optional property (adaptive yaml)") {
+  const ConfigurationTestController controller;
+  core::flow::AdaptiveConfiguration config{controller.getContext()};
+  const auto config_yaml = R"(
+MiNiFi Config Version: 3
+Flow Controller:
+  name: MiNiFi Flow
+Processors:
+  - name: My processor
+    id: 00000000-0000-0000-0000-000000000001
+    class: PropertyTester
+    Properties:
+      OptionalPropertyWithDefaultValue: ~
+      RequiredPropertyWithoutDefaultValue: 'foo'
+Connections: [ ]
+Remote Process Groups: [ ]
+)";
+  const auto root = config.getRootFromPayload(config_yaml);
+  REQUIRE(root);
+  const auto my_proc = root->findProcessorByName("My processor");
+  REQUIRE(my_proc);
+  const auto opt_val = 
my_proc->getProperty("OptionalPropertyWithDefaultValue");
+  CHECK(!opt_val);
+  CHECK(!my_proc->getProperty("OptionalPropertyWithoutDefaultValue"));
+  CHECK(my_proc->getProperty("RequiredPropertyWithDefaultValue") == 
"default_val");
+  CHECK(my_proc->getProperty("RequiredPropertyWithoutDefaultValue") == "foo");
+}
+
+TEST_CASE("Explicitly unsetting optional property (adaptive json)") {
+  const ConfigurationTestController controller;
+  core::flow::AdaptiveConfiguration config{controller.getContext()};
+  const auto config_json = R"(
+{
+  "Flow Controller": {"name": "root"},
+  "Processors": [
+    {
+      "id": "00000000-0000-0000-0000-000000000001",
+      "class": "PropertyTester",
+      "name": "My processor",
+      "Properties": {
+          "OptionalPropertyWithDefaultValue": null,
+          "RequiredPropertyWithoutDefaultValue": "foo"
+      }
+    }
+  ],
+  "Connections": []
+})";
+  const auto root = config.getRootFromPayload(config_json);
+  REQUIRE(root);
+  const auto my_proc = root->findProcessorByName("My processor");
+  REQUIRE(my_proc);
+  const auto opt_val = 
my_proc->getProperty("OptionalPropertyWithDefaultValue");
+  CHECK(!opt_val);
+  CHECK(!my_proc->getProperty("OptionalPropertyWithoutDefaultValue"));
+  CHECK(my_proc->getProperty("RequiredPropertyWithDefaultValue") == 
"default_val");
+  CHECK(my_proc->getProperty("RequiredPropertyWithoutDefaultValue") == "foo");
+}
diff --git a/libminifi/src/core/flow/StructuredConfiguration.cpp 
b/libminifi/src/core/flow/StructuredConfiguration.cpp
index e94eb69b9..47324cb62 100644
--- a/libminifi/src/core/flow/StructuredConfiguration.cpp
+++ b/libminifi/src/core/flow/StructuredConfiguration.cpp
@@ -19,7 +19,6 @@
 #include "core/flow/StructuredConfiguration.h"
 
 #include <memory>
-#include <set>
 #include <vector>
 
 #include "Funnel.h"
@@ -29,8 +28,6 @@
 #include "core/ReferenceParser.h"
 #include "core/flow/CheckRequiredField.h"
 #include "core/flow/StructuredConnectionParser.h"
-#include "core/state/Value.h"
-#include "utils/RegexUtils.h"
 #include "utils/TimeUtil.h"
 #include "utils/crypto/property_encryption/PropertyEncryptionUtils.h"
 #include "utils/PropertyErrors.h"
@@ -244,6 +241,8 @@ void 
StructuredConfiguration::parseParameterProvidersNode(const Node& parameter_
       logger_->log_debug("Created Parameter Provider with UUID {} and name 
{}", id, name);
       if (Node propertiesNode = 
parameter_provider_node[schema_.parameter_provider_properties]) {
         parsePropertiesNode(propertiesNode, *parameter_provider, name, 
nullptr);
+      } else {
+        validateComponentProperties(*parameter_provider, name, "");
       }
     } else {
       logger_->log_debug("Could not locate {}", type);
@@ -384,6 +383,8 @@ void StructuredConfiguration::parseProcessorNode(const 
Node& processors_node, co
     // handle processor properties
     if (Node propertiesNode = procNode[schema_.processor_properties]) {
       parsePropertiesNode(propertiesNode, *processor, procCfg.name, 
parentGroup->getParameterContext());
+    } else {
+      validateComponentProperties(*processor, procCfg.name, "");
     }
 
     // Take care of scheduling
@@ -666,6 +667,8 @@ void StructuredConfiguration::parseControllerServices(const 
Node& controller_ser
         if (auto controllerServiceImpl = 
controller_service_node->getControllerServiceImplementation(); 
controllerServiceImpl) {
           parsePropertiesNode(propertiesNode, *controllerServiceImpl, name, 
parent_group->getParameterContext());
         }
+      } else {
+        validateComponentProperties(*controller_service_node, name, "");
       }
 
       parent_group->addControllerService(controller_service_node->getName(), 
controller_service_node, controller_service_node->getUUIDStr());
@@ -936,6 +939,18 @@ void 
StructuredConfiguration::parsePropertyNodeElement(const std::string& proper
     ParameterContext* parameter_context) {
   logger_->log_trace("Encountered {}", property_name);
   if (!property_value_node || property_value_node.isNull()) {
+    auto my_prop = component.getSupportedProperty(property_name);
+    if (!my_prop.has_value()) {
+      // Dynamic property fallback for previous workflow
+      return;
+    }
+    if (my_prop->getRequired()) {
+      raiseComponentError(component.getName(), "", fmt::format("Can't 
explicitly unset required property: '{}'", property_name));
+    }
+    const auto prop_def_cleared = 
component.clearPropertyDefaultValue(property_name);
+    if (!prop_def_cleared) {
+      raiseComponentError(component.getName(), "", 
prop_def_cleared.error().message());
+    }
     return;
   }
   if (property_value_node.isSequence()) {
diff --git a/libminifi/test/resources/TestC2DescribeCoreComponentState.yml 
b/libminifi/test/resources/TestC2DescribeCoreComponentState.yml
index 03cf1a69c..8252ff947 100644
--- a/libminifi/test/resources/TestC2DescribeCoreComponentState.yml
+++ b/libminifi/test/resources/TestC2DescribeCoreComponentState.yml
@@ -29,6 +29,8 @@ Processors:
     penalization period: 30 sec
     yield period: 1 sec
     run duration nanos: 0
+    Properties:
+      File to Tail: test.log
     auto-terminated relationships list:
       - success
   - name: TailFile2
@@ -40,6 +42,8 @@ Processors:
     penalization period: 30 sec
     yield period: 1 sec
     run duration nanos: 0
+    Properties:
+      File to Tail: test.log
     auto-terminated relationships list:
       - success
 
diff --git a/minifi-api/include/minifi-cpp/core/ConfigurableComponent.h 
b/minifi-api/include/minifi-cpp/core/ConfigurableComponent.h
index 377903754..733294c8b 100644
--- a/minifi-api/include/minifi-cpp/core/ConfigurableComponent.h
+++ b/minifi-api/include/minifi-cpp/core/ConfigurableComponent.h
@@ -39,6 +39,7 @@ class ConfigurableComponent : public virtual CoreComponent {
   virtual std::expected<void, std::error_code> setProperty(std::string_view 
name, std::string value) = 0;
   virtual std::expected<void, std::error_code> appendProperty(std::string_view 
name, std::string value) = 0;
   virtual std::expected<void, std::error_code> clearProperty(std::string_view 
name) = 0;
+  virtual std::expected<void, std::error_code> 
clearPropertyDefaultValue(std::string_view name) = 0;
 
   [[nodiscard]] virtual std::expected<std::string, std::error_code> 
getDynamicProperty(std::string_view name) const = 0;
   virtual std::expected<void, std::error_code> setDynamicProperty(std::string 
name, std::string value) = 0;
diff --git a/minifi-api/include/minifi-cpp/core/Property.h 
b/minifi-api/include/minifi-cpp/core/Property.h
index d802b7221..8c7c4d686 100644
--- a/minifi-api/include/minifi-cpp/core/Property.h
+++ b/minifi-api/include/minifi-cpp/core/Property.h
@@ -56,6 +56,7 @@ class Property final {
   std::vector<std::string> getAllowedValues() const { return allowed_values_; }
   void setAllowedValues(std::vector<std::string> allowed_values) { 
allowed_values_ = std::move(allowed_values); }
   std::optional<std::string> getDefaultValue() const { return default_value_; }
+  void clearDefaultValue() { default_value_ = std::nullopt; }
   std::string getName() const;
   std::string getDisplayName() const;
   std::vector<std::string> getAllowedTypes() const;

Reply via email to