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

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

commit e1250658ee3f09496372f458f694ff3eb46e5a26
Author: Gabor Gyimesi <gamezb...@gmail.com>
AuthorDate: Mon May 6 20:38:12 2024 +0200

    MINIFICPP-2295 Add SSLContextService support for NiFi Python processors
    
    Closes #1762
    
    Signed-off-by: Marton Szasz <sza...@apache.org>
---
 docker/test/integration/features/python.feature    |   3 +-
 docker/test/integration/features/steps/steps.py    |   1 +
 .../resources/python/SpecialPropertyTypeChecker.py |  15 ++-
 extensions/python/ExecutePythonProcessor.cpp       |   7 +-
 extensions/python/ExecutePythonProcessor.h         |   2 +-
 extensions/python/PYTHON.md                        |   8 +-
 extensions/python/PythonBindings.cpp               |   4 +-
 extensions/python/PythonProcessor.cpp              |   4 +-
 extensions/python/PythonProcessor.h                |   2 +-
 .../pythonprocessors/nifiapi/flowfiletransform.py  |   3 +-
 .../python/pythonprocessors/nifiapi/properties.py  |  14 ++-
 extensions/python/types/PyProcessContext.cpp       |  29 +++++-
 extensions/python/types/PyProcessContext.h         |   2 +
 extensions/python/types/PyProcessor.cpp            |  10 +-
 extensions/python/types/PySSLContextService.cpp    | 104 +++++++++++++++++++++
 .../{PyProcessContext.h => PySSLContextService.h}  |  22 +++--
 16 files changed, 203 insertions(+), 27 deletions(-)

diff --git a/docker/test/integration/features/python.feature 
b/docker/test/integration/features/python.feature
index 4f0110902..b8e945905 100644
--- a/docker/test/integration/features/python.feature
+++ b/docker/test/integration/features/python.feature
@@ -116,11 +116,12 @@ Feature: MiNiFi can use python processors in its flows
     Then flowfiles with these contents are placed in the monitored directory 
in less than 10 seconds: 
"test_data_one,test_data_two,test_data_three,test_data_four"
 
   @USE_NIFI_PYTHON_PROCESSORS
-  Scenario: MiNiFi C++ can use special property types in NiFi native python 
processors
+  Scenario: MiNiFi C++ can use special property types including controller 
services in NiFi native python processors
     Given a GenerateFlowFile processor with the "File Size" property set to 
"0B"
     And a SpecialPropertyTypeChecker processor
     And a PutFile processor with the "Directory" property set to "/tmp/output"
     And python is installed on the MiNiFi agent with a pre-created virtualenv
+    And a SSL context service is set up for the following processor: 
"SpecialPropertyTypeChecker"
 
     And the "success" relationship of the GenerateFlowFile processor is 
connected to the SpecialPropertyTypeChecker
     And the "success" relationship of the SpecialPropertyTypeChecker processor 
