node-red-contrib-teamogy-api 0.0.1
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/README.md +99 -0
- package/package.json +27 -0
- package/teamogy-client.html +596 -0
- package/teamogy-client.js +288 -0
package/README.md
ADDED
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
# Teamogy API Node
|
|
2
|
+
|
|
3
|
+
Node for connecting to the Teamogy API.
|
|
4
|
+
|
|
5
|
+
## Connection Configuration
|
|
6
|
+
|
|
7
|
+
You must configure a Connection before using this node.
|
|
8
|
+
|
|
9
|
+
| Parameter | Type | Required | Description |
|
|
10
|
+
|-----------|------|----------|-------------|
|
|
11
|
+
| Name | string | Optional | You can enter a name or it will be generated on first save |
|
|
12
|
+
| Subdomain | string | Required | Enter the name of the subdomain and the domain, in the format: subdomain.domain.com |
|
|
13
|
+
| Unit/Agency | number | Required | Enter the number of unit or agency |
|
|
14
|
+
| Token | string | Required | Enter the generated API token for the given subdomain and Unit/Agency |
|
|
15
|
+
| Req/min | number | Required | Enter the maximum number of API requests per minute for the given subdomain, if the limit is greater than allowed, further requests will be rejected (not processed) |
|
|
16
|
+
|
|
17
|
+
## Input
|
|
18
|
+
|
|
19
|
+
| Parameter | Type | Required | Description |
|
|
20
|
+
|-----------|------|----------|-------------|
|
|
21
|
+
| Name | string | Optional | You can enter a name for the given node (recommended) |
|
|
22
|
+
| Connection | selection - list | Required | Select the configured connection |
|
|
23
|
+
| Request views | checkbox - yes/no | Required | Check for requests for API views (see description below) |
|
|
24
|
+
| Token | selection - list | Required | Select the desired entity |
|
|
25
|
+
| Req/min | selection - list | Required | Select an available method |
|
|
26
|
+
| Source | selection - list | Required | The parameters or even the request body can be static, i.e. from the Params and Body fields in the form, or dynamic as part of the input message |
|
|
27
|
+
| Source of body | string | Required | The parameter set the source of the body, default is msg.payload |
|
|
28
|
+
| Params | string \| object | Optional | You can enter the params in the case of a static request as a string, in the case of a dynamic also as an object, according to the API specification for the given entity |
|
|
29
|
+
| Body | JSON string \| object | Optional | You can enter the body in the case of a static request as a JSON string, in the case of a dynamic also as an object, you can nest objects and arrays in the body without restrictions according to the API specification for the given entity |
|
|
30
|
+
| Merge | checkbox - yes/no | Optional | Check if you want the output message to contain all returned records. In the background, processing is still taking place according to the Paging settings, but individual messages are not sent to the output, but only the final merged one containing all the returned records. (if not specified the value no is used) |
|
|
31
|
+
| Limit | number | Optional | Limitation of the total number of records in the response (if not specified, the value 0 is used, the value 0 means no limit) |
|
|
32
|
+
| Paging | number | Optional | Division of responses into multiple parts with the number of records returned (if not specified, the value 1000 is used) |
|
|
33
|
+
| Offset | number | Optional | Determining from which records to return, e.g. if you have a total of 100 records and want to return records 50-100, set Offset to 50, if you want to return all records leave the option at 0 (if not specified, the value 0 is used) |
|
|
34
|
+
| Skip | boolean | Optional, in message only | Adding the msg.skip=true will allow the message to pass through to the output without processing |
|
|
35
|
+
|
|
36
|
+
## Examples
|
|
37
|
+
|
|
38
|
+
### Static Request Examples
|
|
39
|
+
|
|
40
|
+
Enter the desired values according to the selected entity and method in the Params field. Values for Params are listed on separate lines:
|
|
41
|
+
|
|
42
|
+
```
|
|
43
|
+
id=3
|
|
44
|
+
registration=12345678
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
For API views:
|
|
48
|
+
|
|
49
|
+
```
|
|
50
|
+
columns=firstName,lastName
|
|
51
|
+
externalFilter=id>5
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
For Body field (JSON):
|
|
55
|
+
|
|
56
|
+
```json
|
|
57
|
+
{
|
|
58
|
+
"id": 3,
|
|
59
|
+
"registration": 12345678
|
|
60
|
+
}
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
### Dynamic Request Examples
|
|
64
|
+
|
|
65
|
+
For API views, as string:
|
|
66
|
+
```
|
|
67
|
+
id=2&name=John
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
Or as an object:
|
|
71
|
+
```javascript
|
|
72
|
+
msg.params.id = 2
|
|
73
|
+
msg.params.name = "John"
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
For Body, as JSON string:
|
|
77
|
+
```json
|
|
78
|
+
{"id": 3, "name": "John"}
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
Or as an object:
|
|
82
|
+
```javascript
|
|
83
|
+
msg.body.id = 2
|
|
84
|
+
msg.body.name = "John"
|
|
85
|
+
msg.body.address = addresses // array of objects
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
## Output
|
|
89
|
+
|
|
90
|
+
| Property | Type | Description |
|
|
91
|
+
|----------|------|-------------|
|
|
92
|
+
| payload | string \| object | The standard output or error of the response |
|
|
93
|
+
| count | number | Number of records in the output |
|
|
94
|
+
| msg.* | string \| object | All properties of the input message |
|
|
95
|
+
|
|
96
|
+
## References
|
|
97
|
+
|
|
98
|
+
- [Teamogy Flow docs](https://teamogy.com/teamogy-flow) - full description of Teamogy Flow
|
|
99
|
+
- [Teamogy API docs](https://readme.teamogy.com/reference/integration-options) - full description of parameters for `msg.params` and `msg.body` properties, and also options for API views
|
package/package.json
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "node-red-contrib-teamogy-api",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"description": "",
|
|
5
|
+
"scripts": {
|
|
6
|
+
"test": "echo \"Error: no test specified\" && exit 1"
|
|
7
|
+
},
|
|
8
|
+
"keywords": [
|
|
9
|
+
"node-red",
|
|
10
|
+
"teamogy",
|
|
11
|
+
"api"
|
|
12
|
+
],
|
|
13
|
+
"repository": {
|
|
14
|
+
"type": "git",
|
|
15
|
+
"url": "https://github.com/teamogy-team/node-red-nodes"
|
|
16
|
+
},
|
|
17
|
+
"author": "",
|
|
18
|
+
"license": "",
|
|
19
|
+
"node-red": {
|
|
20
|
+
"nodes": {
|
|
21
|
+
"teamogy-client": "teamogy-client.js"
|
|
22
|
+
}
|
|
23
|
+
},
|
|
24
|
+
"dependencies": {
|
|
25
|
+
"node-fetch": "^3.3.2"
|
|
26
|
+
}
|
|
27
|
+
}
|
|
@@ -0,0 +1,596 @@
|
|
|
1
|
+
<script type="text/javascript">
|
|
2
|
+
let requests = `[
|
|
3
|
+
{"v":"r_companies", "e":"Contacts", "m":["GET","POST","PATCH"]},
|
|
4
|
+
{"v":"r_documents", "e":"Documents", "m":["GET","POST","PATCH"]},
|
|
5
|
+
{"v":"r_jobs", "e":"Jobs", "m":["GET","POST","PATCH"]},
|
|
6
|
+
{"v":"r_tasks", "e":"Tasks", "m":["GET","POST","PATCH"]},
|
|
7
|
+
{"v":"r_time", "e":"Time", "m":["POST"]},
|
|
8
|
+
{"v":"r_users", "e":"Users", "m":["GET","POST"]}
|
|
9
|
+
]`;
|
|
10
|
+
|
|
11
|
+
let values1 = JSON.parse(requests)
|
|
12
|
+
|
|
13
|
+
let views = `[
|
|
14
|
+
{"v":"v_binders", "e":"Binders", "m":["GET"]},
|
|
15
|
+
{"v":"v_brands", "e":"Brands", "m":["GET"]},
|
|
16
|
+
{"v":"v_cashboxes", "e":"Cashboxes", "m":["GET"]},
|
|
17
|
+
{"v":"v_companies", "e":"Companies", "m":["GET"]},
|
|
18
|
+
{"v":"v_companies-client", "e":"Companies - client", "m":["GET"]},
|
|
19
|
+
{"v":"v_companies-supplier", "e":"Companies - supplier", "m":["GET"]},
|
|
20
|
+
{"v":"v_documents", "e":"Documents", "m":["GET"]},
|
|
21
|
+
{"v":"v_documents-order-purchase", "e":"Documents order - purchase", "m":["GET"]},
|
|
22
|
+
{"v":"v_documents-quotation-sales", "e":"Documents quotation - sales", "m":["GET"]},
|
|
23
|
+
{"v":"v_gems", "e":"Gems", "m":["GET"]},
|
|
24
|
+
{"v":"v_groups", "e":"Groups", "m":["GET"]},
|
|
25
|
+
{"v":"v_internals-purchase", "e":"Internals - purchase", "m":["GET"]},
|
|
26
|
+
{"v":"v_internals-sales", "e":"Internals - sales", "m":["GET"]},
|
|
27
|
+
{"v":"v_intervals-absence", "e":"Intervals - absence", "m":["GET"]},
|
|
28
|
+
{"v":"v_intervals-attendance", "e":"Intervals - attendance", "m":["GET"]},
|
|
29
|
+
{"v":"v_intervals-track", "e":"Intervals - track", "m":["GET"]},
|
|
30
|
+
{"v":"v_invoices-purchase", "e":"Invoices - purchase", "m":["GET"]},
|
|
31
|
+
{"v":"v_invoices-sales", "e":"Invoices - sales", "m":["GET"]},
|
|
32
|
+
{"v":"v_jobs", "e":"Jobs", "m":["GET"]},
|
|
33
|
+
{"v":"v_journal", "e":"Journal", "m":["GET"]},
|
|
34
|
+
{"v":"v_overheads-client", "e":"Overheads - client", "m":["GET"]},
|
|
35
|
+
{"v":"v_overheads-internal", "e":"Overheads. - internal", "m":["GET"]},
|
|
36
|
+
{"v":"v_opportunities", "e":"Opportunities", "m":["GET"]},
|
|
37
|
+
{"v":"v_persons", "e":"Persons", "m":["GET"]},
|
|
38
|
+
{"v":"v_tasks", "e":"Tasks", "m":["GET"]},
|
|
39
|
+
{"v":"v_users", "e":"Users", "m":["GET"]}
|
|
40
|
+
]`;
|
|
41
|
+
|
|
42
|
+
let values2 = JSON.parse(views)
|
|
43
|
+
|
|
44
|
+
function decodeJwt(token) {
|
|
45
|
+
try {
|
|
46
|
+
return JSON.parse(atob(token.split(".")[1]));
|
|
47
|
+
} catch (e) {
|
|
48
|
+
return null;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
</script>
|
|
53
|
+
|
|
54
|
+
<script type="text/x-red" data-template-name="teamogy-config">
|
|
55
|
+
|
|
56
|
+
<div class="form-row">
|
|
57
|
+
<label for="node-config-input-name"><i class="fa fa-tag"></i><span>Name</span></label>
|
|
58
|
+
<input type="text" id="node-config-input-name" placeholder="name">
|
|
59
|
+
</div>
|
|
60
|
+
|
|
61
|
+
<div class="form-row">
|
|
62
|
+
<ul style="min-width: 600px; margin-bottom: 20px;" id="config-tabs"></ul>
|
|
63
|
+
</div>
|
|
64
|
+
|
|
65
|
+
<div id="config-tabs-content" style="min-height: 170px;">
|
|
66
|
+
|
|
67
|
+
<div id="config-tab-config" style="display: none;">
|
|
68
|
+
|
|
69
|
+
<div class="form-row">
|
|
70
|
+
<label for="node-config-input-host"><i class="fa fa-server"></i> <span>Domain</span></label>
|
|
71
|
+
<input type="text" id="node-config-input-host" placeholder="subdomain.teamogy.com">
|
|
72
|
+
</div>
|
|
73
|
+
|
|
74
|
+
<div class="form-row">
|
|
75
|
+
<label for="node-config-input-unit"><i class="fa fa-square-o"></i> <span>Unit/Agency</span></label>
|
|
76
|
+
<input type="number" id="node-config-input-unit" min="1" max="100" step="1" placeholder="1" style="width: 20%">
|
|
77
|
+
</div>
|
|
78
|
+
|
|
79
|
+
<div class="form-row">
|
|
80
|
+
<label for="node-config-input-token"><i class="fa fa-lock"></i> <span>Token</span></label>
|
|
81
|
+
<input type="password" id="node-config-input-token" placeholder="token">
|
|
82
|
+
</div>
|
|
83
|
+
|
|
84
|
+
<div class="form-row">
|
|
85
|
+
<label for="node-config-input-tokendata" style="vertical-align: top;"><i class="fa fa-lock"></i> <span>Token data</span></label>
|
|
86
|
+
<label id="node-config-label-tokendata" style="width: 50%"></label>
|
|
87
|
+
<input type="hidden" id="node-config-input-tokendata" placeholder="">
|
|
88
|
+
</div>
|
|
89
|
+
|
|
90
|
+
<div class="form-row">
|
|
91
|
+
<label for="node-config-input-apilimit"><i class="fa fa-step-forward"></i> <span>Req/min</span></label>
|
|
92
|
+
<input type="number" id="node-config-input-apilimit" min="1" max="1000" step="1" placeholder="60" style="width: 20%">
|
|
93
|
+
</div>
|
|
94
|
+
|
|
95
|
+
</div>
|
|
96
|
+
|
|
97
|
+
</div>
|
|
98
|
+
</script>
|
|
99
|
+
|
|
100
|
+
<script type="text/javascript">
|
|
101
|
+
RED.nodes.registerType('teamogy-config', {
|
|
102
|
+
category: "config",
|
|
103
|
+
color: "#fe4c00",
|
|
104
|
+
defaults: {
|
|
105
|
+
name: {
|
|
106
|
+
value: ""
|
|
107
|
+
},
|
|
108
|
+
host: {
|
|
109
|
+
value: "",
|
|
110
|
+
required: true
|
|
111
|
+
},
|
|
112
|
+
unit: {
|
|
113
|
+
value: 1,
|
|
114
|
+
required: true
|
|
115
|
+
},
|
|
116
|
+
tokendata: {
|
|
117
|
+
value: "dfgd",
|
|
118
|
+
required: false
|
|
119
|
+
},
|
|
120
|
+
apilimit: {
|
|
121
|
+
value: 60,
|
|
122
|
+
required: true
|
|
123
|
+
}
|
|
124
|
+
},
|
|
125
|
+
credentials: {
|
|
126
|
+
token: {
|
|
127
|
+
type: "password",
|
|
128
|
+
required: true
|
|
129
|
+
},
|
|
130
|
+
required: true
|
|
131
|
+
},
|
|
132
|
+
label: function() {
|
|
133
|
+
if(!this.name) {
|
|
134
|
+
this.name = this.host + "/" + this.unit;
|
|
135
|
+
}
|
|
136
|
+
return this.name;
|
|
137
|
+
},
|
|
138
|
+
labelStyle: function() {
|
|
139
|
+
return this.name ? "node_label_italic" : "";
|
|
140
|
+
},
|
|
141
|
+
oneditprepare: function() {
|
|
142
|
+
$('#node-config-label-tokendata').html(this.tokendata);
|
|
143
|
+
|
|
144
|
+
$('#node-config-input-token').on('change', function() {
|
|
145
|
+
|
|
146
|
+
if($(this).val() != '__PWRD__') {
|
|
147
|
+
|
|
148
|
+
let pt = decodeJwt($('#node-config-input-token').val())
|
|
149
|
+
|
|
150
|
+
if(pt == null) {
|
|
151
|
+
$('#node-config-label-tokendata').html('Invalid token format')
|
|
152
|
+
$('#node-config-input-tokendata').val('Invalid token format')
|
|
153
|
+
} else {
|
|
154
|
+
pt.iat = new Date(pt.iat * 1000).toISOString()
|
|
155
|
+
|
|
156
|
+
let rt = ''
|
|
157
|
+
$.each(pt, function( key, value ) {
|
|
158
|
+
rt = rt + key + ": " + value + '<br>'
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
$('#node-config-label-tokendata').html(rt)
|
|
162
|
+
$('#node-config-input-tokendata').val(rt)
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
})
|
|
167
|
+
|
|
168
|
+
$('#node-config-input-unit').on('change', function() {
|
|
169
|
+
if(!$.isNumeric($(this).val())) { $(this).val(1) } else {
|
|
170
|
+
if($(this).val() < 1 || $(this).val() > 100 ) { $(this).val(1) }
|
|
171
|
+
}
|
|
172
|
+
})
|
|
173
|
+
|
|
174
|
+
$('#node-config-input-apilimit').on('change', function() {
|
|
175
|
+
if(!$.isNumeric($(this).val())) { $(this).val(60) } else {
|
|
176
|
+
if($(this).val() < 1 || $(this).val() > 1000 ) { $(this).val(60) }
|
|
177
|
+
}
|
|
178
|
+
})
|
|
179
|
+
|
|
180
|
+
const tabs = RED.tabs.create({
|
|
181
|
+
id: "config-tabs",
|
|
182
|
+
onchange: function(tab) {
|
|
183
|
+
$("#config-tabs-content").children().hide();
|
|
184
|
+
$("#" + tab.id).show();
|
|
185
|
+
}
|
|
186
|
+
});
|
|
187
|
+
|
|
188
|
+
tabs.addTab({
|
|
189
|
+
id: "config-tab-config",
|
|
190
|
+
label: "Connection"
|
|
191
|
+
});
|
|
192
|
+
}
|
|
193
|
+
});
|
|
194
|
+
</script>
|
|
195
|
+
|
|
196
|
+
<style type="text/css">
|
|
197
|
+
.dynamic-dropdown-row {
|
|
198
|
+
padding: 6px 0;
|
|
199
|
+
}
|
|
200
|
+
</style>
|
|
201
|
+
|
|
202
|
+
<script type="text/x-red" data-template-name="teamogy-client">
|
|
203
|
+
|
|
204
|
+
<div class="form-row">
|
|
205
|
+
<label for="node-input-name" style="width: 130px"><i class="icon-tag"></i><span>Name</span></label>
|
|
206
|
+
<input type="text" id="node-input-name" placeholder="name" style="width:65%">
|
|
207
|
+
</div>
|
|
208
|
+
|
|
209
|
+
<div class="form-row">
|
|
210
|
+
<ul style="min-width: 600px; margin-bottom: 20px;" id="config-tabs"></ul>
|
|
211
|
+
</div>
|
|
212
|
+
|
|
213
|
+
<div id="config-tabs-content" style="min-height: 170px;">
|
|
214
|
+
|
|
215
|
+
<div id="config-tab-config" style="display: none;">
|
|
216
|
+
|
|
217
|
+
<div class="form-row">
|
|
218
|
+
<label for="node-input-configuration" style="width: 130px"><i class="fa fa-globe"></i><span> Connection</span></label>
|
|
219
|
+
<input type="text" id="node-input-configuration" style="width:65%">
|
|
220
|
+
</div>
|
|
221
|
+
|
|
222
|
+
<div class="form-row">
|
|
223
|
+
<label for="node-input-mode" style="width: 130px"><i class="fa fa-globe"></i> <span>Request views</span></label>
|
|
224
|
+
<input type="checkbox" id="node-input-mode" style=" width:20px;">
|
|
225
|
+
</div>
|
|
226
|
+
|
|
227
|
+
<div class="form-row">
|
|
228
|
+
<label for="node-input-entity" style="width: 130px"><i class="fa fa-caret-down"></i> Select entity</label>
|
|
229
|
+
<select id="node-input-entity" style="width:65%">
|
|
230
|
+
</select>
|
|
231
|
+
</div>
|
|
232
|
+
|
|
233
|
+
<div class="form-row">
|
|
234
|
+
<label for="node-input-method" style="width: 130px"><i class="fa fa-caret-down"></i> Select method</label>
|
|
235
|
+
<select id="node-input-method" style="width:65%">
|
|
236
|
+
</select>
|
|
237
|
+
</div>
|
|
238
|
+
|
|
239
|
+
<div class="form-row">
|
|
240
|
+
<label for="node-input-source" style="width: 130px"><i class="fa fa-caret-down"></i> Source</label>
|
|
241
|
+
<select id="node-input-source" style='width:65%'>
|
|
242
|
+
<option value="static">Static command</option>
|
|
243
|
+
<option value="dynamic">Dynamic from message </option>
|
|
244
|
+
</select>
|
|
245
|
+
</div>
|
|
246
|
+
|
|
247
|
+
<div class="form-row" id="node-row-editor">
|
|
248
|
+
<div class="form-row">
|
|
249
|
+
<label for="node-input-params" style="width: 130px"><i class="fa fa-code"></i> Parameters</label>
|
|
250
|
+
</div>
|
|
251
|
+
<div class="form-row">
|
|
252
|
+
<div style="height: 100px; min-height:100px;" class="node-text-editor" id="node-input-params" style='width:65%'></div>
|
|
253
|
+
</div>
|
|
254
|
+
<div class="form-row">
|
|
255
|
+
<label for="node-input-body" style="width: 130px"><i class="fa fa-code"></i> Body</label>
|
|
256
|
+
</div>
|
|
257
|
+
<div class="form-row">
|
|
258
|
+
<div style="height: 100px; min-height:100px;" class="node-text-editor" id="node-input-body" style='width:65%'></div>
|
|
259
|
+
</div>
|
|
260
|
+
</div>
|
|
261
|
+
|
|
262
|
+
<div class="form-row" id="node-row-response-option">
|
|
263
|
+
<label style="width: 130px"><span>Response option</span></label>
|
|
264
|
+
<div style="margin-bottom: 10px;"></div>
|
|
265
|
+
<div class="form-row">
|
|
266
|
+
<label for="node-input-merge" style="width: 130px"><i class="fa fa-compress"></i> <span> Merge</span></label>
|
|
267
|
+
<input type="checkbox" id="node-input-merge" style=" width:20px;">
|
|
268
|
+
</div>
|
|
269
|
+
<div class="form-row">
|
|
270
|
+
<label for="node-input-limit" style="width: 130px"><i class="fa fa-step-forward"></i><span> Limit</span></label>
|
|
271
|
+
<input type="number" id="node-input-limit" min="0" step="1" style="width:20%">
|
|
272
|
+
</div>
|
|
273
|
+
<div class="form-row">
|
|
274
|
+
<label for="node-input-paging" style="width: 130px"><i class="fa fa-eject"></i><span> Paging</span></label>
|
|
275
|
+
<input type="number" id="node-input-paging" min="0" max="1000" step="1" style="width:20%">
|
|
276
|
+
</div>
|
|
277
|
+
<div class="form-row">
|
|
278
|
+
<label for="node-input-offset" style="width: 130px"><i class="fa fa-forward"></i><span> Offset</span></label>
|
|
279
|
+
<input type="number" id="node-input-offset" min="0" step="1" style="width:20%">
|
|
280
|
+
</div>
|
|
281
|
+
</div>
|
|
282
|
+
|
|
283
|
+
<div class="form-row" id="node-row-editor-dynamic">
|
|
284
|
+
<div class="form-row">
|
|
285
|
+
<label for="node-input-params" style="width: 130px"><i class="fa fa-code"></i> Source of body</label>
|
|
286
|
+
<input type="text" id="node-input-body-source" style="width: 65%">
|
|
287
|
+
<input type="hidden" id="node-input-body-source-type" style="width: 0px">
|
|
288
|
+
</div>
|
|
289
|
+
</div>
|
|
290
|
+
</div>
|
|
291
|
+
</div>
|
|
292
|
+
</script>
|
|
293
|
+
|
|
294
|
+
<script type="text/javascript">
|
|
295
|
+
RED.nodes.registerType('teamogy-client',{
|
|
296
|
+
category: 'TF collection',
|
|
297
|
+
color: '#fe4c00',
|
|
298
|
+
defaults: {
|
|
299
|
+
name: {
|
|
300
|
+
value: ""
|
|
301
|
+
},
|
|
302
|
+
configuration: {
|
|
303
|
+
type: "teamogy-config",
|
|
304
|
+
required: true
|
|
305
|
+
},
|
|
306
|
+
mode: {
|
|
307
|
+
value: false
|
|
308
|
+
},
|
|
309
|
+
method: {
|
|
310
|
+
value: "GET"
|
|
311
|
+
},
|
|
312
|
+
entity: {
|
|
313
|
+
value: "r_users"
|
|
314
|
+
},
|
|
315
|
+
source: {
|
|
316
|
+
value: "static"
|
|
317
|
+
},
|
|
318
|
+
cparams: {
|
|
319
|
+
value: ""
|
|
320
|
+
},
|
|
321
|
+
cbody: {
|
|
322
|
+
value: ""
|
|
323
|
+
},
|
|
324
|
+
bodysource: {
|
|
325
|
+
value: "payload"
|
|
326
|
+
},
|
|
327
|
+
merge: {
|
|
328
|
+
value: true
|
|
329
|
+
},
|
|
330
|
+
limit: {
|
|
331
|
+
value: 0,
|
|
332
|
+
required: true
|
|
333
|
+
},
|
|
334
|
+
paging: {
|
|
335
|
+
value: 1000,
|
|
336
|
+
required: true
|
|
337
|
+
},
|
|
338
|
+
offset: {
|
|
339
|
+
value: 0,
|
|
340
|
+
required: true
|
|
341
|
+
}
|
|
342
|
+
},
|
|
343
|
+
inputs: 1,
|
|
344
|
+
outputs:1,
|
|
345
|
+
icon: "bridge.svg",
|
|
346
|
+
align: "left",
|
|
347
|
+
label: function() {
|
|
348
|
+
return this.name || "teamogy";
|
|
349
|
+
},
|
|
350
|
+
paletteLabel: function() {
|
|
351
|
+
return this.name || "teamogy";
|
|
352
|
+
},
|
|
353
|
+
labelStyle: function() {
|
|
354
|
+
return this.name ? "node_label_italic" : "";
|
|
355
|
+
},
|
|
356
|
+
oneditprepare : function() {
|
|
357
|
+
let values
|
|
358
|
+
let mode = this.mode;
|
|
359
|
+
let entity = this.entity;
|
|
360
|
+
let method = this.method;
|
|
361
|
+
let source = this.source;
|
|
362
|
+
let params = this.cparams;
|
|
363
|
+
let body = this.cbody;
|
|
364
|
+
|
|
365
|
+
$('#node-input-source').val(this.source);
|
|
366
|
+
$('#node-input-merge').val(this.merge);
|
|
367
|
+
$('#node-input-limit').val(this.limit);
|
|
368
|
+
$('#node-input-paging').val(this.paging);
|
|
369
|
+
$('#node-input-offset').val(this.offset);
|
|
370
|
+
|
|
371
|
+
$('#node-input-mode').val(this.mode);
|
|
372
|
+
$('#node-input-body-source').val(this.bodysource);
|
|
373
|
+
|
|
374
|
+
|
|
375
|
+
if(this.mode===true) {
|
|
376
|
+
values = values2;
|
|
377
|
+
} else {
|
|
378
|
+
values = values1;
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
$('#node-input-mode').on('change', function() {
|
|
382
|
+
if ($(this).is(':checked')) {
|
|
383
|
+
values = values2;
|
|
384
|
+
if($('#node-input-source').val()=='static'){ $('#node-row-response-option').show(); }
|
|
385
|
+
} else {
|
|
386
|
+
values = values1;
|
|
387
|
+
$('#node-row-response-option').hide();
|
|
388
|
+
}
|
|
389
|
+
let se = fillEntity (values)
|
|
390
|
+
fillMethod (se)
|
|
391
|
+
})
|
|
392
|
+
|
|
393
|
+
|
|
394
|
+
function fillEntity (values) {
|
|
395
|
+
$('#node-input-entity').empty()
|
|
396
|
+
for (let value of values) {
|
|
397
|
+
$('#node-input-entity').append($("<option></option>").attr("value", value.v).text(value.e));
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
const fe = $('#node-input-entity option[value=' + entity +']').val();
|
|
401
|
+
if(fe != undefined ) {
|
|
402
|
+
$('#node-input-entity').val(entity);
|
|
403
|
+
} else {
|
|
404
|
+
$('#node-input-entity').prop('selectedIndex',0);;
|
|
405
|
+
}
|
|
406
|
+
return $('#node-input-entity').val();
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
function fillMethod (entity) {
|
|
410
|
+
$('#node-input-method').empty()
|
|
411
|
+
for (let value of values) {
|
|
412
|
+
if (value.v === entity) {
|
|
413
|
+
for (let method of value.m) {
|
|
414
|
+
$('#node-input-method').append($("<option></option>").attr("value", method).text(method));
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
const fe = $('#node-input-method option[value=' + method +']').val();
|
|
420
|
+
if(fe != undefined ) {
|
|
421
|
+
$('#node-input-method').val(method);
|
|
422
|
+
} else {
|
|
423
|
+
$('#node-input-method').prop('selectedIndex',0);;
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
$('#node-input-entity').on('change', function() {
|
|
428
|
+
fillMethod ($(this).val())
|
|
429
|
+
})
|
|
430
|
+
|
|
431
|
+
$('#node-input-source').on('change', function() {
|
|
432
|
+
if($(this).val()=='static') {
|
|
433
|
+
$('#node-row-editor').show();
|
|
434
|
+
$('#node-row-editor-dynamic').hide();
|
|
435
|
+
if($('#node-input-mode').is(':checked') == true) {
|
|
436
|
+
$('#node-row-response-option').show();
|
|
437
|
+
}
|
|
438
|
+
} else {
|
|
439
|
+
$('#node-row-editor').hide();
|
|
440
|
+
$('#node-row-editor-dynamic').show();
|
|
441
|
+
$('#node-row-response-option').hide();
|
|
442
|
+
}
|
|
443
|
+
})
|
|
444
|
+
|
|
445
|
+
$("#node-input-body-source").typedInput({
|
|
446
|
+
type:"msg",
|
|
447
|
+
types:["msg"],
|
|
448
|
+
typeField: "#node-input-body-source-type"
|
|
449
|
+
})
|
|
450
|
+
|
|
451
|
+
$('#node-input-limit').on('change', function() {
|
|
452
|
+
if(!$.isNumeric($(this).val())) { $(this).val(0) }
|
|
453
|
+
})
|
|
454
|
+
|
|
455
|
+
$('#node-input-paging').on('change', function() {
|
|
456
|
+
if(!$.isNumeric($(this).val())) { $(this).val(1000) }
|
|
457
|
+
})
|
|
458
|
+
|
|
459
|
+
$('#node-input-offset').on('change', function() {
|
|
460
|
+
if(!$.isNumeric($(this).val())) { $(this).val(0) }
|
|
461
|
+
})
|
|
462
|
+
|
|
463
|
+
this.editorp = RED.editor.createEditor({
|
|
464
|
+
id: 'node-input-params',
|
|
465
|
+
mode: 'ace/mode/text',
|
|
466
|
+
value: this.cparams
|
|
467
|
+
});
|
|
468
|
+
|
|
469
|
+
this.editorb = RED.editor.createEditor({
|
|
470
|
+
id: 'node-input-body',
|
|
471
|
+
mode: 'ace/mode/json',
|
|
472
|
+
value: this.cbody
|
|
473
|
+
});
|
|
474
|
+
|
|
475
|
+
const tabs = RED.tabs.create({
|
|
476
|
+
id: "config-tabs",
|
|
477
|
+
onchange: function(tab) {
|
|
478
|
+
$("#config-tabs-content").children().hide();
|
|
479
|
+
$("#" + tab.id).show();
|
|
480
|
+
}
|
|
481
|
+
});
|
|
482
|
+
|
|
483
|
+
tabs.addTab({
|
|
484
|
+
id: "config-tab-config",
|
|
485
|
+
label: "Configuration"
|
|
486
|
+
});
|
|
487
|
+
},
|
|
488
|
+
oneditsave: function(){
|
|
489
|
+
this.mode = $("#node-input-mode").val();
|
|
490
|
+
this.entity = $("#node-input-entity").val();
|
|
491
|
+
this.method = $("#node-input-method").val();
|
|
492
|
+
this.source = $("#node-input-source").val();
|
|
493
|
+
this.merge = $("#node-input-merge").val();
|
|
494
|
+
this.bodysource = $("#node-input-body-source").val();
|
|
495
|
+
|
|
496
|
+
if(!$("#node-input-limit").val()) {this.limit = 0} else {this.limit = $("#node-input-limit").val();}
|
|
497
|
+
if(!$("#node-input-offset").val()) {this.offset = 0} else {this.offset = $("#node-input-offset").val();}
|
|
498
|
+
this.paging = $("#node-input-paging").val();
|
|
499
|
+
|
|
500
|
+
this.cparams = this.editorp.getValue();
|
|
501
|
+
this.editorp.destroy();
|
|
502
|
+
delete this.editorp;
|
|
503
|
+
|
|
504
|
+
this.cbody = this.editorb.getValue();
|
|
505
|
+
this.editorb.destroy();
|
|
506
|
+
delete this.editorb;
|
|
507
|
+
},
|
|
508
|
+
oneditcancel: function() {
|
|
509
|
+
this.editorp.destroy();
|
|
510
|
+
delete this.editorp;
|
|
511
|
+
|
|
512
|
+
this.editorb.destroy();
|
|
513
|
+
delete this.editorb;
|
|
514
|
+
}
|
|
515
|
+
});
|
|
516
|
+
</script>
|
|
517
|
+
|
|
518
|
+
<script type="text/html" data-help-name="teamogy-client">
|
|
519
|
+
<p>Connect to a Teamogy API.</p>
|
|
520
|
+
|
|
521
|
+
<p>You must configure Connection before using this node.</p>
|
|
522
|
+
|
|
523
|
+
<h3>Connection configuration</h3>
|
|
524
|
+
<dl class="message-properties">
|
|
525
|
+
<dt class="optional">Name (optional)<span class="property-type">string</span></dt><dd> you can enter a name or it will be generated on first save</dd>
|
|
526
|
+
<dt>Subdomain (required)<span class="property-type">string</span></dt><dd> enter the name of the subdomain and the domain, in the format: subdomain.domain.com</dd>
|
|
527
|
+
<dt>Unit/Agency (required)<span class="property-type">number</span></dt><dd> enter the number of unit or agency</dd>
|
|
528
|
+
<dt>Token (required)<span class="property-type">string</span></dt><dd> enter the generated API token for the given subdomain and Unit/Agency</dd>
|
|
529
|
+
<dt>Req/min (required)<span class="property-type">number</span></dt><dd> enter the maximum number of API requests per minute for the given subdomain, if the limit is greater than allowed, further requests will be rejected (not processed)</dd>
|
|
530
|
+
</dl>
|
|
531
|
+
|
|
532
|
+
<h3>Input</h3>
|
|
533
|
+
<dl class="message-properties">
|
|
534
|
+
<dt class="optional">Name (optional)<span class="property-type">string</span></dt><dd> you can enter a name for the given node (recommended)</dd>
|
|
535
|
+
<dt>Connection (required)<span class="property-type">selection - list</span></dt><dd> select the configured connection</dd>
|
|
536
|
+
<dt>Request views (required)<span class="property-type">checkbox - yes/no</span></dt><dd> check for requests for API views (see description below)</dd>
|
|
537
|
+
<dt>Token (required)<span class="property-type">selection - list</span></dt><dd> select the desired entity</dd>
|
|
538
|
+
<dt>Req/min (required)<span class="property-type">selection - list</span></dt><dd> select an available method</dd>
|
|
539
|
+
<dt>Source (required)<span class="property-type">selection - list</span></dt><dd> the parameters or even the request body can be static, i.e. from the Params and Body fields in the form, or dynamic as part of the input message.</dd>
|
|
540
|
+
<dt>Source of body (required)<span class="property-type">string</span></dt><dd> the parameter set the source of the body, default is msg.payload</dd>
|
|
541
|
+
<dt>Params (optional)<span class="property-type">string | object</span></dt><dd> you can enter the params in the case of a static request as a string, in the case of a dynamic also as an object, according to the API specification for the given entity</dd>
|
|
542
|
+
<dd> </dd>
|
|
543
|
+
<dd> Examples:</dd>
|
|
544
|
+
<dd> In the case of a static request, enter the desired values according to the selected entity and method in the Params field. Values for Params are listed on separate lines.</dd>
|
|
545
|
+
<dd> id=3</dd>
|
|
546
|
+
<dd> registration=12345678</dd>
|
|
547
|
+
<dd> </dd>
|
|
548
|
+
<dd> In the case of a static request for API views </dd>
|
|
549
|
+
<dd> columns=firstName,lastName</dd>
|
|
550
|
+
<dd> externalFilter=id>5</dd>
|
|
551
|
+
<dd> </dd>
|
|
552
|
+
<dd> In the case of a dynamic request for API views, as string</dd>
|
|
553
|
+
<dd> id=2&name=John</dd>
|
|
554
|
+
<dd> </dd>
|
|
555
|
+
<dd> or also as an object </dd>
|
|
556
|
+
<dd> msg.params.id = 2</dd>
|
|
557
|
+
<dd> msg.params.name = "John"</dd>
|
|
558
|
+
<dd> </dd>
|
|
559
|
+
<dt>Body (optional)<span class="property-type">JSON string | object</span></dt><dd> you can enter the body in the case of a static request as a JSON string, in the case of a dynamic also as an object, you can nest objects and arrays in the body without restrictions according to the API specification for the given entity</dd>
|
|
560
|
+
<dd> </dd>
|
|
561
|
+
<dd> Examples:</dd>
|
|
562
|
+
<dd> In the case of a static request, enter the desired values according to the selected entity and method in the Body field.</dd>
|
|
563
|
+
<dd> {</dd>
|
|
564
|
+
<dd> "id": 3,</dd>
|
|
565
|
+
<dd> "registration": 12345678</dd>
|
|
566
|
+
<dd> }</dd>
|
|
567
|
+
<dd> </dd>
|
|
568
|
+
<dd> In the case of a dynamic request for API views, as JSON string</dd>
|
|
569
|
+
<dd>{"id": 3,"name": "John"}</dd>
|
|
570
|
+
<dd> </dd>
|
|
571
|
+
<dd> or also as an object </dd>
|
|
572
|
+
<dd> msg.body.id = 2</dd>
|
|
573
|
+
<dd> msg.body.name = "John"</dd>
|
|
574
|
+
<dd> msg.body.address = addresses // array of objects</dd>
|
|
575
|
+
<dd> </dd>
|
|
576
|
+
<dd> If you use requests for API views you can use additional options, the options can also be used in dynamic requests</dd>
|
|
577
|
+
<dt class="optional">Merge (optional)<span class="property-type">checkbox - yes/no</span></dt><dd> check if you want the output message to contain all returned records. In the background, processing is still taking place according to the Paging settings, but individual messages are not sent to the output, but only the final merged one containing all the returned records. (if not specified the value no is used)</dd>
|
|
578
|
+
<dt class="optional">Limit (optional)<span class="property-type">number</span></dt><dd> limitation of the total number of records in the response (if not specified, the value 0 is used, the value 0 means no limit)</dd>
|
|
579
|
+
<dt class="optional">Paging (optional)<span class="property-type">number</span></dt><dd> division of responses into multiple parts with the number of records returned (if not specified, the value 1000 is used)</dd>
|
|
580
|
+
<dt class="optional">Offset (optional)<span class="property-type">number</span></dt><dd> determining from which records to return, e.g. if you have a total of 100 records and want to return records 50-100, set Offset to 50, if you want to return all records leave the option at 0 (if not specified, the value 0 is used)</dd>
|
|
581
|
+
<dt class="optional">Skip (optional, in message only)<span class="property-type">boolean</span></dt><dd> adding the msg.skip=true will allow the message to pass through to the output without processing</dd>
|
|
582
|
+
</dl>
|
|
583
|
+
|
|
584
|
+
<h3>Output</h3>
|
|
585
|
+
<dl class="message-properties">
|
|
586
|
+
<dt>payload<span class="property-type">string | object</span></dt><dd> the standard output or error of the response</dd>
|
|
587
|
+
<dt>count<span class="property-type">number</span></dt><dd> number of records in the output</dd>
|
|
588
|
+
<dt>msg.*<span class="property-type">string | object</span></dt><dd> all properties of the input message</dd>
|
|
589
|
+
</dl>
|
|
590
|
+
|
|
591
|
+
<h3>References</h3>
|
|
592
|
+
<ul>
|
|
593
|
+
<li><a href="https://teamogy.com/teamogy-flow">Teamogy Flow docs</a> - full description of Teamogy Flow</li>
|
|
594
|
+
<li><a href="https://readme.teamogy.com/reference/integration-options">Teamogy API docs</a> - full description of parameters for <code>msg.params</code> and <code>msg.body</code> properties, and also options for API views</li>
|
|
595
|
+
</ul>
|
|
596
|
+
</script>
|
|
@@ -0,0 +1,288 @@
|
|
|
1
|
+
function isEmpty(value) { return (value == null || (typeof value === "string" && value.trim().length === 0)); }
|
|
2
|
+
|
|
3
|
+
module.exports = function(RED) {
|
|
4
|
+
|
|
5
|
+
function ConnectionNode(n) {
|
|
6
|
+
try {
|
|
7
|
+
RED.nodes.createNode(this, n);
|
|
8
|
+
|
|
9
|
+
this.name = n.name;
|
|
10
|
+
this.host = n.host;
|
|
11
|
+
this.unit = n.unit;
|
|
12
|
+
this.apilimit = n.apilimit;
|
|
13
|
+
this.tokendata = n.tokendata;
|
|
14
|
+
if (this.credentials) {
|
|
15
|
+
this.token = this.credentials.token;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
if(typeof this.context().global.get('cache_' + this.host ) == 'undefined') {
|
|
19
|
+
let cache = []
|
|
20
|
+
this.context().global.set('cache_' + this.host, cache)
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
} catch (e) {
|
|
24
|
+
node.error(e);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
RED.nodes.registerType('teamogy-config', ConnectionNode, {
|
|
29
|
+
credentials: {
|
|
30
|
+
token: {type: 'password'}
|
|
31
|
+
}
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
function teamogyClient(data) {
|
|
35
|
+
try {
|
|
36
|
+
var node = this;
|
|
37
|
+
|
|
38
|
+
RED.nodes.createNode(node,data);
|
|
39
|
+
|
|
40
|
+
this.config = RED.nodes.getNode(data.configuration);
|
|
41
|
+
|
|
42
|
+
let token = this.config.credentials.token
|
|
43
|
+
let host = this.config.host
|
|
44
|
+
let unit = this.config.unit
|
|
45
|
+
|
|
46
|
+
let clientid = data.id
|
|
47
|
+
let c = this.context().global.get('cache_' + host)
|
|
48
|
+
let st = null;
|
|
49
|
+
|
|
50
|
+
async function sendmsg(mesg) {
|
|
51
|
+
try {
|
|
52
|
+
const msg = { ... mesg.msg }
|
|
53
|
+
let mparams = ''
|
|
54
|
+
let body = ''
|
|
55
|
+
let mmerge = false
|
|
56
|
+
let mlimit = 0
|
|
57
|
+
let mpaging = 1000
|
|
58
|
+
let moffset = 0
|
|
59
|
+
|
|
60
|
+
if(data.source=='dynamic') {
|
|
61
|
+
if(typeof msg.params == 'string') { mparams = msg.params }
|
|
62
|
+
if(typeof msg.params == 'object') {
|
|
63
|
+
|
|
64
|
+
let pa=Object.entries(msg.params)
|
|
65
|
+
if(pa.length > 0) {
|
|
66
|
+
mparams = pa[0][0] + '=' + pa[0][1]
|
|
67
|
+
|
|
68
|
+
for (let i = 1; i < pa.length; i++) {
|
|
69
|
+
if(pa[i].length > 0) { mparams = mparams + '&' + pa[i][0] + '=' + pa[i][1] }
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
if(typeof msg[data.bodysource] == 'string') { body = msg[data.bodysource] }
|
|
75
|
+
if(typeof msg[data.bodysource] == 'object') { body = JSON.stringify(msg[data.bodysource]) }
|
|
76
|
+
|
|
77
|
+
if(typeof msg.merge == 'boolean') { mmerge = msg.merge }
|
|
78
|
+
if(typeof msg.limit == 'number') { mlimit = msg.limit }
|
|
79
|
+
if(typeof msg.paging == 'number') { mpaging = msg.paging }
|
|
80
|
+
if(typeof msg.offset == 'number') { moffset = msg.offset }
|
|
81
|
+
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
if(data.source=='static') {
|
|
85
|
+
const pa = data.cparams.split(/\r?\n/)
|
|
86
|
+
if(pa.length > 0) {
|
|
87
|
+
mparams = pa[0]
|
|
88
|
+
|
|
89
|
+
for (let i = 1; i < pa.length; i++) {
|
|
90
|
+
if(pa[i].length > 0) { mparams = mparams + '&' + pa[i] }
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
body = data.cbody
|
|
94
|
+
mmerge = data.merge
|
|
95
|
+
mlimit = data.limit
|
|
96
|
+
mpaging = data.paging
|
|
97
|
+
moffset = data.offset
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
const headers = {
|
|
101
|
+
'Authorization': 'Bearer ' + token,
|
|
102
|
+
'Accept': 'application/json',
|
|
103
|
+
'Content-type': 'application/json'
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
let url = `https://${host}/rest/v1/${unit}/`
|
|
107
|
+
|
|
108
|
+
if(data.entity.split('_')[0] == 'v') { url = url + 'views/'}
|
|
109
|
+
|
|
110
|
+
url = url + data.entity.split('_')[1].replaceAll('-','.')
|
|
111
|
+
|
|
112
|
+
if(!isEmpty(mparams)) { url = url + '?' + mparams }
|
|
113
|
+
|
|
114
|
+
const doAsyncJobs = async () => {
|
|
115
|
+
try {
|
|
116
|
+
|
|
117
|
+
let newMsg = JSON.parse(JSON.stringify(mesg.msg));
|
|
118
|
+
|
|
119
|
+
let metadata = {};
|
|
120
|
+
metadata.count = 0
|
|
121
|
+
metadata.limit = 0
|
|
122
|
+
let rdata = [];
|
|
123
|
+
|
|
124
|
+
let method = data.method
|
|
125
|
+
let offset = moffset
|
|
126
|
+
|
|
127
|
+
if(method == 'GET') { body = null }
|
|
128
|
+
|
|
129
|
+
if(data.entity.split('_')[0] == 'v') {
|
|
130
|
+
if(isEmpty(mparams)) { url = url + '?' } else { url = url + '&' }
|
|
131
|
+
|
|
132
|
+
while (offset != null) {
|
|
133
|
+
|
|
134
|
+
if(parseInt(mlimit) == 0) { mlimit = 1000000000}
|
|
135
|
+
if(parseInt(mlimit) < parseInt(mpaging)) { mpaging = mlimit }
|
|
136
|
+
|
|
137
|
+
eurl = encodeURI(url + 'limit=' + mpaging +'&offset=' + offset)
|
|
138
|
+
|
|
139
|
+
const response = await fetch(eurl, {headers, method, body});
|
|
140
|
+
|
|
141
|
+
if(response.status >= 200 && response.status < 300) {
|
|
142
|
+
|
|
143
|
+
const body = await response.json();
|
|
144
|
+
offset = body?.metadata?.nextOffset
|
|
145
|
+
metadata.count = metadata.count + parseInt(body?.metadata?.count)
|
|
146
|
+
metadata.limit = body?.metadata?.limit
|
|
147
|
+
metadata.nextOffset = offset
|
|
148
|
+
|
|
149
|
+
if(mlimit - metadata.count < mpaging) { mpaging = mlimit - metadata.count}
|
|
150
|
+
|
|
151
|
+
if(mmerge == false) {
|
|
152
|
+
newMsg.payload=body;
|
|
153
|
+
newMsg.payload.count=body.data.length;
|
|
154
|
+
node.send(JSON.parse(JSON.stringify(newMsg)));
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
if(mmerge == true) {
|
|
158
|
+
for(let bd of body.data) {
|
|
159
|
+
rdata.push(bd)
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
if(mlimit <= metadata.count) { break; }
|
|
164
|
+
|
|
165
|
+
} else {
|
|
166
|
+
node.error('Response status: ' + response.status);
|
|
167
|
+
node.error('Response status: ' + await response.text());
|
|
168
|
+
break;
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
if(mmerge == true) {
|
|
173
|
+
let body = {}
|
|
174
|
+
metadata.limit = parseInt(data.paging)
|
|
175
|
+
body.metadata = metadata
|
|
176
|
+
body.data = rdata
|
|
177
|
+
body.count = rdata.length
|
|
178
|
+
newMsg.payload = body
|
|
179
|
+
node.send(JSON.parse(JSON.stringify(newMsg)));
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
if(data.entity.split('_')[0] == 'r') {
|
|
184
|
+
|
|
185
|
+
const response = await fetch(encodeURI(url), {headers, method, body});
|
|
186
|
+
|
|
187
|
+
if(response.status >= 200 && response.status < 300) {
|
|
188
|
+
const body = await response.json();
|
|
189
|
+
newMsg.payload = body
|
|
190
|
+
node.send(JSON.parse(JSON.stringify(newMsg)));
|
|
191
|
+
} else {
|
|
192
|
+
let payload = {}
|
|
193
|
+
payload.status = response.status
|
|
194
|
+
payload.text = await response.text()
|
|
195
|
+
|
|
196
|
+
newMsg.payload = payload
|
|
197
|
+
node.send(JSON.parse(JSON.stringify(newMsg)));
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
} catch (e) {
|
|
201
|
+
node.error(e);
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
const r = await doAsyncJobs()
|
|
206
|
+
|
|
207
|
+
} catch (e) {
|
|
208
|
+
node.error(e);
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
function fa(arr,cid) {
|
|
213
|
+
try {
|
|
214
|
+
let na = arr.filter(function (el) {
|
|
215
|
+
return el.clientid == cid
|
|
216
|
+
});
|
|
217
|
+
return na.length
|
|
218
|
+
} catch (e) {
|
|
219
|
+
node.error(e);
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
async function setTimer(host) {
|
|
224
|
+
try {
|
|
225
|
+
if(c.length > 0) {
|
|
226
|
+
let nd = new Date()
|
|
227
|
+
if(nd.getTime() > c[0].stime) {
|
|
228
|
+
if(c[0].clientid == clientid) {
|
|
229
|
+
let m = c.shift(0);
|
|
230
|
+
await sendmsg(m)
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
let fal = fa(c,clientid);
|
|
235
|
+
|
|
236
|
+
if(fal == 0) {
|
|
237
|
+
node.status({fill: "green", shape: "dot", text: fal + " waiting messages"});
|
|
238
|
+
} else {
|
|
239
|
+
node.status({fill: "yellow", shape: "dot", text: fal + " waiting messages"});
|
|
240
|
+
}
|
|
241
|
+
} else {
|
|
242
|
+
clearInterval(st)
|
|
243
|
+
st = null
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
catch (e) {
|
|
247
|
+
node.error(e);
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
node.on('input', async function(msg) {
|
|
252
|
+
try {
|
|
253
|
+
if(st == null){ st = setInterval(function () { setTimer(host) }, 1000)}
|
|
254
|
+
|
|
255
|
+
if(msg.skip != true) {
|
|
256
|
+
let stime = 0
|
|
257
|
+
let nd = new Date()
|
|
258
|
+
|
|
259
|
+
if(c.length > 0) {
|
|
260
|
+
let hstime = c.findLast((el) => el);
|
|
261
|
+
stime = hstime.stime + (60000 / this.config.apilimit)
|
|
262
|
+
} else {
|
|
263
|
+
stime = nd.getTime()
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
let md = {}
|
|
267
|
+
md.msg = msg
|
|
268
|
+
md.host = host
|
|
269
|
+
md.rtime = nd.getTime();
|
|
270
|
+
md.stime = stime
|
|
271
|
+
md.clientid = clientid
|
|
272
|
+
c.push(md)
|
|
273
|
+
} else {
|
|
274
|
+
node.send(msg)
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
catch (e) {
|
|
278
|
+
node.error(e);
|
|
279
|
+
}
|
|
280
|
+
});
|
|
281
|
+
|
|
282
|
+
} catch (e) {
|
|
283
|
+
node.error(e);
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
RED.nodes.registerType("teamogy-client",teamogyClient);
|
|
288
|
+
}
|