package nodered import future.keywords.if import future.keywords.contains import future.keywords.in config_node(typ) if typ in {"mqtt-broker", "ui-base", "ui-page", "ui-group"} welcome_isolated(typ) if typ in {"debug", "mqtt-broker", "ui-base", "ui-page", "ui-group", "ui-text", "ui-chart", "ui-gauge", "ui-button", "ui-switch", "ui-slider", "inject", "comment", "mqtt in", "catch", "status", "link in"} broker_node(id) if { some node in input node.type == "mqtt-broker" node.id == id } node_exists(id) if { some node in input node.id == id } has_inbound_wire(target_id) if { some node in input some wire in node.wires target_id in wire } has_outbound_wire(source_id) if { some node in input node.id == source_id node.wires count(node.wires) > 0 } deny_missing_type contains msg if { some node in input not node.type msg := sprintf("node %s: missing type field", [node.id]) } deny_missing_field contains msg if { some node in input node.type == "mqtt-broker" required := {"type", "broker", "port"} some f in required not node[f] msg := sprintf("node %s (mqtt-broker): missing %s", [node.id, f]) } deny_missing_field contains msg if { some node in input node.type == "mqtt in" required := {"type", "topic", "broker", "z"} some f in required not node[f] msg := sprintf("node %s (mqtt in): missing %s", [node.id, f]) } deny_missing_field contains msg if { some node in input node.type == "mqtt out" required := {"type", "broker", "z"} some f in required not node[f] msg := sprintf("node %s (mqtt out): missing %s", [node.id, f]) } deny_missing_field contains msg if { some node in input node.type == "debug" required := {"type", "z"} some f in required not node[f] msg := sprintf("node %s (debug): missing %s", [node.id, f]) } deny_missing_field contains msg if { some node in input node.type in {"ui-text", "ui-button", "ui-chart", "ui-gauge", "ui-switch", "ui-slider", "ui-dropdown"} required := {"type", "group", "z"} some f in required not node[f] msg := sprintf("node %s (%s): missing %s", [node.id, node.type, f]) } deny_missing_field contains msg if { some node in input node.type == "ui-group" required := {"type", "page", "name"} some f in required not node[f] msg := sprintf("node %s (ui-group): missing %s", [node.id, f]) } deny_missing_field contains msg if { some node in input node.type == "ui-page" required := {"type", "name", "ui"} some f in required not node[f] msg := sprintf("node %s (ui-page): missing %s", [node.id, f]) } deny_missing_field contains msg if { some node in input node.type == "ui-base" required := {"type", "name"} some f in required not node[f] msg := sprintf("node %s (ui-base): missing %s", [node.id, f]) } deny_broker_ref contains msg if { some node in input node.type in {"mqtt in", "mqtt out"} not broker_node(node.broker) msg := sprintf("node %s (%s): references unknown broker %s", [node.id, node.type, node.broker]) } ui_page(id) if { some node in input node.type == "ui-page" node.id == id } ui_group(id) if { some node in input node.type == "ui-group" node.id == id } deny_group_ref contains msg if { some node in input node.type in {"ui-text", "ui-button", "ui-chart", "ui-gauge", "ui-switch", "ui-slider", "ui-dropdown"} not ui_group(node.group) msg := sprintf("node %s (%s): references unknown group %s", [node.id, node.type, node.group]) } deny_page_ref contains msg if { some node in input node.type == "ui-group" not ui_page(node.page) msg := sprintf("node %s (ui-group): references unknown page %s", [node.id, node.page]) } deny_wire_ref contains msg if { some node in input some wire in node.wires some target in wire not node_exists(target) msg := sprintf("node %s (%s): wire to unknown node %s", [node.id, node.type, target]) } deny_missing_flow contains msg if { some node in input not config_node(node.type) not node.z msg := sprintf("node %s (%s): missing flow assignment (z)", [node.id, node.type]) } deny_isolated_node contains msg if { some node in input not welcome_isolated(node.type) not has_inbound_wire(node.id) not has_outbound_wire(node.id) msg := sprintf("node %s (%s): has no wires", [node.id, node.type]) } deny_broker_port contains msg if { some node in input node.type == "mqtt-broker" not is_valid_port(to_number(node.port)) msg := sprintf("node %s (mqtt-broker): invalid port %s", [node.id, node.port]) } is_valid_port(p) if { p >= 1; p <= 65535 } # Check that all flow nodes have wires field deny_missing_wires contains msg if { some node in input node.type in {"mqtt in", "mqtt out", "function", "debug", "inject", "ui-text", "ui-button", "change", "switch", "delay", "exec"} not "wires" in object.keys(node) msg := sprintf("node %s (%s): missing wires field", [node.id, node.type]) } # Check mqtt-broker has autoConnect = true deny_no_autoconnect contains msg if { some node in input node.type == "mqtt-broker" not node.autoConnect msg := sprintf("node %s (mqtt-broker): autoConnect must be true", [node.id]) } valid_protocol_version(v) if { some p in {"3", "4", "5"} v == p } deny_invalid_protocol contains msg if { some node in input node.type == "mqtt-broker" node.protocolVersion not valid_protocol_version(node.protocolVersion) msg := sprintf("node %s (mqtt-broker): invalid protocolVersion %s", [node.id, node.protocolVersion]) } # Check that broker has at least one user deny_unused_broker contains msg if { some broker in input broker.type == "mqtt-broker" not has_broker_user(broker.id) msg := sprintf("node %s (mqtt-broker): no mqtt in/out nodes reference this broker", [broker.id]) } has_broker_user(bid) if { some node in input node.broker == bid } # nodered 5.0 bug: tabs with internal `nodes` array break flow loading. # Use FLAT structure: all nodes at top level with z field referencing tab id. deny_wrapped_tab contains msg if { some tab in input tab.type == "tab" tab.nodes count(tab.nodes) > 0 msg := sprintf("tab %s has internal nodes array (use flat structure - all nodes at top level with z field)", [tab.id]) }