@yousolution/node-red-contrib-you-sap-service-layer 0.0.3 → 0.0.4
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 -4
- package/data/package.json +1 -1
- package/nodes/manipulateEntitySap.html +177 -0
- package/nodes/manipulateEntitySap.js +43 -0
- package/nodes/serviceSap.html +207 -0
- package/nodes/serviceSap.js +41 -0
- package/nodes/support.js +29 -3
- package/package.json +4 -2
- package/resources/entities.json +59 -0
- package/resources/services.json +343 -0
- package/test/manipulateEntitySap.spec.js +191 -0
- package/test/serviceSap.spec.js +170 -0
- package/test/support.spec.js +280 -0
package/CHANGELOG.md
CHANGED
|
@@ -2,12 +2,17 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to this project will be documented in this file.
|
|
4
4
|
|
|
5
|
-
# [0.0.
|
|
5
|
+
# [0.0.4] - 2022-05-12
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
- First public release of unofficial SAP Service Layer for NODE-RED.
|
|
7
|
+
- Add all services of SAP service layer
|
|
8
|
+
- Add manipulate entity
|
|
10
9
|
|
|
11
10
|
# [0.0.3] - 2022-04-15
|
|
12
11
|
|
|
13
12
|
- Refactoring and add unit tests
|
|
13
|
+
|
|
14
|
+
# [0.0.1] - 2021-11-22
|
|
15
|
+
|
|
16
|
+
### First release.
|
|
17
|
+
|
|
18
|
+
- First public release of unofficial SAP Service Layer for NODE-RED.
|
package/data/package.json
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
"dependencies": {
|
|
7
7
|
"@types/node-red": "^1.1.1",
|
|
8
8
|
"@yousolution/node-red-contrib-you-sap-service-layer": "file:yousolution-node-red-contrib-you-sap-service-layer-0.0.3.tgz",
|
|
9
|
-
"@yousolution/node-red-contrib-you-yousolution.cloud": "
|
|
9
|
+
"@yousolution/node-red-contrib-you-yousolution.cloud": "0.0.1",
|
|
10
10
|
"faker": "^6.6.6",
|
|
11
11
|
"node-red-contrib-sse-client": "~0.2.2",
|
|
12
12
|
"node-red-node-email": "~1.14.0"
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
<script type="text/javascript">
|
|
2
|
+
let entities = [];
|
|
3
|
+
let entitiesData = {};
|
|
4
|
+
$.getJSON('entities', (data, status) => {
|
|
5
|
+
for(entity in data) {
|
|
6
|
+
entitiesData = data;
|
|
7
|
+
entities.push(entity);
|
|
8
|
+
}
|
|
9
|
+
});
|
|
10
|
+
RED.nodes.registerType('manipulateEntitySap',{
|
|
11
|
+
category: 'Sap',
|
|
12
|
+
color: '#FFC300',
|
|
13
|
+
defaults: {
|
|
14
|
+
name: {value: ''},
|
|
15
|
+
entity: {value: ''},
|
|
16
|
+
manipulateMethod: {value: ''},
|
|
17
|
+
entityId: {value: ''},
|
|
18
|
+
headers: {value: ''},
|
|
19
|
+
bodyPost: {value: ''}
|
|
20
|
+
},
|
|
21
|
+
inputs:1,
|
|
22
|
+
outputs:1,
|
|
23
|
+
icon: 'font-awesome/fa-pencil-square',
|
|
24
|
+
label: function() {
|
|
25
|
+
return this.name||"Sap manipulate entity";
|
|
26
|
+
},
|
|
27
|
+
oneditprepare: function() {
|
|
28
|
+
|
|
29
|
+
entities.forEach((entity) => {
|
|
30
|
+
$('#node-input-entity')
|
|
31
|
+
.append($("<option></option>")
|
|
32
|
+
.attr("value", entity)
|
|
33
|
+
.text(entity));
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
// set the previous value
|
|
37
|
+
if(this.entity) {
|
|
38
|
+
$('#node-input-entity').val(this.entity);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
$("#node-input-entity").change((event) => {
|
|
43
|
+
const entity = $("#node-input-entity").val();
|
|
44
|
+
|
|
45
|
+
$('#node-input-manipulateMethod').empty();
|
|
46
|
+
|
|
47
|
+
if(entitiesData[entity]) {
|
|
48
|
+
entitiesData[entity].forEach((endpoint) => {
|
|
49
|
+
$('#node-input-manipulateMethod')
|
|
50
|
+
.append($("<option></option>")
|
|
51
|
+
.attr("value", endpoint)
|
|
52
|
+
.text(endpoint));
|
|
53
|
+
});
|
|
54
|
+
// trick check if change with click or not
|
|
55
|
+
if(!event.originalEvent){
|
|
56
|
+
$('#node-input-manipulateMethod').val(this.manipulateMethod);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
$("#node-input-entityId").typedInput({
|
|
63
|
+
type:"msg",
|
|
64
|
+
types:["msg"],
|
|
65
|
+
typeField: "#node-input-entityId-type",
|
|
66
|
+
value: 'entityId'
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
$("#node-input-headers").typedInput({
|
|
70
|
+
type:"msg",
|
|
71
|
+
types:["msg"],
|
|
72
|
+
typeField: "#node-input-headers-type",
|
|
73
|
+
value: 'headers'
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
$("#node-input-bodyPost").typedInput({
|
|
77
|
+
type:"msg",
|
|
78
|
+
types:["msg"],
|
|
79
|
+
typeField: "#node-input-bodyPost-type",
|
|
80
|
+
value: 'bodyPost'
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
}
|
|
85
|
+
});
|
|
86
|
+
</script>
|
|
87
|
+
|
|
88
|
+
<script type="text/html" data-template-name="manipulateEntitySap">
|
|
89
|
+
<div class="form-row">
|
|
90
|
+
<label for="node-input-name"><i class="fa fa-tag"></i> Name</label>
|
|
91
|
+
<input type="text" id="node-input-name" placeholder="Name">
|
|
92
|
+
</div>
|
|
93
|
+
|
|
94
|
+
<div class="form-row">
|
|
95
|
+
<label for="node-input-type"><i class="fa fa-gears"></i> Entity</label>
|
|
96
|
+
<select name="node-input-entity" id="node-input-entity">
|
|
97
|
+
<option></option>
|
|
98
|
+
</select>
|
|
99
|
+
</div>
|
|
100
|
+
|
|
101
|
+
<div class="form-row">
|
|
102
|
+
<label for="node-input-type"><i class="fa fa-gears"></i> Method</label>
|
|
103
|
+
<select name="node-input-manipulateMethod" id="node-input-manipulateMethod">
|
|
104
|
+
</select>
|
|
105
|
+
</div>
|
|
106
|
+
|
|
107
|
+
<div class="form-row" id="container-entityId">
|
|
108
|
+
<label for="node-input-type"><i class="fa fa-comment"></i> EntityId</label>
|
|
109
|
+
<input type="text" id="node-input-entityId">
|
|
110
|
+
<input type="hidden" id="node-input-entityId-type">
|
|
111
|
+
</div>
|
|
112
|
+
|
|
113
|
+
<div class="form-row">
|
|
114
|
+
<label for="node-input-type"><i class="fa fa-comment"></i> Headers</label>
|
|
115
|
+
<input type="text" id="node-input-headers">
|
|
116
|
+
<input type="hidden" id="node-input-headers-type">
|
|
117
|
+
</div>
|
|
118
|
+
|
|
119
|
+
<div class="form-row">
|
|
120
|
+
<label for="node-input-type"><i class="fa fa-comment"></i> BodyPost</label>
|
|
121
|
+
<input type="text" id="node-input-bodyPost">
|
|
122
|
+
<input type="hidden" id="node-input-bodyPost-type">
|
|
123
|
+
</div>
|
|
124
|
+
</script>
|
|
125
|
+
|
|
126
|
+
<!-- Documentation -->
|
|
127
|
+
<script type="text/html" data-help-name="manipulateEntitySap">
|
|
128
|
+
<p>Manipulate action</p>
|
|
129
|
+
|
|
130
|
+
<h3>Inputs</h3>
|
|
131
|
+
<dl class="message-properties">
|
|
132
|
+
<dt>Name
|
|
133
|
+
<span class="property-type">string</span>
|
|
134
|
+
</dt>
|
|
135
|
+
<dd> the node's name </dd>
|
|
136
|
+
<dt>Entity
|
|
137
|
+
<span class="property-type">string</span>
|
|
138
|
+
</dt>
|
|
139
|
+
<dd> the entity name of SAP </dd>
|
|
140
|
+
<dt>entityId
|
|
141
|
+
<span class="property-type">number | string</span>
|
|
142
|
+
</dt>
|
|
143
|
+
<dd> the id of the entity of SAP </dd>
|
|
144
|
+
<dt>bodyPost
|
|
145
|
+
<span class="property-type">object</span>
|
|
146
|
+
</dt>
|
|
147
|
+
<dd> data to update to the entity </dd>
|
|
148
|
+
</dl>
|
|
149
|
+
|
|
150
|
+
<h3>Outputs</h3>
|
|
151
|
+
<ol class="node-ports">
|
|
152
|
+
<li>Standard output
|
|
153
|
+
<dl class="message-properties">
|
|
154
|
+
<dt>payload <span class="property-type">string</span></dt>
|
|
155
|
+
<dd>the standard output of the command.</dd>
|
|
156
|
+
</dl>
|
|
157
|
+
</li>
|
|
158
|
+
</ol>
|
|
159
|
+
|
|
160
|
+
<h3>Details</h3>
|
|
161
|
+
<p>this node is used to update the entity of SAP.
|
|
162
|
+
See the examples to understand how to use it.
|
|
163
|
+
</p>
|
|
164
|
+
<!-- <p><code>msg.payload</code> is used as the payload of the published message.
|
|
165
|
+
If it contains an Object it will be converted to a JSON string before being sent.
|
|
166
|
+
If it contains a binary Buffer the message will be published as-is.</p>
|
|
167
|
+
<p>The topic used can be configured in the node or, if left blank, can be set
|
|
168
|
+
by <code>msg.topic</code>.</p>
|
|
169
|
+
<p>Likewise the QoS and retain values can be configured in the node or, if left
|
|
170
|
+
blank, set by <code>msg.qos</code> and <code>msg.retain</code> respectively.</p> -->
|
|
171
|
+
|
|
172
|
+
<h3>References</h3>
|
|
173
|
+
<ul>
|
|
174
|
+
<li><a href="https://sap-samples.github.io/smb-summit-hackathon/b1sl.html" target="_black">Service layer API docs</a> - for more details </li>
|
|
175
|
+
<li><a href="https://github.com/yousolution-cloud/node-red-contrib-you-sap-service-layer">@yousolution-cloud/node-red-contrib-you-sap-service-layer</a> - the nodes github repository</li>
|
|
176
|
+
</ul>
|
|
177
|
+
</script>
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0';
|
|
2
|
+
const axios = require('axios');
|
|
3
|
+
const Support = require('./support');
|
|
4
|
+
const entities = require('../resources/entities.json');
|
|
5
|
+
|
|
6
|
+
module.exports = function (RED) {
|
|
7
|
+
function ManipulateEntitySap(config) {
|
|
8
|
+
RED.nodes.createNode(this, config);
|
|
9
|
+
const node = this;
|
|
10
|
+
|
|
11
|
+
// reset status
|
|
12
|
+
node.status({});
|
|
13
|
+
|
|
14
|
+
node.on('input', async (msg, send, done) => {
|
|
15
|
+
// reset status
|
|
16
|
+
node.status({});
|
|
17
|
+
try {
|
|
18
|
+
const data = msg[config.bodyPost];
|
|
19
|
+
// if (!data) {
|
|
20
|
+
// node.status({ fill: 'red', shape: 'dot', text: 'bodyPatch must have value' });
|
|
21
|
+
// done(new Error('bodyPatch must have value'));
|
|
22
|
+
// return;
|
|
23
|
+
// }
|
|
24
|
+
const options = { method: 'POST', hasRawQuery: false, hasEntityId: true, isManipulate: true, data: data };
|
|
25
|
+
const login = Support.login;
|
|
26
|
+
const result = await Support.sendRequest({ node, msg, config, axios, login, options });
|
|
27
|
+
msg.payload = result.data;
|
|
28
|
+
msg.statusCode = result.status;
|
|
29
|
+
node.status({ fill: 'green', shape: 'dot', text: 'success' });
|
|
30
|
+
node.send(msg);
|
|
31
|
+
} catch (error) {
|
|
32
|
+
node.status({ fill: 'red', shape: 'dot', text: 'Error' });
|
|
33
|
+
done(error);
|
|
34
|
+
}
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
RED.httpAdmin.get('/entities', RED.auth.needsPermission('manipulateEntitySap.read'), (req, res) => {
|
|
39
|
+
console.log('entities');
|
|
40
|
+
res.json(entities);
|
|
41
|
+
});
|
|
42
|
+
RED.nodes.registerType('manipulateEntitySap', ManipulateEntitySap, {});
|
|
43
|
+
};
|
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
<script type="text/javascript">
|
|
2
|
+
let servicesName = [];
|
|
3
|
+
let servicesData = {};
|
|
4
|
+
$.getJSON('services', (data, status) => {
|
|
5
|
+
for(service in data) {
|
|
6
|
+
servicesData = data;
|
|
7
|
+
servicesName.push(service);
|
|
8
|
+
}
|
|
9
|
+
});
|
|
10
|
+
RED.nodes.registerType('serviceSap',{
|
|
11
|
+
category: 'Sap',
|
|
12
|
+
color: '#FFC300',
|
|
13
|
+
defaults: {
|
|
14
|
+
name: {value: ''},
|
|
15
|
+
serviceName: {value: ''},
|
|
16
|
+
service: {value: ''},
|
|
17
|
+
// entity: {value: ''},
|
|
18
|
+
// udo: {value: ''},
|
|
19
|
+
// udt: {value: ''},
|
|
20
|
+
// entityId: {value: ''},
|
|
21
|
+
// docEntry: {value: ''},
|
|
22
|
+
// code: {value: ''},
|
|
23
|
+
headers: {value: ''},
|
|
24
|
+
bodyPatch: {value: ''}
|
|
25
|
+
},
|
|
26
|
+
inputs:1,
|
|
27
|
+
outputs:1,
|
|
28
|
+
icon: 'font-awesome/fa-gears',
|
|
29
|
+
label: function() {
|
|
30
|
+
return this.name||"Sap service";
|
|
31
|
+
},
|
|
32
|
+
oneditprepare: function() {
|
|
33
|
+
// $("#node-input-entityId").typedInput({
|
|
34
|
+
// type:"msg",
|
|
35
|
+
// types:["msg"],
|
|
36
|
+
// typeField: "#node-input-entityId-type",
|
|
37
|
+
// value: 'entityId'
|
|
38
|
+
// });
|
|
39
|
+
|
|
40
|
+
// $("#node-input-docEntry").typedInput({
|
|
41
|
+
// type:"msg",
|
|
42
|
+
// types:["msg"],
|
|
43
|
+
// typeField: "#node-input-docEntry-type",
|
|
44
|
+
// value: 'docEntry'
|
|
45
|
+
// });
|
|
46
|
+
|
|
47
|
+
// $("#node-input-code").typedInput({
|
|
48
|
+
// type:"msg",
|
|
49
|
+
// types:["msg"],
|
|
50
|
+
// typeField: "#node-input-code-type",
|
|
51
|
+
// value: 'code'
|
|
52
|
+
// });
|
|
53
|
+
|
|
54
|
+
$("#node-input-headers").typedInput({
|
|
55
|
+
type:"msg",
|
|
56
|
+
types:["msg"],
|
|
57
|
+
typeField: "#node-input-headers-type",
|
|
58
|
+
value: 'headers'
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
$("#node-input-bodyPatch").typedInput({
|
|
62
|
+
type:"msg",
|
|
63
|
+
types:["msg"],
|
|
64
|
+
typeField: "#node-input-bodyPatch-type",
|
|
65
|
+
value: 'bodyPatch'
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
// jQuery("#node-input-entity").change(function() {
|
|
69
|
+
// jQuery('#container-udo').hide();
|
|
70
|
+
// jQuery('#container-docEntry').hide();
|
|
71
|
+
// jQuery('#container-udt').hide();
|
|
72
|
+
// jQuery('#container-code').hide();
|
|
73
|
+
// jQuery('#container-entityId').hide();
|
|
74
|
+
|
|
75
|
+
// if (jQuery(this).val() === 'UDO'){
|
|
76
|
+
// jQuery('#container-udo').show();
|
|
77
|
+
// jQuery('#container-docEntry').show();
|
|
78
|
+
// }
|
|
79
|
+
// if (jQuery(this).val() === 'UDT'){
|
|
80
|
+
// jQuery('#container-udt').show();
|
|
81
|
+
// jQuery('#container-code').show();
|
|
82
|
+
// }
|
|
83
|
+
// if(jQuery(this).val() !== 'UDO' && jQuery(this).val() !== 'UDT') {
|
|
84
|
+
// jQuery('#container-entityId').show();
|
|
85
|
+
// }
|
|
86
|
+
// });
|
|
87
|
+
|
|
88
|
+
servicesName.forEach((service) => {
|
|
89
|
+
$('#node-input-serviceName')
|
|
90
|
+
.append($("<option></option>")
|
|
91
|
+
.attr("value", service)
|
|
92
|
+
.text(service));
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
// set the previous value
|
|
96
|
+
if(this.serviceName) {
|
|
97
|
+
$('#node-input-serviceName').val(this.serviceName);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
$("#node-input-serviceName").change((event) => {
|
|
102
|
+
const service = $("#node-input-serviceName").val();
|
|
103
|
+
|
|
104
|
+
$('#node-input-service').empty();
|
|
105
|
+
|
|
106
|
+
if(servicesData[service]) {
|
|
107
|
+
servicesData[service].forEach((endpoint) => {
|
|
108
|
+
$('#node-input-service')
|
|
109
|
+
.append($("<option></option>")
|
|
110
|
+
.attr("value", endpoint)
|
|
111
|
+
.text(endpoint));
|
|
112
|
+
});
|
|
113
|
+
// trick check if change with click or not
|
|
114
|
+
if(!event.originalEvent){
|
|
115
|
+
$('#node-input-service').val(this.service);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
}
|
|
121
|
+
});
|
|
122
|
+
</script>
|
|
123
|
+
|
|
124
|
+
<script type="text/html" data-template-name="serviceSap">
|
|
125
|
+
<div class="form-row">
|
|
126
|
+
<label for="node-input-name"><i class="fa fa-tag"></i> Name</label>
|
|
127
|
+
<input type="text" id="node-input-name" placeholder="Name">
|
|
128
|
+
</div>
|
|
129
|
+
|
|
130
|
+
<div class="form-row">
|
|
131
|
+
<label for="node-input-type"><i class="fa fa-gears"></i> Service</label>
|
|
132
|
+
<select name="node-input-serviceName" id="node-input-serviceName">
|
|
133
|
+
<option></option>
|
|
134
|
+
</select>
|
|
135
|
+
</div>
|
|
136
|
+
|
|
137
|
+
<div class="form-row">
|
|
138
|
+
<label for="node-input-type"><i class="fa fa-gears"></i> Endpoint</label>
|
|
139
|
+
<select name="node-input-service" id="node-input-service">
|
|
140
|
+
</select>
|
|
141
|
+
</div>
|
|
142
|
+
|
|
143
|
+
<div class="form-row">
|
|
144
|
+
<label for="node-input-type"><i class="fa fa-cog"></i> Headers</label>
|
|
145
|
+
<input type="text" id="node-input-headers">
|
|
146
|
+
<input type="hidden" id="node-input-headers-type">
|
|
147
|
+
</div>
|
|
148
|
+
|
|
149
|
+
<div class="form-row">
|
|
150
|
+
<label for="node-input-type"><i class="fa fa-cog"></i> BodyPatch</label>
|
|
151
|
+
<input type="text" id="node-input-bodyPatch">
|
|
152
|
+
<input type="hidden" id="node-input-bodyPatch-type">
|
|
153
|
+
</div>
|
|
154
|
+
</script>
|
|
155
|
+
|
|
156
|
+
<!-- Documentation -->
|
|
157
|
+
<script type="text/html" data-help-name="serviceSap">
|
|
158
|
+
<p>Patch action</p>
|
|
159
|
+
|
|
160
|
+
<h3>Inputs</h3>
|
|
161
|
+
<dl class="message-properties">
|
|
162
|
+
<dt>Name
|
|
163
|
+
<span class="property-type">string</span>
|
|
164
|
+
</dt>
|
|
165
|
+
<dd> the node's name </dd>
|
|
166
|
+
<dt>Entity
|
|
167
|
+
<span class="property-type">string</span>
|
|
168
|
+
</dt>
|
|
169
|
+
<dd> the entity name of SAP </dd>
|
|
170
|
+
<dt>entityId
|
|
171
|
+
<span class="property-type">number | string</span>
|
|
172
|
+
</dt>
|
|
173
|
+
<dd> the id of the entity of SAP </dd>
|
|
174
|
+
<dt>bodyPatch
|
|
175
|
+
<span class="property-type">object</span>
|
|
176
|
+
</dt>
|
|
177
|
+
<dd> data to update to the entity </dd>
|
|
178
|
+
</dl>
|
|
179
|
+
|
|
180
|
+
<h3>Outputs</h3>
|
|
181
|
+
<ol class="node-ports">
|
|
182
|
+
<li>Standard output
|
|
183
|
+
<dl class="message-properties">
|
|
184
|
+
<dt>payload <span class="property-type">string</span></dt>
|
|
185
|
+
<dd>the standard output of the command.</dd>
|
|
186
|
+
</dl>
|
|
187
|
+
</li>
|
|
188
|
+
</ol>
|
|
189
|
+
|
|
190
|
+
<h3>Details</h3>
|
|
191
|
+
<p>this node is used to update the entity of SAP.
|
|
192
|
+
See the examples to understand how to use it.
|
|
193
|
+
</p>
|
|
194
|
+
<!-- <p><code>msg.payload</code> is used as the payload of the published message.
|
|
195
|
+
If it contains an Object it will be converted to a JSON string before being sent.
|
|
196
|
+
If it contains a binary Buffer the message will be published as-is.</p>
|
|
197
|
+
<p>The topic used can be configured in the node or, if left blank, can be set
|
|
198
|
+
by <code>msg.topic</code>.</p>
|
|
199
|
+
<p>Likewise the QoS and retain values can be configured in the node or, if left
|
|
200
|
+
blank, set by <code>msg.qos</code> and <code>msg.retain</code> respectively.</p> -->
|
|
201
|
+
|
|
202
|
+
<h3>References</h3>
|
|
203
|
+
<ul>
|
|
204
|
+
<li><a href="https://sap-samples.github.io/smb-summit-hackathon/b1sl.html" target="_black">Service layer API docs</a> - for more details </li>
|
|
205
|
+
<li><a href="https://github.com/yousolution-cloud/node-red-contrib-you-sap-service-layer">@yousolution-cloud/node-red-contrib-you-sap-service-layer</a> - the nodes github repository</li>
|
|
206
|
+
</ul>
|
|
207
|
+
</script>
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0';
|
|
2
|
+
const axios = require('axios');
|
|
3
|
+
const Support = require('./support');
|
|
4
|
+
const services = require('../resources/services.json');
|
|
5
|
+
|
|
6
|
+
module.exports = function (RED) {
|
|
7
|
+
function ServiceSapNode(config) {
|
|
8
|
+
RED.nodes.createNode(this, config);
|
|
9
|
+
const node = this;
|
|
10
|
+
// reset status
|
|
11
|
+
node.status({});
|
|
12
|
+
|
|
13
|
+
node.on('input', async (msg, send, done) => {
|
|
14
|
+
// reset status
|
|
15
|
+
node.status({});
|
|
16
|
+
try {
|
|
17
|
+
const data = msg[config.bodyPost];
|
|
18
|
+
// if (!data) {
|
|
19
|
+
// node.status({ fill: 'red', shape: 'dot', text: 'bodyPost must have value' });
|
|
20
|
+
// done(new Error('bodyPost must have value'));
|
|
21
|
+
// return;
|
|
22
|
+
// }
|
|
23
|
+
const options = { method: 'POST', hasRawQuery: false, isService: true, data: data };
|
|
24
|
+
const login = Support.login;
|
|
25
|
+
const result = await Support.sendRequest({ node, msg, config, axios, login, options });
|
|
26
|
+
msg.payload = result;
|
|
27
|
+
msg.statusCode = result.status;
|
|
28
|
+
node.status({ fill: 'green', shape: 'dot', text: 'success' });
|
|
29
|
+
node.send(msg);
|
|
30
|
+
} catch (error) {
|
|
31
|
+
node.status({ fill: 'red', shape: 'dot', text: 'Error' });
|
|
32
|
+
done(error);
|
|
33
|
+
}
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
RED.httpAdmin.get('/services', RED.auth.needsPermission('serviceSap.read'), (req, res) => {
|
|
37
|
+
res.json(services);
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
RED.nodes.registerType('serviceSap', ServiceSapNode, {});
|
|
41
|
+
};
|
package/nodes/support.js
CHANGED
|
@@ -148,6 +148,8 @@ function generateRequest(node, msg, config, options) {
|
|
|
148
148
|
hasEntityId: false,
|
|
149
149
|
isClose: false,
|
|
150
150
|
isCrossJoin: false,
|
|
151
|
+
service: null,
|
|
152
|
+
manipulateMethod: null,
|
|
151
153
|
method: 'GET',
|
|
152
154
|
data: null,
|
|
153
155
|
};
|
|
@@ -160,10 +162,15 @@ function generateRequest(node, msg, config, options) {
|
|
|
160
162
|
options.hasEntityId = options.hasEntityId || false;
|
|
161
163
|
options.isClose = options.isClose || false;
|
|
162
164
|
options.isCrossJoin = options.isCrossJoin || false;
|
|
165
|
+
options.isManipulate = options.isManipulate || false;
|
|
166
|
+
options.isService = options.isService || false;
|
|
167
|
+
options.service = options.service || null;
|
|
168
|
+
options.manipulateMethod = options.manipulateMethod || null;
|
|
163
169
|
|
|
164
170
|
const { idAuthNode, host, port, version, cookies } = getSapParams(node, msg, config);
|
|
165
171
|
|
|
166
172
|
let rawQuery = null;
|
|
173
|
+
let url;
|
|
167
174
|
if (options.hasRawQuery) {
|
|
168
175
|
try {
|
|
169
176
|
rawQuery = eval(config.query);
|
|
@@ -173,10 +180,16 @@ function generateRequest(node, msg, config, options) {
|
|
|
173
180
|
}
|
|
174
181
|
|
|
175
182
|
let entity = config.entity;
|
|
176
|
-
if (!entity) {
|
|
183
|
+
if (!entity && !options.isService) {
|
|
177
184
|
throw new Error('Missing entity');
|
|
178
185
|
}
|
|
179
186
|
|
|
187
|
+
if (options.isService) {
|
|
188
|
+
if (!config.service) {
|
|
189
|
+
throw new Error('Missing service');
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
|
|
180
193
|
if (entity == 'UDO') {
|
|
181
194
|
entity = config.udo;
|
|
182
195
|
}
|
|
@@ -191,8 +204,6 @@ function generateRequest(node, msg, config, options) {
|
|
|
191
204
|
url = `https://${host}:${port}/b1s/${version}/${entity}/${partnerName}/${scriptName}`;
|
|
192
205
|
}
|
|
193
206
|
|
|
194
|
-
let url;
|
|
195
|
-
|
|
196
207
|
const odataNextLink = msg[config.nextLink];
|
|
197
208
|
|
|
198
209
|
if (!odataNextLink) {
|
|
@@ -240,6 +251,21 @@ function generateRequest(node, msg, config, options) {
|
|
|
240
251
|
if (options.isClose) {
|
|
241
252
|
url += `/Close`;
|
|
242
253
|
}
|
|
254
|
+
|
|
255
|
+
if (options.isManipulate) {
|
|
256
|
+
if (!config.manipulateMethod) {
|
|
257
|
+
throw new Error('Missing method');
|
|
258
|
+
}
|
|
259
|
+
if (thickIdApi.includes(entity)) {
|
|
260
|
+
url = `https://${host}:${port}/b1s/${version}/${entity}('${entityId}')/${config.manipulateMethod}`;
|
|
261
|
+
} else {
|
|
262
|
+
url = `https://${host}:${port}/b1s/${version}/${entity}(${entityId})/${config.manipulateMethod}`;
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
if (config.service) {
|
|
268
|
+
url = `https://${host}:${port}/b1s/${version}/${config.service}`;
|
|
243
269
|
}
|
|
244
270
|
|
|
245
271
|
if (rawQuery && !odataNextLink) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@yousolution/node-red-contrib-you-sap-service-layer",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.4",
|
|
4
4
|
"description": "Unofficial module SAP Service Layer for NODE-RED",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"scripts": {
|
|
@@ -28,7 +28,9 @@
|
|
|
28
28
|
"patchSap": "/nodes/patchSap.js",
|
|
29
29
|
"closeSap": "/nodes/closeSap.js",
|
|
30
30
|
"crossJoinSap": "/nodes/crossJoinSap.js",
|
|
31
|
-
"nextLink": "/nodes/nextLink.js"
|
|
31
|
+
"nextLink": "/nodes/nextLink.js",
|
|
32
|
+
"serviceSap": "/nodes/serviceSap.js",
|
|
33
|
+
"manipulateEntitySap": "/nodes/manipulateEntitySap.js"
|
|
32
34
|
}
|
|
33
35
|
},
|
|
34
36
|
"dependencies": {
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
{
|
|
2
|
+
"AlertManagementsShow": ["GetAlertManagement", "GetAlertManagementList"],
|
|
3
|
+
"ApprovalStages": ["GetApprovalStage", "RemoveApprovalStage"],
|
|
4
|
+
"ApprovalTemplates": ["GetApprovalTemplate", "RemoveApprovalTemplate"],
|
|
5
|
+
"BankChargesAllocationCodes": ["SetDefaultBankChargesAllocationCode"],
|
|
6
|
+
"BlanketAgreements": ["CancelBlanketAgreement", "GetRelatedDocuments"],
|
|
7
|
+
"BrazilMultiIndexers": ["GetIndexerTypeList"],
|
|
8
|
+
"BrazilNumericIndexers": ["GetIndexerTypeList"],
|
|
9
|
+
"BrazilStringIndexers": ["GetIndexerTypeList"],
|
|
10
|
+
"Campaigns": ["Cancel"],
|
|
11
|
+
"CorrectionInvoice": ["Close", "Cancel", "Reopen", "CreateCancellationDocument"],
|
|
12
|
+
"CorrectionInvoiceReversal": ["Close", "Cancel", "Reopen", "CreateCancellationDocument"],
|
|
13
|
+
"CorrectionPurchaseInvoice": ["Close", "Cancel", "Reopen", "CreateCancellationDocument"],
|
|
14
|
+
"CorrectionPurchaseInvoiceReversal": ["Close", "Cancel", "Reopen", "CreateCancellationDocument"],
|
|
15
|
+
"CreditNotes": ["Close", "Cancel", "Reopen", "CreateCancellationDocument"],
|
|
16
|
+
"DeliveryNotes": ["Close", "Cancel", "Reopen", "CreateCancellationDocument"],
|
|
17
|
+
"Deposits": ["CancelDeposit", "CancelDepositbyCurrentSystemDate"],
|
|
18
|
+
"DownPayments": ["Close", "Cancel", "Reopen", "CreateCancellationDocument"],
|
|
19
|
+
"Drafts": ["Close", "Cancel", "Reopen", "CreateCancellationDocument"],
|
|
20
|
+
"EmployeesInfo": ["Close", "Cancel"],
|
|
21
|
+
"GoodsReturnRequest": ["Close", "Cancel", "Reopen", "SaveDraftToDocument", "CreateCancellationDocument"],
|
|
22
|
+
"IncomingPayments": ["Cancel", "GetApprovalTemplates", "CancelbyCurrentSystemDate"],
|
|
23
|
+
"InventoryCountings": ["Close"],
|
|
24
|
+
"InventoryGenEntries": ["Close", "Cancel", "Reopen", "CreateCancellationDocument"],
|
|
25
|
+
"InventoryGenExits": ["Close", "Cancel", "Reopen", "CreateCancellationDocument"],
|
|
26
|
+
"InventoryTransferRequests": ["Close", "Cancel", "SaveDraftToDocument"],
|
|
27
|
+
"Invoices": ["Close", "Cancel", "Reopen", "CreateCancellationDocument"],
|
|
28
|
+
"Items": ["Cancel"],
|
|
29
|
+
"JournalEntries": ["Cancel"],
|
|
30
|
+
"LandedCosts": ["CloseLandedCost", "CancelLandedCost"],
|
|
31
|
+
"MaterialRevaluation": ["Cancel", "Close"],
|
|
32
|
+
"Messages": ["GetMessage"],
|
|
33
|
+
"Orders": ["Close", "Cancel", "Reopen", "CreateCancellationDocument"],
|
|
34
|
+
"PaymentDrafts": ["Cancel", "SaveDraftToDocument", "GetApprovalTemplates", "CancelbyCurrentSystemDate"],
|
|
35
|
+
"PickLists": ["GetReleasedAllocation"],
|
|
36
|
+
"ProductionOrders": ["Cancel"],
|
|
37
|
+
"ProjectManagements": ["CancelProject"],
|
|
38
|
+
"PurchaseCreditNotes": ["Close", "Cancel", "Reopen", "CreateCancellationDocument"],
|
|
39
|
+
"PurchaseDeliveryNotes": ["Close", "Cancel", "Reopen", "CreateCancellationDocument"],
|
|
40
|
+
"PurchaseDownPayments": ["Close", "Cancel", "Reopen", "CreateCancellationDocument"],
|
|
41
|
+
"PurchaseInvoices": ["Close", "Cancel", "Reopen", "CreateCancellationDocument"],
|
|
42
|
+
"PurchaseOrders": ["Close", "Cancel", "Reopen", "CreateCancellationDocument"],
|
|
43
|
+
"PurchaseQuotations": ["Close", "Cancel", "Reopen", "CreateCancellationDocument"],
|
|
44
|
+
"PurchaseRequests": ["Close", "Cancel", "Reopen", "CreateCancellationDocument"],
|
|
45
|
+
"PurchaseReturns": ["Close", "Cancel", "Reopen", "CreateCancellationDocument"],
|
|
46
|
+
"Quotations": ["Close", "Cancel", "Reopen", "CreateCancellationDocument"],
|
|
47
|
+
"Resources": ["CreateLinkedItem"],
|
|
48
|
+
"ReturnRequest": ["Close", "Cancel", "Reopen", "SaveDraftToDocument", "CreateCancellationDocument"],
|
|
49
|
+
"Returns": ["Close", "Cancel", "Reopen", "CreateCancellationDocument"],
|
|
50
|
+
"SalesOpportunities": ["Close"],
|
|
51
|
+
"ServiceCalls": ["Close"],
|
|
52
|
+
"ServiceContracts": ["Cancel", "Close"],
|
|
53
|
+
"StockTransferDrafts": ["Cancel", "Close", "SaveDraftToDocument"],
|
|
54
|
+
"StockTransfers": ["Cancel", "Close"],
|
|
55
|
+
"TaxInvoiceReport": ["CancelTaxInvoiceReport"],
|
|
56
|
+
"TaxWebSites": ["SetAsDefault"],
|
|
57
|
+
"Users": ["Close"],
|
|
58
|
+
"VendorPayments": ["Cancel", "GetApprovalTemplates", "CancelbyCurrentSystemDate"]
|
|
59
|
+
}
|