ti2 1.0.15 → 1.0.18

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.
@@ -23,6 +23,7 @@ User + app integration credentials can be added after the app has been added to
23
23
  A MySQl instance is required and the following environment variables are required:
24
24
 
25
25
  - DB_URL (mysql connection in url format)
26
+ - REDIS_URL (a redis database url to store the background queue)
26
27
  - dbCryptoKey (integration details encription key, should be a 32 chars base64 encoded random string)
27
28
  - adminKey (a key for admin related requests)
28
29
  - jwtKey (a key to encrypt user sessions request)
@@ -31,6 +32,7 @@ A MySQl instance is required and the following environment variables are require
31
32
 
32
33
  ```
33
34
  DB_URL=mysql://root:@mysqlserver/ti2development
35
+ REDIS_URL=redis://redis:6379
34
36
  dbCryptoKey=cG9VYmJyQ2tlSDVHeXQ4RUN4VlNieEJRejNYWDlZU0g=
35
37
  jwtSecret=C8k0mrHVfWVTP7pIoZEHRrvFgTULhw3E4swDq1aoDH4P
36
38
  frontendKey=IpOiLahrGQxsQimwoK7Z
@@ -52,7 +54,7 @@ $ npm i ti2 ti2-tourconnect
52
54
  After installing the ti2 package you need to run the database migrations for ti2:
53
55
 
54
56
  ```
55
- $ npm explore ti2<pluginName> -- npx sequelize db:migrate"
57
+ $ ti2 db migrate
56
58
  ```
57
59
 
58
60
  ### Entry File
@@ -61,21 +63,57 @@ then just create an entry file like this :
61
63
 
62
64
  ```javascript
63
65
  // index.js
64
- const ti2 = require('ti2')({
65
- plugins: {
66
- travelgate: require('ti2-travelgate'),
67
- ventrata: require('ti2-ventrata'),
68
- tourconnect: require('ti2-tourconnect'),
69
- },
70
- });
71
- ```
72
66
 
73
- and stat the server using node:
67
+ module.exports = (async () => {
68
+ const ti2 = await require('ti2')({
69
+ plugins: {
70
+ ventrata: require('ti2-ventrata'),
71
+ travelgate: require('ti2-travelgate'),
72
+ tourconnect: require('ti2-tourconnect'),
73
+ },
74
+ });
75
+ return ti2;
76
+ })();
74
77
 
75
78
  ```
79
+
80
+ and start the server:
81
+
82
+ ```bash
76
83
  $ node index.js
77
84
  ```
78
85
 
86
+ in order to support the background job queue (required by some plugins) you should consider the following example:
87
+
88
+ ```javascript
89
+ const plugins = {
90
+ ventrata: require('ti2-ventrata'),
91
+ travelgate: require('ti2-travelgate'),
92
+ tourconnect: require('ti2-tourconnect'),
93
+ };
94
+
95
+ module.exports = (async () => {
96
+ if (process.argv.indexOf('worker') > 0) {
97
+ const worker = await require('ti2')({
98
+ plugins,
99
+ worker: true,
100
+ });
101
+ return worker;
102
+ } else {
103
+ const ti2 = await require('ti2')({
104
+ plugins,
105
+ });
106
+ return ti2;
107
+ }
108
+ })();
109
+ ```
110
+
111
+ and start a background worker (on a second terminal), required for some plugins:
112
+
113
+ ```bash
114
+ $ node index.js worker
115
+ ```
116
+
79
117
  ## API logging
80
118
 
81
119
  Currently logging is supported via elastic search, an elasticLogClient value can ben passed down, example:
@@ -87,12 +125,26 @@ const elasticLogsClient = (() => {
87
125
  return new Client({ node: elasticLogs });
88
126
  })();
89
127
 
