Search code examples
node-red

Is it possible to change the referrence of a subflow to a different subflow on Node-RED?


I usually work with different instances of Node-RED and I like to create subflows to encapsulate the code. However, I always end up with my palette of nodes with something like this:

enter image description here

I've read this thread on the official forum where it's said that the behavior of duplicating subflows while importing them was the way Node-RED worked that time. However, this thread is from September 2019, is this behavior still true? If yes, is there any "non-official" way of solving this problem? My guess would be entering inside a file where I find and replace all "exec node (2)" and "exec node (3)" to "exec node" just so when I restart node-red all those subflows will have become "exec node" and I will be able to erase the other ones because they're not being used anywhere anymore. Is it possible?


Solution

  • Manual solution

    After looking to the flow_server.json file inside the Node-RED folder for a while I've figured out a solution to this problem. Basically, you need to do the following:

    1. Find the file flow_server.json inside your .node-red folder (if the projects feature is enabled it's inside the folder projects)
    2. Backup this file with one of a different name just so you don't accidentally mess up with all your flows in case something goes wrong
    3. Get the id of both nodes: the one that you want to keep and the one that is duplicated
    4. Search and replace all subflow:idSubflowToSubstitute to subflow:idSubflowToKeep. In my case it was: subflow:9e7f93e5.544bc8 to subflow:caeb9890.9cdb18
    5. Restart the service that's running Node-RED

    PS: This solution assumes that the duplicated nodes are identical. I don't really know the consequences of following this procedure with different subflows.


    Subflow solution

    I've also created a subflow that does it in an easier way for me. It relies on activating the fs library for Node-RED. Backup your flow_server.json file before experimenting with it...

    [{"id":"b8749b41.f740e","type":"subflow","name":"fix duplication (2)","info":"","category":"essencial","in":[{"x":520,"y":460,"wires":[{"id":"948937d8.1051a8"}]}],"out":[],"env":[{"name":"flowFile","type":"str","value":"/home/user/.node-red/flow_server.json"},{"name":"subflowToKeep","type":"str","value":""},{"name":"subflowToSubstitute","type":"str","value":""}],"color":"#A6D2FF","icon":"node-red/swap.svg"},{"id":"948937d8.1051a8","type":"function","z":"b8749b41.f740e","name":"fix duplication","func":"const fs = global.get('fs');\n\nconst flowFile = env.get(\"flowFile\");\nconst subflowToKeep = env.get(\"subflowToKeep\")\nconst subflowToSubstitute = env.get(\"subflowToSubstitute\");\n\nlet flowString;\nlet flowObject;\n\ntry {\n    flowString = fs.readFileSync(flowFile, 'utf8');\n    flowObject = JSON.parse(flowString);\n\n    if (checkIfSubflowExists(subflowToKeep) && checkIfSubflowExists(subflowToSubstitute)) {\n        let subflowToKeepString = \"\";\n        let subflowToSubstituteString = \"\";\n        flowObject.forEach(({ type, name, id }) => {\n            if ( type === 'subflow'){\n                switch(name) {\n                    case subflowToKeep:\n                        subflowToKeepString = `subflow:${id}`\n                        node.warn(name + \"- \" + subflowToKeepString);\n                        break;\n                    case subflowToSubstitute:\n                        subflowToSubstituteString = `subflow:${id}`\n                        node.warn(name + \"- \" + subflowToSubstituteString);\n                        break;\n                }\n            }\n        });\n\n        if ( subflowToSubstituteString !== \"\" && subflowToKeepString !== \"\" ){\n            var regex = new RegExp(subflowToSubstituteString, \"g\");\n                if ( flowString.search(regex) >= 0 ){\n                    flowString = flowString.replace(regex, subflowToKeepString);\n                    fs.writeFileSync(flowFile, flowString );\n                    node.warn(\"nodes updated! restart node-red.\")\n                    return msg;\n                }\n        }\n    }\n    node.warn(\"Nothing to do. Check the input parameters correctly...\");\n    return msg;\n} catch(err){\n    node.warn(err)\n    return msg;\n}\n\n// auxiliary functions\nfunction checkIfSubflowExists(key){\n    let hasKey = false;\n    flowObject.forEach(({ type, name }) => {\n        if ( type === 'subflow'){\n            if ( name === key ){\n                hasKey = true;\n            }\n        }\n    });\n    return hasKey;\n}\n","outputs":0,"noerr":0,"x":860,"y":460,"wires":[]},{"id":"a437e02b.0f67d8","type":"subflow:b8749b41.f740e","z":"26278f81.44628","name":"","env":[],"x":1190,"y":380,"wires":[]}]