@yousolution/node-red-contrib-you-sap-service-layer 0.2.11 → 0.2.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +9 -0
- package/nodes/authenticateSap.html +194 -2
- package/nodes/authenticateSap.js +12 -4
- package/nodes/deleteSap.html +5 -0
- package/nodes/support.js +9 -4
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,15 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to this project will be documented in this file.
|
|
4
4
|
|
|
5
|
+
# [0.2.13] - 2025-11-21
|
|
6
|
+
|
|
7
|
+
- Add Manage Static Additional Header on Nodes from Login Configuration
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
# [0.2.12] - 2025-08-05
|
|
11
|
+
|
|
12
|
+
- Bug Fix on DeleteSAP node for manage Drafts cancelation
|
|
13
|
+
|
|
5
14
|
# [0.2.11] - 2025-07-28
|
|
6
15
|
|
|
7
16
|
- Add Manage NextLink on ServiceNode
|
|
@@ -1,4 +1,88 @@
|
|
|
1
1
|
<script type="text/javascript">
|
|
2
|
+
const headerTypes = [
|
|
3
|
+
{ value: "Accept", label: "Accept", hasValue: false },
|
|
4
|
+
{ value: "Accept-Encoding", label: "Accept-Encoding", hasValue: false },
|
|
5
|
+
{ value: "Accept-Language", label: "Accept-Language", hasValue: false },
|
|
6
|
+
{ value: "Authorization", label: "Authorization", hasValue: false },
|
|
7
|
+
{ value: "Content-Type", label: "Content-Type", hasValue: false },
|
|
8
|
+
{ value: "Cache-Control", label: "Cache-Control", hasValue: false },
|
|
9
|
+
{ value: "User-Agent", label: "User-Agent", hasValue: false },
|
|
10
|
+
{ value: "Location", label: "Location", hasValue: false },
|
|
11
|
+
{ value: "x-tunnel-id", label: "TunnelID", hasValue: false },
|
|
12
|
+
{ value: "other", label: "Other", hasValue: true, icon: "red/images/typedInput/az.svg" }
|
|
13
|
+
//{ value: "msg", label: "msg.", hasValue: true },
|
|
14
|
+
]
|
|
15
|
+
const headerOptions = {};
|
|
16
|
+
const defaultOptions = [
|
|
17
|
+
{ value: "other", label: "Other", hasValue: true, icon: "red/images/typedInput/az.svg" },
|
|
18
|
+
// { value: "msg", label: "msg.", hasValue: true },
|
|
19
|
+
];
|
|
20
|
+
headerOptions["accept"] = [
|
|
21
|
+
{ value: "text/plain", label: "text/plain", hasValue: false },
|
|
22
|
+
{ value: "text/html", label: "text/html", hasValue: false },
|
|
23
|
+
{ value: "application/json", label: "application/json", hasValue: false },
|
|
24
|
+
{ value: "application/xml", label: "application/xml", hasValue: false },
|
|
25
|
+
...defaultOptions,
|
|
26
|
+
];
|
|
27
|
+
headerOptions["accept-encoding"] = [
|
|
28
|
+
{ value: "gzip", label: "gzip", hasValue: false },
|
|
29
|
+
{ value: "deflate", label: "deflate", hasValue: false },
|
|
30
|
+
{ value: "compress", label: "compress", hasValue: false },
|
|
31
|
+
{ value: "br", label: "br", hasValue: false },
|
|
32
|
+
{ value: "gzip, deflate", label: "gzip, deflate", hasValue: false },
|
|
33
|
+
{ value: "gzip, deflate, br", label: "gzip, deflate, br", hasValue: false },
|
|
34
|
+
...defaultOptions,
|
|
35
|
+
];
|
|
36
|
+
headerOptions["accept-language"] = [
|
|
37
|
+
{ value: "*", label: "*", hasValue: false },
|
|
38
|
+
{ value: "en-GB, en-US, en;q=0.9", label: "en-GB, en-US, en;q=0.9", hasValue: false },
|
|
39
|
+
{ 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 },
|
|
40
|
+
{ value: "es-mx,es,en;q=0.5", label: "es-mx,es,en;q=0.5", hasValue: false },
|
|
41
|
+
{ value: "fr-CH, fr;q=0.9, en;q=0.8", label: "fr-CH, fr;q=0.9, en;q=0.8", hasValue: false },
|
|
42
|
+
{ 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 },
|
|
43
|
+
{ value: "ja-JP, jp", label: "ja-JP, jp", hasValue: false },
|
|
44
|
+
...defaultOptions,
|
|
45
|
+
];
|
|
46
|
+
headerOptions["content-type"] = [
|
|
47
|
+
{ value: "text/css", label: "text/css", hasValue: false },
|
|
48
|
+
{ value: "text/plain", label: "text/plain", hasValue: false },
|
|
49
|
+
{ value: "text/html", label: "text/html", hasValue: false },
|
|
50
|
+
{ value: "application/json", label: "application/json", hasValue: false },
|
|
51
|
+
{ value: "application/octet-stream", label: "application/octet-stream", hasValue: false },
|
|
52
|
+
{ value: "application/pdf", label: "application/pdf", hasValue: false },
|
|
53
|
+
{ value: "application/xml", label: "application/xml", hasValue: false },
|
|
54
|
+
{ value: "application/zip", label: "application/zip", hasValue: false },
|
|
55
|
+
{ value: "multipart/form-data", label: "multipart/form-data", hasValue: false },
|
|
56
|
+
{ value: "audio/aac", label: "audio/aac", hasValue: false },
|
|
57
|
+
{ value: "audio/ac3", label: "audio/ac3", hasValue: false },
|
|
58
|
+
{ value: "audio/basic", label: "audio/basic", hasValue: false },
|
|
59
|
+
{ value: "audio/mp4", label: "audio/mp4", hasValue: false },
|
|
60
|
+
{ value: "audio/ogg", label: "audio/ogg", hasValue: false },
|
|
61
|
+
{ value: "image/bmp", label: "image/bmp", hasValue: false },
|
|
62
|
+
{ value: "image/gif", label: "image/gif", hasValue: false },
|
|
63
|
+
{ value: "image/jpeg", label: "image/jpeg", hasValue: false },
|
|
64
|
+
{ value: "image/png", label: "image/png", hasValue: false },
|
|
65
|
+
{ value: "image/tiff", label: "image/tiff", hasValue: false },
|
|
66
|
+
...defaultOptions,
|
|
67
|
+
];
|
|
68
|
+
headerOptions["cache-control"] = [
|
|
69
|
+
{ value: "max-age=0", label: "max-age=0", hasValue: false },
|
|
70
|
+
{ value: "max-age=86400", label: "max-age=86400", hasValue: false },
|
|
71
|
+
{ value: "no-cache", label: "no-cache", hasValue: false },
|
|
72
|
+
...defaultOptions,
|
|
73
|
+
];
|
|
74
|
+
|
|
75
|
+
headerOptions["user-agent"] = [
|
|
76
|
+
{ value: "Mozilla/5.0", label: "Mozilla/5.0", hasValue: false },
|
|
77
|
+
...defaultOptions,
|
|
78
|
+
];
|
|
79
|
+
|
|
80
|
+
function getHeaderOptions(headerName) {
|
|
81
|
+
const lc = (headerName || "").toLowerCase();
|
|
82
|
+
let opts = headerOptions[lc];
|
|
83
|
+
return opts || defaultOptions;
|
|
84
|
+
}
|
|
85
|
+
|
|
2
86
|
RED.nodes.registerType('authenticateSap',{
|
|
3
87
|
category: 'Sap',
|
|
4
88
|
color: '#FFC300',
|
|
@@ -6,7 +90,8 @@
|
|
|
6
90
|
name: {value: ''},
|
|
7
91
|
host: {value: ''},
|
|
8
92
|
port: {value: ''},
|
|
9
|
-
version: {value: ''}
|
|
93
|
+
version: {value: ''},
|
|
94
|
+
headers: { value: [] }
|
|
10
95
|
},
|
|
11
96
|
credentials: {
|
|
12
97
|
company: {type: "string"},
|
|
@@ -33,7 +118,105 @@
|
|
|
33
118
|
types:["msg", "str"],
|
|
34
119
|
typeField: "#node-input-userType"
|
|
35
120
|
});
|
|
36
|
-
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
const node = this;
|
|
124
|
+
const hasMatch = function (arr, value) {
|
|
125
|
+
return arr.some(function (ht) {
|
|
126
|
+
return ht.value === value
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
const headerList = $("#node-input-headers-container").css('min-height', '150px').css('min-width', '450px').editableList({
|
|
130
|
+
addItem: function (container, i, header) {
|
|
131
|
+
const row = $('<div/>').css({
|
|
132
|
+
overflow: 'hidden',
|
|
133
|
+
whiteSpace: 'nowrap',
|
|
134
|
+
display: 'flex'
|
|
135
|
+
}).appendTo(container);
|
|
136
|
+
const propertNameCell = $('<div/>').css({ 'flex-grow': 1 }).appendTo(row);
|
|
137
|
+
const propertyName = $('<input/>', { class: "node-input-header-name", type: "text", style: "width: 100%" })
|
|
138
|
+
.appendTo(propertNameCell)
|
|
139
|
+
.typedInput({ types: headerTypes });
|
|
140
|
+
|
|
141
|
+
const propertyValueCell = $('<div/>').css({ 'flex-grow': 1, 'margin-left': '10px' }).appendTo(row);
|
|
142
|
+
const propertyValue = $('<input/>', { class: "node-input-header-value", type: "text", style: "width: 100%" })
|
|
143
|
+
.appendTo(propertyValueCell)
|
|
144
|
+
.typedInput({
|
|
145
|
+
types: getHeaderOptions(header.keyType)
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
const setup = function(_header) {
|
|
149
|
+
const headerTypeIsAPreset = function(h) {return hasMatch(headerTypes, h) };
|
|
150
|
+
const headerValueIsAPreset = function(h, v) {return hasMatch(getHeaderOptions(h), v) };
|
|
151
|
+
const {keyType, keyValue, valueType, valueValue} = header;
|
|
152
|
+
if(keyType == "msg" || keyType == "other") {
|
|
153
|
+
propertyName.typedInput('type', keyType);
|
|
154
|
+
propertyName.typedInput('value', keyValue);
|
|
155
|
+
} else if (headerTypeIsAPreset(keyType)) {
|
|
156
|
+
propertyName.typedInput('type', keyType);
|
|
157
|
+
} else {
|
|
158
|
+
propertyName.typedInput('type', "other");
|
|
159
|
+
propertyName.typedInput('value', keyValue);
|
|
160
|
+
}
|
|
161
|
+
if(valueType == "msg" || valueType == "other") {
|
|
162
|
+
propertyValue.typedInput('type', valueType);
|
|
163
|
+
propertyValue.typedInput('value', valueValue);
|
|
164
|
+
} else if (headerValueIsAPreset(propertyName.typedInput('type'), valueType)) {
|
|
165
|
+
propertyValue.typedInput('type', valueType);
|
|
166
|
+
} else {
|
|
167
|
+
propertyValue.typedInput('type', "other");
|
|
168
|
+
propertyValue.typedInput('value', valueValue);
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
setup(header);
|
|
172
|
+
|
|
173
|
+
propertyName.on('change', function (event) {
|
|
174
|
+
propertyValue.typedInput('types', getHeaderOptions(propertyName.typedInput('type')));
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
},
|
|
178
|
+
sortable: true,
|
|
179
|
+
removable: true
|
|
180
|
+
});
|
|
181
|
+
if (node.headers) {
|
|
182
|
+
for (let index = 0; index < node.headers.length; index++) {
|
|
183
|
+
const element = node.headers[index];
|
|
184
|
+
headerList.editableList('addItem', node.headers[index]);
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
},
|
|
189
|
+
oneditsave: function() {
|
|
190
|
+
const node = this;
|
|
191
|
+
const headers = $("#node-input-headers-container").editableList('items');
|
|
192
|
+
node.headers = [];
|
|
193
|
+
headers.each(function(i) {
|
|
194
|
+
const header = $(this);
|
|
195
|
+
const keyType = header.find(".node-input-header-name").typedInput('type');
|
|
196
|
+
const keyValue = header.find(".node-input-header-name").typedInput('value');
|
|
197
|
+
const valueType = header.find(".node-input-header-value").typedInput('type');
|
|
198
|
+
const valueValue = header.find(".node-input-header-value").typedInput('value');
|
|
199
|
+
if (keyType !== '' || keyType === 'other' || keyType === 'msg') {
|
|
200
|
+
node.headers.push({
|
|
201
|
+
keyType, keyValue, valueType, valueValue
|
|
202
|
+
})
|
|
203
|
+
}
|
|
204
|
+
});
|
|
205
|
+
},
|
|
206
|
+
oneditresize: function(size) {
|
|
207
|
+
const dlg = $("#dialog-form");
|
|
208
|
+
const expandRow = dlg.find('.node-input-headers-container-row');
|
|
209
|
+
let height = dlg.height() - 5;
|
|
210
|
+
if(expandRow && expandRow.length){
|
|
211
|
+
const siblingRows = dlg.find('> .form-row:not(.node-input-headers-container-row)');
|
|
212
|
+
for (let i = 0; i < siblingRows.size(); i++) {
|
|
213
|
+
const cr = $(siblingRows[i]);
|
|
214
|
+
if(cr.is(":visible"))
|
|
215
|
+
height -= cr.outerHeight(true);
|
|
216
|
+
}
|
|
217
|
+
$("#node-input-headers-container").editableList('height',height);
|
|
218
|
+
}
|
|
219
|
+
}
|
|
37
220
|
});
|
|
38
221
|
</script>
|
|
39
222
|
|
|
@@ -77,6 +260,15 @@
|
|
|
77
260
|
<label for="node-input-password"><i class="fa fa-lock"></i> Password</label>
|
|
78
261
|
<input type="password" id="node-input-password" placeholder="password">
|
|
79
262
|
</div>
|
|
263
|
+
|
|
264
|
+
<div>
|
|
265
|
+
<div class="form-row" style="margin-bottom:0;">
|
|
266
|
+
<label><i class="fa fa-list"></i> <span>Headers</span></label>
|
|
267
|
+
</div>
|
|
268
|
+
<div class="form-row node-input-headers-container-row">
|
|
269
|
+
<ol id="node-input-headers-container"></ol>
|
|
270
|
+
</div>
|
|
271
|
+
</div>
|
|
80
272
|
</script>
|
|
81
273
|
|
|
82
274
|
|
package/nodes/authenticateSap.js
CHANGED
|
@@ -9,12 +9,23 @@ module.exports = function (RED) {
|
|
|
9
9
|
// reset status
|
|
10
10
|
node.status({});
|
|
11
11
|
|
|
12
|
+
let ConfigsHeaders = config.headers ? config.headers.reduce((acc, header) => {
|
|
13
|
+
// Se keyValue è vuoto, usa keyType come chiave
|
|
14
|
+
const key = header.keyValue === "" ? header.keyType : header.keyValue;
|
|
15
|
+
const value = header.valueValue;
|
|
16
|
+
|
|
17
|
+
acc[key] = value;
|
|
18
|
+
return acc;
|
|
19
|
+
}, {}) : {};
|
|
20
|
+
|
|
21
|
+
|
|
12
22
|
const globalContext = node.context().global;
|
|
13
23
|
|
|
14
24
|
globalContext.set(`_YOU_SapServiceLayer_${node.id}`, {
|
|
15
25
|
host: config.host,
|
|
16
26
|
port: config.port,
|
|
17
27
|
version: config.version,
|
|
28
|
+
staticHeaders: ConfigsHeaders,
|
|
18
29
|
credentials: {
|
|
19
30
|
CompanyDB: node.credentials.company,
|
|
20
31
|
UserName: node.credentials.user,
|
|
@@ -50,17 +61,14 @@ module.exports = function (RED) {
|
|
|
50
61
|
|
|
51
62
|
}
|
|
52
63
|
|
|
53
|
-
|
|
54
64
|
//If User setted from msg
|
|
55
65
|
if (node.credentials.userType == 'msg') {
|
|
56
66
|
const user = msg[node.credentials.user];
|
|
57
67
|
let currentUser = globalContext.get(`_YOU_SapServiceLayer_${node.id}.credentials.UserName`);
|
|
58
68
|
|
|
59
69
|
if(user !== currentUser) {
|
|
60
|
-
console.log('Reset User');
|
|
61
70
|
globalContext.set(`_YOU_SapServiceLayer_${node.id}.headers`, null);
|
|
62
71
|
}
|
|
63
|
-
|
|
64
72
|
globalContext.set(`_YOU_SapServiceLayer_${node.id}.credentials.UserName`, user);
|
|
65
73
|
}
|
|
66
74
|
|
|
@@ -89,7 +97,6 @@ module.exports = function (RED) {
|
|
|
89
97
|
validToken = minutesDifference > 25 ? false : true;
|
|
90
98
|
}
|
|
91
99
|
|
|
92
|
-
|
|
93
100
|
if (!headers || !validToken) {
|
|
94
101
|
try {
|
|
95
102
|
const result = await Support.login(node, node.id);
|
|
@@ -132,6 +139,7 @@ module.exports = function (RED) {
|
|
|
132
139
|
user: { type: 'text' },
|
|
133
140
|
userType: { type: 'text' },
|
|
134
141
|
password: { type: 'password' },
|
|
142
|
+
headers: {},
|
|
135
143
|
},
|
|
136
144
|
});
|
|
137
145
|
};
|
package/nodes/deleteSap.html
CHANGED
package/nodes/support.js
CHANGED
|
@@ -72,6 +72,7 @@ async function login(node, idAuth) {
|
|
|
72
72
|
const url = `https://${host}:${port}/b1s/${version}/Login`;
|
|
73
73
|
|
|
74
74
|
const credentials = globalContext.get(`_YOU_SapServiceLayer_${idAuth}.credentials`);
|
|
75
|
+
const staticHeaders = globalContext.get(`_YOU_SapServiceLayer_${idAuth}.staticHeaders`);
|
|
75
76
|
const dataString = JSON.stringify(credentials);
|
|
76
77
|
|
|
77
78
|
const options = {
|
|
@@ -80,6 +81,7 @@ async function login(node, idAuth) {
|
|
|
80
81
|
rejectUnauthorized: false,
|
|
81
82
|
data: credentials,
|
|
82
83
|
headers: {
|
|
84
|
+
...staticHeaders,
|
|
83
85
|
'Content-Type': 'application/json',
|
|
84
86
|
'Content-Length': dataString.length,
|
|
85
87
|
},
|
|
@@ -181,7 +183,7 @@ function generateRequest(node, msg, config, options) {
|
|
|
181
183
|
options.service = options.service || null;
|
|
182
184
|
options.manipulateMethod = options.manipulateMethod || null;
|
|
183
185
|
|
|
184
|
-
const { idAuthNode, host, port, version, cookies } = getSapParams(node, msg, config);
|
|
186
|
+
const { idAuthNode, host, port, version, cookies, staticHeaders } = getSapParams(node, msg, config);
|
|
185
187
|
|
|
186
188
|
let rawQuery = null;
|
|
187
189
|
let url;
|
|
@@ -251,7 +253,9 @@ function generateRequest(node, msg, config, options) {
|
|
|
251
253
|
if (!entityId && config.entity != 'UDO' && config.entity != 'UDT') {
|
|
252
254
|
throw new Error('Missing entityId');
|
|
253
255
|
}
|
|
256
|
+
|
|
254
257
|
const docEntry = msg[config.docEntry];
|
|
258
|
+
|
|
255
259
|
if (config.entity == 'UDO') {
|
|
256
260
|
if (!docEntry) {
|
|
257
261
|
throw new Error('Missing docEntry');
|
|
@@ -326,8 +330,9 @@ function generateRequest(node, msg, config, options) {
|
|
|
326
330
|
}
|
|
327
331
|
|
|
328
332
|
// const cookies = flowContext.get(`_YOU_SapServiceLayer_${idAuthNode}.headers`).join(';');
|
|
329
|
-
const headers = { ...msg[config.headers], Cookie: cookies };
|
|
333
|
+
const headers = { ...staticHeaders , ...msg[config.headers], Cookie: cookies };
|
|
330
334
|
|
|
335
|
+
console.log('Headers:', headers);
|
|
331
336
|
let axiosOptions = {
|
|
332
337
|
method: options.method,
|
|
333
338
|
url: url,
|
|
@@ -354,13 +359,13 @@ function getSapParams(node, msg) {
|
|
|
354
359
|
const host = globalContext.get(`_YOU_SapServiceLayer_${idAuthNode}.host`);
|
|
355
360
|
const port = globalContext.get(`_YOU_SapServiceLayer_${idAuthNode}.port`);
|
|
356
361
|
const version = globalContext.get(`_YOU_SapServiceLayer_${idAuthNode}.version`);
|
|
357
|
-
|
|
362
|
+
const staticHeaders = globalContext.get(`_YOU_SapServiceLayer_${idAuthNode}.staticHeaders`);
|
|
358
363
|
// if (!flowContext.get(`_YOU_SapServiceLayer_${idAuthNode}.headers`)) {
|
|
359
364
|
// throw new Error('Authentication failed');
|
|
360
365
|
// }
|
|
361
366
|
const cookies = globalContext.get(`_YOU_SapServiceLayer_${idAuthNode}.headers`).join(';');
|
|
362
367
|
|
|
363
|
-
return { idAuthNode: idAuthNode, host: host, port: port, version: version, cookies: cookies };
|
|
368
|
+
return { idAuthNode: idAuthNode, host: host, port: port, version: version, cookies: cookies, staticHeaders: staticHeaders };
|
|
364
369
|
} catch (error) {
|
|
365
370
|
throw new Error('Authentication failed');
|
|
366
371
|
}
|