ti2 1.0.14 → 1.0.17

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.
@@ -216,14 +216,17 @@ Ti2 uses [Sequelize v6.13](https://sequelize.org/v6) which is the database ORM w
216
216
 
217
217
  Migrations should be run after the fact, from the root of ti2 instance like so:
218
218
 
219
-
220
219
  ```
221
- $ npm explore ti2<pluginName> -- npx sequelize db:migrate"
220
+ $ ti2 dbapp [plugin name] migrate
222
221
  ```
223
222
 
224
223
  We strongly encourage all the plugin's table names are prefixed with the plugin name (i.e. ti2-pluginName-cacheTable).
225
224
 
226
- This should be executed after the ti2 project migrations have been executed.
225
+ This should be executed after all ti2 host migrations had been executed.
226
+
227
+ ```
228
+ $ ti2 db migrate
229
+ ```
227
230
 
228
231
  ## Scheduled Task (to be released on v2)
229
232
 
@@ -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
+ };