| # 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. |
| |
| import traceback |
| from abc import abstractmethod |
| from minifi_native import ProcessContext, ProcessSession |
| from .processorbase import ProcessorBase, WriteCallback |
| from .properties import FlowFile as FlowFileProxy |
| from .properties import ProcessContext as ProcessContextProxy |
| |
| |
| class FlowFileTransformResult: |
| def __init__(self, relationship: str, attributes=None, contents=None): |
| self.relationship = relationship |
| self.attributes = attributes |
| if contents is not None and isinstance(contents, str): |
| self.contents = str.encode(contents) |
| else: |
| self.contents = contents |
| |
| def getRelationship(self): |
| return self.relationship |
| |
| def getContents(self): |
| return self.contents |
| |
| def getAttributes(self): |
| return self.attributes |
| |
| |
| class FlowFileTransform(ProcessorBase): |
| # These will be added through the python bindings using C API |
| logger = None |
| REL_SUCCESS = None |
| REL_FAILURE = None |
| REL_ORIGINAL = None |
| |
| def onTrigger(self, context: ProcessContext, session: ProcessSession): |
| original_flow_file = session.get() |
| if not original_flow_file: |
| return |
| |
| flow_file = session.clone(original_flow_file) |
| |
| flow_file_proxy = FlowFileProxy(session, flow_file) |
| context_proxy = ProcessContextProxy(context, self) |
| try: |
| result = self.transform(context_proxy, flow_file_proxy) |
| except Exception: |
| self.logger.error("Failed to transform flow file due to error:\n{}".format(traceback.format_exc())) |
| session.remove(flow_file) |
| session.transfer(original_flow_file, self.REL_FAILURE) |
| return |
| |
| if result.getRelationship() == "original": |
| session.remove(flow_file) |
| self.logger.error("Result relationship cannot be 'original', it is reserved for the original flow file, and transferred automatically in non-failure cases.") |
| session.transfer(original_flow_file, self.REL_FAILURE) |
| return |
| |
| result_attributes = result.getAttributes() |
| if result.getRelationship() == "failure": |
| session.remove(flow_file) |
| if result_attributes is not None: |
| for name, value in result_attributes.items(): |
| original_flow_file.setAttribute(name, value) |
| if result.getContents() is not None: |
| self.logger.error("'failure' relationship should not have content, the original flow file will be transferred automatically in this case.") |
| session.transfer(original_flow_file, self.REL_FAILURE) |
| return |
| |
| if result_attributes is not None: |
| for name, value in result_attributes.items(): |
| flow_file.setAttribute(name, value) |
| |
| result_content = result.getContents() |
| if result_content is not None: |
| session.write(flow_file, WriteCallback(result_content)) |
| |
| if result.getRelationship() == "success": |
| session.transfer(flow_file, self.REL_SUCCESS) |
| else: |
| session.transferToCustomRelationship(flow_file, result.getRelationship()) |
| session.transfer(original_flow_file, self.REL_ORIGINAL) |
| |
| @abstractmethod |
| def transform(self, context: ProcessContextProxy, flowFile: FlowFileProxy) -> FlowFileTransformResult: |
| pass |