| /** |
| * 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 <memory> |
| #include <string> |
| |
| #include "core/ProcessContextImpl.h" |
| #include "core/Resource.h" |
| #include "core/ProcessorImpl.h" |
| #include "minifi-cpp/core/PropertyDefinition.h" |
| #include "core/PropertyDefinitionBuilder.h" |
| #include "minifi-cpp/core/RelationshipDefinition.h" |
| #include "unit/TestBase.h" |
| #include "unit/Catch.h" |
| |
| namespace org::apache::nifi::minifi { |
| |
| class DummyProcessContextExprProcessor : public core::ProcessorImpl { |
| public: |
| using core::ProcessorImpl::ProcessorImpl; |
| |
| static constexpr const char* Description = "A processor that does nothing."; |
| static constexpr auto SimpleProperty = core::PropertyDefinitionBuilder<>::createProperty("Simple Property") |
| .withDescription("Just a simple string property") |
| .build(); |
| static constexpr auto ExpressionLanguageProperty = core::PropertyDefinitionBuilder<>::createProperty("Expression Language Property") |
| .withDescription("A property which supports expression language") |
| .supportsExpressionLanguage(true) |
| .build(); |
| static constexpr auto Properties = std::to_array<core::PropertyReference>({SimpleProperty, ExpressionLanguageProperty}); |
| static constexpr auto Relationships = std::array<core::RelationshipDefinition, 0>{}; |
| static constexpr bool SupportsDynamicProperties = true; |
| static constexpr bool SupportsDynamicRelationships = true; |
| static constexpr core::annotation::Input InputRequirement = core::annotation::Input::INPUT_ALLOWED; |
| static constexpr bool IsSingleThreaded = false; |
| ADD_COMMON_VIRTUAL_FUNCTIONS_FOR_PROCESSORS |
| |
| void initialize() override { setSupportedProperties(Properties); } |
| }; |
| |
| REGISTER_RESOURCE(DummyProcessContextExprProcessor, Processor); |
| |
| } // namespace org::apache::nifi::minifi |
| |
| TEST_CASE("ProcessContextExpr can update existing processor properties", "[setProperty][getProperty]") { |
| TestController test_controller; |
| std::shared_ptr<TestPlan> test_plan = test_controller.createPlan(); |
| [[maybe_unused]] minifi::core::Processor* dummy_processor = test_plan->addProcessor("DummyProcessContextExprProcessor", "dummy_processor"); |
| std::shared_ptr<minifi::core::ProcessContext> context = [test_plan] { test_plan->runNextProcessor(); return test_plan->getCurrentContext(); }(); |
| REQUIRE(dynamic_pointer_cast<minifi::core::ProcessContextImpl>(context) != nullptr); |
| |
| SECTION("Set and get simple property") { |
| SECTION("Using a Property reference parameter") { |
| REQUIRE(context->setProperty(minifi::DummyProcessContextExprProcessor::SimpleProperty, "foo")); |
| CHECK(context->getProperty(minifi::DummyProcessContextExprProcessor::SimpleProperty, nullptr) == "foo"); |
| |
| REQUIRE(context->setProperty(minifi::DummyProcessContextExprProcessor::SimpleProperty, "bar")); |
| CHECK(context->getProperty(minifi::DummyProcessContextExprProcessor::SimpleProperty, nullptr) == "bar"); |
| } |
| |
| SECTION("Using a string parameter") { |
| REQUIRE(context->setProperty(minifi::DummyProcessContextExprProcessor::SimpleProperty, "foo")); |
| CHECK(context->getProperty(minifi::DummyProcessContextExprProcessor::SimpleProperty, nullptr) == "foo"); |
| |
| REQUIRE(context->setProperty(minifi::DummyProcessContextExprProcessor::SimpleProperty, "bar")); |
| CHECK(context->getProperty(minifi::DummyProcessContextExprProcessor::SimpleProperty, nullptr) == "bar"); |
| } |
| } |
| |
| SECTION("Set and get expression language property") { |
| SECTION("Using a Property reference parameter") { |
| REQUIRE(context->setProperty(minifi::DummyProcessContextExprProcessor::ExpressionLanguageProperty, "foo")); |
| CHECK(context->getProperty(minifi::DummyProcessContextExprProcessor::ExpressionLanguageProperty, nullptr) == "foo"); |
| |
| REQUIRE(context->setProperty(minifi::DummyProcessContextExprProcessor::ExpressionLanguageProperty, "bar")); |
| CHECK(context->getProperty(minifi::DummyProcessContextExprProcessor::ExpressionLanguageProperty, nullptr) == "bar"); |
| } |
| |
| SECTION("Using a string parameter") { |
| REQUIRE(context->setProperty(minifi::DummyProcessContextExprProcessor::ExpressionLanguageProperty, "foo")); |
| CHECK(context->getProperty(minifi::DummyProcessContextExprProcessor::ExpressionLanguageProperty, nullptr) == "foo"); |
| |
| REQUIRE(context->setProperty(minifi::DummyProcessContextExprProcessor::ExpressionLanguageProperty, "bar")); |
| CHECK(context->getProperty(minifi::DummyProcessContextExprProcessor::ExpressionLanguageProperty, nullptr) == "bar"); |
| } |
| } |
| } |
| |
| TEST_CASE("Expression language via variable registry") { |
| const std::shared_ptr<minifi::Configure> configuration = std::make_shared<minifi::ConfigureImpl>(); |
| configuration->set("foo", "foo_val"); |
| configuration->set("bar", "bar_val"); |
| configuration->set("minifi.variable.registry.blacklist", "foo"); |
| |
| TestController test_controller; |
| std::shared_ptr<TestPlan> test_plan = test_controller.createPlan(configuration); |
| |
| [[maybe_unused]] minifi::core::Processor* dummy_processor = test_plan->addProcessor("DummyProcessContextExprProcessor", "dummy_processor"); |
| std::shared_ptr<minifi::core::ProcessContext> context = [test_plan] { test_plan->runNextProcessor(); return test_plan->getCurrentContext(); }(); |
| REQUIRE(dynamic_pointer_cast<minifi::core::ProcessContextImpl>(context) != nullptr); |
| |
| { |
| REQUIRE(context->setProperty(minifi::DummyProcessContextExprProcessor::ExpressionLanguageProperty, "${bar}")); |
| CHECK(context->getProperty(minifi::DummyProcessContextExprProcessor::ExpressionLanguageProperty, nullptr) == "bar_val"); |
| } |
| { |
| REQUIRE(context->setProperty(minifi::DummyProcessContextExprProcessor::ExpressionLanguageProperty, "${foo}")); |
| CHECK(context->getProperty(minifi::DummyProcessContextExprProcessor::ExpressionLanguageProperty, nullptr) == ""); |
| } |
| { |
| REQUIRE(context->setProperty(minifi::DummyProcessContextExprProcessor::SimpleProperty, "${bar}")); |
| CHECK(context->getProperty(minifi::DummyProcessContextExprProcessor::SimpleProperty, nullptr) == "${bar}"); |
| } |
| { |
| REQUIRE(context->setProperty(minifi::DummyProcessContextExprProcessor::SimpleProperty, "${foo}")); |
| CHECK(context->getProperty(minifi::DummyProcessContextExprProcessor::SimpleProperty, nullptr) == "${foo}"); |
| } |
| } |
| |
| TEST_CASE("ProcessContextExpr can use expression language in dynamic properties", "[getDynamicProperty][getDynamicProperties]") { |
| TestController test_controller; |
| std::shared_ptr<TestPlan> test_plan = test_controller.createPlan(); |
| std::ignore = test_plan->addProcessor("DummyProcessor", "dummy_processor"); |
| test_plan->runNextProcessor(); |
| const auto context = test_plan->getCurrentContext(); |
| REQUIRE(dynamic_pointer_cast<core::ProcessContextImpl>(context) != nullptr); |
| |
| core::FlowFileImpl flow_file; |
| flow_file.setAttribute("attr_a", "myAttributeValue"); |
| REQUIRE(context->setDynamicProperty("MyDynamicProperty", "${attr_a}")); |
| |
| SECTION("Use getDynamicProperties") { |
| for (size_t i = 0; i < 2; ++i) { |
| auto properties = context->getDynamicProperties(&flow_file); |
| REQUIRE(properties.size() == 1); |
| CHECK(properties["MyDynamicProperty"] == "myAttributeValue"); |
| } |
| } |
| |
| SECTION("Use getDynamicProperty") { |
| for (size_t i = 0; i < 2; ++i) { |
| auto property = context->getDynamicProperty("MyDynamicProperty", &flow_file); |
| REQUIRE(property); |
| CHECK(*property == "myAttributeValue"); |
| } |
| } |
| } |
| |
| TEST_CASE("ProcessContextExpr is mutex guarded properly") { |
| TestController test_controller; |
| const std::shared_ptr<TestPlan> test_plan = test_controller.createPlan(); |
| std::ignore = test_plan->addProcessor("DummyProcessor", "dummy_processor"); |
| test_plan->runNextProcessor(); |
| const auto context = test_plan->getCurrentContext(); |
| REQUIRE(dynamic_pointer_cast<core::ProcessContextImpl>(context) != nullptr); |
| |
| auto play_with_context = [=]() { |
| for (auto i = 0; i < 100; ++i) { |
| CHECK(context->setDynamicProperty("foo", fmt::format("${{literal('{}')}}", std::this_thread::get_id()))); |
| const auto dynamic_properties = context->getDynamicProperties(); |
| std::this_thread::sleep_for(std::chrono::milliseconds(10)); |
| } |
| }; |
| |
| std::thread thread_one{play_with_context}; |
| std::thread thread_two{play_with_context}; |
| std::thread thread_three{play_with_context}; |
| |
| REQUIRE_NOTHROW(thread_one.join()); |
| REQUIRE_NOTHROW(thread_two.join()); |
| REQUIRE_NOTHROW(thread_three.join()); |
| } |