mirror of
				https://github.com/node-red/node-red.git
				synced 2025-03-01 10:36:34 +00:00 
			
		
		
		
	Add UI for common headers/values
- Wrap HTML node script in IFFE (isolate module level vars & functions) - Add UI elements for setting headers in http req node edit form - Update built in help - Add tests
This commit is contained in:
		| @@ -104,10 +104,100 @@ | ||||
|         <label for="node-input-name"><i class="fa fa-tag"></i> <span data-i18n="common.label.name"></span></label> | ||||
|         <input type="text" id="node-input-name" data-i18n="[placeholder]common.label.name"> | ||||
|     </div> | ||||
|     <div class="form-row" style="margin-bottom:0;"> | ||||
|         <label><i class="fa fa-list"></i> <span data-i18n="httpin.label.headers"></span></label> | ||||
|     </div> | ||||
|     <div class="form-row node-input-headers-container-row"> | ||||
|         <ol id="node-input-headers-container"></ol> | ||||
|     </div> | ||||
|     <div class="form-tips" id="tip-json" hidden><span data-i18n="httpin.tip.req"></span></div> | ||||
| </script> | ||||
|  | ||||
| <script type="text/javascript"> | ||||
| (function() { | ||||
|     const headerTypes = [ | ||||
|         { value: "Accept", label: "Accept", hasValue: false }, | ||||
|         { value: "Accept-Charset", label: "Accept-Charset", hasValue: false }, | ||||
|         { value: "Accept-Encoding", label: "Accept-Encoding", hasValue: false }, | ||||
|         { value: "Accept-Language", label: "Accept-Language", hasValue: false }, | ||||
|         { value: "Authorization", label: "Authorization", hasValue: false }, | ||||
|         { value: "content-type", label: "Content-Type", hasValue: false }, | ||||
|         { value: "Cache-Control", label: "Cache-Control", hasValue: false }, | ||||
|         { value: "User-Agent", label: "User-Agent", hasValue: false }, | ||||
|         { value: "location", label: "Location", hasValue: false }, | ||||
|         { value: "other", label: "other", hasValue: true, icon: "red/images/typedInput/az.png" }, | ||||
|         { value: "msg", label: "msg.", hasValue: true }, | ||||
|     ] | ||||
|     const headerOptions = {}; | ||||
|     const defaultOptions = [ | ||||
|         { value: "other", label: "other", hasValue: true, icon: "red/images/typedInput/az.png" }, | ||||
|         { value: "msg", label: "msg.", hasValue: true }, | ||||
|     ]; | ||||
|     headerOptions["accept"] = [ | ||||
|         { value: "text/plain", label: "text/plain", hasValue: false }, | ||||
|         { value: "text/html", label: "text/html", hasValue: false }, | ||||
|         { value: "application/json", label: "application/json", hasValue: false }, | ||||
|         { value: "application/xml", label: "application/xml", hasValue: false }, | ||||
|         ...defaultOptions, | ||||
|     ]; | ||||
|     headerOptions["accept-encoding"] = [ | ||||
|         { value: "gzip", label: "gzip", hasValue: false }, | ||||
|         { value: "deflate", label: "deflate", hasValue: false }, | ||||
|         { value: "gzip", label: "gzip", hasValue: false }, | ||||
|         { value: "gzip, deflate", label: "gzip, deflate", hasValue: false }, | ||||
|         { value: "gzip, deflate, br", label: "gzip, deflate, br", hasValue: false }, | ||||
|         ...defaultOptions, | ||||
|     ]; | ||||
|     headerOptions["accept-language"] = [ | ||||
|         { value: "*", label: "*", hasValue: false }, | ||||
|         { value: "en-GB, en-US, en;q=0.9", label: "en-GB, en-US, en;q=0.9", hasValue: false }, | ||||
|         { value: "de-AT, de-DE;q=0.9, en;q=0.5", label: "de-AT, de-DE;q=0.9, en;q=0.5", hasValue: false }, | ||||
|         { value: "es-mx,es,en;q=0.5", label: "es-mx,es,en;q=0.5", hasValue: false }, | ||||
|         { value: "fr-CH, fr;q=0.9, en;q=0.8", label: "fr-CH, fr;q=0.9, en;q=0.8", hasValue: false }, | ||||
|         { value: "zh-CN, zh-TW; q = 0.9, zh-HK; q = 0.8, zh; q = 0.7, en; q = 0.6", label: "zh-CN, zh-TW; q = 0.9, zh-HK; q = 0.8, zh; q = 0.7, en; q = 0.6", hasValue: false }, | ||||
|         { value: "jp", label: "jp", hasValue: false }, | ||||
|         ...defaultOptions, | ||||
|     ]; | ||||
|     headerOptions["content-type"] = [ | ||||
|         { value: "text/css", label: "text/css", hasValue: false }, | ||||
|         { value: "text/plain", label: "text/plain", hasValue: false }, | ||||
|         { value: "text/html", label: "text/html", hasValue: false }, | ||||
|         { value: "application/json", label: "application/json", hasValue: false }, | ||||
|         { value: "application/octet-stream", label: "application/octet-stream", hasValue: false }, | ||||
|         { value: "application/pdf", label: "application/pdf", hasValue: false }, | ||||
|         { value: "application/xml", label: "application/xml", hasValue: false }, | ||||
|         { value: "application/zip", label: "application/zip", hasValue: false }, | ||||
|         { value: "multipart/form-data", label: "multipart/form-data", hasValue: false }, | ||||
|         { value: "audio/aac", label: "audio/aac", hasValue: false }, | ||||
|         { value: "audio/ac3", label: "audio/ac3", hasValue: false }, | ||||
|         { value: "audio/basic", label: "audio/basic", hasValue: false }, | ||||
|         { value: "audio/mp4", label: "audio/mp4", hasValue: false }, | ||||
|         { value: "audio/ogg", label: "audio/ogg", hasValue: false }, | ||||
|         { value: "image/bmp", label: "image/bmp", hasValue: false }, | ||||
|         { value: "image/gif", label: "image/gif", hasValue: false }, | ||||
|         { value: "image/jpeg", label: "image/jpeg", hasValue: false }, | ||||
|         { value: "image/png", label: "image/png", hasValue: false }, | ||||
|         { value: "image/tiff", label: "image/tiff", hasValue: false }, | ||||
|         ...defaultOptions, | ||||
|     ]; | ||||
|     headerOptions["cache-control"] = [ | ||||
|         { value: "max-age=0", label: "max-age=0", hasValue: false }, | ||||
|         { value: "max-age=86400", label: "max-age=86400", hasValue: false }, | ||||
|         { value: "no-cache", label: "no-cache", hasValue: false }, | ||||
|         ...defaultOptions, | ||||
|     ]; | ||||
|  | ||||
|     headerOptions["user-agent"] = [ | ||||
|         { value: "Mozilla/5.0", label: "Mozilla/5.0", hasValue: false }, | ||||
|         ...defaultOptions, | ||||
|     ]; | ||||
|  | ||||
|     function getHeaderOptions(headerName) { | ||||
|         const lc = (headerName || "").toLowerCase(); | ||||
|         let opts = headerOptions[lc]; | ||||
|         return opts || defaultOptions; | ||||
|     } | ||||
|  | ||||
|     RED.nodes.registerType('http request',{ | ||||
|         category: 'network', | ||||
|         color:"rgb(231, 231, 174)", | ||||
| @@ -121,7 +211,8 @@ | ||||
|             persist: {value:false}, | ||||
|             proxy: {type:"http proxy",required: false}, | ||||
|             authType: {value: ""}, | ||||
|             senderr: {value: false} | ||||
|             senderr: {value: false}, | ||||
|             headers: { value: [] } | ||||
|         }, | ||||
|         credentials: { | ||||
|             user: {type:"text"}, | ||||
| @@ -144,8 +235,9 @@ | ||||
|             return this.name?"node_label_italic":""; | ||||
|         }, | ||||
|         oneditprepare: function() { | ||||
|             const node = this; | ||||
|             $("#node-input-useAuth").on("change", function() { | ||||
|                 if ($(this).is(":checked")) { | ||||
|                 if ($(node).is(":checked")) { | ||||
|                     $(".node-input-useAuth-row").show(); | ||||
|                     // Nodes (< version 0.20.x) with credentials but without authentication type, need type 'basic' | ||||
|                     if (!$('#node-input-authType').val()) { | ||||
| @@ -157,9 +249,10 @@ | ||||
|                     $('#node-input-user').val(''); | ||||
|                     $('#node-input-password').val(''); | ||||
|                 } | ||||
|                 RED.tray.resize(); | ||||
|             }); | ||||
|             $("#node-input-authType-select").on("change", function() { | ||||
|                 var val = $(this).val(); | ||||
|                 const val = $(node).val(); | ||||
|                 $("#node-input-authType").val(val); | ||||
|                 if (val === "basic" || val === "digest") { | ||||
|                     $(".node-input-basic-row").show(); | ||||
| @@ -171,24 +264,26 @@ | ||||
|                     $('#node-span-token').show(); | ||||
|                     $('#node-input-user').val(''); | ||||
|                 } | ||||
|                 RED.tray.resize(); | ||||
|             }); | ||||
|             $("#node-input-method").on("change", function() { | ||||
|                 if ($(this).val() == "GET") { | ||||
|                 if ($(node).val() == "GET") { | ||||
|                     $(".node-input-paytoqs-row").show(); | ||||
|                 } else { | ||||
|                     $(".node-input-paytoqs-row").hide(); | ||||
|                 } | ||||
|                 RED.tray.resize(); | ||||
|             }); | ||||
|             if (this.paytoqs === true || this.paytoqs == "query") { | ||||
|             if (node.paytoqs === true || node.paytoqs == "query") { | ||||
|                 $("#node-input-paytoqs").val("query"); | ||||
|             } else if (this.paytoqs === "body") { | ||||
|             } else if (node.paytoqs === "body") { | ||||
|                 $("#node-input-paytoqs").val("body"); | ||||
|             } else { | ||||
|                 $("#node-input-paytoqs").val("ignore"); | ||||
|             } | ||||
|             if (this.authType) { | ||||
|             if (node.authType) { | ||||
|                 $('#node-input-useAuth').prop('checked', true); | ||||
|                 $("#node-input-authType-select").val(this.authType); | ||||
|                 $("#node-input-authType-select").val(node.authType); | ||||
|                 $("#node-input-authType-select").change(); | ||||
|             } else { | ||||
|                 $('#node-input-useAuth').prop('checked', false); | ||||
| @@ -201,8 +296,9 @@ | ||||
|                 } else { | ||||
|                     $("#node-row-tls").hide(); | ||||
|                 } | ||||
|                 RED.tray.resize(); | ||||
|             } | ||||
|             if (this.tls) { | ||||
|             if (node.tls) { | ||||
|                 $('#node-input-usetls').prop('checked', true); | ||||
|             } else { | ||||
|                 $('#node-input-usetls').prop('checked', false); | ||||
| @@ -218,8 +314,9 @@ | ||||
|                 } else { | ||||
|                     $("#node-input-useProxy-row").hide(); | ||||
|                 } | ||||
|                 RED.tray.resize(); | ||||
|             } | ||||
|             if (this.proxy) { | ||||
|             if (node.proxy) { | ||||
|                 $("#node-input-useProxy").prop("checked", true); | ||||
|             } else { | ||||
|                 $("#node-input-useProxy").prop("checked", false); | ||||
| @@ -235,7 +332,70 @@ | ||||
|                 } else { | ||||
|                     $("#tip-json").hide(); | ||||
|                 } | ||||
|                 RED.tray.resize(); | ||||
|             }); | ||||
|             const hasMatch = function (arr, value) { | ||||
|                 return arr.some(function (ht) { | ||||
|                     return ht.value === value | ||||
|                 }); | ||||
|             } | ||||
|             const headerList = $("#node-input-headers-container").css('min-height', '150px').css('min-width', '450px').editableList({ | ||||
|                 addItem: function (container, i, header) { | ||||
|                     const row = $('<div/>').css({ | ||||
|                         overflow: 'hidden', | ||||
|                         whiteSpace: 'nowrap', | ||||
|                         display: 'flex' | ||||
|                     }).appendTo(container); | ||||
|                     const propertNameCell = $('<div/>').css({ 'flex-grow': 1 }).appendTo(row); | ||||
|                     const propertyName = $('<input/>', { class: "node-input-header-name", type: "text", style: "width: 100%" }) | ||||
|                         .appendTo(propertNameCell) | ||||
|                         .typedInput({ types: headerTypes }); | ||||
|  | ||||
|                     const propertyValueCell = $('<div/>').css({ 'flex-grow': 1, 'margin-left': '10px' }).appendTo(row); | ||||
|                     const propertyValue = $('<input/>', { class: "node-input-header-value", type: "text", style: "width: 100%" }) | ||||
|                         .appendTo(propertyValueCell) | ||||
|                         .typedInput({ | ||||
|                             types: getHeaderOptions(header.keyType) | ||||
|                         }); | ||||
|  | ||||
|                     const setup = function(_header) { | ||||
|                         const headerTypeIsAPreset = function(h) {return hasMatch(headerTypes, h) }; | ||||
|                         const headerValueIsAPreset = function(h, v) {return hasMatch(getHeaderOptions(h), v) }; | ||||
|                         const {keyType, keyValue, valueType, valueValue} = header; | ||||
|                         if(keyType == "msg" || keyType == "other") { | ||||
|                             propertyName.typedInput('type', keyType); | ||||
|                             propertyName.typedInput('value', keyValue); | ||||
|                         } else if (headerTypeIsAPreset(keyType)) { | ||||
|                             propertyName.typedInput('type', keyType); | ||||
|                         } else { | ||||
|                             propertyName.typedInput('type', "other"); | ||||
|                             propertyName.typedInput('value', keyValue); | ||||
|                         } | ||||
|                         if(valueType == "msg" || valueType == "other") { | ||||
|                             propertyValue.typedInput('type', valueType); | ||||
|                             propertyValue.typedInput('value', valueValue); | ||||
|                         } else if (headerValueIsAPreset(propertyName.typedInput('type'), valueType)) { | ||||
|                             propertyValue.typedInput('type', valueType); | ||||
|                         } else { | ||||
|                             propertyValue.typedInput('type', "other"); | ||||
|                             propertyValue.typedInput('value', valueValue); | ||||
|                         } | ||||
|                     } | ||||
|                     setup(header); | ||||
|  | ||||
|                     propertyName.on('change', function (event) { | ||||
|                         propertyValue.typedInput('types', getHeaderOptions(propertyName.typedInput('type'))); | ||||
|                     }); | ||||
|  | ||||
|                 }, | ||||
|                 removable: true | ||||
|             }); | ||||
|             if (node.headers) { | ||||
|                 for (let index = 0; index < node.headers.length; index++) { | ||||
|                     const element = node.headers[index]; | ||||
|                     headerList.editableList('addItem', node.headers[index]); | ||||
|                 } | ||||
|             } | ||||
|         }, | ||||
|         oneditsave: function() { | ||||
|             if (!$("#node-input-usetls").is(':checked')) { | ||||
| @@ -244,6 +404,36 @@ | ||||
|             if (!$("#node-input-useProxy").is(":checked")) { | ||||
|                 $("#node-input-proxy").val("_ADD_"); | ||||
|             } | ||||
|             const headers = $("#node-input-headers-container").editableList('items'); | ||||
|             const node = this; | ||||
|             node.headers = []; | ||||
|             headers.each(function(i) { | ||||
|                 const header = $(node); | ||||
|                 const keyType = header.find(".node-input-header-name").typedInput('type'); | ||||
|                 const keyValue = header.find(".node-input-header-name").typedInput('value'); | ||||
|                 const valueType = header.find(".node-input-header-value").typedInput('type'); | ||||
|                 const valueValue = header.find(".node-input-header-value").typedInput('value'); | ||||
|                 if (keyType !== '' || keyType === 'other' || keyType === 'msg') { | ||||
|                     node.headers.push({ | ||||
|                         keyType, keyValue, valueType, valueValue | ||||
|                     }) | ||||
|                 } | ||||
|             }); | ||||
|         }, | ||||
|         oneditresize: function(size) { | ||||
|             const dlg = $("#dialog-form"); | ||||
|             const expandRow = dlg.find('.node-input-headers-container-row'); | ||||
|             let height = dlg.height() - 5; | ||||
|             if(expandRow && expandRow.length){ | ||||
|                 const siblingRows = dlg.find('> .form-row:not(.node-input-headers-container-row)'); | ||||
|                 for (let i = 0; i < siblingRows.size(); i++) { | ||||
|                     const cr = $(siblingRows[i]); | ||||
|                     if(cr.is(":visible")) | ||||
|                         height -= cr.outerHeight(true); | ||||
|                 } | ||||
|                 $("#node-input-headers-container").editableList('height',height); | ||||
|             }  | ||||
|         } | ||||
|     }); | ||||
| })();     | ||||
| </script> | ||||
|   | ||||
| @@ -73,7 +73,7 @@ in your Node-RED user directory (${RED.settings.userDir}). | ||||
|         var paytobody = false; | ||||
|         var redirectList = []; | ||||
|         var sendErrorsToCatch = n.senderr; | ||||
|  | ||||
|         node.headers = n.headers || []; | ||||
|         var nodeHTTPPersistent = n["persist"]; | ||||
|         if (n.tls) { | ||||
|             var tlsNode = RED.nodes.getNode(n.tls); | ||||
| @@ -105,6 +105,37 @@ in your Node-RED user directory (${RED.settings.userDir}). | ||||
|             timingLog = RED.settings.httpRequestTimingLog; | ||||
|         } | ||||
|  | ||||
|         /** | ||||
|          * Case insensitive header value update util function | ||||
|          * @param {object} headersObject The opt.headers object to update | ||||
|          * @param {string} name The header name | ||||
|          * @param {string} value The header value to set (if blank, header is removed) | ||||
|          */ | ||||
|         const updateHeader = function(headersObject, name, value ) { | ||||
|             const hn = name.toLowerCase(); | ||||
|             const keys = Object.keys(headersObject); | ||||
|             const matchingKeys = keys.filter(e => e.toLowerCase() == hn) | ||||
|             const updateKey = (k,v) => { | ||||
|                 delete headersObject[k]; //delete incase of case change | ||||
|                 if(v) { headersObject[name] = v } //re-add with requested name & value | ||||
|             } | ||||
|             if(matchingKeys.length == 0) { | ||||
|                 updateKey(name, value) | ||||
|             } else { | ||||
|                 matchingKeys.forEach(k => { | ||||
|                     updateKey(k, value); | ||||
|                 }); | ||||
|             } | ||||
|         } | ||||
|         /** | ||||
|          * @param {Object} headersObject | ||||
|          * @param {string} name | ||||
|          * @return {any} value | ||||
|         */ | ||||
|         const getHeaderValue = (headersObject, name) => { | ||||
|             const asLowercase = name.toLowercase(); | ||||
|             return headersObject[Object.keys(headersObject).find(k => k.toLowerCase() === asLowercase)]; | ||||
|         } | ||||
|         this.on("input",function(msg,nodeSend,nodeDone) { | ||||
|             checkNodeAgentPatch(); | ||||
|             //reset redirectList on each request | ||||
| @@ -183,7 +214,6 @@ in your Node-RED user directory (${RED.settings.userDir}). | ||||
|             // TODO: add UI option to auto decompress. Setting to false for 1.x compatibility | ||||
|             opts.decompress = false; | ||||
|             opts.method = method; | ||||
|             opts.headers = {}; | ||||
|             opts.retry = 0; | ||||
|             opts.responseType = 'buffer'; | ||||
|             opts.maxRedirects = 21; | ||||
| @@ -229,34 +259,85 @@ in your Node-RED user directory (${RED.settings.userDir}). | ||||
|                 ] | ||||
|             } | ||||
|  | ||||
|             var ctSet = "Content-Type"; // set default camel case | ||||
|             var clSet = "Content-Length"; | ||||
|             let ctSet = "Content-Type"; // set default camel case | ||||
|             let clSet = "Content-Length"; | ||||
|             const normaliseKnownHeader = function (name) { | ||||
|                 const _name = name.toLowerCase(); | ||||
|                 // only normalise the known headers used later in this | ||||
|                 // function. Otherwise leave them alone. | ||||
|                 switch (_name) { | ||||
|                     case "content-type": | ||||
|                         ctSet = name; | ||||
|                         name = _name; | ||||
|                         break; | ||||
|                     case "content-length": | ||||
|                         clSet = name; | ||||
|                         name = _name; | ||||
|                         break; | ||||
|                 } | ||||
|                 return name; | ||||
|             } | ||||
|  | ||||
|             opts.headers = {}; | ||||
|             //add msg.headers  | ||||
|             //NOTE: ui headers will take precidence over msg.headers | ||||
|             if (msg.headers) { | ||||
|                 if (msg.headers.hasOwnProperty('x-node-red-request-node')) { | ||||
|                     var headerHash = msg.headers['x-node-red-request-node']; | ||||
|                     const headerHash = msg.headers['x-node-red-request-node']; | ||||
|                     delete msg.headers['x-node-red-request-node']; | ||||
|                     var hash = hashSum(msg.headers); | ||||
|                     const hash = hashSum(msg.headers); | ||||
|                     if (hash === headerHash) { | ||||
|                         delete msg.headers; | ||||
|                     } | ||||
|                 } | ||||
|                 if (msg.headers) { | ||||
|                     for (var v in msg.headers) { | ||||
|                         if (msg.headers.hasOwnProperty(v)) { | ||||
|                             var name = v.toLowerCase(); | ||||
|                             if (name !== "content-type" && name !== "content-length") { | ||||
|                                 // only normalise the known headers used later in this | ||||
|                                 // function. Otherwise leave them alone. | ||||
|                                 name = v; | ||||
|                             } | ||||
|                             else if (name === 'content-type') { ctSet = v; } | ||||
|                             else { clSet = v; } | ||||
|                             opts.headers[name] = msg.headers[v]; | ||||
|                         } | ||||
|                     for (let hn in msg.headers) { | ||||
|                         const name = normaliseKnownHeader(hn); | ||||
|                         updateHeader(opts.headers, name, msg.headers[hn]); | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|  | ||||
|             //add/remove/update headers from UI. | ||||
|             if (node.headers.length) { | ||||
|                 for (let index = 0; index < node.headers.length; index++) { | ||||
|                     const header = node.headers[index]; | ||||
|                     let headerName, headerValue; | ||||
|                     if (header.keyType === "other") { | ||||
|                         headerName = header.keyValue | ||||
|                     } else if (header.keyType === "msg") { | ||||
|                         RED.util.evaluateNodeProperty(header.keyValue, header.keyType, node, msg, (err, value) => { | ||||
|                             if (err) { | ||||
|                                 //ignore header | ||||
|                             } else { | ||||
|                                 headerName = value; | ||||
|                             } | ||||
|                         }); | ||||
|                     } else { | ||||
|                         headerName = header.keyType | ||||
|                     } | ||||
|                     if (!headerName) { | ||||
|                         continue; //skip if header name is empyy | ||||
|                     } | ||||
|                     if (header.valueType === "other") { | ||||
|                         headerValue = header.valueValue | ||||
|                     } else if (header.valueType === "msg") { | ||||
|                         RED.util.evaluateNodeProperty(header.valueValue, header.valueType, node, msg, (err, value) => { | ||||
|                             if (err) { | ||||
|                                 //ignore header | ||||
|                             } else { | ||||
|                                 headerValue = value; | ||||
|                             } | ||||
|                         }); | ||||
|                     } else { | ||||
|                         headerValue = header.valueType | ||||
|                     } | ||||
|                     const hn = normaliseKnownHeader(headerName); | ||||
|                     updateHeader(opts.headers, hn, headerValue); | ||||
|                 } | ||||
|             } | ||||
|  | ||||
|  | ||||
|             if (msg.hasOwnProperty('followRedirects')) { | ||||
|                 opts.followRedirect = !!msg.followRedirects; | ||||
|             } | ||||
|   | ||||
| @@ -25,7 +25,7 @@ | ||||
|         <dd>If not configured in the node, this optional property sets the HTTP method of the request. | ||||
|             Must be one of <code>GET</code>, <code>PUT</code>, <code>POST</code>, <code>PATCH</code> or <code>DELETE</code>.</dd> | ||||
|         <dt class="optional">headers <span class="property-type">object</span></dt> | ||||
|         <dd>Sets the HTTP headers of the request.</dd> | ||||
|         <dd>Sets the HTTP headers of the request. NOTE: Any headers set in the UI will overwrite any matching headers in <code>msg.headers</code> </dd> | ||||
|         <dt class="optional">cookies <span class="property-type">object</span></dt> | ||||
|         <dd>If set, can be used to send cookies with the request.</dd> | ||||
|         <dt class="optional">payload</dt> | ||||
|   | ||||
| @@ -1500,6 +1500,50 @@ describe('HTTP Request Node', function() { | ||||
|                 n1.receive({payload:{foo:"bar"}, headers: { 'content-type': 'text/plain', "x-node-red-request-node":"INVALID_SUM"}}); | ||||
|             }); | ||||
|         }); | ||||
|  | ||||
|         it('should use ui headers', function (done) { | ||||
|             const flow = [ | ||||
|                 { | ||||
|                     id: "n1", type: "http request", wires: [["n2"]], method: "GET", ret: "obj", url: getTestURL('/rawHeaders'), | ||||
|                     "headers": [ | ||||
|                         { "keyType": "Accept", "keyValue": "", "valueType": "application/json", "valueValue": "" },//set "Accept" to "application/json" | ||||
|                         { "keyType": "Content-Type", "keyValue": "", "valueType": "application/json", "valueValue": "" },//overwrite msg.headers['content-type'] with UI header 'Content-Type'] | ||||
|                         { "keyType": "msg", "keyValue": "dynamicHeaderName", "valueType": "msg", "valueValue": "dynamicHeaderValue" }, //dynamic msg.dynamicHeaderName/msg.dynamicHeaderValue | ||||
|                         { "keyType": "other", "keyValue": "static-header-name", "valueType": "other", "valueValue": "static-header-value" }, //static "other" header and value | ||||
|                         { "keyType": "Location", "keyValue": "", "valueType": "other", "valueValue": "" }, //delete "Location" header (initially set in msg.headers['location'] by passing empty string for value | ||||
|                     ] | ||||
|                 }, | ||||
|                 { id: "n2", type: "helper" }]; | ||||
|             helper.load(httpRequestNode, flow, function () { | ||||
|                 const n1 = helper.getNode("n1"); | ||||
|                 const n2 = helper.getNode("n2"); | ||||
|                 n2.on("input", function (msg) { | ||||
|                     try { | ||||
|                         msg.should.have.property('statusCode',200); | ||||
|                         msg.payload.should.have.property('headers'); | ||||
|                         //msg.headers['Accept'] should be set by Flow UI | ||||
|                         msg.payload.headers.should.have.property('Accept').which.startWith('application/json'); | ||||
|                         //msg.headers['content-type'] should be updated to 'Content-Type' by the value set in the Flow UI | ||||
|                         msg.payload.headers.should.have.property('Content-Type').which.startWith('application/json'); | ||||
|                         //msg.dynamicHeaderName should be present in headers with the value of msg.dynamicHeaderValue | ||||
|                         msg.payload.headers.should.have.property('dyn-header-name').which.startWith('dyn-header-value'); | ||||
|                         //static (custom) header set in Flow UI should be present  | ||||
|                         msg.payload.headers.should.have.property('static-header-name').which.startWith('static-header-value'); | ||||
|                         //msg.headers['location'] should be deleted because Flow UI "Location" header has a blank value | ||||
|                         //ensures headers with matching characters but different case are eliminated | ||||
|                         msg.payload.headers.should.not.have.property('location'); | ||||
|                         msg.payload.headers.should.not.have.property('Location'); | ||||
|                         done(); | ||||
|                     } catch (err) { | ||||
|                         done(err); | ||||
|                     } | ||||
|                 }); | ||||
|                 // Pass in a headers property with a "content-type" & "location" header | ||||
|                 // Pass in dynamicHeaderName & dynamicHeaderValue properties to set the Flow UI `msg.xxx` header entries | ||||
|                 n1.receive({ payload: { foo: "bar" }, dynamicHeaderName: "dyn-header-name", dynamicHeaderValue: "dyn-header-value", headers: { 'content-type': 'text/plain', 'location': 'london' } }); | ||||
|             }); | ||||
|         }); | ||||
|  | ||||
|     }); | ||||
|  | ||||
|     describe('protocol', function() { | ||||
| @@ -1976,8 +2020,14 @@ describe('HTTP Request Node', function() { | ||||
|  | ||||
|     describe('file-upload', function() { | ||||
|         it('should upload a file', function(done) { | ||||
|             var flow = [{id:'n1',type:'http request',wires:[['n2']],method:'POST',ret:'obj',url:getTestURL('/file-upload')}, | ||||
|                 {id:"n2", type:"helper"}]; | ||||
|             const flow = [ | ||||
|                 { | ||||
|                     id: 'n1', type: 'http request', wires: [['n2']], method: 'POST', ret: 'obj', url: getTestURL('/file-upload'), headers: [ | ||||
|                         { "keyType": "Content-Type", "keyValue": "", "valueType": "multipart/form-data", "valueValue": "" } | ||||
|                     ] | ||||
|                 }, | ||||
|                 { id: "n2", type: "helper" } | ||||
|             ]; | ||||
|             helper.load(httpRequestNode, flow, function() { | ||||
|                 var n1 = helper.getNode("n1"); | ||||
|                 var n2 = helper.getNode("n2"); | ||||
| @@ -1995,9 +2045,6 @@ describe('HTTP Request Node', function() { | ||||
|                     } | ||||
|                 }); | ||||
|                 n1.receive({ | ||||
|                     headers: { | ||||
|                         'content-type':'multipart/form-data' | ||||
|                     }, | ||||
|                     payload: { | ||||
|                         file: { | ||||
|                             value: Buffer.from("Hello World"), | ||||
|   | ||||
		Reference in New Issue
	
	Block a user