@slingr/slingr-services 0.0.5

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/index.js ADDED
@@ -0,0 +1,432 @@
1
+ //////////////////
2
+ // Dependencies //
3
+ //////////////////
4
+ //We load env vars from dotenv if we are in local deployment
5
+ //The file loaded will depend on the NODE_ENV var.If not defined, it dafaults to .env
6
+ if (process.env._local_deployment === undefined) {
7
+ let envPath = process.env.NODE_ENV === undefined ? './.env' : `./.${process.env.NODE_ENV}.env`;
8
+ require('dotenv').config({ path: envPath});
9
+ }
10
+ const
11
+ util = require('util'),
12
+ fs = require('fs'),
13
+ http = require('http'),
14
+ https = require('https'),
15
+ axios = require('axios'),
16
+ compression = require('compression'),
17
+ express = require('express'),
18
+ bodyParser = require('body-parser');
19
+
20
+ //The instance of the service.
21
+ let svc = {
22
+ settings: null,
23
+ hooks: {
24
+ onSvcServicesConfigured: null,
25
+ onConfigurationReady: null,
26
+ onWebServicesReady: null,
27
+ onSvcStart: null,
28
+ onSvcStop: null
29
+ },
30
+ functions: {},
31
+ webServices: {},
32
+ dataStores: null,
33
+ logger: null,
34
+ appLogger: null,
35
+ events: null,
36
+ files: null,
37
+ users: null,
38
+ application: null,
39
+ locks: null,
40
+ httpModule: {}
41
+ };
42
+ let settings,
43
+ serviceConfig,
44
+ definitions,
45
+ logger,
46
+ appLogger,
47
+ events,
48
+ files,
49
+ dataStores,
50
+ users,
51
+ application,
52
+ locks,
53
+ httpModule;
54
+
55
+ ///////////////////////
56
+ // Svc Services //
57
+ ///////////////////////
58
+ const loadSvcServices = async () => {
59
+ logger = require('./services/loggers/logs');
60
+ appLogger = require('./services/loggers/appLogs');
61
+ events = require('./services/events/events');
62
+ files = require('./services/files/files');
63
+ dataStores = require('./services/dataStores/dataStores');
64
+ users = require('./services/users/users');
65
+ application = require('./services/application/application');
66
+ locks = require('./services/locks/locks');
67
+
68
+ httpModule = require('./services/httpModule/httpModule');
69
+
70
+ settings.isUsingProxy = await require('./services/settings/settings').checkProxyStatus();
71
+
72
+ svc.logger = logger;
73
+ svc.appLogger = appLogger;
74
+ svc.events = events;
75
+ svc.files = files;
76
+ svc.dataStores = dataStores;
77
+ svc.users = users;
78
+ svc.application = application;
79
+ svc.locks = locks;
80
+ svc.httpModule = axios;
81
+ }
82
+
83
+ const convertException = (err, code) => {
84
+ if (!err) {
85
+ return {
86
+ __svc_exception__: true,
87
+ message: 'There is an issue on the service',
88
+ error: !code ? { code: 'general', name: 'General exception' } : code
89
+ }
90
+ } else {
91
+ if (typeof err === 'string') {
92
+ return {
93
+ __svc_exception__: true,
94
+ message: err,
95
+ error: !code ? { code: 'general', name: 'General exception' } : code
96
+ }
97
+ } else if (err.__svc_exception__) {
98
+ return err
99
+ } else if (err.message) {
100
+ return {
101
+ __svc_exception__: true,
102
+ message: err.message,
103
+ additionalInfo: err,
104
+ error: !code ? { code: 'general', name: 'General exception' } : code
105
+ }
106
+ } else {
107
+ return {
108
+ __svc_exception__: true,
109
+ message: 'There is an issue on the service',
110
+ additionalInfo: err,
111
+ error: !code ? { code: 'general', name: 'General exception' } : code
112
+ }
113
+ }
114
+ }
115
+ };
116
+
117
+ ///////////////////////////////////////////////////////////////////////////////////////////////////
118
+ // HTTP service: Service API & Webhooks set-up
119
+ ///////////////////////////////////////////////////////////////////////////////////////////////////
120
+ const webServicesServer = express(),
121
+ apiRouter = express.Router(),
122
+ webhookRouter = express.Router();
123
+
124
+ const loadPlatformWebServices = () => {
125
+ let firstLocalDeploymentWarning = false;
126
+ // process functions
127
+ apiRouter.get('/configuration', (req, res) => {
128
+ logger.info("Configuration request");
129
+ let validToken = false;
130
+ if (settings.token === req?.headers?.token) {
131
+ validToken = true
132
+ }
133
+ if (!validToken && localDeployment) {
134
+ // if the service is running in local environment, pass token validation
135
+ if (!firstLocalDeploymentWarning) {
136
+ firstLocalDeploymentWarning = true;
137
+ logger.info("Invalid or empty token on request. Ignored exceptions of this kind because the service is running in local deployment.");
138
+ }
139
+ validToken = true
140
+ }
141
+ let response = null;
142
+ if (validToken) {
143
+ try {
144
+ response = {
145
+ app: settings.applicationName,
146
+ name: settings.svcName,
147
+ env: settings.environment,
148
+ perUser: definitions.configurationType === 'PER_USER',
149
+ configuration: {
150
+ _svc_name: settings.svcName,
151
+ _app_name: settings.applicationName,
152
+ _pod_id: settings.podId,
153
+ _environment: settings.environment,
154
+ _local_deployment: settings.localDeployment,
155
+ _custom_domain: settings.domainCustom,
156
+ _base_domain: settings.domainBase,
157
+ _webservices_port: settings.webServicesPort,
158
+ _debug: settings.debug,
159
+ _token: '-',
160
+ _profile: settings.profile,
161
+ _svcs_services_api: settings.svcsServicesApi,
162
+ _service_config: settings.serviceConfig
163
+ },
164
+ js: '',
165
+ listeners: '',
166
+ functions: [],
167
+ events: []
168
+ };
169
+
170
+ if (!!definitions.configurationHelpUrl) {
171
+ response.configurationHelpUrl = definitions.configurationHelpUrl;
172
+ }
173
+ if (!!definitions.functions) {
174
+ response.functions = definitions.functions;
175
+ }
176
+ if (!!definitions.events) {
177
+ response.events = definitions.events;
178
+ }
179
+ if (!!definitions.configuration) {
180
+ response.conf = definitions.configuration;
181
+ }
182
+ if (!!definitions.userConfiguration) {
183
+ response.userConf = definitions.userConfiguration;
184
+ }
185
+ if (!!definitions.userConfigurationButtons) {
186
+ response.userConfButtons = definitions.userConfigurationButtons;
187
+ }
188
+ if (!!definitions.listeners) {
189
+ let listeners = '';
190
+ for (let i in definitions.listeners) {
191
+ try {
192
+ let fileContent = fs.readFileSync('./listeners/' + definitions.listeners[i], 'utf8');
193
+ if (fileContent) {
194
+ listeners += '\n/* */\n';
195
+ listeners += fileContent;
196
+ listeners += '\n/* */\n';
197
+ }
198
+ } catch (err) {
199
+ logger.warn('Listeners file [' + definitions.listeners[i] + '] can not be read: ' + convertException(err));
200
+ }
201
+ }
202
+ response.listeners = listeners;
203
+ }
204
+ } catch (err) {
205
+ if (err && err.message) {
206
+ response = convertException(err.message)
207
+ } else {
208
+ err = util.inspect(err, { showHidden: true, depth: null });
209
+ if (err && err.startsWith("'") && err.endsWith("'")) {
210
+ err = err.substring(1, err.length - 1);
211
+ }
212
+ response = convertException(err)
213
+ }
214
+ }
215
+ if (!response) {
216
+ response = convertException("Empty service response")
217
+ }
218
+ logger.info("Configuration response from service");
219
+ res.send(response);
220
+ } else {
221
+ logger.info("Invalid token when try to get configuration");
222
+ res.status(401).send('Invalid token')
223
+ }
224
+ });
225
+ //add await on function if async
226
+ apiRouter.post('/function', async (req, res, next) => {
227
+
228
+ firstLocalDeploymentWarning = false;
229
+ let validToken = false;
230
+ if (req?.headers?.token === settings.token) {
231
+ validToken = true
232
+ }
233
+ if (!validToken && settings.localDeployment) {
234
+ // if the service is running in local environment, pass token validation
235
+ if (!firstLocalDeploymentWarning) {
236
+ firstLocalDeploymentWarning = true;
237
+ logger.warn("Invalid or empty token on request. Ignored exceptions of this kind because the service is running in local deployment.");
238
+ }
239
+ validToken = true
240
+ }
241
+ let response = null;
242
+ let responseCode = 200;
243
+ if (!validToken) {
244
+ logger.info("Invalid token when try to get configuration");
245
+ res.status(401).send('Invalid token')
246
+ } else {
247
+ let body = req.body || {};
248
+ let functionName = body.function;
249
+
250
+ if (!functionName) {
251
+ response = convertException('Empty function name', { code: 'argumentException', name: 'Argument invalid' });
252
+ responseCode = 404;
253
+ } else {
254
+ if (svc.functions[functionName]) {
255
+ try {
256
+ if (body.params?.__request_params__) body.params = body.params.__request_params__;
257
+ response = await svc.functions[functionName](body);
258
+ } catch (err) {
259
+ if (!!err && !!err.message) {
260
+ if (!!err.__svc_exception__) {
261
+ response = convertException(err)
262
+ } else {
263
+ response = convertException(err.message)
264
+ }
265
+ } else {
266
+ err = util.inspect(err, { showHidden: true, depth: null });
267
+ if (!!err && err.startsWith("'") && err.endsWith("'")) {
268
+ err = err.substring(1, err.length - 1);
269
+ }
270
+ response = convertException(err);
271
+ }
272
+ responseCode = 500;
273
+ }
274
+ if (!response) {
275
+ response = convertException("Empty service response");
276
+ responseCode = 400;
277
+ }
278
+
279
+ res.status(responseCode).send({
280
+ date: +new Date(),
281
+ data: response
282
+ });
283
+
284
+ } else {
285
+ next(new Error('Cannot find function [' + functionName + '] on service [' + svcName + ']'));
286
+ }
287
+
288
+ }
289
+ }
290
+ });
291
+
292
+ apiRouter.get('/system/alive', (req, res) => {
293
+ validToken = false;
294
+ if (req?.headers?.token === settings.token) {
295
+ validToken = true;
296
+ }
297
+ if (!validToken && settings.localDeployment) {
298
+ // if the service is running in local environment, pass token validation
299
+ if (!firstLocalDeploymentWarning) {
300
+ firstLocalDeploymentWarning = true;
301
+ logger.warn("Invalid or empty token on request. Ignored exceptions of this kind because the service is running in local deployment.");
302
+ }
303
+ validToken = true;
304
+ }
305
+ if (!validToken) {
306
+ logger.info("Invalid token when try to check health");
307
+ res.status(401).send('Invalid token');
308
+ } else {
309
+ res.send({ started: true });
310
+ }
311
+ });
312
+ }
313
+
314
+ const loadDeveloperWebServices = () => {
315
+
316
+ for (const webService in svc.webServices) {
317
+ if (svc.webServices.hasOwnProperty(webService)) {
318
+ let currentWebService = svc.webServices[webService]
319
+ let webServiceMethod = currentWebService.method.toUpperCase();
320
+ let webServicePath = currentWebService.path;
321
+ let webServiceHandler = currentWebService.handler;
322
+ if (webServiceMethod === '') throw new Error('Method for webhook must not be empty. Please review the webhooks provided.')
323
+ switch (webServiceMethod) {
324
+ case 'GET':
325
+ webhookRouter.get(webServicePath, webServiceHandler);
326
+ break;
327
+ case 'PUT':
328
+ webhookRouter.put(webServicePath, webServiceHandler);
329
+ break;
330
+ case 'PATCH':
331
+ webhookRouter.patch(webServicePath, webServiceHandler);
332
+ break;
333
+ case 'POST':
334
+ webhookRouter.post(webServicePath, webServiceHandler);
335
+ break;
336
+ case 'DELETE':
337
+ webhookRouter.delete(webServicePath, webServiceHandler);
338
+ break;
339
+ case 'HEAD':
340
+ webhookRouter.head(webServicePath, webServiceHandler);
341
+ break;
342
+ case 'OPTIONS':
343
+ webhookRouter.options(webServicePath, webServiceHandler);
344
+ break;
345
+ case 'CONNECT':
346
+ webhookRouter.connect(webServicePath, webServiceHandler);
347
+ break;
348
+ case 'TRACE':
349
+ webhookRouter.trace(webServicePath, webServiceHandler);
350
+ break;
351
+ default:
352
+ break;
353
+ }
354
+ }
355
+ }
356
+
357
+ };
358
+
359
+ const loadSvcWebServices = () => {
360
+
361
+ loadPlatformWebServices();
362
+ loadDeveloperWebServices();
363
+
364
+ webServicesServer.use(compression());
365
+ webServicesServer.use(bodyParser.urlencoded({ extended: true })); // configure app to use bodyParser()
366
+ webServicesServer.use(bodyParser.json()); // this will let us get the data from a POST
367
+ webServicesServer.use('/api', apiRouter); // all of our routes will be prefixed with /api
368
+ webServicesServer.use('/', webhookRouter); // all of our routes will be prefixed with nothing
369
+
370
+ if (settings.useSsl) {
371
+ // start https service
372
+ const sslCredentials = {
373
+ key: settings.sslKey,
374
+ cert: settings.sslCert
375
+ };
376
+ https.createServer(sslCredentials, webServicesServer).listen(settings.webServicesPort, function () {
377
+ logger.info('Https service ready on port [' + settings.webServicesPort + ']');
378
+ });
379
+ } else {
380
+ // start http service
381
+ http.createServer(webServicesServer).listen(settings.webServicesPort, function () {
382
+ logger.info('Http service ready on port [' + settings.webServicesPort + ']');
383
+ });
384
+ }
385
+
386
+ }
387
+
388
+ const startSvc = async () => {
389
+
390
+ ; ({ settings, serviceConfig: serviceConfig, definitions } = require('./services/configuration/configuration'));
391
+
392
+ svc.settings = settings;
393
+ svc.serviceConfig = serviceConfig;
394
+ svc.definitions = definitions;
395
+ if (svc.hooks.onConfigurationReady) hooks.onConfigurationReady();
396
+
397
+ await loadSvcServices();
398
+ if (svc.hooks.onSvcServicesConfigured) hooks.onSvcServicesConfigured();
399
+
400
+ loadSvcWebServices();
401
+ if (svc.hooks.onWebServicesReady) hooks.onWebServicesReady();
402
+
403
+ ///////////////////////////////////////////////////////////////////////////////////////////////////
404
+ // node.js related events
405
+ ///////////////////////////////////////////////////////////////////////////////////////////////////
406
+
407
+ process.on('exit', code => logger.info('Service stopped - exit code [' + code + ']'));
408
+ process.on('SIGINT', () => {
409
+ logger.info('Service stopped');
410
+ process.exit(0)
411
+ });
412
+ process.on('beforeExit', code => {
413
+ hooks.onSvcStop(code);
414
+ logger.info('Stopping service - exit code [' + code + ']')
415
+ });
416
+ process.on('warning', warning => logger.warn('Warning [' + util.inspect(warning, { showHidden: true, depth: null }) + ']'));
417
+
418
+ appLogger.info('Service [' + settings.svcName + '] is being initialized');
419
+
420
+ logger.info(">>> Memory initial usage: " + util.inspect(process.memoryUsage(), { showHidden: true, depth: null }));
421
+
422
+ ///////////////////////////////////////////////////////////////////////////////////////////////////
423
+ // Service Started
424
+ ///////////////////////////////////////////////////////////////////////////////////////////////////
425
+ appLogger.info('Service [' + settings.svcName + '] started');
426
+ svc.hooks.onSvcStart();
427
+
428
+ if (settings.isUsingProxy) require('./services/settings/settings').refreshCache();
429
+ };
430
+ svc.start = startSvc;
431
+
432
+ module.exports = svc;
package/package.json ADDED
@@ -0,0 +1,32 @@
1
+ {
2
+ "name": "@slingr/slingr-services",
3
+ "version": "0.0.5",
4
+ "description": "Nodejs SDK to create Slingr Services",
5
+ "main": "index.js",
6
+ "repository": {
7
+ "type": "git",
8
+ "url": "https://github.com/slingr-stack/platform-sdk-services/tree/master/service-nodejs-sdk"
9
+ },
10
+ "keywords": [
11
+ "slingr",
12
+ "nodejs",
13
+ "services",
14
+ "framework",
15
+ "sdk"
16
+ ],
17
+ "dependencies": {
18
+ "axios": "^0.26.1",
19
+ "axios-retry": "^3.2.4",
20
+ "body-parser": "^1.17.2",
21
+ "compression": "^1.7.0",
22
+ "dayjs": "^1.11.0",
23
+ "dotenv": "^16.0.1",
24
+ "express": "^4.15.3",
25
+ "form-data": "^4.0.0",
26
+ "locks": "^0.2.2",
27
+ "moment": "^2.18.1",
28
+ "util": "^0.10.3"
29
+ },
30
+ "author": "Slingr",
31
+ "license": "MIT"
32
+ }
@@ -0,0 +1,49 @@
1
+ const logger = require('../loggers/logs');
2
+ const httpModule = require('../httpModule/httpModule');
3
+
4
+ const getUserInformationByToken = async (token) => {
5
+ if(!token) {
6
+ throw new Error('User token can\'t be empty.');
7
+ }
8
+ let userInfo;
9
+ try {
10
+ userInfo = await httpModule.get('/users',{
11
+ params: {
12
+ userToken: token
13
+ }
14
+ });
15
+ } catch (error) {
16
+ logger.error('Error while triying to get information by token for token ['+token+'].')
17
+ throw new Error('Error while triying to get information by token for token ['+token+'].')
18
+ }
19
+ if (!userInfo) {
20
+ logger.error('No user found with token ['+token+']');
21
+ throw new Error('No user found with token ['+token+']');
22
+ }
23
+ }
24
+
25
+ const getUserInformationByEmail = async (email) => {
26
+ if(!email) {
27
+ throw new Error('User email can\'t be empty.');
28
+ }
29
+ let userInfo;
30
+ try {
31
+ userInfo = await httpModule.get('/users',{
32
+ params: {
33
+ userEmail: email
34
+ }
35
+ });
36
+ } catch (error) {
37
+ logger.error('Error while triying to get information by email for email ['+email+'].')
38
+ throw new Error('Error while triying to get information by email for email ['+email+'].')
39
+ }
40
+ if (!userInfo) {
41
+ logger.error('No user found with email ['+email+']');
42
+ throw new Error('No user found with email ['+email+']');
43
+ }
44
+ }
45
+
46
+ module.exports = {
47
+ getUserInformationByToken,
48
+ getUserInformationByEmail,
49
+ }
@@ -0,0 +1,171 @@
1
+ const fs = require('fs');
2
+ //////////////////////////////////////
3
+ // Load Configuration from env vars //
4
+ //////////////////////////////////////
5
+ const {
6
+ // Service constants
7
+ _svc_name: svcName,
8
+ _app_name: applicationName,
9
+ _pod_id: _podId,
10
+ _environment: environment,
11
+ _local_deployment: _localDeployment,
12
+ _debug: _debug,
13
+ // HTTP services properties
14
+ _webservices_port: webServicesPort,
15
+ _token: token,
16
+ _profile: profile,
17
+ _extension_broker_api: _extension_broker_api,
18
+ _useSsl: _useSsl,
19
+ SSL_KEY: sslKey,
20
+ SSL_CERT: sslCert,
21
+ // System properties
22
+ _custom_domain: domainCustom,
23
+ _base_domain: domainBase,
24
+ // Service specific properties
25
+ _service_config: _service_config
26
+ } = { ...process.env };
27
+
28
+
29
+ const podId = _podId.length > 5 ? _podId.substring(_podId.length - 5) : _podId;
30
+ const localDeployment = _localDeployment !== 'false' && !!_localDeployment;
31
+ const debug = _debug !== 'false' && !!_debug;
32
+ const useSsl = !localDeployment || _useSsl;
33
+ const serviceConfig = JSON.parse(_service_config);
34
+
35
+ const maskToken = token => {
36
+ if (!token) {
37
+ return '-';
38
+ }
39
+ return token.length < 10 ? '.'.repeat(token.length) :
40
+ token.length < 20 ? token.substr(0, 2) + '.'.repeat(token.length - 4) + token.substr(token.length - 2) :
41
+ token.substr(0, 4) + '.'.repeat(token.length - 8) + token.substr(token.length - 4)
42
+ };
43
+
44
+ const maskedToken = maskToken(token);
45
+
46
+ let cDomain = domainCustom;
47
+ if (cDomain) {
48
+ cDomain = (localDeployment ? 'http' : 'https') + '://' + cDomain
49
+ } else {
50
+ cDomain = (localDeployment ? 'http' : 'https') + '://' + applicationName + '.' + domainBase + '/' + environment
51
+ }
52
+ const domain = cDomain.toLowerCase();
53
+ const secondaryDomain = ((localDeployment ? 'http' : 'https') + '://' + domainBase + '/' + applicationName + '/' + environment).toLowerCase();
54
+
55
+ const webhookUrl = domain + '/services/' + svcName;
56
+
57
+ const proto = useSsl ? 'https' : 'http';
58
+ console.log('Configured service [' + svcName + ']: ' +
59
+ proto + ' [0.0.0.0:' + webServicesPort + '], ' +
60
+ 'webhook [' + webhookUrl + '], ' +
61
+ 'token [' + maskedToken + '], ' +
62
+ (localDeployment ? ', local deployment' : '')
63
+ );
64
+
65
+ const settings = {
66
+ svcName,
67
+ applicationName,
68
+ podId,
69
+ environment,
70
+ localDeployment,
71
+ webServicesPort,
72
+ token,
73
+ maskedToken,
74
+ profile,
75
+ _extension_broker_api,
76
+ webhookUrl,
77
+ useSsl,
78
+ sslKey,
79
+ sslCert,
80
+ domainCustom,
81
+ secondaryDomain,
82
+ domainBase,
83
+ debug,
84
+ serviceConfig: serviceConfig
85
+ }
86
+
87
+ module.exports = {
88
+ settings,
89
+ serviceConfig: settings.serviceConfig
90
+ };
91
+
92
+ //Some definitions constants
93
+ const USER_CONNECTED_EVENT = 'userConnected',
94
+ USER_DISCONNECTED_EVENT = 'userDisconnected',
95
+ USER_CONNECT_FUNCTION = 'connectUser',
96
+ USER_DISCONNECT_FUNCTION = 'disconnectUser';
97
+
98
+
99
+ const logger = require('../loggers/logs');
100
+ let definitions;
101
+ try {
102
+ definitions = JSON.parse(fs.readFileSync('./appService.json', 'utf8'));
103
+ } catch (error) {
104
+ logger.error('Service definitions could not be loaded from \'appService.json\' file: ',error);
105
+ }
106
+
107
+ //If the service is a PER-USER service, we have to load the user datastore, and the user related events
108
+ if (definitions.configurationType === 'PER_USER') {
109
+
110
+ // add the USERS data store if this is not defined
111
+ definitions.stores.push({name: '__svc_users__'});
112
+
113
+ // add PER USER events if these are not defined
114
+ const hasUserConnectedEvent = definitions.events.some((event)=>{event.name===USER_CONNECTED_EVENT});
115
+ if (!hasUserConnectedEvent) {
116
+ definitions.events.push({
117
+ label: 'User connected',
118
+ name: USER_CONNECTED_EVENT,
119
+ eventType: 'PER_USER',
120
+ description: 'Event triggered when the current user is connected to the service.'
121
+ })
122
+ }
123
+ const hasUserDisconnectedEvent = definitions.events.some((event)=>{event.name===USER_DISCONNECTED_EVENT});
124
+ if (!hasUserDisconnectedEvent) {
125
+ definitions.events.push({
126
+ label: 'User disconnected',
127
+ name: USER_DISCONNECTED_EVENT,
128
+ eventType: 'PER_USER',
129
+ description: 'Event triggered when the current user is disconnected from the service.'
130
+ })
131
+ }
132
+
133
+ // add PER USER functions if these are not defined
134
+ const hasUserConnectedFunction = definitions.functions.some((func)=>{func.name===USER_CONNECT_FUNCTION});
135
+ if (!hasUserConnectedFunction) {
136
+ definitions.functions.push({
137
+ label: 'Connect User',
138
+ name: USER_CONNECT_FUNCTION,
139
+ eventType: 'PER_USER',
140
+ description: 'Connects the user to the service.',
141
+ callbacks: [
142
+ {
143
+ name: USER_CONNECTED_EVENT,
144
+ maxWaitingTime: 60000,
145
+ maxExpectedResponses: 1
146
+ }
147
+ ]
148
+ })
149
+ }
150
+ const hasUserDisconnectFunction = definitions.functions.some((func)=>{func.name===USER_DISCONNECT_FUNCTION});
151
+ if (!hasUserDisconnectFunction) {
152
+ definitions.functions.push({
153
+ label: 'Disconnect User',
154
+ name: USER_DISCONNECT_FUNCTION,
155
+ eventType: 'PER_USER',
156
+ description: 'Disconnects the user from the service.',
157
+ callbacks: [
158
+ {
159
+ name: USER_DISCONNECTED_EVENT,
160
+ maxWaitingTime: 60000,
161
+ maxExpectedResponses: 1
162
+ },
163
+ ]
164
+ })
165
+ }
166
+
167
+ }
168
+
169
+
170
+ module.exports.definitions = definitions;
171
+