HTTP POST Request on /flows - just get ´unexpected_error´ / ´config.forEach is not a function´

Hi there,

i need to do a POST request to http://HOSTNAME:1880/flows - to reach a click on the "Deploy" button via machine-to-machine communication... (to simulate the humam user clicks in browser)

Somebody told me a POST request to http://HOSTNAME:1880/flows is the correct way to reach that...

-> I already tested the GET way, but so it seems a GET request to http://HOSTNAME:1880/flows does only gives me back the current config (ALL flows...) in JSON...

But the only one thing i get with that source (just a test or prototype) is that:

{
	"code": "unexpected_error",
	"message": "config.forEach is not a function"
}

Here is the PHP source code on my Windows development workstation - with Apache 2.4.x and an older PHP 7.0.9 version (does not matter i.m.o. - maybe therer are some newer version, but anyway... ;-))

<?php
ini_set('display_errors', 1);
ini_set('display_startup_errors', 1);
error_reporting(E_ALL);

header('Content-Type: text/json; charset=utf-8');

require_once $_SERVER['DOCUMENT_ROOT'] . "/functions.inc.php";
use Functions;

$DEBUG_MODE = true;
if (! $DEBUG_MODE) {
    ini_set("display_errors", 0);
    ini_set("display_startup_errors", 0);
} else {
    header("Content-type: text/json");
}

ini_set('max_execution_time', 0);

$ch = curl_init();
$webserverPort = 1880;
$webserverHost = "localhost";
$protocol = "http";
$url = $protocol . "://" . $webserverHost . ":" . $webserverPort . "/flows";

$data = null; // ToDo

// Just for dev purposes {
if ($data == null) {
    $data = "";
}
// } Just for dev purposes

$GLOBALS['Node-RED-API-Version'] = "v2";
$GLOBALS['Node-RED-Deployment-Type'] = "full";
$GLOBALS['X-Requested-With'] = "XMLHttpRequest";

class CurlClass
{

    private $myGlobals = null;

    private $myCurl = null;

    private $myURL = null;

    // J.M., 2021-09-24, Constructor {
    function __construct($URL, $curl)
    {
        eval("global \$GLOBALS;"); // Otherwise, Eclipse PDT 2021-06 (4.20.0) will show an unused variable, nobody knows why...
        $this->myGlobals = &$GLOBALS;
        $this->myURL = $URL;
        $this->myCurl = $curl;
    }

    // } J.M., 2021-09-24, Constructor
    private static function getUserAgent()
    {
        $ret = "User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3359.139 Safari/537.36";

        if (isset($_SERVER['HTTP_USER_AGENT'])) {
            $ret = $_SERVER['HTTP_USER_AGENT'];
        }

        return $ret;
    }

    public function initCURL()
    {
        global $ch;
        global $url;
        global $data;
        global $callback;
        global $obj;

        curl_setopt($ch, CURLOPT_TIMEOUT, 100);
        curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 120);
        curl_setopt($ch, CURLOPT_URL, $url);
        curl_setopt($ch, CURLOPT_COOKIESESSION, true);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);

        // //////////////////////////////////////////////////////////////////////////////////////
        $httpHeadersArr = array();
        $httpHeadersArr['User-Agent'] = CurlClass::getUserAgent();
        $httpHeadersArr['Node-RED-API-Version'] = $this->myGlobals['Node-RED-API-Version'];
        $httpHeadersArr['Node-RED-Deployment-Type'] = $this->myGlobals['Node-RED-Deployment-Type'];
        $httpHeadersArr['X-Requested-With'] = $this->myGlobals['X-Requested-With'];
        // //////////////////////////////////////////////////////////////////////////////////////
        
        curl_setopt($ch, CURLOPT_POST, true);        
        
        if (! ($data == null || $data == "")) {
            curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
        }

        // curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "POST");     

        curl_setopt($ch, CURLOPT_HTTPHEADER, $httpHeadersArr);

        if (false) {
            curl_setopt($ch, CURLOPT_HEADER, 1); // For debugging purposes only
        } else {
            curl_setopt($ch, CURLOPT_HEADER, 0); // Default value, don't include response headers in response
        }

        /*
         $splitter = new SplitCurlByLines();
         curl_setopt($this->myCurl, CURLOPT_WRITEFUNCTION, array(
         $splitter,
         'curlCallback'
         ));
         */

        // curl_setopt($ch, CURLOPT_POSTFIELDSIZE, $data.length()); // CURLOPT_POSTFIELDSIZE is NOT available in PHP!!
        // curl_setopt($ch, CURLOPT_WRITEDATA, null); // CURLOPT_WRITEDATA is NOT available in PHP!!
        
        Functions::println($url); 
    }
}

class SplitCurlByLines
{
    // The last line could be unfinished. We should not proccess it yet
    public function curlCallback($curl, $data)
    {
        $this->currentLine .= $data;
        $lines = explode("\n", $this->currentLine);

        $numLines = count($lines) - 1;
        $this->currentLine = $lines[$numLines]; // Save for the next callback

        for ($i = 0; $i < $numLines; ++ $i) {
            $this->processLine($lines[$i]); // Do whatever you want
            ++ $this->totalLineCount; // Statistics
            $this->totalLength += strlen($lines[$i]) + 1;
        }

        return strlen($data); // Ask curl for more data (!= value will stop)
    }

    // Do what ever you want (split CSV, ...)
    public function processLine($str)
    {
        echo $str . "\n";
    }

    public $currentLine = '';

    public $totalLineCount = 0;

    public $totalLength = 0;
}

try {
    // Script starts here
    $stream = new CurlClass($url, $ch);
    $stream->initCURL();
    $httpResponse = curl_exec($ch);
    // Functions::println($url);
    // Functions::println("");    
    $formatted_json = Functions::format_json($httpResponse);
    Functions::println($formatted_json);
    // Script ends here
} catch (Exception $ex) {
    $vardump = var_export($ex, true) . "\r\n";
    print_r($vardump);
    exit(- 1);
} finally {
    try {
        curl_close($ch);
    } catch (Exception $ex2) {
        foo($ex2);
        exit(+ 1);
    }
}

// Noop {
function foo($arg)
{
    if ($arg) {
        $arg = ! $arg;
    }

    while ($arg);
}
// } Noop

// echo "Script ends here..." . "\r\n";
exit(0);
?>

Thank you very very much for your feedbacks.

With kind regards,
Jan

Hi @bfxnapnxpv

What data are you sending in the POST request? It should be the new flow configuration you want to deploy to the runtime.

That error response isn't very helpful, but I think it is a result of the format of your data being incorrect.

The expected body format is documented here: POST /flows : Node-RED