is connected to the PutFile
diff --git a/docker/test/integration/features/steps/steps.py 
b/docker/test/integration/features/steps/steps.py
index e778c2c46..4ca2fae96 100644
--- a/docker/test/integration/features/steps/steps.py
+++ b/docker/test/integration/features/steps/steps.py
@@ -1265,6 +1265,7 @@ def step_impl(context, lines: str, timeout_seconds: int):
 
 
 @given(u'a SSL context service is set up for Grafana Loki processor 
\"{processor_name}\"')
+@given(u'a SSL context service is set up for the following processor: 
\"{processor_name}\"')
 def step_impl(context, processor_name: str):
     setUpSslContextServiceForProcessor(context, processor_name)
 
diff --git 
a/docker/test/integration/resources/python/SpecialPropertyTypeChecker.py 
b/docker/test/integration/resources/python/SpecialPropertyTypeChecker.py
index 23354d594..ce404a754 100644
--- a/docker/test/integration/resources/python/SpecialPropertyTypeChecker.py
+++ b/docker/test/integration/resources/python/SpecialPropertyTypeChecker.py
@@ -33,10 +33,17 @@ class SpecialPropertyTypeChecker(FlowFileTransform):
         default_value="100 MB",
         required=True
     )
+    SSL_CONTEXT_PROPERTY = PropertyDescriptor(
+        name="SSL Context Service",
+        description="Dummy property that should be an SSL Context",
+        controller_service_definition="SSLContextService",
+        required=True
+    )
 
     property_descriptors = [
         TIME_PERIOD_PROPERTY,
-        DATA_SIZE_PROPERTY
+        DATA_SIZE_PROPERTY,
+        SSL_CONTEXT_PROPERTY
     ]
 
     def __init__(self, **kwargs):
@@ -96,4 +103,10 @@ class SpecialPropertyTypeChecker(FlowFileTransform):
             self.logger.error("Data size property conversion to gigabytes is 
not working as expected")
             return FlowFileTransformResult("failure", contents="Data size 
property conversion to gigabytes is not working as expected")
 
+        ssl_context = 
context.getProperty(self.SSL_CONTEXT_PROPERTY).asControllerService()
+        cert = ssl_context.getCertificateFile()
+        if cert != "/tmp/resources/minifi_client.crt":
+            self.logger.error("SSL Context Service property is not working as 
expected")
+            return FlowFileTransformResult("failure", contents="SSL Context 
Service property is not working as expected")
+
         return FlowFileTransformResult("success", contents="Check successful!")
diff --git a/extensions/python/ExecutePythonProcessor.cpp 
b/extensions/python/ExecutePythonProcessor.cpp
index a98d446d1..b69b87189 100644
--- a/extensions/python/ExecutePythonProcessor.cpp
+++ b/extensions/python/ExecutePythonProcessor.cpp
@@ -23,6 +23,7 @@
 #include "PythonConfigState.h"
 #include "types/PyRelationship.h"
 #include "types/PyLogger.h"
+#include "controllers/SSLContextService.h"
 
 #include "utils/StringUtils.h"
 #include "utils/file/FileUtils.h"
@@ -158,7 +159,7 @@ std::unique_ptr<PythonScriptEngine> 
ExecutePythonProcessor::createScriptEngine()
 }
 
 void ExecutePythonProcessor::addProperty(const std::string &name, const 
std::string &description, const std::optional<std::string> &defaultvalue, bool 
required, bool el,
-      bool sensitive, const std::optional<int64_t>& property_type_code) {
+      bool sensitive, const std::optional<int64_t>& property_type_code, const 
std::optional<std::string>& controller_service_type_name) {
   auto property = 
core::PropertyDefinitionBuilder<>::createProperty(name).withDescription(description).isRequired(required).supportsExpressionLanguage(el).isSensitive(sensitive);
   if (defaultvalue) {
     property.withDefaultValue(*defaultvalue);
@@ -167,6 +168,10 @@ void ExecutePythonProcessor::addProperty(const std::string 
&name, const std::str
     
property.withPropertyType(core::StandardPropertyTypes::translateCodeToPropertyType(static_cast<core::StandardPropertyTypes::PropertyTypeCode>(*property_type_code)));
   }
 
+  if (controller_service_type_name && *controller_service_type_name == 
"SSLContextService") {
+    property.withAllowedTypes<controllers::SSLContextService>();
+  }
+
   std::lock_guard<std::mutex> lock(python_properties_mutex_);
   python_properties_.emplace_back(property.build());
 }
diff --git a/extensions/python/ExecutePythonProcessor.h 
b/extensions/python/ExecutePythonProcessor.h
index 7de09a37a..7927fe58b 100644
--- a/extensions/python/ExecutePythonProcessor.h
+++ b/extensions/python/ExecutePythonProcessor.h
@@ -96,7 +96,7 @@ class ExecutePythonProcessor : public core::Processor {
   }
 
   void addProperty(const std::string &name, const std::string &description, 
const std::optional<std::string> &defaultvalue, bool required, bool el,
-      bool sensitive, const std::optional<int64_t>& property_type_code);
+      bool sensitive, const std::optional<int64_t>& property_type_code, const 
std::optional<std::string>& controller_service_type_name);
 
   std::vector<core::Property> getPythonProperties() const {
     std::lock_guard<std::mutex> lock(python_properties_mutex_);
diff --git a/extensions/python/PYTHON.md b/extensions/python/PYTHON.md
index c38b2055d..97aaaecc0 100644
--- a/extensions/python/PYTHON.md
+++ b/extensions/python/PYTHON.md
@@ -91,7 +91,7 @@ def describe(processor):
 
 onInitialize is also passed the processor reference and can be where you set 
properties. The first argument is the property display name,
 followed by the description, and default value. The next three arguments are 
booleans describing if the property is required, support expression language, 
and if it is a sensitive property.
-The last argument is the property type code. The property type code is an 
integer that represents the type of the property. The supported property type 
codes and their corresponding types:
+The seventh argument is the property type code. The property type code is an 
integer that represents the type of the property. The supported property type 
codes and their corresponding types:
 ```
 INTEGER = 0
 LONG = 1
@@ -102,10 +102,13 @@ NON_BLANK = 5
 PORT = 6
 ```
 
+The last parameter of addProperty is the controller service type. If the 
property is a controller service, the controller service type should be 
provided. It should be the non-qualified type name of the controller service. 
Currently SSLContextService is the only controller service type supported.
+
 ```python
 def onInitialize(processor):
   processor.setSupportsDynamicProperties()
-  processor.addProperty("property name","description","default value", True 
/*required*/, False /*expression language supported*/, False /*sensitive*/, 1 
/*property type code*/)
+  # arguments: property name, description, default value, is required, 
expression language supported, is sensitive, property type code, controller 
service type name
+  processor.addProperty("property name", "description", "default value", True, 
False, False, 1, None)
 ```
 
 The onSchedule function is passed the context and session factory. This should 
be where your processor loads and reads properties via
@@ -158,7 +161,6 @@ In the flow configuration these Python processors can be 
referenced by their ful
 
 Due to some differences between the NiFi and MiNiFi C++ processors and 
implementation, there are some limitations using the NiFi Python processors:
 - Record based processors are not yet supported in MiNiFi C++, so the NiFi 
Python processors inherited from RecordTransform are not supported.
-- Controller properties are not supported at the moment.
 - There are some validators in NiFi that are not present in MiNiFi C++, so 
some property validations will be missing using the NiFi Python processors.
 - Allowable values specified in NiFi Python processors are ignored in MiNiFi 
C++ (due to MiNiFi C++ requiring them to be specified at compile time), so the 
property values are not pre-verified.
 - MiNiFi C++ only supports expression language with flow file attributes, so 
only FLOWFILE_ATTRIBUTES expression language scope is supported, otherwise the 
expression language will not be evaluated.
diff --git a/extensions/python/PythonBindings.cpp 
b/extensions/python/PythonBindings.cpp
index b113ac864..6cd3d069d 100644
--- a/extensions/python/PythonBindings.cpp
+++ b/extensions/python/PythonBindings.cpp
@@ -27,6 +27,7 @@
 #include "types/PyOutputStream.h"
 #include "types/PyStateManager.h"
 #include "types/PyDataConverter.h"
+#include "types/PySSLContextService.h"
 
 namespace org::apache::nifi::minifi::extensions::python {
 extern "C" {
@@ -60,7 +61,8 @@ PyInit_minifi_native(void) {
       std::make_pair(PyRelationship::typeObject(), "Relationship"),
       std::make_pair(PyInputStream::typeObject(), "InputStream"),
       std::make_pair(PyOutputStream::typeObject(), "OutputStream"),
-      std::make_pair(PyStateManager::typeObject(), "StateManager")
+      std::make_pair(PyStateManager::typeObject(), "StateManager"),
+      std::make_pair(PySSLContextService::typeObject(), "SSLContextService")
   });
 
   for (const auto& type : types) {
diff --git a/extensions/python/PythonProcessor.cpp 
b/extensions/python/PythonProcessor.cpp
index 07fd23211..f7097a5b4 100644
--- a/extensions/python/PythonProcessor.cpp
+++ b/extensions/python/PythonProcessor.cpp
@@ -39,8 +39,8 @@ void PythonProcessor::setDescription(const std::string& desc) 
{
 }
 
 void PythonProcessor::addProperty(const std::string& name, const std::string& 
description, const std::optional<std::string>& defaultvalue,
-    bool required, bool el, bool sensitive, const std::optional<int64_t>& 
property_type_code) {
-  processor_->addProperty(name, description, defaultvalue, required, el, 
sensitive, property_type_code);
+    bool required, bool el, bool sensitive, const std::optional<int64_t>& 
property_type_code, const std::optional<std::string>& 
controller_service_type_name) {
+  processor_->addProperty(name, description, defaultvalue, required, el, 
sensitive, property_type_code, controller_service_type_name);
 }
 
 }  // namespace org::apache::nifi::minifi::extensions::python
diff --git a/extensions/python/PythonProcessor.h 
b/extensions/python/PythonProcessor.h
index 6ec20e2a6..135effbd2 100644
--- a/extensions/python/PythonProcessor.h
+++ b/extensions/python/PythonProcessor.h
@@ -39,7 +39,7 @@ class PythonProcessor {
   void setDescription(const std::string& desc);
 
   void addProperty(const std::string& name, const std::string& description, 
const std::optional<std::string>& defaultvalue,
-    bool required, bool el, bool sensitive, const std::optional<int64_t>& 
property_type_code);
+    bool required, bool el, bool sensitive, const std::optional<int64_t>& 
property_type_code, const std::optional<std::string>& 
controller_service_type_name);
 
  private:
   python::processors::ExecutePythonProcessor* processor_;
diff --git a/extensions/python/pythonprocessors/nifiapi/flowfiletransform.py 
b/extensions/python/pythonprocessors/nifiapi/flowfiletransform.py
index a1edcd76f..6a7c8a530 100644
--- a/extensions/python/pythonprocessors/nifiapi/flowfiletransform.py
+++ b/extensions/python/pythonprocessors/nifiapi/flowfiletransform.py
@@ -68,7 +68,8 @@ class FlowFileTransform(ABC):
 
             # MiNiFi C++ does not support dependant properties, so if a 
property depends on another property, it should not be required
             is_required = True if property.required and not 
property.dependencies else False
-            processor.addProperty(property.name, property.description, 
property.defaultValue, is_required, expression_language_supported, 
property.sensitive, property_type_code)
+            processor.addProperty(property.name, property.description, 
property.defaultValue, is_required, expression_language_supported,
+                                  property.sensitive, property_type_code, 
property.controllerServiceDefinition)
 
     def onScheduled(self, context_proxy: ProcessContextProxy):
         pass
diff --git a/extensions/python/pythonprocessors/nifiapi/properties.py 
b/extensions/python/pythonprocessors/nifiapi/properties.py
index 4f98c48aa..5968774bf 100644
--- a/extensions/python/pythonprocessors/nifiapi/properties.py
+++ b/extensions/python/pythonprocessors/nifiapi/properties.py
@@ -192,13 +192,14 @@ class FlowFileProxy:
 
 
 class PythonPropertyValue:
-    def __init__(self, cpp_context: ProcessContext, name: str, string_value: 
str, el_supported: bool):
+    def __init__(self, cpp_context: ProcessContext, name: str, string_value: 
str, el_supported: bool, controller_service_definition: str):
         self.cpp_context = cpp_context
         self.value = None
         self.name = name
         if string_value is not None:
             self.value = string_value
         self.el_supported = el_supported
+        self.controller_service_definition = controller_service_definition
 
     def getValue(self) -> str:
         return self.value
@@ -263,7 +264,12 @@ class PythonPropertyValue:
         # If Expression Language is supported and present, evaluate it and 
return a new PropertyValue.
         # Otherwise just return self, in order to avoid the cost of making the 
call to cpp for getProperty
         new_string_value = self.cpp_context.getProperty(self.name, 
flow_file_proxy.flow_file)
-        return PythonPropertyValue(self.cpp_context, self.name, 
new_string_value, self.el_supported)
+        return PythonPropertyValue(self.cpp_context, self.name, 
new_string_value, self.el_supported, self.controller_service_definition)
+
+    def asControllerService(self):
+        if not self.controller_service_definition:
+            raise Exception("Controller Service definition is not set, 
getProperty must be called with a property descriptor instead of string value")
+        return self.cpp_context.getControllerService(self.value, 
self.controller_service_definition)
 
 
 class ProcessContextProxy:
@@ -276,8 +282,10 @@ class ProcessContextProxy:
         if isinstance(descriptor, str):
             property_name = descriptor
             expression_language_support = True
+            controller_service_definition = None
         else:
             property_name = descriptor.name
             expression_language_support = descriptor.expressionLanguageScope 
!= ExpressionLanguageScope.NONE
+            controller_service_definition = 
descriptor.controllerServiceDefinition
         property_value = self.cpp_context.getProperty(property_name)
-        return PythonPropertyValue(self.cpp_context, property_name, 
property_value, expression_language_support)
+        return PythonPropertyValue(self.cpp_context, property_name, 
property_value, expression_language_support, controller_service_definition)
diff --git a/extensions/python/types/PyProcessContext.cpp 
b/extensions/python/types/PyProcessContext.cpp
index d428d0e08..81527c10d 100644
--- a/extensions/python/types/PyProcessContext.cpp
+++ b/extensions/python/types/PyProcessContext.cpp
@@ -16,9 +16,11 @@ a * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 
express or implied.
  */
 
 #include "PyProcessContext.h"
+
+#include <string>
+
 #include "PyStateManager.h"
 #include "PyScriptFlowFile.h"
-#include <string>
 #include "PyException.h"
 
 extern "C" {
@@ -27,6 +29,7 @@ namespace org::apache::nifi::minifi::extensions::python {
 static PyMethodDef PyProcessContext_methods[] = {  // 
NOLINT(cppcoreguidelines-avoid-c-arrays)
     {"getProperty", (PyCFunction) PyProcessContext::getProperty, METH_VARARGS, 
nullptr},
     {"getStateManager", (PyCFunction) PyProcessContext::getStateManager, 
METH_VARARGS, nullptr},
+    {"getControllerService", (PyCFunction) 
PyProcessContext::getControllerService, METH_VARARGS, nullptr},
     {}  /* Sentinel */
 };
 
@@ -104,6 +107,30 @@ PyObject* 
PyProcessContext::getStateManager(PyProcessContext* self, PyObject*) {
   return object::returnReference(context->getStateManager());
 }
 
+PyObject* PyProcessContext::getControllerService(PyProcessContext* self, 
PyObject* args) {
+  auto context = self->process_context_.lock();
+  if (!context) {
+    PyErr_SetString(PyExc_AttributeError, "tried reading process context 
outside 'on_trigger'");
+    return nullptr;
+  }
+
+  const char* controller_service_name = nullptr;
+  const char* controller_service_type = nullptr;
+  if (!PyArg_ParseTuple(args, "s|s", &controller_service_name, 
&controller_service_type)) {
+    throw PyException();
+  }
+
+  if (auto controller_service = 
context->getControllerService(controller_service_name)) {
+    std::string controller_service_type_str = controller_service_type;
+    if (controller_service_type_str == "SSLContextService") {
+      auto ssl_ctx_service = 
std::dynamic_pointer_cast<controllers::SSLContextService>(controller_service);
+      return object::returnReference(std::weak_ptr(ssl_ctx_service));
+    }
+  }
+
+  Py_RETURN_NONE;
+}
+
 PyTypeObject* PyProcessContext::typeObject() {
   static OwnedObject 
PyProcessContextType{PyType_FromSpec(&PyProcessContextTypeSpec)};
   return reinterpret_cast<PyTypeObject*>(PyProcessContextType.get());
diff --git a/extensions/python/types/PyProcessContext.h 
b/extensions/python/types/PyProcessContext.h
index decf67478..b484344b6 100644
--- a/extensions/python/types/PyProcessContext.h
+++ b/extensions/python/types/PyProcessContext.h
@@ -19,6 +19,7 @@
 #include <memory>
 
 #include "core/ProcessContext.h"
+#include "PySSLContextService.h"
 #include "../PythonBindings.h"
 
 namespace org::apache::nifi::minifi::extensions::python {
@@ -35,6 +36,7 @@ struct PyProcessContext {
 
   static PyObject* getProperty(PyProcessContext* self, PyObject* args);
   static PyObject* getStateManager(PyProcessContext* self, PyObject* args);
+  static PyObject* getControllerService(PyProcessContext* self, PyObject* 
args);
 
   static PyTypeObject* typeObject();
 };
diff --git a/extensions/python/types/PyProcessor.cpp 
b/extensions/python/types/PyProcessor.cpp
index ce5a5ccb5..c6b817eaa 100644
--- a/extensions/python/types/PyProcessor.cpp
+++ b/extensions/python/types/PyProcessor.cpp
@@ -133,7 +133,15 @@ PyObject* PyProcessor::addProperty(PyProcessor* self, 
PyObject* args) {
     }
   }
 
-  processor->addProperty(name.toUtf8String(), description.toUtf8String(), 
default_value, is_required, supports_expression_language, sensitive, 
validator_value);
+  std::optional<std::string> controller_service_type_name;
+  if (arg_size > 7) {
+    auto controller_service_type_name_pystr = BorrowedStr::fromTuple(args, 7);
+    if (controller_service_type_name_pystr.get() && 
controller_service_type_name_pystr.get() != Py_None) {
+      controller_service_type_name = 
controller_service_type_name_pystr.toUtf8String();
+    }
+  }
+
+  processor->addProperty(name.toUtf8String(), description.toUtf8String(), 
default_value, is_required, supports_expression_language, sensitive, 
validator_value, controller_service_type_name);
   Py_RETURN_NONE;
 }
 
diff --git a/extensions/python/types/PySSLContextService.cpp 
b/extensions/python/types/PySSLContextService.cpp
new file mode 100644
index 000000000..179f5d8f3
--- /dev/null
+++ b/extensions/python/types/PySSLContextService.cpp
@@ -0,0 +1,104 @@
+/**
+ * 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,
+a * 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 "PySSLContextService.h"
+#include <string>
+#include "PyException.h"
+
+extern "C" {
+namespace org::apache::nifi::minifi::extensions::python {
+
+static PyMethodDef PySSLContextService_methods[] = {  // 
NOLINT(cppcoreguidelines-avoid-c-arrays)
+    {"getCertificateFile", (PyCFunction) 
PySSLContextService::getCertificateFile, METH_VARARGS, nullptr},
+    {"getPassphrase", (PyCFunction) PySSLContextService::getPassphrase, 
METH_VARARGS, nullptr},
+    {"getPrivateKeyFile", (PyCFunction) 
PySSLContextService::getPrivateKeyFile, METH_VARARGS, nullptr},
+    {"getCACertificate", (PyCFunction) PySSLContextService::getCACertificate, 
METH_VARARGS, nullptr},
+    {}  /* Sentinel */
+};
+
+static PyType_Slot PySSLContextServiceTypeSpecSlots[] = {  // 
NOLINT(cppcoreguidelines-avoid-c-arrays)
+    {Py_tp_dealloc, 
reinterpret_cast<void*>(pythonAllocatedInstanceDealloc<PySSLContextService>)},
+    {Py_tp_init, reinterpret_cast<void*>(PySSLContextService::init)},
+    {Py_tp_methods, reinterpret_cast<void*>(PySSLContextService_methods)},
+    {Py_tp_new, 
reinterpret_cast<void*>(newPythonAllocatedInstance<PySSLContextService>)},
+    {}  /* Sentinel */
+};
+
+static PyType_Spec PySSLContextServiceTypeSpec{
+    .name = "minifi_native.SSLContextService",
+    .basicsize = sizeof(PySSLContextService),
+    .itemsize = 0,
+    .flags = Py_TPFLAGS_DEFAULT,
+    .slots = PySSLContextServiceTypeSpecSlots
+};
+
+int PySSLContextService::init(PySSLContextService* self, PyObject* args, 
PyObject*) {
+  PyObject* weak_ptr_capsule = nullptr;
+  if (!PyArg_ParseTuple(args, "O", &weak_ptr_capsule)) {
+    return -1;
+  }
+
+  auto ssl_context_service = PyCapsule_GetPointer(weak_ptr_capsule, 
HeldTypeName);
+  if (!ssl_context_service)
+    throw PyException();
+  self->ssl_context_service_ = *static_cast<HeldType*>(ssl_context_service);
+  return 0;
+}
+
+PyObject* PySSLContextService::getCertificateFile(PySSLContextService* self, 
PyObject* /*args*/) {
+  auto ssl_context_service = self->ssl_context_service_.lock();
+  if (!ssl_context_service) {
+    PyErr_SetString(PyExc_AttributeError, "tried reading ssl context service 
outside 'on_trigger'");
+    Py_RETURN_NONE;
+  }
+  return 
object::returnReference(ssl_context_service->getCertificateFile().string());
+}
+
+PyObject* PySSLContextService::getPassphrase(PySSLContextService* self, 
PyObject* /*args*/) {
+  auto ssl_context_service = self->ssl_context_service_.lock();
+  if (!ssl_context_service) {
+    PyErr_SetString(PyExc_AttributeError, "tried reading ssl context service 
outside 'on_trigger'");
+    Py_RETURN_NONE;
+  }
+  return object::returnReference(ssl_context_service->getPassphrase());
+}
+
+PyObject* PySSLContextService::getPrivateKeyFile(PySSLContextService* self, 
PyObject* /*args*/) {
+  auto ssl_context_service = self->ssl_context_service_.lock();
+  if (!ssl_context_service) {
+    PyErr_SetString(PyExc_AttributeError, "tried reading ssl context service 
outside 'on_trigger'");
+    Py_RETURN_NONE;
+  }
+  return 
object::returnReference(ssl_context_service->getPrivateKeyFile().string());
+}
+
+PyObject* PySSLContextService::getCACertificate(PySSLContextService* self, 
PyObject* /*args*/) {
+  auto ssl_context_service = self->ssl_context_service_.lock();
+  if (!ssl_context_service) {
+    PyErr_SetString(PyExc_AttributeError, "tried reading ssl context service 
outside 'on_trigger'");
+    Py_RETURN_NONE;
+  }
+  return 
object::returnReference(ssl_context_service->getCACertificate().string());
+}
+
+PyTypeObject* PySSLContextService::typeObject() {
+  static OwnedObject 
PySSLContextServiceType{PyType_FromSpec(&PySSLContextServiceTypeSpec)};
+  return reinterpret_cast<PyTypeObject*>(PySSLContextServiceType.get());
+}
+
+}  // namespace org::apache::nifi::minifi::extensions::python
+}  // extern "C"
diff --git a/extensions/python/types/PyProcessContext.h 
b/extensions/python/types/PySSLContextService.h
similarity index 58%
copy from extensions/python/types/PyProcessContext.h
copy to extensions/python/types/PySSLContextService.h
index decf67478..043096ef1 100644
--- a/extensions/python/types/PyProcessContext.h
+++ b/extensions/python/types/PySSLContextService.h
@@ -18,29 +18,31 @@
 
 #include <memory>
 
-#include "core/ProcessContext.h"
+#include "controllers/SSLContextService.h"
 #include "../PythonBindings.h"
 
 namespace org::apache::nifi::minifi::extensions::python {
 
-struct PyProcessContext {
-  PyProcessContext() {}
-  using HeldType = std::weak_ptr<core::ProcessContext>;
-  static constexpr const char* HeldTypeName = "PyProcessContext::HeldType";
+struct PySSLContextService {
+  PySSLContextService() {}
+  using HeldType = std::weak_ptr<controllers::SSLContextService>;
+  static constexpr const char* HeldTypeName = "PySSLContextService::HeldType";
 
   PyObject_HEAD
-  HeldType process_context_;
+  HeldType ssl_context_service_;
 
-  static int init(PyProcessContext* self, PyObject* args, PyObject* kwds);
+  static int init(PySSLContextService* self, PyObject* args, PyObject* kwds);
 
-  static PyObject* getProperty(PyProcessContext* self, PyObject* args);
-  static PyObject* getStateManager(PyProcessContext* self, PyObject* args);
+  static PyObject* getCertificateFile(PySSLContextService* self, PyObject* 
args);
+  static PyObject* getPassphrase(PySSLContextService* self, PyObject* args);
+  static PyObject* getPrivateKeyFile(PySSLContextService* self, PyObject* 
args);
+  static PyObject* getCACertificate(PySSLContextService* self, PyObject* args);
 
   static PyTypeObject* typeObject();
 };
 
 namespace object {
 template<>
-struct Converter<PyProcessContext::HeldType> : public 
HolderTypeConverter<PyProcessContext> {};
+struct Converter<PySSLContextService::HeldType> : public 
HolderTypeConverter<PySSLContextService> {};
 }  // namespace object
 }  // namespace org::apache::nifi::minifi::extensions::python

Reply via email to