def _get_parent_id_list( self, pipeline_definition: PipelineDefinition, node_id_list: list, parent_list: list ) -> List: """ Helper function to return a complete list of parent node_ids :param pipeline_definition: the complete pipeline definition :param node_id_list: list of parent node ids :param parent_list: the list to add additional found parent node ids :return: """ for node_id in node_id_list: node = pipeline_definition.get_node(node_id) if node: if node.type in ["execution_node", "super_node"]: parent_list.append(node_id) node_ids = list(x.get("node_id_ref", None) for x in node.component_links) for nid in node_ids: # look-ahead to determine if node is a binding node if pipeline_definition.get_node(nid).type == "binding": node_ids.remove(nid) for super_node in pipeline_definition.get_supernodes(): if super_node.subflow_pipeline_id == nid: links = list(x.get("node_id_ref", None) for x in super_node.component_links) node_ids.append(links) self._get_parent_id_list(pipeline_definition, node_ids, parent_list) else: # binding node pass return parent_list
async def _validate_custom_component_node_properties( self, node: Node, response: ValidationResponse, pipeline_definition: PipelineDefinition, pipeline_runtime: str ): """ Validates the properties of the custom component node :param node: the node to be validated :param response: the validation response object to attach any error messages :param pipeline_definition: the pipeline definition containing the node :param pipeline_runtime: the pipeline runtime selected :return: """ component_list = await PipelineProcessorManager.instance().get_components(pipeline_runtime) components = ComponentCache.to_canvas_palette(component_list) # Full dict of properties for the operation e.g. current params, optionals etc component_property_dict = await self._get_component_properties(pipeline_runtime, components, node.op) # List of just the current parameters for the component current_parameter_defaults_list = list( map(lambda x: str(x).replace("elyra_", ""), component_property_dict["current_parameters"].keys()) ) # Remove the non component_parameter jinja templated values we do not check against current_parameter_defaults_list.remove("component_source") current_parameter_defaults_list.remove("label") for default_parameter in current_parameter_defaults_list: node_param = node.get_component_parameter(default_parameter) if self._is_required_property(component_property_dict, default_parameter): if not node_param: response.add_message( severity=ValidationSeverity.Error, message_type="invalidNodeProperty", message="Node is missing required property.", data={"nodeID": node.id, "nodeName": node.label, "propertyName": default_parameter}, ) elif self._get_component_type(component_property_dict, default_parameter) == "inputpath": # Any component property with type `InputPath` will be a dictionary of two keys # "value": the node ID of the parent node containing the output # "option": the name of the key (which is an output) of the above referenced node if ( not isinstance(node_param, dict) or len(node_param) != 2 or set(node_param.keys()) != {"value", "option"} ): response.add_message( severity=ValidationSeverity.Error, message_type="invalidNodeProperty", message="Node has malformed `InputPath` parameter structure", data={"nodeID": node.id, "nodeName": node.label}, ) node_ids = list(x.get("node_id_ref", None) for x in node.component_links) parent_list = self._get_parent_id_list(pipeline_definition, node_ids, []) node_param_value = node_param.get("value") if node_param_value not in parent_list: response.add_message( severity=ValidationSeverity.Error, message_type="invalidNodeProperty", message="Node contains an invalid inputpath reference. Please " "check your node-to-node connections", data={"nodeID": node.id, "nodeName": node.label}, ) elif isinstance(node_param, dict) and node_param.get("activeControl") == "NestedEnumControl": if not node_param.get("NestedEnumControl"): response.add_message( severity=ValidationSeverity.Error, message_type="invalidNodeProperty", message="Node contains an invalid reference to an node output. Please " "check the node properties are configured properly", data={"nodeID": node.id, "nodeName": node.label}, ) else: # TODO: Update this hardcoded check for xcom_push. This parameter is specific to a runtime # (Airflow). i.e. abstraction for byo validation? node_param_value = node_param["NestedEnumControl"].get("value") upstream_node = pipeline_definition.get_node(node_param_value) xcom_param = upstream_node.get_component_parameter("xcom_push") if xcom_param: xcom_value = xcom_param.get("BooleanControl") if not xcom_value: response.add_message( severity=ValidationSeverity.Error, message_type="invalidNodeProperty", message="Node contains an invalid input reference. The parent " "node does not have the xcom_push property enabled", data={ "nodeID": node.id, "nodeName": node.label, "parentNodeID": upstream_node.label, }, )