90
- const ti2 = require('ti2')({
91
- elasticLogsClient,
92
- plugins: {
93
- ventrata: require('ti2-ventrata'),
94
- travelgate: require('ti2-travelgate'),
95
- tourconnect: require('ti2-tourconnect'),
96
- },
97
- });
128
+ const plugins = {
129
+ ventrata: require('ti2-ventrata'),
130
+ travelgate: require('ti2-travelgate'),
131
+ tourconnect: require('ti2-tourconnect'),
132
+ };
133
+
134
+ module.exports = (async () => {
135
+ if (process.argv.indexOf('worker') > 0) {
136
+ const worker = await require('ti2')({
137
+ elasticLogsClient,
138
+ plugins,
139
+ worker: true,
140
+ });
141
+ return worker;
142
+ } else {
143
+ const ti2 = await require('ti2')({
144
+ elasticLogsClient,
145
+ plugins,
146
+ });
147
+ return ti2;
148
+ }
149
+ })();
98
150
  ```
@@ -0,0 +1,53 @@
1
+ const throng = require('throng');
2
+ require('util').inspect.defaultOptions.depth = null;
3
+ const fakePlugin = require('../test/plugin');
4
+
5
+ const { queue, saveResult } = require('./queue');
6
+
7
+ const workers = process.env.WEB_CONCURRENCY || 2;
8
+
9
+ const maxJobsPerWorker = 1;
10
+
11
+ const worker = ({ plugins: pluginsParam }) => (id, disconnect) => {
12
+ console.log(`Started worker ${id}`);
13
+
14
+ const bye = () => {
15
+ console.log(`Worker ${id} exiting`);
16
+ disconnect();
17
+ process.exit(0);
18
+ };
19
+
20
+ process.on('SIGTERM', bye);
21
+ process.on('SIGUSR2', bye);
22
+ process.on('SIGINT', bye);
23
+
24
+ queue.process(maxJobsPerWorker, async job => {
25
+ const {
26
+ data: {
27
+ pluginName,
28
+ method,
29
+ userId,
30
+ inTesting,
31
+ },
32
+ id: jobId,
33
+ data: params,
34
+ } = job;
35
+ console.log(`job ${jobId} > running ${pluginName}:${method} for ${userId}`);
36
+ const plugins = await (async () => {
37
+ if (inTesting) {
38
+ return [...pluginsParam, await new fakePlugin({ name: pluginName })];
39
+ }
40
+ return pluginsParam;
41
+ })();
42
+ const thePlugin = plugins.find(({ name }) => name === pluginName);
43
+ const resultValue = await thePlugin[method](params);
44
+ console.log(`job ${jobId} > completed`);
45
+ await saveResult({ id: jobId, resultValue });
46
+ });
47
+ };
48
+
49
+ module.exports = async args => throng({
50
+ workers,
51
+ worker: worker(args),
52
+ signals: ['SIGUSR2', 'SIGTERM', 'SIGINT'],
53
+ });
@@ -0,0 +1,88 @@
1
+ const Queue = require('bull');
2
+ const Redis = require('ioredis');
3
+ const R = require('ramda');
4
+
5
+ const REDIS_URL = process.env.REDIS_URL || 'redis://redis:6379';
6
+ const itemsTTL = 3 * 60 * 60; // 3 hours
7
+
8
+ const queue = new Queue('work', `${REDIS_URL}/0`);
9
+ const redisResults = new Redis(`${REDIS_URL}/1`);
10
+
11
+ const getPending = async () => {
12
+ const pending = await queue.count();
13
+ const running = await queue.getActiveCount();
14
+ return pending + running;
15
+ };
16
+
17
+ const allDone = async () => {
18
+ const left1 = await getPending();
19
+ if (left1 === 0) return true;
20
+ return (new Promise(resolve => {
21
+ queue.on('global:completed', async () => {
22
+ const left = await getPending();
23
+ if (left === 0) resolve();
24
+ });
25
+ }));
26
+ };
27
+
28
+ queue.on('failed', (job, err) => {
29
+ console.log(`job ${job.id} failed`, err);
30
+ });
31
+
32
+ const addJob = async (payload, paramsParam) => {
33
+ const params = paramsParam || {};
34
+ const inTesting = Boolean(process.env.JEST_WORKER_ID);
35
+ const id = R.path(['id'], await queue.add({
36
+ ...payload,
37
+ inTesting,
38
+ }, {
39
+ removeOnComplete: true,
40
+ ...params,
41
+ }));
42
+ return id;
43
+ };
44
+
45
+ const saveResult = async ({ id, resultValue }) => {
46
+ await redisResults.set(id, JSON.stringify(resultValue), 'EX', itemsTTL);
47
+ };
48
+
49
+ const jobStatus = async ({ jobId }) => {
50
+ try {
51
+ const job = await queue.getJob(jobId);
52
+ if (!job) {
53
+ // check if it is done
54
+ const result = JSON.parse(await redisResults.get(jobId));
55
+ // await global.sleep();
56
+ if (result) {
57
+ return {
58
+ jobId,
59
+ status: 'success',
60
+ result,
61
+ };
62
+ }
63
+ return { // 404
64
+ jobId,
65
+ status: 'failed',
66
+ };
67
+ }
68
+ const state = await job.getState();
69
+ return {
70
+ jobId,
71
+ status: state.toLowerCase(),
72
+ };
73
+ } catch (err) {
74
+ console.log(`could not get job status for ${jobId}`, err);
75
+ return { // 500
76
+ jobId,
77
+ status: 'failed',
78
+ };
79
+ }
80
+ };
81
+ module.exports = {
82
+ addJob,
83
+ allDone,
84
+ jobStatus,
85
+ queue,
86
+ saveResult,
87
+ redisResults,
88
+ };